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