]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
49504c936150654cde506be6e43a00cdbe756165
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / notifications.qc
1 // ================================================
2 //  Unified notification system, written by Samual
3 //  Last updated: September, 2012
4 // ================================================
5
6 // main types/groups of notifications
7 #define MSG_INFO 1 // "Global" information messages (sent to console, and notify panel if it has an icon)
8 #define MSG_CENTER 2 // "Personal" centerprint messages
9 #define MSG_WEAPON 3 // "Personal" weapon messages (like "You got the Nex", sent to weapon notify panel)
10
11 #define NO_STR_ARG ""
12 #define NO_FL_ARG -12345
13
14 #define F_NAME 1
15 #define F_STRNUM 2
16 #define F_FLNUM 3
17
18 // allow sending of notifications to also pass through to spectators (specifically for centerprints)
19 #ifdef SVQC
20 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
21 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
22 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
23 #endif
24
25 #define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
26 #ifdef CSQC
27 string got_commandkey;
28 #define ADD_CSQC_AUTOCVAR(name) var float autocvar_notification_##name = TRUE;
29 var float autocvar_notification_ctf_capture_verbose = TRUE;
30 var float autocvar_notification_ctf_pickup_team_verbose = TRUE;
31 var float autocvar_notification_ctf_pickup_enemy_verbose = TRUE;
32 #define CHECK_AUTOCVAR(name) if(autocvar_notification_##name)
33 #define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
34 #define PASS_KEY ((((got_commandkey = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(got_commandkey, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), got_commandkey) : "")
35 #else
36 #define ADD_CSQC_AUTOCVAR(name)
37 #endif
38
39
40 // ====================================
41 //  Notifications List and Information
42 // ====================================
43 /*
44  List of all notifications (including identifiers and display information)
45  Format: name, strnum, flnum, args, *icon/CPID, *durcnt, normal, gentle
46  Asterisked fields are not present in all notification types.
47  Specifications:
48     Name of notification
49     Number of STRING arguments (so that networking knows how many to send/receive)
50     Number of FLOAT arguments (so that networking knows how many to send/receive)
51     Arguments for sprintf(string, args), if no args needed then use ""
52     *Icon/CPID:
53       MSG_INFO: STRING: icon string name for the hud notify panel, "" if no icon is used
54       MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
55     *Duration/Countdown:
56       MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
57     Normal message (string for sprintf when gentle messages are NOT enabled)
58     Gentle message (string for sprintf when gentle messages ARE enabled)
59
60  Messages have ^F1, ^F2, and ^BG in them-- these are replaced
61  with colors according to the cvars the user has chosen.
62     ^F1 = highest priority, "primary"
63     ^F2 = next highest priority, "secondary"
64     ^BG = normal/less important priority, "tertiary"
65
66  Guidlines (please try and follow these):
67     -ALWAYS start the string with a color, preferably background.
68     -ALWAYS reset a color after a name (this way they don't set it for the whole string).
69     -NEVER re-declare an event twice.
70     -NEVER add or remove fields from the format, it SHOULD already work.
71     -MSG_INFO messages must ALWAYS end with a new line: \n
72     -Be clean and simple with your notification naming,
73      nothing too long for the name field... Abbreviations are your friend. :D
74     -Keep the spacing as clean as possible... if the arguments are abnormally long,
75       it's okay to go out of line a bit... but try and keep it clean still.
76     -Keep the notifications in alphabetical order.
77     ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
78 */
79
80 // weaponorder[f1].netname
81
82 #define MULTITEAM_INFO(prefix,teams,strnum,flnum,args,hudargs,icon,normal,gentle) \
83         MSG_INFO_NOTIF(prefix##RED, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_1)), TCR(normal, COL_TEAM_1, strtoupper(STR_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(STR_TEAM_1))) \
84         MSG_INFO_NOTIF(prefix##BLUE, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_2)), TCR(normal, COL_TEAM_2, strtoupper(STR_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(STR_TEAM_2))) \
85         #if teams >= 3 \
86                 MSG_INFO_NOTIF(prefix##YELLOW, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_3)), TCR(normal, COL_TEAM_3, strtoupper(STR_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(STR_TEAM_3))) \
87         #endif \
88         #if teams >= 4 \
89                 MSG_INFO_NOTIF(prefix##PINK, strnum, flnum, args, hudargs, sprintf(icon, strtolower(STR_TEAM_4)), TCR(normal, COL_TEAM_4, strtoupper(STR_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STR_TEAM_4))) \
90         #endif
91 #define MSG_INFO_NOTIFICATIONS \
92         MSG_INFO_NOTIF(INFO_EMPTY,                                                      0, 0, NO_STR_ARG, XPND2("", ""),                                        "", "", "") \
93         MULTITEAM_INFO(INFO_SCORES_, 4,                                         0, 0, NO_STR_ARG, XPND2("", ""),                                        "", _("^TC^TT ^BGteam scores!\n"), "") \
94         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_DROPPED_, 2,         0, 0, NO_STR_ARG, XPND2("", ""),                                        "", _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself\n"), "") \
95         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_DAMAGED_, 2,         0, 0, NO_STR_ARG, XPND2("", ""),                                        "", _("^BGThe ^TC^TT^BG flag was destroyed and returned to base\n"), "") \
96         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_SPEEDRUN_, 2,        0, 1, f1/100, XPND2("", ""),                                            "", _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself\n"), "") \
97         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_NEEDKILL_, 2,        0, 0, NO_STR_ARG, XPND2("", ""),                                        "", _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base\n"), "") \
98         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_ABORTRUN_, 2,        0, 0, NO_STR_ARG, XPND2("", ""),                                        "", _("^BGThe ^TC^TT^BG flag was returned to base by its owner\n"), "") \
99         MULTITEAM_INFO(INFO_CTF_FLAGRETURN_TIMEOUT_, 2,         0, 0, NO_STR_ARG, XPND2("", ""),                                        "", _("^BGThe ^TC^TT^BG flag has returned to the base\n"), "") \
100         MULTITEAM_INFO(INFO_CTF_PICKUP_, 2,                                     1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_taken", _("^BG%s^BG got the ^TC^TT^BG flag\n"), "") \
101         MULTITEAM_INFO(INFO_CTF_RETURN_, 2,                                     1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_returned", _("^BG%s^BG returned the ^TC^TT^BG flag\n"), "") \
102         MULTITEAM_INFO(INFO_CTF_LOST_, 2,                                       1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_lost", _("^BG%s^BG lost the ^TC^TT^BG flag\n"), "") \
103         MULTITEAM_INFO(INFO_CTF_CAPTURE_, 2,                            1, 0, s1, XPND2(s1, ""),                                                        "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag\n"), "") \
104         MULTITEAM_INFO(INFO_CTF_CAPTURE_TIME_, 2,                       1, 1, XPND2(s1, f1/100), XPND2(s1, ""),                         "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%.2f^BG seconds\n"), "") \
105         MULTITEAM_INFO(INFO_CTF_CAPTURE_BROKEN_, 2,                     2, 2, XPND4(s1, f1/100, s2, f2/100), XPND2(s1, ""),     "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%.2f^BG seconds, breaking ^BG%s^BG's previous record of ^F2%.2f^BG seconds\n"), "") \
106         MULTITEAM_INFO(INFO_CTF_CAPTURE_UNBROKEN_, 2,           2, 2, XPND4(s1, f1/100, s2, f2/100), XPND2(s1, ""),     "notify_%s_captured", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F2%.2f^BG seconds, failing to break ^BG%s^BG's previous record of ^F1%.2f^BG seconds\n"), "") \
107         #undef MSG_INFO_NOTIF
108
109 #define MULTITEAM_CENTER(prefix,teams,strnum,flnum,args,cpid,durcnt,normal,gentle) \
110         MSG_CENTER_NOTIF(prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(STR_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(STR_TEAM_1))) \
111         MSG_CENTER_NOTIF(prefix##BLUE, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_2, strtoupper(STR_TEAM_2)), TCR(gentle, COL_TEAM_2, strtoupper(STR_TEAM_2))) \
112         #if teams >= 3 \
113                 MSG_CENTER_NOTIF(prefix##YELLOW, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_3, strtoupper(STR_TEAM_3)), TCR(gentle, COL_TEAM_3, strtoupper(STR_TEAM_3))) \
114         #endif \
115         #if teams >= 4 \
116                 MSG_CENTER_NOTIF(prefix##PINK, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_4, strtoupper(STR_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STR_TEAM_4))) \
117         #endif
118 #define MSG_CENTER_NOTIFICATIONS \
119         MSG_CENTER_NOTIF(CENTER_EMPTY,                                                  0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), "", "") \
120         MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_SHIELDED,             0, 0, NO_STR_ARG,                               CPID_CTF_CAPSHIELD,             XPND2(0, 0), _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
121         MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_FREE,                 0, 0, NO_STR_ARG,                               CPID_CTF_CAPSHIELD,             XPND2(0, 0), _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
122         MULTITEAM_CENTER(CENTER_CTF_PASS_OTHER_, 2,                             2, 0, XPND2(s1, s2),                    CPID_CTF_PASS,                  XPND2(0, 0), _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
123         MULTITEAM_CENTER(CENTER_CTF_PASS_SENT_, 2,                              1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
124         MULTITEAM_CENTER(CENTER_CTF_PASS_RECEIVED_, 2,                  1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
125         MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTING,                    1, 0, s1,                                               CPID_CTF_PASS,                  XPND2(0, 0), _("^BGRequesting %s^BG to pass you the flag"), "") \
126         MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTED,                     1, 0, XPND2(s1, PASS_KEY),              CPID_CTF_PASS,                  XPND2(0, 0), _("^BG%s^BG requests you to pass the flag%s"), "") \
127         MULTITEAM_CENTER(CENTER_CTF_RETURN_, 2,                                 0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou returned the ^TC^TT^BG flag!"), "") \
128         MULTITEAM_CENTER(CENTER_CTF_CAPTURE_, 2,                                0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou captured the ^TC^TT^BG flag!"), "") \
129         MULTITEAM_CENTER(CENTER_CTF_PICKUP_, 2,                                 0, 0, NO_STR_ARG,                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYou got the ^TC^TT^BG flag!"), "") \
130         MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM,                                1, 0, s1,                                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
131         MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM_VERBOSE,                2, 0, XPND3(s1, s2, s1),                CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
132         MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY,                               1, 0, s1,                                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
133         MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY_VERBOSE,               2, 0, XPND3(s1, s2, s1),                CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!"), "") \
134         MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_CARRIER,                  0, 0, NO_STR_ARG,                               CPID_STALEMATE,                 XPND2(0, 0), _("^BGStalemate! Enemies can now see you on radar!"), "") \
135         MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_OTHER,                    0, 0, NO_STR_ARG,                               CPID_STALEMATE,                 XPND2(0, 0), _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "") \
136         MSG_CENTER_NOTIF(CENTER_CTF_FLAG_THROW_PUNISH,                  0, 1, f1,                                               CPID_CTF_LOWPRIO,               XPND2(0, 0), _("^BGToo many flag throws! Throwing disabled for %d seconds."), "") \
137         MSG_CENTER_NOTIF(CENTER_EMPTY,                                                  0, 0, NO_STR_ARG,                               NO_CPID,                                XPND2(0, 0), "", "") \
138         #undef MSG_CENTER_NOTIF
139
140 #define MSG_WEAPON_NOTIFICATIONS \
141         MSG_WEAPON_NOTIF(DEATH_MARBLES_LOST3, 2, 1, XPND3(s1, s2, f1), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
142         #undef MSG_WEAPON_NOTIF
143
144
145 // ====================================
146 //  Initialization/Create Declarations
147 // ====================================
148
149 #define NOTIF_FIRST 1
150 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
151 float NOTIF_INFO_COUNT;
152 float NOTIF_CENTER_COUNT;
153 float NOTIF_WEAPON_COUNT;
154 float NOTIF_CPID_COUNT;
155
156 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
157         ADD_CSQC_AUTOCVAR(name) \
158         float name; \
159         void RegisterNotification_##name() \
160         { \
161                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
162                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
163         } \
164         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
165
166 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
167         ADD_CSQC_AUTOCVAR(name) \
168         float name; \
169         float cpid; \
170         void RegisterNotification_##name() \
171         { \
172                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
173                 SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
174                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
175         } \
176         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
177
178 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
179         ADD_CSQC_AUTOCVAR(name) \
180         float name; \
181         void RegisterNotification_##name() \
182         { \
183                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
184                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
185         } \
186         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
187
188 // NOW we actually activate the declarations
189 MSG_INFO_NOTIFICATIONS
190 MSG_CENTER_NOTIFICATIONS
191 MSG_WEAPON_NOTIFICATIONS
192
193
194 // ======================
195 //  Supporting Functions
196 // ======================
197
198 // select between the normal or the gentle message string based on client (or server) settings
199 string normal_or_gentle(string normal, string gentle)
200 {
201         #ifndef MENUQC
202                 #ifdef CSQC
203                 if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
204                 #else
205                 if(autocvar_sv_gentle)
206                 #endif
207                         return ((gentle != "") ? gentle : normal);
208                 else
209                         return normal;
210         #else
211                 return normal;
212         #endif
213 }
214
215 float notif_stringcount(string s1, string s2)
216 {
217         float stringcount;
218         if(s1 != NO_STR_ARG) ++stringcount;
219         if(s2 != NO_STR_ARG) ++stringcount;
220         return stringcount;
221 }
222
223 float notif_floatcount(float f1, float f2, float f3)
224 {
225         float floatcount;
226         if(f1 != NO_FL_ARG) ++floatcount;
227         if(f2 != NO_FL_ARG) ++floatcount;
228         if(f3 != NO_FL_ARG) ++floatcount;
229         return floatcount;
230 }
231
232 // get the actual name of a notification and return it as a string
233 string Get_Field_Value(float field, float net_type, float net_name)
234 {
235         string output;
236         
237         #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
238                 if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
239                 else if(field == F_STRNUM) { output = ftos(strnum); } \
240                 else if(field == F_FLNUM) { output = ftos(flnum); }
241         
242         switch(net_type)
243         {
244                 case MSG_INFO:
245                 {
246                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
247                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
248                         MSG_INFO_NOTIFICATIONS
249                         break;
250                 }
251                 case MSG_CENTER:
252                 {
253                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
254                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
255                         MSG_CENTER_NOTIFICATIONS
256                         break;
257                 }
258                 case MSG_WEAPON:
259                 {
260                         #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
261                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
262                         MSG_WEAPON_NOTIFICATIONS
263                         break;
264                 }
265         }
266
267         #undef GET_FIELD_VALUE_OUTPUT
268         return output;
269 }
270
271 // team code replace
272 string TCR(string input, string teamcolor, string teamtext)
273 {
274         input = strreplace("^TC", teamcolor, input);
275         input = strreplace("^TT", teamtext, input);
276         return input;
277 }
278
279 // color code replace, place inside of sprintf and parse the string
280 string CCR(string input)
281 {
282         input = strreplace("^F1", "^2", input); // autocvar_notification_colors_F1 
283         input = strreplace("^F2", "^3", input); // autocvar_notification_colors_F2
284         input = strreplace("^K1", "^1", input); // autocvar_notification_colors_K1
285         input = strreplace("^K2", "^3", input); // autocvar_notification_colors_K2
286         input = strreplace("^BG", "^7", input); // autocvar_notification_colors_BG
287         input = strreplace("^N", "^7", input); // "none"-- reset to white
288         return input;
289 }
290
291
292 // =============================
293 //  Debug/Maintenance Functions
294 // =============================
295
296 #define NOTIF_Write(type,name,text) fputs(fh, (sprintf("seta %s 1 // %s - %s\n", name, type, strreplace("\n", "\\n", text))))
297 void Dump_Notifications(float fh)
298 {
299         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) { NOTIF_Write("MSG_INFO", VAR_TO_TEXT(name), normal); }
300         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) { NOTIF_Write("MSG_CENTER", VAR_TO_TEXT(name), normal); }
301         #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) { NOTIF_Write("MSG_WEAPON", VAR_TO_TEXT(name), normal); }
302         MSG_INFO_NOTIFICATIONS
303         MSG_CENTER_NOTIFICATIONS
304         MSG_WEAPON_NOTIFICATIONS
305         return;
306 }
307
308
309 // ===============================
310 //  Frontend Notification Pushing
311 // ===============================
312
313 #ifdef CSQC
314 #define KN_MAX_ENTRIES 10
315 float kn_index;
316 float killnotify_times[KN_MAX_ENTRIES];
317 string killnotify_icon[KN_MAX_ENTRIES];
318 string killnotify_attackers[KN_MAX_ENTRIES];
319 string killnotify_victims[KN_MAX_ENTRIES];
320 // 0 = "Y [used by] X", 1 = "X [did action to] Y"
321 void HUD_Notify_Push(string icon, string attacker, string victim)
322 {
323         if(icon != "")
324         {
325                 --kn_index;
326                 if (kn_index == -1) { kn_index = KN_MAX_ENTRIES-1; }
327                 killnotify_times[kn_index] = time;
328
329                 // icon
330                 if(killnotify_icon[kn_index]) { strunzone(killnotify_icon[kn_index]); }
331                 killnotify_icon[kn_index] = strzone(icon);
332
333                 // attacker
334                 if(killnotify_attackers[kn_index]) { strunzone(killnotify_attackers[kn_index]); }
335                 killnotify_attackers[kn_index] = strzone(attacker);
336
337                 // victim
338                 if(killnotify_victims[kn_index]) { strunzone(killnotify_victims[kn_index]); }
339                 killnotify_victims[kn_index] = strzone(victim);
340         }
341 }
342
343 void Local_Notification(float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
344 {
345         switch(net_type)
346         {
347                 case MSG_INFO:
348                 {
349                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
350                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
351                                 { \
352                                         print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); \
353                                         if(strtolower(icon) != "") { HUD_Notify_Push(icon, hudargs); } \
354                                 } }
355                         MSG_INFO_NOTIFICATIONS
356                         break;
357                 }
358                 case MSG_CENTER:
359                 {
360                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
361                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
362                                 { \
363                                         centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); \
364                                 } }
365                         MSG_CENTER_NOTIFICATIONS
366                         break;
367                 }
368                 case MSG_WEAPON:
369                 {
370                         #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
371                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
372                                 { \
373                                         print("unhandled\n"); \
374                                 } }
375                         MSG_WEAPON_NOTIFICATIONS
376                         break;
377                 }
378         }
379 }
380 #endif
381
382
383 // =========================
384 //  Notification Networking
385 // =========================
386
387 #ifdef CSQC
388 void Read_Notification(void)
389 {
390         float net_type = ReadByte();
391         float net_name = ReadShort();
392
393         float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
394         float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
395         
396         Local_Notification(net_type, net_name,
397                 ((stringcount >= 1) ? ReadString() : ""),
398                 ((stringcount == 2) ? ReadString() : ""),
399                 ((floatcount >= 1) ? ReadLong() : 0),
400                 ((floatcount >= 2) ? ReadLong() : 0),
401                 ((floatcount == 3) ? ReadLong() : 0));
402 }
403 #endif
404
405 #ifdef SVQC
406 void Send_Notification(entity client, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
407 {
408         if(net_type && net_name)
409         {
410                 //print("notification: ", Get_Field_Value(F_NAME, net_type, net_name), ": ", ftos(net_name), ".\n");
411
412                 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
413                 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
414                 
415                 if(notif_stringcount(s1, s2) > stringcount) { backtrace("Too many string arguments for notification!\n"); return; }
416                 if(notif_floatcount(f1, f2, f3) > floatcount) { backtrace("Too many float arguments for notification!\n"); return; }
417                 
418                 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
419                 {
420                         // personal/direct notification sent to ONE person and their spectators
421                         msg_entity = client;
422                         WRITESPECTATABLE_MSG_ONE({
423                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
424                                 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
425                                 WriteByte(MSG_ONE, net_type);
426                                 WriteShort(MSG_ONE, net_name);
427                                 if(stringcount >= 1) { WriteString(MSG_ONE, s1); }
428                                 if(stringcount == 2) { WriteString(MSG_ONE, s2); }
429                                 if(floatcount >= 1) { WriteLong(MSG_ONE, f1); }
430                                 if(floatcount >= 2) { WriteLong(MSG_ONE, f2); }
431                                 if(floatcount == 3) { WriteLong(MSG_ONE, f3); }
432                         });
433                 }
434                 else
435                 {
436                         // global notification sent to EVERYONE
437                         WriteByte(MSG_ALL, SVC_TEMPENTITY);
438                         WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
439                         WriteByte(MSG_ALL, net_type);
440                         WriteShort(MSG_ALL, net_name);
441                         if(stringcount >= 1) { WriteString(MSG_ALL, s1); }
442                         if(stringcount == 2) { WriteString(MSG_ALL, s2); }
443                         if(floatcount >= 1) { WriteLong(MSG_ALL, f1); }
444                         if(floatcount >= 2) { WriteLong(MSG_ALL, f2); }
445                         if(floatcount == 3) { WriteLong(MSG_ALL, f3); }
446                 }
447
448                 if(!server_is_local && (net_type == MSG_INFO))
449                 {
450                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
451                                 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
452                         MSG_INFO_NOTIFICATIONS
453                 }
454         }
455         else { backtrace("Incorrect usage of Send_Notification!\n"); }
456 }
457
458 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
459 {
460         entity tmp_entity;
461         FOR_EACH_REALCLIENT(tmp_entity)
462         {
463                 if(tmp_entity.classname == STR_PLAYER)
464                 if(tmp_entity.team == targetteam)
465                 if(tmp_entity != except)
466                 {
467                         Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
468                 }
469         }
470 }
471
472 // WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
473 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
474 {
475         entity tmp_entity;
476         FOR_EACH_REALCLIENT(tmp_entity)
477         {
478                 if((tmp_entity.classname == STR_PLAYER) || spectators)
479                 if(tmp_entity != except)
480                 {
481                         Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
482                 }
483         }
484 }
485
486
487 // =============================
488 //  LEGACY NOTIFICATION SYSTEMS
489 // =============================
490
491 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
492 {
493         WriteByte(MSG_ALL, SVC_TEMPENTITY);
494         WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
495         WriteString(MSG_ALL, s1);
496         WriteString(MSG_ALL, s2);
497         WriteString(MSG_ALL, s3);
498         WriteShort(MSG_ALL, msg);
499         WriteByte(MSG_ALL, type);
500 }
501
502 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
503 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
504 {
505         if (clienttype(e) == CLIENTTYPE_REAL)
506         {
507                 msg_entity = e;
508                 WRITESPECTATABLE_MSG_ONE({
509                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
510                         WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
511                         WriteString(MSG_ONE, s1);
512                         WriteString(MSG_ONE, s2);
513                         WriteShort(MSG_ONE, msg);
514                         WriteByte(MSG_ONE, type);
515                 });
516         }
517 }
518
519 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
520 {
521         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
522         {
523                 msg_entity = e;
524                 WRITESPECTATABLE_MSG_ONE({
525                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
526                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
527                         WriteByte(MSG_ONE, id);
528                         WriteString(MSG_ONE, s);
529                         if (id != 0 && s != "")
530                         {
531                                 WriteByte(MSG_ONE, duration);
532                                 WriteByte(MSG_ONE, countdown_num);
533                         }
534                 });
535         }
536 }
537 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
538 {
539         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
540 }
541 #endif