]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
More integration
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: March, 2013
4 // ================================================
5
6 string Get_Notif_TypeName(float net_type)
7 {
8         switch(net_type)
9         {
10                 case MSG_ANNCE: return "MSG_ANNCE";
11                 case MSG_INFO: return "MSG_INFO";
12                 case MSG_CENTER: return "MSG_CENTER";
13                 case MSG_CENTER_CPID: return "MSG_CENTER_CPID";
14                 case MSG_MULTI: return "MSG_MULTI";
15         }
16         backtrace(sprintf("Get_Notif_TypeName(%d): Improper net type!\n", net_type));
17         return "";
18 }
19
20 entity Get_Notif_Ent(float net_type, float net_name)
21 {
22         switch(net_type)
23         {
24                 case MSG_ANNCE: return msg_annce_notifs[net_name - 1];
25                 case MSG_INFO: return msg_info_notifs[net_name - 1];
26                 case MSG_CENTER: return msg_center_notifs[net_name - 1];
27                 case MSG_MULTI: return msg_multi_notifs[net_name - 1];
28         }
29         backtrace(sprintf("Get_Notif_Ent(%d, %d): Improper net type!\n", net_type, net_name));
30         return world;
31 }
32
33 string Notification_CheckArgs_TypeName(float net_type, float net_name)
34 {
35         // check supplied type and name for errors
36         string checkargs = "";
37         #define CHECKARG_TYPENAME(type) case MSG_##type##: \
38                 { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
39                 { checkargs = sprintf("Improper name: %d!", net_name); } break; }
40         switch(net_type)
41         {
42                 CHECKARG_TYPENAME(ANNCE)
43                 CHECKARG_TYPENAME(INFO)
44                 CHECKARG_TYPENAME(CENTER)
45                 CHECKARG_TYPENAME(MULTI)
46                 default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
47         }
48         #undef CHECKARG_TYPENAME
49         return checkargs;
50 }
51
52 #ifdef SVQC
53 string Notification_CheckArgs(
54         float broadcast, entity client,
55         float net_type, float net_name)
56 {
57         // check supplied broadcast, target, type, and name for errors
58         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
59         if(checkargs != "") { checkargs = strcat(checkargs, " "); }
60         switch(broadcast)
61         {
62                 case NOTIF_ONE:
63                 case NOTIF_ONE_ONLY:
64                 {
65                         if(IS_NOT_A_CLIENT(client))
66                                 { checkargs = sprintf("%sNo client provided!", checkargs); }
67                         break;
68                 }
69                 
70                 case NOTIF_ALL_EXCEPT:
71                 {
72                         if(IS_NOT_A_CLIENT(client))
73                                 { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
74                         break;
75                 }
76                 
77                 case NOTIF_ALL:
78                 {
79                         if(client)
80                                 { checkargs = sprintf("%sEntity provided when world was required!", checkargs); }
81                         break;
82                 }
83                 
84                 case NOTIF_TEAM:
85                 case NOTIF_TEAM_EXCEPT:
86                 {
87                         if not(teamplay) { checkargs = sprintf("%sTeamplay not active!", checkargs); }
88                         else if(IS_NOT_A_CLIENT(client))
89                         {
90                                 if(broadcast == NOTIF_TEAM) { checkargs = sprintf("%sNo client provided!", checkargs); }
91                                 else { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
92                         }
93                         break;
94                 }
95                 
96                 default: { checkargs = sprintf("%sImproper broadcast: %d!", checkargs, broadcast); break; }
97         }
98         return checkargs;
99 }
100 #endif
101
102 // ===============================
103 //  Initialization Core Functions
104 // ===============================
105
106 // used by restartnotifs command to initialize notifications
107 void Destroy_Notification_Entity(entity notif)
108 {
109         if(notif.nent_name != "") { strunzone(notif.nent_name); }
110         if(notif.nent_snd != "") { strunzone(notif.nent_snd); }
111         if(notif.nent_args != "") { strunzone(notif.nent_args); }
112         if(notif.nent_hudargs != "") { strunzone(notif.nent_hudargs); }
113         if(notif.nent_icon != "") { strunzone(notif.nent_icon); }
114         if(notif.nent_durcnt != "") { strunzone(notif.nent_durcnt); }
115         if(notif.nent_string != "") { strunzone(notif.nent_string); }
116         remove(notif);
117 }
118
119 void Destroy_All_Notifications(void)
120 {
121         entity notif;
122         float i;
123         
124         #define DESTROY_LOOP(type,count) \
125                 for(i = 1; i <= count; ++i) \
126                 { \
127                         notif = Get_Notif_Ent(type, i); \
128                         if not(notif) { backtrace("Destroy_All_Notifications(): Missing notification entity!\n"); return; } \
129                         Destroy_Notification_Entity(notif); \
130                 }
131
132         // kill all networked notifications and centerprints
133         #ifdef SVQC
134         Kill_Notification(NOTIF_ALL, world, 0, 0);
135         #else
136         reset_centerprint_messages();
137         #endif
138
139         // kill all real notification entities
140         DESTROY_LOOP(MSG_ANNCE, NOTIF_ANNCE_COUNT)
141         DESTROY_LOOP(MSG_INFO, NOTIF_INFO_COUNT)
142         DESTROY_LOOP(MSG_CENTER, NOTIF_CENTER_COUNT)
143         DESTROY_LOOP(MSG_MULTI, NOTIF_MULTI_COUNT)
144         #undef DESTROY_LOOP
145 }
146
147 string Process_Notif_Line(
148         float msg_is_info,
149         float chat,
150         string input,
151         string notiftype,
152         string notifname,
153         string stringtype)
154 {
155         if(msg_is_info)
156         {
157                 #ifdef CSQC
158                 if((chat && autocvar_notification_allow_chatboxprint)
159                         || (autocvar_notification_allow_chatboxprint == 2))
160                 {
161                         // pass 1: add ETX char at beginning of line
162                         input = strcat("\{3}", input);
163
164                         // pass 2: add ETX char at end of each new line (so that
165                         // messages with multiple lines are put through chatbox too)
166                         input = strreplace("\n", "\n\{3}", input);
167
168                         // pass 3: strip trailing ETX char
169                         if(substring(input, (strlen(input) - 1), 1) == "\{3}")
170                                 { input = substring(input, 0, (strlen(input) - 1)); }
171                 }
172                 #endif
173                 if(substring(input, (strlen(input) - 1), 1) != "\n")
174                 {
175                         print(sprintf(
176                                 strcat(
177                                         "^1MISSING/BROKEN NEW LINE AT END OF NOTIFICATION: ",
178                                         "^7net_type = %s, net_name = %s, string = %s.\n"
179                                 ),
180                                 notiftype,
181                                 notifname,
182                                 stringtype
183                         ));
184                         notif_error = TRUE;
185                         return strcat(input, "\n");
186                 }
187         }
188         return input;
189 }
190
191 string Process_Notif_Args(
192         float arg_type,
193         string args,
194         string notiftype,
195         string notifname)
196 {
197         string selected, remaining = args;
198         float sel_num = 0;
199
200         for(;(remaining != "");)
201         {
202                 selected = car(remaining); remaining = cdr(remaining);
203
204                 switch(arg_type)
205                 {
206                         case 1: // normal args
207                         {
208                                 if(sel_num == NOTIF_MAX_ARGS)
209                                 {
210                                         print(sprintf(
211                                                 strcat(
212                                                         "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
213                                                         "^7net_type = %s, net_name = %s, max args = %d.\n"
214                                                 ),
215                                                 notiftype,
216                                                 notifname,
217                                                 NOTIF_MAX_ARGS
218                                         ));
219                                         notif_error = TRUE;
220                                         break;
221                                 }
222
223                                 switch(strtolower(selected))
224                                 {
225                                         #define ARG_CASE(prog,selected,result) \
226                                                 #if (prog != ARG_DC) \
227                                                         case selected: { ++sel_num; break; } \
228                                                 #endif
229                                         NOTIF_ARGUMENT_LIST
230                                         #undef ARG_CASE
231                                         default:
232                                         {
233                                                 print(sprintf(
234                                                         strcat(
235                                                                 "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
236                                                                 "^7net_type = %s, net_name = %s, args arg = '%s'.\n"
237                                                         ),
238                                                         notiftype,
239                                                         notifname,
240                                                         selected
241                                                 ));
242                                                 notif_error = TRUE;
243                                                 break;
244                                         }
245                                 }
246                                 break;
247                         }
248                         case 2: // hudargs
249                         {
250                                 if(sel_num == NOTIF_MAX_HUDARGS)
251                                 {
252                                         print(sprintf(
253                                                 strcat(
254                                                         "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
255                                                         "^7net_type = %s, net_name = %s, max hudargs = %d.\n"
256                                                 ),
257                                                 notiftype,
258                                                 notifname,
259                                                 NOTIF_MAX_HUDARGS
260                                         ));
261                                         notif_error = TRUE;
262                                         break;
263                                 }
264
265                                 switch(strtolower(selected))
266                                 {
267                                         #define ARG_CASE(prog,selected,result) \
268                                                 #if (prog == ARG_CS_SV_HA) \
269                                                         case selected: { ++sel_num; break; } \
270                                                 #endif
271                                         NOTIF_ARGUMENT_LIST
272                                         #undef ARG_CASE
273                                         default:
274                                         {
275                                                 print(sprintf(
276                                                         strcat(
277                                                                 "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
278                                                                 "^7net_type = %s, net_name = %s, hudargs arg = '%s'.\n"
279                                                         ),
280                                                         notiftype,
281                                                         notifname,
282                                                         selected
283                                                 ));
284                                                 notif_error = TRUE;
285                                                 break;
286                                         }
287                                 }
288                                 break;
289                         }
290                         case 3: // durcnt 
291                         {
292                                 if(sel_num == NOTIF_MAX_DURCNT)
293                                 {
294                                         print(sprintf(
295                                                 strcat(
296                                                         "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
297                                                         "^7net_type = %s, net_name = %s, max durcnt = %d.\n"
298                                                 ),
299                                                 notiftype,
300                                                 notifname,
301                                                 NOTIF_MAX_DURCNT
302                                         ));
303                                         notif_error = TRUE;
304                                         break;
305                                 }
306
307                                 switch(strtolower(selected))
308                                 {
309                                         #define ARG_CASE(prog,selected,result) \
310                                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
311                                                         case selected: { ++sel_num; break; } \
312                                                 #endif
313                                         NOTIF_ARGUMENT_LIST
314                                         #undef ARG_CASE
315                                         default:
316                                         {
317                                                 if(ftos(stof(selected)) != "") { ++sel_num; }
318                                                 else
319                                                 {
320                                                         print(sprintf(
321                                                                 strcat(
322                                                                         "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
323                                                                         "^7net_type = %s, net_name = %s, durcnt arg = '%s'.\n"
324                                                                 ),
325                                                                 notiftype,
326                                                                 notifname,
327                                                                 selected
328                                                         ));
329                                                         notif_error = TRUE;
330                                                 }
331                                                 break;
332                                         }
333                                 }
334                                 break;
335                         }
336                 }
337         }
338         return args;
339 }
340
341 void Create_Notification_Entity(
342         float var_default,
343         float var_cvar,
344         float typeid,
345         float nameid,
346         string namestring,
347         float anncename,
348         float infoname,
349         float centername,
350         float channel,
351         string snd,
352         float vol,
353         float position,
354         float strnum,
355         float flnum,
356         string args,
357         string hudargs,
358         string icon,
359         float cpid,
360         string durcnt,
361         string normal,
362         string gentle,
363         float msg_is_info,
364         float msg_is_multi)
365 {
366         // =====================
367         //  Global Entity Setup
368         // =====================
369         entity notif = spawn();
370         string typestring = "";
371         switch(typeid)
372         {
373                 case MSG_ANNCE:
374                 {
375                         typestring = "MSG_ANNCE";
376                         msg_info_notifs[nameid - 1] = notif;
377                         notif.classname = "msg_annce_notification";
378                         break;
379                 }
380                 case MSG_INFO:
381                 {
382                         typestring = "MSG_INFO";
383                         msg_info_notifs[nameid - 1] = notif;
384                         notif.classname = "msg_info_notification";
385                         break;
386                 }
387                 case MSG_CENTER:
388                 {
389                         typestring = "MSG_CENTER";
390                         msg_center_notifs[nameid - 1] = notif;
391                         notif.classname = "msg_center_notification";
392                         break;
393                 }
394                 case MSG_MULTI:
395                 {
396                         typestring = "MSG_MULTI";
397                         msg_multi_notifs[nameid - 1] = notif;
398                         notif.classname = "MSG_MULTI_notification";
399                         break;
400                 }
401
402                 default:
403                 {
404                         error(sprintf(
405                                 strcat(
406                                         "^1NOTIFICATION WITH IMPROPER TYPE: ",
407                                         "^7net_type = %d, net_name = %s.\n"
408                                 ),
409                                 typeid,
410                                 namestring
411                         ));
412                         return; // It's not possible to recover from this one
413                 }
414         }
415         notif.nent_default = var_default;
416         notif.nent_name = strzone(namestring);
417         notif.nent_id = nameid;
418         notif.nent_enabled = (1 <= var_cvar);
419
420         // Other pre-notif-setup requisites
421         notif_error = FALSE;
422
423         // ====================
424         //  Notification Setup
425         // ====================
426         if(msg_is_multi)
427         {
428                 // Set MSG_MULTI string/float counts
429                 if((infoname == NO_MSG) && (centername == NO_MSG))
430                 {
431                         print(sprintf(
432                                 strcat(
433                                         "^1NOTIFICATION WITH NO SUBCALLS: ",
434                                         "^7net_type = %s, net_name = %s.\n"
435                                 ),
436                                 typestring,
437                                 namestring
438                         ));
439                         notif_error = TRUE;
440                 }
441                 else
442                 {
443                         float infoname_stringcount = 0, infoname_floatcount = 0;
444                         float centername_stringcount = 0, centername_floatcount = 0;
445                         
446                         if(infoname != NO_MSG)
447                         {
448                                 notif.nent_msginfo = msg_info_notifs[infoname - 1];
449                                 infoname_stringcount = notif.nent_msginfo.nent_stringcount;
450                                 infoname_floatcount = notif.nent_msginfo.nent_floatcount;
451                         }
452                         
453                         if(centername != NO_MSG)
454                         {
455                                 notif.nent_msgcenter = msg_center_notifs[centername - 1];
456                                 centername_stringcount = notif.nent_msgcenter.nent_stringcount;
457                                 centername_floatcount = notif.nent_msgcenter.nent_floatcount;
458                         }
459                         
460                         // set the requirements of THIS notification to the totals of its subcalls
461                         notif.nent_stringcount = max(infoname_stringcount, centername_stringcount);
462                         notif.nent_floatcount = max(infoname_floatcount, centername_floatcount);
463                 }
464         }
465         else if(typeid == MSG_ANNCE)
466         {
467                 // Set MSG_ANNCE information and handle precaching
468                 if(snd != "")
469                 {
470                         notif.nent_channel = channel;
471                         notif.nent_snd = strzone(snd);
472                         notif.nent_vol = vol;
473                         notif.nent_position = position;
474                 }
475                 else
476                 {
477                         print(sprintf(
478                                 strcat(
479                                         "^1NOTIFICATION WITH NO SOUND: ",
480                                         "^7net_type = %s, net_name = %s.\n"
481                                 ),
482                                 typestring,
483                                 namestring
484                         ));
485                         notif_error = TRUE;
486                 }
487         }
488         else
489         {
490                 // Set MSG_INFO and MSG_CENTER string/float counts
491                 notif.nent_stringcount = strnum;
492                 notif.nent_floatcount = flnum;
493
494                 // Only initialize arguments if we're either a client or on a dedicated server
495                 #ifdef SVQC
496                 float should_process_args = server_is_dedicated;
497                 #else
498                 float should_process_args = TRUE;
499                 #endif
500
501                 if(should_process_args)
502                 {
503                         // ========================
504                         //  Process Main Arguments
505                         // ========================
506                         if(strnum + flnum)
507                         {
508                                 if(args != "")
509                                 {
510                                         notif.nent_args = strzone(
511                                                 Process_Notif_Args(1, args, typestring, namestring));
512                                 }
513                                 else if((hudargs == "") && (durcnt ==""))
514                                 {
515                                         print(sprintf(
516                                                 strcat(
517                                                         "^1NOTIFICATION HAS ARG COUNTS BUT NO ARGS OR HUDARGS OR DURCNT: ",
518                                                         "^7net_type = %s, net_name = %s, strnum = %d, flnum = %d\n"
519                                                 ),
520                                                 typestring,
521                                                 namestring,
522                                                 strnum,
523                                                 flnum
524                                         ));
525                                         notif_error = TRUE;
526                                 }
527                         }
528                         else if(args != "")
529                         {
530                                 notif.nent_args = strzone(
531                                         Process_Notif_Args(1, args, typestring, namestring));
532                         }
533
534
535                         // =======================================
536                         //  Process HUD and Centerprint Arguments
537                         //    Only processed on CSQC, as these
538                         //    args are only for HUD features.
539                         // =======================================
540                         #ifdef CSQC
541                         if(hudargs != "")
542                         {
543                                 notif.nent_hudargs = strzone(
544                                         Process_Notif_Args(2, hudargs, typestring, namestring));
545                                         
546                                 if(icon != "") { notif.nent_icon = strzone(icon); }
547                                 else
548                                 {
549                                         print(sprintf(
550                                                 strcat(
551                                                         "^1NOTIFICATION HAS HUDARGS BUT NO ICON: ",
552                                                         "^7net_type = %s, net_name = %s.\n"
553                                                 ),
554                                                 typestring,
555                                                 namestring
556                                         ));
557                                         notif_error = TRUE;
558                                 }
559                         }
560                         else if(icon != "")
561                         {
562                                 print(sprintf(
563                                         strcat(
564                                                 "^1NOTIFICATION HAS ICON BUT NO HUDARGS: ",
565                                                 "^7net_type = %s, net_name = %s.\n"
566                                         ),
567                                         typestring,
568                                         namestring
569                                 ));
570                                 notif_error = TRUE;
571                         }
572
573                         if(durcnt != "")
574                         {
575                                 notif.nent_durcnt = strzone(
576                                         Process_Notif_Args(3, durcnt, typestring, namestring));
577                                         
578                                 if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
579                                 else
580                                 {
581                                         print(sprintf(
582                                                 strcat(
583                                                         "^1NOTIFICATION HAS DURCNT BUT NO CPID: ",
584                                                         "^7net_type = %s, net_name = %s.\n"
585                                                 ),
586                                                 typestring,
587                                                 namestring
588                                         ));
589                                         notif_error = TRUE;
590                                 }
591                         } 
592                         else if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
593                         #endif
594
595
596                         // ======================
597                         //  Process Notif String
598                         // ======================
599                         #define SET_NOTIF_STRING(string,stringname) \
600                                 notif.nent_string = strzone(CCR( \
601                                         Process_Notif_Line( \
602                                                 msg_is_info, \
603                                                 (var_cvar > 1), \
604                                                 string, \
605                                                 typestring, \
606                                                 namestring, \
607                                                 stringname \
608                                         )) \
609                                 );
610
611                         if(GENTLE)
612                         {
613                                 if(gentle != "") { SET_NOTIF_STRING(gentle, "GENTLE") }
614                                 else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
615                         }
616                         else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
617                         
618                         #undef SET_NOTIF_STRING
619
620                         // Check to make sure a string was chosen
621                         if(notif.nent_string == "")
622                         {
623                                 print(sprintf(
624                                         strcat(
625                                                 "^1EMPTY NOTIFICATION: ",
626                                                 "^7net_type = %s, net_name = %s.\n"
627                                         ),
628                                         typestring,
629                                         namestring
630                                 ));
631                                 notif_error = TRUE;
632                         }
633                 }
634         }
635
636         // now check to see if any errors happened 
637         if(notif_error)
638         {
639                 notif.nent_enabled = FALSE; // disable the notification so it can't cause trouble
640                 notif_global_error = TRUE; // throw the red flag that an error happened on init
641         }
642 }
643
644
645 // =========================================
646 //  Cvar Handling With 'dumpnotifs' Command
647 // =========================================
648
649 void Dump_Notifications(float fh, float alsoprint)
650 {
651         #define NOTIF_WRITE(a) { \
652                 fputs(fh, a); \
653                 if(alsoprint) { print(a); } }
654         #define NOTIF_WRITE_ENTITY(name,default,description) { \
655                 notif_msg = \
656                         sprintf( \
657                                 "seta notification_%s \"%d\" \"%s\"\n", \
658                                 name, default, description \
659                         ); \
660                 NOTIF_WRITE(notif_msg) }
661         #define NOTIF_WRITE_HARDCODED(cvar,default,description) { \
662                 notif_msg = \
663                         sprintf( \
664                                 "seta notification_%s \"%s\" \"%s\"\n", \
665                                 cvar, default, description \
666                         ); \
667                 NOTIF_WRITE(notif_msg) }
668
669         string notif_msg;
670         float i;
671         entity e;
672
673         // Note: This warning only applies to the notifications.cfg file that is output...
674
675         // You ARE supposed to manually edit this function to add i.e. hard coded
676         // notification variables for mutators or game modes or such and then
677         // regenerate the notifications.cfg file from the new code.
678
679         NOTIF_WRITE("// ********************************************** //\n");
680         NOTIF_WRITE("// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n");
681         NOTIF_WRITE("// **                                          ** //\n");
682         NOTIF_WRITE("// **  This file is automatically generated    ** //\n");
683         NOTIF_WRITE("// **  by code with the command 'dumpnotifs'.  ** //\n");
684         NOTIF_WRITE("// **                                          ** //\n");
685         NOTIF_WRITE("// **  If you add a new notification, please   ** //\n");
686         NOTIF_WRITE("// **  regenerate this file with that command  ** //\n");
687         NOTIF_WRITE("// **  making sure that the output matches     ** //\n");
688         NOTIF_WRITE("// **  with the lists and defaults in code.    ** //\n");
689         NOTIF_WRITE("// **                                          ** //\n");
690         NOTIF_WRITE("// ********************************************** //\n");
691
692         // These notifications will also append their string as a comment...
693         // This is not necessary, and does not matter if they vary between config versions,
694         // it is just a semi-helpful tool for those who want to manually change their user settings.
695
696         NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications (count = %d):\n", NOTIF_ANNCE_COUNT));
697         for(i = 1; i <= NOTIF_ANNCE_COUNT; ++i)
698         {
699                 e = Get_Notif_Ent(MSG_ANNCE, i);
700                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
701                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)");
702         }
703
704         NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
705         for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
706         {
707                 e = Get_Notif_Ent(MSG_INFO, i);
708                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
709                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)");
710         }
711
712         NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
713         for(i = 1; i <= NOTIF_CENTER_COUNT; ++i)
714         {
715                 e = Get_Notif_Ent(MSG_CENTER, i);
716                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
717                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = centerprint");
718         }
719
720         NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications (count = %d):\n", NOTIF_MULTI_COUNT));
721         for(i = 1; i <= NOTIF_MULTI_COUNT; ++i)
722         {
723                 e = Get_Notif_Ent(MSG_MULTI, i);
724                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
725                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = trigger subcalls");
726         }
727
728         // edit these to match whichever cvars are used for specific notification options
729         NOTIF_WRITE("\n// HARD CODED notification variables:\n");
730         NOTIF_WRITE_HARDCODED("allow_chatboxprint",              "1",    "Allow notifications to be printed to chat box by setting notification cvar to 2 (You can also set this cvar to 2 to force ALL notifications to be printed to the chatbox)");
731         NOTIF_WRITE_HARDCODED("ctf_capture_verbose",             "0",    "Show extra information when someone captures a flag");
732         NOTIF_WRITE_HARDCODED("ctf_pickup_enemy_verbose",        "0",    "Show extra information if an enemy picks up a flag");
733         NOTIF_WRITE_HARDCODED("ctf_pickup_team_verbose",         "0",    "Show extra information if a team mate picks up a flag");
734         NOTIF_WRITE_HARDCODED("errors_are_fatal",                "1",    "If a notification fails upon initialization, cause a Host_Error to stop the program");
735         NOTIF_WRITE_HARDCODED("frag_verbose",                    "1",    "Show extra information when you frag someone (or when you are fragged");
736         NOTIF_WRITE_HARDCODED("item_centerprinttime",            "1.5",  "How long to show item information centerprint messages (like 'You got the Electro' or such)");
737         NOTIF_WRITE_HARDCODED("lifetime_mapload",                "10",   "Amount of time that notification entities last immediately at mapload (in seconds) to help prevent notifications from being lost on early init (like gamestart countdown)");
738         NOTIF_WRITE_HARDCODED("lifetime_runtime",                "0.5",  "Amount of time that notification entities last on the server during runtime (In seconds)");
739         NOTIF_WRITE_HARDCODED("server_allows_frag_verbose",      "1",    "Server side cvar for showing extra information in frag messages... 0 = no extra frag information, 1 = frag information only in warmup, 2 = frag information allowed all the time");
740         NOTIF_WRITE_HARDCODED("server_allows_location",          "1",    "Server side cvar for allowing death messages to show location information too");
741         NOTIF_WRITE_HARDCODED("show_location",                   "0",    "Append location information to MSG_INFO death/kill messages");
742         NOTIF_WRITE_HARDCODED("show_location_string",            "",     "Replacement string piped into sprintf, so you can do different messages like this: ' at the %s' or ' (near %s)'");
743         NOTIF_WRITE_HARDCODED("show_sprees",                     "1",    "Print information about sprees in death/kill messages");
744         NOTIF_WRITE_HARDCODED("show_sprees_center",              "1",    "Show spree information in MSG_CENTER messages... 0 = off, 1 = target (but only for first victim) and attacker");
745         NOTIF_WRITE_HARDCODED("show_sprees_center_specialonly",  "1",    "Don't show spree information in MSG_CENTER messages if it isn't an achievement");
746         NOTIF_WRITE_HARDCODED("show_sprees_info",                "3",    "Show spree information in MSG_INFO messages... 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker");
747         NOTIF_WRITE_HARDCODED("show_sprees_info_newline",        "1",    "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself");
748         NOTIF_WRITE_HARDCODED("show_sprees_info_specialonly",    "1",    "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement");
749
750         NOTIF_WRITE(sprintf(
751                 strcat(
752                         "\n// Notification counts (total = %d): ",
753                         "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n"
754                 ),
755                 (
756                         NOTIF_ANNCE_COUNT +
757                         NOTIF_INFO_COUNT +
758                         NOTIF_CENTER_COUNT +
759                         NOTIF_MULTI_COUNT
760                 ),
761                 NOTIF_ANNCE_COUNT,
762                 NOTIF_INFO_COUNT,
763                 NOTIF_CENTER_COUNT,
764                 NOTIF_MULTI_COUNT
765         ));
766         
767         return;
768         #undef NOTIF_WRITE_HARDCODED
769         #undef NOTIF_WRITE_ENTITY
770         #undef NOTIF_WRITE
771 }
772
773 #ifdef SVQC
774 void Notification_GetCvars()
775 {
776         GetCvars_handleFloat(get_cvars_s, get_cvars_f, FRAG_VERBOSE, "notification_frag_verbose");
777 }
778 #endif
779
780
781 // ===============================
782 //  Frontend Notification Pushing
783 // ===============================
784
785 string Local_Notification_sprintf(
786         string input, string args, 
787         string s1, string s2, string s3, string s4,
788         float f1, float f2, float f3, float f4)
789 {
790         #ifdef NOTIFICATIONS_DEBUG
791         dprint(sprintf(
792                 "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
793                 MakeConsoleSafe(input),
794                 args,
795                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
796                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
797         ));
798         #endif
799         
800         string selected;
801         float sel_num;
802         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
803
804         string tmp_s;
805
806         for(sel_num = 0;(args != "");)
807         {
808                 selected = car(args); args = cdr(args);
809                 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
810                 switch(strtolower(selected))
811                 {
812                         #define ARG_CASE(prog,selected,result) \
813                                 #ifdef CSQC \
814                                         #if (prog != ARG_SV) && (prog != ARG_DC) \
815                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
816                                         #endif \
817                                 #else \
818                                         #if (prog != ARG_CS) && (prog != ARG_DC) \
819                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
820                                         #endif \
821                                 #endif
822                         NOTIF_ARGUMENT_LIST
823                         #undef ARG_CASE
824                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
825                 }
826         }
827         return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
828 }
829
830 #ifdef CSQC
831 void Local_Notification_HUD_Notify_Push(
832         string icon, string hudargs,
833         string s1, string s2, string s3, string s4)
834 {
835         string selected;
836         float sel_num;
837         arg_slot[0] = ""; arg_slot[1] = "";
838
839         for(sel_num = 0;(hudargs != "");)
840         {
841                 selected = car(hudargs); hudargs = cdr(hudargs);
842                 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
843                 switch(strtolower(selected))
844                 {
845                         #define ARG_CASE(prog,selected,result) \
846                                 #if (prog == ARG_CS_SV_HA) \
847                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
848                                 #endif
849                         NOTIF_ARGUMENT_LIST
850                         #undef ARG_CASE
851                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
852                 }
853         }
854         #ifdef NOTIFICATIONS_DEBUG
855         dprint(sprintf(
856                 "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
857                 icon,
858                 hudargs,
859                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
860                 MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
861         ));
862         #endif
863         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
864 }
865
866 void Local_Notification_centerprint_generic(
867         string input, string durcnt,
868         float cpid, float f1, float f2)
869 {
870         string selected;
871         float sel_num;
872         arg_slot[0] = ""; arg_slot[1] = "";
873
874         for(sel_num = 0;(durcnt != "");)
875         {
876                 selected = car(durcnt); durcnt = cdr(durcnt);
877                 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
878                 switch(strtolower(selected))
879                 {
880                         #define ARG_CASE(prog,selected,result) \
881                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
882                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
883                                 #endif
884                         NOTIF_ARGUMENT_LIST
885                         #undef ARG_CASE
886                         default:
887                         {
888                                 if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
889                                 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
890                                 break;
891                         }
892                 }
893         }
894         #ifdef NOTIFICATIONS_DEBUG
895         dprint(sprintf(
896                 "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
897                 MakeConsoleSafe(input),
898                 durcnt,
899                 f1, f2,
900                 stof(arg_slot[0]), stof(arg_slot[1])
901         ));
902         #endif
903         centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
904 }
905 #endif
906
907 void Local_Notification(float net_type, float net_name, ...count)
908 {
909         // check supplied type and name for errors
910         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
911         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
912
913         entity notif = Get_Notif_Ent(net_type, net_name);
914         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
915         if not(notif.nent_enabled)
916         {
917                 #ifdef NOTIFICATIONS_DEBUG
918                 dprint(sprintf(
919                         "Local_Notification(%s, %s): Entity was disabled...\n",
920                         Get_Notif_TypeName(net_type),
921                         notif.nent_name
922                 ));
923                 #endif
924                 return;
925         }
926         
927         if((notif.nent_stringcount + notif.nent_floatcount) > count)
928         {
929                 backtrace(sprintf(
930                         strcat(
931                                 "Not enough arguments for Local_Notification(%s, %s, ...)! ",
932                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
933                                 "Check the definition and function call for accuracy...?\n"
934                         ),
935                         Get_Notif_TypeName(net_type), notif.nent_name,
936                         notif.nent_stringcount, notif.nent_floatcount, count
937                 ));
938                 return;
939         }
940         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
941         {
942                 backtrace(sprintf(
943                         strcat(
944                                 "Too many arguments for Local_Notification(%s, %s, ...)! ",
945                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
946                                 "Check the definition and function call for accuracy...?\n"
947                         ),
948                         Get_Notif_TypeName(net_type), notif.nent_name,
949                         notif.nent_stringcount, notif.nent_floatcount, count
950                 ));
951                 return;
952         }
953
954         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
955         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
956         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
957         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
958         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
959         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
960         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
961         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
962
963         #ifdef NOTIFICATIONS_DEBUG
964         dprint(sprintf(
965                 "Local_Notification(%s, %s, %s, %s);\n",
966                 Get_Notif_TypeName(net_type),
967                 notif.nent_name,
968                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
969                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
970         ));
971         #endif
972         
973         switch(net_type)
974         {
975                 case MSG_ANNCE:
976                 {
977                         print("blah blah\n");
978                         break;
979                 }
980                 
981                 case MSG_INFO:
982                 {
983                         print(
984                                 Local_Notification_sprintf(
985                                         notif.nent_string,
986                                         notif.nent_args, 
987                                         s1, s2, s3, s4,
988                                         f1, f2, f3, f4)
989                         );
990                         #ifdef CSQC 
991                         if(notif.nent_icon != "")
992                         {
993                                 Local_Notification_HUD_Notify_Push(
994                                         notif.nent_icon,
995                                         notif.nent_hudargs,
996                                         s1, s2, s3, s4);
997                         } 
998                         #endif 
999                         break;
1000                 }
1001                 
1002                 #ifdef CSQC
1003                 case MSG_CENTER:
1004                 {
1005                         Local_Notification_centerprint_generic(
1006                                 Local_Notification_sprintf(
1007                                         notif.nent_string,
1008                                         notif.nent_args, 
1009                                         s1, s2, s3, s4,
1010                                         f1, f2, f3, f4),
1011                                 notif.nent_durcnt,
1012                                 notif.nent_cpid,
1013                                 f1, f2);
1014                         break;
1015                 }
1016                 #endif
1017                 
1018                 case MSG_MULTI:
1019                 {
1020                         if(notif.nent_msginfo)
1021                         if(notif.nent_msginfo.nent_enabled)
1022                         {
1023                                 Local_Notification_WOVA(
1024                                         MSG_INFO,
1025                                         notif.nent_msginfo.nent_id, 
1026                                         notif.nent_msginfo.nent_stringcount, 
1027                                         notif.nent_msginfo.nent_floatcount, 
1028                                         s1, s2, s3, s4,
1029                                         f1, f2, f3, f4);
1030                         }
1031                         #ifdef CSQC
1032                         if(notif.nent_msgcenter)
1033                         if(notif.nent_msgcenter.nent_enabled)
1034                         {
1035                                 Local_Notification_WOVA(
1036                                         MSG_CENTER,
1037                                         notif.nent_msgcenter.nent_id, 
1038                                         notif.nent_msgcenter.nent_stringcount, 
1039                                         notif.nent_msgcenter.nent_floatcount, 
1040                                         s1, s2, s3, s4,
1041                                         f1, f2, f3, f4); 
1042                         }
1043                         #endif
1044                         break;
1045                 }
1046         }
1047 }
1048
1049 // WOVA = Without Variable Arguments 
1050 void Local_Notification_WOVA(
1051         float net_type, float net_name,
1052         float stringcount, float floatcount,
1053         string s1, string s2, string s3, string s4,
1054         float f1, float f2, float f3, float f4)
1055 {
1056         #define VARITEM(stringc,floatc,args) \
1057                 if((stringcount == stringc) && (floatcount == floatc)) \
1058                         { Local_Notification(net_type, net_name, args); return; }
1059         EIGHT_VARS_TO_VARARGS_VARLIST
1060         #undef VARITEM
1061         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1062 }
1063
1064
1065 // =========================
1066 //  Notification Networking
1067 // =========================
1068
1069 #ifdef CSQC
1070 void Read_Notification(float is_new)
1071 {
1072         float net_type = ReadByte();
1073         float net_name = ReadShort();
1074
1075         entity notif;
1076
1077         if(net_type == MSG_CENTER_CPID)
1078         {
1079                 #ifdef NOTIFICATIONS_DEBUG
1080                 dprint(sprintf(
1081                         "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n",
1082                         is_new,
1083                         time,
1084                         Get_Notif_TypeName(net_type),
1085                         net_name
1086                 ));
1087                 #endif
1088                 
1089                 if(is_new)
1090                 {
1091                         if(net_name == 0) { reset_centerprint_messages(); }
1092                         else if(net_name != NO_CPID)
1093                         {
1094                                 // in this case, net_name IS the cpid we want to kill
1095                                 centerprint_generic(net_name, "", 0, 0);
1096                         }
1097                         else
1098                         {
1099                                 backtrace(sprintf(
1100                                         "Read_Notification(%d) at %f: ^1TRIED TO KILL NO_CPID CENTERPRINT!\n",
1101                                         is_new,
1102                                         time
1103                                 ));
1104                         } 
1105                 }
1106         }
1107         else
1108         {
1109                 notif = Get_Notif_Ent(net_type, net_name);
1110                 if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
1111
1112                 #ifdef NOTIFICATIONS_DEBUG
1113                 dprint(sprintf(
1114                         "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
1115                         is_new,
1116                         time,
1117                         Get_Notif_TypeName(net_type),
1118                         notif.nent_name
1119                 ));
1120                 #endif
1121
1122                 string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
1123                 string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
1124                 string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
1125                 string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
1126                 float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
1127                 float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
1128                 float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
1129                 float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
1130         
1131                 if(is_new)
1132                 {
1133                         Local_Notification_WOVA(
1134                                 net_type, net_name,
1135                                 notif.nent_stringcount,
1136                                 notif.nent_floatcount,
1137                                 s1, s2, s3, s4,
1138                                 f1, f2, f3, f4);
1139                 }
1140         }
1141 }
1142 #endif
1143
1144 #ifdef SVQC
1145 void Net_Notification_Remove()
1146 {
1147         if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
1148         
1149         #ifdef NOTIFICATIONS_DEBUG
1150         dprint(sprintf(
1151                 "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
1152                 time,
1153                 ((self.nent_net_name == -1) ? "Killed" : "Removed"),
1154                 Get_Notif_TypeName(self.nent_net_type),
1155                 self.owner.nent_name
1156         ));
1157         #endif
1158         
1159         float i;
1160         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
1161         remove(self);
1162 }
1163
1164 float Net_Write_Notification(entity client, float sf)
1165 {
1166         float i, send = FALSE;
1167         
1168         switch(self.nent_broadcast)
1169         {
1170                 case NOTIF_ONE: // send to one client and their spectator
1171                 {
1172                         if(
1173                                 (client == self.nent_client)
1174                                 ||
1175                                 (
1176                                         IS_SPEC(client)
1177                                         &&
1178                                         (client.enemy == self.nent_client)
1179                                 )
1180                         ) { send = TRUE; }
1181                         break;
1182                 }
1183                 case NOTIF_ONE_ONLY: // send ONLY to one client
1184                 {
1185                         if(client == self.nent_client) { send = TRUE; }
1186                         break;
1187                 }
1188                 case NOTIF_TEAM: // send only to X team and their spectators
1189                 {
1190                         if(
1191                                 (client.team == self.nent_client.team)
1192                                 ||
1193                                 (
1194                                         IS_SPEC(client)
1195                                         &&
1196                                         (client.enemy.team == self.nent_client.team)
1197                                 )
1198                         ) { send = TRUE; }
1199                         break;
1200                 }
1201                 case NOTIF_TEAM_EXCEPT: // send only to X team and their spectators, except for Y person and their spectators
1202                 {
1203                         if(
1204                                 (client != self.nent_client)
1205                                 &&
1206                                 (
1207                                         (client.team == self.nent_client.team)
1208                                         ||
1209                                         (
1210                                                 IS_SPEC(client)
1211                                                 &&
1212                                                 (
1213                                                         (client.enemy != self.nent_client)
1214                                                         &&
1215                                                         (client.enemy.team == self.nent_client.team)
1216                                                 )
1217                                         )
1218                                 )
1219                         ) { send = TRUE; }
1220                         break;
1221                 }
1222                 case NOTIF_ALL: // send to everyone
1223                 {
1224                         send = TRUE;
1225                         break;
1226                 }
1227                 case NOTIF_ALL_EXCEPT: // send to everyone except X person and their spectators
1228                 {
1229                         if(
1230                                 (client != self.nent_client)
1231                                 &&
1232                                 !(
1233                                         IS_SPEC(client)
1234                                         &&
1235                                         (client.enemy == self.nent_client)
1236                                 )
1237                         ) { send = TRUE; }
1238                         break;
1239                 }
1240                 default: { send = FALSE; break; }
1241         }
1242
1243         if(send)
1244         {               
1245                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1246                 WriteByte(MSG_ENTITY, self.nent_net_type);
1247                 WriteShort(MSG_ENTITY, self.nent_net_name);
1248                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
1249                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
1250         }
1251
1252         return send; 
1253 }
1254
1255 void Kill_Notification(
1256         float broadcast, entity client,
1257         float net_type, float net_name)
1258 {
1259         string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
1260         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
1261
1262         #ifdef NOTIFICATIONS_DEBUG
1263         dprint(sprintf(
1264                 "Kill_Notification(%d, '%s', %s, %d);\n",
1265                 broadcast,
1266                 client.netname,
1267                 Get_Notif_TypeName(net_type),
1268                 net_name
1269         ));
1270         #endif
1271
1272         entity notif, net_notif;
1273         float killed_cpid = NO_CPID;
1274         
1275         switch(net_type)
1276         {
1277                 case 0:
1278                 {
1279                         killed_cpid = 0; // kill ALL centerprints
1280                         break;
1281                 }
1282                 
1283                 case MSG_CENTER:
1284                 {
1285                         if(net_name)
1286                         {
1287                                 entity notif = Get_Notif_Ent(net_type, net_name);
1288                                 if not(notif) { backtrace("Kill_Notification: Could not find notification entity!\n"); return; }
1289                                 
1290                                 if(notif.nent_cpid)
1291                                         killed_cpid = notif.nent_cpid;
1292                                 else
1293                                         killed_cpid = NO_CPID;
1294                         }
1295                         else
1296                         {
1297                                 killed_cpid = 0; // kill ALL centerprints
1298                         }
1299                         break;
1300                 }
1301
1302                 case MSG_CENTER_CPID:
1303                 {
1304                         killed_cpid = net_name;
1305                         break;
1306                 }
1307         }
1308
1309         if(killed_cpid != NO_CPID)
1310         {
1311                 net_notif = spawn();
1312                 net_notif.classname = "net_kill_notification";
1313                 net_notif.nent_broadcast = broadcast;
1314                 net_notif.nent_client = client;
1315                 net_notif.nent_net_type = MSG_CENTER_CPID;
1316                 net_notif.nent_net_name = killed_cpid;
1317                 Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
1318         }
1319
1320         for(notif = world; (notif = find(notif, classname, "net_notification"));)
1321         {
1322                 if(net_type)
1323                 {
1324                         if(killed_cpid != NO_CPID)
1325                         {
1326                                 if(notif.owner.nent_cpid == killed_cpid)
1327                                 {
1328                                         notif.nent_net_name = -1;
1329                                         notif.nextthink = time;
1330                                 }
1331                                 else { continue; } // we ARE looking for a specific CPID, don't kill everything else too
1332                         }
1333                         else if(notif.nent_net_type == net_type)
1334                         {
1335                                 if(net_name)
1336                                 {
1337                                         if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.nextthink = time; }
1338                                         else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
1339                                 }
1340                                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1341                         }
1342                         else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
1343                 }
1344                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1345         }
1346 }
1347
1348 void Send_Notification(
1349         float broadcast, entity client,
1350         float net_type, float net_name,
1351         ...count)
1352 {
1353         // check supplied broadcast, target, type, and name for errors
1354         string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
1355         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
1356
1357         // retreive counts for the arguments of this notification
1358         entity notif = Get_Notif_Ent(net_type, net_name);
1359         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
1360
1361         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1362         {
1363                 backtrace(sprintf(
1364                         strcat(
1365                                 "Not enough arguments for Send_Notification(%d, %s, %s, ...)! ",
1366                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1367                                 "Check the definition and function call for accuracy...?\n"
1368                         ),
1369                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1370                         notif.nent_stringcount, notif.nent_floatcount, count
1371                 ));
1372                 return;
1373         }
1374         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1375         {
1376                 backtrace(sprintf(
1377                         strcat(
1378                                 "Too many arguments for Send_Notification(%d, %s, %s, ...)! ",
1379                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1380                                 "Check the definition and function call for accuracy...?\n"
1381                         ),
1382                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1383                         notif.nent_stringcount, notif.nent_floatcount, count
1384                 ));
1385                 return;
1386         }
1387
1388         #ifdef NOTIFICATIONS_DEBUG
1389         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1390         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1391         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1392         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1393         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1394         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1395         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1396         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1397         dprint(sprintf(
1398                 "Send_Notification(%d, %s, %s, %s, %s);\n",
1399                 broadcast,
1400                 Get_Notif_TypeName(net_type),
1401                 notif.nent_name,
1402                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1403                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1404         ));
1405         #endif
1406
1407         entity net_notif = spawn();
1408         net_notif.owner = notif;
1409         net_notif.classname = "net_notification";
1410         net_notif.nent_broadcast = broadcast;
1411         net_notif.nent_client = client;
1412         net_notif.nent_net_type = net_type;
1413         net_notif.nent_net_name = net_name;
1414         net_notif.nent_stringcount = notif.nent_stringcount;
1415         net_notif.nent_floatcount = notif.nent_floatcount;
1416         
1417         float i;
1418         for(i = 0; i < net_notif.nent_stringcount; ++i) { net_notif.nent_strings[i] = strzone(...(i, string)); }
1419         for(i = 0; i < net_notif.nent_floatcount; ++i) { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
1420
1421         net_notif.think = Net_Notification_Remove;
1422         net_notif.nextthink =
1423                 ((time > autocvar_notification_lifetime_mapload)
1424                 ?
1425                         (time + autocvar_notification_lifetime_runtime)
1426                         :
1427                         autocvar_notification_lifetime_mapload
1428                 ); 
1429
1430         Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
1431
1432         if(server_is_dedicated && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT) && (net_type != MSG_ANNCE) && (net_type != MSG_CENTER))
1433         {
1434                 Local_Notification_WOVA(
1435                         net_type, net_name,
1436                         notif.nent_stringcount,
1437                         notif.nent_floatcount,
1438                         IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3),
1439                         IFFL(0), IFFL(1), IFFL(2), IFFL(3));
1440         }
1441 }
1442
1443 // WOVA = Without Variable Arguments 
1444 void Send_Notification_WOVA(
1445         float broadcast, entity client,
1446         float net_type, float net_name,
1447         string s1, string s2, string s3, string s4,
1448         float f1, float f2, float f3, float f4)
1449 {
1450         entity notif = Get_Notif_Ent(net_type, net_name);
1451         
1452         #ifdef NOTIFICATIONS_DEBUG
1453         dprint(sprintf(
1454                 "Send_Notification_WOVA(%d, %s, %s, %s, %s);\n",
1455                 broadcast,
1456                 Get_Notif_TypeName(net_type),
1457                 notif.nent_name,
1458                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1459                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1460         ));
1461         #endif
1462         
1463         #define VARITEM(stringc,floatc,args) \
1464                 if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1465                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1466         EIGHT_VARS_TO_VARARGS_VARLIST
1467         #undef VARITEM
1468         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1469 }
1470 #endif // ifdef SVQC