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