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