]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Rename notif_any to notif_all
[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_MSG) { 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         #define DESTROY_LOOP(type,count) \
580                 for(i = 1; i <= count; ++i) \
581                 { \
582                         notif = Get_Notif_Ent(type, i); \
583                         if not(notif) { backtrace("Destroy_All_Notifications(): Missing notification entity!\n"); return; } \
584                         Destroy_Notification_Entity(notif); \
585                 }
586
587         // kill all networked notifications
588         #ifdef SVQC
589         Kill_Notification(NOTIF_ALL, world, 0, 0);
590         #endif
591
592         // kill all real notification entities
593         DESTROY_LOOP(MSG_INFO, NOTIF_INFO_COUNT)
594         DESTROY_LOOP(MSG_CENTER, NOTIF_CENTER_COUNT)
595         DESTROY_LOOP(MSG_MULTI, NOTIF_MULTI_COUNT)
596         #undef DESTROY_LOOP
597 }
598
599
600 // =========================================
601 //  Cvar Handling With 'dumpnotifs' Command
602 // =========================================
603
604 void Dump_Notifications(float fh, float alsoprint)
605 {
606         #define NOTIF_WRITE(a) { \
607                 fputs(fh, a); \
608                 if(alsoprint) { print(a); } }
609         #define NOTIF_WRITE_ENTITY(name,default,description) { \
610                 notif_msg = \
611                         sprintf( \
612                                 "seta notification_%s \"%d\" \"%s\"\n", \
613                                 name, default, description \
614                         ); \
615                 NOTIF_WRITE(notif_msg) }
616         #define NOTIF_WRITE_HARDCODED(cvar,default,description) { \
617                 notif_msg = \
618                         sprintf( \
619                                 "seta notification_%s \"%s\" \"%s\"\n", \
620                                 cvar, default, description \
621                         ); \
622                 NOTIF_WRITE(notif_msg) }
623
624         string notif_msg;
625         float i;
626         entity e;
627
628         // Note: This warning only applies to the notifications.cfg file that is output...
629
630         // You ARE supposed to manually edit this function to add i.e. hard coded
631         // notification variables for mutators or game modes or such and then
632         // regenerate the notifications.cfg file from the new code.
633
634         NOTIF_WRITE("// ********************************************** //\n");
635         NOTIF_WRITE("// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n");
636         NOTIF_WRITE("// **                                          ** //\n");
637         NOTIF_WRITE("// **  This file is automatically generated    ** //\n");
638         NOTIF_WRITE("// **  by code with the command 'dumpnotifs'.  ** //\n");
639         NOTIF_WRITE("// **                                          ** //\n");
640         NOTIF_WRITE("// **  If you add a new notification, please   ** //\n");
641         NOTIF_WRITE("// **  regenerate this file with that command  ** //\n");
642         NOTIF_WRITE("// **  making sure that the output matches     ** //\n");
643         NOTIF_WRITE("// **  with the lists and defaults in code.    ** //\n");
644         NOTIF_WRITE("// **                                          ** //\n");
645         NOTIF_WRITE("// ********************************************** //\n");
646
647         // These notifications will also append their string as a comment...
648         // This is not necessary, and does not matter if they vary between config versions,
649         // it is just a semi-helpful tool for those who want to manually change their user settings.
650
651         NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
652         for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
653         {
654                 e = Get_Notif_Ent(MSG_INFO, i);
655                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
656                 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)");
657         }
658
659         NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
660         for(i = 1; i <= NOTIF_CENTER_COUNT; ++i)
661         {
662                 e = Get_Notif_Ent(MSG_CENTER, i);
663                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
664                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = centerprint");
665         }
666
667         NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications (count = %d):\n", NOTIF_MULTI_COUNT));
668         for(i = 1; i <= NOTIF_MULTI_COUNT; ++i)
669         {
670                 e = Get_Notif_Ent(MSG_MULTI, i);
671                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
672                 NOTIF_WRITE_ENTITY(e.nent_name, e.nent_default, "Notification control cvar: 0 = off, 1 = trigger subcalls");
673         }
674
675         // edit these to match whichever cvars are used for specific notification options
676         NOTIF_WRITE("\n// HARD CODED notification variables:\n");
677         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)");
678         NOTIF_WRITE_HARDCODED("show_location",                                          "0",    "Append location information to MSG_INFO death/kill messages");
679         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)'");
680         NOTIF_WRITE_HARDCODED("show_sprees",                                            "1",    "Print information about sprees in death/kill messages");
681         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");
682         NOTIF_WRITE_HARDCODED("show_sprees_center_specialonly",         "1",    "Don't show spree information in MSG_CENTER messages if it isn't an achievement");
683         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");
684         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");
685         NOTIF_WRITE_HARDCODED("show_sprees_info_specialonly",           "1",    "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement");
686         NOTIF_WRITE_HARDCODED("item_centerprinttime",                           "1.5",  "How long to show item information centerprint messages (like 'You got the Electro' or such)");
687         NOTIF_WRITE_HARDCODED("errors_are_fatal",                                       "1",    "If a notification fails upon initialization, cause a Host_Error to stop the program");
688         NOTIF_WRITE_HARDCODED("ctf_pickup_team_verbose",                        "0",    "Show extra information if a team mate picks up a flag");
689         NOTIF_WRITE_HARDCODED("ctf_pickup_enemy_verbose",                       "0",    "Show extra information if an enemy picks up a flag");
690         NOTIF_WRITE_HARDCODED("ctf_capture_verbose",                            "0",    "Show extra information when someone captures a flag");
691         NOTIF_WRITE_HARDCODED("frag_verbose",                                           "1",    "Show extra information when you frag someone (or when you are fragged");
692         NOTIF_WRITE_HARDCODED("lifetime_runtime",                                       "0.5",  "Amount of time that notification entities last on the server during runtime (In seconds)");
693         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)");
694
695         NOTIF_WRITE(sprintf(
696                 strcat(
697                         "\n// Notification counts (total = %d): ",
698                         "MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d\n"
699                 ),
700                 (
701                         NOTIF_INFO_COUNT +
702                         NOTIF_CENTER_COUNT +
703                         NOTIF_MULTI_COUNT
704                 ), 
705                 NOTIF_INFO_COUNT,
706                 NOTIF_CENTER_COUNT,
707                 NOTIF_MULTI_COUNT
708         ));
709         
710         return;
711         #undef NOTIF_WRITE_HARDCODED
712         #undef NOTIF_WRITE_ENTITY
713         #undef NOTIF_WRITE
714 }
715
716 #ifdef SVQC
717 void Notification_GetCvars()
718 {
719         GetCvars_handleFloat(get_cvars_s, get_cvars_f, FRAG_VERBOSE, "notification_frag_verbose");
720 }
721 #endif
722
723
724 // ===============================
725 //  Frontend Notification Pushing
726 // ===============================
727
728 string Local_Notification_sprintf(
729         string input, string args, 
730         string s1, string s2, string s3, string s4,
731         float f1, float f2, float f3, float f4)
732 {
733         #ifdef NOTIFICATIONS_DEBUG
734         dprint(sprintf(
735                 "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
736                 strreplace("\n", "\\n", input),
737                 args,
738                 strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
739                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
740         ));
741         #endif
742         
743         string selected;
744         float sel_num;
745         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
746
747         string tmp_s;
748
749         for(sel_num = 0;(args != "");)
750         {
751                 selected = car(args); args = cdr(args);
752                 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
753                 switch(strtolower(selected))
754                 {
755                         #define ARG_CASE(prog,selected,result) \
756                                 #ifdef CSQC \
757                                         #if (prog != ARG_SV) && (prog != ARG_DC) \
758                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
759                                         #endif \
760                                 #else \
761                                         #if (prog != ARG_CS) && (prog != ARG_DC) \
762                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
763                                         #endif \
764                                 #endif
765                         NOTIF_ARGUMENT_LIST
766                         #undef ARG_CASE
767                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
768                 }
769         }
770         return sprintf(input, arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
771 }
772
773 #ifdef CSQC
774 void Local_Notification_HUD_Notify_Push(
775         string icon, string hudargs,
776         string s1, string s2, string s3, string s4)
777 {
778         string selected;
779         float sel_num;
780         arg_slot[0] = ""; arg_slot[1] = "";
781
782         for(sel_num = 0;(hudargs != "");)
783         {
784                 selected = car(hudargs); hudargs = cdr(hudargs);
785                 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
786                 switch(strtolower(selected))
787                 {
788                         #define ARG_CASE(prog,selected,result) \
789                                 #if (prog == ARG_CS_SV_HA) \
790                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
791                                 #endif
792                         NOTIF_ARGUMENT_LIST
793                         #undef ARG_CASE
794                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
795                 }
796         }
797         #ifdef NOTIFICATIONS_DEBUG
798         dprint(sprintf(
799                 "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
800                 icon,
801                 hudargs,
802                 strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
803                 strreplace("\n", "\\n", sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
804         ));
805         #endif
806         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
807 }
808
809 void Local_Notification_centerprint_generic(
810         string input, string durcnt,
811         float cpid, float f1, float f2)
812 {
813         string selected;
814         float sel_num;
815         arg_slot[0] = ""; arg_slot[1] = "";
816
817         for(sel_num = 0;(durcnt != "");)
818         {
819                 selected = car(durcnt); durcnt = cdr(durcnt);
820                 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
821                 switch(strtolower(selected))
822                 {
823                         #define ARG_CASE(prog,selected,result) \
824                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
825                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
826                                 #endif
827                         NOTIF_ARGUMENT_LIST
828                         #undef ARG_CASE
829                         default:
830                         {
831                                 if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
832                                 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
833                                 break;
834                         }
835                 }
836         }
837         #ifdef NOTIFICATIONS_DEBUG
838         dprint(sprintf(
839                 "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
840                 strreplace("\n", "\\n", input),
841                 durcnt,
842                 f1, f2,
843                 stof(arg_slot[0]), stof(arg_slot[1])
844         ));
845         #endif
846         centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
847 }
848 #endif
849
850 void Local_Notification(float net_type, float net_name, ...count)
851 {
852         // check supplied type and name for errors
853         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
854         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
855
856         entity notif = Get_Notif_Ent(net_type, net_name);
857         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
858
859         #ifdef NOTIFICATIONS_DEBUG
860         if not(notif.nent_enabled)
861         {
862                 dprint(sprintf(
863                         "Local_Notification(%s, %s): Entity was disabled...\n",
864                         Get_Notif_TypeName(net_type),
865                         notif.nent_name
866                 ));
867                 return;
868         }
869         #endif
870         
871         if((notif.nent_stringcount + notif.nent_floatcount) > count)
872         {
873                 backtrace(sprintf(
874                         strcat(
875                                 "Not enough arguments for Local_Notification(%s, %s, ...)! ",
876                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
877                                 "Check the definition and function call for accuracy...?\n"
878                         ),
879                         Get_Notif_TypeName(net_type), notif.nent_name,
880                         notif.nent_stringcount, notif.nent_floatcount, count
881                 ));
882                 return;
883         }
884         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
885         {
886                 backtrace(sprintf(
887                         strcat(
888                                 "Too many arguments for Local_Notification(%s, %s, ...)! ",
889                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
890                                 "Check the definition and function call for accuracy...?\n"
891                         ),
892                         Get_Notif_TypeName(net_type), notif.nent_name,
893                         notif.nent_stringcount, notif.nent_floatcount, count
894                 ));
895                 return;
896         }
897
898         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
899         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
900         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
901         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
902         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
903         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
904         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
905         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
906
907         #ifdef NOTIFICATIONS_DEBUG
908         dprint(sprintf(
909                 "Local_Notification(%s, %s, %s, %s);\n",
910                 Get_Notif_TypeName(net_type),
911                 notif.nent_name,
912                 strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
913                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
914         ));
915         #endif
916         
917         switch(net_type)
918         {
919                 case MSG_INFO:
920                 {
921                         print(
922                                 Local_Notification_sprintf(
923                                         notif.nent_string,
924                                         notif.nent_args, 
925                                         s1, s2, s3, s4,
926                                         f1, f2, f3, f4)
927                         );
928                         #ifdef CSQC 
929                         if(notif.nent_icon != "")
930                         {
931                                 Local_Notification_HUD_Notify_Push(
932                                         notif.nent_icon,
933                                         notif.nent_hudargs,
934                                         s1, s2, s3, s4);
935                         } 
936                         #endif 
937                         break;
938                 }
939                 
940                 #ifdef CSQC
941                 case MSG_CENTER:
942                 {
943                         Local_Notification_centerprint_generic(
944                                 Local_Notification_sprintf(
945                                         notif.nent_string,
946                                         notif.nent_args, 
947                                         s1, s2, s3, s4,
948                                         f1, f2, f3, f4),
949                                 notif.nent_durcnt,
950                                 notif.nent_cpid,
951                                 f1, f2);
952                         break;
953                 }
954                 #endif
955                 
956                 case MSG_MULTI:
957                 {
958                         if(notif.nent_msginfo)
959                         if(notif.nent_msginfo.nent_enabled)
960                         {
961                                 Local_Notification_WOVA(
962                                         MSG_INFO,
963                                         notif.nent_msginfo.nent_id, 
964                                         notif.nent_msginfo.nent_stringcount, 
965                                         notif.nent_msginfo.nent_floatcount, 
966                                         s1, s2, s3, s4,
967                                         f1, f2, f3, f4);
968                         }
969                         #ifdef CSQC
970                         if(notif.nent_msgcenter)
971                         if(notif.nent_msgcenter.nent_enabled)
972                         {
973                                 Local_Notification_WOVA(
974                                         MSG_CENTER,
975                                         notif.nent_msgcenter.nent_id, 
976                                         notif.nent_msgcenter.nent_stringcount, 
977                                         notif.nent_msgcenter.nent_floatcount, 
978                                         s1, s2, s3, s4,
979                                         f1, f2, f3, f4); 
980                         }
981                         #endif
982                         break;
983                 }
984         }
985 }
986
987 // WOVA = Without Variable Arguments 
988 void Local_Notification_WOVA(
989         float net_type, float net_name,
990         float stringcount, float floatcount,
991         string s1, string s2, string s3, string s4,
992         float f1, float f2, float f3, float f4)
993 {
994         #define VARITEM(stringc,floatc,args) \
995                 if((stringcount == stringc) && (floatcount == floatc)) \
996                         { Local_Notification(net_type, net_name, args); return; }
997         EIGHT_VARS_TO_VARARGS_VARLIST
998         #undef VARITEM
999         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1000 }
1001
1002
1003 // =========================
1004 //  Notification Networking
1005 // =========================
1006
1007 #ifdef CSQC
1008 void Read_Notification(float is_new)
1009 {
1010         float net_type = ReadByte();
1011         float net_name = ReadShort();
1012
1013         entity notif;
1014
1015         if(net_type == MSG_CENTER_KILL)
1016         {
1017                 if(is_new)
1018                 {
1019                         if(net_name == 0) { reset_centerprint_messages(); }
1020                         else
1021                         {
1022                                 notif = Get_Notif_Ent(MSG_CENTER, net_name);
1023                                 if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
1024                                 centerprint_generic(notif.nent_cpid, "", 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         #ifdef NOTIFICATIONS_DEBUG
1069         if not(self) { dprint(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
1070         if(self.nent_net_name == -1)
1071         {
1072                 dprint(sprintf(
1073                         "Net_Notification_Remove() at %f: Killed '%s' notification\n",
1074                         time,
1075                         Get_Notif_TypeName(self.nent_net_type)
1076                 ));
1077         }
1078         else
1079         #endif
1080         {
1081                 string checkargs = Notification_CheckArgs_TypeName(self.nent_net_type, self.nent_net_name);
1082                 if(checkargs != "") { dprint(sprintf("Incorrect usage of Net_Notification_Remove() at %f: %s\n", time, checkargs)); return; }
1083
1084                 #ifdef NOTIFICATIONS_DEBUG
1085                 entity realent = Get_Notif_Ent(self.nent_net_type, self.nent_net_name);
1086                 dprint(sprintf(
1087                         "Net_Notification_Remove() at %f: Removed '%s - %s' notification\n",
1088                         time,
1089                         Get_Notif_TypeName(self.nent_net_type), 
1090                         realent.nent_name
1091                 ));
1092                 #endif
1093         }
1094         
1095         float i;
1096         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
1097         remove(self);
1098 }
1099
1100 float Net_Write_Notification(entity client, float sf)
1101 {
1102         float i, send = FALSE;
1103         
1104         switch(self.nent_broadcast)
1105         {
1106                 case NOTIF_ONE: // send to one client and their spectator
1107                 {
1108                         if(
1109                                 (client == self.nent_client)
1110                                 ||
1111                                 (
1112                                         (client.classname == STR_SPECTATOR)
1113                                         &&
1114                                         (client.enemy == self.nent_client)
1115                                 )
1116                         ) { send = TRUE; }
1117                         break;
1118                 }
1119                 case NOTIF_ONE_ONLY: // send ONLY to one client
1120                 {
1121                         if(client == self.nent_client) { send = TRUE; }
1122                         break;
1123                 }
1124                 case NOTIF_TEAM: // send only to X team and their spectators
1125                 {
1126                         if(
1127                                 (client.team == self.nent_client.team)
1128                                 ||
1129                                 (
1130                                         (client.classname == STR_SPECTATOR)
1131                                         &&
1132                                         (client.enemy.team == self.nent_client.team)
1133                                 )
1134                         ) { send = TRUE; }
1135                         break;
1136                 }
1137                 case NOTIF_TEAM_EXCEPT: // send only to X team and their spectators, except for Y person and their spectators
1138                 {
1139                         if(
1140                                 (client != self.nent_client)
1141                                 &&
1142                                 (
1143                                         (client.team == self.nent_client.team)
1144                                         ||
1145                                         (
1146                                                 (client.classname == STR_SPECTATOR)
1147                                                 &&
1148                                                 (
1149                                                         (client.enemy != self.nent_client)
1150                                                         &&
1151                                                         (client.enemy.team == self.nent_client.team)
1152                                                 )
1153                                         )
1154                                 )
1155                         ) { send = TRUE; }
1156                         break;
1157                 }
1158                 case NOTIF_ALL: // send to everyone
1159                 {
1160                         send = TRUE;
1161                         break;
1162                 }
1163                 case NOTIF_ALL_EXCEPT: // send to everyone except X person and their spectators
1164                 {
1165                         if(
1166                                 (client != self.nent_client)
1167                                 &&
1168                                 !(
1169                                         (client.classname == STR_SPECTATOR)
1170                                         &&
1171                                         (client.enemy == self.nent_client)
1172                                 )
1173                         ) { send = TRUE; }
1174                         break;
1175                 }
1176                 default: { send = FALSE; break; }
1177         }
1178
1179         if(send)
1180         {               
1181                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1182                 WriteByte(MSG_ENTITY, self.nent_net_type);
1183                 WriteShort(MSG_ENTITY, self.nent_net_name);
1184                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
1185                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
1186         }
1187
1188         return send; 
1189 }
1190
1191 void Kill_Notification(
1192         float broadcast, entity client,
1193         float net_type, float net_name)
1194 {
1195         string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
1196         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
1197
1198         #ifdef NOTIFICATIONS_DEBUG
1199         dprint(sprintf(
1200                 "Kill_Notification(%d, '%s', %d, %d);\n",
1201                 broadcast,
1202                 client.netname,
1203                 net_type,
1204                 net_name
1205         ));
1206         #endif
1207
1208         entity notif, net_notif;
1209
1210         // if no name is provided, just kill ALL the centerprint notifications
1211         if(net_type == MSG_CENTER)
1212         {
1213                 net_notif = spawn();
1214                 net_notif.classname = "net_kill_notification";
1215                 net_notif.nent_broadcast = broadcast;
1216                 net_notif.nent_client = client;
1217                 net_notif.nent_net_type = MSG_CENTER_KILL;
1218                 net_notif.nent_net_name = net_name;
1219                 Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
1220         }
1221
1222         for(notif = world; (notif = find(notif, classname, "net_notification"));)
1223         {
1224                 // now kill the old send notification entity
1225                 if(net_type)
1226                 {
1227                         if(notif.nent_net_type == net_type)
1228                         {
1229                                 if(net_name)
1230                                 {
1231                                         if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.think(); }
1232                                         else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
1233                                 }
1234                                 else { notif.nent_net_name = -1; notif.think(); }
1235                         }
1236                         else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
1237                 }
1238                 else { notif.nent_net_name = -1; notif.think(); }
1239         }
1240 }
1241
1242 void Send_Notification(
1243         float broadcast, entity client,
1244         float net_type, float net_name,
1245         ...count)
1246 {
1247         // check supplied broadcast, target, type, and name for errors
1248         string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
1249         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
1250
1251         // retreive counts for the arguments of this notification
1252         entity notif = Get_Notif_Ent(net_type, net_name);
1253         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
1254
1255         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1256         {
1257                 backtrace(sprintf(
1258                         strcat(
1259                                 "Not enough arguments for Send_Notification(%d, %s, %s, ...)! ",
1260                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1261                                 "Check the definition and function call for accuracy...?\n"
1262                         ),
1263                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1264                         notif.nent_stringcount, notif.nent_floatcount, count
1265                 ));
1266                 return;
1267         }
1268         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1269         {
1270                 backtrace(sprintf(
1271                         strcat(
1272                                 "Too many arguments for Send_Notification(%d, %s, %s, ...)! ",
1273                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1274                                 "Check the definition and function call for accuracy...?\n"
1275                         ),
1276                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1277                         notif.nent_stringcount, notif.nent_floatcount, count
1278                 ));
1279                 return;
1280         }
1281
1282         #ifdef NOTIFICATIONS_DEBUG
1283         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1284         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1285         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1286         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1287         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1288         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1289         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1290         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1291         dprint(sprintf(
1292                 "Send_Notification(%d, %s, %s, %s, %s);\n",
1293                 broadcast,
1294                 Get_Notif_TypeName(net_type),
1295                 notif.nent_name,
1296                 strreplace("\n", "\\n", sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1297                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1298         ));
1299         #endif
1300
1301         entity net_notif = spawn();
1302         net_notif.classname = "net_notification";
1303         net_notif.nent_broadcast = broadcast;
1304         net_notif.nent_client = client;
1305         net_notif.nent_net_type = net_type;
1306         net_notif.nent_net_name = net_name;
1307         net_notif.nent_stringcount = notif.nent_stringcount;
1308         net_notif.nent_floatcount = notif.nent_floatcount;
1309         
1310         float i;
1311         for(i = 0; i < net_notif.nent_stringcount; ++i) { net_notif.nent_strings[i] = strzone(...(i, string)); }
1312         for(i = 0; i < net_notif.nent_floatcount; ++i) { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
1313
1314         net_notif.think = Net_Notification_Remove;
1315         net_notif.nextthink =
1316                 ((time > autocvar_notification_lifetime_mapload)
1317                 ?
1318                         (time + autocvar_notification_lifetime_runtime)
1319                         :
1320                         autocvar_notification_lifetime_mapload
1321                 ); 
1322
1323         Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
1324
1325         if(server_is_dedicated && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT) && (net_type != MSG_CENTER))
1326         {
1327                 Local_Notification_WOVA(
1328                         net_type, net_name,
1329                         notif.nent_stringcount,
1330                         notif.nent_floatcount,
1331                         IFSTR(0), IFSTR(1), IFSTR(2), IFSTR(3),
1332                         IFFL(0), IFFL(1), IFFL(2), IFFL(3));
1333         }
1334 }
1335
1336 // WOVA = Without Variable Arguments 
1337 void Send_Notification_WOVA(
1338         float broadcast, entity client,
1339         float net_type, float net_name,
1340         string s1, string s2, string s3, string s4,
1341         float f1, float f2, float f3, float f4)
1342 {
1343         entity notif = Get_Notif_Ent(net_type, net_name);
1344         
1345         #ifdef NOTIFICATIONS_DEBUG
1346         dprint(sprintf(
1347                 "Send_Notification_WOVA(%d, %s, %s, %s, %s - %d %d);\n",
1348                 broadcast,
1349                 Get_Notif_TypeName(net_type),
1350                 notif.nent_name,
1351                 sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4),
1352                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4),
1353                 notif.nent_stringcount, notif.nent_floatcount
1354         ));
1355         #endif
1356         
1357         #define VARITEM(stringc,floatc,args) \
1358                 if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1359                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1360         EIGHT_VARS_TO_VARARGS_VARLIST
1361         #undef VARITEM
1362         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1363 }
1364
1365
1366 // =============================
1367 //  LEGACY NOTIFICATION SYSTEMS
1368 // =============================
1369
1370 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1371 {
1372         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
1373         {
1374                 msg_entity = e;
1375                 WRITESPECTATABLE_MSG_ONE({
1376                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
1377                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1378                         WriteByte(MSG_ONE, id);
1379                         WriteString(MSG_ONE, s);
1380                         if (id != 0 && s != "")
1381                         {
1382                                 WriteByte(MSG_ONE, duration);
1383                                 WriteByte(MSG_ONE, countdown_num);
1384                         }
1385                 });
1386         }
1387 }
1388 #endif // ifdef SVQC