]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qh
Merge branch 'master' into terencehill/menu_optimization
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qh
1 #ifndef NOTIFICATIONS_H
2 #define NOTIFICATIONS_H
3
4 #include "command/all.qh"
5
6 #include "constants.qh"
7 #include "teams.qh"
8 #include "util.qh"
9
10 // ================================================
11 //  Unified notification system, written by Samual
12 //  Last updated: March, 2013
13 // ================================================
14
15 // main types/groups of notifications
16 const int MSG_ANNCE = 1; // "Global" AND "personal" announcer messages
17 const int MSG_INFO = 2; // "Global" information messages
18 const int MSG_CENTER = 3; // "Personal" centerprint messages
19 const int MSG_CENTER_CPID = 4; // Kill centerprint message
20 const int MSG_MULTI = 5; // Subcall MSG_INFO and/or MSG_CENTER notifications
21 const int MSG_CHOICE = 6; // Choose which subcall wrapper to activate
22
23 // negative confirmations
24 const int NO_MSG = -12345;  // allows various things to know when no information is added
25 const int NOTIF_ABORT = -1234;   // allows Send_Notification to safely abort sending
26
27 #define EIGHT_VARS_TO_VARARGS_VARLIST \
28     VARITEM(1, 0, s1) \
29     VARITEM(2, 0, XPD(s1, s2)) \
30     VARITEM(3, 0, XPD(s1, s2, s3)) \
31     VARITEM(4, 0, XPD(s1, s2, s3, s4)) \
32     VARITEM(0, 1, f1) \
33     VARITEM(1, 1, XPD(s1, f1)) \
34     VARITEM(2, 1, XPD(s1, s2, f1)) \
35     VARITEM(3, 1, XPD(s1, s2, s3, f1)) \
36     VARITEM(4, 1, XPD(s1, s2, s3, s4, f1)) \
37     VARITEM(0, 2, XPD(f1, f2)) \
38     VARITEM(1, 2, XPD(s1, f1, f2)) \
39     VARITEM(2, 2, XPD(s1, s2, f1, f2)) \
40     VARITEM(3, 2, XPD(s1, s2, s3, f1, f2)) \
41     VARITEM(4, 2, XPD(s1, s2, s3, s4, f1, f2)) \
42     VARITEM(0, 3, XPD(f1, f2, f3)) \
43     VARITEM(1, 3, XPD(s1, f1, f2, f3)) \
44     VARITEM(2, 3, XPD(s1, s2, f1, f2, f3)) \
45     VARITEM(3, 3, XPD(s1, s2, s3, f1, f2, f3)) \
46     VARITEM(4, 3, XPD(s1, s2, s3, s4, f1, f2, f3)) \
47     VARITEM(0, 4, XPD(f1, f2, f3, f4)) \
48     VARITEM(1, 4, XPD(s1, f1, f2, f3, f4)) \
49     VARITEM(2, 4, XPD(s1, s2, f1, f2, f3, f4)) \
50     VARITEM(3, 4, XPD(s1, s2, s3, f1, f2, f3, f4)) \
51     VARITEM(4, 4, XPD(s1, s2, s3, s4, f1, f2, f3, f4))
52
53 void Destroy_All_Notifications();
54 void Create_Notification_Entity(
55     float var_default,
56     float var_cvar,
57     float typeId,
58     float nameid,
59     string namestring,
60     int strnum,
61     int flnum,
62     /* MSG_ANNCE */
63     float channel,
64     string snd,
65     float vol,
66     float position,
67     /* MSG_INFO & MSG_CENTER */
68     string args,
69     string hudargs,
70     string icon,
71     float cpid,
72     string durcnt,
73     string normal,
74     string gentle,
75     /* MSG_MULTI */
76     float anncename,
77     float infoname,
78     float centername,
79     /* MSG_CHOICE */
80     float challow_def,
81     float challow_var,
82     float chtype,
83     float optiona,
84     float optionb);
85
86 void Dump_Notifications(float fh, float alsoprint);
87
88
89 GENERIC_COMMAND(dumpnotifs, "Dump all notifications into notifications_dump.txt")
90 {
91         switch(request)
92         {
93                 case CMD_REQUEST_COMMAND:
94                 {
95                         #ifndef MENUQC
96                         float fh, alsoprint = false;
97                         string filename = argv(1);
98
99                         if(filename == "")
100                         {
101                                 filename = "notifications_dump.cfg";
102                                 alsoprint = false;
103                         }
104                         else if(filename == "-")
105                         {
106                                 filename = "notifications_dump.cfg";
107                                 alsoprint = true;
108                         }
109                         fh = fopen(filename, FILE_WRITE);
110
111                         if(fh >= 0)
112                         {
113                                 Dump_Notifications(fh, alsoprint);
114                                 LOG_INFOF("Dumping notifications... File located in ^2data/data/%s^7.\n", filename);
115                                 fclose(fh);
116                         }
117                         else
118                         {
119                                 LOG_INFOF("^1Error: ^7Could not open file '%s'!\n", filename);
120                         }
121                         #else
122                         LOG_INFO(_("Notification dump command only works with cl_cmd and sv_cmd.\n"));
123                         #endif
124                         return;
125                 }
126
127                 default:
128                 case CMD_REQUEST_USAGE:
129                 {
130                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpnotifs [filename]"));
131                         LOG_INFO("  Where 'filename' is the file to write (default is notifications_dump.cfg),\n");
132                         LOG_INFO("  if supplied with '-' output to console as well as default,\n");
133                         LOG_INFO("  if left blank, it will only write to default.\n");
134                         return;
135                 }
136         }
137 }
138
139 #ifdef NOTIFICATIONS_DEBUG
140 void Debug_Notification(string input);
141 #endif
142
143 void Local_Notification(int net_type, int net_name, ...count);
144 void Local_Notification_WOVA(
145     float net_type, float net_name,
146     float stringcount, float floatcount,
147     string s1, string s2, string s3, string s4,
148     float f1, float f2, float f3, float f4);
149
150 #ifdef CSQC // CLIENT ONLY
151 string prev_soundfile;
152 float prev_soundtime;
153 #endif
154
155 #ifdef SVQC // SERVER ONLY
156 const float NOTIF_ONE = 1;
157 const float NOTIF_ONE_ONLY = 2;
158 const float NOTIF_TEAM = 3;
159 const float NOTIF_TEAM_EXCEPT = 4;
160 const float NOTIF_ALL = 5;
161 const float NOTIF_ALL_EXCEPT = 6;
162
163 void Kill_Notification(
164     float broadcast, entity client,
165     float net_type, float net_name);
166 void Send_Notification(
167     float broadcast, entity client,
168     float net_type, float net_name,
169     ...count);
170 void Send_Notification_WOVA(
171     float broadcast, entity client,
172     float net_type, float net_name,
173     float stringcount, float floatcount,
174     string s1, string s2, string s3, string s4,
175     float f1, float f2, float f3, float f4);
176 void Send_Notification_WOCOVA(
177     float broadcast, entity client,
178     float net_type, float net_name,
179     string s1, string s2, string s3, string s4,
180     float f1, float f2, float f3, float f4);
181 #endif
182
183 // ===========================
184 //  Special CVAR Declarations
185 // ===========================
186
187 // MAKE SURE THIS IS ALWAYS SYNCHRONIZED WITH THE DUMP
188 // NOTIFICATIONS FUNCTION IN THE .QC FILE!
189
190 #define NOTIF_ADD_AUTOCVAR(name,default) float autocvar_notification_##name = default;
191
192 float autocvar_notification_show_location = false;
193 string autocvar_notification_show_location_string = ""; //_(" at the %s");
194 float autocvar_notification_show_sprees = true;
195 float autocvar_notification_show_sprees_info = 3; // 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker
196 float autocvar_notification_show_sprees_info_newline = true;
197 float autocvar_notification_show_sprees_info_specialonly = true;
198 float autocvar_notification_errors_are_fatal = true;
199 float autocvar_notification_lifetime_runtime = 0.5;
200 float autocvar_notification_lifetime_mapload = 10;
201 float autocvar_notification_debug = false;
202
203 #ifdef SVQC
204 .float FRAG_VERBOSE;
205 void Notification_GetCvars();
206 float autocvar_notification_server_allows_location = 1; // 0 = no, 1 = yes
207 #else
208 float autocvar_notification_item_centerprinttime = 1.5;
209
210 // 0 = no, 1 = yes, 2 = forced on for all MSG_INFO notifs
211 // DISABLED IN CODE, BUT ENABLED IN CONFIG FOR COMPATIBILITY WITH OLD CLIENTS
212 float autocvar_notification_allow_chatboxprint = 0;
213
214 float autocvar_notification_show_sprees_center = true;
215 float autocvar_notification_show_sprees_center_specialonly = true;
216 #endif
217
218
219 // ============================
220 //  Notification Argument List
221 // ============================
222 /*
223  These arguments get replaced with the Local_Notification_sprintf
224  and other such functions found in notifications.qc to supply data
225  from networked notifications to their usage in sprintf... It
226  allows for more dynamic data to be inferred by the local
227  notification parser, so that the server does not have to network
228  anything too crazy on a per-client/per-situation basis.
229
230  Pay attention to the CSQC/SVQC relations, some of these are redefined
231  in slightly different ways for different programs, this is because the
232  server does a more conservative approach to the notifs than the client.
233
234  All arguments are swapped into strings, so be sure that your
235  sprintf usage matches with proper %s placement.
236
237  Argument descriptions:
238     s1-s4: string arguments to be literally swapped into sprintf
239     s2loc: s2 string of locations of deaths or other events
240     s3loc: s3 string of locations of deaths or other events
241     f1-f4: float arguments expanded into strings to be swapped into sprintf
242     f1p2dec: f1 float to string with 2 decimal places
243     f2p2dec: f2 float to string with 2 decimal places
244     f2primsec: f2 float primary or secondary selection for weapons
245     f3primsec: f3 float primary or secondary selection for weapons
246     f1secs: count_seconds of f1
247     f1ord: count_ordinal of f1
248     f1time: process_time of f1
249     f1race_time: mmssss of f1
250     f2race_time: mmssss of f2
251     race_col: color of race time/position (i.e. good or bad)
252     race_diff: show time difference between f2 and f3
253     missing_teams: show which teams still need players
254     pass_key: find the keybind for "passing" or "dropping" in CTF game mode
255     frag_ping: show the ping of a player
256     frag_stats: show health/armor/ping of a player
257     frag_pos: show score status and position in the match of a player
258     spree_cen: centerprint notif for kill spree/how many kills they have
259     spree_inf: info notif for kill spree/how many kills they have
260     spree_end: placed at the end of murder messages to show ending of sprees
261     spree_lost: placed at the end of suicide messages to show losing of sprees
262     item_wepname: return full name of a weapon from weaponid
263     item_wepammo: ammo display for weapon from string
264     item_centime: amount of time to display weapon message in centerprint
265     item_buffname: return full name of a buff from buffid
266     death_team: show the full name of the team a player is switching from
267     minigame1_name: return human readable name of a minigame from its id(s1)
268     minigame1_d: return descriptor name of a minigame from its id(s1)
269 */
270
271 const float NOTIF_MAX_ARGS = 7;
272 const float NOTIF_MAX_HUDARGS = 2;
273 const float NOTIF_MAX_DURCNT = 2;
274
275 string arg_slot[NOTIF_MAX_ARGS];
276
277 const float ARG_CS_SV_HA = 1; // enabled on CSQC, SVQC, and Hudargs
278 const float ARG_CS_SV_DC = 2; // enabled on CSQC, SVQC, and durcnt centerprint
279 const float ARG_CS_SV = 3; // enabled on CSQC and SVQC
280 const float ARG_CS = 4; // unique result to CSQC
281 const float ARG_SV = 5; // unique result to SVQC
282 const float ARG_DC = 6; // unique result to durcnt/centerprint
283
284 // todo possible idea.... declare how many floats/strings each arg needs, and then dynamically increment the input
285 // this way, we don't need to have duplicates like i.e. s2loc and s3loc?
286
287 string BUFF_NAME(int i);
288
289 #define NOTIF_ARGUMENT_LIST \
290     ARG_CASE(ARG_CS_SV_HA,  "s1",            s1) \
291     ARG_CASE(ARG_CS_SV_HA,  "s2",            s2) \
292     ARG_CASE(ARG_CS_SV_HA,  "s3",            s3) \
293     ARG_CASE(ARG_CS_SV_HA,  "s4",            s4) \
294     ARG_CASE(ARG_CS_SV,     "s2loc",         ((autocvar_notification_show_location && (s2 != "")) ? sprintf(( ((tmp_s = autocvar_notification_show_location_string) != "") ? tmp_s : _(" (near %s)") ), s2) : "")) \
295     ARG_CASE(ARG_CS_SV,     "s3loc",         ((autocvar_notification_show_location && (s3 != "")) ? sprintf(( ((tmp_s = autocvar_notification_show_location_string) != "") ? tmp_s : _(" (near %s)") ), s3) : "")) \
296     ARG_CASE(ARG_CS_SV_DC,  "f1",            ftos(f1)) \
297     ARG_CASE(ARG_CS_SV_DC,  "f2",            ftos(f2)) \
298     ARG_CASE(ARG_CS_SV,     "f3",            ftos(f3)) \
299     ARG_CASE(ARG_CS_SV,     "f4",            ftos(f4)) \
300     ARG_CASE(ARG_CS_SV,     "f1p2dec",       ftos_decimals(f1/100, 2)) \
301     ARG_CASE(ARG_CS_SV,     "f2p2dec",       ftos_decimals(f2/100, 2)) \
302     ARG_CASE(ARG_CS,        "f2primsec",     (f2 ? _("secondary") : _("primary"))) \
303     ARG_CASE(ARG_CS,        "f3primsec",     (f3 ? _("secondary") : _("primary"))) \
304     ARG_CASE(ARG_CS,        "f1secs",        count_seconds(f1)) \
305     ARG_CASE(ARG_CS_SV,     "f1ord",         count_ordinal(f1)) \
306     ARG_CASE(ARG_CS,        "f1time",        process_time(2, f1)) \
307     ARG_CASE(ARG_CS_SV_HA,  "f1race_time",   mmssss(f1)) \
308     ARG_CASE(ARG_CS_SV_HA,  "f2race_time",   mmssss(f2)) \
309     ARG_CASE(ARG_CS_SV_HA,  "f3race_time",   mmssss(f3)) \
310     ARG_CASE(ARG_CS_SV,     "race_col",      CCR(((f1 == 1) ? "^F1" : "^F2"))) \
311     ARG_CASE(ARG_CS_SV,     "race_diff",     ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
312     ARG_CASE(ARG_CS,        "missing_teams", notif_arg_missing_teams(f1)) \
313     ARG_CASE(ARG_CS,        "pass_key",      ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
314     ARG_CASE(ARG_CS,        "frag_ping",     notif_arg_frag_ping(true, f2)) \
315     ARG_CASE(ARG_CS,        "frag_stats",    notif_arg_frag_stats(f2, f3, f4)) \
316     /*ARG_CASE(ARG_CS,      "frag_pos",      ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : ""))*/ \
317     ARG_CASE(ARG_CS,        "spree_cen",     (autocvar_notification_show_sprees ? notif_arg_spree_cen(f1) : "")) \
318     ARG_CASE(ARG_CS_SV,     "spree_inf",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(1, input, s2, f2) : "")) \
319     ARG_CASE(ARG_CS_SV,     "spree_end",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
320     ARG_CASE(ARG_CS_SV,     "spree_lost",    (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
321     ARG_CASE(ARG_CS_SV,     "item_wepname",  Weapons_from(f1).m_name) \
322     ARG_CASE(ARG_CS_SV,     "item_buffname", BUFF_NAME(f1)) \
323     ARG_CASE(ARG_CS_SV,     "f3buffname",    BUFF_NAME(f3)) \
324     ARG_CASE(ARG_CS_SV,     "item_wepammo",  (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \
325     ARG_CASE(ARG_DC,        "item_centime",  ftos(autocvar_notification_item_centerprinttime)) \
326     ARG_CASE(ARG_SV,        "death_team",    Team_ColoredFullName(f1)) \
327     ARG_CASE(ARG_CS,        "death_team",    Team_ColoredFullName(f1 - 1)) \
328     ARG_CASE(ARG_CS_SV_HA,  "minigame1_name",find(world,netname,s1).descriptor.message) \
329     ARG_CASE(ARG_CS_SV_HA,  "minigame1_d",   find(world,netname,s1).descriptor.netname)
330
331 #define NOTIF_HIT_MAX(count,funcname) MACRO_BEGIN { \
332     if(sel_num == count) { backtrace(sprintf("%s: Hit maximum arguments!\n", funcname)); break; } \
333 } MACRO_END
334 #define NOTIF_HIT_UNKNOWN(token,funcname) { backtrace(sprintf("%s: Hit unknown token in selected string! '%s'\n", funcname, selected)); break; }
335
336 #define KILL_SPREE_LIST \
337     SPREE_ITEM(3, 03, _("TRIPLE FRAG! "), _("%s^K1 made a TRIPLE FRAG! %s^BG"), _("%s^K1 made a TRIPLE SCORE! %s^BG")) \
338     SPREE_ITEM(5, 05, _("RAGE! "), _("%s^K1 unlocked RAGE! %s^BG"), _("%s^K1 made FIVE SCORES IN A ROW! %s^BG")) \
339     SPREE_ITEM(10, 10, _("MASSACRE! "), _("%s^K1 started a MASSACRE! %s^BG"), _("%s^K1 made TEN SCORES IN A ROW! %s^BG")) \
340     SPREE_ITEM(15, 15, _("MAYHEM! "), _("%s^K1 executed MAYHEM! %s^BG"), _("%s^K1 made FIFTEEN SCORES IN A ROW! %s^BG")) \
341     SPREE_ITEM(20, 20, _("BERSERKER! "), _("%s^K1 is a BERSERKER! %s^BG"), _("%s^K1 made TWENTY SCORES IN A ROW! %s^BG")) \
342     SPREE_ITEM(25, 25, _("CARNAGE! "), _("%s^K1 inflicts CARNAGE! %s^BG"), _("%s^K1 made TWENTY FIVE SCORES IN A ROW! %s^BG")) \
343     SPREE_ITEM(30, 30, _("ARMAGEDDON! "), _("%s^K1 unleashes ARMAGEDDON! %s^BG"), _("%s^K1 made THIRTY SCORES IN A ROW! %s^BG"))
344
345 #ifdef CSQC
346 string notif_arg_frag_ping(float newline, float fping)
347 {
348     if(fping == NO_MSG)
349         return sprintf(CCR(_("%s(^F1Bot^BG)")), (newline ? "\n" : " "));
350     else
351         return sprintf(CCR(_("%s(Ping ^F1%d^BG)")), (newline ? "\n" : " "), fping);
352 }
353
354 string notif_arg_frag_stats(float fhealth, float farmor, float fping)
355 {
356     if (!(fhealth < 1))
357         return sprintf(CCR(_("\n(Health ^1%d^BG / Armor ^2%d^BG)%s")), fhealth, farmor, notif_arg_frag_ping(false, fping));
358     else
359         return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(false, fping));
360 }
361
362 string notif_arg_missing_teams(float f1)
363 {
364     return sprintf("%s%s%s%s",
365         ((f1 & 1) ?
366             sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_1), ((f1 & (2 + 4 + 8)) ? ", " : ""))
367             :
368             ""
369         ),
370         ((f1 & 2) ?
371             sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_2), ((f1 & (4 + 8)) ? ", " : ""))
372             :
373             ""
374         ),
375         ((f1 & 4) ?
376             sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_3), ((f1 & 8) ? ", " : ""))
377             :
378             ""
379         ),
380         ((f1 & 8) ?
381             Team_ColoredFullName(NUM_TEAM_4)
382             :
383             ""
384         )
385     );
386 }
387
388 string notif_arg_spree_cen(float spree)
389 {
390     // 0 = off, 1 = target (but only for first victim) and attacker
391     if(autocvar_notification_show_sprees_center)
392     {
393         if(spree > 1)
394         {
395             #define SPREE_ITEM(counta,countb,center,normal,gentle) \
396                 case counta: { return normal_or_gentle(center, sprintf(_("%d score spree! "), spree)); }
397
398             switch(spree)
399             {
400                 KILL_SPREE_LIST
401                 default:
402                 {
403                     if (!autocvar_notification_show_sprees_center_specialonly)
404                     {
405                         return
406                             sprintf(
407                                 normal_or_gentle(
408                                     _("%d frag spree! "),
409                                     _("%d score spree! ")
410                                 ),
411                                 spree);
412                     }
413                     else { return ""; } // don't show spree information if it isn't an achievement
414                 }
415             }
416
417             #undef SPREE_ITEM
418         }
419         else if(spree == -1) // first blood
420         {
421             return normal_or_gentle(_("First blood! "), _("First score! "));
422         }
423         else if(spree == -2) // first victim
424         {
425             return normal_or_gentle(_("First victim! "), _("First casualty! "));
426         }
427     }
428     return "";
429 }
430 #endif
431
432 string notif_arg_spree_inf(float type, string input, string player, float spree)
433 {
434     switch(type)
435     {
436         case 1: // attacker kill spree
437         {
438             // 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker
439             // this conditional (& 2) is true for 2 and 3
440             if(autocvar_notification_show_sprees_info & 2)
441             {
442                 #ifdef CSQC
443                 string spree_newline =
444                     ( autocvar_notification_show_sprees_info_newline ?
445                         ((substring(input, 0, 1) == "\{3}") ? "\n\{3}" : "\n") : "" );
446                 #else
447                 string spree_newline =
448                     (autocvar_notification_show_sprees_info_newline ? "\n" : "");
449                 #endif
450
451                 if(spree > 1)
452                 {
453                     #define SPREE_ITEM(counta,countb,center,normal,gentle) \
454                         case counta: { return sprintf(CCR(normal_or_gentle(normal, gentle)), player, spree_newline); }
455
456                     switch(spree)
457                     {
458                         KILL_SPREE_LIST
459                         default:
460                         {
461                             if (!autocvar_notification_show_sprees_info_specialonly)
462                             {
463                                 return
464                                     sprintf(
465                                         CCR(normal_or_gentle(
466                                             _("%s^K1 has %d frags in a row! %s^BG"),
467                                             _("%s^K1 made %d scores in a row! %s^BG")
468                                         )),
469                                         player,
470                                         spree,
471                                         spree_newline
472                                     );
473                             }
474                             else { return ""; } // don't show spree information if it isn't an achievement
475                         }
476                     }
477
478                     #undef SPREE_ITEM
479                 }
480                 else if(spree == -1) // firstblood
481                 {
482                     return
483                         sprintf(
484                             CCR(normal_or_gentle(
485                                 _("%s^K1 drew first blood! %s^BG"),
486                                 _("%s^K1 got the first score! %s^BG")
487                             )),
488                             player,
489                             spree_newline
490                         );
491                 }
492             }
493             break;
494         }
495
496         case -1: // kill spree ended
497         {
498             if((spree > 1) && (autocvar_notification_show_sprees_info & 1))
499             {
500                 return
501                     sprintf(normal_or_gentle(
502                         _(", ending their %d frag spree"),
503                         _(", ending their %d score spree")
504                         ),
505                         spree
506                     );
507             }
508             break;
509         }
510
511         case -2: // kill spree lost
512         {
513             if((spree > 1) && (autocvar_notification_show_sprees_info & 1))
514             {
515                 return
516                     sprintf(normal_or_gentle(
517                         _(", losing their %d frag spree"),
518                         _(", losing their %d score spree")
519                         ),
520                         spree
521                     );
522             }
523             break;
524         }
525     }
526     return "";
527 }
528
529
530 // ====================================
531 //  Initialization/Create Declarations
532 // ====================================
533
534 enum {
535     NO_CPID
536 ,   CPID_ASSAULT_ROLE
537 ,   CPID_ROUND
538 ,   CPID_CAMPCHECK
539 ,   CPID_CTF_CAPSHIELD
540 ,   CPID_CTF_LOWPRIO
541 ,   CPID_CTF_PASS
542 ,   CPID_STALEMATE
543 ,   CPID_NADES
544 ,   CPID_IDLING
545 ,   CPID_ITEM
546 ,   CPID_PREVENT_JOIN
547 ,   CPID_KEEPAWAY
548 ,   CPID_KEEPAWAY_WARN
549 ,   CPID_KEYHUNT
550 ,   CPID_KEYHUNT_OTHER
551 ,   CPID_LMS
552 ,   CPID_MISSING_TEAMS
553 ,   CPID_MISSING_PLAYERS
554 ,   CPID_INSTAGIB_FINDAMMO
555 ,   CPID_MOTD
556 ,   CPID_NIX
557 ,   CPID_ONSLAUGHT
558 ,   CPID_ONS_CAPSHIELD
559 ,   CPID_OVERTIME
560 ,   CPID_POWERUP
561 ,   CPID_RACE_FINISHLAP
562 ,   CPID_TEAMCHANGE
563 ,   CPID_TIMEOUT
564 ,   CPID_VEHICLES
565 ,   CPID_VEHICLES_OTHER
566 // always last
567 ,   NOTIF_CPID_COUNT
568 };
569 // notification counts
570 const float NOTIF_FIRST = 1;
571 float NOTIF_ANNCE_COUNT;
572 float NOTIF_INFO_COUNT;
573 float NOTIF_CENTER_COUNT;
574 float NOTIF_MULTI_COUNT;
575 float NOTIF_CHOICE_COUNT;
576
577 // notification limits -- INCREASE AS NECESSARY
578 const float NOTIF_ANNCE_MAX   = 400;
579 const float NOTIF_INFO_MAX    = 450;
580 const float NOTIF_CENTER_MAX  = 350;
581 const float NOTIF_MULTI_MAX   = 300;
582 const float NOTIF_CHOICE_MAX  = 50;
583
584 // notification entities
585 entity msg_annce_notifs[NOTIF_ANNCE_MAX];
586 entity msg_info_notifs[NOTIF_INFO_MAX];
587 entity msg_center_notifs[NOTIF_CENTER_MAX];
588 entity msg_multi_notifs[NOTIF_MULTI_MAX];
589 entity msg_choice_notifs[NOTIF_CHOICE_MAX];
590
591 // common notification entity values
592 .float nent_default;
593 .float nent_enabled;
594 .float nent_type;
595 .float nent_id;
596 .string nent_name;
597 .int nent_stringcount;
598 .int nent_floatcount;
599
600 // MSG_ANNCE entity values
601 .float nent_channel;
602 .string nent_snd;
603 .float nent_vol;
604 .float nent_position;
605
606 // MSG_INFO and MSG_CENTER entity values
607 .string nent_args; // used by both
608 .string nent_hudargs; // used by info
609 .string nent_icon; // used by info
610 .float nent_cpid; // used by center
611 .string nent_durcnt; // used by center
612 .string nent_string; // used by both
613
614 // MSG_MULTI entity values
615 .entity nent_msgannce;
616 .entity nent_msginfo;
617 .entity nent_msgcenter;
618
619 // MSG_CHOICE entity values
620 .float nent_challow_def;
621 .float nent_challow_var;
622 .entity nent_optiona;
623 .entity nent_optionb;
624
625 // networked notification entity values
626 .float nent_broadcast;
627 .entity nent_client;
628 .float nent_net_type;
629 .float nent_net_name;
630 .string nent_strings[4];
631 .float nent_floats[4];
632
633 // other notification properties
634 .float msg_choice_choices[NOTIF_CHOICE_MAX]; // set on each player containing MSG_CHOICE choices
635
636 // initialization error detection
637 float notif_error;
638 float notif_global_error;
639
640 #define MSG_ANNCE_NOTIF(default,name,channel,sound,volume,position) \
641     NOTIF_ADD_AUTOCVAR(name, default) \
642     float name; \
643     void RegisterNotification_##name() \
644     { \
645         SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_ANNCE_COUNT) \
646         CHECK_MAX_COUNT(name, NOTIF_ANNCE_MAX, NOTIF_ANNCE_COUNT, "MSG_ANNCE") \
647         Create_Notification_Entity( \
648             /* COMMON ======================== */ \
649             default,            /* var_default */ \
650             ACVNN(name),        /* var_cvar    */ \
651             MSG_ANNCE,          /* typeId      */ \
652             name,               /* nameid      */ \
653             strtoupper(#name),  /* namestring  */ \
654             NO_MSG,             /* strnum      */ \
655             NO_MSG,             /* flnum       */ \
656             /* ANNCE ============= */ \
657             channel,   /* channel  */ \
658             sound,     /* snd      */ \
659             volume,    /* vol      */ \
660             position,  /* position */ \
661             /* INFO & CENTER == */ \
662             "",      /* args    */ \
663             "",      /* hudargs */ \
664             "",      /* icon    */ \
665             NO_MSG,  /* cpid    */ \
666             "",      /* durcnt  */ \
667             "",      /* normal  */ \
668             "",      /* gentle  */ \
669             /* MULTI ============= */ \
670             NO_MSG,  /* anncename  */ \
671             NO_MSG,  /* infoname   */ \
672             NO_MSG,  /* centername */ \
673             /* MSG_CHOICE ========== */ \
674             NO_MSG,   /* challow_def */ \
675             NO_MSG,   /* challow_var */ \
676             NO_MSG,   /* chtype      */ \
677             NO_MSG,   /* optiona     */ \
678             NO_MSG);  /* optionb     */ \
679     } \
680     ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
681
682 #define MSG_INFO_NOTIF(default,name,strnum,flnum,args,hudargs,icon,normal,gentle) \
683     NOTIF_ADD_AUTOCVAR(name, default) \
684     int name; \
685     void RegisterNotification_##name() \
686     { \
687         SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
688         CHECK_MAX_COUNT(name, NOTIF_INFO_MAX, NOTIF_INFO_COUNT, "MSG_INFO") \
689         Create_Notification_Entity( \
690             /* COMMON ======================== */ \
691             default,            /* var_default */ \
692             ACVNN(name),        /* var_cvar    */ \
693             MSG_INFO,           /* typeId      */ \
694             name,               /* nameid      */ \
695             strtoupper(#name),  /* namestring  */ \
696             strnum,             /* strnum      */ \
697             flnum,              /* flnum       */ \
698             /* ANNCE =========== */ \
699             NO_MSG,  /* channel  */ \
700             "",      /* snd      */ \
701             NO_MSG,  /* vol      */ \
702             NO_MSG,  /* position */ \
703             /* INFO & CENTER === */ \
704             args,     /* args    */ \
705             hudargs,  /* hudargs */ \
706             icon,     /* icon    */ \
707             NO_MSG,   /* cpid    */ \
708             "",       /* durcnt  */ \
709             normal,   /* normal  */ \
710             gentle,   /* gentle  */ \
711             /* MULTI ============= */ \
712             NO_MSG,  /* anncename  */ \
713             NO_MSG,  /* infoname   */ \
714             NO_MSG,  /* centername */ \
715             /* CHOICE ============== */ \
716             NO_MSG,   /* challow_def */ \
717             NO_MSG,   /* challow_var */ \
718             NO_MSG,   /* chtype      */ \
719             NO_MSG,   /* optiona     */ \
720             NO_MSG);  /* optionb     */ \
721     } \
722     ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
723
724 .string nent_iconargs;
725 #define MULTIICON_INFO(default,name,strnum,flnum,args,hudargs,iconargs,icon,normal,gentle) \
726     NOTIF_ADD_AUTOCVAR(name, default) \
727     int name; \
728     void RegisterNotification_##name() \
729     { \
730         SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
731         CHECK_MAX_COUNT(name, NOTIF_INFO_MAX, NOTIF_INFO_COUNT, "MSG_INFO") \
732         Create_Notification_Entity( \
733             /* COMMON ======================== */ \
734             default,            /* var_default */ \
735             ACVNN(name),        /* var_cvar    */ \
736             MSG_INFO,           /* typeid      */ \
737             name,               /* nameid      */ \
738             strtoupper(#name),  /* namestring  */ \
739             strnum,             /* strnum      */ \
740             flnum,              /* flnum       */ \
741             /* ANNCE =========== */ \
742             NO_MSG,  /* channel  */ \
743             "",      /* snd      */ \
744             NO_MSG,  /* vol      */ \
745             NO_MSG,  /* position */ \
746             /* INFO & CENTER === */ \
747             args,     /* args    */ \
748             hudargs,  /* hudargs */ \
749             icon,     /* icon    */ \
750             NO_MSG,   /* cpid    */ \
751             "",       /* durcnt  */ \
752             normal,   /* normal  */ \
753             gentle,   /* gentle  */ \
754             /* MULTI ============= */ \
755             NO_MSG,  /* anncename  */ \
756             NO_MSG,  /* infoname   */ \
757             NO_MSG,  /* centername */ \
758             /* CHOICE ============== */ \
759             NO_MSG,   /* challow_def */ \
760             NO_MSG,   /* challow_var */ \
761             NO_MSG,   /* chtype      */ \
762             NO_MSG,   /* optiona     */ \
763             NO_MSG);  /* optionb     */ \
764         msg_info_notifs[name - 1].nent_iconargs = iconargs; \
765     } \
766     ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
767
768 #define MSG_CENTER_NOTIF(default,name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
769     NOTIF_ADD_AUTOCVAR(name, default) \
770     float name; \
771     void RegisterNotification_##name() \
772     { \
773         SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
774         CHECK_MAX_COUNT(name, NOTIF_CENTER_MAX, NOTIF_CENTER_COUNT, "MSG_CENTER") \
775         Create_Notification_Entity( \
776             /* COMMON ======================== */ \
777             default,            /* var_default */ \
778             ACVNN(name),        /* var_cvar    */ \
779             MSG_CENTER,         /* typeId      */ \
780             name,               /* nameid      */ \
781             strtoupper(#name),  /* namestring  */ \
782             strnum,             /* strnum      */ \
783             flnum,              /* flnum       */ \
784             /* ANNCE =========== */ \
785             NO_MSG,  /* channel  */ \
786             "",      /* snd      */ \
787             NO_MSG,  /* vol      */ \
788             NO_MSG,  /* position */ \
789             /* INFO & CENTER == */ \
790             args,    /* args    */ \
791             "",      /* hudargs */ \
792             "",      /* icon    */ \
793             cpid,    /* cpid    */ \
794             durcnt,  /* durcnt  */ \
795             normal,  /* normal  */ \
796             gentle,  /* gentle  */ \
797             /* MULTI ============= */ \
798             NO_MSG,  /* anncename  */ \
799             NO_MSG,  /* infoname   */ \
800             NO_MSG,  /* centername */ \
801             /* CHOICE ============== */ \
802             NO_MSG,   /* challow_def */ \
803             NO_MSG,   /* challow_var */ \
804             NO_MSG,   /* chtype      */ \
805             NO_MSG,   /* optiona     */ \
806             NO_MSG);  /* optionb     */ \
807     } \
808     ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
809
810 #define MSG_MULTI_NOTIF(default,name,anncename,infoname,centername) \
811     NOTIF_ADD_AUTOCVAR(name, default) \
812     int name; \
813     void RegisterNotification_##name() \
814     { \
815         SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_MULTI_COUNT) \
816         CHECK_MAX_COUNT(name, NOTIF_MULTI_MAX, NOTIF_MULTI_COUNT, "MSG_MULTI") \
817         Create_Notification_Entity( \
818             /* COMMON ======================== */ \
819             default,            /* var_default */ \
820             ACVNN(name),        /* var_cvar    */ \
821             MSG_MULTI,          /* typeId      */ \
822             name,               /* nameid      */ \
823             strtoupper(#name),  /* namestring  */ \
824             NO_MSG,             /* strnum      */ \
825             NO_MSG,             /* flnum       */ \
826             /* ANNCE =========== */ \
827             NO_MSG,  /* channel  */ \
828             "",      /* snd      */ \
829             NO_MSG,  /* vol      */ \
830             NO_MSG,  /* position */ \
831             /* INFO & CENTER == */ \
832             "",      /* args    */ \
833             "",      /* hudargs */ \
834             "",      /* icon    */ \
835             NO_MSG,  /* cpid    */ \
836             "",      /* durcnt  */ \
837             "",      /* normal  */ \
838             "",      /* gentle  */ \
839             /* MULTI ================= */ \
840             anncename,   /* anncename  */ \
841             infoname,    /* infoname   */ \
842             centername,  /* centername */ \
843             /* CHOICE ============== */ \
844             NO_MSG,   /* challow_def */ \
845             NO_MSG,   /* challow_var */ \
846             NO_MSG,   /* chtype      */ \
847             NO_MSG,   /* optiona     */ \
848             NO_MSG);  /* optionb     */ \
849     } \
850     ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
851
852 #define ACVNN(name) autocvar_notification_##name
853
854 #define MSG_CHOICE_NOTIF(default,challow,name,chtype,optiona,optionb) \
855     NOTIF_ADD_AUTOCVAR(name, default) \
856     NOTIF_ADD_AUTOCVAR(name##_ALLOWED, challow) \
857     float name; \
858     void RegisterNotification_##name() \
859     { \
860         SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CHOICE_COUNT) \
861         CHECK_MAX_COUNT(name, NOTIF_CHOICE_MAX, NOTIF_CHOICE_COUNT, "MSG_CHOICE") \
862         Create_Notification_Entity( \
863             /* COMMON ======================== */ \
864             default,            /* var_default */ \
865             ACVNN(name),        /* var_cvar    */ \
866             MSG_CHOICE,         /* typeId      */ \
867             name,               /* nameid      */ \
868             strtoupper(#name),  /* namestring  */ \
869             NO_MSG,             /* strnum      */ \
870             NO_MSG,             /* flnum       */ \
871             /* ANNCE =========== */ \
872             NO_MSG,  /* channel  */ \
873             "",      /* snd      */ \
874             NO_MSG,  /* vol      */ \
875             NO_MSG,  /* position */ \
876             /* INFO & CENTER == */ \
877             "",      /* args    */ \
878             "",      /* hudargs */ \
879             "",      /* icon    */ \
880             NO_MSG,  /* cpid    */ \
881             "",      /* durcnt  */ \
882             "",      /* normal  */ \
883             "",      /* gentle  */ \
884             /* MULTI ============= */ \
885             NO_MSG,  /* anncename  */ \
886             NO_MSG,  /* infoname   */ \
887             NO_MSG,  /* centername */ \
888             /* CHOICE ============================================= */ \
889             challow,                                 /* challow_def */ \
890             autocvar_notification_##name##_ALLOWED,  /* challow_var */ \
891             chtype,                                  /* chtype      */ \
892             optiona,                                 /* optiona     */ \
893             optionb);                                /* optionb     */ \
894     } \
895     ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
896
897 void RegisterNotifications_First()
898 {
899     notif_global_error = false;
900 }
901
902 void RegisterNotifications_Done()
903 {
904     if(notif_global_error)
905     {
906         // shit happened... stop the loading of the program now if this is unacceptable
907         if(autocvar_notification_errors_are_fatal)
908             LOG_FATAL("Notification initialization failed! Read above and fix the errors!\n");
909         else
910             LOG_SEVERE("Notification initialization failed! Read above and fix the errors!\n");
911     }
912 }
913
914 // NOW we actually activate the declarations
915 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotifications_First)
916 #include "notifications.inc"
917 ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotifications_Done)
918
919 STATIC_INIT(RegisterNotifications) { CALL_ACCUMULATED_FUNCTION(RegisterNotifications); }
920
921 #endif