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