Xonotic
all.qc
Go to the documentation of this file.
1 #include "all.qh"
2 
3 #if defined(CSQC)
4  #include <client/announcer.qh>
5 #elif defined(MENUQC)
6 #elif defined(SVQC)
7  #include <common/constants.qh>
8  #include <common/net_linked.qh>
9  #include <common/teams.qh>
11  #include <server/mutators/_mod.qh>
12  #include <server/world.qh>
13 #endif
14 
15 // ================================================
16 // Unified notification system, written by Samual
17 // Last updated: August, 2013
18 // ================================================
19 
20 #ifdef SVQC
21 string Notification_CheckArgs(
22  NOTIF broadcast, entity client)
23 {
24  // check supplied broadcast and target for errors
25  switch (broadcast)
26  {
27  case NOTIF_ONE:
28  case NOTIF_ONE_ONLY:
29  {
30  if (IS_NOT_A_CLIENT(client)) {
31  return "No client provided!";
32  }
33  break;
34  }
35 
36  case NOTIF_ALL_EXCEPT:
37  {
38  if (IS_NOT_A_CLIENT(client)) {
39  return "Exception can't be a non-client!";
40  }
41  break;
42  }
43 
44  case NOTIF_ALL:
45  {
46  if (client) {
47  return "Entity provided when NULL was required!";
48  }
49  break;
50  }
51 
52  case NOTIF_TEAM:
53  {
54  if (!teamplay) {
55  return "Teamplay not active!";
56  } else if (!client.team) {
57  // checkargs = sprintf("%sNo team provided!", checkargs);
58  }
59  break;
60  }
61 
62  case NOTIF_TEAM_EXCEPT:
63  {
64  if (!teamplay) {
65  return "Teamplay not active!";
66  } else if (IS_NOT_A_CLIENT(client)) {
67  return "Exception can't be a non-client!";
68  }
69  break;
70  }
71 
72  default:
73  {
74  return sprintf("Improper broadcast: %d!", broadcast);
75  }
76  }
77  return "";
78 }
79 
80 bool Notification_ShouldSend(NOTIF broadcast, entity to_client, entity other_client)
81 {
82  if(!IS_REAL_CLIENT(to_client))
83  return false;
84 
85  switch (broadcast)
86  {
87  case NOTIF_ONE:
88  return (
89  (to_client == other_client)
90  ||
91  (IS_SPEC(to_client) && (to_client.enemy == other_client))
92  );
93  case NOTIF_ONE_ONLY:
94  return (to_client == other_client);
95  case NOTIF_TEAM:
96  return (
97  (to_client.team == other_client.team)
98  ||
99  (
100  IS_SPEC(to_client)
101  &&
102  (to_client.enemy.team == other_client.team)
103  )
104  );
105  case NOTIF_TEAM_EXCEPT:
106  return (
107  (to_client != other_client)
108  &&
109  (
110  (to_client.team == other_client.team)
111  ||
112  (
113  IS_SPEC(to_client)
114  &&
115  (
116  (to_client.enemy != other_client)
117  &&
118  (to_client.enemy.team == other_client.team)
119  )
120  )
121  )
122  );
123  case NOTIF_ALL:
124  return true;
125  case NOTIF_ALL_EXCEPT:
126  return (
127  (to_client != other_client)
128  &&
129  !(
130  IS_SPEC(to_client)
131  &&
132  (to_client.enemy == other_client)
133  )
134  );
135  default:
136  return false;
137  }
138 }
139 
140 #endif
141 
142 // ===============================
143 // Initialization Core Functions
144 // ===============================
145 
146 // used by restartnotifs command to initialize notifications
148 {
149  if (notif.nent_name != "") strunzone(notif.nent_name);
150  if (notif.nent_snd != "") strunzone(notif.nent_snd);
151  if (notif.nent_args != "") strunzone(notif.nent_args);
152  if (notif.nent_hudargs != "") strunzone(notif.nent_hudargs);
153  if (notif.nent_icon != "") strunzone(notif.nent_icon);
154  if (notif.nent_durcnt != "") strunzone(notif.nent_durcnt);
155  if (notif.nent_string != "") strunzone(notif.nent_string);
156  delete(notif);
157 }
158 
160 {
161  // kill all networked notifications and centerprints
162  #ifdef SVQC
163  Kill_Notification(NOTIF_ALL, NULL, MSG_Null, CPID_Null);
164  #else
166  #endif
167 
168  // kill all real notification entities
169  FOREACH(Notifications, true, { Destroy_Notification_Entity(it); });
170 }
171 
173  MSG typeId,
174  bool chat,
175  string input,
176  string notiftype,
177  string notifname,
178  string stringtype)
179 {
180  #ifdef CSQC
181  if(typeId == MSG_INFO)
182  {
185  {
186  // pass 1: add ETX char at beginning of line
187  input = strcat("\{3}", input);
188 
189  // pass 2: add ETX char at end of each new line (so that
190  // messages with multiple lines are put through chatbox too)
191  input = strreplace("\n", "\n\{3}", input);
192 
193  // pass 3: strip trailing ETX char
194  if(substring(input, (strlen(input) - 1), 1) == "\{3}")
195  { input = substring(input, 0, (strlen(input) - 1)); }
196  }
197  }
198  #endif
199 
200  // done to both MSG_INFO and MSG_CENTER
201  if(substring(input, (strlen(input) - 1), 1) == "\n")
202  {
203  LOG_INFOF(
204  (
205  "^1TRAILING NEW LINE AT END OF NOTIFICATION: "
206  "^7net_type = %s, net_name = %s, string = %s."
207  ),
208  notiftype,
209  notifname,
210  stringtype
211  );
212  notif_error = true;
213  input = substring(input, 1, (strlen(input) - 1));
214  }
215 
216  return input;
217 }
218 
220  float arg_type,
221  string args,
222  string notiftype,
223  string notifname)
224 {
225  string selected, remaining = args;
226  float sel_num = 0;
227 
228  for (;(remaining != "");)
229  {
230  selected = car(remaining); remaining = cdr(remaining);
231 
232  switch(arg_type)
233  {
234  case 1: // normal args
235  {
236  if(sel_num == NOTIF_MAX_ARGS)
237  {
238  LOG_INFOF(
239  (
240  "^1NOTIFICATION HAS TOO MANY ARGUMENTS: "
241  "^7net_type = %s, net_name = %s, max args = %d."
242  ),
243  notiftype,
244  notifname,
246  );
247  notif_error = true;
248  break;
249  }
250 
251  switch(strtolower(selected))
252  {
253  #define ARG_CASE_ARG_CS_SV_HA(selected,result) case selected: ++sel_num; break;
254  #define ARG_CASE_ARG_CS_SV_DC(selected,result) case selected: ++sel_num; break;
255  #define ARG_CASE_ARG_CS_SV(selected,result) case selected: ++sel_num; break;
256  #define ARG_CASE_ARG_CS(selected,result) case selected: ++sel_num; break;
257  #define ARG_CASE_ARG_SV(selected,result) case selected: ++sel_num; break;
258  #define ARG_CASE_ARG_DC(selected,result)
259  #define ARG_CASE(prog,selected,result) ARG_CASE_##prog(selected,result)
261  #undef ARG_CASE
262  #undef ARG_CASE_ARG_DC
263  #undef ARG_CASE_ARG_SV
264  #undef ARG_CASE_ARG_CS
265  #undef ARG_CASE_ARG_CS_SV
266  #undef ARG_CASE_ARG_CS_SV_DC
267  #undef ARG_CASE_ARG_CS_SV_HA
268  default:
269  {
270  LOG_INFOF(
271  (
272  "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: "
273  "^7net_type = %s, net_name = %s, args arg = '%s'."
274  ),
275  notiftype,
276  notifname,
277  selected
278  );
279  notif_error = true;
280  break;
281  }
282  }
283  break;
284  }
285  case 2: // hudargs
286  {
287  if(sel_num == NOTIF_MAX_HUDARGS)
288  {
289  LOG_INFOF(
290  (
291  "^1NOTIFICATION HAS TOO MANY ARGUMENTS: "
292  "^7net_type = %s, net_name = %s, max hudargs = %d."
293  ),
294  notiftype,
295  notifname,
297  );
298  notif_error = true;
299  break;
300  }
301 
302  switch(strtolower(selected))
303  {
304  #define ARG_CASE_ARG_CS_SV_HA(selected,result) case selected: ++sel_num; break;
305  #define ARG_CASE_ARG_CS_SV_DC(selected,result)
306  #define ARG_CASE_ARG_CS_SV(selected,result)
307  #define ARG_CASE_ARG_CS(selected,result)
308  #define ARG_CASE_ARG_SV(selected,result)
309  #define ARG_CASE_ARG_DC(selected,result)
310  #define ARG_CASE(prog,selected,result) ARG_CASE_##prog(selected,result)
312  #undef ARG_CASE
313  #undef ARG_CASE_ARG_DC
314  #undef ARG_CASE_ARG_SV
315  #undef ARG_CASE_ARG_CS
316  #undef ARG_CASE_ARG_CS_SV
317  #undef ARG_CASE_ARG_CS_SV_DC
318  #undef ARG_CASE_ARG_CS_SV_HA
319  default:
320  {
321  LOG_INFOF(
322  (
323  "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: "
324  "^7net_type = %s, net_name = %s, hudargs arg = '%s'."
325  ),
326  notiftype,
327  notifname,
328  selected
329  );
330  notif_error = true;
331  break;
332  }
333  }
334  break;
335  }
336  case 3: // durcnt
337  {
338  if(sel_num == NOTIF_MAX_DURCNT)
339  {
340  LOG_INFOF(
341  (
342  "^1NOTIFICATION HAS TOO MANY ARGUMENTS: "
343  "^7net_type = %s, net_name = %s, max durcnt = %d."
344  ),
345  notiftype,
346  notifname,
348  );
349  notif_error = true;
350  break;
351  }
352 
353  switch(strtolower(selected))
354  {
355  #define ARG_CASE_ARG_CS_SV_HA(selected,result)
356  #define ARG_CASE_ARG_CS_SV_DC(selected,result) case selected: ++sel_num; break;
357  #define ARG_CASE_ARG_CS_SV(selected,result)
358  #define ARG_CASE_ARG_CS(selected,result)
359  #define ARG_CASE_ARG_SV(selected,result)
360  #define ARG_CASE_ARG_DC(selected,result) case selected: ++sel_num; break;
361  #define ARG_CASE(prog,selected,result) ARG_CASE_##prog(selected,result)
363  #undef ARG_CASE
364  #undef ARG_CASE_ARG_DC
365  #undef ARG_CASE_ARG_SV
366  #undef ARG_CASE_ARG_CS
367  #undef ARG_CASE_ARG_CS_SV
368  #undef ARG_CASE_ARG_CS_SV_DC
369  #undef ARG_CASE_ARG_CS_SV_HA
370  default:
371  {
372  if(ftos(stof(selected)) != "") { ++sel_num; }
373  else
374  {
375  LOG_INFOF(
376  (
377  "^1NOTIFICATION WITH UNKNOWN TOKEN IN ARGUMENT STRING: "
378  "^7net_type = %s, net_name = %s, durcnt arg = '%s'."
379  ),
380  notiftype,
381  notifname,
382  selected
383  );
384  notif_error = true;
385  }
386  break;
387  }
388  }
389  break;
390  }
391  }
392  }
393  return args;
394 }
395 
397  float var_default,
398  float var_cvar,
399  MSG typeId,
400  string namestring,
401  int teamnum)
402 {
403  // =====================
404  // Global Entity Setup
405  // =====================
406  notif.nent_default = var_default;
407  notif.nent_enabled = (var_cvar >= 1);
408  notif.nent_type = typeId;
409  notif.nent_name = strzone(namestring);
410  notif.nent_teamnum = teamnum;
411 
412  // Other pre-notif-setup requisites
413  notif_error = false;
414 
415  switch (typeId)
416  {
417  case MSG_ANNCE:
418  case MSG_INFO:
419  case MSG_CENTER:
420  case MSG_MULTI:
421  case MSG_CHOICE:
422  break;
423  default:
424  LOG_INFOF(
425  (
426  "^1NOTIFICATION WITH IMPROPER TYPE: "
427  "^7net_type = %d, net_name = %s."
428  ),
429  typeId,
430  namestring
431  );
432  notif_error = true;
433  break;
434  }
435 
436  // now check to see if any errors happened
437  if (notif_error)
438  {
439  notif.nent_enabled = false; // disable the notification so it can't cause trouble
440  notif_global_error = true; // throw the red flag that an error happened on init
441  }
442 }
443 
444 #define AnnouncerFilename(snd) sprintf("announcer/%s/%s.wav", AnnouncerOption(), snd)
445 
447  float var_cvar,
448  string namestring,
449  /* MSG_ANNCE */
450  float channel,
451  string snd,
452  float vol,
453  float position)
454  {
455  // Set MSG_ANNCE information and handle precaching
456  #ifdef CSQC
457  MSG typeId = MSG_ANNCE;
458  if (!(GENTLE && (var_cvar == 1)))
459  {
460  if(snd != "")
461  {
462  if(notif.nent_enabled)
463  {
465  notif.nent_channel = channel;
466  notif.nent_snd = strzone(snd);
467  notif.nent_vol = vol;
468  notif.nent_position = position;
469  }
470  }
471  else
472  {
473  string typestring = Get_Notif_TypeName(typeId);
474  LOG_INFOF(
475  (
476  "^1NOTIFICATION WITH NO SOUND: "
477  "^7net_type = %s, net_name = %s."
478  ),
479  typestring,
480  namestring
481  );
482  notif_error = true;
483  }
484  }
485  else { notif.nent_enabled = false; }
486  #else
487  notif.nent_enabled = false;
488  #endif
489 
490  }
491 
493  float var_cvar,
494  string namestring,
495  int strnum,
496  int flnum,
497  /* MSG_INFO & MSG_CENTER */
498  string args,
499  string hudargs,
500  string icon,
501  CPID cpid,
502  string durcnt,
503  string normal,
504  string gentle)
505  {
506  MSG typeId = notif.nent_type;
507  // Set MSG_INFO and MSG_CENTER string/float counts
508  notif.nent_stringcount = strnum;
509  notif.nent_floatcount = flnum;
510 
511  // Only initialize arguments if we're either a client or on a dedicated server
512  #ifdef SVQC
513  float should_process_args = server_is_dedicated;
514  #else
515  float should_process_args = true;
516  #endif
517  string typestring = Get_Notif_TypeName(typeId);
518  if(should_process_args)
519  {
520  // ========================
521  // Process Main Arguments
522  // ========================
523  if(strnum + flnum)
524  {
525  if(args != "")
526  {
527  notif.nent_args = strzone(
528  Process_Notif_Args(1, args, typestring, namestring));
529  }
530  else if((hudargs == "") && (durcnt ==""))
531  {
532  LOG_INFOF(
533  (
534  "^1NOTIFICATION HAS ARG COUNTS BUT NO ARGS OR HUDARGS OR DURCNT: "
535  "^7net_type = %s, net_name = %s, strnum = %d, flnum = %d"
536  ),
537  typestring,
538  namestring,
539  strnum,
540  flnum
541  );
542  notif_error = true;
543  }
544  }
545  else if(args != "")
546  {
547  notif.nent_args = strzone(
548  Process_Notif_Args(1, args, typestring, namestring));
549  }
550 
551 
552  // =======================================
553  // Process HUD and Centerprint Arguments
554  // Only processed on CSQC, as these
555  // args are only for HUD features.
556  // =======================================
557  #ifdef CSQC
558  if(hudargs != "")
559  {
560  notif.nent_hudargs = strzone(
561  Process_Notif_Args(2, hudargs, typestring, namestring));
562 
563  if(icon != "") { notif.nent_icon = strzone(icon); }
564  else
565  {
566  LOG_INFOF(
567  (
568  "^1NOTIFICATION HAS HUDARGS BUT NO ICON: "
569  "^7net_type = %s, net_name = %s."
570  ),
571  typestring,
572  namestring
573  );
574  notif_error = true;
575  }
576  }
577  else if(icon != "")
578  {
579  LOG_WARNF(
580  (
581  "^1NOTIFICATION HAS ICON BUT NO HUDARGS: "
582  "^7net_type = %s, net_name = %s.\n"
583  ),
584  typestring,
585  namestring
586  );
587  notif_error = true;
588  }
589 
590  if (durcnt != "")
591  {
592  notif.nent_durcnt = strzone(Process_Notif_Args(3, durcnt, typestring, namestring));
593 
594  if (cpid == CPID_Null && durcnt != "0 0")
595  {
596  LOG_WARNF(
597  (
598  "Notification has durcnt but no cpid: "
599  "net_type = %s, net_name = %s."
600  ),
601  typestring,
602  namestring
603  );
604  notif_error = true;
605  }
606  }
607  notif.nent_cpid = cpid;
608  #endif
609 
610 
611  // ======================
612  // Process Notif String
613  // ======================
614  #define SET_NOTIF_STRING(string,stringname) MACRO_BEGIN \
615  notif.nent_string = strzone(CCR( \
616  Process_Notif_Line( \
617  typeId, \
618  (var_cvar > 1), \
619  string, \
620  typestring, \
621  namestring, \
622  stringname \
623  )) \
624  ); \
625  MACRO_END
626 
627  if(GENTLE)
628  {
629  if(gentle != "") { SET_NOTIF_STRING(gentle, "GENTLE"); }
630  else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL"); }
631  }
632  else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL"); }
633  #undef SET_NOTIF_STRING
634 
635  // Check to make sure a string was chosen
636  if(notif.nent_string == "")
637  {
638  LOG_INFOF(
639  (
640  "^1EMPTY NOTIFICATION: "
641  "^7net_type = %s, net_name = %s."
642  ),
643  typestring,
644  namestring
645  );
646  notif_error = true;
647  }
648  }
649  }
650 
652  float var_cvar,
653  string namestring,
654  /* MSG_MULTI */
655  Notification anncename,
656  Notification infoname,
657  Notification centername)
658  {
659  MSG typeId = MSG_MULTI;
660  // Set MSG_MULTI string/float counts
661  if (!anncename && !infoname && !centername)
662  {
663  string typestring = Get_Notif_TypeName(typeId);
664  LOG_INFOF(
665  (
666  "^1NOTIFICATION WITH NO SUBCALLS: "
667  "^7net_type = %s, net_name = %s."
668  ),
669  typestring,
670  namestring
671  );
672  notif_error = true;
673  }
674  else
675  {
676  // announcements don't actually need any arguments, so lets not even count them.
677  if (anncename) { notif.nent_msgannce = anncename; }
678 
679  float infoname_stringcount = 0, infoname_floatcount = 0;
680  float centername_stringcount = 0, centername_floatcount = 0;
681 
682  if (infoname)
683  {
684  notif.nent_msginfo = infoname;
685  infoname_stringcount = notif.nent_msginfo.nent_stringcount;
686  infoname_floatcount = notif.nent_msginfo.nent_floatcount;
687  }
688 
689  if (centername)
690  {
691  notif.nent_msgcenter = centername;
692  centername_stringcount = notif.nent_msgcenter.nent_stringcount;
693  centername_floatcount = notif.nent_msgcenter.nent_floatcount;
694  }
695 
696  // set the requirements of THIS notification to the totals of its subcalls
697  notif.nent_stringcount = max(infoname_stringcount, centername_stringcount);
698  notif.nent_floatcount = max(infoname_floatcount, centername_floatcount);
699  }
700  }
701 
703  float var_cvar,
704  string namestring,
705  /* MSG_CHOICE */
706  float challow_def,
707  float challow_var,
708  MSG chtype,
709  Notification optiona,
710  Notification optionb)
711  {
712  MSG typeId = MSG_CHOICE;
713  if (chtype == MSG_Null || !optiona || !optionb)
714  {
715  string typestring = Get_Notif_TypeName(typeId);
716  LOG_INFOF(
717  (
718  "^1NOTIFICATION IS MISSING CHOICE PARAMS: "
719  "^7net_type = %s, net_name = %s."
720  ),
721  typestring,
722  namestring
723  );
724  notif_error = true;
725  }
726  else
727  {
728  notif.nent_optiona = optiona;
729  notif.nent_optionb = optionb;
730  notif.nent_challow_def = challow_def; // 0: never allowed, 1: allowed in warmup, 2: always allowed
731  notif.nent_challow_var = challow_var; // 0: never allowed, 1: allowed in warmup, 2: always allowed
732  notif.nent_stringcount = max(notif.nent_optiona.nent_stringcount, notif.nent_optionb.nent_stringcount);
733  notif.nent_floatcount = max(notif.nent_optiona.nent_floatcount, notif.nent_optionb.nent_floatcount);
734 
735  /*#ifdef NOTIFICATIONS_DEBUG
736  Debug_Notification(sprintf(
737  "Create_Notification_Entity(...): MSG_CHOICE: %s\n%s\n%s\n",
738  notif.nent_name,
739  sprintf(
740  "^ optiona: %s %s : %d %d",
741  Get_Notif_TypeName(notif.nent_optiona.nent_type),
742  notif.nent_optiona.nent_name,
743  notif.nent_optiona.nent_stringcount,
744  notif.nent_optiona.nent_floatcount
745  ),
746  sprintf(
747  "^ optionb: %s %s : %d %d",
748  Get_Notif_TypeName(notif.nent_optionb.nent_type),
749  notif.nent_optionb.nent_name,
750  notif.nent_optionb.nent_stringcount,
751  notif.nent_optionb.nent_floatcount
752  )
753  ));
754  #endif*/
755  }
756  }
757 
758 
759 // ===============
760 // Cvar Handling
761 // ===============
762 
763 // used by MSG_CHOICE to build list of choices
764 #ifdef SVQC
765 void Notification_GetCvars(entity this, entity store)
766 {
767  FOREACH(Notifications, it.nent_type == MSG_CHOICE && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
768  GetCvars_handleFloat(
769  this,
770  store,
771  get_cvars_s,
772  get_cvars_f,
773  msg_choice_choices[it.nent_choice_idx],
774  sprintf("notification_%s", Get_Notif_CvarName(it))
775  );
776  });
777 }
778 #endif
779 
781 void Dump_Notifications(int fh, bool alsoprint)
782 {
783  #define NOTIF_WRITE(str) write_String_To_File(fh, str, alsoprint)
784 
785  #define NOTIF_WRITE_ENTITY(e, description) \
786  NOTIF_WRITE(sprintf( \
787  "seta notification_%s \"%d\" \"%s\"\n", \
788  Get_Notif_CvarName(e), e.nent_default, description \
789  ))
790 
791  #define NOTIF_WRITE_ENTITY_CHOICE(e, descriptiona, descriptionb) \
792  NOTIF_WRITE(sprintf( \
793  "seta notification_%s \"%d\" \"%s\"\n" \
794  "seta notification_%s_ALLOWED \"%d\" \"%s\"\n", \
795  Get_Notif_CvarName(e), e.nent_default, descriptiona, \
796  Get_Notif_CvarName(e), e.nent_challow_def, descriptionb \
797  ))
798 
799  #define NOTIF_WRITE_HARDCODED(cvar, default, description) \
800  NOTIF_WRITE("seta notification_" cvar " \"" default "\" \"" description "\"\n")
801 
802  // Note: This warning only applies to the notifications.cfg file that is output...
803  // You ARE supposed to manually edit this function to add i.e. hard coded
804  // notification variables for mutators or game modes or such and then
805  // regenerate the notifications.cfg file from the new code.
806 
807  NOTIF_WRITE(
808  "// ********************************************** //\n"
809  "// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n"
810  "// ** ** //\n"
811  "// ** This file is automatically generated ** //\n"
812  "// ** by code with the command 'dumpnotifs'. ** //\n"
813  "// ** ** //\n"
814  "// ** If you add a new notification, please ** //\n"
815  "// ** regenerate this file with that command ** //\n"
816  "// ** making sure that the output matches ** //\n"
817  "// ** with the lists and defaults in code. ** //\n"
818  "// ** ** //\n"
819  "// ********************************************** //\n");
820 
821  // These notifications will also append their string as a comment...
822  // This is not necessary, and does not matter if they vary between config versions,
823  // it is just a semi-helpful tool for those who want to manually change their user settings.
824 
825  int NOTIF_ANNCE_COUNT = 0;
826  int NOTIF_INFO_COUNT = 0;
827  int NOTIF_CENTER_COUNT = 0;
828  int NOTIF_MULTI_COUNT = 0;
829  int NOTIF_CHOICE_COUNT = 0;
830  FOREACH(Notifications, true, {
831  switch (it.nent_type)
832  {
833  case MSG_ANNCE: ++NOTIF_ANNCE_COUNT; break;
834  case MSG_INFO: ++NOTIF_INFO_COUNT; break;
835  case MSG_CENTER: ++NOTIF_CENTER_COUNT; break;
836  case MSG_MULTI: ++NOTIF_MULTI_COUNT; break;
837  case MSG_CHOICE: ++NOTIF_CHOICE_COUNT; break;
838  }
839  });
840 
841  NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications (count = %d):\n", NOTIF_ANNCE_COUNT));
842  FOREACH(Notifications, it.nent_type == MSG_ANNCE && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
843  NOTIF_WRITE_ENTITY(it,
844  "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled"
845  );
846  });
847 
848  NOTIF_WRITE(sprintf("\n// MSG_INFO notifications (count = %d):\n", NOTIF_INFO_COUNT));
849  FOREACH(Notifications, it.nent_type == MSG_INFO && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
850  NOTIF_WRITE_ENTITY(it,
851  "0 = off, 1 = print to console, "
852  "2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
853  );
854  });
855 
856  NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications (count = %d):\n", NOTIF_CENTER_COUNT));
857  FOREACH(Notifications, it.nent_type == MSG_CENTER && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
858  NOTIF_WRITE_ENTITY(it,
859  "0 = off, 1 = centerprint"
860  );
861  });
862 
863  NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications (count = %d):\n", NOTIF_MULTI_COUNT));
864  FOREACH(Notifications, it.nent_type == MSG_MULTI && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
865  NOTIF_WRITE_ENTITY(it,
866  "Enable this multiple notification"
867  );
868  });
869 
870  NOTIF_WRITE(sprintf("\n// MSG_CHOICE notifications (count = %d):\n", NOTIF_CHOICE_COUNT));
871  FOREACH(Notifications, it.nent_type == MSG_CHOICE && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
872  NOTIF_WRITE_ENTITY_CHOICE(it,
873  "Choice for this notification 0 = off, 1 = default message, 2 = verbose message",
874  "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always"
875  );
876  });
877 
878  // edit these to match whichever cvars are used for specific notification options
879  NOTIF_WRITE("\n// HARD CODED notification variables:\n");
880 
882  "allow_chatboxprint", "1",
883  "Allow INFO notifications to be printed to chat box "
884  "0 = do not allow, "
885  "1 = allow only if allowed by individual notification_INFO* cvars, "
886  "2 = force all INFO notifications to be printed to the chatbox"
887  );
888 
890  "debug", "0",
891  "Print extra debug information on all notification function calls "
892  "(Requires -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation)... "
893  "0 = disabled, 1 = dprint, 2 = print"
894  );
895 
897  "errors_are_fatal", "1",
898  "If a notification fails upon initialization, cause a Host_Error to stop the program"
899  );
900 
902  "item_centerprinttime", "1.5",
903  "How long to show item information centerprint messages (like 'You got the Electro' or such)"
904  );
905 
907  "lifetime_mapload", "10",
908  "Amount of time that notification entities last immediately at mapload (in seconds) "
909  "to help prevent notifications from being lost on early init (like gamestart countdown)"
910  );
911 
913  "lifetime_runtime", "0.5",
914  "Amount of time that notification entities last on the server during runtime (In seconds)"
915  );
916 
918  "server_allows_location", "1",
919  "Server side cvar for allowing death messages to show location information too"
920  );
921 
923  "show_location", "0",
924  "Append location information to MSG_INFO death/kill messages"
925  );
926 
928  "show_location_string", "",
929  "Replacement string piped into sprintf, "
930  "so you can do different messages like this: ' at the %s' or ' (near %s)'"
931  );
932 
934  "show_sprees", "1",
935  "Print information about sprees in death/kill messages"
936  );
937 
939  "show_sprees_center", "1",
940  "Show spree information in MSG_CENTER messages... "
941  "0 = off, 1 = target (but only for first victim) and attacker"
942  );
943 
945  "show_sprees_center_specialonly", "1",
946  "Don't show spree information in MSG_CENTER messages if it isn't an achievement"
947  );
948 
950  "show_sprees_info", "3",
951  "Show spree information in MSG_INFO messages... "
952  "0 = off, 1 = target only, 2 = attacker only, 3 = target and attacker"
953  );
954 
956  "show_sprees_info_newline", "1",
957  "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
958  );
959 
961  "show_sprees_info_specialonly", "1",
962  "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement"
963  );
964 
965  NOTIF_WRITE(sprintf(
966  (
967  "\n// Notification counts (total = %d): "
968  "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d\n"
969  ),
970  (
971  NOTIF_ANNCE_COUNT +
972  NOTIF_INFO_COUNT +
973  NOTIF_CENTER_COUNT +
974  NOTIF_MULTI_COUNT +
975  NOTIF_CHOICE_COUNT
976  ),
977  NOTIF_ANNCE_COUNT,
978  NOTIF_INFO_COUNT,
979  NOTIF_CENTER_COUNT,
980  NOTIF_MULTI_COUNT,
981  NOTIF_CHOICE_COUNT
982  ));
983  #undef NOTIF_WRITE_HARDCODED
984  #undef NOTIF_WRITE_ENTITY
985  #undef NOTIF_WRITE
986 }
987 
988 
989 // ===============================
990 // Frontend Notification Pushing
991 // ===============================
992 
994  string input, string args,
995  string s1, string s2, string s3, string s4,
996  int f1, float f2, float f3, float f4)
997 {
998  #ifdef NOTIFICATIONS_DEBUG
999  Debug_Notification(sprintf(
1000  "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
1001  MakeConsoleSafe(input),
1002  args,
1003  MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1004  sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1005  ));
1006  #endif
1007 
1008  for (int sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
1009 
1010  for (int sel_num = 0; (args != ""); )
1011  {
1012  string selected = car(args); args = cdr(args);
1013  NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf");
1014  string tmp_s; // used by NOTIF_ARGUMENT_LIST
1015  switch (strtolower(selected))
1016  {
1017  #define ARG_CASE_ARG_CS_SV_HA(selected, result) case selected: arg_slot[sel_num++] = result; break;
1018  #define ARG_CASE_ARG_CS_SV_DC(selected, result) case selected: arg_slot[sel_num++] = result; break;
1019  #define ARG_CASE_ARG_CS_SV(selected, result) case selected: arg_slot[sel_num++] = result; break;
1020 #ifdef CSQC
1021  #define ARG_CASE_ARG_CS(selected, result) case selected: arg_slot[sel_num++] = result; break;
1022  #define ARG_CASE_ARG_SV(selected, result)
1023 #else
1024  #define ARG_CASE_ARG_CS(selected, result)
1025  #define ARG_CASE_ARG_SV(selected, result) case selected: arg_slot[sel_num++] = result; break;
1026 #endif
1027  #define ARG_CASE_ARG_DC(selected, result)
1028  #define ARG_CASE(prog, selected, result) ARG_CASE_##prog(selected, result)
1030  #undef ARG_CASE
1031  #undef ARG_CASE_ARG_DC
1032  #undef ARG_CASE_ARG_SV
1033  #undef ARG_CASE_ARG_CS
1034  #undef ARG_CASE_ARG_CS_SV
1035  #undef ARG_CASE_ARG_CS_SV_DC
1036  #undef ARG_CASE_ARG_CS_SV_HA
1037  default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
1038  }
1039  }
1040  return sprintf(
1041  strcat(input, "\n"),
1042  arg_slot[0],
1043  arg_slot[1],
1044  arg_slot[2],
1045  arg_slot[3],
1046  arg_slot[4],
1047  arg_slot[5],
1048  arg_slot[6]
1049  );
1050 }
1051 
1052 #ifdef CSQC
1053 void Local_Notification_sound(int soundchannel, string soundfile, float soundvolume, float soundposition)
1054 {
1055  if ((soundfile != prev_soundfile) || (time >= (prev_soundtime + autocvar_cl_announcer_antispam)))
1056  {
1057  #ifdef NOTIFICATIONS_DEBUG
1058  Debug_Notification(sprintf(
1059  "Local_Notification_sound(%f, '%s', %f, %f);\n",
1060  soundchannel,
1061  AnnouncerFilename(soundfile),
1062  soundvolume,
1063  soundposition
1064  ));
1065  #endif
1066 
1067  _sound(NULL, soundchannel, AnnouncerFilename(soundfile), soundvolume, soundposition);
1068 
1069  strcpy(prev_soundfile, soundfile);
1070  prev_soundtime = time;
1071  }
1072  else
1073  {
1074  #ifdef NOTIFICATIONS_DEBUG
1075  Debug_Notification(sprintf(
1076  (
1077  "Local_Notification_sound(%f, '%s', %f, %f) "
1078  "^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', timediff: %f, limit: %f\n"
1079  ),
1080  soundchannel,
1081  AnnouncerFilename(soundfile),
1082  soundvolume,
1083  soundposition,
1084  prev_soundfile,
1085  (time - prev_soundtime),
1087  ));
1088  #endif
1089  }
1090 }
1091 
1092 void Local_Notification_HUD_Notify_Push(
1093  string icon, string hudargs,
1094  string s1, string s2, string s3, string s4,
1095  float f1, float f2, float f3, float f4)
1096 {
1097  arg_slot[0] = ""; arg_slot[1] = "";
1098 
1099  for (int sel_num = 0; (hudargs != ""); )
1100  {
1101  string selected = car(hudargs); hudargs = cdr(hudargs);
1102  NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push");
1103  switch (strtolower(selected))
1104  {
1105  #define ARG_CASE_ARG_CS_SV_HA(selected, result) case selected: arg_slot[sel_num++] = result; break;
1106  #define ARG_CASE_ARG_CS_SV_DC(selected, result)
1107  #define ARG_CASE_ARG_CS_SV(selected, result)
1108  #define ARG_CASE_ARG_CS(selected, result)
1109  #define ARG_CASE_ARG_SV(selected, result)
1110  #define ARG_CASE_ARG_DC(selected, result)
1111  #define ARG_CASE(prog, selected, result) ARG_CASE_##prog(selected, result)
1113  #undef ARG_CASE
1114  #undef ARG_CASE_ARG_DC
1115  #undef ARG_CASE_ARG_SV
1116  #undef ARG_CASE_ARG_CS
1117  #undef ARG_CASE_ARG_CS_SV
1118  #undef ARG_CASE_ARG_CS_SV_DC
1119  #undef ARG_CASE_ARG_CS_SV_HA
1120  default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1121  }
1122  }
1123  #ifdef NOTIFICATIONS_DEBUG
1124  Debug_Notification(sprintf(
1125  "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s, %s);\n",
1126  icon,
1127  hudargs,
1128  MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1129  sprintf("%d, %d, %d, %d", f1, f2, f3, f4),
1130  MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
1131  ));
1132  #endif
1133  HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
1134 }
1135 
1136 void Local_Notification_centerprint_Add(
1137  string input, string durcnt,
1138  CPID cpid, float f1, float f2)
1139 {
1140  arg_slot[0] = ""; arg_slot[1] = "";
1141 
1142  for (int sel_num = 0; (durcnt != ""); )
1143  {
1144  string selected = car(durcnt); durcnt = cdr(durcnt);
1145  NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_Add");
1146  switch (strtolower(selected))
1147  {
1148  #define ARG_CASE_ARG_CS_SV_HA(selected, result)
1149  #define ARG_CASE_ARG_CS_SV_DC(selected, result) case selected: arg_slot[sel_num++] = result; break;
1150  #define ARG_CASE_ARG_CS_SV(selected, result)
1151  #define ARG_CASE_ARG_CS(selected, result)
1152  #define ARG_CASE_ARG_SV(selected, result)
1153  #define ARG_CASE_ARG_DC(selected, result) case selected: arg_slot[sel_num++] = result; break;
1154  #define ARG_CASE(prog, selected, result) ARG_CASE_##prog(selected,result)
1156  #undef ARG_CASE
1157  #undef ARG_CASE_ARG_DC
1158  #undef ARG_CASE_ARG_SV
1159  #undef ARG_CASE_ARG_CS
1160  #undef ARG_CASE_ARG_CS_SV
1161  #undef ARG_CASE_ARG_CS_SV_DC
1162  #undef ARG_CASE_ARG_CS_SV_HA
1163  default:
1164  {
1165  if (/* wtf */ ftos(stof(selected)) != "") { arg_slot[sel_num++] = selected; }
1166  else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_Add") }
1167  break;
1168  }
1169  }
1170  }
1171  #ifdef NOTIFICATIONS_DEBUG
1172  Debug_Notification(sprintf(
1173  "Local_Notification_centerprint_Add('%s^7', '%s', %d, %d, %d, %d);\n",
1174  MakeConsoleSafe(input),
1175  durcnt,
1176  f1, f2,
1177  stof(arg_slot[0]),
1178  stof(arg_slot[1])
1179  ));
1180  #endif
1181  centerprint_Add(ORDINAL(cpid), input, stof(arg_slot[0]), stof(arg_slot[1]));
1182 }
1183 #endif
1184 
1185 void Local_Notification(MSG net_type, Notification net_name, ...count)
1186 {
1187  // retreive entity of this notification
1188  entity notif = net_name;
1189  if (!notif)
1190  {
1191  #ifdef NOTIFICATIONS_DEBUG
1192  Debug_Notification(sprintf(
1193  "Local_Notification(%s, NULL, ...);\n",
1194  Get_Notif_TypeName(net_type)
1195  ));
1196  #endif
1197  LOG_WARNF("Incorrect usage of Local_Notification: %s", "Null notification");
1198  return;
1199  }
1200 
1201  // check if the notification is enabled
1202  if (!notif.nent_enabled)
1203  {
1204  #ifdef NOTIFICATIONS_DEBUG
1205  Debug_Notification(sprintf(
1206  "Local_Notification(%s, %s, ...): Entity was disabled...\n",
1207  Get_Notif_TypeName(net_type),
1208  notif.nent_name
1209  ));
1210  #endif
1211  return;
1212  }
1213 
1214  string s1 = CCR((notif.nent_stringcount > 0) ? ...(0, string) : "");
1215  string s2 = CCR((notif.nent_stringcount > 1) ? ...(1, string) : "");
1216  string s3 = CCR((notif.nent_stringcount > 2) ? ...(2, string) : "");
1217  string s4 = CCR((notif.nent_stringcount > 3) ? ...(3, string) : "");
1218  float f1 = ((notif.nent_floatcount > 0) ? ...((notif.nent_stringcount + 0), float) : 0);
1219  float f2 = ((notif.nent_floatcount > 1) ? ...((notif.nent_stringcount + 1), float) : 0);
1220  float f3 = ((notif.nent_floatcount > 2) ? ...((notif.nent_stringcount + 2), float) : 0);
1221  float f4 = ((notif.nent_floatcount > 3) ? ...((notif.nent_stringcount + 3), float) : 0);
1222 
1223  #ifdef NOTIFICATIONS_DEBUG
1224  Debug_Notification(sprintf(
1225  "Local_Notification(%s, %s, %s, %s);\n",
1226  Get_Notif_TypeName(net_type),
1227  notif.nent_name,
1228  MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1229  sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1230  ));
1231  #endif
1232 
1233  if ((notif.nent_stringcount + notif.nent_floatcount) != count)
1234  {
1235  backtrace(sprintf(
1236  (
1237  "Arguments mismatch for Local_Notification(%s, %s, ...)! "
1238  "stringcount(%d) + floatcount(%d) != count(%d)\n"
1239  "Check the definition and function call for accuracy...?\n"
1240  ),
1241  Get_Notif_TypeName(net_type),
1242  notif.nent_name,
1243  notif.nent_stringcount,
1244  notif.nent_floatcount,
1245  count
1246  ));
1247  return;
1248  }
1249 
1250  switch (net_type)
1251  {
1252  case MSG_ANNCE:
1253  {
1254  #ifdef CSQC
1255  Local_Notification_sound(notif.nent_channel, notif.nent_snd, notif.nent_vol, notif.nent_position);
1256  #else
1257  backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
1258  #endif
1259  break;
1260  }
1261 
1262  case MSG_INFO:
1263  {
1264  print(
1266  notif.nent_string,
1267  notif.nent_args,
1268  s1, s2, s3, s4,
1269  f1, f2, f3, f4)
1270  );
1271  #ifdef CSQC
1272  if (notif.nent_icon != "")
1273  {
1274  if (notif.nent_iconargs != "")
1275  {
1276  string s = Local_Notification_sprintf(
1277  notif.nent_icon,notif.nent_iconargs,
1278  s1, s2, s3, s4, f1, f2, f3, f4);
1279  // remove the trailing newline
1280  notif.nent_icon = strzone(substring(s, 0, -1));
1281  }
1282  Local_Notification_HUD_Notify_Push(
1283  notif.nent_icon,
1284  notif.nent_hudargs,
1285  s1, s2, s3, s4,
1286  f1, f2, f3, f4);
1287  }
1288  #endif
1289  break;
1290  }
1291 
1292  #ifdef CSQC
1293  case MSG_CENTER:
1294  {
1295  Local_Notification_centerprint_Add(
1297  notif.nent_string,
1298  notif.nent_args,
1299  s1, s2, s3, s4,
1300  f1, f2, f3, f4),
1301  notif.nent_durcnt,
1302  notif.nent_cpid,
1303  f1, f2);
1304  break;
1305  }
1306  #endif
1307 
1308  case MSG_MULTI:
1309  {
1310  if (notif.nent_msginfo && notif.nent_msginfo.nent_enabled)
1311  {
1313  MSG_INFO,
1314  notif.nent_msginfo,
1315  notif.nent_msginfo.nent_stringcount,
1316  notif.nent_msginfo.nent_floatcount,
1317  s1, s2, s3, s4,
1318  f1, f2, f3, f4);
1319  }
1320  #ifdef CSQC
1321  if (notif.nent_msgannce && notif.nent_msgannce.nent_enabled)
1322  {
1324  MSG_ANNCE,
1325  notif.nent_msgannce,
1326  0, 0,
1327  "", "", "", "",
1328  0, 0, 0, 0);
1329  }
1330  if (notif.nent_msgcenter && notif.nent_msgcenter.nent_enabled)
1331  {
1333  MSG_CENTER,
1334  notif.nent_msgcenter,
1335  notif.nent_msgcenter.nent_stringcount,
1336  notif.nent_msgcenter.nent_floatcount,
1337  s1, s2, s3, s4,
1338  f1, f2, f3, f4);
1339  }
1340  #endif
1341  break;
1342  }
1343 
1344  case MSG_CHOICE:
1345  {
1346  entity found_choice = notif.nent_optiona;
1347  if (notif.nent_challow_var && (warmup_stage || (notif.nent_challow_var == 2))) {
1348  switch (cvar(sprintf("notification_%s", Get_Notif_CvarName(notif))))
1349  {
1350  case 1: break;
1351  case 2: found_choice = notif.nent_optionb; break;
1352  default: return; // not enabled anyway
1353  }
1354  }
1355 
1357  found_choice.nent_type,
1358  found_choice,
1359  found_choice.nent_stringcount,
1360  found_choice.nent_floatcount,
1361  s1, s2, s3, s4,
1362  f1, f2, f3, f4);
1363  }
1364  }
1365 }
1366 
1367 // WOVA = Without Variable Arguments
1369  MSG net_type, Notification net_name,
1370  float stringcount, float floatcount,
1371  string s1, string s2, string s3, string s4,
1372  float f1, float f2, float f3, float f4)
1373 {
1374  #define VARITEM(stringc, floatc, args) \
1375  if ((stringcount == stringc) && (floatcount == floatc)) \
1376  { Local_Notification(net_type, net_name, args); return; }
1378  #undef VARITEM
1379  Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1380 }
1381 
1382 
1383 // =========================
1384 // Notification Networking
1385 // =========================
1386 
1388 REGISTER_NET_LINKED(ENT_CLIENT_NOTIFICATION)
1389 
1390 #ifdef CSQC
1391 NET_HANDLE(ENT_CLIENT_NOTIFICATION, bool is_new)
1392 {
1393  make_pure(this);
1394  MSG net_type = ENUMCAST(MSG, ReadByte());
1395  int net_name = ReadShort();
1396  return = true;
1397 
1398  if (net_type == MSG_CENTER_KILL)
1399  {
1400  if (!is_new) return;
1401  // killing
1402  #ifdef NOTIFICATIONS_DEBUG
1403  Debug_Notification(sprintf(
1404  "Read_Notification(%d) at %f: net_type = %s, cpid = %d\n",
1405  is_new,
1406  time,
1407  Get_Notif_TypeName(net_type),
1408  net_name
1409  ));
1410  #endif
1411  int _net_name = net_name;
1412  CPID net_name = ENUMCAST(CPID, _net_name);
1413  if (net_name == CPID_Null) {
1415  } else {
1416  centerprint_Kill(ORDINAL(net_name));// kill group
1417  }
1418  return;
1419  }
1420 
1421  Notification notif = Get_Notif_Ent(net_type, net_name);
1422 
1423  #ifdef NOTIFICATIONS_DEBUG
1424  Debug_Notification(sprintf(
1425  "Read_Notification(%d) at %f: net_type = %s, net_name = %s (%d)\n",
1426  is_new,
1427  time,
1428  Get_Notif_TypeName(net_type),
1429  notif.registered_id,
1430  net_name
1431  ));
1432  #endif
1433 
1434  if (!notif) {
1435  backtrace("Read_Notification: Could not find notification entity!\n");
1436  return false;
1437  }
1438 
1439  string s1 = ((notif.nent_stringcount > 0) ? ReadString() : "");
1440  string s2 = ((notif.nent_stringcount > 1) ? ReadString() : "");
1441  string s3 = ((notif.nent_stringcount > 2) ? ReadString() : "");
1442  string s4 = ((notif.nent_stringcount > 3) ? ReadString() : "");
1443  float f1 = ((notif.nent_floatcount > 0) ? ReadLong() : 0);
1444  float f2 = ((notif.nent_floatcount > 1) ? ReadLong() : 0);
1445  float f3 = ((notif.nent_floatcount > 2) ? ReadLong() : 0);
1446  float f4 = ((notif.nent_floatcount > 3) ? ReadLong() : 0);
1447 
1448  if (!is_new) return;
1450  net_type, notif,
1451  notif.nent_stringcount,
1452  notif.nent_floatcount,
1453  s1, s2, s3, s4,
1454  f1, f2, f3, f4);
1455 }
1456 #endif
1457 
1458 #ifdef SVQC
1459 void Net_Notification_Remove(entity this)
1460 {
1461  #ifdef NOTIFICATIONS_DEBUG
1462  Debug_Notification(sprintf(
1463  "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
1464  time,
1465  ((this.nent_net_name == -1) ? "Killed" : "Removed"),
1467  this.owner.nent_name
1468  ));
1469  #endif
1470  for (int i = 0; i < this.nent_stringcount; ++i) { strfree(this.nent_strings[i]); }
1471  delete(this);
1472 }
1473 
1474 bool Net_Write_Notification(entity this, entity client, int sf)
1475 {
1476  if (!Notification_ShouldSend(this.nent_broadcast, client, this.nent_client)) return false;
1477  WriteHeader(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1478  WriteByte(MSG_ENTITY, ORDINAL(this.nent_net_type));
1479  WriteShort(MSG_ENTITY, this.nent_net_name);
1480  for (int i = 0; i < this.nent_stringcount; ++i) { WriteString(MSG_ENTITY, this.nent_strings[i]); }
1481  for (int i = 0; i < this.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, this.nent_floats[i]); }
1482  return true;
1483 }
1484 
1485 void Kill_Notification(
1486  NOTIF broadcast, entity client,
1488  MSG net_type,
1490  CPID net_cpid)
1491 {
1492  #ifdef NOTIFICATIONS_DEBUG
1493  Debug_Notification(sprintf(
1494  "Kill_Notification(%s, '%s', %s, %d);\n",
1495  Get_Notif_BroadcastName(broadcast),
1496  client.netname,
1497  (net_type ? Get_Notif_TypeName(net_type) : "0"),
1498  net_cpid
1499  ));
1500  #endif
1501 
1502  string checkargs = Notification_CheckArgs(broadcast, client);
1503  if (checkargs != "") { LOG_WARNF("Incorrect usage of Kill_Notification: %s", checkargs); return; }
1504 
1505  entity net_notif = new_pure(net_kill_notification);
1506  net_notif.nent_broadcast = broadcast;
1507  net_notif.nent_client = client;
1508  net_notif.nent_net_type = MSG_CENTER_KILL;
1509  net_notif.nent_net_name = ORDINAL(net_cpid);
1510  Net_LinkEntity(net_notif, false, autocvar_notification_lifetime_runtime, Net_Write_Notification);
1511 
1512  IL_EACH(g_notifications,
1513  (it.owner.nent_type == net_type || net_type == MSG_Null) && (it.owner.nent_cpid == net_cpid || net_cpid == CPID_Null),
1514  {
1515  it.nent_net_name = -1;
1516  it.nextthink = time;
1517  }
1518  );
1519 }
1520 
1521 void Send_Notification(
1522  NOTIF broadcast, entity client,
1523  MSG net_type, Notification net_name,
1524  ...count)
1525 {
1526  if (broadcast == NOTIF_ONE_ONLY && !IS_REAL_CLIENT(client)) return;
1527  entity notif = net_name;
1528  string parms = sprintf("%s, '%s', %s, %s",
1529  Get_Notif_BroadcastName(broadcast),
1530  client.classname,
1531  Get_Notif_TypeName(net_type),
1532  net_name.registered_id
1533  );
1534  #ifdef NOTIFICATIONS_DEBUG
1535  Debug_Notification(sprintf("Send_Notification(%s, ...%d);\n", parms, count));
1536  #endif
1537 
1538  if (!notif)
1539  {
1540  LOG_WARN("Send_Notification: Could not find notification entity!");
1541  return;
1542  }
1543 
1544  // check supplied broadcast, target, type, and name for errors
1545  string checkargs = Notification_CheckArgs(broadcast, client);
1546  if (!net_name) { checkargs = sprintf("No notification provided! %s", checkargs); }
1547  if (checkargs != "")
1548  {
1549  LOG_WARNF("Incorrect usage of Send_Notification: %s", checkargs);
1550  return;
1551  }
1552 
1553  string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1554  string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1555  string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1556  string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1557  float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1558  float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1559  float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1560  float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1561 
1562  #ifdef NOTIFICATIONS_DEBUG
1563  Debug_Notification(sprintf(
1564  "Send_Notification(%s, %s, %s);\n",
1565  parms,
1566  MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1567  sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1568  ));
1569  #endif
1570 
1571  if ((notif.nent_stringcount + notif.nent_floatcount) != count)
1572  {
1573  LOG_WARNF(
1574  "Argument mismatch for Send_Notification(%s, ...)! "
1575  "stringcount(%d) + floatcount(%d) != count(%d)\n"
1576  "Check the definition and function call for accuracy...?\n",
1577  parms,
1578  notif.nent_stringcount,
1579  notif.nent_floatcount,
1580  count
1581  );
1582  return;
1583  }
1584 
1586  && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT)
1587  && !(net_type == MSG_ANNCE || net_type == MSG_CENTER)
1588  )
1589  {
1591  net_type, net_name,
1592  notif.nent_stringcount,
1593  notif.nent_floatcount,
1594  s1, s2, s3, s4,
1595  f1, f2, f3, f4);
1596  }
1597 
1598  if (net_type == MSG_CHOICE)
1599  {
1600  // THIS GETS TRICKY... now we have to cycle through each possible player (checking broadcast)
1601  // and then do an individual NOTIF_ONE_ONLY recursive call for each one depending on their option...
1602  // It's slow, but it's better than the alternatives:
1603  // 1. Constantly networking all info and letting client decide
1604  // 2. Manually handling each separate call on per-usage basis (See old CTF usage of verbose)
1605  entity found_choice;
1606 
1607  #define RECURSE_FROM_CHOICE(ent,action) MACRO_BEGIN \
1608  if (notif.nent_challow_var && (warmup_stage || (notif.nent_challow_var == 2))) { \
1609  switch (CS_CVAR(ent).msg_choice_choices[net_name.nent_choice_idx]) \
1610  { \
1611  case 1: found_choice = notif.nent_optiona; break; \
1612  case 2: found_choice = notif.nent_optionb; break; \
1613  default: action; \
1614  } \
1615  } else { \
1616  found_choice = notif.nent_optiona; \
1617  } \
1618  Send_Notification_WOVA( \
1619  NOTIF_ONE_ONLY, \
1620  ent, \
1621  found_choice.nent_type, \
1622  found_choice, \
1623  found_choice.nent_stringcount, \
1624  found_choice.nent_floatcount, \
1625  s1, s2, s3, s4, \
1626  f1, f2, f3, f4); \
1627  MACRO_END
1628 
1629  switch (broadcast)
1630  {
1631  case NOTIF_ONE_ONLY: // we can potentially save processing power with this broadcast method
1632  {
1633  if (IS_REAL_CLIENT(client)) {
1634  RECURSE_FROM_CHOICE(client, return);
1635  }
1636  break;
1637  }
1638  default:
1639  {
1640  FOREACH_CLIENT(Notification_ShouldSend(broadcast, it, client), {
1641  RECURSE_FROM_CHOICE(it, continue);
1642  });
1643  break;
1644  }
1645  }
1646  }
1647  else
1648  {
1649  entity net_notif = new_pure(net_notification);
1650  IL_PUSH(g_notifications, net_notif);
1651  net_notif.owner = notif;
1652  net_notif.nent_broadcast = broadcast;
1653  net_notif.nent_client = client;
1654  net_notif.nent_net_type = net_type;
1655  net_notif.nent_net_name = notif.m_id;
1656  net_notif.nent_stringcount = notif.nent_stringcount;
1657  net_notif.nent_floatcount = notif.nent_floatcount;
1658 
1659  for (int i = 0; i < net_notif.nent_stringcount; ++i) {
1660  net_notif.nent_strings[i] = strzone(...(i, string));
1661  }
1662  for (int i = 0; i < net_notif.nent_floatcount; ++i) {
1663  net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float);
1664  }
1665 
1666  setthink(net_notif, Net_Notification_Remove);
1667  net_notif.nextthink = (time > autocvar_notification_lifetime_mapload)
1668  ? (time + autocvar_notification_lifetime_runtime)
1669  : autocvar_notification_lifetime_mapload;
1670 
1671  Net_LinkEntity(net_notif, false, 0, Net_Write_Notification);
1672  }
1673 }
1674 
1675 // WOVA = Without Variable Arguments
1676 void Send_Notification_WOVA(
1677  NOTIF broadcast, entity client,
1678  MSG net_type, Notification net_name,
1679  float stringcount, float floatcount,
1680  string s1, string s2, string s3, string s4,
1681  float f1, float f2, float f3, float f4)
1682 {
1683  #ifdef NOTIFICATIONS_DEBUG
1684  entity notif = net_name;
1685  Debug_Notification(sprintf(
1686  "Send_Notification_WOVA(%s, %d, %d, %s, %s);\n",
1687  sprintf(
1688  "%s, '%s', %s, %s",
1689  Get_Notif_BroadcastName(broadcast),
1690  client.classname,
1691  Get_Notif_TypeName(net_type),
1692  notif.nent_name
1693  ),
1694  stringcount,
1695  floatcount,
1696  MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1697  sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1698  ));
1699  #endif
1700 
1701  #define VARITEM(stringc, floatc, args) \
1702  if ((stringcount == stringc) && (floatcount == floatc)) \
1703  { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1705  #undef VARITEM
1706  Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1707 }
1708 
1709 // WOCOVA = Without Counts Or Variable Arguments
1710 void Send_Notification_WOCOVA(
1711  NOTIF broadcast, entity client,
1712  MSG net_type, Notification net_name,
1713  string s1, string s2, string s3, string s4,
1714  float f1, float f2, float f3, float f4)
1715 {
1716  entity notif = net_name;
1717 
1718  #ifdef NOTIFICATIONS_DEBUG
1719  Debug_Notification(sprintf(
1720  "Send_Notification_WOCOVA(%s, %s, %s);\n",
1721  sprintf(
1722  "%s, '%s', %s, %s",
1723  Get_Notif_BroadcastName(broadcast),
1724  client.classname,
1725  Get_Notif_TypeName(net_type),
1726  notif.nent_name
1727  ),
1728  MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1729  sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1730  ));
1731  #endif
1732 
1733  #define VARITEM(stringc, floatc, args) \
1734  if ((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1735  { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1737  #undef VARITEM
1738  Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1739 }
1740 #endif // ifdef SVQC
bool notif_error
Definition: all.qh:717
#define IL_EACH(this, cond, body)
#define LOG_WARN(...)
Definition: log.qh:66
void Dump_Notifications(int fh, bool alsoprint)
used to output notifications.cfg file
Definition: all.qc:781
#define REGISTER_NET_LINKED(id)
Definition: net.qh:67
#define NOTIF_ARGUMENT_LIST
Definition: all.qh:403
spree_inf s1 s2 s3loc s2 s1
Definition: all.inc:265
#define NOTIF_HIT_UNKNOWN(token, funcname)
Definition: all.qh:452
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
void centerprint_Kill(int id)
Definition: centerprint.qc:126
const float NOTIF_MAX_HUDARGS
Definition: all.qh:386
#define ReadString
float(entity, float) PlayerPhysplug
#define ORDINAL(it)
Definition: enumclass.qh:25
Notification Get_Notif_Ent(MSG net_type, int net_name)
Definition: all.qh:734
float nent_net_name
Definition: all.qh:696
entity() spawn
MSG nent_net_type
Definition: all.qh:695
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
ERASEABLE string cdr(string s)
returns all but first word
Definition: string.qh:249
bool warmup_stage
Definition: main.qh:103
ERASEABLE string MakeConsoleSafe(string input)
escape the string to make it safe for consoles
Definition: cvar.qh:24
#define NET_HANDLE(id, param)
Definition: net.qh:12
const float NOTIF_MAX_ARGS
Definition: all.qh:385
int nent_stringcount
Definition: all.qh:661
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
#define NOTIF_WRITE(str)
#define NOTIF_HIT_MAX(count, funcname)
Definition: all.qh:448
const float NOTIF_MAX_DURCNT
Definition: all.qh:387
ERASEABLE string car(string s)
returns first word
Definition: string.qh:240
void Create_Notification_Entity(entity notif, float var_default, float var_cvar, MSG typeId, string namestring, int teamnum)
Definition: all.qc:396
float autocvar_notification_allow_chatboxprint
Definition: all.qh:324
entity owner
Definition: main.qh:73
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
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
string Local_Notification_sprintf(string input, string args, string s1, string s2, string s3, string s4, int f1, float f2, float f3, float f4)
Definition: all.qc:993
float autocvar_cl_announcer_antispam
Definition: announcer.qh:4
#define strcpy(this, s)
Definition: string.qh:49
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
#define LOG_WARNF(...)
Definition: log.qh:67
void Destroy_Notification_Entity(entity notif)
Definition: all.qc:147
spree_cen s1 CPID_Null
Definition: all.inc:583
#define SET_NOTIF_STRING(string, stringname)
string Get_Notif_TypeName(MSG net_type)
main types/groups of notifications
Definition: all.qh:30
#define IS_SPEC(v)
Definition: utils.qh:10
string Process_Notif_Line(MSG typeId, bool chat, string input, string notiftype, string notifname, string stringtype)
Definition: all.qc:172
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
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define LOG_INFOF(...)
Definition: log.qh:71
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"))
void Destroy_All_Notifications()
Definition: all.qc:159
#define NOTIF_WRITE_HARDCODED(cvar, default, description)
#define NULL
Definition: post.qh:17
#define backtrace(msg)
Definition: log.qh:105
#define AnnouncerFilename(snd)
Definition: all.qc:444
int nent_floatcount
Definition: all.qh:662
#define IS_NOT_A_CLIENT(v)
was: (clienttype(v) == CLIENTTYPE_NOTACLIENT)
Definition: utils.qh:19
string Process_Notif_Args(float arg_type, string args, string notiftype, string notifname)
Definition: all.qc:219
entity nent_client
Definition: all.qh:694
#define make_pure(e)
Definition: oo.qh:12
float teamplay
Definition: progsdefs.qc:31
string Get_Notif_CvarName(Notification notif)
Definition: all.qh:727
void Create_Notification_Entity_Annce(entity notif, float var_cvar, string namestring, float channel, string snd, float vol, float position)
Definition: all.qc:446
float count
Definition: powerups.qc:22
entity Notification
always last
Definition: all.qh:82
#define _sound(e, c, s, v, a)
Definition: sound.qh:50
void centerprint_Add(int new_id, string strMessage, float duration, int countdown_num)
Definition: centerprint.qc:46
#define EIGHT_VARS_TO_VARARGS_VARLIST
Definition: all.qh:88
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
#define setthink(e, f)
void Create_Notification_Entity_Multi(entity notif, float var_cvar, string namestring, Notification anncename, Notification infoname, Notification centername)
Definition: all.qc:651
void HUD_Notify_Push(string icon, string attacker, string victim)
Definition: notify.qc:19
#define strfree(this)
Definition: string.qh:56
const int NUM_TEAM_1
Definition: teams.qh:18
void Local_Notification(MSG net_type, Notification net_name,...count)
Definition: all.qc:1185
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
float time
Definition: csprogsdefs.qc:16
void centerprint_KillAll()
Definition: centerprint.qc:137
string arg_slot[NOTIF_MAX_ARGS]
Definition: all.qh:389
#define ENUMCAST(T, it)
Definition: enumclass.qh:26
#define FOREACH(list, cond, body)
Definition: iter.qh:19
bool server_is_dedicated
Definition: world.qh:37
float nent_floats[4]
Definition: all.qh:698