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