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