]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Merge remote-tracking branch 'origin/terencehill/hud_crash_fix'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: June, 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(description) { \
670                 notif_msg = \
671                         sprintf( \
672                                 "seta notification_%s \"%d\" \"%s\"\n", \
673                                 e.nent_name, e.nent_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                 
717                 NOTIF_WRITE_ENTITY(
718                         "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)"
719                 );
720         }
721
722         NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
723         for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
724         {
725                 e = Get_Notif_Ent(MSG_INFO, i);
726                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
727                 
728                 NOTIF_WRITE_ENTITY(
729                         "Notification control cvar: 0 = off, 1 = print to console, "
730                         "2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
731                 );
732         }
733
734         NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
735         for(i = 1; i <= NOTIF_CENTER_COUNT; ++i)
736         {
737                 e = Get_Notif_Ent(MSG_CENTER, i);
738                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
739                 
740                 NOTIF_WRITE_ENTITY(
741                         "Notification control cvar: 0 = off, 1 = centerprint"
742                 );
743         }
744
745         NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications (count = %d):\n", NOTIF_MULTI_COUNT));
746         for(i = 1; i <= NOTIF_MULTI_COUNT; ++i)
747         {
748                 e = Get_Notif_Ent(MSG_MULTI, i);
749                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
750                 
751                 NOTIF_WRITE_ENTITY(
752                         "Notification control cvar: 0 = off, 1 = trigger subcalls"
753                 );
754         }
755
756         // edit these to match whichever cvars are used for specific notification options
757         NOTIF_WRITE("\n// HARD CODED notification variables:\n");
758         
759         NOTIF_WRITE_HARDCODED(
760                 "allow_chatboxprint", "1",
761                 "Allow notifications to be printed to chat box by setting notification cvar to 2 "
762                 "(You can also set this cvar to 2 to force ALL notifications to be printed to the chatbox)"
763         );
764         
765         NOTIF_WRITE_HARDCODED(
766                 "ctf_capture_verbose", "0",
767                 "Show extra information when someone captures a flag"
768         );
769         
770         NOTIF_WRITE_HARDCODED(
771                 "ctf_pickup_enemy_verbose", "0",
772                 "Show extra information if an enemy picks up a flag"
773         );
774         
775         NOTIF_WRITE_HARDCODED(
776                 "ctf_pickup_team_verbose", "0",
777                 "Show extra information if a team mate picks up a flag"
778         );
779         
780         NOTIF_WRITE_HARDCODED(
781                 "debug", "0",
782                 "Print extra debug information on all notification function calls "
783                 "(Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... "
784                 "0 = disabled, 1 = dprint, 2 = print"
785         );
786         
787         NOTIF_WRITE_HARDCODED(
788                 "errors_are_fatal", "1",
789                 "If a notification fails upon initialization, cause a Host_Error to stop the program"
790         );
791         
792         NOTIF_WRITE_HARDCODED(
793                 "frag_verbose", "1",
794                 "Show extra information when you frag someone (or when you are fragged"
795         );
796         
797         NOTIF_WRITE_HARDCODED(
798                 "item_centerprinttime", "1.5",
799                 "How long to show item information centerprint messages (like 'You got the Electro' or such)"
800         );
801         
802         NOTIF_WRITE_HARDCODED(
803                 "lifetime_mapload", "10",
804                 "Amount of time that notification entities last immediately at mapload (in seconds) "
805                 "to help prevent notifications from being lost on early init (like gamestart countdown)"
806         );
807         
808         NOTIF_WRITE_HARDCODED(
809                 "lifetime_runtime", "0.5",
810                 "Amount of time that notification entities last on the server during runtime (In seconds)"
811         );
812         
813         NOTIF_WRITE_HARDCODED(
814                 "server_allows_frag_verbose", "1",
815                 "Server side cvar for showing extra information in frag messages... 0 = no extra frag information, "
816                 "1 = frag information only in warmup, 2 = frag information allowed all the time"
817         );
818         
819         NOTIF_WRITE_HARDCODED(
820                 "server_allows_location", "1",
821                 "Server side cvar for allowing death messages to show location information too"
822         );
823         
824         NOTIF_WRITE_HARDCODED(
825                 "show_location", "0",
826                 "Append location information to MSG_INFO death/kill messages"
827         );
828         
829         NOTIF_WRITE_HARDCODED(
830                 "show_location_string", "",
831                 "Replacement string piped into sprintf, "
832                 "so you can do different messages like this: ' at the %s' or ' (near %s)'"
833         );
834         
835         NOTIF_WRITE_HARDCODED(
836                 "show_sprees", "1",
837                 "Print information about sprees in death/kill messages"
838         );
839         
840         NOTIF_WRITE_HARDCODED(
841                 "show_sprees_center", "1",
842                 "Show spree information in MSG_CENTER messages... "
843                 "0 = off, 1 = target (but only for first victim) and attacker"
844         );
845         
846         NOTIF_WRITE_HARDCODED(
847                 "show_sprees_center_specialonly", "1",
848                 "Don't show spree information in MSG_CENTER messages if it isn't an achievement"
849         );
850         
851         NOTIF_WRITE_HARDCODED(
852                 "show_sprees_info", "3",
853                 "Show spree information in MSG_INFO messages... "
854                 "0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker"
855         );
856         
857         NOTIF_WRITE_HARDCODED(
858                 "show_sprees_info_newline", "1",
859                 "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
860         );
861         
862         NOTIF_WRITE_HARDCODED(
863                 "show_sprees_info_specialonly", "1",
864                 "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
865         );
866
867         NOTIF_WRITE(sprintf(
868                 strcat(
869                         "\n// Notification counts (total = %d): ",
870                         "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n"
871                 ),
872                 (
873                         NOTIF_ANNCE_COUNT +
874                         NOTIF_INFO_COUNT +
875                         NOTIF_CENTER_COUNT +
876                         NOTIF_MULTI_COUNT
877                 ),
878                 NOTIF_ANNCE_COUNT,
879                 NOTIF_INFO_COUNT,
880                 NOTIF_CENTER_COUNT,
881                 NOTIF_MULTI_COUNT
882         ));
883         
884         return;
885         #undef NOTIF_WRITE_HARDCODED
886         #undef NOTIF_WRITE_ENTITY
887         #undef NOTIF_WRITE
888 }
889
890 #ifdef SVQC
891 void Notification_GetCvars()
892 {
893         GetCvars_handleFloat(get_cvars_s, get_cvars_f, FRAG_VERBOSE, "notification_frag_verbose");
894 }
895 #endif
896
897
898 // ===============================
899 //  Frontend Notification Pushing
900 // ===============================
901
902 #ifdef NOTIFICATIONS_DEBUG
903 void Debug_Notification(string input)
904 {
905         switch(autocvar_notification_debug)
906         {
907                 case 1: { dprint(input); break; }
908                 case 2: { print(input); break; }
909         }
910 }
911 #endif
912
913 string Local_Notification_sprintf(
914         string input, string args, 
915         string s1, string s2, string s3, string s4,
916         float f1, float f2, float f3, float f4)
917 {
918         #ifdef NOTIFICATIONS_DEBUG
919         Debug_Notification(sprintf(
920                 "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
921                 MakeConsoleSafe(input),
922                 args,
923                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
924                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
925         ));
926         #endif
927         
928         string selected;
929         float sel_num;
930         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
931
932         string tmp_s;
933
934         for(sel_num = 0;(args != "");)
935         {
936                 selected = car(args); args = cdr(args);
937                 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
938                 switch(strtolower(selected))
939                 {
940                         #define ARG_CASE(prog,selected,result) \
941                                 #ifdef CSQC \
942                                         #if (prog != ARG_SV) && (prog != ARG_DC) \
943                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
944                                         #endif \
945                                 #else \
946                                         #if (prog != ARG_CS) && (prog != ARG_DC) \
947                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
948                                         #endif \
949                                 #endif
950                         NOTIF_ARGUMENT_LIST
951                         #undef ARG_CASE
952                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
953                 }
954         }
955         return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
956 }
957
958 #ifdef CSQC
959 void Local_Notification_sound(
960         float soundchannel, string soundfile,
961         float soundvolume, float soundposition)
962 {
963         if((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam)))
964         {
965                 #ifdef NOTIFICATIONS_DEBUG
966                 Debug_Notification(sprintf(
967                         "Local_Notification_sound(world, %f, '%s', %f, %f);\n",
968                         soundchannel,
969                         sprintf(
970                                 "announcer/%s/%s.wav",
971                                 autocvar_cl_announcer,
972                                 soundfile
973                         ),
974                         soundvolume,
975                         soundposition
976                 ));
977                 #endif
978                 
979                 sound(
980                         world,
981                         soundchannel,
982                         sprintf(
983                                 "announcer/%s/%s.wav",
984                                 autocvar_cl_announcer,
985                                 soundfile
986                         ),
987                         soundvolume,
988                         soundposition
989                 );
990                 
991                 if(prev_soundfile) { strunzone(prev_soundfile); }
992                 prev_soundfile = strzone(soundfile);
993                 prev_soundtime = time;
994         }
995         else
996         {
997                 #ifdef NOTIFICATIONS_DEBUG
998                 Debug_Notification(sprintf(
999                         strcat(
1000                                 "Local_Notification_sound(world, %f, '%s', %f, %f) ",
1001                                 "^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', timediff: %f, limit: %f\n"
1002                          ),
1003                         soundchannel,
1004                         sprintf(
1005                                 "announcer/%s/%s.wav",
1006                                 autocvar_cl_announcer,
1007                                 soundfile
1008                         ),
1009                         soundvolume,
1010                         soundposition,
1011                         prev_soundfile,
1012                         (time - prev_soundtime),
1013                         autocvar_cl_announcer_antispam
1014                 ));
1015                 #endif
1016         }
1017 }
1018
1019 void Local_Notification_HUD_Notify_Push(
1020         string icon, string hudargs,
1021         string s1, string s2, string s3, string s4)
1022 {
1023         string selected;
1024         float sel_num;
1025         arg_slot[0] = ""; arg_slot[1] = "";
1026
1027         for(sel_num = 0;(hudargs != "");)
1028         {
1029                 selected = car(hudargs); hudargs = cdr(hudargs);
1030                 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1031                 switch(strtolower(selected))
1032                 {
1033                         #define ARG_CASE(prog,selected,result) \
1034                                 #if (prog == ARG_CS_SV_HA) \
1035                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1036                                 #endif
1037                         NOTIF_ARGUMENT_LIST
1038                         #undef ARG_CASE
1039                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1040                 }
1041         }
1042         #ifdef NOTIFICATIONS_DEBUG
1043         Debug_Notification(sprintf(
1044                 "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
1045                 icon,
1046                 hudargs,
1047                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1048                 MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
1049         ));
1050         #endif
1051         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
1052 }
1053
1054 void Local_Notification_centerprint_generic(
1055         string input, string durcnt,
1056         float cpid, float f1, float f2)
1057 {
1058         string selected;
1059         float sel_num;
1060         arg_slot[0] = ""; arg_slot[1] = "";
1061
1062         for(sel_num = 0;(durcnt != "");)
1063         {
1064                 selected = car(durcnt); durcnt = cdr(durcnt);
1065                 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
1066                 switch(strtolower(selected))
1067                 {
1068                         #define ARG_CASE(prog,selected,result) \
1069                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
1070                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1071                                 #endif
1072                         NOTIF_ARGUMENT_LIST
1073                         #undef ARG_CASE
1074                         default:
1075                         {
1076                                 if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
1077                                 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
1078                                 break;
1079                         }
1080                 }
1081         }
1082         #ifdef NOTIFICATIONS_DEBUG
1083         Debug_Notification(sprintf(
1084                 "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
1085                 MakeConsoleSafe(input),
1086                 durcnt,
1087                 f1, f2,
1088                 stof(arg_slot[0]), stof(arg_slot[1])
1089         ));
1090         #endif
1091         centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
1092 }
1093 #endif
1094
1095 void Local_Notification(float net_type, float net_name, ...count)
1096 {
1097         // check supplied type and name for errors
1098         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
1099         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
1100
1101         entity notif = Get_Notif_Ent(net_type, net_name);
1102         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
1103         if not(notif.nent_enabled)
1104         {
1105                 #ifdef NOTIFICATIONS_DEBUG
1106                 Debug_Notification(sprintf(
1107                         "Local_Notification(%s, %s): Entity was disabled...\n",
1108                         Get_Notif_TypeName(net_type),
1109                         notif.nent_name
1110                 ));
1111                 #endif
1112                 return;
1113         }
1114         
1115         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1116         {
1117                 backtrace(sprintf(
1118                         strcat(
1119                                 "Not enough arguments for Local_Notification(%s, %s, ...)! ",
1120                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1121                                 "Check the definition and function call for accuracy...?\n"
1122                         ),
1123                         Get_Notif_TypeName(net_type), notif.nent_name,
1124                         notif.nent_stringcount, notif.nent_floatcount, count
1125                 ));
1126                 return;
1127         }
1128         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1129         {
1130                 backtrace(sprintf(
1131                         strcat(
1132                                 "Too many arguments for Local_Notification(%s, %s, ...)! ",
1133                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1134                                 "Check the definition and function call for accuracy...?\n"
1135                         ),
1136                         Get_Notif_TypeName(net_type), notif.nent_name,
1137                         notif.nent_stringcount, notif.nent_floatcount, count
1138                 ));
1139                 return;
1140         }
1141
1142         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1143         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1144         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1145         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1146         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1147         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1148         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1149         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1150
1151         #ifdef NOTIFICATIONS_DEBUG
1152         Debug_Notification(sprintf(
1153                 "Local_Notification(%s, %s, %s, %s);\n",
1154                 Get_Notif_TypeName(net_type),
1155                 notif.nent_name,
1156                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1157                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1158         ));
1159         #endif
1160         
1161         switch(net_type)
1162         {
1163                 case MSG_ANNCE:
1164                 {
1165                         #ifdef CSQC
1166                         Local_Notification_sound(
1167                                 notif.nent_channel,
1168                                 notif.nent_snd,
1169                                 notif.nent_vol,
1170                                 notif.nent_position
1171                         );
1172                         #else
1173                         backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
1174                         #endif
1175                         break;
1176                 }
1177                 
1178                 case MSG_INFO:
1179                 {
1180                         print(
1181                                 Local_Notification_sprintf(
1182                                         notif.nent_string,
1183                                         notif.nent_args, 
1184                                         s1, s2, s3, s4,
1185                                         f1, f2, f3, f4)
1186                         );
1187                         #ifdef CSQC 
1188                         if(notif.nent_icon != "")
1189                         {
1190                                 Local_Notification_HUD_Notify_Push(
1191                                         notif.nent_icon,
1192                                         notif.nent_hudargs,
1193                                         s1, s2, s3, s4);
1194                         } 
1195                         #endif 
1196                         break;
1197                 }
1198                 
1199                 #ifdef CSQC
1200                 case MSG_CENTER:
1201                 {
1202                         Local_Notification_centerprint_generic(
1203                                 Local_Notification_sprintf(
1204                                         notif.nent_string,
1205                                         notif.nent_args, 
1206                                         s1, s2, s3, s4,
1207                                         f1, f2, f3, f4),
1208                                 notif.nent_durcnt,
1209                                 notif.nent_cpid,
1210                                 f1, f2);
1211                         break;
1212                 }
1213                 #endif
1214                 
1215                 case MSG_MULTI:
1216                 {
1217                         if(notif.nent_msginfo)
1218                         if(notif.nent_msginfo.nent_enabled)
1219                         {
1220                                 Local_Notification_WOVA(
1221                                         MSG_INFO,
1222                                         notif.nent_msginfo.nent_id, 
1223                                         notif.nent_msginfo.nent_stringcount, 
1224                                         notif.nent_msginfo.nent_floatcount, 
1225                                         s1, s2, s3, s4,
1226                                         f1, f2, f3, f4);
1227                         }
1228                         #ifdef CSQC
1229                         if(notif.nent_msgannce)
1230                         if(notif.nent_msgannce.nent_enabled)
1231                         {
1232                                 Local_Notification_WOVA(
1233                                         MSG_ANNCE,
1234                                         notif.nent_msgannce.nent_id, 
1235                                         0, 0, 
1236                                         "", "", "", "",
1237                                         0, 0, 0, 0);
1238                         }
1239                         if(notif.nent_msgcenter)
1240                         if(notif.nent_msgcenter.nent_enabled)
1241                         {
1242                                 Local_Notification_WOVA(
1243                                         MSG_CENTER,
1244                                         notif.nent_msgcenter.nent_id, 
1245                                         notif.nent_msgcenter.nent_stringcount, 
1246                                         notif.nent_msgcenter.nent_floatcount, 
1247                                         s1, s2, s3, s4,
1248                                         f1, f2, f3, f4); 
1249                         }
1250                         #endif
1251                         break;
1252                 }
1253         }
1254 }
1255
1256 // WOVA = Without Variable Arguments 
1257 void Local_Notification_WOVA(
1258         float net_type, float net_name,
1259         float stringcount, float floatcount,
1260         string s1, string s2, string s3, string s4,
1261         float f1, float f2, float f3, float f4)
1262 {
1263         #define VARITEM(stringc,floatc,args) \
1264                 if((stringcount == stringc) && (floatcount == floatc)) \
1265                         { Local_Notification(net_type, net_name, args); return; }
1266         EIGHT_VARS_TO_VARARGS_VARLIST
1267         #undef VARITEM
1268         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1269 }
1270
1271
1272 // =========================
1273 //  Notification Networking
1274 // =========================
1275
1276 #ifdef CSQC
1277 void Read_Notification(float is_new)
1278 {
1279         float net_type = ReadByte();
1280         float net_name = ReadShort();
1281
1282         entity notif;
1283
1284         if(net_type == MSG_CENTER_CPID)
1285         {
1286                 #ifdef NOTIFICATIONS_DEBUG
1287                 Debug_Notification(sprintf(
1288                         "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n",
1289                         is_new,
1290                         time,
1291                         Get_Notif_TypeName(net_type),
1292                         net_name
1293                 ));
1294                 #endif
1295                 
1296                 if(is_new)
1297                 {
1298                         if(net_name == 0) { reset_centerprint_messages(); }
1299                         else if(net_name != NO_CPID)
1300                         {
1301                                 // in this case, net_name IS the cpid we want to kill
1302                                 centerprint_generic(net_name, "", 0, 0);
1303                         }
1304                         else
1305                         {
1306                                 backtrace(sprintf(
1307                                         "Read_Notification(%d) at %f: ^1TRIED TO KILL NO_CPID CENTERPRINT!\n",
1308                                         is_new,
1309                                         time
1310                                 ));
1311                         } 
1312                 }
1313         }
1314         else
1315         {
1316                 notif = Get_Notif_Ent(net_type, net_name);
1317                 if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
1318
1319                 #ifdef NOTIFICATIONS_DEBUG
1320                 Debug_Notification(sprintf(
1321                         "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
1322                         is_new,
1323                         time,
1324                         Get_Notif_TypeName(net_type),
1325                         notif.nent_name
1326                 ));
1327                 #endif
1328
1329                 string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
1330                 string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
1331                 string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
1332                 string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
1333                 float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
1334                 float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
1335                 float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
1336                 float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
1337         
1338                 if(is_new)
1339                 {
1340                         Local_Notification_WOVA(
1341                                 net_type, net_name,
1342                                 notif.nent_stringcount,
1343                                 notif.nent_floatcount,
1344                                 s1, s2, s3, s4,
1345                                 f1, f2, f3, f4);
1346                 }
1347         }
1348 }
1349 #endif
1350
1351 #ifdef SVQC
1352 void Net_Notification_Remove()
1353 {
1354         if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
1355         
1356         #ifdef NOTIFICATIONS_DEBUG
1357         Debug_Notification(sprintf(
1358                 "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
1359                 time,
1360                 ((self.nent_net_name == -1) ? "Killed" : "Removed"),
1361                 Get_Notif_TypeName(self.nent_net_type),
1362                 self.owner.nent_name
1363         ));
1364         #endif
1365         
1366         float i;
1367         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
1368         remove(self);
1369 }
1370
1371 float Net_Write_Notification(entity client, float sf)
1372 {
1373         float i, send = FALSE;
1374         
1375         switch(self.nent_broadcast)
1376         {
1377                 case NOTIF_ONE: // send to one client and their spectator
1378                 {
1379                         if(
1380                                 (client == self.nent_client)
1381                                 ||
1382                                 (
1383                                         IS_SPEC(client)
1384                                         &&
1385                                         (client.enemy == self.nent_client)
1386                                 )
1387                         ) { send = TRUE; }
1388                         break;
1389                 }
1390                 case NOTIF_ONE_ONLY: // send ONLY to one client
1391                 {
1392                         if(client == self.nent_client) { send = TRUE; }
1393                         break;
1394                 }
1395                 case NOTIF_TEAM: // send only to X team and their spectators
1396                 {
1397                         if(
1398                                 (client.team == self.nent_client.team)
1399                                 ||
1400                                 (
1401                                         IS_SPEC(client)
1402                                         &&
1403                                         (client.enemy.team == self.nent_client.team)
1404                                 )
1405                         ) { send = TRUE; }
1406                         break;
1407                 }
1408                 case NOTIF_TEAM_EXCEPT: // send only to X team and their spectators, except for Y person and their spectators
1409                 {
1410                         if(
1411                                 (client != self.nent_client)
1412                                 &&
1413                                 (
1414                                         (client.team == self.nent_client.team)
1415                                         ||
1416                                         (
1417                                                 IS_SPEC(client)
1418                                                 &&
1419                                                 (
1420                                                         (client.enemy != self.nent_client)
1421                                                         &&
1422                                                         (client.enemy.team == self.nent_client.team)
1423                                                 )
1424                                         )
1425                                 )
1426                         ) { send = TRUE; }
1427                         break;
1428                 }
1429                 case NOTIF_ALL: // send to everyone
1430                 {
1431                         send = TRUE;
1432                         break;
1433                 }
1434                 case NOTIF_ALL_EXCEPT: // send to everyone except X person and their spectators
1435                 {
1436                         if(
1437                                 (client != self.nent_client)
1438                                 &&
1439                                 !(
1440                                         IS_SPEC(client)
1441                                         &&
1442                                         (client.enemy == self.nent_client)
1443                                 )
1444                         ) { send = TRUE; }
1445                         break;
1446                 }
1447                 default: { send = FALSE; break; }
1448         }
1449
1450         if(send)
1451         {               
1452                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1453                 WriteByte(MSG_ENTITY, self.nent_net_type);
1454                 WriteShort(MSG_ENTITY, self.nent_net_name);
1455                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
1456                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
1457         }
1458
1459         return send; 
1460 }
1461
1462 void Kill_Notification(
1463         float broadcast, entity client,
1464         float net_type, float net_name)
1465 {
1466         string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
1467         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
1468
1469         #ifdef NOTIFICATIONS_DEBUG
1470         Debug_Notification(sprintf(
1471                 "Kill_Notification(%d, '%s', %s, %d);\n",
1472                 broadcast,
1473                 client.netname,
1474                 (net_type ? Get_Notif_TypeName(net_type) : "0"),
1475                 net_name
1476         ));
1477         #endif
1478
1479         entity notif, net_notif;
1480         float killed_cpid = NO_CPID;
1481         
1482         switch(net_type)
1483         {
1484                 case 0:
1485                 {
1486                         killed_cpid = 0; // kill ALL centerprints
1487                         break;
1488                 }
1489                 
1490                 case MSG_CENTER:
1491                 {
1492                         if(net_name)
1493                         {
1494                                 entity notif = Get_Notif_Ent(net_type, net_name);
1495                                 if not(notif) { backtrace("Kill_Notification: Could not find notification entity!\n"); return; }
1496                                 
1497                                 if(notif.nent_cpid)
1498                                         killed_cpid = notif.nent_cpid;
1499                                 else
1500                                         killed_cpid = NO_CPID;
1501                         }
1502                         else
1503                         {
1504                                 killed_cpid = 0; // kill ALL centerprints
1505                         }
1506                         break;
1507                 }
1508
1509                 case MSG_CENTER_CPID:
1510                 {
1511                         killed_cpid = net_name;
1512                         break;
1513                 }
1514         }
1515
1516         if(killed_cpid != NO_CPID)
1517         {
1518                 net_notif = spawn();
1519                 net_notif.classname = "net_kill_notification";
1520                 net_notif.nent_broadcast = broadcast;
1521                 net_notif.nent_client = client;
1522                 net_notif.nent_net_type = MSG_CENTER_CPID;
1523                 net_notif.nent_net_name = killed_cpid;
1524                 Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
1525         }
1526
1527         for(notif = world; (notif = find(notif, classname, "net_notification"));)
1528         {
1529                 if(net_type)
1530                 {
1531                         if((killed_cpid != NO_CPID) && (notif.nent_net_type == MSG_CENTER))
1532                         {
1533                                 if(notif.owner.nent_cpid == killed_cpid)
1534                                 {
1535                                         notif.nent_net_name = -1;
1536                                         notif.nextthink = time;
1537                                 }
1538                                 else { continue; } // we ARE looking for a specific CPID, don't kill everything else too
1539                         }
1540                         else if(notif.nent_net_type == net_type)
1541                         {
1542                                 if(net_name)
1543                                 {
1544                                         if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.nextthink = time; }
1545                                         else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
1546                                 }
1547                                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1548                         }
1549                         else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
1550                 }
1551                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1552         }
1553 }
1554
1555 void Send_Notification(
1556         float broadcast, entity client,
1557         float net_type, float net_name,
1558         ...count)
1559 {
1560         // check supplied broadcast, target, type, and name for errors
1561         string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
1562         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
1563
1564         // retreive counts for the arguments of this notification
1565         entity notif = Get_Notif_Ent(net_type, net_name);
1566         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
1567
1568         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1569         {
1570                 backtrace(sprintf(
1571                         strcat(
1572                                 "Not enough arguments for Send_Notification(%d, %s, %s, ...)! ",
1573                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1574                                 "Check the definition and function call for accuracy...?\n"
1575                         ),
1576                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1577                         notif.nent_stringcount, notif.nent_floatcount, count
1578                 ));
1579                 return;
1580         }
1581         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1582         {
1583                 backtrace(sprintf(
1584                         strcat(
1585                                 "Too many arguments for Send_Notification(%d, %s, %s, ...)! ",
1586                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1587                                 "Check the definition and function call for accuracy...?\n"
1588                         ),
1589                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1590                         notif.nent_stringcount, notif.nent_floatcount, count
1591                 ));
1592                 return;
1593         }
1594
1595         #ifdef NOTIFICATIONS_DEBUG
1596         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1597         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1598         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1599         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1600         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1601         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1602         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1603         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1604         Debug_Notification(sprintf(
1605                 "Send_Notification(%d, %s, %s, %s, %s);\n",
1606                 broadcast,
1607                 Get_Notif_TypeName(net_type),
1608                 notif.nent_name,
1609                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1610                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1611         ));
1612         #endif
1613
1614         entity net_notif = spawn();
1615         net_notif.owner = notif;
1616         net_notif.classname = "net_notification";
1617         net_notif.nent_broadcast = broadcast;
1618         net_notif.nent_client = client;
1619         net_notif.nent_net_type = net_type;
1620         net_notif.nent_net_name = net_name;
1621         net_notif.nent_stringcount = notif.nent_stringcount;
1622         net_notif.nent_floatcount = notif.nent_floatcount;
1623         
1624         float i;
1625         for(i = 0; i < net_notif.nent_stringcount; ++i)
1626                 { net_notif.nent_strings[i] = strzone(...(i, string)); }
1627         for(i = 0; i < net_notif.nent_floatcount; ++i)
1628                 { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
1629
1630         net_notif.think = Net_Notification_Remove;
1631         net_notif.nextthink =
1632                 ((time > autocvar_notification_lifetime_mapload)
1633                 ?
1634                         (time + autocvar_notification_lifetime_runtime)
1635                         :
1636                         autocvar_notification_lifetime_mapload
1637                 ); 
1638
1639         Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
1640
1641         if(
1642                 server_is_dedicated
1643                 &&
1644                 (
1645                         broadcast == NOTIF_ALL
1646                         ||
1647                         broadcast == NOTIF_ALL_EXCEPT
1648                 )
1649                 &&
1650                 !(
1651                         net_type == MSG_ANNCE
1652                         ||
1653                         net_type == MSG_CENTER
1654                 )
1655         )
1656         {
1657                 Local_Notification_WOVA(
1658                         net_type, net_name,
1659                         notif.nent_stringcount,
1660                         notif.nent_floatcount,
1661                         IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3),
1662                         IFFL(0), IFFL(1), IFFL(2), IFFL(3));
1663         }
1664 }
1665
1666 // WOVA = Without Variable Arguments 
1667 void Send_Notification_WOVA(
1668         float broadcast, entity client,
1669         float net_type, float net_name,
1670         string s1, string s2, string s3, string s4,
1671         float f1, float f2, float f3, float f4)
1672 {
1673         entity notif = Get_Notif_Ent(net_type, net_name);
1674         
1675         #ifdef NOTIFICATIONS_DEBUG
1676         Debug_Notification(sprintf(
1677                 "Send_Notification_WOVA(%d, %s, %s, %s, %s);\n",
1678                 broadcast,
1679                 Get_Notif_TypeName(net_type),
1680                 notif.nent_name,
1681                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1682                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1683         ));
1684         #endif
1685         
1686         #define VARITEM(stringc,floatc,args) \
1687                 if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1688                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1689         EIGHT_VARS_TO_VARARGS_VARLIST
1690         #undef VARITEM
1691         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1692 }
1693 #endif // ifdef SVQC