]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/notifications.qc
More work on unifying team functions
[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         #undef MSG_CENTER_NOTIF
138
139 #define MSG_WEAPON_NOTIFICATIONS \
140         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"), "") \
141         #undef MSG_WEAPON_NOTIF
142
143
144 // ====================================
145 //  Initialization/Create Declarations
146 // ====================================
147
148 #define NOTIF_FIRST 1
149 #define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
150 float NOTIF_INFO_COUNT;
151 float NOTIF_CENTER_COUNT;
152 float NOTIF_WEAPON_COUNT;
153 float NOTIF_CPID_COUNT;
154
155 #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
156         ADD_CSQC_AUTOCVAR(name) \
157         float name; \
158         void RegisterNotification_##name() \
159         { \
160                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
161                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
162         } \
163         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
164
165 #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
166         ADD_CSQC_AUTOCVAR(name) \
167         float name; \
168         float cpid; \
169         void RegisterNotification_##name() \
170         { \
171                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
172                 SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
173                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
174         } \
175         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
176
177 #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
178         ADD_CSQC_AUTOCVAR(name) \
179         float name; \
180         void RegisterNotification_##name() \
181         { \
182                 SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
183                 CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
184         } \
185         ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
186
187 // NOW we actually activate the declarations
188 MSG_INFO_NOTIFICATIONS
189 MSG_CENTER_NOTIFICATIONS
190 MSG_WEAPON_NOTIFICATIONS
191
192
193 // ======================
194 //  Supporting Functions
195 // ======================
196
197 // select between the normal or the gentle message string based on client (or server) settings
198 string normal_or_gentle(string normal, string gentle)
199 {
200         #ifdef CSQC
201         if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
202         #else
203         if(autocvar_sv_gentle)
204         #endif
205                 return ((gentle != "") ? gentle : normal);
206         else
207                 return normal;
208 }
209
210 float notif_stringcount(string s1, string s2)
211 {
212         float stringcount;
213         if(s1 != NO_STR_ARG) ++stringcount;
214         if(s2 != NO_STR_ARG) ++stringcount;
215         return stringcount;
216 }
217
218 float notif_floatcount(float f1, float f2, float f3)
219 {
220         float floatcount;
221         if(f1 != NO_FL_ARG) ++floatcount;
222         if(f2 != NO_FL_ARG) ++floatcount;
223         if(f3 != NO_FL_ARG) ++floatcount;
224         return floatcount;
225 }
226
227 // get the actual name of a notification and return it as a string
228 string Get_Field_Value(float field, float net_type, float net_name)
229 {
230         string output;
231         
232         #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
233                 if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
234                 else if(field == F_STRNUM) { output = ftos(strnum); } \
235                 else if(field == F_FLNUM) { output = ftos(flnum); }
236         
237         switch(net_type)
238         {
239                 case MSG_INFO:
240                 {
241                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
242                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
243                         MSG_INFO_NOTIFICATIONS
244                         break;
245                 }
246                 case MSG_CENTER:
247                 {
248                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
249                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
250                         MSG_CENTER_NOTIFICATIONS
251                         break;
252                 }
253                 case MSG_WEAPON:
254                 {
255                         #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
256                                 { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
257                         MSG_WEAPON_NOTIFICATIONS
258                         break;
259                 }
260         }
261
262         #undef GET_FIELD_VALUE_OUTPUT
263         return output;
264 }
265
266 // team code replace
267 string TCR(string input, string teamcolor, string teamtext)
268 {
269         input = strreplace("^TC", teamcolor, input);
270         input = strreplace("^TT", teamtext, input);
271         return input;
272 }
273
274 // color code replace, place inside of sprintf and parse the string
275 string CCR(string input)
276 {
277         input = strreplace("^F1", "^2", input); // autocvar_notification_colors_F1 
278         input = strreplace("^F2", "^3", input); // autocvar_notification_colors_F2
279         input = strreplace("^K1", "^1", input); // autocvar_notification_colors_K1
280         input = strreplace("^K2", "^3", input); // autocvar_notification_colors_K2
281         input = strreplace("^BG", "^7", input); // autocvar_notification_colors_BG
282         input = strreplace("^N", "^7", input); // "none"-- reset to white
283         return input;
284 }
285
286
287 // ===============================
288 //  Frontend Notification Pushing
289 // ===============================
290
291 #ifdef CSQC
292 #define KN_MAX_ENTRIES 10
293 float kn_index;
294 float killnotify_times[KN_MAX_ENTRIES];
295 string killnotify_icon[KN_MAX_ENTRIES];
296 string killnotify_attackers[KN_MAX_ENTRIES];
297 string killnotify_victims[KN_MAX_ENTRIES];
298 // 0 = "Y [used by] X", 1 = "X [did action to] Y"
299 void HUD_Notify_Push(string icon, string attacker, string victim)
300 {
301         if(icon != "")
302         {
303                 --kn_index;
304                 if (kn_index == -1) { kn_index = KN_MAX_ENTRIES-1; }
305                 killnotify_times[kn_index] = time;
306
307                 // icon
308                 if(killnotify_icon[kn_index]) { strunzone(killnotify_icon[kn_index]); }
309                 killnotify_icon[kn_index] = strzone(icon);
310
311                 // attacker
312                 if(killnotify_attackers[kn_index]) { strunzone(killnotify_attackers[kn_index]); }
313                 killnotify_attackers[kn_index] = strzone(attacker);
314
315                 // victim
316                 if(killnotify_victims[kn_index]) { strunzone(killnotify_victims[kn_index]); }
317                 killnotify_victims[kn_index] = strzone(victim);
318         }
319 }
320
321 void Local_Notification(float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
322 {
323         switch(net_type)
324         {
325                 case MSG_INFO:
326                 {
327                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
328                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
329                                 { \
330                                         print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); \
331                                         if(strtolower(icon) != "") { HUD_Notify_Push(icon, hudargs); } \
332                                 } }
333                         MSG_INFO_NOTIFICATIONS
334                         break;
335                 }
336                 case MSG_CENTER:
337                 {
338                         #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
339                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
340                                 { \
341                                         centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); \
342                                 } }
343                         MSG_CENTER_NOTIFICATIONS
344                         break;
345                 }
346                 case MSG_WEAPON:
347                 {
348                         #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
349                                 { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) \
350                                 { \
351                                         print("unhandled\n"); \
352                                 } }
353                         MSG_WEAPON_NOTIFICATIONS
354                         break;
355                 }
356         }
357 }
358 #endif
359
360
361 // =========================
362 //  Notification Networking
363 // =========================
364
365 #ifdef CSQC
366 void Read_Notification(void)
367 {
368         float net_type = ReadByte();
369         float net_name = ReadShort();
370
371         float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
372         float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
373         
374         Local_Notification(net_type, net_name,
375                 ((stringcount >= 1) ? ReadString() : ""),
376                 ((stringcount == 2) ? ReadString() : ""),
377                 ((floatcount >= 1) ? ReadLong() : 0),
378                 ((floatcount >= 2) ? ReadLong() : 0),
379                 ((floatcount == 3) ? ReadLong() : 0));
380 }
381 #endif
382
383 #ifdef SVQC
384 void Send_Notification(entity client, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
385 {
386         if(net_type && net_name)
387         {
388                 //print("notification: ", Get_Field_Value(F_NAME, net_type, net_name), ": ", ftos(net_name), ".\n");
389
390                 float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
391                 float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
392                 
393                 if(notif_stringcount(s1, s2) > stringcount) { backtrace("Too many string arguments for notification!\n"); return; }
394                 if(notif_floatcount(f1, f2, f3) > floatcount) { backtrace("Too many float arguments for notification!\n"); return; }
395                 
396                 if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
397                 {
398                         // personal/direct notification sent to ONE person and their spectators
399                         msg_entity = client;
400                         WRITESPECTATABLE_MSG_ONE({
401                                 WriteByte(MSG_ONE, SVC_TEMPENTITY);
402                                 WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
403                                 WriteByte(MSG_ONE, net_type);
404                                 WriteShort(MSG_ONE, net_name);
405                                 if(stringcount >= 1) { WriteString(MSG_ONE, s1); }
406                                 if(stringcount == 2) { WriteString(MSG_ONE, s2); }
407                                 if(floatcount >= 1) { WriteLong(MSG_ONE, f1); }
408                                 if(floatcount >= 2) { WriteLong(MSG_ONE, f2); }
409                                 if(floatcount == 3) { WriteLong(MSG_ONE, f3); }
410                         });
411                 }
412                 else
413                 {
414                         // global notification sent to EVERYONE
415                         WriteByte(MSG_ALL, SVC_TEMPENTITY);
416                         WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
417                         WriteByte(MSG_ALL, net_type);
418                         WriteShort(MSG_ALL, net_name);
419                         if(stringcount >= 1) { WriteString(MSG_ALL, s1); }
420                         if(stringcount == 2) { WriteString(MSG_ALL, s2); }
421                         if(floatcount >= 1) { WriteLong(MSG_ALL, f1); }
422                         if(floatcount >= 2) { WriteLong(MSG_ALL, f2); }
423                         if(floatcount == 3) { WriteLong(MSG_ALL, f3); }
424                 }
425
426                 if(!server_is_local && (net_type == MSG_INFO))
427                 {
428                         #define MSG_INFO_NOTIF(name,strnum,flnum,args,hudargs,icon,normal,gentle) \
429                                 { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
430                         MSG_INFO_NOTIFICATIONS
431                 }
432         }
433         else { backtrace("Incorrect usage of Send_Notification!\n"); }
434 }
435
436 void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
437 {
438         entity tmp_entity;
439         FOR_EACH_REALCLIENT(tmp_entity)
440         {
441                 if(tmp_entity.classname == STR_PLAYER)
442                 if(tmp_entity.team == targetteam)
443                 if(tmp_entity != except)
444                 {
445                         Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
446                 }
447         }
448 }
449
450 // WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
451 void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
452 {
453         entity tmp_entity;
454         FOR_EACH_REALCLIENT(tmp_entity)
455         {
456                 if((tmp_entity.classname == STR_PLAYER) || spectators)
457                 if(tmp_entity != except)
458                 {
459                         Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
460                 }
461         }
462 }
463
464
465 // =============================
466 //  LEGACY NOTIFICATION SYSTEMS
467 // =============================
468
469 void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
470 {
471         WriteByte(MSG_ALL, SVC_TEMPENTITY);
472         WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
473         WriteString(MSG_ALL, s1);
474         WriteString(MSG_ALL, s2);
475         WriteString(MSG_ALL, s3);
476         WriteShort(MSG_ALL, msg);
477         WriteByte(MSG_ALL, type);
478 }
479
480 // Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
481 void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
482 {
483         if (clienttype(e) == CLIENTTYPE_REAL)
484         {
485                 msg_entity = e;
486                 WRITESPECTATABLE_MSG_ONE({
487                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
488                         WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
489                         WriteString(MSG_ONE, s1);
490                         WriteString(MSG_ONE, s2);
491                         WriteShort(MSG_ONE, msg);
492                         WriteByte(MSG_ONE, type);
493                 });
494         }
495 }
496
497 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
498 {
499         if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
500         {
501                 msg_entity = e;
502                 WRITESPECTATABLE_MSG_ONE({
503                         WriteByte(MSG_ONE, SVC_TEMPENTITY);
504                         WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
505                         WriteByte(MSG_ONE, id);
506                         WriteString(MSG_ONE, s);
507                         if (id != 0 && s != "")
508                         {
509                                 WriteByte(MSG_ONE, duration);
510                                 WriteByte(MSG_ONE, countdown_num);
511                         }
512                 });
513         }
514 }
515 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
516 {
517         Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
518 }
519 #endif