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