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