]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
58dcdc82e641c9042517f0c132cecb72c79c9ad1
[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                 "item_centerprinttime", "1.5",
1049                 "How long to show item information centerprint messages (like 'You got the Electro' or such)"
1050         );
1051         
1052         NOTIF_WRITE_HARDCODED(
1053                 "lifetime_mapload", "10",
1054                 "Amount of time that notification entities last immediately at mapload (in seconds) "
1055                 "to help prevent notifications from being lost on early init (like gamestart countdown)"
1056         );
1057         
1058         NOTIF_WRITE_HARDCODED(
1059                 "lifetime_runtime", "0.5",
1060                 "Amount of time that notification entities last on the server during runtime (In seconds)"
1061         );
1062         
1063         NOTIF_WRITE_HARDCODED(
1064                 "server_allows_location", "1",
1065                 "Server side cvar for allowing death messages to show location information too"
1066         );
1067         
1068         NOTIF_WRITE_HARDCODED(
1069                 "show_location", "0",
1070                 "Append location information to MSG_INFO death/kill messages"
1071         );
1072         
1073         NOTIF_WRITE_HARDCODED(
1074                 "show_location_string", "",
1075                 "Replacement string piped into sprintf, "
1076                 "so you can do different messages like this: ' at the %s' or ' (near %s)'"
1077         );
1078         
1079         NOTIF_WRITE_HARDCODED(
1080                 "show_sprees", "1",
1081                 "Print information about sprees in death/kill messages"
1082         );
1083         
1084         NOTIF_WRITE_HARDCODED(
1085                 "show_sprees_center", "1",
1086                 "Show spree information in MSG_CENTER messages... "
1087                 "0 = off, 1 = target (but only for first victim) and attacker"
1088         );
1089         
1090         NOTIF_WRITE_HARDCODED(
1091                 "show_sprees_center_specialonly", "1",
1092                 "Don't show spree information in MSG_CENTER messages if it isn't an achievement"
1093         );
1094         
1095         NOTIF_WRITE_HARDCODED(
1096                 "show_sprees_info", "3",
1097                 "Show spree information in MSG_INFO messages... "
1098                 "0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker"
1099         );
1100         
1101         NOTIF_WRITE_HARDCODED(
1102                 "show_sprees_info_newline", "1",
1103                 "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
1104         );
1105         
1106         NOTIF_WRITE_HARDCODED(
1107                 "show_sprees_info_specialonly", "1",
1108                 "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
1109         );
1110
1111         NOTIF_WRITE(sprintf(
1112                 strcat(
1113                         "\n// Notification counts (total = %d): ",
1114                         "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d\n"
1115                 ),
1116                 (
1117                         NOTIF_ANNCE_COUNT +
1118                         NOTIF_INFO_COUNT +
1119                         NOTIF_CENTER_COUNT +
1120                         NOTIF_MULTI_COUNT +
1121                         NOTIF_CHOICE_COUNT
1122                 ),
1123                 NOTIF_ANNCE_COUNT,
1124                 NOTIF_INFO_COUNT,
1125                 NOTIF_CENTER_COUNT,
1126                 NOTIF_MULTI_COUNT,
1127                 NOTIF_CHOICE_COUNT
1128         ));
1129         
1130         return;
1131         #undef NOTIF_WRITE_HARDCODED
1132         #undef NOTIF_WRITE_ENTITY
1133         #undef NOTIF_WRITE
1134 }
1135
1136
1137 // ===============================
1138 //  Frontend Notification Pushing
1139 // ===============================
1140
1141 #ifdef NOTIFICATIONS_DEBUG
1142 void Debug_Notification(string input)
1143 {
1144         switch(autocvar_notification_debug)
1145         {
1146                 case 1: { dprint(input); break; }
1147                 case 2: { print(input); break; }
1148         }
1149 }
1150 #endif
1151
1152 string Local_Notification_sprintf(
1153         string input, string args, 
1154         string s1, string s2, string s3, string s4,
1155         float f1, float f2, float f3, float f4)
1156 {
1157         #ifdef NOTIFICATIONS_DEBUG
1158         Debug_Notification(sprintf(
1159                 "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
1160                 MakeConsoleSafe(input),
1161                 args,
1162                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1163                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1164         ));
1165         #endif
1166         
1167         string selected;
1168         float sel_num;
1169         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
1170
1171         string tmp_s;
1172
1173         for(sel_num = 0;(args != "");)
1174         {
1175                 selected = car(args); args = cdr(args);
1176                 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
1177                 switch(strtolower(selected))
1178                 {
1179                         #define ARG_CASE(prog,selected,result) \
1180                                 #ifdef CSQC \
1181                                         #if (prog != ARG_SV) && (prog != ARG_DC) \
1182                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1183                                         #endif \
1184                                 #else \
1185                                         #if (prog != ARG_CS) && (prog != ARG_DC) \
1186                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1187                                         #endif \
1188                                 #endif
1189                         NOTIF_ARGUMENT_LIST
1190                         #undef ARG_CASE
1191                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
1192                 }
1193         }
1194         return sprintf(
1195                 strcat(input, "\n"),
1196                 arg_slot[0],
1197                 arg_slot[1],
1198                 arg_slot[2],
1199                 arg_slot[3],
1200                 arg_slot[4],
1201                 arg_slot[5],
1202                 arg_slot[6]
1203         );
1204 }
1205
1206 #ifdef CSQC
1207 void Local_Notification_sound(
1208         float soundchannel, string soundfile,
1209         float soundvolume, float soundposition)
1210 {
1211         if((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam)))
1212         {
1213                 #ifdef NOTIFICATIONS_DEBUG
1214                 Debug_Notification(sprintf(
1215                         "Local_Notification_sound(world, %f, '%s', %f, %f);\n",
1216                         soundchannel,
1217                         sprintf(
1218                                 "announcer/%s/%s.wav",
1219                                 autocvar_cl_announcer,
1220                                 soundfile
1221                         ),
1222                         soundvolume,
1223                         soundposition
1224                 ));
1225                 #endif
1226                 
1227                 sound(
1228                         world,
1229                         soundchannel,
1230                         sprintf(
1231                                 "announcer/%s/%s.wav",
1232                                 autocvar_cl_announcer,
1233                                 soundfile
1234                         ),
1235                         soundvolume,
1236                         soundposition
1237                 );
1238                 
1239                 if(prev_soundfile) { strunzone(prev_soundfile); }
1240                 prev_soundfile = strzone(soundfile);
1241                 prev_soundtime = time;
1242         }
1243         else
1244         {
1245                 #ifdef NOTIFICATIONS_DEBUG
1246                 Debug_Notification(sprintf(
1247                         strcat(
1248                                 "Local_Notification_sound(world, %f, '%s', %f, %f) ",
1249                                 "^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', timediff: %f, limit: %f\n"
1250                          ),
1251                         soundchannel,
1252                         sprintf(
1253                                 "announcer/%s/%s.wav",
1254                                 autocvar_cl_announcer,
1255                                 soundfile
1256                         ),
1257                         soundvolume,
1258                         soundposition,
1259                         prev_soundfile,
1260                         (time - prev_soundtime),
1261                         autocvar_cl_announcer_antispam
1262                 ));
1263                 #endif
1264         }
1265 }
1266
1267 void Local_Notification_HUD_Notify_Push(
1268         string icon, string hudargs,
1269         string s1, string s2, string s3, string s4)
1270 {
1271         string selected;
1272         float sel_num;
1273         arg_slot[0] = ""; arg_slot[1] = "";
1274
1275         for(sel_num = 0;(hudargs != "");)
1276         {
1277                 selected = car(hudargs); hudargs = cdr(hudargs);
1278                 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1279                 switch(strtolower(selected))
1280                 {
1281                         #define ARG_CASE(prog,selected,result) \
1282                                 #if (prog == ARG_CS_SV_HA) \
1283                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1284                                 #endif
1285                         NOTIF_ARGUMENT_LIST
1286                         #undef ARG_CASE
1287                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1288                 }
1289         }
1290         #ifdef NOTIFICATIONS_DEBUG
1291         Debug_Notification(sprintf(
1292                 "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
1293                 icon,
1294                 hudargs,
1295                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1296                 MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
1297         ));
1298         #endif
1299         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
1300 }
1301
1302 void Local_Notification_centerprint_generic(
1303         string input, string durcnt,
1304         float cpid, float f1, float f2)
1305 {
1306         string selected;
1307         float sel_num;
1308         arg_slot[0] = ""; arg_slot[1] = "";
1309
1310         for(sel_num = 0;(durcnt != "");)
1311         {
1312                 selected = car(durcnt); durcnt = cdr(durcnt);
1313                 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
1314                 switch(strtolower(selected))
1315                 {
1316                         #define ARG_CASE(prog,selected,result) \
1317                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
1318                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1319                                 #endif
1320                         NOTIF_ARGUMENT_LIST
1321                         #undef ARG_CASE
1322                         default:
1323                         {
1324                                 if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
1325                                 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
1326                                 break;
1327                         }
1328                 }
1329         }
1330         #ifdef NOTIFICATIONS_DEBUG
1331         Debug_Notification(sprintf(
1332                 "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
1333                 MakeConsoleSafe(input),
1334                 durcnt,
1335                 f1, f2,
1336                 stof(arg_slot[0]), stof(arg_slot[1])
1337         ));
1338         #endif
1339         centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
1340 }
1341 #endif
1342
1343 void Local_Notification(float net_type, float net_name, ...count)
1344 {
1345         // check supplied type and name for errors
1346         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
1347         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
1348
1349         entity notif = Get_Notif_Ent(net_type, net_name);
1350         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
1351         if not(notif.nent_enabled)
1352         {
1353                 #ifdef NOTIFICATIONS_DEBUG
1354                 Debug_Notification(sprintf(
1355                         "Local_Notification(%s, %s): Entity was disabled...\n",
1356                         Get_Notif_TypeName(net_type),
1357                         notif.nent_name
1358                 ));
1359                 #endif
1360                 return;
1361         }
1362         
1363         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1364         {
1365                 backtrace(sprintf(
1366                         strcat(
1367                                 "Not enough arguments for Local_Notification(%s, %s, ...)! ",
1368                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1369                                 "Check the definition and function call for accuracy...?\n"
1370                         ),
1371                         Get_Notif_TypeName(net_type), notif.nent_name,
1372                         notif.nent_stringcount, notif.nent_floatcount, count
1373                 ));
1374                 return;
1375         }
1376         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1377         {
1378                 backtrace(sprintf(
1379                         strcat(
1380                                 "Too many arguments for Local_Notification(%s, %s, ...)! ",
1381                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1382                                 "Check the definition and function call for accuracy...?\n"
1383                         ),
1384                         Get_Notif_TypeName(net_type), notif.nent_name,
1385                         notif.nent_stringcount, notif.nent_floatcount, count
1386                 ));
1387                 return;
1388         }
1389
1390         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1391         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1392         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1393         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1394         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1395         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1396         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1397         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1398
1399         #ifdef NOTIFICATIONS_DEBUG
1400         Debug_Notification(sprintf(
1401                 "Local_Notification(%s, %s, %s, %s);\n",
1402                 Get_Notif_TypeName(net_type),
1403                 notif.nent_name,
1404                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1405                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1406         ));
1407         #endif
1408         
1409         switch(net_type)
1410         {
1411                 case MSG_ANNCE:
1412                 {
1413                         #ifdef CSQC
1414                         Local_Notification_sound(
1415                                 notif.nent_channel,
1416                                 notif.nent_snd,
1417                                 notif.nent_vol,
1418                                 notif.nent_position
1419                         );
1420                         #else
1421                         backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
1422                         #endif
1423                         break;
1424                 }
1425                 
1426                 case MSG_INFO:
1427                 {
1428                         print(
1429                                 Local_Notification_sprintf(
1430                                         notif.nent_string,
1431                                         notif.nent_args, 
1432                                         s1, s2, s3, s4,
1433                                         f1, f2, f3, f4)
1434                         );
1435                         #ifdef CSQC 
1436                         if(notif.nent_icon != "")
1437                         {
1438                                 Local_Notification_HUD_Notify_Push(
1439                                         notif.nent_icon,
1440                                         notif.nent_hudargs,
1441                                         s1, s2, s3, s4);
1442                         } 
1443                         #endif 
1444                         break;
1445                 }
1446                 
1447                 #ifdef CSQC
1448                 case MSG_CENTER:
1449                 {
1450                         Local_Notification_centerprint_generic(
1451                                 Local_Notification_sprintf(
1452                                         notif.nent_string,
1453                                         notif.nent_args, 
1454                                         s1, s2, s3, s4,
1455                                         f1, f2, f3, f4),
1456                                 notif.nent_durcnt,
1457                                 notif.nent_cpid,
1458                                 f1, f2);
1459                         break;
1460                 }
1461                 #endif
1462                 
1463                 case MSG_MULTI:
1464                 {
1465                         if(notif.nent_msginfo)
1466                         if(notif.nent_msginfo.nent_enabled)
1467                         {
1468                                 Local_Notification_WOVA(
1469                                         MSG_INFO,
1470                                         notif.nent_msginfo.nent_id, 
1471                                         notif.nent_msginfo.nent_stringcount, 
1472                                         notif.nent_msginfo.nent_floatcount, 
1473                                         s1, s2, s3, s4,
1474                                         f1, f2, f3, f4);
1475                         }
1476                         #ifdef CSQC
1477                         if(notif.nent_msgannce)
1478                         if(notif.nent_msgannce.nent_enabled)
1479                         {
1480                                 Local_Notification_WOVA(
1481                                         MSG_ANNCE,
1482                                         notif.nent_msgannce.nent_id, 
1483                                         0, 0, 
1484                                         "", "", "", "",
1485                                         0, 0, 0, 0);
1486                         }
1487                         if(notif.nent_msgcenter)
1488                         if(notif.nent_msgcenter.nent_enabled)
1489                         {
1490                                 Local_Notification_WOVA(
1491                                         MSG_CENTER,
1492                                         notif.nent_msgcenter.nent_id, 
1493                                         notif.nent_msgcenter.nent_stringcount, 
1494                                         notif.nent_msgcenter.nent_floatcount, 
1495                                         s1, s2, s3, s4,
1496                                         f1, f2, f3, f4); 
1497                         }
1498                         #endif
1499                         break;
1500                 }
1501
1502                 case MSG_CHOICE:
1503                 {
1504                         entity found_choice;
1505                         
1506                         if(notif.nent_challow_var && (warmup_stage || (notif.nent_challow_var == 2)))
1507                         {
1508                                 switch(cvar_string(sprintf("notification_%s", notif.nent_name)))
1509                                 {
1510                                         case 1: found_choice = notif.nent_optiona; break;
1511                                         case 2: found_choice = notif.nent_optionb; break;
1512                                         default: return; // not enabled anyway
1513                                 }
1514                         }
1515                         else { found_choice = notif.nent_optiona; }
1516                         
1517                         Local_Notification_WOVA(
1518                                 found_choice.nent_type,
1519                                 found_choice.nent_id, 
1520                                 found_choice.nent_stringcount, 
1521                                 found_choice.nent_floatcount, 
1522                                 s1, s2, s3, s4,
1523                                 f1, f2, f3, f4); 
1524                 }
1525         }
1526 }
1527
1528 // WOVA = Without Variable Arguments 
1529 void Local_Notification_WOVA(
1530         float net_type, float net_name,
1531         float stringcount, float floatcount,
1532         string s1, string s2, string s3, string s4,
1533         float f1, float f2, float f3, float f4)
1534 {
1535         #define VARITEM(stringc,floatc,args) \
1536                 if((stringcount == stringc) && (floatcount == floatc)) \
1537                         { Local_Notification(net_type, net_name, args); return; }
1538         EIGHT_VARS_TO_VARARGS_VARLIST
1539         #undef VARITEM
1540         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1541 }
1542
1543
1544 // =========================
1545 //  Notification Networking
1546 // =========================
1547
1548 #ifdef CSQC
1549 void Read_Notification(float is_new)
1550 {
1551         float net_type = ReadByte();
1552         float net_name = ReadShort();
1553
1554         entity notif;
1555
1556         if(net_type == MSG_CENTER_CPID)
1557         {
1558                 #ifdef NOTIFICATIONS_DEBUG
1559                 Debug_Notification(sprintf(
1560                         "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n",
1561                         is_new,
1562                         time,
1563                         Get_Notif_TypeName(net_type),
1564                         net_name
1565                 ));
1566                 #endif
1567                 
1568                 if(is_new)
1569                 {
1570                         if(net_name == 0) { reset_centerprint_messages(); }
1571                         else if(net_name != NO_CPID)
1572                         {
1573                                 // in this case, net_name IS the cpid we want to kill
1574                                 centerprint_generic(net_name, "", 0, 0);
1575                         }
1576                         else
1577                         {
1578                                 backtrace(sprintf(
1579                                         "Read_Notification(%d) at %f: ^1TRIED TO KILL NO_CPID CENTERPRINT!\n",
1580                                         is_new,
1581                                         time
1582                                 ));
1583                         } 
1584                 }
1585         }
1586         else
1587         {
1588                 notif = Get_Notif_Ent(net_type, net_name);
1589                 if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
1590
1591                 #ifdef NOTIFICATIONS_DEBUG
1592                 Debug_Notification(sprintf(
1593                         "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
1594                         is_new,
1595                         time,
1596                         Get_Notif_TypeName(net_type),
1597                         notif.nent_name
1598                 ));
1599                 #endif
1600
1601                 string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
1602                 string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
1603                 string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
1604                 string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
1605                 float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
1606                 float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
1607                 float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
1608                 float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
1609         
1610                 if(is_new)
1611                 {
1612                         Local_Notification_WOVA(
1613                                 net_type, net_name,
1614                                 notif.nent_stringcount,
1615                                 notif.nent_floatcount,
1616                                 s1, s2, s3, s4,
1617                                 f1, f2, f3, f4);
1618                 }
1619         }
1620 }
1621 #endif
1622
1623 #ifdef SVQC
1624 void Net_Notification_Remove()
1625 {
1626         if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
1627         
1628         #ifdef NOTIFICATIONS_DEBUG
1629         Debug_Notification(sprintf(
1630                 "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
1631                 time,
1632                 ((self.nent_net_name == -1) ? "Killed" : "Removed"),
1633                 Get_Notif_TypeName(self.nent_net_type),
1634                 self.owner.nent_name
1635         ));
1636         #endif
1637         
1638         float i;
1639         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
1640         remove(self);
1641 }
1642
1643 float Net_Write_Notification(entity client, float sf)
1644 {
1645         if(Notification_ShouldSend(self.nent_broadcast, client, self.nent_client))
1646         {
1647                 float i;
1648                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1649                 WriteByte(MSG_ENTITY, self.nent_net_type);
1650                 WriteShort(MSG_ENTITY, self.nent_net_name);
1651                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
1652                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
1653                 return TRUE;
1654         }
1655         else { return FALSE; }
1656 }
1657
1658 void Kill_Notification(
1659         float broadcast, entity client,
1660         float net_type, float net_name)
1661 {
1662         string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
1663         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
1664
1665         #ifdef NOTIFICATIONS_DEBUG
1666         Debug_Notification(sprintf(
1667                 "Kill_Notification(%s, '%s', %s, %d);\n",
1668                 Get_Notif_BroadcastName(broadcast),
1669                 client.netname,
1670                 (net_type ? Get_Notif_TypeName(net_type) : "0"),
1671                 net_name
1672         ));
1673         #endif
1674
1675         entity notif, net_notif;
1676         float killed_cpid = NO_CPID;
1677         
1678         switch(net_type)
1679         {
1680                 case 0:
1681                 {
1682                         killed_cpid = 0; // kill ALL centerprints
1683                         break;
1684                 }
1685                 
1686                 case MSG_CENTER:
1687                 {
1688                         if(net_name)
1689                         {
1690                                 entity notif = Get_Notif_Ent(net_type, net_name);
1691                                 if not(notif) { backtrace("Kill_Notification: Could not find notification entity!\n"); return; }
1692                                 
1693                                 if(notif.nent_cpid)
1694                                         killed_cpid = notif.nent_cpid;
1695                                 else
1696                                         killed_cpid = NO_CPID;
1697                         }
1698                         else
1699                         {
1700                                 killed_cpid = 0; // kill ALL centerprints
1701                         }
1702                         break;
1703                 }
1704
1705                 case MSG_CENTER_CPID:
1706                 {
1707                         killed_cpid = net_name;
1708                         break;
1709                 }
1710         }
1711
1712         if(killed_cpid != NO_CPID)
1713         {
1714                 net_notif = spawn();
1715                 net_notif.classname = "net_kill_notification";
1716                 net_notif.nent_broadcast = broadcast;
1717                 net_notif.nent_client = client;
1718                 net_notif.nent_net_type = MSG_CENTER_CPID;
1719                 net_notif.nent_net_name = killed_cpid;
1720                 Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
1721         }
1722
1723         for(notif = world; (notif = find(notif, classname, "net_notification"));)
1724         {
1725                 if(net_type)
1726                 {
1727                         if((killed_cpid != NO_CPID) && (notif.nent_net_type == MSG_CENTER))
1728                         {
1729                                 if(notif.owner.nent_cpid == killed_cpid)
1730                                 {
1731                                         notif.nent_net_name = -1;
1732                                         notif.nextthink = time;
1733                                 }
1734                                 else { continue; } // we ARE looking for a specific CPID, don't kill everything else too
1735                         }
1736                         else if(notif.nent_net_type == net_type)
1737                         {
1738                                 if(net_name)
1739                                 {
1740                                         if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.nextthink = time; }
1741                                         else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
1742                                 }
1743                                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1744                         }
1745                         else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
1746                 }
1747                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1748         }
1749 }
1750
1751 void Send_Notification(
1752         float broadcast, entity client,
1753         float net_type, float net_name,
1754         ...count)
1755 {
1756         // check supplied broadcast, target, type, and name for errors
1757         string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
1758         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
1759
1760         // retreive counts for the arguments of this notification
1761         entity notif = Get_Notif_Ent(net_type, net_name);
1762         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
1763
1764         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1765         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1766         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1767         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1768         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1769         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1770         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1771         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1772
1773         #ifdef NOTIFICATIONS_DEBUG
1774         Debug_Notification(sprintf(
1775                 "Send_Notification(%s, %s, %s, %s, %s);\n",
1776                 Get_Notif_BroadcastName(broadcast),
1777                 Get_Notif_TypeName(net_type),
1778                 notif.nent_name,
1779                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1780                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1781         ));
1782         #endif
1783
1784         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1785         {
1786                 backtrace(sprintf(
1787                         strcat(
1788                                 "Not enough arguments for Send_Notification(%s, %s, %s, ...)! ",
1789                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1790                                 "Check the definition and function call for accuracy...?\n"
1791                         ),
1792                         Get_Notif_BroadcastName(broadcast),
1793                         Get_Notif_TypeName(net_type), notif.nent_name,
1794                         notif.nent_stringcount, notif.nent_floatcount, count
1795                 ));
1796                 return;
1797         }
1798         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1799         {
1800                 backtrace(sprintf(
1801                         strcat(
1802                                 "Too many arguments for Send_Notification(%s, %s, %s, ...)! ",
1803                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1804                                 "Check the definition and function call for accuracy...?\n"
1805                         ),
1806                         Get_Notif_BroadcastName(broadcast),
1807                         Get_Notif_TypeName(net_type), notif.nent_name,
1808                         notif.nent_stringcount, notif.nent_floatcount, count
1809                 ));
1810                 return;
1811         }
1812
1813         if(
1814                 server_is_dedicated
1815                 &&
1816                 (
1817                         broadcast == NOTIF_ALL
1818                         ||
1819                         broadcast == NOTIF_ALL_EXCEPT
1820                 )
1821                 &&
1822                 !(
1823                         net_type == MSG_ANNCE
1824                         ||
1825                         net_type == MSG_CENTER
1826                 )
1827         )
1828         {
1829                 Local_Notification_WOVA(
1830                         net_type, net_name,
1831                         notif.nent_stringcount,
1832                         notif.nent_floatcount,
1833                         s1, s2, s3, s4,
1834                         f1, f2, f3, f4);
1835         }
1836
1837         if(net_type == MSG_CHOICE)
1838         {
1839                 // THIS GETS TRICKY... now we have to cycle through each possible player (checking broadcast)
1840                 // and then do an individual NOTIF_ONE_ONLY recursive call for each one depending on their option...
1841                 // It's slow, but it's better than the alternatives:
1842                 //   1. Constantly networking all info and letting client decide
1843                 //   2. Manually handling each separate call on per-usage basis (See old CTF usage of verbose)
1844                 entity found_choice; 
1845
1846                 #define RECURSE_FROM_CHOICE(ent,action) \
1847                         if(notif.nent_challow_var && (warmup_stage || (notif.nent_challow_var == 2))) \
1848                         { \
1849                                 switch(ent.msg_choice_choices[net_name]) \
1850                                 { \
1851                                         case 1: found_choice = notif.nent_optiona; break; \
1852                                         case 2: found_choice = notif.nent_optionb; break; \
1853                                         default: action; \
1854                                 } \
1855                         } \
1856                         else { found_choice = notif.nent_optiona; } \
1857                         Send_Notification_WOVA( \
1858                                 NOTIF_ONE_ONLY, \
1859                                 ent, \
1860                                 found_choice.nent_type, \
1861                                 found_choice.nent_id, \
1862                                 found_choice.nent_stringcount, \
1863                                 found_choice.nent_floatcount, \
1864                                 s1, s2, s3, s4, \
1865                                 f1, f2, f3, f4);
1866
1867                 switch(broadcast)
1868                 {
1869                         case NOTIF_ONE_ONLY: // we can potentially save processing power with this broadcast method
1870                         {
1871                                 if(IS_REAL_CLIENT(client))
1872                                         { RECURSE_FROM_CHOICE(client, return) }
1873                                 break;
1874                         }
1875                         default:
1876                         {
1877                                 entity to;
1878                                 FOR_EACH_REALCLIENT(to)
1879                                         { if(Notification_ShouldSend(broadcast, to, client))
1880                                                 { RECURSE_FROM_CHOICE(to, continue) } }
1881                                 break;
1882                         }
1883                 }
1884         }
1885         else
1886         {
1887                 entity net_notif = spawn();
1888                 net_notif.owner = notif;
1889                 net_notif.classname = "net_notification";
1890                 net_notif.nent_broadcast = broadcast;
1891                 net_notif.nent_client = client;
1892                 net_notif.nent_net_type = net_type;
1893                 net_notif.nent_net_name = net_name;
1894                 net_notif.nent_stringcount = notif.nent_stringcount;
1895                 net_notif.nent_floatcount = notif.nent_floatcount;
1896                 
1897                 float i;
1898                 for(i = 0; i < net_notif.nent_stringcount; ++i)
1899                         { net_notif.nent_strings[i] = strzone(...(i, string)); }
1900                 for(i = 0; i < net_notif.nent_floatcount; ++i)
1901                         { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
1902
1903                 net_notif.think = Net_Notification_Remove;
1904                 net_notif.nextthink =
1905                         ((time > autocvar_notification_lifetime_mapload)
1906                         ?
1907                                 (time + autocvar_notification_lifetime_runtime)
1908                                 :
1909                                 autocvar_notification_lifetime_mapload
1910                         ); 
1911
1912                 Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
1913         }
1914 }
1915
1916 // WOVA = Without Variable Arguments 
1917 void Send_Notification_WOVA(
1918         float broadcast, entity client,
1919         float net_type, float net_name,
1920         float stringcount, float floatcount,
1921         string s1, string s2, string s3, string s4,
1922         float f1, float f2, float f3, float f4)
1923 {
1924         #ifdef NOTIFICATIONS_DEBUG
1925         entity notif = Get_Notif_Ent(net_type, net_name);
1926         Debug_Notification(sprintf(
1927                 "Send_Notification_WOVA(%s, %s, %d, %d, %d, %s, %s);\n",
1928                 Get_Notif_BroadcastName(broadcast),
1929                 Get_Notif_TypeName(net_type),
1930                 notif.nent_name,
1931                 stringcount,
1932                 floatcount,
1933                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1934                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1935         ));
1936         #endif
1937         
1938         #define VARITEM(stringc,floatc,args) \
1939                 if((stringcount == stringc) && (floatcount == floatc)) \
1940                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1941         EIGHT_VARS_TO_VARARGS_VARLIST
1942         #undef VARITEM
1943         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1944 }
1945
1946 // WOCOVA = Without Counts Or Variable Arguments 
1947 void Send_Notification_WOCOVA(
1948         float broadcast, entity client,
1949         float net_type, float net_name,
1950         string s1, string s2, string s3, string s4,
1951         float f1, float f2, float f3, float f4)
1952 {
1953         entity notif = Get_Notif_Ent(net_type, net_name);
1954         
1955         #ifdef NOTIFICATIONS_DEBUG
1956         Debug_Notification(sprintf(
1957                 "Send_Notification_WOCOVA(%s, %s, %s, %s, %s);\n",
1958                 Get_Notif_BroadcastName(broadcast),
1959                 Get_Notif_TypeName(net_type),
1960                 notif.nent_name,
1961                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1962                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1963         ));
1964         #endif
1965         
1966         #define VARITEM(stringc,floatc,args) \
1967                 if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1968                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1969         EIGHT_VARS_TO_VARARGS_VARLIST
1970         #undef VARITEM
1971         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1972 }
1973 #endif // ifdef SVQC