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#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>
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
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
80bool 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 {
183 if((chat && autocvar_notification_allow_chatboxprint)
184 || (autocvar_notification_allow_chatboxprint == 2))
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 float queuetime)
455 {
456 // Set MSG_ANNCE information and handle precaching
457 #ifdef CSQC
458 MSG typeId = MSG_ANNCE;
459 if (!(GENTLE && (var_cvar == 1)))
460 {
461 if(snd != "")
462 {
463 if(notif.nent_enabled)
464 {
466 notif.nent_channel = channel;
467 notif.nent_snd = strzone(snd);
468 notif.nent_vol = vol;
469 notif.nent_position = position;
470 notif.nent_queuetime = queuetime;
471 }
472 }
473 else
474 {
475 string typestring = Get_Notif_TypeName(typeId);
476 LOG_INFOF(
477 (
478 "^1NOTIFICATION WITH NO SOUND: "
479 "^7net_type = %s, net_name = %s."
480 ),
481 typestring,
482 namestring
483 );
484 notif_error = true;
485 }
486 }
487 else { notif.nent_enabled = false; }
488 #else
489 notif.nent_enabled = false;
490 #endif
491
492 }
493
495 float var_cvar,
496 string namestring,
497 int strnum,
498 int flnum,
499 /* MSG_INFO & MSG_CENTER */
500 string args,
501 string hudargs,
502 string icon,
503 CPID cpid,
504 string durcnt,
505 string normal,
506 string gentle)
507 {
508 MSG typeId = notif.nent_type;
509 // Set MSG_INFO and MSG_CENTER string/float counts
510 notif.nent_stringcount = strnum;
511 notif.nent_floatcount = flnum;
512
513 // Only initialize arguments if we're either a client or on a dedicated server
514 #ifdef SVQC
515 float should_process_args = autocvar_sv_dedicated;
516 #else
517 float should_process_args = true;
518 #endif
519 string typestring = Get_Notif_TypeName(typeId);
520 if(should_process_args)
521 {
522 // ========================
523 // Process Main Arguments
524 // ========================
525 if(strnum + flnum)
526 {
527 if(args != "")
528 {
529 notif.nent_args = strzone(
530 Process_Notif_Args(1, args, typestring, namestring));
531 }
532 else if((hudargs == "") && (durcnt ==""))
533 {
534 LOG_INFOF(
535 (
536 "^1NOTIFICATION HAS ARG COUNTS BUT NO ARGS OR HUDARGS OR DURCNT: "
537 "^7net_type = %s, net_name = %s, strnum = %d, flnum = %d"
538 ),
539 typestring,
540 namestring,
541 strnum,
542 flnum
543 );
544 notif_error = true;
545 }
546 }
547 else if(args != "")
548 {
549 notif.nent_args = strzone(
550 Process_Notif_Args(1, args, typestring, namestring));
551 }
552
553
554 // =======================================
555 // Process HUD and Centerprint Arguments
556 // Only processed on CSQC, as these
557 // args are only for HUD features.
558 // =======================================
559 #ifdef CSQC
560 if(hudargs != "")
561 {
562 notif.nent_hudargs = strzone(
563 Process_Notif_Args(2, hudargs, typestring, namestring));
564
565 if(icon != "") { notif.nent_icon = strzone(icon); }
566 else
567 {
568 LOG_INFOF(
569 (
570 "^1NOTIFICATION HAS HUDARGS BUT NO ICON: "
571 "^7net_type = %s, net_name = %s."
572 ),
573 typestring,
574 namestring
575 );
576 notif_error = true;
577 }
578 }
579 else if(icon != "")
580 {
581 LOG_WARNF(
582 (
583 "^1NOTIFICATION HAS ICON BUT NO HUDARGS: "
584 "^7net_type = %s, net_name = %s.\n"
585 ),
586 typestring,
587 namestring
588 );
589 notif_error = true;
590 }
591
592 if (durcnt != "")
593 {
594 notif.nent_durcnt = strzone(Process_Notif_Args(3, durcnt, typestring, namestring));
595
596 if (cpid == CPID_Null && durcnt != "0 0")
597 {
598 LOG_WARNF(
599 (
600 "Notification has durcnt but no cpid: "
601 "net_type = %s, net_name = %s."
602 ),
603 typestring,
604 namestring
605 );
606 notif_error = true;
607 }
608 }
609 notif.nent_cpid = cpid;
610 #endif
611
612
613 // ======================
614 // Process Notif String
615 // ======================
616 #define SET_NOTIF_STRING(string,stringname) MACRO_BEGIN \
617 notif.nent_string = strzone(CCR( \
618 Process_Notif_Line( \
619 typeId, \
620 (var_cvar > 1), \
621 string, \
622 typestring, \
623 namestring, \
624 stringname \
625 )) \
626 ); \
627 MACRO_END
628
629 if(GENTLE)
630 {
631 if(gentle != "") { SET_NOTIF_STRING(gentle, "GENTLE"); }
632 else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL"); }
633 }
634 else if(normal != "") { SET_NOTIF_STRING(normal, "NORMAL"); }
635 #undef SET_NOTIF_STRING
636
637 // Check to make sure a string was chosen
638 if(notif.nent_string == "")
639 {
640 LOG_INFOF(
641 (
642 "^1EMPTY NOTIFICATION: "
643 "^7net_type = %s, net_name = %s."
644 ),
645 typestring,
646 namestring
647 );
648 notif_error = true;
649 }
650 }
651 }
652
654 float var_cvar,
655 string namestring,
656 /* MSG_MULTI */
657 Notification anncename,
658 Notification infoname,
659 Notification centername)
660 {
661 MSG typeId = MSG_MULTI;
662 // Set MSG_MULTI string/float counts
663 if (!anncename && !infoname && !centername)
664 {
665 string typestring = Get_Notif_TypeName(typeId);
666 LOG_INFOF(
667 (
668 "^1NOTIFICATION WITH NO SUBCALLS: "
669 "^7net_type = %s, net_name = %s."
670 ),
671 typestring,
672 namestring
673 );
674 notif_error = true;
675 }
676 else
677 {
678 // announcements don't actually need any arguments, so lets not even count them.
679 if (anncename) { notif.nent_msgannce = anncename; }
680
681 float infoname_stringcount = 0, infoname_floatcount = 0;
682 float centername_stringcount = 0, centername_floatcount = 0;
683
684 if (infoname)
685 {
686 notif.nent_msginfo = infoname;
687 infoname_stringcount = notif.nent_msginfo.nent_stringcount;
688 infoname_floatcount = notif.nent_msginfo.nent_floatcount;
689 }
690
691 if (centername)
692 {
693 notif.nent_msgcenter = centername;
694 centername_stringcount = notif.nent_msgcenter.nent_stringcount;
695 centername_floatcount = notif.nent_msgcenter.nent_floatcount;
696 }
697
698 // set the requirements of THIS notification to the totals of its subcalls
699 notif.nent_stringcount = max(infoname_stringcount, centername_stringcount);
700 notif.nent_floatcount = max(infoname_floatcount, centername_floatcount);
701 }
702 }
703
705 float var_cvar,
706 string namestring,
707 /* MSG_CHOICE */
708 float challow_def,
709 float challow_var,
710 MSG chtype,
711 Notification optiona,
712 Notification optionb)
713 {
714 MSG typeId = MSG_CHOICE;
715 if (chtype == MSG_Null || !optiona || !optionb)
716 {
717 string typestring = Get_Notif_TypeName(typeId);
718 LOG_INFOF(
719 (
720 "^1NOTIFICATION IS MISSING CHOICE PARAMS: "
721 "^7net_type = %s, net_name = %s."
722 ),
723 typestring,
724 namestring
725 );
726 notif_error = true;
727 }
728 else
729 {
730 notif.nent_optiona = optiona;
731 notif.nent_optionb = optionb;
732 notif.nent_challow_def = challow_def; // 0: never allowed, 1: allowed in warmup, 2: always allowed
733 notif.nent_challow_var = challow_var; // 0: never allowed, 1: allowed in warmup, 2: always allowed
734 notif.nent_stringcount = max(notif.nent_optiona.nent_stringcount, notif.nent_optionb.nent_stringcount);
735 notif.nent_floatcount = max(notif.nent_optiona.nent_floatcount, notif.nent_optionb.nent_floatcount);
736
737 /*#ifdef NOTIFICATIONS_DEBUG
738 Debug_Notification(sprintf(
739 "Create_Notification_Entity(...): MSG_CHOICE: %s\n%s\n%s\n",
740 notif.nent_name,
741 sprintf(
742 "^ optiona: %s %s : %d %d",
743 Get_Notif_TypeName(notif.nent_optiona.nent_type),
744 notif.nent_optiona.nent_name,
745 notif.nent_optiona.nent_stringcount,
746 notif.nent_optiona.nent_floatcount
747 ),
748 sprintf(
749 "^ optionb: %s %s : %d %d",
750 Get_Notif_TypeName(notif.nent_optionb.nent_type),
751 notif.nent_optionb.nent_name,
752 notif.nent_optionb.nent_stringcount,
753 notif.nent_optionb.nent_floatcount
754 )
755 ));
756 #endif*/
757 }
758 }
759
760
761// ===============
762// Cvar Handling
763// ===============
764
765// used by MSG_CHOICE to build list of choices
766#ifdef SVQC
768{
769 FOREACH(Notifications, it.nent_type == MSG_CHOICE && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
770 GetCvars_handleFloat(
771 this,
772 store,
773 get_cvars_s,
774 get_cvars_f,
775 msg_choice_choices[it.nent_choice_idx],
776 sprintf("notification_%s", Get_Notif_CvarName(it))
777 );
778 });
779}
780#endif
781
783void Dump_Notifications(int fh, bool alsoprint)
784{
785 #define NOTIF_WRITE(str) write_String_To_File(fh, str, alsoprint)
786
787 #define NOTIF_WRITE_ENTITY(e, description) \
788 NOTIF_WRITE(sprintf( \
789 "seta notification_%s \"%d\" \"%s\"\n", \
790 Get_Notif_CvarName(e), e.nent_default, description \
791 ))
792
793 #define NOTIF_WRITE_ENTITY_CHOICE(e, descriptiona, descriptionb) \
794 NOTIF_WRITE(sprintf( \
795 "seta notification_%s \"%d\" \"%s\"\n" \
796 "seta notification_%s_ALLOWED \"%d\" \"%s\"\n", \
797 Get_Notif_CvarName(e), e.nent_default, descriptiona, \
798 Get_Notif_CvarName(e), e.nent_challow_def, descriptionb \
799 ))
800
801 #define NOTIF_WRITE_HARDCODED(cvar, default, description) \
802 NOTIF_WRITE("seta notification_" cvar " \"" default "\" \"" description "\"\n")
803
804 // Note: This warning only applies to the notifications.cfg file that is output...
805 // You ARE supposed to manually edit this function to add i.e. hard coded
806 // notification variables for mutators or gametypes or such and then
807 // regenerate the notifications.cfg file from the new code.
808
810 "// ********************************************** //\n"
811 "// ** WARNING - DO NOT MANUALLY EDIT THIS FILE ** //\n"
812 "// ** ** //\n"
813 "// ** This file is automatically generated ** //\n"
814 "// ** by code with the command 'dumpnotifs'. ** //\n"
815 "// ** ** //\n"
816 "// ** If you add a new notification, please ** //\n"
817 "// ** regenerate this file with that command ** //\n"
818 "// ** making sure that the output matches ** //\n"
819 "// ** with the lists and defaults in code. ** //\n"
820 "// ** ** //\n"
821 "// ********************************************** //\n");
822
823 // These notifications will also append their string as a comment...
824 // This is not necessary, and does not matter if they vary between config versions,
825 // it is just a semi-helpful tool for those who want to manually change their user settings.
826
827 int NOTIF_ANNCE_COUNT = 0;
828 int NOTIF_INFO_COUNT = 0;
829 int NOTIF_CENTER_COUNT = 0;
830 int NOTIF_MULTI_COUNT = 0;
831 int NOTIF_CHOICE_COUNT = 0;
832 FOREACH(Notifications, true, {
833 switch (it.nent_type)
834 {
835 case MSG_ANNCE: ++NOTIF_ANNCE_COUNT; break;
836 case MSG_INFO: ++NOTIF_INFO_COUNT; break;
837 case MSG_CENTER: ++NOTIF_CENTER_COUNT; break;
838 case MSG_MULTI: ++NOTIF_MULTI_COUNT; break;
839 case MSG_CHOICE: ++NOTIF_CHOICE_COUNT; break;
840 }
841 });
842
843 NOTIF_WRITE(sprintf("\n// MSG_ANNCE notifications:\n"));
844 FOREACH(Notifications, it.nent_type == MSG_ANNCE && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
845 NOTIF_WRITE_ENTITY(it,
846 "\\\"0\\\" = disabled, \\\"1\\\" = enabled if gentle mode is off, \\\"2\\\" = always enabled"
847 );
848 });
849
850 NOTIF_WRITE(sprintf("\n// MSG_INFO notifications:\n"));
851 FOREACH(Notifications, it.nent_type == MSG_INFO && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
852 NOTIF_WRITE_ENTITY(it,
853 "\\\"0\\\" = off, \\\"1\\\" = print to console, "
854 "\\\"2\\\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
855 );
856 });
857
858 NOTIF_WRITE(sprintf("\n// MSG_CENTER notifications:\n"));
859 FOREACH(Notifications, it.nent_type == MSG_CENTER && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
860 NOTIF_WRITE_ENTITY(it,
861 "\\\"0\\\" = off, \\\"1\\\" = centerprint"
862 );
863 });
864
865 NOTIF_WRITE(sprintf("\n// MSG_MULTI notifications:\n"));
866 FOREACH(Notifications, it.nent_type == MSG_MULTI && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
867 NOTIF_WRITE_ENTITY(it,
868 "enable this multiple notification"
869 );
870 });
871
872 NOTIF_WRITE(sprintf("\n// MSG_CHOICE notifications:\n"));
873 FOREACH(Notifications, it.nent_type == MSG_CHOICE && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), {
874 NOTIF_WRITE_ENTITY_CHOICE(it,
875 "choice for this notification; \\\"0\\\" = off, \\\"1\\\" = default message, \\\"2\\\" = verbose message",
876 "allow choice for this notification; \\\"0\\\" = off, \\\"1\\\" = only in warmup-stage, \\\"2\\\" = always"
877 );
878 });
879
880 // edit these to match whichever cvars are used for specific notification options
881 NOTIF_WRITE("\n// HARD CODED notification variables:\n");
882
884 "allow_chatboxprint", "1",
885 "allow INFO notifications to be printed to chat box; "
886 "\\\"0\\\" = do not allow, "
887 "\\\"1\\\" = allow only if allowed by individual notification_INFO* cvars, "
888 "\\\"2\\\" = force all INFO notifications to be printed to the chatbox"
889 );
890
892 "debug", "0",
893 "print extra debug information on all notification function calls "
894 "(REQUIRES: -DNOTIFICATIONS_DEBUG flag to be enabled on QCSRC compilation); "
895 "\\\"0\\\" = disabled, \\\"1\\\" = dprint, \\\"2\\\" = print"
896 );
897
899 "errors_are_fatal", "1",
900 "if a notification fails upon initialization, cause a Host_Error to stop the program"
901 );
902
904 "item_centerprinttime", "1.5",
905 "how long to show item information centerprint messages (like 'You got the Electro' or such)"
906 );
907
909 "lifetime_mapload", "10",
910 "amount of time that notification entities last immediately at mapload (in seconds) "
911 "to help prevent notifications from being lost on early init (like gamestart countdown)"
912 );
913
915 "lifetime_runtime", "0.5",
916 "amount of time that notification entities last on the server during runtime (in seconds)"
917 );
918
920 "server_allows_location", "1",
921 "server side cvar for allowing death messages to show location information too"
922 );
923
925 "show_location", "0",
926 "append location information to MSG_INFO death/kill messages"
927 );
928
930 "show_location_string", "",
931 "replacement string piped into sprintf, "
932 "so you can do different messages like ' at the %s' or ' (near %s)'"
933 );
934
936 "show_sprees", "1",
937 "print information about sprees in death/kill messages"
938 );
939
941 "show_sprees_center", "1",
942 "show spree information in MSG_CENTER messages; "
943 "\\\"0\\\" = off, \\\"1\\\" = target (but only for first victim) and attacker"
944 );
945
947 "show_sprees_center_specialonly", "1",
948 "do not show spree information in MSG_CENTER messages if it is not an achievement"
949 );
950
952 "show_sprees_info", "3",
953 "show spree information in MSG_INFO messages; "
954 "\\\"0\\\" = off, \\\"1\\\" = target only, \\\"2\\\" = attacker only, \\\"3\\\" = target and attacker"
955 );
956
958 "show_sprees_info_newline", "1",
959 "show attacker spree information for MSG_INFO messages on a separate line than the death notification itself"
960 );
961
963 "show_sprees_info_specialonly", "1",
964 "do not show attacker spree information in MSG_INFO messages if it is not an achievement"
965 );
966
967 LOG_INFOF("Notification counts (total = %d): "
968 "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d\n",
969 (
970 NOTIF_ANNCE_COUNT +
971 NOTIF_INFO_COUNT +
972 NOTIF_CENTER_COUNT +
973 NOTIF_MULTI_COUNT +
974 NOTIF_CHOICE_COUNT
975 ),
976 NOTIF_ANNCE_COUNT,
977 NOTIF_INFO_COUNT,
978 NOTIF_CENTER_COUNT,
979 NOTIF_MULTI_COUNT,
980 NOTIF_CHOICE_COUNT
981 );
982 #undef NOTIF_WRITE_HARDCODED
983 #undef NOTIF_WRITE_ENTITY
984 #undef NOTIF_WRITE
985}
986
987
988// ===============================
989// Frontend Notification Pushing
990// ===============================
991
993 string input, string args,
994 string s1, string s2, string s3, string s4,
995 int f1, float f2, float f3, float f4)
996{
997 #ifdef NOTIFICATIONS_DEBUG
998 Debug_Notification(sprintf(
999 "Local_Notification_sprintf('%s^7', '%s', %s, %s);\n",
1000 MakeConsoleSafe(input),
1001 args,
1002 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1003 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1004 ));
1005 #endif
1006
1007 for (int sel_num = 0; sel_num < NOTIF_MAX_ARGS; ++sel_num) { arg_slot[sel_num] = ""; }
1008
1009 for (int sel_num = 0; (args != ""); )
1010 {
1011 string selected = car(args); args = cdr(args);
1012 NOTIF_HIT_MAX(NOTIF_MAX_ARGS, "Local_Notification_sprintf");
1013 string tmp_s; // used by NOTIF_ARGUMENT_LIST
1014 switch (strtolower(selected))
1015 {
1016 #define ARG_CASE_ARG_CS_SV_HA(selected, result) case selected: arg_slot[sel_num++] = result; break;
1017 #define ARG_CASE_ARG_CS_SV_DC(selected, result) case selected: arg_slot[sel_num++] = result; break;
1018 #define ARG_CASE_ARG_CS_SV(selected, result) case selected: arg_slot[sel_num++] = result; break;
1019#ifdef CSQC
1020 #define ARG_CASE_ARG_CS(selected, result) case selected: arg_slot[sel_num++] = result; break;
1021 #define ARG_CASE_ARG_SV(selected, result)
1022#else
1023 #define ARG_CASE_ARG_CS(selected, result)
1024 #define ARG_CASE_ARG_SV(selected, result) case selected: arg_slot[sel_num++] = result; break;
1025#endif
1026 #define ARG_CASE_ARG_DC(selected, result)
1027 #define ARG_CASE(prog, selected, result) ARG_CASE_##prog(selected, result)
1029 #undef ARG_CASE
1030 #undef ARG_CASE_ARG_DC
1031 #undef ARG_CASE_ARG_SV
1032 #undef ARG_CASE_ARG_CS
1033 #undef ARG_CASE_ARG_CS_SV
1034 #undef ARG_CASE_ARG_CS_SV_DC
1035 #undef ARG_CASE_ARG_CS_SV_HA
1036 default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_ARGS, "Local_Notification_sprintf")
1037 }
1038 }
1039 return sprintf(
1040 strcat(input, "\n"),
1041 arg_slot[0],
1042 arg_slot[1],
1043 arg_slot[2],
1044 arg_slot[3],
1045 arg_slot[4],
1046 arg_slot[5],
1047 arg_slot[6]
1048 );
1049}
1050
1051#ifdef CSQC
1052void Local_Notification_sound(int soundchannel, string soundfile, float soundvolume, float soundposition)
1053{
1055 {
1056 #ifdef NOTIFICATIONS_DEBUG
1057 Debug_Notification(sprintf(
1058 "Local_Notification_sound(%f, '%s', %f, %f);\n",
1059 soundchannel,
1060 AnnouncerFilename(soundfile),
1061 soundvolume,
1062 soundposition
1063 ));
1064 #endif
1065
1066 _sound(NULL, soundchannel, AnnouncerFilename(soundfile), soundvolume, soundposition);
1067
1068 strcpy(prev_soundfile, soundfile);
1070 }
1071 else
1072 {
1073 #ifdef NOTIFICATIONS_DEBUG
1074 Debug_Notification(sprintf(
1075 (
1076 "Local_Notification_sound(%f, '%s', %f, %f) "
1077 "^1BLOCKED BY ANTISPAM:^7 prevsnd: '%s', timediff: %f, limit: %f\n"
1078 ),
1079 soundchannel,
1080 AnnouncerFilename(soundfile),
1081 soundvolume,
1082 soundposition,
1084 (time - prev_soundtime),
1086 ));
1087 #endif
1088 }
1089}
1090
1092 string icon, string hudargs,
1093 string s1, string s2, string s3, string s4,
1094 float f1, float f2, float f3, float f4)
1095{
1096 arg_slot[0] = ""; arg_slot[1] = "";
1097
1098 for (int sel_num = 0; (hudargs != ""); )
1099 {
1100 string selected = car(hudargs); hudargs = cdr(hudargs);
1101 NOTIF_HIT_MAX(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push");
1102 switch (strtolower(selected))
1103 {
1104 #define ARG_CASE_ARG_CS_SV_HA(selected, result) case selected: arg_slot[sel_num++] = result; break;
1105 #define ARG_CASE_ARG_CS_SV_DC(selected, result)
1106 #define ARG_CASE_ARG_CS_SV(selected, result)
1107 #define ARG_CASE_ARG_CS(selected, result)
1108 #define ARG_CASE_ARG_SV(selected, result)
1109 #define ARG_CASE_ARG_DC(selected, result)
1110 #define ARG_CASE(prog, selected, result) ARG_CASE_##prog(selected, result)
1112 #undef ARG_CASE
1113 #undef ARG_CASE_ARG_DC
1114 #undef ARG_CASE_ARG_SV
1115 #undef ARG_CASE_ARG_CS
1116 #undef ARG_CASE_ARG_CS_SV
1117 #undef ARG_CASE_ARG_CS_SV_DC
1118 #undef ARG_CASE_ARG_CS_SV_HA
1119 default: NOTIF_HIT_UNKNOWN(NOTIF_MAX_HUDARGS, "Local_Notification_HUD_Notify_Push")
1120 }
1121 }
1122 #ifdef NOTIFICATIONS_DEBUG
1123 Debug_Notification(sprintf(
1124 "Local_Notification_HUD_Notify_Push('%s^7', '%s', %s, %s, %s);\n",
1125 icon,
1126 hudargs,
1127 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1128 sprintf("%d, %d, %d, %d", f1, f2, f3, f4),
1129 MakeConsoleSafe(sprintf("'%s^7', '%s^7'", stof(arg_slot[0]), stof(arg_slot[1])))
1130 ));
1131 #endif
1132 HUD_Notify_Push(icon, arg_slot[0], arg_slot[1]);
1133}
1134
1136 string input, string durcnt,
1137 CPID cpid, float f1, float f2)
1138{
1139 arg_slot[0] = ""; arg_slot[1] = "";
1140
1141 for (int sel_num = 0; (durcnt != ""); )
1142 {
1143 string selected = car(durcnt); durcnt = cdr(durcnt);
1144 NOTIF_HIT_MAX(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_Add");
1145 switch (strtolower(selected))
1146 {
1147 #define ARG_CASE_ARG_CS_SV_HA(selected, result)
1148 #define ARG_CASE_ARG_CS_SV_DC(selected, result) case selected: arg_slot[sel_num++] = result; break;
1149 #define ARG_CASE_ARG_CS_SV(selected, result)
1150 #define ARG_CASE_ARG_CS(selected, result)
1151 #define ARG_CASE_ARG_SV(selected, result)
1152 #define ARG_CASE_ARG_DC(selected, result) case selected: arg_slot[sel_num++] = result; break;
1153 #define ARG_CASE(prog, selected, result) ARG_CASE_##prog(selected,result)
1155 #undef ARG_CASE
1156 #undef ARG_CASE_ARG_DC
1157 #undef ARG_CASE_ARG_SV
1158 #undef ARG_CASE_ARG_CS
1159 #undef ARG_CASE_ARG_CS_SV
1160 #undef ARG_CASE_ARG_CS_SV_DC
1161 #undef ARG_CASE_ARG_CS_SV_HA
1162 default:
1163 {
1164 if (/* wtf */ ftos(stof(selected)) != "") { arg_slot[sel_num++] = selected; }
1165 else { NOTIF_HIT_UNKNOWN(NOTIF_MAX_DURCNT, "Local_Notification_centerprint_Add") }
1166 break;
1167 }
1168 }
1169 }
1170 #ifdef NOTIFICATIONS_DEBUG
1171 Debug_Notification(sprintf(
1172 "Local_Notification_centerprint_Add('%s^7', '%s', %d, %d, %d, %d);\n",
1173 MakeConsoleSafe(input),
1174 durcnt,
1175 f1, f2,
1176 stof(arg_slot[0]),
1177 stof(arg_slot[1])
1178 ));
1179 #endif
1180 centerprint_Add(ORDINAL(cpid), input, stof(arg_slot[0]), stof(arg_slot[1]));
1181}
1182
1183void Local_Notification_Queue_Run(MSG net_type, entity notif)
1184{
1185 switch (net_type)
1186 {
1187 case MSG_ANNCE:
1188 {
1189 Local_Notification_sound(notif.nent_channel, notif.nent_snd, notif.nent_vol, notif.nent_position);
1190 break;
1191 }
1192 }
1193}
1194
1195void Local_Notification_Queue_Add(MSG net_type, entity notif, float queue_time)
1196{
1197 // Guess length if required
1198 if(queue_time == 0)
1199 queue_time = soundlength(AnnouncerFilename(notif.nent_snd));
1200
1201 if(queue_time == -1 || time > notif_queue_next_time) {
1202 // Run immediately
1203 Local_Notification_Queue_Run(net_type, notif);
1204 notif_queue_next_time = time + queue_time;
1205 } else {
1206 // Put in queue
1207 if(notif_queue_length >= NOTIF_QUEUE_MAX) return;
1208
1212
1213 notif_queue_next_time += queue_time;
1215 }
1216}
1217
1219{
1221 return;
1222
1224
1225 // Shift queue to the left
1227 for (int j = 0; j < notif_queue_length; ++j) {
1231 }
1232}
1233
1234#endif
1235
1236void Local_Notification(MSG net_type, Notification net_name, ...count)
1237{
1238 // retreive entity of this notification
1239 entity notif = net_name;
1240 if (!notif)
1241 {
1242 #ifdef NOTIFICATIONS_DEBUG
1243 Debug_Notification(sprintf(
1244 "Local_Notification(%s, NULL, ...);\n",
1245 Get_Notif_TypeName(net_type)
1246 ));
1247 #endif
1248 LOG_WARNF("Incorrect usage of Local_Notification: %s", "Null notification");
1249 return;
1250 }
1251
1252 // check if the notification is enabled
1253 if (!notif.nent_enabled)
1254 {
1255 #ifdef NOTIFICATIONS_DEBUG
1256 Debug_Notification(sprintf(
1257 "Local_Notification(%s, %s, ...): Entity was disabled...\n",
1258 Get_Notif_TypeName(net_type),
1259 notif.nent_name
1260 ));
1261 #endif
1262 return;
1263 }
1264
1265 string s1 = (notif.nent_stringcount > 0) ? ...(0, string) : "";
1266 string s2 = (notif.nent_stringcount > 1) ? ...(1, string) : "";
1267 string s3 = (notif.nent_stringcount > 2) ? ...(2, string) : "";
1268 string s4 = (notif.nent_stringcount > 3) ? ...(3, string) : "";
1269 float f1 = ((notif.nent_floatcount > 0) ? ...((notif.nent_stringcount + 0), float) : 0);
1270 float f2 = ((notif.nent_floatcount > 1) ? ...((notif.nent_stringcount + 1), float) : 0);
1271 float f3 = ((notif.nent_floatcount > 2) ? ...((notif.nent_stringcount + 2), float) : 0);
1272 float f4 = ((notif.nent_floatcount > 3) ? ...((notif.nent_stringcount + 3), float) : 0);
1273
1274 #ifdef NOTIFICATIONS_DEBUG
1275 Debug_Notification(sprintf(
1276 "Local_Notification(%s, %s, %s, %s);\n",
1277 Get_Notif_TypeName(net_type),
1278 notif.nent_name,
1279 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1280 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1281 ));
1282 #endif
1283
1284 if ((notif.nent_stringcount + notif.nent_floatcount) != count)
1285 {
1286 backtrace(sprintf(
1287 (
1288 "Arguments mismatch for Local_Notification(%s, %s, ...)! "
1289 "stringcount(%d) + floatcount(%d) != count(%d)\n"
1290 "Check the definition and function call for accuracy...?\n"
1291 ),
1292 Get_Notif_TypeName(net_type),
1293 notif.nent_name,
1294 notif.nent_stringcount,
1295 notif.nent_floatcount,
1296 count
1297 ));
1298 return;
1299 }
1300
1301 switch (net_type)
1302 {
1303 case MSG_ANNCE:
1304 {
1305 #ifdef CSQC
1306 Local_Notification_Queue_Add(net_type, notif, notif.nent_queuetime);
1307 #else
1308 backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n");
1309 #endif
1310 break;
1311 }
1312
1313 case MSG_INFO:
1314 {
1315 print(
1317 notif.nent_string,
1318 notif.nent_args,
1319 s1, s2, s3, s4,
1320 f1, f2, f3, f4)
1321 );
1322 #ifdef CSQC
1323 if (notif.nent_icon != "")
1324 {
1325 if (notif.nent_iconargs != "")
1326 {
1327 string s = Local_Notification_sprintf(
1328 notif.nent_icon,notif.nent_iconargs,
1329 s1, s2, s3, s4, f1, f2, f3, f4);
1330 // remove the trailing newline
1331 notif.nent_icon = strzone(substring(s, 0, -1));
1332 }
1334 notif.nent_icon,
1335 notif.nent_hudargs,
1336 s1, s2, s3, s4,
1337 f1, f2, f3, f4);
1338 }
1339 #endif
1340 break;
1341 }
1342
1343 #ifdef CSQC
1344 case MSG_CENTER:
1345 {
1348 notif.nent_string,
1349 notif.nent_args,
1350 s1, s2, s3, s4,
1351 f1, f2, f3, f4),
1352 notif.nent_durcnt,
1353 notif.nent_cpid,
1354 f1, f2);
1355 break;
1356 }
1357 #endif
1358
1359 case MSG_MULTI:
1360 {
1361 if (notif.nent_msginfo && notif.nent_msginfo.nent_enabled)
1362 {
1364 MSG_INFO,
1365 notif.nent_msginfo,
1366 notif.nent_msginfo.nent_stringcount,
1367 notif.nent_msginfo.nent_floatcount,
1368 s1, s2, s3, s4,
1369 f1, f2, f3, f4);
1370 }
1371 #ifdef CSQC
1372 if (notif.nent_msgannce && notif.nent_msgannce.nent_enabled)
1373 {
1375 MSG_ANNCE,
1376 notif.nent_msgannce,
1377 0, 0,
1378 "", "", "", "",
1379 0, 0, 0, 0);
1380 }
1381 if (notif.nent_msgcenter && notif.nent_msgcenter.nent_enabled)
1382 {
1384 MSG_CENTER,
1385 notif.nent_msgcenter,
1386 notif.nent_msgcenter.nent_stringcount,
1387 notif.nent_msgcenter.nent_floatcount,
1388 s1, s2, s3, s4,
1389 f1, f2, f3, f4);
1390 }
1391 #endif
1392 break;
1393 }
1394
1395 case MSG_CHOICE:
1396 {
1397 entity found_choice = notif.nent_optiona;
1398 if (notif.nent_challow_var && (warmup_stage || (notif.nent_challow_var == 2))) {
1399 switch (cvar(sprintf("notification_%s", Get_Notif_CvarName(notif))))
1400 {
1401 case 1: break;
1402 case 2: found_choice = notif.nent_optionb; break;
1403 default: return; // not enabled anyway
1404 }
1405 }
1406
1408 found_choice.nent_type,
1409 found_choice,
1410 found_choice.nent_stringcount,
1411 found_choice.nent_floatcount,
1412 s1, s2, s3, s4,
1413 f1, f2, f3, f4);
1414 break;
1415 }
1416 }
1417}
1418
1419// WOVA = Without Variable Arguments
1421 MSG net_type, Notification net_name,
1422 float stringcount, float floatcount,
1423 string s1, string s2, string s3, string s4,
1424 float f1, float f2, float f3, float f4)
1425{
1426 #define VARITEM(stringc, floatc, args) \
1427 if ((stringcount == stringc) && (floatcount == floatc)) \
1428 { Local_Notification(net_type, net_name, args); return; }
1430 #undef VARITEM
1431 Local_Notification(net_type, net_name); // some notifications don't have any arguments at all
1432}
1433
1434
1435// =========================
1436// Notification Networking
1437// =========================
1438
1440REGISTER_NET_LINKED(ENT_CLIENT_NOTIFICATION)
1441
1442#ifdef CSQC
1443NET_HANDLE(ENT_CLIENT_NOTIFICATION, bool is_new)
1444{
1445 make_pure(this);
1446 MSG net_type = ENUMCAST(MSG, ReadByte());
1447 int net_name = ReadShort();
1448 return = true;
1449
1450 if (net_type == MSG_CENTER_KILL)
1451 {
1452 if (!is_new) return;
1453 // killing
1454 #ifdef NOTIFICATIONS_DEBUG
1455 Debug_Notification(sprintf(
1456 "Read_Notification(%d) at %f: net_type = %s, cpid = %d\n",
1457 is_new,
1458 time,
1459 Get_Notif_TypeName(net_type),
1460 net_name
1461 ));
1462 #endif
1463 int _net_name = net_name;
1464 CPID net_name = ENUMCAST(CPID, _net_name);
1465 if (net_name == CPID_Null) {
1467 } else {
1468 centerprint_Kill(ORDINAL(net_name));// kill group
1469 }
1470 return;
1471 }
1472
1473 Notification notif = Get_Notif_Ent(net_type, net_name);
1474
1475 #ifdef NOTIFICATIONS_DEBUG
1476 Debug_Notification(sprintf(
1477 "Read_Notification(%d) at %f: net_type = %s, net_name = %s (%d)\n",
1478 is_new,
1479 time,
1480 Get_Notif_TypeName(net_type),
1481 notif.registered_id,
1482 net_name
1483 ));
1484 #endif
1485
1486 if (!notif) {
1487 backtrace("Read_Notification: Could not find notification entity!\n");
1488 return false;
1489 }
1490
1491 string s1 = ((notif.nent_stringcount > 0) ? ReadString() : "");
1492 string s2 = ((notif.nent_stringcount > 1) ? ReadString() : "");
1493 string s3 = ((notif.nent_stringcount > 2) ? ReadString() : "");
1494 string s4 = ((notif.nent_stringcount > 3) ? ReadString() : "");
1495 float f1 = ((notif.nent_floatcount > 0) ? ReadLong() : 0);
1496 float f2 = ((notif.nent_floatcount > 1) ? ReadLong() : 0);
1497 float f3 = ((notif.nent_floatcount > 2) ? ReadLong() : 0);
1498 float f4 = ((notif.nent_floatcount > 3) ? ReadLong() : 0);
1499
1500 if (!is_new) return;
1502 net_type, notif,
1503 notif.nent_stringcount,
1504 notif.nent_floatcount,
1505 s1, s2, s3, s4,
1506 f1, f2, f3, f4);
1507}
1508#endif
1509
1510#ifdef SVQC
1512{
1513 #ifdef NOTIFICATIONS_DEBUG
1514 Debug_Notification(sprintf(
1515 "Net_Notification_Remove() at %f: %s '%s - %s' notification\n",
1516 time,
1517 ((this.nent_net_name == -1) ? "Killed" : "Removed"),
1519 this.owner.nent_name
1520 ));
1521 #endif
1522 for (int i = 0; i < this.nent_stringcount; ++i) { strfree(this.nent_strings[i]); }
1523 delete(this);
1524}
1525
1526bool Net_Write_Notification(entity this, entity client, int sf)
1527{
1528 if (!Notification_ShouldSend(this.nent_broadcast, client, this.nent_client)) return false;
1529 WriteHeader(MSG_ENTITY, ENT_CLIENT_NOTIFICATION);
1532 for (int i = 0; i < this.nent_stringcount; ++i) { WriteString(MSG_ENTITY, this.nent_strings[i]); }
1533 for (int i = 0; i < this.nent_floatcount; ++i) { WriteLong(MSG_ENTITY, this.nent_floats[i]); }
1534 return true;
1535}
1536
1538 NOTIF broadcast, entity client,
1540 MSG net_type,
1542 CPID net_cpid)
1543{
1544 #ifdef NOTIFICATIONS_DEBUG
1545 Debug_Notification(sprintf(
1546 "Kill_Notification(%s, '%s', %s, %d);\n",
1547 Get_Notif_BroadcastName(broadcast),
1548 client.netname,
1549 (net_type ? Get_Notif_TypeName(net_type) : "0"),
1550 net_cpid
1551 ));
1552 #endif
1553
1554 string checkargs = Notification_CheckArgs(broadcast, client);
1555 if (checkargs != "") { LOG_WARNF("Incorrect usage of Kill_Notification: %s", checkargs); return; }
1556
1557 entity net_notif = new_pure(net_kill_notification);
1558 net_notif.nent_broadcast = broadcast;
1559 net_notif.nent_client = client;
1560 net_notif.nent_net_type = MSG_CENTER_KILL;
1561 net_notif.nent_net_name = ORDINAL(net_cpid);
1563
1565 (it.owner.nent_type == net_type || net_type == MSG_Null) && (it.owner.nent_cpid == net_cpid || net_cpid == CPID_Null),
1566 {
1567 it.nent_net_name = -1;
1568 it.nextthink = time;
1569 }
1570 );
1571}
1572
1574 NOTIF broadcast, entity client,
1575 MSG net_type, Notification net_name,
1576 ...count)
1577{
1578 if (broadcast == NOTIF_ONE_ONLY && !IS_REAL_CLIENT(client)) return;
1579 entity notif = net_name;
1580 string parms = sprintf("%s, '%s', %s, %s",
1581 Get_Notif_BroadcastName(broadcast),
1582 client.classname,
1583 Get_Notif_TypeName(net_type),
1584 net_name.registered_id
1585 );
1586 #ifdef NOTIFICATIONS_DEBUG
1587 Debug_Notification(sprintf("Send_Notification(%s, ...%d);\n", parms, count));
1588 #endif
1589
1590 if (!notif)
1591 {
1592 LOG_WARN("Send_Notification: Could not find notification entity!");
1593 return;
1594 }
1595
1596 // check supplied broadcast, target, type, and name for errors
1597 string checkargs = Notification_CheckArgs(broadcast, client);
1598 if (!net_name) { checkargs = sprintf("No notification provided! %s", checkargs); }
1599 if (checkargs != "")
1600 {
1601 LOG_WARNF("Incorrect usage of Send_Notification: %s", checkargs);
1602 return;
1603 }
1604
1605 string s1 = ((0 < notif.nent_stringcount) ? ...(0, string) : "");
1606 string s2 = ((1 < notif.nent_stringcount) ? ...(1, string) : "");
1607 string s3 = ((2 < notif.nent_stringcount) ? ...(2, string) : "");
1608 string s4 = ((3 < notif.nent_stringcount) ? ...(3, string) : "");
1609 float f1 = ((0 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 0), float) : 0);
1610 float f2 = ((1 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 1), float) : 0);
1611 float f3 = ((2 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 2), float) : 0);
1612 float f4 = ((3 < notif.nent_floatcount) ? ...((notif.nent_stringcount + 3), float) : 0);
1613
1614 #ifdef NOTIFICATIONS_DEBUG
1615 Debug_Notification(sprintf(
1616 "Send_Notification(%s, %s, %s);\n",
1617 parms,
1618 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1619 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1620 ));
1621 #endif
1622
1623 if ((notif.nent_stringcount + notif.nent_floatcount) != count)
1624 {
1625 LOG_WARNF(
1626 "Argument mismatch for Send_Notification(%s, ...)! "
1627 "stringcount(%d) + floatcount(%d) != count(%d)\n"
1628 "Check the definition and function call for accuracy...?\n",
1629 parms,
1630 notif.nent_stringcount,
1631 notif.nent_floatcount,
1632 count
1633 );
1634 return;
1635 }
1636
1638 && (broadcast == NOTIF_ALL || broadcast == NOTIF_ALL_EXCEPT)
1639 && !(net_type == MSG_ANNCE || net_type == MSG_CENTER)
1640 )
1641 {
1643 net_type, net_name,
1644 notif.nent_stringcount,
1645 notif.nent_floatcount,
1646 s1, s2, s3, s4,
1647 f1, f2, f3, f4);
1648 }
1649
1650 if (net_type == MSG_CHOICE)
1651 {
1652 // THIS GETS TRICKY... now we have to cycle through each possible player (checking broadcast)
1653 // and then do an individual NOTIF_ONE_ONLY recursive call for each one depending on their option...
1654 // It's slow, but it's better than the alternatives:
1655 // 1. Constantly networking all info and letting client decide
1656 // 2. Manually handling each separate call on per-usage basis (See old CTF usage of verbose)
1657 entity found_choice;
1658
1659 #define RECURSE_FROM_CHOICE(ent,action) MACRO_BEGIN \
1660 if (notif.nent_challow_var && (warmup_stage || (notif.nent_challow_var == 2))) { \
1661 switch (CS_CVAR(ent).msg_choice_choices[net_name.nent_choice_idx]) \
1662 { \
1663 case 1: found_choice = notif.nent_optiona; break; \
1664 case 2: found_choice = notif.nent_optionb; break; \
1665 default: action; \
1666 } \
1667 } else { \
1668 found_choice = notif.nent_optiona; \
1669 } \
1670 Send_Notification_WOVA( \
1671 NOTIF_ONE_ONLY, \
1672 ent, \
1673 found_choice.nent_type, \
1674 found_choice, \
1675 found_choice.nent_stringcount, \
1676 found_choice.nent_floatcount, \
1677 s1, s2, s3, s4, \
1678 f1, f2, f3, f4); \
1679 MACRO_END
1680
1681 switch (broadcast)
1682 {
1683 case NOTIF_ONE_ONLY: // we can potentially save processing power with this broadcast method
1684 {
1685 if (IS_REAL_CLIENT(client)) {
1686 RECURSE_FROM_CHOICE(client, return);
1687 }
1688 break;
1689 }
1690 default:
1691 {
1692 FOREACH_CLIENT(Notification_ShouldSend(broadcast, it, client), {
1693 RECURSE_FROM_CHOICE(it, continue);
1694 });
1695 break;
1696 }
1697 }
1698 }
1699 else
1700 {
1701 entity net_notif = new_pure(net_notification);
1702 IL_PUSH(g_notifications, net_notif);
1703 net_notif.owner = notif;
1704 net_notif.nent_broadcast = broadcast;
1705 net_notif.nent_client = client;
1706 net_notif.nent_net_type = net_type;
1707 net_notif.nent_net_name = notif.m_id;
1708 net_notif.nent_stringcount = notif.nent_stringcount;
1709 net_notif.nent_floatcount = notif.nent_floatcount;
1710
1711 for (int i = 0; i < net_notif.nent_stringcount; ++i) {
1712 net_notif.nent_strings[i] = strzone(...(i, string));
1713 }
1714 for (int i = 0; i < net_notif.nent_floatcount; ++i) {
1715 net_notif.nent_floats[i] = ...((net_notif.nent_stringcount + i), float);
1716 }
1717
1719 net_notif.nextthink = (time > autocvar_notification_lifetime_mapload)
1722
1723 Net_LinkEntity(net_notif, false, 0, Net_Write_Notification);
1724 }
1725}
1726
1727// WOVA = Without Variable Arguments
1729 NOTIF broadcast, entity client,
1730 MSG net_type, Notification net_name,
1731 float stringcount, float floatcount,
1732 string s1, string s2, string s3, string s4,
1733 float f1, float f2, float f3, float f4)
1734{
1735 #ifdef NOTIFICATIONS_DEBUG
1736 entity notif = net_name;
1737 Debug_Notification(sprintf(
1738 "Send_Notification_WOVA(%s, %d, %d, %s, %s);\n",
1739 sprintf(
1740 "%s, '%s', %s, %s",
1741 Get_Notif_BroadcastName(broadcast),
1742 client.classname,
1743 Get_Notif_TypeName(net_type),
1744 notif.nent_name
1745 ),
1746 stringcount,
1747 floatcount,
1748 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1749 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1750 ));
1751 #endif
1752
1753 #define VARITEM(stringc, floatc, args) \
1754 if ((stringcount == stringc) && (floatcount == floatc)) \
1755 { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1757 #undef VARITEM
1758 Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1759}
1760
1761// WOCOVA = Without Counts Or Variable Arguments
1763 NOTIF broadcast, entity client,
1764 MSG net_type, Notification net_name,
1765 string s1, string s2, string s3, string s4,
1766 float f1, float f2, float f3, float f4)
1767{
1768 entity notif = net_name;
1769
1770 #ifdef NOTIFICATIONS_DEBUG
1771 Debug_Notification(sprintf(
1772 "Send_Notification_WOCOVA(%s, %s, %s);\n",
1773 sprintf(
1774 "%s, '%s', %s, %s",
1775 Get_Notif_BroadcastName(broadcast),
1776 client.classname,
1777 Get_Notif_TypeName(net_type),
1778 notif.nent_name
1779 ),
1780 MakeConsoleSafe(sprintf("'%s^7', '%s^7', '%s^7', '%s^7'", s1, s2, s3, s4)),
1781 sprintf("%d, %d, %d, %d", f1, f2, f3, f4)
1782 ));
1783 #endif
1784
1785 #define VARITEM(stringc, floatc, args) \
1786 if ((notif.nent_stringcount == stringc) && (notif.nent_floatcount == floatc)) \
1787 { Send_Notification(broadcast, client, net_type, net_name, args); return; }
1789 #undef VARITEM
1790 Send_Notification(broadcast, client, net_type, net_name); // some notifications don't have any arguments at all
1791}
1792#endif // ifdef SVQC
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:244
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
#define ORDINAL(it)
Definition enumclass.qh:25
#define ENUMCAST(T, it)
Definition enumclass.qh:26
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:115
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_LINKED(id)
Definition net.qh:55
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
int ReadByte()
#define LOG_WARNF(...)
Definition log.qh:62
#define backtrace(msg)
Definition log.qh:99
#define LOG_INFOF(...)
Definition log.qh:66
#define LOG_WARN(...)
Definition log.qh:61
float stof(string val,...)
string substring(string s, float start, float length)
void WriteLong(float data, float dest, float desto)
void WriteString(string 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 WriteByte(float data, float dest, float desto)
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:364
spree_inf s1 s2 s3loc s2 s1
Definition all.inc:281
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 CPID_Null
Definition all.inc:617
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:469
f1
Definition all.inc:561
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:1135
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:494
string Process_Notif_Line(MSG typeId, bool chat, string input, string notiftype, string notifname, string stringtype)
Definition all.qc:172
void Create_Notification_Entity_Annce(entity notif, float var_cvar, string namestring, float channel, string snd, float vol, float position, float queuetime)
Definition all.qc:446
#define AnnouncerFilename(snd)
Definition all.qc:444
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_cpid)
Definition all.qc:1537
void Create_Notification_Entity_Multi(entity notif, float var_cvar, string namestring, Notification anncename, Notification infoname, Notification centername)
Definition all.qc:653
void Destroy_Notification_Entity(entity notif)
Definition all.qc:147
void Local_Notification(MSG net_type, Notification net_name,...count)
Definition all.qc:1236
#define NOTIF_WRITE(str)
bool Net_Write_Notification(entity this, entity client, int sf)
Definition all.qc:1526
void Net_Notification_Remove(entity this)
Definition all.qc:1511
void Local_Notification_Queue_Add(MSG net_type, entity notif, float queue_time)
Definition all.qc:1195
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:704
bool Notification_ShouldSend(NOTIF broadcast, entity to_client, entity other_client)
Definition all.qc:80
#define RECURSE_FROM_CHOICE(ent, action)
void Local_Notification_Queue_Run(MSG net_type, entity notif)
Definition all.qc:1183
void Send_Notification_WOCOVA(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:1762
#define SET_NOTIF_STRING(string, stringname)
string Process_Notif_Args(float arg_type, string args, string notiftype, string notifname)
Definition all.qc:219
void Create_Notification_Entity(entity notif, float var_default, float var_cvar, MSG typeId, string namestring, int teamnum)
Definition all.qc:396
void Local_Notification_Queue_Process()
Definition all.qc:1218
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:1420
void Destroy_All_Notifications()
Definition all.qc:159
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:1091
void Send_Notification_WOVA(NOTIF broadcast, entity client, 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)
Definition all.qc:1728
string Notification_CheckArgs(NOTIF broadcast, entity client)
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:992
#define NOTIF_WRITE_HARDCODED(cvar, default, description)
void Local_Notification_sound(int soundchannel, string soundfile, float soundvolume, float soundposition)
Definition all.qc:1052
void Dump_Notifications(int fh, bool alsoprint)
used to output notifications.cfg file
Definition all.qc:783
void Notification_GetCvars(entity this, entity store)
Definition all.qc:767
string arg_slot[NOTIF_MAX_ARGS]
Definition all.qh:422
int nent_floatcount
Definition all.qh:683
float notif_queue_next_time
Definition all.qh:398
int nent_stringcount
Definition all.qh:682
entity notif_queue_entity[NOTIF_QUEUE_MAX]
Definition all.qh:394
bool notif_error
Definition all.qh:739
const float NOTIF_MAX_HUDARGS
Definition all.qh:389
const float NOTIF_MAX_DURCNT
Definition all.qh:390
IntrusiveList g_notifications
Definition all.qh:240
float autocvar_notification_lifetime_mapload
Definition all.qh:313
Notification Get_Notif_Ent(MSG net_type, int net_name)
Definition all.qh:756
const int NOTIF_QUEUE_MAX
Definition all.qh:393
MSG notif_queue_type[NOTIF_QUEUE_MAX]
Definition all.qh:395
float autocvar_notification_lifetime_runtime
Definition all.qh:312
float nent_net_name
Definition all.qh:718
#define EIGHT_VARS_TO_VARARGS_VARLIST
Definition all.qh:87
string nent_strings[4]
Definition all.qh:719
string Get_Notif_BroadcastName(NOTIF broadcast)
send to one client and their spectators
Definition all.qh:260
entity Notification
always last
Definition all.qh:81
NOTIF nent_broadcast
Definition all.qh:714
string prev_soundfile
Definition all.qh:235
string Get_Notif_TypeName(MSG net_type)
main types/groups of notifications
Definition all.qh:30
#define NOTIF_HIT_UNKNOWN(token, funcname)
Definition all.qh:489
float notif_queue_time[NOTIF_QUEUE_MAX]
Definition all.qh:396
#define NOTIF_HIT_MAX(count, funcname)
Definition all.qh:485
MSG nent_net_type
Definition all.qh:717
bool notif_global_error
Definition all.qh:740
int notif_queue_length
Definition all.qh:399
float prev_soundtime
Definition all.qh:236
entity nent_client
Definition all.qh:716
float nent_floats[4]
Definition all.qh:720
const float NOTIF_MAX_ARGS
Definition all.qh:388
string Get_Notif_CvarName(Notification notif)
Definition all.qh:749
#define NOTIF_ARGUMENT_LIST
Definition all.qh:436
#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:67
#define NULL
Definition post.qh:14
#define setthink(e, f)
#define _sound(e, c, s, v, a)
Definition sound.qh:43
ERASEABLE string car(string s)
returns first word
Definition string.qh:259
ERASEABLE string cdr(string s)
returns all but first word
Definition string.qh:268
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
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:50
bool autocvar_sv_dedicated
Definition world.qh:41