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