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