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