]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Merge remote-tracking branch 'origin/zykure/teamscores'
[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_annce_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((anncename == NO_MSG) && (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                         // announcements don't actually need any arguments, so lets not even count them.
444                         if(anncename != NO_MSG) { notif.nent_msgannce = msg_annce_notifs[anncename - 1]; }
445                         
446                         float infoname_stringcount = 0, infoname_floatcount = 0;
447                         float centername_stringcount = 0, centername_floatcount = 0;
448                         
449                         if(infoname != NO_MSG)
450                         {
451                                 notif.nent_msginfo = msg_info_notifs[infoname - 1];
452                                 infoname_stringcount = notif.nent_msginfo.nent_stringcount;
453                                 infoname_floatcount = notif.nent_msginfo.nent_floatcount;
454                         }
455                         
456                         if(centername != NO_MSG)
457                         {
458                                 notif.nent_msgcenter = msg_center_notifs[centername - 1];
459                                 centername_stringcount = notif.nent_msgcenter.nent_stringcount;
460                                 centername_floatcount = notif.nent_msgcenter.nent_floatcount;
461                         }
462                         
463                         // set the requirements of THIS notification to the totals of its subcalls
464                         notif.nent_stringcount = max(infoname_stringcount, centername_stringcount);
465                         notif.nent_floatcount = max(infoname_floatcount, centername_floatcount);
466                 }
467         }
468         else if(typeid == MSG_ANNCE)
469         {
470                 // Set MSG_ANNCE information and handle precaching
471                 #ifdef CSQC
472                 if not(GENTLE && (var_cvar == 1))
473                 {
474                         if(snd != "")
475                         {
476                                 if(notif.nent_enabled)
477                                 {
478                                         precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd));
479                                         notif.nent_channel = channel;
480                                         notif.nent_snd = strzone(snd);
481                                         notif.nent_vol = vol;
482                                         notif.nent_position = position;
483                                 }
484                         }
485                         else
486                         {
487                                 print(sprintf(
488                                         strcat(
489                                                 "^1NOTIFICATION WITH NO SOUND: ",
490                                                 "^7net_type = %s, net_name = %s.\n"
491                                         ),
492                                         typestring,
493                                         namestring
494                                 ));
495                                 notif_error = TRUE;
496                         }
497                 }
498                 else { notif.nent_enabled = FALSE; }
499                 #else
500                 notif.nent_enabled = FALSE;
501                 #endif
502         }
503         else
504         {
505                 // Set MSG_INFO and MSG_CENTER string/float counts
506                 notif.nent_stringcount = strnum;
507                 notif.nent_floatcount = flnum;
508
509                 // Only initialize arguments if we're either a client or on a dedicated server
510                 #ifdef SVQC
511                 float should_process_args = server_is_dedicated;
512                 #else
513                 float should_process_args = TRUE;
514                 #endif
515
516                 if(should_process_args)
517                 {
518                         // ========================
519                         //  Process Main Arguments
520                         // ========================
521                         if(strnum + flnum)
522                         {
523                                 if(args != "")
524                                 {
525                                         notif.nent_args = strzone(
526                                                 Process_Notif_Args(1, args, typestring, namestring));
527                                 }
528                                 else if((hudargs == "") && (durcnt ==""))
529                                 {
530                                         print(sprintf(
531                                                 strcat(
532                                                         "^1NOTIFICATION HAS ARG COUNTS BUT NO ARGS OR HUDARGS OR DURCNT: ",
533                                                         "^7net_type = %s, net_name = %s, strnum = %d, flnum = %d\n"
534                                                 ),
535                                                 typestring,
536                                                 namestring,
537                                                 strnum,
538                                                 flnum
539                                         ));
540                                         notif_error = TRUE;
541                                 }
542                         }
543                         else if(args != "")
544                         {
545                                 notif.nent_args = strzone(
546                                         Process_Notif_Args(1, args, typestring, namestring));
547                         }
548
549
550                         // =======================================
551                         //  Process HUD and Centerprint Arguments
552                         //    Only processed on CSQC, as these
553                         //    args are only for HUD features.
554                         // =======================================
555                         #ifdef CSQC
556                         if(hudargs != "")
557                         {
558                                 notif.nent_hudargs = strzone(
559                                         Process_Notif_Args(2, hudargs, typestring, namestring));
560                                         
561                                 if(icon != "") { notif.nent_icon = strzone(icon); }
562                                 else
563                                 {
564                                         print(sprintf(
565                                                 strcat(
566                                                         "^1NOTIFICATION HAS HUDARGS BUT NO ICON: ",
567                                                         "^7net_type = %s, net_name = %s.\n"
568                                                 ),
569                                                 typestring,
570                                                 namestring
571                                         ));
572                                         notif_error = TRUE;
573                                 }
574                         }
575                         else if(icon != "")
576                         {
577                                 print(sprintf(
578                                         strcat(
579                                                 "^1NOTIFICATION HAS ICON BUT NO HUDARGS: ",
580                                                 "^7net_type = %s, net_name = %s.\n"
581                                         ),
582                                         typestring,
583                                         namestring
584                                 ));
585                                 notif_error = TRUE;
586                         }
587
588                         if(durcnt != "")
589                         {
590                                 notif.nent_durcnt = strzone(
591                                         Process_Notif_Args(3, durcnt, typestring, namestring));
592                                         
593                                 if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
594                                 else
595                                 {
596                                         print(sprintf(
597                                                 strcat(
598                                                         "^1NOTIFICATION HAS DURCNT BUT NO CPID: ",
599                                                         "^7net_type = %s, net_name = %s.\n"
600                                                 ),
601                                                 typestring,
602                                                 namestring
603                                         ));
604                                         notif_error = TRUE;
605                                 }
606                         } 
607                         else if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
608                         #endif
609
610
611                         // ======================
612                         //  Process Notif String
613                         // ======================
614                         #define SET_NOTIF_STRING(string,stringname) \
615                                 notif.nent_string = strzone(CCR( \
616                                         Process_Notif_Line( \
617                                                 msg_is_info, \
618                                                 (var_cvar > 1), \
619                                                 string, \
620                                                 typestring, \
621                                                 namestring, \
622                                                 stringname \
623                                         )) \
624                                 );
625
626                         if(GENTLE)
627                         {
628                                 if(gentle != "") { SET_NOTIF_STRING(gentle, "GENTLE") }
629                                 else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
630                         }
631                         else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
632                         
633                         #undef SET_NOTIF_STRING
634
635                         // Check to make sure a string was chosen
636                         if(notif.nent_string == "")
637                         {
638                                 print(sprintf(
639                                         strcat(
640                                                 "^1EMPTY NOTIFICATION: ",
641                                                 "^7net_type = %s, net_name = %s.\n"
642                                         ),
643                                         typestring,
644                                         namestring
645                                 ));
646                                 notif_error = TRUE;
647                         }
648                 }
649         }
650
651         // now check to see if any errors happened 
652         if(notif_error)
653         {
654                 notif.nent_enabled = FALSE; // disable the notification so it can't cause trouble
655                 notif_global_error = TRUE; // throw the red flag that an error happened on init
656         }
657 }
658
659
660 // =========================================
661 //  Cvar Handling With 'dumpnotifs' Command
662 // =========================================
663
664 void Dump_Notifications(float fh, float alsoprint)
665 {
666         #define NOTIF_WRITE(a) { \
667                 fputs(fh, a); \
668                 if(alsoprint) { print(a); } }
669         #define NOTIF_WRITE_ENTITY(name,default,description) { \
670                 notif_msg = \
671                         sprintf( \
672                                 "seta notification_%s \"%d\" \"%s\"\n", \
673                                 name, default, description \
674                         ); \
675                 NOTIF_WRITE(notif_msg) }
676         #define NOTIF_WRITE_HARDCODED(cvar,default,description) { \
677                 notif_msg = \
678                         sprintf( \
679                                 "seta notification_%s \"%s\" \"%s\"\n", \
680                                 cvar, default, description \
681                         ); \
682                 NOTIF_WRITE(notif_msg) }
683
684         string notif_msg;
685         float i;
686         entity e;
687
688         // Note: This warning only applies to the notifications.cfg file that is output...
689
690         // You ARE supposed to manually edit this function to add i.e. hard coded
691         // notification variables for mutators or game modes or such and then
692         // regenerate the notifications.cfg file from the new code.
693
694         NOTIF_WRITE("// ********************************************** //\n");
695         NOTIF_WRITE("// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n");
696         NOTIF_WRITE("// **                                          ** //\n");
697         NOTIF_WRITE("// **  This file is automatically generated    ** //\n");
698         NOTIF_WRITE("// **  by code with the command 'dumpnotifs'.  ** //\n");
699         NOTIF_WRITE("// **                                          ** //\n");
700         NOTIF_WRITE("// **  If you add a new notification, please   ** //\n");
701         NOTIF_WRITE("// **  regenerate this file with that command  ** //\n");
702         NOTIF_WRITE("// **  making sure that the output matches     ** //\n");
703         NOTIF_WRITE("// **  with the lists and defaults in code.    ** //\n");
704         NOTIF_WRITE("// **                                          ** //\n");
705         NOTIF_WRITE("// ********************************************** //\n");
706
707         // These notifications will also append their string as a comment...
708         // This is not necessary, and does not matter if they vary between config versions,
709         // it is just a semi-helpful tool for those who want to manually change their user settings.
710
711         NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications (count = %d):\n", NOTIF_ANNCE_COUNT));
712         for(i = 1; i <= NOTIF_ANNCE_COUNT; ++i)
713         {
714                 e = Get_Notif_Ent(MSG_ANNCE, i);
715                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
716                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)");
717         }
718
719         NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
720         for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
721         {
722                 e = Get_Notif_Ent(MSG_INFO, i);
723                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
724                 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)");
725         }
726
727         NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
728         for(i = 1; i <= NOTIF_CENTER_COUNT; ++i)
729         {
730                 e = Get_Notif_Ent(MSG_CENTER, i);
731                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
732                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = centerprint");
733         }
734
735         NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications (count = %d):\n", NOTIF_MULTI_COUNT));
736         for(i = 1; i <= NOTIF_MULTI_COUNT; ++i)
737         {
738                 e = Get_Notif_Ent(MSG_MULTI, i);
739                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
740                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = trigger subcalls");
741         }
742
743         // edit these to match whichever cvars are used for specific notification options
744         NOTIF_WRITE("\n// HARD CODED notification variables:\n");
745         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)");
746         NOTIF_WRITE_HARDCODED("ctf_capture_verbose",             "0",    "Show extra information when someone captures a flag");
747         NOTIF_WRITE_HARDCODED("ctf_pickup_enemy_verbose",        "0",    "Show extra information if an enemy picks up a flag");
748         NOTIF_WRITE_HARDCODED("ctf_pickup_team_verbose",         "0",    "Show extra information if a team mate picks up a flag");
749         NOTIF_WRITE_HARDCODED("debug",                           "0",    "Print extra debug information on all notification function calls (Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... 0 = disabled, 1 = dprint, 2 = print");
750         NOTIF_WRITE_HARDCODED("errors_are_fatal",                "1",    "If a notification fails upon initialization, cause a Host_Error to stop the program");
751         NOTIF_WRITE_HARDCODED("frag_verbose",                    "1",    "Show extra information when you frag someone (or when you are fragged");
752         NOTIF_WRITE_HARDCODED("item_centerprinttime",            "1.5",  "How long to show item information centerprint messages (like 'You got the Electro' or such)");
753         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)");
754         NOTIF_WRITE_HARDCODED("lifetime_runtime",                "0.5",  "Amount of time that notification entities last on the server during runtime (In seconds)");
755         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");
756         NOTIF_WRITE_HARDCODED("server_allows_location",          "1",    "Server side cvar for allowing death messages to show location information too");
757         NOTIF_WRITE_HARDCODED("show_location",                   "0",    "Append location information to MSG_INFO death/kill messages");
758         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)'");
759         NOTIF_WRITE_HARDCODED("show_sprees",                     "1",    "Print information about sprees in death/kill messages");
760         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");
761         NOTIF_WRITE_HARDCODED("show_sprees_center_specialonly",  "1",    "Don't show spree information in MSG_CENTER messages if it isn't an achievement");
762         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");
763         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");
764         NOTIF_WRITE_HARDCODED("show_sprees_info_specialonly",    "1",    "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement");
765
766         NOTIF_WRITE(sprintf(
767                 strcat(
768                         "\n// Notification counts (total = %d): ",
769                         "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n"
770                 ),
771                 (
772                         NOTIF_ANNCE_COUNT +
773                         NOTIF_INFO_COUNT +
774                         NOTIF_CENTER_COUNT +
775                         NOTIF_MULTI_COUNT
776                 ),
777                 NOTIF_ANNCE_COUNT,
778                 NOTIF_INFO_COUNT,
779                 NOTIF_CENTER_COUNT,
780                 NOTIF_MULTI_COUNT
781         ));
782         
783         return;
784         #undef NOTIF_WRITE_HARDCODED
785         #undef NOTIF_WRITE_ENTITY
786         #undef NOTIF_WRITE
787 }
788
789 #ifdef SVQC
790 void Notification_GetCvars()
791 {
792         GetCvars_handleFloat(get_cvars_s, get_cvars_f, FRAG_VERBOSE, "notification_frag_verbose");
793 }
794 #endif
795
796
797 // ===============================
798 //  Frontend Notification Pushing
799 // ===============================
800
801 #ifdef NOTIFICATIONS_DEBUG
802 void Debug_Notification(string input)
803 {
804         switch(autocvar_notification_debug)
805         {
806                 case 1: { dprint(input); break; }
807                 case 2: { print(input); break; }
808         }
809 }
810 #endif
811
812 string Local_Notification_sprintf(
813         string input, string args, 
814         string s1, string s2, string s3, string s4,
815         float f1, float f2, float f3, float f4)
816 {
817         #ifdef NOTIFICATIONS_DEBUG
818         Debug_Notification(sprintf(
819                 "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
820                 MakeConsoleSafe(input),
821                 args,
822                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
823                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
824         ));
825         #endif
826         
827         string selected;
828         float sel_num;
829         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
830
831         string tmp_s;
832
833         for(sel_num = 0;(args != "");)
834         {
835                 selected = car(args); args = cdr(args);
836                 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
837                 switch(strtolower(selected))
838                 {
839                         #define ARG_CASE(prog,selected,result) \
840                                 #ifdef CSQC \
841                                         #if (prog != ARG_SV) && (prog != ARG_DC) \
842                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
843                                         #endif \
844                                 #else \
845                                         #if (prog != ARG_CS) && (prog != ARG_DC) \
846                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
847                                         #endif \
848                                 #endif
849                         NOTIF_ARGUMENT_LIST
850                         #undef ARG_CASE
851                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
852                 }
853         }
854         return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
855 }
856
857 #ifdef CSQC
858 void Local_Notification_sound(
859         float soundchannel, string soundfile,
860         float soundvolume, float soundposition)
861 {
862         if((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam)))
863         {
864                 #ifdef NOTIFICATIONS_DEBUG
865                 Debug_Notification(sprintf(
866                         "Local_Notification_sound(world, %f, '%s', %f, %f);\n",
867                         soundchannel,
868                         sprintf(
869                                 "announcer/%s/%s.wav",
870                                 autocvar_cl_announcer,
871                                 soundfile
872                         ),
873                         soundvolume,
874                         soundposition
875                 ));
876                 #endif
877                 
878                 sound(
879                         world,
880                         soundchannel,
881                         sprintf(
882                                 "announcer/%s/%s.wav",
883                                 autocvar_cl_announcer,
884                                 soundfile
885                         ),
886                         soundvolume,
887                         soundposition
888                 );
889                 
890                 if(prev_soundfile) { strunzone(prev_soundfile); }
891                 prev_soundfile = strzone(soundfile);
892                 prev_soundtime = time;
893         }
894         else
895         {
896                 #ifdef NOTIFICATIONS_DEBUG
897                 Debug_Notification(sprintf(
898                         strcat(
899                                 "Local_Notification_sound(world, %f, '%s', %f, %f) ",
900                                 "^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', timediff: %f, limit: %f\n"
901                          ),
902                         soundchannel,
903                         sprintf(
904                                 "announcer/%s/%s.wav",
905                                 autocvar_cl_announcer,
906                                 soundfile
907                         ),
908                         soundvolume,
909                         soundposition,
910                         prev_soundfile,
911                         (time - prev_soundtime),
912                         autocvar_cl_announcer_antispam
913                 ));
914                 #endif
915         }
916 }
917
918 void Local_Notification_HUD_Notify_Push(
919         string icon, string hudargs,
920         string s1, string s2, string s3, string s4)
921 {
922         string selected;
923         float sel_num;
924         arg_slot[0] = ""; arg_slot[1] = "";
925
926         for(sel_num = 0;(hudargs != "");)
927         {
928                 selected = car(hudargs); hudargs = cdr(hudargs);
929                 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
930                 switch(strtolower(selected))
931                 {
932                         #define ARG_CASE(prog,selected,result) \
933                                 #if (prog == ARG_CS_SV_HA) \
934                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
935                                 #endif
936                         NOTIF_ARGUMENT_LIST
937                         #undef ARG_CASE
938                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
939                 }
940         }
941         #ifdef NOTIFICATIONS_DEBUG
942         Debug_Notification(sprintf(
943                 "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
944                 icon,
945                 hudargs,
946                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
947                 MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
948         ));
949         #endif
950         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
951 }
952
953 void Local_Notification_centerprint_generic(
954         string input, string durcnt,
955         float cpid, float f1, float f2)
956 {
957         string selected;
958         float sel_num;
959         arg_slot[0] = ""; arg_slot[1] = "";
960
961         for(sel_num = 0;(durcnt != "");)
962         {
963                 selected = car(durcnt); durcnt = cdr(durcnt);
964                 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
965                 switch(strtolower(selected))
966                 {
967                         #define ARG_CASE(prog,selected,result) \
968                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
969                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
970                                 #endif
971                         NOTIF_ARGUMENT_LIST
972                         #undef ARG_CASE
973                         default:
974                         {
975                                 if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
976                                 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
977                                 break;
978                         }
979                 }
980         }
981         #ifdef NOTIFICATIONS_DEBUG
982         Debug_Notification(sprintf(
983                 "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
984                 MakeConsoleSafe(input),
985                 durcnt,
986                 f1, f2,
987                 stof(arg_slot[0]), stof(arg_slot[1])
988         ));
989         #endif
990         centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
991 }
992 #endif
993
994 void Local_Notification(float net_type, float net_name, ...count)
995 {
996         // check supplied type and name for errors
997         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
998         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
999
1000         entity notif = Get_Notif_Ent(net_type, net_name);
1001         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
1002         if not(notif.nent_enabled)
1003         {
1004                 #ifdef NOTIFICATIONS_DEBUG
1005                 Debug_Notification(sprintf(
1006                         "Local_Notification(%s, %s): Entity was disabled...\n",
1007                         Get_Notif_TypeName(net_type),
1008                         notif.nent_name
1009                 ));
1010                 #endif
1011                 return;
1012         }
1013         
1014         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1015         {
1016                 backtrace(sprintf(
1017                         strcat(
1018                                 "Not enough arguments for Local_Notification(%s, %s, ...)! ",
1019                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1020                                 "Check the definition and function call for accuracy...?\n"
1021                         ),
1022                         Get_Notif_TypeName(net_type), notif.nent_name,
1023                         notif.nent_stringcount, notif.nent_floatcount, count
1024                 ));
1025                 return;
1026         }
1027         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1028         {
1029                 backtrace(sprintf(
1030                         strcat(
1031                                 "Too many arguments for Local_Notification(%s, %s, ...)! ",
1032                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1033                                 "Check the definition and function call for accuracy...?\n"
1034                         ),
1035                         Get_Notif_TypeName(net_type), notif.nent_name,
1036                         notif.nent_stringcount, notif.nent_floatcount, count
1037                 ));
1038                 return;
1039         }
1040
1041         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1042         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1043         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1044         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1045         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1046         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1047         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1048         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1049
1050         #ifdef NOTIFICATIONS_DEBUG
1051         Debug_Notification(sprintf(
1052                 "Local_Notification(%s, %s, %s, %s);\n",
1053                 Get_Notif_TypeName(net_type),
1054                 notif.nent_name,
1055                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1056                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1057         ));
1058         #endif
1059         
1060         switch(net_type)
1061         {
1062                 case MSG_ANNCE:
1063                 {
1064                         #ifdef CSQC
1065                         Local_Notification_sound(
1066                                 notif.nent_channel,
1067                                 notif.nent_snd,
1068                                 notif.nent_vol,
1069                                 notif.nent_position
1070                         );
1071                         #else
1072                         backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
1073                         #endif
1074                         break;
1075                 }
1076                 
1077                 case MSG_INFO:
1078                 {
1079                         print(
1080                                 Local_Notification_sprintf(
1081                                         notif.nent_string,
1082                                         notif.nent_args, 
1083                                         s1, s2, s3, s4,
1084                                         f1, f2, f3, f4)
1085                         );
1086                         #ifdef CSQC 
1087                         if(notif.nent_icon != "")
1088                         {
1089                                 Local_Notification_HUD_Notify_Push(
1090                                         notif.nent_icon,
1091                                         notif.nent_hudargs,
1092                                         s1, s2, s3, s4);
1093                         } 
1094                         #endif 
1095                         break;
1096                 }
1097                 
1098                 #ifdef CSQC
1099                 case MSG_CENTER:
1100                 {
1101                         Local_Notification_centerprint_generic(
1102                                 Local_Notification_sprintf(
1103                                         notif.nent_string,
1104                                         notif.nent_args, 
1105                                         s1, s2, s3, s4,
1106                                         f1, f2, f3, f4),
1107                                 notif.nent_durcnt,
1108                                 notif.nent_cpid,
1109                                 f1, f2);
1110                         break;
1111                 }
1112                 #endif
1113                 
1114                 case MSG_MULTI:
1115                 {
1116                         if(notif.nent_msginfo)
1117                         if(notif.nent_msginfo.nent_enabled)
1118                         {
1119                                 Local_Notification_WOVA(
1120                                         MSG_INFO,
1121                                         notif.nent_msginfo.nent_id, 
1122                                         notif.nent_msginfo.nent_stringcount, 
1123                                         notif.nent_msginfo.nent_floatcount, 
1124                                         s1, s2, s3, s4,
1125                                         f1, f2, f3, f4);
1126                         }
1127                         #ifdef CSQC
1128                         if(notif.nent_msgannce)
1129                         if(notif.nent_msgannce.nent_enabled)
1130                         {
1131                                 Local_Notification_WOVA(
1132                                         MSG_ANNCE,
1133                                         notif.nent_msgannce.nent_id, 
1134                                         0, 0, 
1135                                         "", "", "", "",
1136                                         0, 0, 0, 0);
1137                         }
1138                         if(notif.nent_msgcenter)
1139                         if(notif.nent_msgcenter.nent_enabled)
1140                         {
1141                                 Local_Notification_WOVA(
1142                                         MSG_CENTER,
1143                                         notif.nent_msgcenter.nent_id, 
1144                                         notif.nent_msgcenter.nent_stringcount, 
1145                                         notif.nent_msgcenter.nent_floatcount, 
1146                                         s1, s2, s3, s4,
1147                                         f1, f2, f3, f4); 
1148                         }
1149                         #endif
1150                         break;
1151                 }
1152         }
1153 }
1154
1155 // WOVA = Without Variable Arguments 
1156 void Local_Notification_WOVA(
1157         float net_type, float net_name,
1158         float stringcount, float floatcount,
1159         string s1, string s2, string s3, string s4,
1160         float f1, float f2, float f3, float f4)
1161 {
1162         #define VARITEM(stringc,floatc,args) \
1163                 if((stringcount == stringc) && (floatcount == floatc)) \
1164                         { Local_Notification(net_type, net_name, args); return; }
1165         EIGHT_VARS_TO_VARARGS_VARLIST
1166         #undef VARITEM
1167         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1168 }
1169
1170
1171 // =========================
1172 //  Notification Networking
1173 // =========================
1174
1175 #ifdef CSQC
1176 void Read_Notification(float is_new)
1177 {
1178         float net_type = ReadByte();
1179         float net_name = ReadShort();
1180
1181         entity notif;
1182
1183         if(net_type == MSG_CENTER_CPID)
1184         {
1185                 #ifdef NOTIFICATIONS_DEBUG
1186                 Debug_Notification(sprintf(
1187                         "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n",
1188                         is_new,
1189                         time,
1190                         Get_Notif_TypeName(net_type),
1191                         net_name
1192                 ));
1193                 #endif
1194                 
1195                 if(is_new)
1196                 {
1197                         if(net_name == 0) { reset_centerprint_messages(); }
1198                         else if(net_name != NO_CPID)
1199                         {
1200                                 // in this case, net_name IS the cpid we want to kill
1201                                 centerprint_generic(net_name, "", 0, 0);
1202                         }
1203                         else
1204                         {
1205                                 backtrace(sprintf(
1206                                         "Read_Notification(%d) at %f: ^1TRIED TO KILL NO_CPID CENTERPRINT!\n",
1207                                         is_new,
1208                                         time
1209                                 ));
1210                         } 
1211                 }
1212         }
1213         else
1214         {
1215                 notif = Get_Notif_Ent(net_type, net_name);
1216                 if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
1217
1218                 #ifdef NOTIFICATIONS_DEBUG
1219                 Debug_Notification(sprintf(
1220                         "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
1221                         is_new,
1222                         time,
1223                         Get_Notif_TypeName(net_type),
1224                         notif.nent_name
1225                 ));
1226                 #endif
1227
1228                 string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
1229                 string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
1230                 string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
1231                 string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
1232                 float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
1233                 float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
1234                 float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
1235                 float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
1236         
1237                 if(is_new)
1238                 {
1239                         Local_Notification_WOVA(
1240                                 net_type, net_name,
1241                                 notif.nent_stringcount,
1242                                 notif.nent_floatcount,
1243                                 s1, s2, s3, s4,
1244                                 f1, f2, f3, f4);
1245                 }
1246         }
1247 }
1248 #endif
1249
1250 #ifdef SVQC
1251 void Net_Notification_Remove()
1252 {
1253         if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
1254         
1255         #ifdef NOTIFICATIONS_DEBUG
1256         Debug_Notification(sprintf(
1257                 "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
1258                 time,
1259                 ((self.nent_net_name == -1) ? "Killed" : "Removed"),
1260                 Get_Notif_TypeName(self.nent_net_type),
1261                 self.owner.nent_name
1262         ));
1263         #endif
1264         
1265         float i;
1266         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
1267         remove(self);
1268 }
1269
1270 float Net_Write_Notification(entity client, float sf)
1271 {
1272         float i, send = FALSE;
1273         
1274         switch(self.nent_broadcast)
1275         {
1276                 case NOTIF_ONE: // send to one client and their spectator
1277                 {
1278                         if(
1279                                 (client == self.nent_client)
1280                                 ||
1281                                 (
1282                                         IS_SPEC(client)
1283                                         &&
1284                                         (client.enemy == self.nent_client)
1285                                 )
1286                         ) { send = TRUE; }
1287                         break;
1288                 }
1289                 case NOTIF_ONE_ONLY: // send ONLY to one client
1290                 {
1291                         if(client == self.nent_client) { send = TRUE; }
1292                         break;
1293                 }
1294                 case NOTIF_TEAM: // send only to X team and their spectators
1295                 {
1296                         if(
1297                                 (client.team == self.nent_client.team)
1298                                 ||
1299                                 (
1300                                         IS_SPEC(client)
1301                                         &&
1302                                         (client.enemy.team == self.nent_client.team)
1303                                 )
1304                         ) { send = TRUE; }
1305                         break;
1306                 }
1307                 case NOTIF_TEAM_EXCEPT: // send only to X team and their spectators, except for Y person and their spectators
1308                 {
1309                         if(
1310                                 (client != self.nent_client)
1311                                 &&
1312                                 (
1313                                         (client.team == self.nent_client.team)
1314                                         ||
1315                                         (
1316                                                 IS_SPEC(client)
1317                                                 &&
1318                                                 (
1319                                                         (client.enemy != self.nent_client)
1320                                                         &&
1321                                                         (client.enemy.team == self.nent_client.team)
1322                                                 )
1323                                         )
1324                                 )
1325                         ) { send = TRUE; }
1326                         break;
1327                 }
1328                 case NOTIF_ALL: // send to everyone
1329                 {
1330                         send = TRUE;
1331                         break;
1332                 }
1333                 case NOTIF_ALL_EXCEPT: // send to everyone except X person and their spectators
1334                 {
1335                         if(
1336                                 (client != self.nent_client)
1337                                 &&
1338                                 !(
1339                                         IS_SPEC(client)
1340                                         &&
1341                                         (client.enemy == self.nent_client)
1342                                 )
1343                         ) { send = TRUE; }
1344                         break;
1345                 }
1346                 default: { send = FALSE; break; }
1347         }
1348
1349         if(send)
1350         {               
1351                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1352                 WriteByte(MSG_ENTITY, self.nent_net_type);
1353                 WriteShort(MSG_ENTITY, self.nent_net_name);
1354                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
1355                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
1356         }
1357
1358         return send; 
1359 }
1360
1361 void Kill_Notification(
1362         float broadcast, entity client,
1363         float net_type, float net_name)
1364 {
1365         string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
1366         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
1367
1368         #ifdef NOTIFICATIONS_DEBUG
1369         Debug_Notification(sprintf(
1370                 "Kill_Notification(%d, '%s', %s, %d);\n",
1371                 broadcast,
1372                 client.netname,
1373                 (net_type ? Get_Notif_TypeName(net_type) : "0"),
1374                 net_name
1375         ));
1376         #endif
1377
1378         entity notif, net_notif;
1379         float killed_cpid = NO_CPID;
1380         
1381         switch(net_type)
1382         {
1383                 case 0:
1384                 {
1385                         killed_cpid = 0; // kill ALL centerprints
1386                         break;
1387                 }
1388                 
1389                 case MSG_CENTER:
1390                 {
1391                         if(net_name)
1392                         {
1393                                 entity notif = Get_Notif_Ent(net_type, net_name);
1394                                 if not(notif) { backtrace("Kill_Notification: Could not find notification entity!\n"); return; }
1395                                 
1396                                 if(notif.nent_cpid)
1397                                         killed_cpid = notif.nent_cpid;
1398                                 else
1399                                         killed_cpid = NO_CPID;
1400                         }
1401                         else
1402                         {
1403                                 killed_cpid = 0; // kill ALL centerprints
1404                         }
1405                         break;
1406                 }
1407
1408                 case MSG_CENTER_CPID:
1409                 {
1410                         killed_cpid = net_name;
1411                         break;
1412                 }
1413         }
1414
1415         if(killed_cpid != NO_CPID)
1416         {
1417                 net_notif = spawn();
1418                 net_notif.classname = "net_kill_notification";
1419                 net_notif.nent_broadcast = broadcast;
1420                 net_notif.nent_client = client;
1421                 net_notif.nent_net_type = MSG_CENTER_CPID;
1422                 net_notif.nent_net_name = killed_cpid;
1423                 Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
1424         }
1425
1426         for(notif = world; (notif = find(notif, classname, "net_notification"));)
1427         {
1428                 if(net_type)
1429                 {
1430                         if((killed_cpid != NO_CPID) && (notif.nent_net_type == MSG_CENTER))
1431                         {
1432                                 if(notif.owner.nent_cpid == killed_cpid)
1433                                 {
1434                                         notif.nent_net_name = -1;
1435                                         notif.nextthink = time;
1436                                 }
1437                                 else { continue; } // we ARE looking for a specific CPID, don't kill everything else too
1438                         }
1439                         else if(notif.nent_net_type == net_type)
1440                         {
1441                                 if(net_name)
1442                                 {
1443                                         if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.nextthink = time; }
1444                                         else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
1445                                 }
1446                                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1447                         }
1448                         else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
1449                 }
1450                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1451         }
1452 }
1453
1454 void Send_Notification(
1455         float broadcast, entity client,
1456         float net_type, float net_name,
1457         ...count)
1458 {
1459         // check supplied broadcast, target, type, and name for errors
1460         string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
1461         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
1462
1463         // retreive counts for the arguments of this notification
1464         entity notif = Get_Notif_Ent(net_type, net_name);
1465         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
1466
1467         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1468         {
1469                 backtrace(sprintf(
1470                         strcat(
1471                                 "Not enough arguments for Send_Notification(%d, %s, %s, ...)! ",
1472                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1473                                 "Check the definition and function call for accuracy...?\n"
1474                         ),
1475                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1476                         notif.nent_stringcount, notif.nent_floatcount, count
1477                 ));
1478                 return;
1479         }
1480         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1481         {
1482                 backtrace(sprintf(
1483                         strcat(
1484                                 "Too many arguments for Send_Notification(%d, %s, %s, ...)! ",
1485                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1486                                 "Check the definition and function call for accuracy...?\n"
1487                         ),
1488                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1489                         notif.nent_stringcount, notif.nent_floatcount, count
1490                 ));
1491                 return;
1492         }
1493
1494         #ifdef NOTIFICATIONS_DEBUG
1495         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1496         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1497         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1498         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1499         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1500         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1501         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1502         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1503         Debug_Notification(sprintf(
1504                 "Send_Notification(%d, %s, %s, %s, %s);\n",
1505                 broadcast,
1506                 Get_Notif_TypeName(net_type),
1507                 notif.nent_name,
1508                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1509                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1510         ));
1511         #endif
1512
1513         entity net_notif = spawn();
1514         net_notif.owner = notif;
1515         net_notif.classname = "net_notification";
1516         net_notif.nent_broadcast = broadcast;
1517         net_notif.nent_client = client;
1518         net_notif.nent_net_type = net_type;
1519         net_notif.nent_net_name = net_name;
1520         net_notif.nent_stringcount = notif.nent_stringcount;
1521         net_notif.nent_floatcount = notif.nent_floatcount;
1522         
1523         float i;
1524         for(i = 0; i < net_notif.nent_stringcount; ++i) { net_notif.nent_strings[i] = strzone(...(i, string)); }
1525         for(i = 0; i < net_notif.nent_floatcount; ++i) { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
1526
1527         net_notif.think = Net_Notification_Remove;
1528         net_notif.nextthink =
1529                 ((time > autocvar_notification_lifetime_mapload)
1530                 ?
1531                         (time + autocvar_notification_lifetime_runtime)
1532                         :
1533                         autocvar_notification_lifetime_mapload
1534                 ); 
1535
1536         Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
1537
1538         if(server_is_dedicated && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT) && (net_type != MSG_ANNCE) && (net_type != MSG_CENTER))
1539         {
1540                 Local_Notification_WOVA(
1541                         net_type, net_name,
1542                         notif.nent_stringcount,
1543                         notif.nent_floatcount,
1544                         IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3),
1545                         IFFL(0), IFFL(1), IFFL(2), IFFL(3));
1546         }
1547 }
1548
1549 // WOVA = Without Variable Arguments 
1550 void Send_Notification_WOVA(
1551         float broadcast, entity client,
1552         float net_type, float net_name,
1553         string s1, string s2, string s3, string s4,
1554         float f1, float f2, float f3, float f4)
1555 {
1556         entity notif = Get_Notif_Ent(net_type, net_name);
1557         
1558         #ifdef NOTIFICATIONS_DEBUG
1559         Debug_Notification(sprintf(
1560                 "Send_Notification_WOVA(%d, %s, %s, %s, %s);\n",
1561                 broadcast,
1562                 Get_Notif_TypeName(net_type),
1563                 notif.nent_name,
1564                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1565                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1566         ));
1567         #endif
1568         
1569         #define VARITEM(stringc,floatc,args) \
1570                 if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1571                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1572         EIGHT_VARS_TO_VARARGS_VARLIST
1573         #undef VARITEM
1574         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1575 }
1576 #endif // ifdef SVQC