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