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