Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
all.qh
Go to the documentation of this file.
1#pragma once
2
4
5#include <common/constants.qh>
9#include <common/teams.qh>
10#include <common/util.qh>
11#include <common/weapons/all.qh>
12
13// Operator for bold notifications
14const string BOLD_OPERATOR = "^BOLD";
15
17ENUMCLASS(MSG)
19 CASE(MSG, ANNCE)
21 CASE(MSG, INFO)
23 CASE(MSG, CENTER)
25 CASE(MSG, MULTI)
27 CASE(MSG, CHOICE)
29 CASE(MSG, CENTER_KILL)
31
32// No need to create another entity field, re-use this one
33#define Get_Notif_Name(notif) notif.registered_id
34
35string Get_Notif_TypeName(MSG net_type)
36{
37 switch (net_type)
38 {
39 case MSG_ANNCE: return "MSG_ANNCE";
40 case MSG_INFO: return "MSG_INFO";
41 case MSG_CENTER: return "MSG_CENTER";
42 case MSG_MULTI: return "MSG_MULTI";
43 case MSG_CHOICE: return "MSG_CHOICE";
44 case MSG_CENTER_KILL: return "MSG_CENTER_KILL";
45 }
46 LOG_WARNF("Get_Notif_TypeName(%d): Improper net type!", ORDINAL(net_type));
47 return "";
48}
49
50ENUMCLASS(CPID)
51 CASE(CPID, ASSAULT_ROLE)
52 CASE(CPID, ROUND)
53 CASE(CPID, CAMPCHECK)
54 CASE(CPID, CTF_CAPSHIELD)
55 CASE(CPID, CTF_LOWPRIO)
56 CASE(CPID, CTF_PASS)
57 CASE(CPID, STALEMATE)
58 CASE(CPID, NADES)
59 CASE(CPID, IDLING)
60 CASE(CPID, REMOVE)
61 CASE(CPID, ITEM)
62 CASE(CPID, PREVENT_JOIN)
63 CASE(CPID, KEEPAWAY)
64 CASE(CPID, KEEPAWAY_WARN)
65 CASE(CPID, KEYHUNT)
66 CASE(CPID, KEYHUNT_OTHER)
67 CASE(CPID, LMS)
68 CASE(CPID, MISSING_PLAYERS)
69 CASE(CPID, INSTAGIB_FINDAMMO)
70 CASE(CPID, NIX)
71 CASE(CPID, ONSLAUGHT)
72 CASE(CPID, ONS_CAPSHIELD)
73 CASE(CPID, OVERTIME)
74 CASE(CPID, POWERUP)
75 CASE(CPID, RACE_FINISHLAP)
76 CASE(CPID, SURVIVAL)
77 CASE(CPID, TEAMCHANGE)
78 CASE(CPID, TIMEOUT)
79 CASE(CPID, TIMEIN)
80 CASE(CPID, VEHICLES)
81 CASE(CPID, VEHICLES_OTHER)
83 CASE(CPID, LAST)
84ENUMCLASS_END(CPID)
85
87
88// used for notification system multi-team identifiers
89#define APP_TEAM_NUM(num, prefix) (((num) == NUM_TEAM_1) ? prefix##_RED : (((num) == NUM_TEAM_2) ? prefix##_BLUE : (((num) == NUM_TEAM_3) ? prefix##_YELLOW : prefix##_PINK)))
90#define APP_NUM(num, prefix) ((num) ? APP_TEAM_NUM(num, prefix) : prefix##_NEUTRAL)
91
92#define EIGHT_VARS_TO_VARARGS_VARLIST(stringcount, floatcount) \
93 if (stringcount >= 0 && stringcount <= 4 && floatcount >= 0 && floatcount <= 4) \
94 switch ((floatcount) * 10 + stringcount) \
95 { /* tenths: number of floats, units: number of strings */ \
96 case 1: VARITEM( s1) \
97 case 2: VARITEM(XPD(s1, s2)) \
98 case 3: VARITEM(XPD(s1, s2, s3)) \
99 case 4: VARITEM(XPD(s1, s2, s3, s4)) \
100 case 10: VARITEM( f1) \
101 case 11: VARITEM(XPD(s1, f1)) \
102 case 12: VARITEM(XPD(s1, s2, f1)) \
103 case 13: VARITEM(XPD(s1, s2, s3, f1)) \
104 case 14: VARITEM(XPD(s1, s2, s3, s4, f1)) \
105 case 20: VARITEM(XPD( f1, f2)) \
106 case 21: VARITEM(XPD(s1, f1, f2)) \
107 case 22: VARITEM(XPD(s1, s2, f1, f2)) \
108 case 23: VARITEM(XPD(s1, s2, s3, f1, f2)) \
109 case 24: VARITEM(XPD(s1, s2, s3, s4, f1, f2)) \
110 case 30: VARITEM(XPD( f1, f2, f3)) \
111 case 31: VARITEM(XPD(s1, f1, f2, f3)) \
112 case 32: VARITEM(XPD(s1, s2, f1, f2, f3)) \
113 case 33: VARITEM(XPD(s1, s2, s3, f1, f2, f3)) \
114 case 34: VARITEM(XPD(s1, s2, s3, s4, f1, f2, f3)) \
115 case 40: VARITEM(XPD( f1, f2, f3, f4)) \
116 case 41: VARITEM(XPD(s1, f1, f2, f3, f4)) \
117 case 42: VARITEM(XPD(s1, s2, f1, f2, f3, f4)) \
118 case 43: VARITEM(XPD(s1, s2, s3, f1, f2, f3, f4)) \
119 case 44: VARITEM(XPD(s1, s2, s3, s4, f1, f2, f3, f4)) \
120 }
121
124 int var_default,
125 int var_cvar,
126 MSG typeId,
127 int teamnum);
129 int var_cvar,
130 /* MSG_ANNCE */
131 int channel,
132 string snd,
133 float vol,
134 float position,
135 float queuetime);
137 int var_cvar,
138 int strnum,
139 int flnum,
140 /* MSG_INFO & MSG_CENTER */
141 string args,
142 string hudargs,
143 string icon,
144 CPID cpid,
145 string durcnt,
146 string normal,
147 string gentle);
149 int var_cvar,
150 /* MSG_MULTI */
151 Notification anncename,
152 Notification infoname,
153 Notification centername);
155 int var_cvar,
156 /* MSG_CHOICE */
157 int challow_def,
158 int challow_var,
159 MSG chtype,
160 Notification optiona,
161 Notification optionb);
162
163void Dump_Notifications(int fh, bool alsoprint);
164
165#define DEFAULT_FILENAME "notifications_dump.cfg"
166// NOTE: dumpeffectinfo, dumpnotifs, dumpturrets and dumpweapons use similar code
167GENERIC_COMMAND(dumpnotifs, "Dump all notifications into " DEFAULT_FILENAME, false)
168{
169 switch (request)
170 {
172 {
173 #ifdef GAMEQC
174 string filename = argv(1);
175 bool alsoprint = false;
176 if (filename == "")
177 {
178 filename = DEFAULT_FILENAME;
179 alsoprint = false;
180 }
181 else if (filename == "-")
182 {
183 filename = DEFAULT_FILENAME;
184 alsoprint = true;
185 }
186 int fh = fopen(filename, FILE_WRITE);
187 if (fh >= 0)
188 {
189 Dump_Notifications(fh, alsoprint);
190 LOG_INFOF("Dumping notifications... File located in ^2data/data/%s^7.", filename);
191 fclose(fh);
192 }
193 else
194 LOG_INFOF("^1Error: ^7Could not open file '%s'!", filename);
195 #else
196 LOG_INFO("Notification dump command only works with cl_cmd and sv_cmd.");
197 #endif
198 return;
199 }
200 default:
202 LOG_HELP("Usage:^3 ", GetProgramCommandPrefix(), " dumpnotifs [<filename>]");
203 LOG_HELPF(" Where <filename> is the file to write (default is %s),", DEFAULT_FILENAME);
204 LOG_HELP(" if supplied with '-' output to console as well as default,");
205 LOG_HELP(" if left blank, it will only write to default.");
206 return;
207 }
208}
209#undef DEFAULT_FILENAME
210
211#ifdef NOTIFICATIONS_DEBUG
212int autocvar_notification_debug = 0;
213void Debug_Notification(string input)
214{
215 switch (autocvar_notification_debug)
216 {
217 case 1: LOG_TRACE(input); break;
218 case 2: LOG_INFO(input); break;
219 }
220}
221#endif
222
223void Local_Notification(MSG net_type, Notification net_name, ...count);
226 MSG net_type, Notification net_name,
227 string s1, string s2, string s3, string s4,
228 float f1, float f2, float f3, float f4);
229
230#ifdef CSQC
233#endif
234
235#ifdef SVQC
241#endif
242
243#ifdef SVQC
244ENUMCLASS(NOTIF)
246 CASE(NOTIF, ONE)
248 CASE(NOTIF, ONE_ONLY)
250 CASE(NOTIF, TEAM)
252 CASE(NOTIF, TEAM_EXCEPT)
254 CASE(NOTIF, ALL)
256 CASE(NOTIF, ALL_EXCEPT)
257ENUMCLASS_END(NOTIF)
258
260{
261 switch (broadcast)
262 {
263 case NOTIF_ONE: return "NOTIF_ONE";
264 case NOTIF_ONE_ONLY: return "NOTIF_ONE_ONLY";
265 case NOTIF_ALL_EXCEPT: return "NOTIF_ALL_EXCEPT";
266 case NOTIF_ALL: return "NOTIF_ALL";
267 case NOTIF_TEAM: return "NOTIF_TEAM";
268 case NOTIF_TEAM_EXCEPT: return "NOTIF_TEAM_EXCEPT";
269 }
270 LOG_WARNF("Get_Notif_BroadcastName(%d): Improper broadcast!", broadcast);
271 return "";
272}
273
275 NOTIF broadcast, entity client,
276 MSG net_type, CPID net_name);
278 NOTIF broadcast, entity client,
279 MSG net_type, Notification net_name,
280 ...count);
282 NOTIF broadcast, entity client,
283 MSG net_type, Notification net_name,
284 string s1, string s2, string s3, string s4,
285 float f1, float f2, float f3, float f4);
286#endif
287
288// ===========================
289// Special CVAR Declarations
290// ===========================
291
292// MAKE SURE THIS IS ALWAYS SYNCHRONIZED WITH THE DUMP
293// NOTIFICATIONS FUNCTION IN THE .QC FILE!
294
296string autocvar_notification_show_location_string = ""; //_(" at the %s");
298int autocvar_notification_show_sprees_info = 3; // 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker
302#ifdef SVQC
305#endif
306
307#ifdef SVQC
308void Notification_GetCvars(entity this, entity store);
310#else
311float autocvar_notification_item_centerprinttime = 1.5;
312
313// 0 = no, 1 = yes, 2 = forced on for all MSG_INFO notifs
314// DISABLED IN CODE, BUT ENABLED IN CONFIG FOR COMPATIBILITY WITH OLD CLIENTS
315int autocvar_notification_allow_chatboxprint = 0;
316
317bool autocvar_notification_show_sprees_center = true;
318bool autocvar_notification_show_sprees_center_specialonly = true;
319#endif
320
321
322// ============================
323// Notification Argument List
324// ============================
325/*
326 These arguments get replaced with the Local_Notification_sprintf
327 and other such functions found in all.qc to supply data
328 from networked notifications to their usage in sprintf... It
329 allows for more dynamic data to be inferred by the local
330 notification parser, so that the server does not have to network
331 anything too crazy on a per-client/per-situation basis.
332
333 Pay attention to the CSQC/SVQC relations, some of these are redefined
334 in slightly different ways for different programs, this is because the
335 server does a more conservative approach to the notifs than the client.
336
337 All arguments are swapped into strings, so be sure that your
338 sprintf usage matches with proper %s placement.
339
340 Argument descriptions:
341 s1-s4: string arguments to be literally swapped into sprintf
342 s2loc: s2 string of locations of deaths or other events
343 s3loc: s3 string of locations of deaths or other events
344 s4loc: s4 string of locations of deaths or other events
345 f1-f4: float arguments expanded into strings to be swapped into sprintf
346 f1dtime: f1 float to string with 2 decimal places
347 f2dtime: f2 float to string with 2 decimal places
348 f2primsec: f2 float primary or secondary selection for weapons
349 f3primsec: f3 float primary or secondary selection for weapons
350 f1secs: count_seconds of f1
351 f1points: point or points depending on f1
352 f1ord: count_ordinal of f1
353 f1time: process_time of f1
354 f1race_time: TIME_ENCODED_TOSTRING of f1
355 f2race_time: TIME_ENCODED_TOSTRING of f2
356 f3race_time: TIME_ENCODED_TOSTRING of f3
357 race_col: color of race time/position (i.e. good or bad)
358 race_diff: show time difference between f2 and f3
359 pass_key: find the keybind for "passing" or "dropping" in CTF gametype
360 nade_key: find the keybind for nade throwing
361 join_key: find the keybind for joining the game
362 frag_ping: show the ping of a player
363 frag_stats: show health/armor/ping of a player
364 frag_pos: show score status and position in the match of a player
365 spree_cen: centerprint notif for kill spree/how many kills they have
366 spree_inf: info notif for kill spree/how many kills they have
367 spree_end: placed at the end of murder messages to show ending of sprees
368 spree_lost: placed at the end of suicide messages to show losing of sprees
369 item_wepname: return full name of a weapon from weaponid
370 item_wepammo: ammo display for weapon from f1 and f2
371 item_centime: amount of time to display weapon message in centerprint
372 item_buffname: return full name of a buff from buffid f1
373 f3buffname: return full name of a buff from buffid f3
374 death_team: show the full name of the team a player is switching from
375 minigame1_name: return human readable name of a minigame from its id(s1)
376 minigame1_d: return descriptor name of a minigame from its id(s1)
377*/
378
379const int NOTIF_MAX_ARGS = 7;
380const int NOTIF_MAX_HUDARGS = 2;
381const int NOTIF_MAX_DURCNT = 2;
382
383#ifdef CSQC
384const int NOTIF_QUEUE_MAX = 10;
388
391
393
395string hash_replace(string full_msg, int ent_msg_num, string ent_msg, string attacker_name)
396{
397 int s_ofs = 0;
398 while (--ent_msg_num >= 0)
399 s_ofs += strstrofs(full_msg, "%s", s_ofs);
400 string col = find_last_color_code(substring(full_msg, 0, s_ofs));
401
402 int hash_ofs = strstrofs(ent_msg, "#", 0);
403 if (hash_ofs < 0)
404 ent_msg = strcat(ent_msg, " ", CCR("^BG"), attacker_name, col);
405 else
406 ent_msg = strreplace("#", strcat(CCR("^BG"), attacker_name, col), ent_msg);
407 return ent_msg;
408}
409
410#define HASH_REPLACE(full_msg, num, attacker_name) hash_replace(input, num, s##num, attacker_name)
411#endif // CSQC
412
414
415const int ARG_CS_SV_HA = 1;
416const int ARG_CS_SV_DC = 2;
417const int ARG_CS_SV = 3;
418const int ARG_CS = 4;
419const int ARG_SV = 5;
420const int ARG_DC = 6;
421
422// TODO possible idea.... declare how many floats/strings each arg needs, and then dynamically increment the input
423// this way, we don't need to have duplicates like i.e. s2loc and s3loc?
424
425#define NOTIF_ARGUMENT_LIST \
426 ARG_CASE(ARG_CS_SV_HA, "s1", s1) \
427 ARG_CASE(ARG_CS_SV_HA, "s2", s2) \
428 ARG_CASE(ARG_CS_SV_HA, "s3", s3) \
429 ARG_CASE(ARG_CS_SV_HA, "s4", s4) \
430 ARG_CASE(ARG_CS_SV, "s2loc", ((autocvar_notification_show_location && (s2 != "")) ? sprintf(( ((tmp_s = autocvar_notification_show_location_string) != "") ? tmp_s : _(" (near %s)") ), s2) : "")) \
431 ARG_CASE(ARG_CS_SV, "s3loc", ((autocvar_notification_show_location && (s3 != "")) ? sprintf(( ((tmp_s = autocvar_notification_show_location_string) != "") ? tmp_s : _(" (near %s)") ), s3) : "")) \
432 ARG_CASE(ARG_CS_SV, "s4loc", ((autocvar_notification_show_location && (s4 != "")) ? sprintf(( ((tmp_s = autocvar_notification_show_location_string) != "") ? tmp_s : _(" (near %s)") ), s4) : "")) \
433 ARG_CASE(ARG_CS_SV_DC, "f1", ftos(f1)) \
434 ARG_CASE(ARG_CS_SV_DC, "f2", ftos(f2)) \
435 ARG_CASE(ARG_CS_SV, "f3", ftos(f3)) \
436 ARG_CASE(ARG_CS_SV, "f4", ftos(f4)) \
437 ARG_CASE(ARG_CS_SV, "f1dtime", ftos_decimals(TIME_DECODE(f1), 2)) \
438 ARG_CASE(ARG_CS_SV, "f2dtime", ftos_decimals(TIME_DECODE(f2), 2)) \
439 ARG_CASE(ARG_CS, "f2primsec", (f2 ? _("secondary") : _("primary"))) \
440 ARG_CASE(ARG_CS, "f3primsec", (f3 ? _("secondary") : _("primary"))) \
441 ARG_CASE(ARG_CS, "f1secs", count_seconds(f1)) \
442 ARG_CASE(ARG_CS, "f2secs", count_seconds(f2)) \
443 ARG_CASE(ARG_CS, "f1points", count_points(f1)) \
444 ARG_CASE(ARG_CS_SV, "f1ord", count_ordinal(f1)) \
445 ARG_CASE(ARG_CS_SV, "f1time", process_time(2, f1)) \
446 ARG_CASE(ARG_CS_SV_HA, "f1race_time", TIME_ENCODED_TOSTRING(f1, true)) \
447 ARG_CASE(ARG_CS_SV_HA, "f2race_time", TIME_ENCODED_TOSTRING(f2, true)) \
448 ARG_CASE(ARG_CS_SV_HA, "f3race_time", TIME_ENCODED_TOSTRING(f3, true)) \
449 ARG_CASE(ARG_CS_SV, "race_col", CCR((f1 == 1) ? "^F1" : "^F2")) \
450 ARG_CASE(ARG_CS_SV, "race_diff", ((TIME_ENCODED_TOSTRING(fabs(f2 - f3), true) == "0.00") ? "^3[+0.0]" : (f2 > f3) ? sprintf("^1[+%s]", TIME_ENCODED_TOSTRING(f2 - f3, true)) : sprintf("^2[-%s]", TIME_ENCODED_TOSTRING(f3 - f2, true)))) \
451 ARG_CASE(ARG_CS, "pass_key", getcommandkey(_("drop flag"), "+use")) \
452 ARG_CASE(ARG_CS, "nade_key", getcommandkey(_("throw nade"), "weapon_drop")) \
453 ARG_CASE(ARG_CS, "join_key", getcommandkey(_("jump"), "+jump")) \
454 ARG_CASE(ARG_CS, "frag_ping", notif_arg_frag_ping(true, f2)) \
455 ARG_CASE(ARG_CS, "frag_stats", notif_arg_frag_stats(f2, f3, f4)) \
456 /*ARG_CASE(ARG_CS, "frag_pos", ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : ""))*/ \
457 ARG_CASE(ARG_CS, "spree_cen", (autocvar_notification_show_sprees ? notif_arg_spree_cen(f1) : "")) \
458 ARG_CASE(ARG_CS_SV, "spree_inf", (autocvar_notification_show_sprees ? notif_arg_spree_inf(1, input, s2, f2) : "")) \
459 ARG_CASE(ARG_CS_SV, "spree_end", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
460 ARG_CASE(ARG_CS_SV, "spree_lost", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
461 ARG_CASE(ARG_CS_SV, "item_wepname", REGISTRY_GET(Weapons, f1).m_name) \
462 ARG_CASE(ARG_CS_SV, "item_buffname", REGISTRY_GET(StatusEffects, f1).m_name) \
463 ARG_CASE(ARG_CS_SV, "item_wepammo", (f2 > 0 ? notif_arg_item_wepammo(f1, f2) : "")) \
464 ARG_CASE(ARG_DC, "item_centime", ftos(autocvar_notification_item_centerprinttime)) \
465 ARG_CASE(ARG_SV, "death_team", Team_ColoredFullName(f1)) \
466 ARG_CASE(ARG_CS, "death_team", Team_ColoredFullName(f1 - 1)) \
467 ARG_CASE(ARG_SV, "s3#s2", s3) \
468 ARG_CASE(ARG_SV, "#s2", s2) \
469 ARG_CASE(ARG_CS, "s3#s2", HASH_REPLACE(input, 3, s2)) \
470 ARG_CASE(ARG_CS, "#s2", "") \
471 ARG_CASE(ARG_CS_SV_HA, "minigame1_name",find(NULL, netname, s1).descriptor.message) \
472 ARG_CASE(ARG_CS_SV_HA, "minigame1_d", find(NULL, netname, s1).descriptor.netname)
473
474#define NOTIF_HIT_MAX(count, funcname) MACRO_BEGIN \
475 if (sel_num == count) \
476 { \
477 backtrace(sprintf("%s: Hit maximum arguments!\n", funcname)); \
478 break; \
479 } \
480MACRO_END
481
482#define NOTIF_HIT_UNKNOWN(token, funcname) \
483{ \
484 backtrace(sprintf("%s: Hit unknown token in selected string! '%s'\n", funcname, selected)); \
485 break; \
486}
487
488#define KILL_SPREE_LIST \
489 SPREE_ITEM(3, 03, _("TRIPLE FRAG!"), _("%s^K1 made a TRIPLE FRAG!"), _("%s^K1 made a TRIPLE SCORE!")) \
490 SPREE_ITEM(5, 05, _("RAGE!"), _("%s^K1 unlocked RAGE!"), _("%s^K1 made FIVE SCORES IN A ROW!")) \
491 SPREE_ITEM(10, 10, _("MASSACRE!"), _("%s^K1 started a MASSACRE!"), _("%s^K1 made TEN SCORES IN A ROW!")) \
492 SPREE_ITEM(15, 15, _("MAYHEM!"), _("%s^K1 executed MAYHEM!"), _("%s^K1 made FIFTEEN SCORES IN A ROW!")) \
493 SPREE_ITEM(20, 20, _("BERSERKER!"), _("%s^K1 is a BERSERKER!"), _("%s^K1 made TWENTY SCORES IN A ROW!")) \
494 SPREE_ITEM(25, 25, _("CARNAGE!"), _("%s^K1 inflicts CARNAGE!"), _("%s^K1 made TWENTY FIVE SCORES IN A ROW!")) \
495 SPREE_ITEM(30, 30, _("ARMAGEDDON!"), _("%s^K1 unleashes ARMAGEDDON!"), _("%s^K1 made THIRTY SCORES IN A ROW!"))
496
497#ifdef CSQC
498string notif_arg_frag_ping(bool newline, float fping)
499{
500 string s = newline ? "\n" : " ";
501 if (fping < 0)
502 return sprintf(CCR(_("%s(^F1Bot^BG)")), s);
503 else
504 return sprintf(CCR(_("%s(Ping ^F1%d^BG)")), s, fping);
505}
506
507string notif_arg_frag_stats(float fhealth, float farmor, float fping)
508{
509 string s = notif_arg_frag_ping(false, fping);
510 if (fhealth > 1)
511 return sprintf(CCR(_("\n(Health ^1%d^BG / Armor ^2%d^BG)%s")), fhealth, farmor, s);
512 else
513 return sprintf(CCR(_("\n(^F4Dead^BG)%s")), s);
514}
515
516string notif_arg_spree_cen(float spree)
517{
518 // 0 = off, 1 = target (but only for first victim) and attacker
519 if (autocvar_notification_show_sprees_center)
520 {
521 if (spree > 1)
522 {
523 #define SPREE_ITEM(counta, countb, center, normal, gentle) \
524 case counta: \
525 return strcat(normal_or_gentle(center, sprintf(_("%d score spree!"), spree)), " ");
526
527 switch (spree)
528 {
530 default:
531 if (!autocvar_notification_show_sprees_center_specialonly)
532 return sprintf(
534 _("%d frag spree!"),
535 _("%d score spree!")
536 ), " "),
537 spree);
538 else
539 return ""; // don't show spree information if it isn't an achievement
540 }
541 #undef SPREE_ITEM
542 }
543 else if (spree == -1) // first blood
544 return strcat(normal_or_gentle(_("First blood!"), _("First score!")), " ");
545 else if (spree == -2) // first victim
546 return strcat(normal_or_gentle(_("First victim!"), _("First casualty!")), " ");
547 }
548 return "";
549}
550#endif
551
552string notif_arg_spree_inf(float type, string input, string player, float spree)
553{
554 switch (type)
555 {
556 case 1: // attacker kill spree
557 {
558 // 0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker
559 // this conditional (& 2) is true for 2 and 3
561 {
562 #ifdef CSQC
564 ? ((substring(input, 0, 1) == "\{3}") ? "\n\{3}" : "\n")
565 : "";
566 #else
568 ? "\n"
569 : "";
570 #endif
571
572 if (spree > 1)
573 {
574 #define SPREE_ITEM(counta, countb, center, normal, gentle) \
575 case counta: \
576 return sprintf(CCR(strcat(normal_or_gentle(normal, gentle), " %s^BG")), \
577 player, spree_newline);
578
579 switch (spree)
580 {
582 default:
584 return sprintf(
586 _("%s^K1 has %d frags in a row!"),
587 _("%s^K1 made %d scores in a row!")
588 ), " %s^BG")),
589 player,
590 spree,
591 spree_newline
592 );
593 else
594 return ""; // don't show spree information if it isn't an achievement
595 }
596 #undef SPREE_ITEM
597 }
598 else if (spree == -1) // firstblood
599 return sprintf(
601 _("%s^K1 drew first blood!"),
602 _("%s^K1 got the first score!")
603 ), " %s^BG")),
604 player,
605 spree_newline
606 );
607 }
608 break;
609 }
610
611 case -1: // kill spree ended
612 if (spree > 1 && (autocvar_notification_show_sprees_info & 1))
613 return sprintf(normal_or_gentle(
614 _(", ending their %d frag spree"),
615 _(", ending their %d score spree")
616 ),
617 spree
618 );
619 break;
620
621 case -2: // kill spree lost
622 if (spree > 1 && (autocvar_notification_show_sprees_info & 1))
623 return sprintf(normal_or_gentle(
624 _(", losing their %d frag spree"),
625 _(", losing their %d score spree")
626 ),
627 spree
628 );
629 break;
630 }
631 return "";
632}
633
634string notif_arg_item_wepammo(float f1, float f2)
635{
637 if (wep.ammo_type == RES_NONE)
638 return ""; // doesn't use ammo
639
640 return sprintf(_(" (%s)"), count_ammo(f2, wep.ammo_type));
641}
642
643
644// ====================================
645// Initialization/Create Declarations
646// ====================================
647
648// common notification entity values
655
656// MSG_ANNCE entity values
658.string nent_snd;
659.float nent_vol;
662
663// MSG_INFO and MSG_CENTER entity values
664.string nent_args; // used by both
665.string nent_hudargs; // used by info
666.string nent_icon; // used by info
667.CPID nent_cpid; // used by center
668.string nent_durcnt; // used by center
669.string nent_string; // used by both
670
671// MSG_MULTI entity values
675
676// MSG_CHOICE entity values
681
682// networked notification entity values
683#ifdef SVQC
685#endif
689.string nent_strings[4];
690.float nent_floats[4];
691
692// ACVNN: autocvar notification name, ACVNNA: autocvar notification name _ALLOWED
693#ifdef CSQC
694 #define ACVNN(name, defaultvalue) autocvar_notification_##name
695 #define ACVNNA(name, defaultvalue) autocvar_notification_##name##_ALLOWED
696 #define NOTIF_ADD_AUTOCVAR(name, defaultvalue) int autocvar_notification_##name = defaultvalue;
697 #define NOTIF_ADD_AUTOCVAR_ALLOWED(name, defaultvalue) int autocvar_notification_##name##_ALLOWED = defaultvalue;
698#else
699 // SVQC doesn't need the autocvars, it can just use the default values
700 #define ACVNN(name, defaultvalue) (defaultvalue)
701 #define ACVNNA(name, defaultvalue) (defaultvalue)
702 #define NOTIF_ADD_AUTOCVAR(name, defaultvalue)
703 #define NOTIF_ADD_AUTOCVAR_ALLOWED(name, defaultvalue)
704#endif
705
706REGISTRY(Notifications, BITS(11))
707REGISTER_REGISTRY(Notifications)
708REGISTRY_SORT(Notifications)
709
710REGISTRY_DEFINE_GET(Notifications, NULL)
711STATIC_INIT(Notifications)
712{
713 FOREACH(Notifications, true, it.m_id = i);
714}
715REGISTRY_CHECK(Notifications)
716
717const int NOTIF_CHOICE_MAX = 20;
718// NOTE: a team choice is actually made of 4 choices (one per team) with the same nent_choice_idx
719// thus they are counted as 1 in nent_choice_count
723// initialization error detection
726
727STATIC_INIT_LATE(Notif_Choices)
728{
730 LOG_FATALF("Too many MSG_CHOICE notifications (%d), hit NOTIF_CHOICE_MAX (%d) limit",
732}
733
735{
736 if (!notif.nent_teamnum)
737 return Get_Notif_Name(notif);
738 return substring(Get_Notif_Name(notif), 0, -strlen(Static_Team_ColorName(notif.nent_teamnum)) - 2);
739}
740
741Notification Get_Notif_Ent(MSG net_type, int net_name)
742{
743 Notification it = REGISTRY_GET(Notifications, net_name);
744 if (it.nent_type != net_type)
745 {
746 LOG_WARNF("Get_Notif_Ent(%s (%d), %s (%d)): Improper net type '%s'!",
747 Get_Notif_TypeName(net_type), net_type,
748 Get_Notif_Name(it), net_name,
749 Get_Notif_TypeName(it.nent_type)
750 );
751 return NULL;
752 }
753 return it;
754}
755
756#define MSG_ANNCE_NOTIF_TEAM(teamnum, name, cvarname, defaultvalue, sound, channel, volume, position, queuetime) \
757 MSG_ANNCE_NOTIF_(teamnum, ANNCE_##name, ANNCE_##cvarname, defaultvalue, sound, channel, volume, position, queuetime)
758#define MSG_ANNCE_NOTIF(name, defaultvalue, sound, channel, volume, position, queuetime) \
759 NOTIF_ADD_AUTOCVAR(ANNCE_##name, defaultvalue) \
760 MSG_ANNCE_NOTIF_(0, ANNCE_##name, ANNCE_##name, defaultvalue, sound, channel, volume, position, queuetime)
761
762#define MSG_ANNCE_NOTIF_(teamnum, name, cvarname, defaultvalue, sound, channel, volume, position, queuetime) \
763 REGISTER(Notifications, name, m_id, new_pure(msg_annce_notification)) \
764 { \
765 Create_Notification_Entity(this, defaultvalue, ACVNN(cvarname, defaultvalue), MSG_ANNCE, teamnum); \
766 Create_Notification_Entity_Annce(this, ACVNN(cvarname, defaultvalue), \
767 channel, /* channel */ \
768 sound, /* snd */ \
769 volume, /* vol */ \
770 position, /* position */ \
771 queuetime); /* queuetime */ \
772 }
773
774#define MSG_INFO_NOTIF_TEAM(teamnum, name, cvarname, defaultvalue, strnum, flnum, args, hudargs, icon, normal, gentle) \
775 MSG_INFO_NOTIF_(teamnum, INFO_##name, INFO_##cvarname, defaultvalue, strnum, flnum, args, hudargs, icon, normal, gentle)
776
777#define MSG_INFO_NOTIF(name, defaultvalue, strnum, flnum, args, hudargs, icon, normal, gentle) \
778 NOTIF_ADD_AUTOCVAR(INFO_##name, defaultvalue) \
779 MSG_INFO_NOTIF_(0, INFO_##name, INFO_##name, defaultvalue, strnum, flnum, args, hudargs, icon, normal, gentle)
780
781#define MSG_INFO_NOTIF_(teamnum, name, cvarname, defaultvalue, strnum, flnum, args, hudargs, icon, normal, gentle) \
782 REGISTER(Notifications, name, m_id, new_pure(msg_info_notification)) \
783 { \
784 Create_Notification_Entity(this, defaultvalue, ACVNN(cvarname, defaultvalue), MSG_INFO, teamnum); \
785 Create_Notification_Entity_InfoCenter(this, ACVNN(cvarname, defaultvalue), strnum, flnum, \
786 args, /* args */ \
787 hudargs, /* hudargs */ \
788 icon, /* icon */ \
789 CPID_Null, /* cpid */ \
790 "", /* durcnt */ \
791 normal, /* normal */ \
792 gentle); /* gentle */ \
793 }
794
796#define MULTIICON_INFO(name, defaultvalue, strnum, flnum, args, hudargs, iconargs, icon, normal, gentle) \
797 MULTIICON_INFO_(INFO_##name, defaultvalue, strnum, flnum, args, hudargs, iconargs, icon, normal, gentle)
798
799#define MULTIICON_INFO_(name, defaultvalue, strnum, flnum, args, hudargs, iconargs, icon, normal, gentle) \
800 NOTIF_ADD_AUTOCVAR(name, defaultvalue) \
801 REGISTER(Notifications, name, m_id, new_pure(msg_info_notification)) \
802 { \
803 Create_Notification_Entity(this, defaultvalue, ACVNN(name, defaultvalue), MSG_INFO, 0); \
804 Create_Notification_Entity_InfoCenter(this, ACVNN(name, defaultvalue), strnum, flnum, \
805 args, /* args */ \
806 hudargs, /* hudargs */ \
807 icon, /* icon */ \
808 CPID_Null, /* cpid */ \
809 "", /* durcnt */ \
810 normal, /* normal */ \
811 gentle); /* gentle */ \
812 this.nent_iconargs = iconargs; \
813 }
814
815#define MSG_CENTER_NOTIF_TEAM(teamnum, name, cvarname, defaultvalue, strnum, flnum, args, cpid, durcnt, normal, gentle) \
816 MSG_CENTER_NOTIF_(teamnum, CENTER_##name, CENTER_##cvarname, defaultvalue, strnum, flnum, args, cpid, durcnt, normal, gentle)
817#define MSG_CENTER_NOTIF(name, defaultvalue, strnum, flnum, args, cpid, durcnt, normal, gentle) \
818 NOTIF_ADD_AUTOCVAR(CENTER_##name, defaultvalue) \
819 MSG_CENTER_NOTIF_(0, CENTER_##name, CENTER_##name, defaultvalue, strnum, flnum, args, cpid, durcnt, normal, gentle)
820
821#define MSG_CENTER_NOTIF_(teamnum, name, cvarname, defaultvalue, strnum, flnum, args, cpid, durcnt, normal, gentle) \
822 REGISTER(Notifications, name, m_id, new_pure(msg_center_notification)) \
823 { \
824 Create_Notification_Entity(this, defaultvalue, ACVNN(cvarname, defaultvalue), MSG_CENTER, teamnum); \
825 Create_Notification_Entity_InfoCenter(this, ACVNN(cvarname, defaultvalue), strnum, flnum, \
826 args, /* args */ \
827 "", /* hudargs */ \
828 "", /* icon */ \
829 cpid, /* cpid */ \
830 durcnt, /* durcnt */ \
831 normal, /* normal */ \
832 gentle); /* gentle */ \
833 }
834
835#define MSG_MULTI_NOTIF(name, defaultvalue, anncename, infoname, centername) \
836 NOTIF_ADD_AUTOCVAR(name, defaultvalue) \
837 REGISTER(Notifications, name, m_id, new_pure(msg_multi_notification)) \
838 { \
839 Create_Notification_Entity(this, defaultvalue, ACVNN(name, defaultvalue), MSG_MULTI, 0); \
840 Create_Notification_Entity_Multi(this, ACVNN(name, defaultvalue), \
841 anncename, /* anncename */ \
842 infoname, /* infoname */ \
843 centername); /* centername */ \
844 }
845
846#define MSG_CHOICE_NOTIF_TEAM(teamnum, name, cvarname, defaultvalue, challow, chtype, optiona, optionb) \
847 MSG_CHOICE_NOTIF_(teamnum, CHOICE_##name, CHOICE_##cvarname, defaultvalue, challow, chtype, optiona, optionb)
848#define MSG_CHOICE_NOTIF(name, defaultvalue, challow, chtype, optiona, optionb) \
849 NOTIF_ADD_AUTOCVAR(CHOICE_##name, defaultvalue) \
850 NOTIF_ADD_AUTOCVAR_ALLOWED(CHOICE_##name, challow) \
851 MSG_CHOICE_NOTIF_(0, CHOICE_##name, CHOICE_##name, defaultvalue, challow, chtype, optiona, optionb)
852
853#define MSG_CHOICE_NOTIF_(teamnum, name, cvarname, defaultvalue, challow, chtype, optiona, optionb) \
854 REGISTER(Notifications, name, m_id, new_pure(msg_choice_notification)) \
855 { \
856 this.nent_choice_idx = nent_choice_count; \
857 if (!teamnum || teamnum == NUM_TEAM_4) \
858 ++nent_choice_count; \
859 Create_Notification_Entity(this, defaultvalue, ACVNN(cvarname, defaultvalue), MSG_CHOICE, teamnum); \
860 Create_Notification_Entity_Choice(this, ACVNN(cvarname, defaultvalue), \
861 challow, /* challow_def */ \
862 ACVNNA(cvarname, challow), /* challow_var */ \
863 chtype, /* chtype */ \
864 optiona, /* optiona */ \
865 optionb); /* optionb */ \
866 }
867
868REGISTRY_BEGIN(Notifications)
869{
870 notif_global_error = false;
871}
872
873REGISTRY_END(Notifications)
874{
876 return;
877 // shit happened... stop the loading of the program now if this is unacceptable
879 LOG_FATAL("Notification initialization failed! Read above and fix the errors!");
880 else
881 LOG_SEVERE("Notification initialization failed! Read above and fix the errors!");
882}
883
884#ifdef CSQC
886void ReplicateVars(bool would_destroy)
887{
888 if (!would_destroy)
889 FOREACH(Notifications, it.nent_type == MSG_CHOICE && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1),
890 {
891 string cvarname = strcat("notification_", Get_Notif_CvarName(it));
892 // NOTE: REPLICATE_SIMPLE can return;
893 REPLICATE_SIMPLE(it.cvar_value, cvarname);
894 });
895}
896#endif
897
898#include "all.inc"
#define BITS(n)
Definition bits.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
string() ReadString_Raw
limitations: NULL cannot be present elements can only be present once a maximum of IL_MAX lists can e...
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:37
Resource ammo_type
M: ammotype : main ammo type.
Definition weapon.qh:51
float count
Definition powerups.qc:22
const int CMD_REQUEST_COMMAND
Definition command.qh:3
const int CMD_REQUEST_USAGE
Definition command.qh:4
#define GENERIC_COMMAND(id, description, menubased)
Definition reg.qh:12
string count_ammo(int num, entity res)
Definition ammo.qc:5
string find_last_color_code(string s)
Definition util.qc:956
#define normal_or_gentle(normal, gentle)
Definition util.qh:250
const float FILE_WRITE
#define strstrofs
#define strlen
#define ORDINAL(it)
Definition enumclass.qh:25
#define ENUMCLASS_END(id)
Definition enumclass.qh:24
#define CASE(class, id)
Definition enumclass.qh:23
#define ENUMCLASS(id)
Definition enumclass.qh:22
#define GetProgramCommandPrefix()
Definition generic.qh:25
Weapons
Definition guide.qh:113
#define IL_NEW()
#define FOREACH(list, cond, body)
Definition iter.qh:19
#define USING(name, T)
Definition _all.inc:72
#define LOG_WARNF(...)
Definition log.qh:59
#define LOG_HELP(...)
Definition log.qh:83
#define LOG_FATALF(...)
Definition log.qh:51
#define LOG_INFO(...)
Definition log.qh:62
#define LOG_TRACE(...)
Definition log.qh:74
#define LOG_HELPF(...)
Definition log.qh:84
#define LOG_SEVERE(...)
Definition log.qh:54
#define LOG_FATAL(...)
Definition log.qh:50
#define LOG_INFOF(...)
Definition log.qh:63
void fclose(float fhandle)
string substring(string s, float start, float length)
float fopen(string filename, float mode)
string argv(float n)
s2 minigame1_name s2
Definition all.inc:458
s2 minigame1_name minigames s f2
Definition all.inc:573
spree_inf s1 s2 s3loc s2 s1
Definition all.inc:285
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
s2 minigame1_name minigames s f1
Definition all.inc:569
const int NOTIF_MAX_HUDARGS
Definition all.qh:380
const int ARG_CS_SV_HA
enabled on CSQC, SVQC, and Hudargs
Definition all.qh:415
void Local_Notification_Core(MSG net_type, Notification net_name, string s1, string s2, string s3, string s4, float f1, float f2, float f3, float f4)
glue for networking, forwards to Local_Notification
Definition all.qc:1341
#define Get_Notif_Name(notif)
main types/groups of notifications
Definition all.qh:33
const int NOTIF_MAX_DURCNT
Definition all.qh:381
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_name)
Definition all.qc:1464
#define KILL_SPREE_LIST
Definition all.qh:488
void Create_Notification_Entity_InfoCenter(entity notif, int var_cvar, int strnum, int flnum, string args, string hudargs, string icon, CPID cpid, string durcnt, string normal, string gentle)
Definition all.qc:436
string nent_string
Definition all.qh:669
CPID nent_cpid
Definition all.qh:667
entity nent_msginfo
Definition all.qh:673
int msg_choice_choices[NOTIF_CHOICE_MAX]
set on each player containing MSG_CHOICE choices
Definition all.qh:722
const string BOLD_OPERATOR
Definition all.qh:14
entity nent_msgannce
Definition all.qh:672
string arg_slot[NOTIF_MAX_ARGS]
Definition all.qh:413
#define DEFAULT_FILENAME
Definition all.qh:165
int nent_floatcount
Definition all.qh:653
int cvar_value
Definition all.qh:885
int nent_choice_idx
Definition all.qh:721
void ReplicateVars(bool would_destroy)
Definition all.qh:886
bool autocvar_notification_show_location
Definition all.qh:295
float nent_position
Definition all.qh:660
const int ARG_DC
unique result to durcnt/centerprint
Definition all.qh:420
float notif_queue_next_time
Definition all.qh:389
void Send_Notification_Core(NOTIF broadcast, entity client, MSG net_type, Notification net_name, string s1, string s2, string s3, string s4, float f1, float f2, float f3, float f4)
Definition all.qc:1646
int nent_stringcount
Definition all.qh:652
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1500
int nent_teamnum
Definition all.qh:654
entity notif_queue_entity[NOTIF_QUEUE_MAX]
Definition all.qh:385
void Local_Notification(MSG net_type, Notification net_name,...count)
Definition all.qc:1176
bool notif_error
Definition all.qh:724
string nent_hudargs
Definition all.qh:665
IntrusiveList g_notifications
Definition all.qh:236
bool autocvar_notification_show_sprees
Definition all.qh:297
string nent_durcnt
Definition all.qh:668
float autocvar_notification_lifetime_mapload
Definition all.qh:304
Notification Get_Notif_Ent(MSG net_type, int net_name)
Definition all.qh:741
void Create_Notification_Entity_Multi(entity notif, int var_cvar, Notification anncename, Notification infoname, Notification centername)
Definition all.qc:594
const int NOTIF_QUEUE_MAX
Definition all.qh:384
int nent_challow_var
Definition all.qh:678
MSG notif_queue_type[NOTIF_QUEUE_MAX]
Definition all.qh:386
string nent_iconargs
Definition all.qh:795
const int ARG_CS_SV
enabled on CSQC and SVQC
Definition all.qh:417
bool autocvar_notification_server_allows_location
Definition all.qh:309
int autocvar_notification_show_sprees_info
Definition all.qh:298
string notif_arg_item_wepammo(float f1, float f2)
Definition all.qh:634
float autocvar_notification_lifetime_runtime
Definition all.qh:303
int nent_channel
Definition all.qh:657
int nent_net_name
Definition all.qh:688
entity nent_optiona
Definition all.qh:679
string nent_strings[4]
Definition all.qh:689
string Get_Notif_BroadcastName(NOTIF broadcast)
send to one client and their spectators
Definition all.qh:259
NOTIF nent_broadcast
Definition all.qh:684
float nent_queuetime
Definition all.qh:661
string prev_soundfile
Definition all.qh:231
void Create_Notification_Entity_Choice(entity notif, int var_cvar, int challow_def, int challow_var, MSG chtype, Notification optiona, Notification optionb)
Definition all.qc:644
void Create_Notification_Entity_Annce(entity notif, int var_cvar, int channel, string snd, float vol, float position, float queuetime)
Definition all.qc:390
string notif_arg_frag_ping(bool newline, float fping)
Definition all.qh:498
entity Notification
always last
Definition all.qh:86
void Create_Notification_Entity(entity notif, int var_default, int var_cvar, MSG typeId, int teamnum)
Definition all.qc:342
float nent_vol
Definition all.qh:659
entity nent_msgcenter
Definition all.qh:674
void Local_Notification_Queue_Process()
Definition all.qc:1158
string Get_Notif_TypeName(MSG net_type)
Definition all.qh:35
string nent_icon
Definition all.qh:666
string autocvar_notification_show_location_string
Definition all.qh:296
string hash_replace(string full_msg, int ent_msg_num, string ent_msg, string attacker_name)
replaces # with attacker_name in ent_msg (if not found attacker_name is appended)
Definition all.qh:395
string notif_arg_spree_cen(float spree)
Definition all.qh:516
void Destroy_All_Notifications()
Definition all.qc:110
string nent_snd
Definition all.qh:658
float notif_queue_time[NOTIF_QUEUE_MAX]
Definition all.qh:387
MSG nent_net_type
Definition all.qh:687
bool notif_global_error
Definition all.qh:725
bool autocvar_notification_show_sprees_info_specialonly
Definition all.qh:300
int notif_queue_length
Definition all.qh:390
float prev_soundtime
Definition all.qh:232
string nent_args
Definition all.qh:664
entity nent_client
Definition all.qh:686
const int NOTIF_CHOICE_MAX
Definition all.qh:717
string notif_arg_spree_inf(float type, string input, string player, float spree)
Definition all.qh:552
int nent_default
Definition all.qh:649
MSG nent_type
Definition all.qh:651
const int ARG_CS
unique result to CSQC
Definition all.qh:418
int nent_choice_count
Definition all.qh:720
int nent_challow_def
Definition all.qh:677
bool autocvar_notification_errors_are_fatal
Definition all.qh:301
string notif_arg_frag_stats(float fhealth, float farmor, float fping)
Definition all.qh:507
void Dump_Notifications(int fh, bool alsoprint)
Used to output notifications.cfg file.
Definition all.qc:722
bool autocvar_notification_show_sprees_info_newline
Definition all.qh:299
float nent_floats[4]
Definition all.qh:690
entity nent_optionb
Definition all.qh:680
const int ARG_CS_SV_DC
enabled on CSQC, SVQC, and durcnt centerprint
Definition all.qh:416
bool nent_enabled
Definition all.qh:650
const int NOTIF_MAX_ARGS
Definition all.qh:379
const int ARG_SV
unique result to SVQC
Definition all.qh:419
string Get_Notif_CvarName(Notification notif)
Definition all.qh:734
void Notification_GetCvars(entity this, entity store)
Used by MSG_CHOICE to build list of choices.
Definition all.qc:707
#define NULL
Definition post.qh:14
#define REGISTRY_SORT(...)
Definition registry.qh:163
#define REGISTRY_BEGIN(id)
Called before initializing a registry.
Definition registry.qh:74
#define REGISTRY_END(id)
Called after initializing a registry.
Definition registry.qh:83
#define REGISTER_REGISTRY(id)
Definition registry.qh:270
#define REGISTRY(id, max)
Declares a new registry.
Definition registry.qh:23
#define REGISTRY_CHECK(id)
Definition registry.qh:213
#define REGISTRY_DEFINE_GET(id, null)
Definition registry.qh:50
#define REGISTRY_GET(id, i)
Definition registry.qh:62
#define STATIC_INIT_LATE(func)
directly after STATIC_INIT
Definition static.qh:38
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:33
string CCR(string input)
color code replace, place inside of sprintf and parse the string
Definition string.qh:214
string Static_Team_ColorName(int teamid)
Definition teams.qh:103
const int NUM_TEAM_1
Definition teams.qh:13