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