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