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