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