]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
Most of the framework should now properly support MSG_CHOICE
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: June, 2013
4 // ================================================
5
6 string Get_Notif_TypeName(float net_type)
7 {
8         switch(net_type)
9         {
10                 case MSG_ANNCE: return "MSG_ANNCE";
11                 case MSG_INFO: return "MSG_INFO";
12                 case MSG_CENTER: return "MSG_CENTER";
13                 case MSG_CENTER_CPID: return "MSG_CENTER_CPID";
14                 case MSG_MULTI: return "MSG_MULTI";
15                 case MSG_CHOICE: return "MSG_CHOICE";
16         }
17         backtrace(sprintf("Get_Notif_TypeName(%d): Improper net type!\n", net_type));
18         return "";
19 }
20
21 entity Get_Notif_Ent(float net_type, float net_name)
22 {
23         switch(net_type)
24         {
25                 case MSG_ANNCE: return msg_annce_notifs[net_name - 1];
26                 case MSG_INFO: return msg_info_notifs[net_name - 1];
27                 case MSG_CENTER: return msg_center_notifs[net_name - 1];
28                 case MSG_MULTI: return msg_multi_notifs[net_name - 1];
29                 case MSG_CHOICE: return msg_choice_notifs[net_name - 1];
30         }
31         backtrace(sprintf("Get_Notif_Ent(%d, %d): Improper net type!\n", net_type, net_name));
32         return world;
33 }
34
35 string Notification_CheckArgs_TypeName(float net_type, float net_name)
36 {
37         // check supplied type and name for errors
38         string checkargs = "";
39         #define CHECKARG_TYPENAME(type) case MSG_##type##: \
40                 { if(!net_name || (net_name > NOTIF_##type##_COUNT)) \
41                 { checkargs = sprintf("Improper name: %d!", net_name); } break; }
42         switch(net_type)
43         {
44                 CHECKARG_TYPENAME(ANNCE)
45                 CHECKARG_TYPENAME(INFO)
46                 CHECKARG_TYPENAME(CENTER)
47                 CHECKARG_TYPENAME(MULTI)
48                 CHECKARG_TYPENAME(CHOICE)
49                 default: { checkargs = sprintf("Improper type: %d!", checkargs, net_type); break; }
50         }
51         #undef CHECKARG_TYPENAME
52         return checkargs;
53 }
54
55 #ifdef SVQC
56 string Notification_CheckArgs(
57         float broadcast, entity client,
58         float net_type, float net_name)
59 {
60         // check supplied broadcast, target, type, and name for errors
61         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
62         if(checkargs != "") { checkargs = strcat(checkargs, " "); }
63         switch(broadcast)
64         {
65                 case NOTIF_ONE:
66                 case NOTIF_ONE_ONLY:
67                 {
68                         if(IS_NOT_A_CLIENT(client))
69                                 { checkargs = sprintf("%sNo client provided!", checkargs); }
70                         break;
71                 }
72                 
73                 case NOTIF_ALL_EXCEPT:
74                 {
75                         if(IS_NOT_A_CLIENT(client))
76                                 { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
77                         break;
78                 }
79                 
80                 case NOTIF_ALL:
81                 {
82                         if(client)
83                                 { checkargs = sprintf("%sEntity provided when world was required!", checkargs); }
84                         break;
85                 }
86                 
87                 case NOTIF_TEAM:
88                 case NOTIF_TEAM_EXCEPT:
89                 {
90                         if not(teamplay) { checkargs = sprintf("%sTeamplay not active!", checkargs); }
91                         else if(IS_NOT_A_CLIENT(client))
92                         {
93                                 if(broadcast == NOTIF_TEAM) { checkargs = sprintf("%sNo client provided!", checkargs); }
94                                 else { checkargs = sprintf("%sException can't be a non-client!", checkargs); }
95                         }
96                         break;
97                 }
98                 
99                 default: { checkargs = sprintf("%sImproper broadcast: %d!", checkargs, broadcast); break; }
100         }
101         return checkargs;
102 }
103
104 float Notification_ShouldSend(float broadcast, entity to_client, entity other_client)
105 {
106         switch(broadcast)
107         {
108                 case NOTIF_ONE: // send to one client and their spectator
109                 {
110                         if(
111                                 (to_client == other_client)
112                                 ||
113                                 (
114                                         IS_SPEC(to_client)
115                                         &&
116                                         (to_client.enemy == other_client)
117                                 )
118                         ) { return TRUE; }
119                         break;
120                 }
121                 case NOTIF_ONE_ONLY: // send ONLY to one client
122                 {
123                         if(to_client == other_client) { return TRUE; }
124                         break;
125                 }
126                 case NOTIF_TEAM: // send only to X team and their spectators
127                 {
128                         if(
129                                 (to_client.team == other_client.team)
130                                 ||
131                                 (
132                                         IS_SPEC(to_client)
133                                         &&
134                                         (to_client.enemy.team == other_client.team)
135                                 )
136                         ) { return TRUE; }
137                         break;
138                 }
139                 case NOTIF_TEAM_EXCEPT: // send only to X team and their spectators, except for Y person and their spectators
140                 {
141                         if(
142                                 (to_client != other_client)
143                                 &&
144                                 (
145                                         (to_client.team == other_client.team)
146                                         ||
147                                         (
148                                                 IS_SPEC(to_client)
149                                                 &&
150                                                 (
151                                                         (to_client.enemy != other_client)
152                                                         &&
153                                                         (to_client.enemy.team == other_client.team)
154                                                 )
155                                         )
156                                 )
157                         ) { return TRUE; }
158                         break;
159                 }
160                 case NOTIF_ALL: // send to everyone
161                 {
162                         return TRUE;
163                 }
164                 case NOTIF_ALL_EXCEPT: // send to everyone except X person and their spectators
165                 {
166                         if(
167                                 (to_client != other_client)
168                                 &&
169                                 !(
170                                         IS_SPEC(to_client)
171                                         &&
172                                         (to_client.enemy == other_client)
173                                 )
174                         ) { return TRUE; }
175                         break;
176                 }
177         }
178         return FALSE;
179 }
180
181 #endif
182
183 // ===============================
184 //  Initialization Core Functions
185 // ===============================
186
187 // used by restartnotifs command to initialize notifications
188 void Destroy_Notification_Entity(entity notif)
189 {
190         if(notif.nent_name != "") { strunzone(notif.nent_name); }
191         if(notif.nent_snd != "") { strunzone(notif.nent_snd); }
192         if(notif.nent_args != "") { strunzone(notif.nent_args); }
193         if(notif.nent_hudargs != "") { strunzone(notif.nent_hudargs); }
194         if(notif.nent_icon != "") { strunzone(notif.nent_icon); }
195         if(notif.nent_durcnt != "") { strunzone(notif.nent_durcnt); }
196         if(notif.nent_string != "") { strunzone(notif.nent_string); }
197         remove(notif);
198 }
199
200 void Destroy_All_Notifications(void)
201 {
202         entity notif;
203         float i;
204         
205         #define DESTROY_LOOP(type,count) \
206                 for(i = 1; i <= count; ++i) \
207                 { \
208                         notif = Get_Notif_Ent(type, i); \
209                         if not(notif) { backtrace("Destroy_All_Notifications(): Missing notification entity!\n"); return; } \
210                         Destroy_Notification_Entity(notif); \
211                 }
212
213         // kill all networked notifications and centerprints
214         #ifdef SVQC
215         Kill_Notification(NOTIF_ALL, world, 0, 0);
216         #else
217         reset_centerprint_messages();
218         #endif
219
220         // kill all real notification entities
221         DESTROY_LOOP(MSG_ANNCE, NOTIF_ANNCE_COUNT)
222         DESTROY_LOOP(MSG_INFO, NOTIF_INFO_COUNT)
223         DESTROY_LOOP(MSG_CENTER, NOTIF_CENTER_COUNT)
224         DESTROY_LOOP(MSG_MULTI, NOTIF_MULTI_COUNT)
225         DESTROY_LOOP(MSG_CHOICE, NOTIF_CHOICE_COUNT)
226         #undef DESTROY_LOOP
227 }
228
229 string Process_Notif_Line(
230         float typeid,
231         float chat,
232         string input,
233         string notiftype,
234         string notifname,
235         string stringtype)
236 {
237         #ifdef CSQC
238         if(typeid == MSG_INFO)
239         {
240                 if((chat && autocvar_notification_allow_chatboxprint)
241                         || (autocvar_notification_allow_chatboxprint == 2))
242                 {
243                         // pass 1: add ETX char at beginning of line
244                         input = strcat("\{3}", input);
245
246                         // pass 2: add ETX char at end of each new line (so that
247                         // messages with multiple lines are put through chatbox too)
248                         input = strreplace("\n", "\n\{3}", input);
249
250                         // pass 3: strip trailing ETX char
251                         if(substring(input, (strlen(input) - 1), 1) == "\{3}")
252                                 { input = substring(input, 0, (strlen(input) - 1)); }
253                 }
254         }
255         #endif
256
257         // done to both MSG_INFO and MSG_CENTER
258         if(substring(input, (strlen(input) - 1), 1) == "\n")
259         {
260                 print(sprintf(
261                         strcat(
262                                 "^1TRAILING NEW LINE AT END OF NOTIFICATION: ",
263                                 "^7net_type = %s, net_name = %s, string = %s.\n"
264                         ),
265                         notiftype,
266                         notifname,
267                         stringtype
268                 ));
269                 notif_error = TRUE;
270                 input = substring(input, 1, (strlen(input) - 1));
271         }
272
273         return input;
274 }
275
276 string Process_Notif_Args(
277         float arg_type,
278         string args,
279         string notiftype,
280         string notifname)
281 {
282         string selected, remaining = args;
283         float sel_num = 0;
284
285         for(;(remaining != "");)
286         {
287                 selected = car(remaining); remaining = cdr(remaining);
288
289                 switch(arg_type)
290                 {
291                         case 1: // normal args
292                         {
293                                 if(sel_num == NOTIF_MAX_ARGS)
294                                 {
295                                         print(sprintf(
296                                                 strcat(
297                                                         "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
298                                                         "^7net_type = %s, net_name = %s, max args = %d.\n"
299                                                 ),
300                                                 notiftype,
301                                                 notifname,
302                                                 NOTIF_MAX_ARGS
303                                         ));
304                                         notif_error = TRUE;
305                                         break;
306                                 }
307
308                                 switch(strtolower(selected))
309                                 {
310                                         #define ARG_CASE(prog,selected,result) \
311                                                 #if (prog != ARG_DC) \
312                                                         case selected: { ++sel_num; break; } \
313                                                 #endif
314                                         NOTIF_ARGUMENT_LIST
315                                         #undef ARG_CASE
316                                         default:
317                                         {
318                                                 print(sprintf(
319                                                         strcat(
320                                                                 "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
321                                                                 "^7net_type = %s, net_name = %s, args arg = '%s'.\n"
322                                                         ),
323                                                         notiftype,
324                                                         notifname,
325                                                         selected
326                                                 ));
327                                                 notif_error = TRUE;
328                                                 break;
329                                         }
330                                 }
331                                 break;
332                         }
333                         case 2: // hudargs
334                         {
335                                 if(sel_num == NOTIF_MAX_HUDARGS)
336                                 {
337                                         print(sprintf(
338                                                 strcat(
339                                                         "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
340                                                         "^7net_type = %s, net_name = %s, max hudargs = %d.\n"
341                                                 ),
342                                                 notiftype,
343                                                 notifname,
344                                                 NOTIF_MAX_HUDARGS
345                                         ));
346                                         notif_error = TRUE;
347                                         break;
348                                 }
349
350                                 switch(strtolower(selected))
351                                 {
352                                         #define ARG_CASE(prog,selected,result) \
353                                                 #if (prog == ARG_CS_SV_HA) \
354                                                         case selected: { ++sel_num; break; } \
355                                                 #endif
356                                         NOTIF_ARGUMENT_LIST
357                                         #undef ARG_CASE
358                                         default:
359                                         {
360                                                 print(sprintf(
361                                                         strcat(
362                                                                 "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
363                                                                 "^7net_type = %s, net_name = %s, hudargs arg = '%s'.\n"
364                                                         ),
365                                                         notiftype,
366                                                         notifname,
367                                                         selected
368                                                 ));
369                                                 notif_error = TRUE;
370                                                 break;
371                                         }
372                                 }
373                                 break;
374                         }
375                         case 3: // durcnt 
376                         {
377                                 if(sel_num == NOTIF_MAX_DURCNT)
378                                 {
379                                         print(sprintf(
380                                                 strcat(
381                                                         "^1NOTIFICATION HAS TOO MANY ARGUMENTS: ",
382                                                         "^7net_type = %s, net_name = %s, max durcnt = %d.\n"
383                                                 ),
384                                                 notiftype,
385                                                 notifname,
386                                                 NOTIF_MAX_DURCNT
387                                         ));
388                                         notif_error = TRUE;
389                                         break;
390                                 }
391
392                                 switch(strtolower(selected))
393                                 {
394                                         #define ARG_CASE(prog,selected,result) \
395                                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
396                                                         case selected: { ++sel_num; break; } \
397                                                 #endif
398                                         NOTIF_ARGUMENT_LIST
399                                         #undef ARG_CASE
400                                         default:
401                                         {
402                                                 if(ftos(stof(selected)) != "") { ++sel_num; }
403                                                 else
404                                                 {
405                                                         print(sprintf(
406                                                                 strcat(
407                                                                         "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: ",
408                                                                         "^7net_type = %s, net_name = %s, durcnt arg = '%s'.\n"
409                                                                 ),
410                                                                 notiftype,
411                                                                 notifname,
412                                                                 selected
413                                                         ));
414                                                         notif_error = TRUE;
415                                                 }
416                                                 break;
417                                         }
418                                 }
419                                 break;
420                         }
421                 }
422         }
423         return args;
424 }
425
426 void Create_Notification_Entity(
427         float var_default,
428         float var_cvar,
429         float typeid,
430         float nameid,
431         string namestring,
432         float anncename,
433         float infoname,
434         float centername,
435         float channel,
436         string snd,
437         float vol,
438         float position,
439         float strnum,
440         float flnum,
441         string args,
442         string hudargs,
443         string icon,
444         float cpid,
445         string durcnt,
446         string normal,
447         string gentle,
448         float chtype,
449         float optiona,
450         float optionb)
451 {
452         // =====================
453         //  Global Entity Setup
454         // =====================
455         entity notif = spawn();
456         string typestring = "";
457         switch(typeid)
458         {
459                 case MSG_ANNCE:
460                 {
461                         typestring = "MSG_ANNCE";
462                         msg_annce_notifs[nameid - 1] = notif;
463                         notif.classname = "msg_annce_notification";
464                         break;
465                 }
466                 case MSG_INFO:
467                 {
468                         typestring = "MSG_INFO";
469                         msg_info_notifs[nameid - 1] = notif;
470                         notif.classname = "msg_info_notification";
471                         break;
472                 }
473                 case MSG_CENTER:
474                 {
475                         typestring = "MSG_CENTER";
476                         msg_center_notifs[nameid - 1] = notif;
477                         notif.classname = "msg_center_notification";
478                         break;
479                 }
480                 case MSG_MULTI:
481                 {
482                         typestring = "MSG_MULTI";
483                         msg_multi_notifs[nameid - 1] = notif;
484                         notif.classname = "msg_multi_notification";
485                         break;
486                 }
487                 case MSG_CHOICE:
488                 {
489                         typestring = "MSG_CHOICE";
490                         msg_choice_notifs[nameid - 1] = notif;
491                         notif.classname = "msg_choice_notification";
492                         break;
493                 }
494
495                 default:
496                 {
497                         error(sprintf(
498                                 strcat(
499                                         "^1NOTIFICATION WITH IMPROPER TYPE: ",
500                                         "^7net_type = %d, net_name = %s.\n"
501                                 ),
502                                 typeid,
503                                 namestring
504                         ));
505                         return; // It's not possible to recover from this one
506                 }
507         }
508         notif.nent_default = var_default;
509         notif.nent_name = strzone(namestring);
510         notif.nent_id = nameid;
511         notif.nent_enabled = (1 <= var_cvar);
512
513         // Other pre-notif-setup requisites
514         notif_error = FALSE;
515
516         // ====================
517         //  Notification Setup
518         // ====================
519         switch(typeid)
520         {
521                 case MSG_ANNCE:
522                 {
523                         // Set MSG_ANNCE information and handle precaching
524                         #ifdef CSQC
525                         if not(GENTLE && (var_cvar == 1))
526                         {
527                                 if(snd != "")
528                                 {
529                                         if(notif.nent_enabled)
530                                         {
531                                                 precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd));
532                                                 notif.nent_channel = channel;
533                                                 notif.nent_snd = strzone(snd);
534                                                 notif.nent_vol = vol;
535                                                 notif.nent_position = position;
536                                         }
537                                 }
538                                 else
539                                 {
540                                         print(sprintf(
541                                                 strcat(
542                                                         "^1NOTIFICATION WITH NO SOUND: ",
543                                                         "^7net_type = %s, net_name = %s.\n"
544                                                 ),
545                                                 typestring,
546                                                 namestring
547                                         ));
548                                         notif_error = TRUE;
549                                 }
550                         }
551                         else { notif.nent_enabled = FALSE; }
552                         #else
553                         notif.nent_enabled = FALSE;
554                         #endif
555
556                         break;
557                 }
558                 
559                 case MSG_INFO:
560                 case MSG_CENTER:
561                 {
562                         // Set MSG_INFO and MSG_CENTER string/float counts
563                         notif.nent_stringcount = strnum;
564                         notif.nent_floatcount = flnum;
565
566                         // Only initialize arguments if we're either a client or on a dedicated server
567                         #ifdef SVQC
568                         float should_process_args = server_is_dedicated;
569                         #else
570                         float should_process_args = TRUE;
571                         #endif
572
573                         if(should_process_args)
574                         {
575                                 // ========================
576                                 //  Process Main Arguments
577                                 // ========================
578                                 if(strnum + flnum)
579                                 {
580                                         if(args != "")
581                                         {
582                                                 notif.nent_args = strzone(
583                                                         Process_Notif_Args(1, args, typestring, namestring));
584                                         }
585                                         else if((hudargs == "") && (durcnt ==""))
586                                         {
587                                                 print(sprintf(
588                                                         strcat(
589                                                                 "^1NOTIFICATION HAS ARG COUNTS BUT NO ARGS OR HUDARGS OR DURCNT: ",
590                                                                 "^7net_type = %s, net_name = %s, strnum = %d, flnum = %d\n"
591                                                         ),
592                                                         typestring,
593                                                         namestring,
594                                                         strnum,
595                                                         flnum
596                                                 ));
597                                                 notif_error = TRUE;
598                                         }
599                                 }
600                                 else if(args != "")
601                                 {
602                                         notif.nent_args = strzone(
603                                                 Process_Notif_Args(1, args, typestring, namestring));
604                                 }
605
606
607                                 // =======================================
608                                 //  Process HUD and Centerprint Arguments
609                                 //    Only processed on CSQC, as these
610                                 //    args are only for HUD features.
611                                 // =======================================
612                                 #ifdef CSQC
613                                 if(hudargs != "")
614                                 {
615                                         notif.nent_hudargs = strzone(
616                                                 Process_Notif_Args(2, hudargs, typestring, namestring));
617                                                 
618                                         if(icon != "") { notif.nent_icon = strzone(icon); }
619                                         else
620                                         {
621                                                 print(sprintf(
622                                                         strcat(
623                                                                 "^1NOTIFICATION HAS HUDARGS BUT NO ICON: ",
624                                                                 "^7net_type = %s, net_name = %s.\n"
625                                                         ),
626                                                         typestring,
627                                                         namestring
628                                                 ));
629                                                 notif_error = TRUE;
630                                         }
631                                 }
632                                 else if(icon != "")
633                                 {
634                                         print(sprintf(
635                                                 strcat(
636                                                         "^1NOTIFICATION HAS ICON BUT NO HUDARGS: ",
637                                                         "^7net_type = %s, net_name = %s.\n"
638                                                 ),
639                                                 typestring,
640                                                 namestring
641                                         ));
642                                         notif_error = TRUE;
643                                 }
644
645                                 if(durcnt != "")
646                                 {
647                                         notif.nent_durcnt = strzone(
648                                                 Process_Notif_Args(3, durcnt, typestring, namestring));
649                                                 
650                                         if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
651                                         else
652                                         {
653                                                 print(sprintf(
654                                                         strcat(
655                                                                 "^1NOTIFICATION HAS DURCNT BUT NO CPID: ",
656                                                                 "^7net_type = %s, net_name = %s.\n"
657                                                         ),
658                                                         typestring,
659                                                         namestring
660                                                 ));
661                                                 notif_error = TRUE;
662                                         }
663                                 } 
664                                 else if(cpid != NO_MSG) { notif.nent_cpid = cpid; }
665                                 #endif
666
667
668                                 // ======================
669                                 //  Process Notif String
670                                 // ======================
671                                 #define SET_NOTIF_STRING(string,stringname) \
672                                         notif.nent_string = strzone(CCR( \
673                                                 Process_Notif_Line( \
674                                                         typeid, \
675                                                         (var_cvar > 1), \
676                                                         string, \
677                                                         typestring, \
678                                                         namestring, \
679                                                         stringname \
680                                                 )) \
681                                         );
682
683                                 if(GENTLE)
684                                 {
685                                         if(gentle != "") { SET_NOTIF_STRING(gentle, "GENTLE") }
686                                         else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
687                                 }
688                                 else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL") }
689                                 
690                                 #undef SET_NOTIF_STRING
691
692                                 // Check to make sure a string was chosen
693                                 if(notif.nent_string == "")
694                                 {
695                                         print(sprintf(
696                                                 strcat(
697                                                         "^1EMPTY NOTIFICATION: ",
698                                                         "^7net_type = %s, net_name = %s.\n"
699                                                 ),
700                                                 typestring,
701                                                 namestring
702                                         ));
703                                         notif_error = TRUE;
704                                 }
705                         }
706
707                         break;
708                 }
709
710                 case MSG_MULTI:
711                 {
712                         // Set MSG_MULTI string/float counts
713                         if((anncename == NO_MSG) && (infoname == NO_MSG) && (centername == NO_MSG))
714                         {
715                                 print(sprintf(
716                                         strcat(
717                                                 "^1NOTIFICATION WITH NO SUBCALLS: ",
718                                                 "^7net_type = %s, net_name = %s.\n"
719                                         ),
720                                         typestring,
721                                         namestring
722                                 ));
723                                 notif_error = TRUE;
724                         }
725                         else
726                         {
727                                 // announcements don't actually need any arguments, so lets not even count them.
728                                 if(anncename != NO_MSG) { notif.nent_msgannce = msg_annce_notifs[anncename - 1]; }
729                                 
730                                 float infoname_stringcount = 0, infoname_floatcount = 0;
731                                 float centername_stringcount = 0, centername_floatcount = 0;
732                                 
733                                 if(infoname != NO_MSG)
734                                 {
735                                         notif.nent_msginfo = msg_info_notifs[infoname - 1];
736                                         infoname_stringcount = notif.nent_msginfo.nent_stringcount;
737                                         infoname_floatcount = notif.nent_msginfo.nent_floatcount;
738                                 }
739                                 
740                                 if(centername != NO_MSG)
741                                 {
742                                         notif.nent_msgcenter = msg_center_notifs[centername - 1];
743                                         centername_stringcount = notif.nent_msgcenter.nent_stringcount;
744                                         centername_floatcount = notif.nent_msgcenter.nent_floatcount;
745                                 }
746                                 
747                                 // set the requirements of THIS notification to the totals of its subcalls
748                                 notif.nent_stringcount = max(infoname_stringcount, centername_stringcount);
749                                 notif.nent_floatcount = max(infoname_floatcount, centername_floatcount);
750                         }
751                         
752                         break;
753                 }
754
755                 case MSG_CHOICE:
756                 {
757                         break;
758                 }
759                 
760                 default: print("DAFUQ?\n"); notif_error = TRUE; break;
761         }
762
763         // now check to see if any errors happened 
764         if(notif_error)
765         {
766                 notif.nent_enabled = FALSE; // disable the notification so it can't cause trouble
767                 notif_global_error = TRUE; // throw the red flag that an error happened on init
768         }
769 }
770
771
772 // =========================================
773 //  Cvar Handling With 'dumpnotifs' Command
774 // =========================================
775
776 void Dump_Notifications(float fh, float alsoprint)
777 {
778         #define NOTIF_WRITE(a) { \
779                 fputs(fh, a); \
780                 if(alsoprint) { print(a); } }
781         #define NOTIF_WRITE_ENTITY(description) { \
782                 notif_msg = \
783                         sprintf( \
784                                 "seta notification_%s \"%d\" \"%s\"\n", \
785                                 e.nent_name, e.nent_default, description \
786                         ); \
787                 NOTIF_WRITE(notif_msg) }
788         #define NOTIF_WRITE_HARDCODED(cvar,default,description) { \
789                 notif_msg = \
790                         sprintf( \
791                                 "seta notification_%s \"%s\" \"%s\"\n", \
792                                 cvar, default, description \
793                         ); \
794                 NOTIF_WRITE(notif_msg) }
795
796         string notif_msg;
797         float i;
798         entity e;
799
800         // Note: This warning only applies to the notifications.cfg file that is output...
801
802         // You ARE supposed to manually edit this function to add i.e. hard coded
803         // notification variables for mutators or game modes or such and then
804         // regenerate the notifications.cfg file from the new code.
805
806         NOTIF_WRITE("// ********************************************** //\n");
807         NOTIF_WRITE("// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n");
808         NOTIF_WRITE("// **                                          ** //\n");
809         NOTIF_WRITE("// **  This file is automatically generated    ** //\n");
810         NOTIF_WRITE("// **  by code with the command 'dumpnotifs'.  ** //\n");
811         NOTIF_WRITE("// **                                          ** //\n");
812         NOTIF_WRITE("// **  If you add a new notification, please   ** //\n");
813         NOTIF_WRITE("// **  regenerate this file with that command  ** //\n");
814         NOTIF_WRITE("// **  making sure that the output matches     ** //\n");
815         NOTIF_WRITE("// **  with the lists and defaults in code.    ** //\n");
816         NOTIF_WRITE("// **                                          ** //\n");
817         NOTIF_WRITE("// ********************************************** //\n");
818
819         // These notifications will also append their string as a comment...
820         // This is not necessary, and does not matter if they vary between config versions,
821         // it is just a semi-helpful tool for those who want to manually change their user settings.
822
823         NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications (count = %d):\n", NOTIF_ANNCE_COUNT));
824         for(i = 1; i <= NOTIF_ANNCE_COUNT; ++i)
825         {
826                 e = Get_Notif_Ent(MSG_ANNCE, i);
827                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
828                 
829                 NOTIF_WRITE_ENTITY(
830                         "Notification control cvar: 0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled)"
831                 );
832         }
833
834         NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
835         for(i = 1; i <= NOTIF_INFO_COUNT; ++i)
836         {
837                 e = Get_Notif_Ent(MSG_INFO, i);
838                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
839                 
840                 NOTIF_WRITE_ENTITY(
841                         "Notification control cvar: 0 = off, 1 = print to console, "
842                         "2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
843                 );
844         }
845
846         NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
847         for(i = 1; i <= NOTIF_CENTER_COUNT; ++i)
848         {
849                 e = Get_Notif_Ent(MSG_CENTER, i);
850                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
851                 
852                 NOTIF_WRITE_ENTITY(
853                         "Notification control cvar: 0 = off, 1 = centerprint"
854                 );
855         }
856
857         NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications (count = %d):\n", NOTIF_MULTI_COUNT));
858         for(i = 1; i <= NOTIF_MULTI_COUNT; ++i)
859         {
860                 e = Get_Notif_Ent(MSG_MULTI, i);
861                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
862                 
863                 NOTIF_WRITE_ENTITY(
864                         "Notification control cvar: 0 = off, 1 = trigger subcalls"
865                 );
866         }
867
868         NOTIF_WRITE(sprintf("\n// MSG_CHOICE notifications (count = %d):\n", NOTIF_CHOICE_COUNT));
869         for(i = 1; i <= NOTIF_CHOICE_COUNT; ++i)
870         {
871                 e = Get_Notif_Ent(MSG_CHOICE, i);
872                 if not(e) { backtrace("Dump_Notifications(): Missing notification entity!\n"); return; }
873                 
874                 NOTIF_WRITE_ENTITY(
875                         "Notification control cvar: 0 = off, 1 = trigger option A subcall, 2 = trigger option B subcall"
876                 );
877         }
878
879         // edit these to match whichever cvars are used for specific notification options
880         NOTIF_WRITE("\n// HARD CODED notification variables:\n");
881         
882         NOTIF_WRITE_HARDCODED(
883                 "allow_chatboxprint", "1",
884                 "Allow notifications to be printed to chat box by setting notification cvar to 2 "
885                 "(You can also set this cvar to 2 to force ALL notifications to be printed to the chatbox)"
886         );
887         
888         NOTIF_WRITE_HARDCODED(
889                 "ctf_capture_verbose", "0",
890                 "Show extra information when someone captures a flag"
891         );
892         
893         NOTIF_WRITE_HARDCODED(
894                 "ctf_pickup_enemy_verbose", "0",
895                 "Show extra information if an enemy picks up a flag"
896         );
897         
898         NOTIF_WRITE_HARDCODED(
899                 "ctf_pickup_team_verbose", "0",
900                 "Show extra information if a team mate picks up a flag"
901         );
902         
903         NOTIF_WRITE_HARDCODED(
904                 "debug", "0",
905                 "Print extra debug information on all notification function calls "
906                 "(Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... "
907                 "0 = disabled, 1 = dprint, 2 = print"
908         );
909         
910         NOTIF_WRITE_HARDCODED(
911                 "errors_are_fatal", "1",
912                 "If a notification fails upon initialization, cause a Host_Error to stop the program"
913         );
914         
915         NOTIF_WRITE_HARDCODED(
916                 "frag_verbose", "1",
917                 "Show extra information when you frag someone (or when you are fragged"
918         );
919         
920         NOTIF_WRITE_HARDCODED(
921                 "item_centerprinttime", "1.5",
922                 "How long to show item information centerprint messages (like 'You got the Electro' or such)"
923         );
924         
925         NOTIF_WRITE_HARDCODED(
926                 "lifetime_mapload", "10",
927                 "Amount of time that notification entities last immediately at mapload (in seconds) "
928                 "to help prevent notifications from being lost on early init (like gamestart countdown)"
929         );
930         
931         NOTIF_WRITE_HARDCODED(
932                 "lifetime_runtime", "0.5",
933                 "Amount of time that notification entities last on the server during runtime (In seconds)"
934         );
935         
936         NOTIF_WRITE_HARDCODED(
937                 "server_allows_frag_verbose", "1",
938                 "Server side cvar for showing extra information in frag messages... 0 = no extra frag information, "
939                 "1 = frag information only in warmup, 2 = frag information allowed all the time"
940         );
941         
942         NOTIF_WRITE_HARDCODED(
943                 "server_allows_location", "1",
944                 "Server side cvar for allowing death messages to show location information too"
945         );
946         
947         NOTIF_WRITE_HARDCODED(
948                 "show_location", "0",
949                 "Append location information to MSG_INFO death/kill messages"
950         );
951         
952         NOTIF_WRITE_HARDCODED(
953                 "show_location_string", "",
954                 "Replacement string piped into sprintf, "
955                 "so you can do different messages like this: ' at the %s' or ' (near %s)'"
956         );
957         
958         NOTIF_WRITE_HARDCODED(
959                 "show_sprees", "1",
960                 "Print information about sprees in death/kill messages"
961         );
962         
963         NOTIF_WRITE_HARDCODED(
964                 "show_sprees_center", "1",
965                 "Show spree information in MSG_CENTER messages... "
966                 "0 = off, 1 = target (but only for first victim) and attacker"
967         );
968         
969         NOTIF_WRITE_HARDCODED(
970                 "show_sprees_center_specialonly", "1",
971                 "Don't show spree information in MSG_CENTER messages if it isn't an achievement"
972         );
973         
974         NOTIF_WRITE_HARDCODED(
975                 "show_sprees_info", "3",
976                 "Show spree information in MSG_INFO messages... "
977                 "0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker"
978         );
979         
980         NOTIF_WRITE_HARDCODED(
981                 "show_sprees_info_newline", "1",
982                 "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
983         );
984         
985         NOTIF_WRITE_HARDCODED(
986                 "show_sprees_info_specialonly", "1",
987                 "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
988         );
989
990         NOTIF_WRITE(sprintf(
991                 strcat(
992                         "\n// Notification counts (total = %d): ",
993                         "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d\n"
994                 ),
995                 (
996                         NOTIF_ANNCE_COUNT +
997                         NOTIF_INFO_COUNT +
998                         NOTIF_CENTER_COUNT +
999                         NOTIF_MULTI_COUNT +
1000                         NOTIF_CHOICE_COUNT
1001                 ),
1002                 NOTIF_ANNCE_COUNT,
1003                 NOTIF_INFO_COUNT,
1004                 NOTIF_CENTER_COUNT,
1005                 NOTIF_MULTI_COUNT,
1006                 NOTIF_CHOICE_COUNT
1007         ));
1008         
1009         return;
1010         #undef NOTIF_WRITE_HARDCODED
1011         #undef NOTIF_WRITE_ENTITY
1012         #undef NOTIF_WRITE
1013 }
1014
1015 #ifdef SVQC
1016 void Notification_GetCvars()
1017 {
1018         GetCvars_handleFloat(get_cvars_s, get_cvars_f, FRAG_VERBOSE, "notification_frag_verbose");
1019 }
1020 #endif
1021
1022
1023 // ===============================
1024 //  Frontend Notification Pushing
1025 // ===============================
1026
1027 #ifdef NOTIFICATIONS_DEBUG
1028 void Debug_Notification(string input)
1029 {
1030         switch(autocvar_notification_debug)
1031         {
1032                 case 1: { dprint(input); break; }
1033                 case 2: { print(input); break; }
1034         }
1035 }
1036 #endif
1037
1038 string Local_Notification_sprintf(
1039         string input, string args, 
1040         string s1, string s2, string s3, string s4,
1041         float f1, float f2, float f3, float f4)
1042 {
1043         #ifdef NOTIFICATIONS_DEBUG
1044         Debug_Notification(sprintf(
1045                 "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
1046                 MakeConsoleSafe(input),
1047                 args,
1048                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1049                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1050         ));
1051         #endif
1052         
1053         string selected;
1054         float sel_num;
1055         for(sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
1056
1057         string tmp_s;
1058
1059         for(sel_num = 0;(args != "");)
1060         {
1061                 selected = car(args); args = cdr(args);
1062                 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
1063                 switch(strtolower(selected))
1064                 {
1065                         #define ARG_CASE(prog,selected,result) \
1066                                 #ifdef CSQC \
1067                                         #if (prog != ARG_SV) && (prog != ARG_DC) \
1068                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1069                                         #endif \
1070                                 #else \
1071                                         #if (prog != ARG_CS) && (prog != ARG_DC) \
1072                                                 case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1073                                         #endif \
1074                                 #endif
1075                         NOTIF_ARGUMENT_LIST
1076                         #undef ARG_CASE
1077                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
1078                 }
1079         }
1080         return sprintf(strcat(input, "\n"), arg_slot[0], arg_slot[1], arg_slot[2], arg_slot[3], arg_slot[4], arg_slot[5], arg_slot[6]);
1081 }
1082
1083 #ifdef CSQC
1084 void Local_Notification_sound(
1085         float soundchannel, string soundfile,
1086         float soundvolume, float soundposition)
1087 {
1088         if((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam)))
1089         {
1090                 #ifdef NOTIFICATIONS_DEBUG
1091                 Debug_Notification(sprintf(
1092                         "Local_Notification_sound(world, %f, '%s', %f, %f);\n",
1093                         soundchannel,
1094                         sprintf(
1095                                 "announcer/%s/%s.wav",
1096                                 autocvar_cl_announcer,
1097                                 soundfile
1098                         ),
1099                         soundvolume,
1100                         soundposition
1101                 ));
1102                 #endif
1103                 
1104                 sound(
1105                         world,
1106                         soundchannel,
1107                         sprintf(
1108                                 "announcer/%s/%s.wav",
1109                                 autocvar_cl_announcer,
1110                                 soundfile
1111                         ),
1112                         soundvolume,
1113                         soundposition
1114                 );
1115                 
1116                 if(prev_soundfile) { strunzone(prev_soundfile); }
1117                 prev_soundfile = strzone(soundfile);
1118                 prev_soundtime = time;
1119         }
1120         else
1121         {
1122                 #ifdef NOTIFICATIONS_DEBUG
1123                 Debug_Notification(sprintf(
1124                         strcat(
1125                                 "Local_Notification_sound(world, %f, '%s', %f, %f) ",
1126                                 "^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', timediff: %f, limit: %f\n"
1127                          ),
1128                         soundchannel,
1129                         sprintf(
1130                                 "announcer/%s/%s.wav",
1131                                 autocvar_cl_announcer,
1132                                 soundfile
1133                         ),
1134                         soundvolume,
1135                         soundposition,
1136                         prev_soundfile,
1137                         (time - prev_soundtime),
1138                         autocvar_cl_announcer_antispam
1139                 ));
1140                 #endif
1141         }
1142 }
1143
1144 void Local_Notification_HUD_Notify_Push(
1145         string icon, string hudargs,
1146         string s1, string s2, string s3, string s4)
1147 {
1148         string selected;
1149         float sel_num;
1150         arg_slot[0] = ""; arg_slot[1] = "";
1151
1152         for(sel_num = 0;(hudargs != "");)
1153         {
1154                 selected = car(hudargs); hudargs = cdr(hudargs);
1155                 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1156                 switch(strtolower(selected))
1157                 {
1158                         #define ARG_CASE(prog,selected,result) \
1159                                 #if (prog == ARG_CS_SV_HA) \
1160                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1161                                 #endif
1162                         NOTIF_ARGUMENT_LIST
1163                         #undef ARG_CASE
1164                         default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1165                 }
1166         }
1167         #ifdef NOTIFICATIONS_DEBUG
1168         Debug_Notification(sprintf(
1169                 "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s);\n",
1170                 icon,
1171                 hudargs,
1172                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1173                 MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
1174         ));
1175         #endif
1176         HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
1177 }
1178
1179 void Local_Notification_centerprint_generic(
1180         string input, string durcnt,
1181         float cpid, float f1, float f2)
1182 {
1183         string selected;
1184         float sel_num;
1185         arg_slot[0] = ""; arg_slot[1] = "";
1186
1187         for(sel_num = 0;(durcnt != "");)
1188         {
1189                 selected = car(durcnt); durcnt = cdr(durcnt);
1190                 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic")
1191                 switch(strtolower(selected))
1192                 {
1193                         #define ARG_CASE(prog,selected,result) \
1194                                 #if (prog == ARG_CS_SV_DC) || (prog == ARG_DC) \
1195                                         case selected: { arg_slot[sel_num] = result; ++sel_num; break; } \
1196                                 #endif
1197                         NOTIF_ARGUMENT_LIST
1198                         #undef ARG_CASE
1199                         default:
1200                         {
1201                                 if(ftos(stof(selected)) != "") { arg_slot[sel_num] = selected; ++sel_num; }
1202                                 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_generic") }
1203                                 break;
1204                         }
1205                 }
1206         }
1207         #ifdef NOTIFICATIONS_DEBUG
1208         Debug_Notification(sprintf(
1209                 "Local_Notification_centerprint_generic('%s^7', '%s', %d, %d, %d, %d);\n",
1210                 MakeConsoleSafe(input),
1211                 durcnt,
1212                 f1, f2,
1213                 stof(arg_slot[0]), stof(arg_slot[1])
1214         ));
1215         #endif
1216         centerprint_generic(cpid, input, stof(arg_slot[0]), stof(arg_slot[1]));
1217 }
1218 #endif
1219
1220 void Local_Notification(float net_type, float net_name, ...count)
1221 {
1222         // check supplied type and name for errors
1223         string checkargs = Notification_CheckArgs_TypeName(net_type, net_name);
1224         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Local_Notification: %s\n", checkargs)); return; }
1225
1226         entity notif = Get_Notif_Ent(net_type, net_name);
1227         if not(notif) { backtrace("Local_Notification: Could not find notification entity!\n"); return; }
1228         if not(notif.nent_enabled)
1229         {
1230                 #ifdef NOTIFICATIONS_DEBUG
1231                 Debug_Notification(sprintf(
1232                         "Local_Notification(%s, %s): Entity was disabled...\n",
1233                         Get_Notif_TypeName(net_type),
1234                         notif.nent_name
1235                 ));
1236                 #endif
1237                 return;
1238         }
1239         
1240         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1241         {
1242                 backtrace(sprintf(
1243                         strcat(
1244                                 "Not enough arguments for Local_Notification(%s, %s, ...)! ",
1245                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1246                                 "Check the definition and function call for accuracy...?\n"
1247                         ),
1248                         Get_Notif_TypeName(net_type), notif.nent_name,
1249                         notif.nent_stringcount, notif.nent_floatcount, count
1250                 ));
1251                 return;
1252         }
1253         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1254         {
1255                 backtrace(sprintf(
1256                         strcat(
1257                                 "Too many arguments for Local_Notification(%s, %s, ...)! ",
1258                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1259                                 "Check the definition and function call for accuracy...?\n"
1260                         ),
1261                         Get_Notif_TypeName(net_type), notif.nent_name,
1262                         notif.nent_stringcount, notif.nent_floatcount, count
1263                 ));
1264                 return;
1265         }
1266
1267         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1268         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1269         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1270         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1271         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1272         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1273         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1274         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1275
1276         #ifdef NOTIFICATIONS_DEBUG
1277         Debug_Notification(sprintf(
1278                 "Local_Notification(%s, %s, %s, %s);\n",
1279                 Get_Notif_TypeName(net_type),
1280                 notif.nent_name,
1281                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1282                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1283         ));
1284         #endif
1285         
1286         switch(net_type)
1287         {
1288                 case MSG_ANNCE:
1289                 {
1290                         #ifdef CSQC
1291                         Local_Notification_sound(
1292                                 notif.nent_channel,
1293                                 notif.nent_snd,
1294                                 notif.nent_vol,
1295                                 notif.nent_position
1296                         );
1297                         #else
1298                         backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
1299                         #endif
1300                         break;
1301                 }
1302                 
1303                 case MSG_INFO:
1304                 {
1305                         print(
1306                                 Local_Notification_sprintf(
1307                                         notif.nent_string,
1308                                         notif.nent_args, 
1309                                         s1, s2, s3, s4,
1310                                         f1, f2, f3, f4)
1311                         );
1312                         #ifdef CSQC 
1313                         if(notif.nent_icon != "")
1314                         {
1315                                 Local_Notification_HUD_Notify_Push(
1316                                         notif.nent_icon,
1317                                         notif.nent_hudargs,
1318                                         s1, s2, s3, s4);
1319                         } 
1320                         #endif 
1321                         break;
1322                 }
1323                 
1324                 #ifdef CSQC
1325                 case MSG_CENTER:
1326                 {
1327                         Local_Notification_centerprint_generic(
1328                                 Local_Notification_sprintf(
1329                                         notif.nent_string,
1330                                         notif.nent_args, 
1331                                         s1, s2, s3, s4,
1332                                         f1, f2, f3, f4),
1333                                 notif.nent_durcnt,
1334                                 notif.nent_cpid,
1335                                 f1, f2);
1336                         break;
1337                 }
1338                 #endif
1339                 
1340                 case MSG_MULTI:
1341                 {
1342                         if(notif.nent_msginfo)
1343                         if(notif.nent_msginfo.nent_enabled)
1344                         {
1345                                 Local_Notification_WOVA(
1346                                         MSG_INFO,
1347                                         notif.nent_msginfo.nent_id, 
1348                                         notif.nent_msginfo.nent_stringcount, 
1349                                         notif.nent_msginfo.nent_floatcount, 
1350                                         s1, s2, s3, s4,
1351                                         f1, f2, f3, f4);
1352                         }
1353                         #ifdef CSQC
1354                         if(notif.nent_msgannce)
1355                         if(notif.nent_msgannce.nent_enabled)
1356                         {
1357                                 Local_Notification_WOVA(
1358                                         MSG_ANNCE,
1359                                         notif.nent_msgannce.nent_id, 
1360                                         0, 0, 
1361                                         "", "", "", "",
1362                                         0, 0, 0, 0);
1363                         }
1364                         if(notif.nent_msgcenter)
1365                         if(notif.nent_msgcenter.nent_enabled)
1366                         {
1367                                 Local_Notification_WOVA(
1368                                         MSG_CENTER,
1369                                         notif.nent_msgcenter.nent_id, 
1370                                         notif.nent_msgcenter.nent_stringcount, 
1371                                         notif.nent_msgcenter.nent_floatcount, 
1372                                         s1, s2, s3, s4,
1373                                         f1, f2, f3, f4); 
1374                         }
1375                         #endif
1376                         break;
1377                 }
1378         }
1379 }
1380
1381 // WOVA = Without Variable Arguments 
1382 void Local_Notification_WOVA(
1383         float net_type, float net_name,
1384         float stringcount, float floatcount,
1385         string s1, string s2, string s3, string s4,
1386         float f1, float f2, float f3, float f4)
1387 {
1388         #define VARITEM(stringc,floatc,args) \
1389                 if((stringcount == stringc) && (floatcount == floatc)) \
1390                         { Local_Notification(net_type, net_name, args); return; }
1391         EIGHT_VARS_TO_VARARGS_VARLIST
1392         #undef VARITEM
1393         Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1394 }
1395
1396
1397 // =========================
1398 //  Notification Networking
1399 // =========================
1400
1401 #ifdef CSQC
1402 void Read_Notification(float is_new)
1403 {
1404         float net_type = ReadByte();
1405         float net_name = ReadShort();
1406
1407         entity notif;
1408
1409         if(net_type == MSG_CENTER_CPID)
1410         {
1411                 #ifdef NOTIFICATIONS_DEBUG
1412                 Debug_Notification(sprintf(
1413                         "Read_Notification(%d) at %f: net_type = %s, net_name = %d\n",
1414                         is_new,
1415                         time,
1416                         Get_Notif_TypeName(net_type),
1417                         net_name
1418                 ));
1419                 #endif
1420                 
1421                 if(is_new)
1422                 {
1423                         if(net_name == 0) { reset_centerprint_messages(); }
1424                         else if(net_name != NO_CPID)
1425                         {
1426                                 // in this case, net_name IS the cpid we want to kill
1427                                 centerprint_generic(net_name, "", 0, 0);
1428                         }
1429                         else
1430                         {
1431                                 backtrace(sprintf(
1432                                         "Read_Notification(%d) at %f: ^1TRIED TO KILL NO_CPID CENTERPRINT!\n",
1433                                         is_new,
1434                                         time
1435                                 ));
1436                         } 
1437                 }
1438         }
1439         else
1440         {
1441                 notif = Get_Notif_Ent(net_type, net_name);
1442                 if not(notif) { backtrace("Read_Notification: Could not find notification entity!\n"); return; }
1443
1444                 #ifdef NOTIFICATIONS_DEBUG
1445                 Debug_Notification(sprintf(
1446                         "Read_Notification(%d) at %f: net_type = %s, net_name = %s\n",
1447                         is_new,
1448                         time,
1449                         Get_Notif_TypeName(net_type),
1450                         notif.nent_name
1451                 ));
1452                 #endif
1453
1454                 string s1 = ((0 < notif.nent_stringcount) ? ReadString() : "");
1455                 string s2 = ((1 < notif.nent_stringcount) ? ReadString() : "");
1456                 string s3 = ((2 < notif.nent_stringcount) ? ReadString() : "");
1457                 string s4 = ((3 < notif.nent_stringcount) ? ReadString() : "");
1458                 float f1 = ((0 < notif.nent_floatcount) ? ReadLong() : 0);
1459                 float f2 = ((1 < notif.nent_floatcount) ? ReadLong() : 0);
1460                 float f3 = ((2 < notif.nent_floatcount) ? ReadLong() : 0);
1461                 float f4 = ((3 < notif.nent_floatcount) ? ReadLong() : 0);
1462         
1463                 if(is_new)
1464                 {
1465                         Local_Notification_WOVA(
1466                                 net_type, net_name,
1467                                 notif.nent_stringcount,
1468                                 notif.nent_floatcount,
1469                                 s1, s2, s3, s4,
1470                                 f1, f2, f3, f4);
1471                 }
1472         }
1473 }
1474 #endif
1475
1476 #ifdef SVQC
1477 void Net_Notification_Remove()
1478 {
1479         if not(self) { backtrace(sprintf("Net_Notification_Remove() at %f: Missing self!?\n", time)); return; }
1480         
1481         #ifdef NOTIFICATIONS_DEBUG
1482         Debug_Notification(sprintf(
1483                 "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
1484                 time,
1485                 ((self.nent_net_name == -1) ? "Killed" : "Removed"),
1486                 Get_Notif_TypeName(self.nent_net_type),
1487                 self.owner.nent_name
1488         ));
1489         #endif
1490         
1491         float i;
1492         for(i = 0; i < 4; ++i) { if(self.nent_strings[i]) { strunzone(self.nent_strings[i]); } }
1493         remove(self);
1494 }
1495
1496 float Net_Write_Notification(entity client, float sf)
1497 {
1498         if(Notification_ShouldSend(self.nent_broadcast, client, self.nent_client))
1499         {
1500                 float i;
1501                 WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1502                 WriteByte(MSG_ENTITY, self.nent_net_type);
1503                 WriteShort(MSG_ENTITY, self.nent_net_name);
1504                 for(i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } 
1505                 for(i = 0; i < self.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, self.nent_floats[i]); }
1506                 return TRUE;
1507         }
1508         else { return FALSE; }
1509 }
1510
1511 void Kill_Notification(
1512         float broadcast, entity client,
1513         float net_type, float net_name)
1514 {
1515         string checkargs = Notification_CheckArgs(broadcast, client, 1, 1);
1516         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Kill_Notification: %s\n", checkargs)); return; }
1517
1518         #ifdef NOTIFICATIONS_DEBUG
1519         Debug_Notification(sprintf(
1520                 "Kill_Notification(%d, '%s', %s, %d);\n",
1521                 broadcast,
1522                 client.netname,
1523                 (net_type ? Get_Notif_TypeName(net_type) : "0"),
1524                 net_name
1525         ));
1526         #endif
1527
1528         entity notif, net_notif;
1529         float killed_cpid = NO_CPID;
1530         
1531         switch(net_type)
1532         {
1533                 case 0:
1534                 {
1535                         killed_cpid = 0; // kill ALL centerprints
1536                         break;
1537                 }
1538                 
1539                 case MSG_CENTER:
1540                 {
1541                         if(net_name)
1542                         {
1543                                 entity notif = Get_Notif_Ent(net_type, net_name);
1544                                 if not(notif) { backtrace("Kill_Notification: Could not find notification entity!\n"); return; }
1545                                 
1546                                 if(notif.nent_cpid)
1547                                         killed_cpid = notif.nent_cpid;
1548                                 else
1549                                         killed_cpid = NO_CPID;
1550                         }
1551                         else
1552                         {
1553                                 killed_cpid = 0; // kill ALL centerprints
1554                         }
1555                         break;
1556                 }
1557
1558                 case MSG_CENTER_CPID:
1559                 {
1560                         killed_cpid = net_name;
1561                         break;
1562                 }
1563         }
1564
1565         if(killed_cpid != NO_CPID)
1566         {
1567                 net_notif = spawn();
1568                 net_notif.classname = "net_kill_notification";
1569                 net_notif.nent_broadcast = broadcast;
1570                 net_notif.nent_client = client;
1571                 net_notif.nent_net_type = MSG_CENTER_CPID;
1572                 net_notif.nent_net_name = killed_cpid;
1573                 Net_LinkEntity(net_notif, FALSE, autocvar_notification_lifetime_runtime, Net_Write_Notification);
1574         }
1575
1576         for(notif = world; (notif = find(notif, classname, "net_notification"));)
1577         {
1578                 if(net_type)
1579                 {
1580                         if((killed_cpid != NO_CPID) && (notif.nent_net_type == MSG_CENTER))
1581                         {
1582                                 if(notif.owner.nent_cpid == killed_cpid)
1583                                 {
1584                                         notif.nent_net_name = -1;
1585                                         notif.nextthink = time;
1586                                 }
1587                                 else { continue; } // we ARE looking for a specific CPID, don't kill everything else too
1588                         }
1589                         else if(notif.nent_net_type == net_type)
1590                         {
1591                                 if(net_name)
1592                                 {
1593                                         if(notif.nent_net_name == net_name) { notif.nent_net_name = -1; notif.nextthink = time; }
1594                                         else { continue; } // we ARE looking for a certain net_name, don't kill everything else too
1595                                 }
1596                                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1597                         }
1598                         else { continue; } // we ARE looking for a certain net_type, don't kill everything else too
1599                 }
1600                 else { notif.nent_net_name = -1; notif.nextthink = time; }
1601         }
1602 }
1603
1604 void Send_Notification(
1605         float broadcast, entity client,
1606         float net_type, float net_name,
1607         ...count)
1608 {
1609         // check supplied broadcast, target, type, and name for errors
1610         string checkargs = Notification_CheckArgs(broadcast, client, net_type, net_name);
1611         if(checkargs != "") { backtrace(sprintf("Incorrect usage of Send_Notification: %s\n", checkargs)); return; }
1612
1613         // retreive counts for the arguments of this notification
1614         entity notif = Get_Notif_Ent(net_type, net_name);
1615         if not(notif) { backtrace("Send_Notification: Could not find notification entity!\n"); return; }
1616
1617         if((notif.nent_stringcount + notif.nent_floatcount) > count)
1618         {
1619                 backtrace(sprintf(
1620                         strcat(
1621                                 "Not enough arguments for Send_Notification(%d, %s, %s, ...)! ",
1622                                 "stringcount(%d) + floatcount(%d) > count(%d)\n", 
1623                                 "Check the definition and function call for accuracy...?\n"
1624                         ),
1625                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1626                         notif.nent_stringcount, notif.nent_floatcount, count
1627                 ));
1628                 return;
1629         }
1630         else if((notif.nent_stringcount + notif.nent_floatcount) < count)
1631         {
1632                 backtrace(sprintf(
1633                         strcat(
1634                                 "Too many arguments for Send_Notification(%d, %s, %s, ...)! ",
1635                                 "stringcount(%d) + floatcount(%d) < count(%d)\n",
1636                                 "Check the definition and function call for accuracy...?\n"
1637                         ),
1638                         broadcast, Get_Notif_TypeName(net_type), notif.nent_name,
1639                         notif.nent_stringcount, notif.nent_floatcount, count
1640                 ));
1641                 return;
1642         }
1643
1644         string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1645         string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1646         string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1647         string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1648         float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1649         float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1650         float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1651         float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1652
1653         #ifdef NOTIFICATIONS_DEBUG
1654         Debug_Notification(sprintf(
1655                 "Send_Notification(%d, %s, %s, %s, %s);\n",
1656                 broadcast,
1657                 Get_Notif_TypeName(net_type),
1658                 notif.nent_name,
1659                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1660                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1661         ));
1662         #endif
1663
1664         if(
1665                 server_is_dedicated
1666                 &&
1667                 (
1668                         broadcast == NOTIF_ALL
1669                         ||
1670                         broadcast == NOTIF_ALL_EXCEPT
1671                 )
1672                 &&
1673                 !(
1674                         net_type == MSG_ANNCE
1675                         ||
1676                         net_type == MSG_CENTER
1677                 )
1678         )
1679         {
1680                 Local_Notification_WOVA(
1681                         net_type, net_name,
1682                         notif.nent_stringcount,
1683                         notif.nent_floatcount,
1684                         s1, s2, s3, s4,
1685                         f1, f2, f3, f4);
1686         }
1687
1688         if(net_type == MSG_CHOICE)
1689         {
1690                 // THIS GETS TRICKY... now we have to cycle through each possible player (checking broadcast)
1691                 // and then do an individual NOTIF_ONE_ONLY recursive call for each one depending on their option...
1692                 // It's slow, but it's better than the alternatives:
1693                 //   1. Constantly networking all info and letting client decide
1694                 //   2. Manually handling each separate call on per-usage basis (See old CTF usage of verbose)
1695                 entity found_choice; 
1696
1697                 #define RECURSE_FROM_CHOICE(ent,action) \
1698                         switch(ent.msg_choice_choices[net_name]) \
1699                         { \
1700                                 case 1: found_choice = notif.nent_choicea; \
1701                                 case 2: found_choice = notif.nent_choiceb; \
1702                                 default: action; \
1703                         } \
1704                         Send_Notification_WOVA( \
1705                                 NOTIF_ONE_ONLY, \
1706                                 ent, \
1707                                 found_choice.nent_net_type, \
1708                                 found_choice.nent_net_name, \
1709                                 s1, s2, s3, s4, \
1710                                 f1, f2, f3, f4);
1711
1712                 switch(broadcast)
1713                 {
1714                         case NOTIF_ONE_ONLY: // we can potentially save processing power with this broadcast method
1715                         {
1716                                 if(IS_REAL_CLIENT(client))
1717                                         { RECURSE_FROM_CHOICE(client, return) }
1718                                 break;
1719                         }
1720                         default:
1721                         {
1722                                 entity to;
1723                                 FOR_EACH_REALCLIENT(to)
1724                                         { if(Notification_ShouldSend(broadcast, to, client))
1725                                                 { RECURSE_FROM_CHOICE(to, continue) } }
1726                                 break;
1727                         }
1728                 }
1729         }
1730         else
1731         {
1732                 entity net_notif = spawn();
1733                 net_notif.owner = notif;
1734                 net_notif.classname = "net_notification";
1735                 net_notif.nent_broadcast = broadcast;
1736                 net_notif.nent_client = client;
1737                 net_notif.nent_net_type = net_type;
1738                 net_notif.nent_net_name = net_name;
1739                 net_notif.nent_stringcount = notif.nent_stringcount;
1740                 net_notif.nent_floatcount = notif.nent_floatcount;
1741                 
1742                 float i;
1743                 for(i = 0; i < net_notif.nent_stringcount; ++i)
1744                         { net_notif.nent_strings[i] = strzone(...(i, string)); }
1745                 for(i = 0; i < net_notif.nent_floatcount; ++i)
1746                         { net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float); }
1747
1748                 net_notif.think = Net_Notification_Remove;
1749                 net_notif.nextthink =
1750                         ((time > autocvar_notification_lifetime_mapload)
1751                         ?
1752                                 (time + autocvar_notification_lifetime_runtime)
1753                                 :
1754                                 autocvar_notification_lifetime_mapload
1755                         ); 
1756
1757                 Net_LinkEntity(net_notif, FALSE, 0, Net_Write_Notification);
1758         }
1759 }
1760
1761 // WOVA = Without Variable Arguments 
1762 void Send_Notification_WOVA(
1763         float broadcast, entity client,
1764         float net_type, float net_name,
1765         string s1, string s2, string s3, string s4,
1766         float f1, float f2, float f3, float f4)
1767 {
1768         entity notif = Get_Notif_Ent(net_type, net_name);
1769         
1770         #ifdef NOTIFICATIONS_DEBUG
1771         Debug_Notification(sprintf(
1772                 "Send_Notification_WOVA(%d, %s, %s, %s, %s);\n",
1773                 broadcast,
1774                 Get_Notif_TypeName(net_type),
1775                 notif.nent_name,
1776                 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1777                 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1778         ));
1779         #endif
1780         
1781         #define VARITEM(stringc,floatc,args) \
1782                 if((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1783                         { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1784         EIGHT_VARS_TO_VARARGS_VARLIST
1785         #undef VARITEM
1786         Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1787 }
1788 #endif // ifdef SVQC