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