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