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