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