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