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