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