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