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