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