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