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