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