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