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