Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
vote.qc
Go to the documentation of this file.
1#include "vote.qh"
2
4#include <common/constants.qh>
7#include <common/mapinfo.qh>
10#include <common/playerstats.qh>
11#include <common/stats.qh>
12#include <common/util.qh>
14#include <server/campaign.qh>
15#include <server/client.qh>
19#include <server/damage.qh>
20#include <server/gamelog.qh>
23#include <server/race.qh>
25#include <server/scores.qh>
26#include <server/teamplay.qh>
29#include <server/world.qh>
30
31// =============================================
32// Server side voting code, reworked by Samual
33// Last updated: December 27th, 2011
34// =============================================
35
36// Nagger for players to know status of voting
37bool Nagger_SendEntity(entity this, entity to, float sendflags)
38{
39 int nags = 0;
40 WriteHeader(MSG_ENTITY, ENT_CLIENT_NAGGER);
41
42 // bits:
43 // 1 = ready
44 // 2 = player needs to ready up
45 // 4 = vote
46 // 8 = player needs to vote
47 // 16 = warmup
48 // sendflags:
49 // 64 = vote counts
50 // 128 = vote string
51
52 if (warmup_stage)
53 {
54 if (readycount)
55 {
56 nags |= BIT(0);
57 if (!to.ready) nags |= BIT(1);
58 }
59 nags |= BIT(4);
60 }
61
62 if (vote_called)
63 {
64 nags |= BIT(2);
65 if (to.vote_selection == 0) nags |= BIT(3);
66 nags |= sendflags & BIT(6);
67 nags |= sendflags & BIT(7);
68 }
69
70 WriteByte(MSG_ENTITY, nags);
71
72 if (nags & BIT(6))
73 {
77 WriteChar(MSG_ENTITY, to.vote_selection);
78 }
79
81
82 if (nags & BIT(0))
83 {
84 for (int i = 1; i <= maxclients;)
85 {
86 int f = 0;
87 for (int b = 0; b < 8 && i <= maxclients; ++b, ++i)
88 if (edict_num(i).ready)
89 f |= BIT(b);
91 }
92 }
93
94 return true;
95}
96
101
103{
104 if (nagger) nagger.SendFlags |= BIT(7);
105}
106
108{
109 if (nagger) nagger.SendFlags |= BIT(6);
110}
111
113{
114 if (nagger) nagger.SendFlags |= BIT(0);
115}
116
117// If the vote_caller is still here, return their name, otherwise vote_caller_name
119{
120 if (IS_REAL_CLIENT(vote_caller)) return playername(vote_caller.netname, vote_caller.team, false);
121 return vote_caller_name;
122}
123
124// =======================
125// Game logic for voting
126// =======================
127
128void VoteStop(entity stopper, bool show_name);
129void VoteReset(bool verbose)
130{
131 if (verbose && vote_called)
132 {
133 VoteStop(NULL, true);
134 return;
135 }
136
137 FOREACH_CLIENT(true, { it.vote_selection = 0; });
138
139 if (vote_called)
140 {
144 }
145
148 vote_endtime = 0;
149
152
154}
155
156void VoteStop(entity stopper, bool canceled)
157{
158 if (canceled) // Code such as NextLevel() stopped it, not a direct action by a human.
159 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote was canceled\n");
160 else if (stopper == vote_caller)
161 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 stopped their vote\n");
162 else
163 bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n");
164
165 if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));
166 // Don't force them to wait for next vote, this way they can e.g. correct their vote.
167 if ((vote_caller) && (stopper == vote_caller)) vote_caller.vote_waittime = time + autocvar_sv_vote_stop;
168 VoteReset(false);
169}
170
172{
173 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ^1", vote_called_display, "^2 was accepted\n");
174
175 if ((vote_called == VOTE_MASTER) && vote_caller) vote_caller.vote_master = true;
177
178 if (vote_caller) vote_caller.vote_waittime = 0; // people like your votes, you don't need to wait to vote again
179
180 VoteReset(false);
181 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_ACCEPT);
182}
183
185{
186 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 was rejected\n");
187 VoteReset(false);
188 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
189}
190
192{
193 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 timed out\n");
194 VoteReset(false);
195 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
196}
197
198void VoteSpam(float notvoters, float mincount, string result)
199{
201 strcat("\{1}^2* vote results: ^1", ftos(vote_accept_count)),
202 strcat("^2:^1", ftos(vote_reject_count)),
203 ((mincount >= 0) ? strcat("^2 (^1", ftos(mincount), "^2 needed)") : "^2"),
204 strcat(", ^1", ftos(vote_abstain_count), "^2 didn't care"),
205 strcat(", ^1", ftos(notvoters), strcat("^2 didn't ", ((mincount >= 0) ? "" : "have to "), "vote\n"))));
206
208 {
210 strcat(":vote:v", result, ":", ftos(vote_accept_count)),
213 strcat(":", ftos(notvoters)),
214 strcat(":", ftos(mincount))));
215 }
216}
217
218#define spectators_allowed (!autocvar_sv_vote_nospectators || (autocvar_sv_vote_nospectators == 1 && (warmup_stage || intermission_running)))
219
220void VoteCount(float first_count)
221{
223
224 float vote_player_count = 0, notvoters = 0;
225 float vote_real_player_count = 0, vote_real_accept_count = 0;
226 float vote_real_reject_count = 0, vote_real_abstain_count = 0;
227 float vote_needed_of_voted, final_needed_votes;
228 float vote_factor_overall, vote_factor_of_voted;
229
231
232 // add up all the votes from each connected client
234 ++vote_player_count;
235 if (IS_PLAYER(it) || INGAME(it)) ++vote_real_player_count;
236 switch (it.vote_selection)
237 {
240 if (IS_PLAYER(it) || INGAME(it)) ++vote_real_reject_count;
241 break;
244 if (IS_PLAYER(it) || INGAME(it)) ++vote_real_accept_count;
245 break;
248 if (IS_PLAYER(it) || INGAME(it)) ++vote_real_abstain_count;
249 break;
250 default: break;
251 }
252 });
253
254 // Check to see if there are enough players on the server to allow master voting... otherwise, vote master could be used for evil.
255 if ((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count)
256 {
257 if (vote_caller) vote_caller.vote_waittime = 0;
258 print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master.");
259 VoteReset(false);
260 return;
261 }
262
263 // if spectators aren't allowed to vote and there are players in a match, then only count the players in the vote and ignore spectators.
264 if (!spectators_allowed && (vote_real_player_count > 0))
265 {
266 vote_accept_count = vote_real_accept_count;
267 vote_reject_count = vote_real_reject_count;
268 vote_abstain_count = vote_real_abstain_count;
269 vote_player_count = vote_real_player_count;
270 }
271
272 // people who have no opinion in any way :D
273 notvoters = (vote_player_count - vote_accept_count - vote_reject_count - vote_abstain_count);
274
275 // determine the goal for the vote to be passed or rejected normally
276 vote_factor_overall = bound(0.5, autocvar_sv_vote_majority_factor, 0.999);
277 vote_needed_overall = floor((vote_player_count - vote_abstain_count) * vote_factor_overall) + 1;
278
279 // if the vote times out, determine the amount of votes needed of the people who actually already voted
280 vote_factor_of_voted = bound(0.5, autocvar_sv_vote_majority_factor_of_voted, 0.999);
281 vote_needed_of_voted = floor((vote_accept_count + vote_reject_count) * vote_factor_of_voted) + 1;
282
283 // are there any players at all on the server? it could be an admin vote
284 if (vote_player_count == 0 && first_count)
285 {
286 VoteSpam(0, -1, "yes"); // no players at all, just accept it
287 VoteAccept();
288 return;
289 }
290
291 // since there ARE players, finally calculate the result of the vote
293 {
294 VoteSpam(notvoters, -1, "yes"); // there is enough acceptions to pass the vote
295 VoteAccept();
296 return;
297 }
298
299 if (vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall)
300 {
301 VoteSpam(notvoters, -1, "no"); // there is enough rejections to deny the vote
302 VoteReject();
303 return;
304 }
305
306 // there is not enough votes in either direction, now lets just calculate what the voters have said
307 if (time > vote_endtime)
308 {
309 final_needed_votes = vote_needed_overall;
310
312 {
313 if (vote_accept_count >= vote_needed_of_voted)
314 {
315 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "yes");
316 VoteAccept();
317 return;
318 }
319
321 {
322 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "no");
323 VoteReject();
324 return;
325 }
326
327 final_needed_votes = min(vote_needed_overall, vote_needed_of_voted);
328 }
329
330 // it didn't pass or fail, so not enough votes to even make a decision.
331 VoteSpam(notvoters, final_needed_votes, "timeout");
332 VoteTimeout();
333 }
334}
335
337{
338 if (vote_endtime > 0) // a vote was called
339 {
340 if (time > vote_endtime) // time is up
341 VoteCount(false);
342 }
343}
344
345
346// =======================
347// Game logic for warmup
348// =======================
349
350// Resets the state of all clients, items, weapons, waypoints, ... of the map.
351void reset_map(bool is_fake_round_start)
352{
353 if (time <= game_starttime)
354 {
355 if (game_stopped)
356 return;
357
358 if (!is_fake_round_start)
359 {
362 }
363
366 }
367
369 {
370 balanceteams();
372 }
373
375 {
376 shuffleteams();
378 }
379
380 FOREACH_CLIENT(true, {
381 if (time <= game_starttime)
382 accuracy_reset(it); // for spectators too because weapon accuracy is persistent
383 if (!IS_PLAYER(it))
384 continue;
386 entity store = PS(it);
387 if (store)
388 {
389 Inventory_clear(store.inventory);
390 Inventory_update(store);
391 }
392 });
393
394 MUTATOR_CALLHOOK(reset_map_global);
395
397 {
398 if(IS_CLIENT(it))
399 continue;
400 if (it.reset)
401 {
402 it.reset(it);
403 continue;
404 }
405 if (it.team_saved) it.team = it.team_saved;
406 if (it.flags & FL_PROJECTILE) delete(it); // remove any projectiles left
407 });
408
409 // Waypoints and assault start come LAST
411 if (it.reset2) it.reset2(it);
412 });
413
414 // Moving the player reset code here since the player-reset depends
415 // on spawnpoint entities which have to be reset first --blub
416 if (!MUTATOR_CALLHOOK(reset_map_players))
417 {
419 {
420 // PlayerScore_Clear(it);
421 CS(it).killcount = 0;
422 // stop the player from moving so that they stand still once they get respawned
423 it.velocity = '0 0 0';
424 it.avelocity = '0 0 0';
425 CS(it).movement = '0 0 0';
427
428 if(IS_BOT_CLIENT(it))
429 {
430 .entity weaponentity = weaponentities[0];
431 if(it.(weaponentity).m_weapon == WEP_Null)
432 W_NextWeapon(it, 0, weaponentity);
433 }
434 });
435 }
436}
437
438// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
440{
441 if (!warmup_stage) // if the countdown was not aborted
442 reset_map(false);
443 delete(this);
444}
445
446// Forces a restart of the game without actually reloading the map // this is a mess...
447void ReadyRestart_force(bool is_fake_round_start)
448{
450 return;
451 if (!is_fake_round_start && !autocvar_g_campaign)
452 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_COUNTDOWN_RESTART);
453
454 VoteReset(true);
455
456 // clear overtime, we have to decrease timelimit to its original value again.
460
461 if(warmup_stage)
462 game_starttime = time; // Warmup: No countdown in warmup
463 else if (autocvar_g_campaign)
464 game_starttime = time + 3;
465 else
466 game_starttime = time + RESTART_COUNTDOWN; // Go into match mode
467
468 // clear player attributes
470 it.alivetime_start = 0;
471 CS(it).killcount = 0;
472 });
473
474 // if we're ending the warmup stage call the corresponding hook
475 if(!is_fake_round_start && !warmup_stage)
476 localcmd("\nsv_hook_warmupend\n");
477
478 // reset the .ready status of all clients (including spectators and bots)
479 FOREACH_CLIENT(true, { it.ready = false; });
480 readycount = 0;
481 Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
482
483 // lock teams with lockonrestart
486
487 // initiate the restart-countdown-announcer entity
488 if (!is_fake_round_start && sv_ready_restart_after_countdown && !warmup_stage)
489 {
490 entity restart_timer = new_pure(restart_timer);
491 setthink(restart_timer, ReadyRestart_think);
492 restart_timer.nextthink = game_starttime;
493 }
494
495 // If we're returning to warmup join/switch all queued players for consistency:
496 // there's no queueing in warmup and imbalances are handled by sv_teamnagger blocking end of warmup.
497 if (warmup_stage)
498 {
499 // First pass: join player(s) queued for specific team(s)
500 FOREACH_CLIENT(it.wants_join > 0, Join(it, false));
501 // Second pass: join player(s) queued for team autoselection
502 FOREACH_CLIENT(it.wants_join < 0, Join(it, false));
503 }
504
505 // after a restart every players number of allowed timeouts gets reset, too
507 {
508 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
509 }
510
512 {
514 {
515 if (it.vote_master)
516 ClientData_Touch(it.enemy, true);
517 else
518 {
519 Send_Notification(NOTIF_ONE_ONLY, it, MSG_MULTI, SPECTATE_SPEC_NOTALLOWED);
520 TRANSMUTE(Observer, it);
522 }
523 });
524 }
525
527 reset_map(is_fake_round_start);
528
529 if (autocvar_sv_eventlog) GameLogEcho(":restart");
530}
531
532void ReadyRestart(bool forceWarmupEnd)
533{
534 if (MUTATOR_CALLHOOK(ReadyRestart_Deny))
535 {
536 // NOTE: ReadyRestart support is mandatory in campaign
538 error("ReadyRestart must be supported in campaign mode!");
539 localcmd("restart\n"); // if ReadyRestart is denied, restart the server
540 }
541 else if (intermission_running || race_completing) // game is over, ReadyRestart no longer available
542 localcmd("restart\n");
543 else
544 localcmd("\nsv_hook_readyrestart\n");
545
546 if(forceWarmupEnd || autocvar_g_campaign)
547 warmup_stage = 0; // forcefully end warmup and go to match stage
548 else
549 warmup_stage = autocvar_g_warmup; // go into warmup if it's enabled, otherwise restart into match stage
550
551 ReadyRestart_force(false);
552}
553
554/* Count the players who are ready and determine whether or not to restart the match when:
555 * a player presses F4 server/command/cmd.qc ClientCommand_ready()
556 * a player switches from players to specs server/client.qc PutObserverInServer()
557 * a player joins (from specs or directly) server/client.qc PutPlayerInServer()
558 * a player disconnects server/client.qc ClientDisconnect() */
560{
561 // cannot reset the game while a timeout is active or pending
562 if (timeout_status) return;
563
564 int total_players = 0, human_players = 0, humans_ready = 0;
565 readycount = 0;
566
569 if (it.ready) ++readycount;
570 if (IS_REAL_CLIENT(it))
571 {
572 ++human_players;
573 if (it.ready) ++humans_ready;
574 }
575 });
576
578
579 // can't read warmup_stage here as it could have been set to 0 by ReadyRestart()
580 // and we need to use this when checking if we should abort the countdown
581 // map_minplayers can only be > 0 if g_warmup was -1 at worldspawn
582 int minplayers = autocvar_g_warmup > 1 ? autocvar_g_warmup : map_minplayers;
583
584 // This allows warmup to end with zero players to prevent complaints
585 // of server never changing map with legacy config (sv_autopause 0).
586 bool badteams = (teamplay && total_players && autocvar_sv_teamnagger)
588 : false;
589
590 if (total_players < minplayers || badteams)
591 {
592 if (game_starttime > time) // someone bailed during countdown, back to warmup
593 {
594 warmup_stage = autocvar_g_warmup; // CAN change it AFTER calling Nagger_ReadyCounted() this frame
596 if (total_players < minplayers)
597 Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP_MINPLAYERS, minplayers);
598 else
599 Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP_BADTEAMS);
600 if (!sv_ready_restart_after_countdown) // if we ran reset_map() at start of countdown
602 }
603 warmup_limit = -1;
604 return; // don't ReadyRestart if players are ready but too few or teams are bad
605 }
606 else if (warmup_limit <= 0
607 && game_starttime <= time) // No countdown in progress, check prevents early countdown end if only player leaves
608 {
609 // there's enough players now and teams are ok
610 // but we're still in infinite warmup and may need to switch to timed warmup
611 warmup_limit = cvar("g_warmup_limit");
612 if (warmup_limit == 0)
614 if (warmup_limit > 0)
616 // implicit else: g_warmup -1 && g_warmup_limit -1 means
617 // warmup continues until enough players AND enough RUPs (no time limit)
618 }
619
620 if (humans_ready && humans_ready >= rint(human_players * bound(0.5, cvar("g_warmup_majority_factor"), 1)))
621 ReadyRestart(true);
622}
623
624
625// ======================================
626// Supporting functions for VoteCommand
627// ======================================
628
629bool Votecommand_check_assignment(entity caller, float assignment)
630{
631 bool from_server = (!caller);
632
633 if ((assignment == VC_ASGNMNT_BOTH)
634 || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
635 || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
636
637 return false;
638}
639
640string VoteCommand_extractcommand(string input, float startpos, int argc)
641{
642 string output;
643
644 if ((argc - 1) < startpos) output = "";
645 else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
646
647 return output;
648}
649
650bool VoteCommand_checknasty(string vote_command)
651{
652 return !((strstrofs(vote_command, ";", 0) >= 0)
653 || (strstrofs(vote_command, "\n", 0) >= 0)
654 || (strstrofs(vote_command, "\r", 0) >= 0)
655 || (strstrofs(vote_command, "$", 0) >= 0));
656}
657
658// NOTE: requires input to be surrounded by spaces
660{
661 // add a space around the input so the start and end of the list is captured
662 string output = strcat(" ", input, " ");
663 // allow gotomap replacements
664 output = strreplace(" map ", " gotomap ", output);
665 output = strreplace(" chmap ", " gotomap ", output);
666 return output;
667}
668
669bool VoteCommand_checkinlist(string vote_command, string list)
670{
671 if (vote_command == "" || list == "")
672 return false;
673
674 string l = VoteCommand_checkreplacements(list);
675 return (strstrofs(l, VoteCommand_checkreplacements(vote_command), 0) >= 0);
676}
677
678string ValidateMap(string validated_map, entity caller)
679{
680 validated_map = MapInfo_FixName(validated_map);
681
682 if (!validated_map)
683 {
684 print_to(caller, "This map is not available on this server.");
685 return string_null;
686 }
687
689 {
690 if (Map_IsRecent(validated_map))
691 {
692 print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
693 return string_null;
694 }
695 }
696
697 if (!MapInfo_CheckMap(validated_map))
698 {
699 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current gametype."));
700 return string_null;
701 }
702
703 return validated_map;
704}
705
706float VoteCommand_checkargs(float startpos, int argc)
707{
708 float p, q, check, minargs;
709 string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
710 string cmdrestriction = ""; // No we don't.
711 string charlist, arg;
712 float checkmate;
713
714 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
715 cmdrestriction = cvar_string(cvarname);
716 else
717 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
718
719 if (cmdrestriction == "") return true;
720
721 ++startpos; // skip command name
722
723 // check minimum arg count
724
725 // 0 args: argc == startpos
726 // 1 args: argc == startpos + 1
727 // ...
728
729 minargs = stof(cmdrestriction);
730 if (argc - startpos < minargs) return false;
731
732 p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
733
734 for ( ; ; )
735 {
736 // we know that at any time, startpos <= argc - minargs
737 // so this means: argc-minargs >= startpos >= argc, thus
738 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
739 // have been seen already
740
741 if (startpos >= argc) // all args checked? GOOD
742 break;
743
744 if (p < 0) // no more args? FAIL
745 {
746 // exception: exactly minargs left, this one included
747 if (argc - startpos == minargs) break;
748
749 // otherwise fail
750 return false;
751 }
752
753 // cut to next semicolon
754 q = strstrofs(cmdrestriction, ";", p + 1); // find next semicolon
755 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
756 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
757
758 // in case we ever want to allow semicolons in VoteCommand_checknasty
759 // charlist = strreplace("^^", ";", charlist);
760
761 if (charlist != "")
762 {
763 // verify the arg only contains allowed chars
764 arg = argv(startpos);
765 checkmate = strlen(arg);
766 for (check = 0; check < checkmate; ++check)
767 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
768 // not allowed character
769 // all characters are allowed. FINE.
770 }
771
772 ++startpos;
773 --minargs;
774 p = q;
775 }
776
777 return true;
778}
779
780int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
781{
782 string first_command = argv(startpos);
783 int missing_chars = argv_start_index(startpos);
784
785 if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
786 return 0;
787
788 if (caller && !VoteCommand_checkinlist(first_command, vote_list)) return 0;
789
790 if (!VoteCommand_checkargs(startpos, argc)) return 0;
791
792 switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
793 {
794 case MUT_VOTEPARSE_CONTINUE: { break; }
795 case MUT_VOTEPARSE_SUCCESS: { return 1; }
796 case MUT_VOTEPARSE_INVALID: { return -1; }
797 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
798 }
799
800 switch (first_command) // now go through and parse the proper commands to adjust as needed.
801 {
802 case "movetoauto":
803 case "movetored":
804 case "movetoblue":
805 case "movetoyellow":
806 case "movetopink":
807 case "movetospec":
808 {
809 entity victim = GetIndexedEntity(argc, (startpos + 1));
810 float accepted = VerifyClientEntity(victim, true, false);
811 if (accepted > 0)
812 {
813 vote_parsed_command = vote_command;
814 vote_parsed_display = sprintf("^1%s #%d ^7%s", first_command, etof(victim), victim.netname);
815 }
816 else
817 {
818 print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n"));
819 return 0;
820 }
821
822 break;
823 }
824
825 case "kick":
826 case "kickban": // catch all kick/kickban commands
827 {
828 entity victim = GetIndexedEntity(argc, (startpos + 1));
829 float accepted = VerifyClientEntity(victim, true, false);
830
831 if (accepted > 0)
832 {
833 string reason = "No reason provided";
834 if(argc > next_token)
835 reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
836
837 string command_arguments = reason;
838 if (first_command == "kickban")
840
841 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
842 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
843 }
844 else
845 {
846 print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n"));
847 return 0;
848 }
849
850 break;
851 }
852
853 case "map":
854 case "chmap":
855 case "gotomap": // re-direct all map selection commands to gotomap
856 {
857 vote_command = ValidateMap(argv(startpos + 1), caller);
858 if (!vote_command) return -1;
859 vote_parsed_command = strcat("gotomap ", vote_command);
861
862 break;
863 }
864
865 // TODO: replicate the old behaviour of being able to vote for maps from different modes on multimode servers (possibly support it in gotomap too)
866 // maybe fallback instead of aborting if map name is invalid?
867 case "nextmap":
868 {
869 vote_command = ValidateMap(argv(startpos + 1), caller);
870 if (!vote_command) return -1;
871 vote_parsed_command = strcat("nextmap ", vote_command);
873
874 break;
875 }
876
877 case "fraglimit": // include restrictions on the maximum votable frag limit
878 {
879 float fraglimit_vote = stof(argv(startpos + 1));
880 float fraglimit_min = 0;
881 float fraglimit_max = 999999;
882 if(fraglimit_vote > fraglimit_max || fraglimit_vote < fraglimit_min)
883 {
884 print_to(caller, strcat("Invalid fraglimit vote, accepted values are between ", ftos(fraglimit_min), " and ", ftos(fraglimit_max), "."));
885 return -1;
886 }
887 vote_parsed_command = strcat("fraglimit ", ftos(fraglimit_vote));
889
890 break;
891 }
892
893 case "timelimit": // include restrictions on the maximum votable time limit
894 {
895 float timelimit_vote = stof(argv(startpos + 1));
896 if(timelimit_vote > autocvar_timelimit_max || timelimit_vote < autocvar_timelimit_min)
897 {
898 print_to(caller, strcat("Invalid timelimit vote, accepted values are between ", ftos(autocvar_timelimit_min), " and ", ftos(autocvar_timelimit_max), "."));
899 return -1;
900 }
901 vote_parsed_command = strcat("timelimit ", ftos(timelimit_vote));
903
904 break;
905 }
906
907 case "restart":
908 {
909 // add a delay so that vote result can be seen and announcer can be heard
910 // if the vote is accepted
911 vote_parsed_command = strcat("defer 1 ", vote_command);
912 vote_parsed_display = strzone(strcat("^1", vote_command));
913
914 break;
915 }
916
917 case "allready":
918 {
919 if(!warmup_stage) {
920 print_to(caller, "Game already started. Use the resetmatch command to restart the match.");
921 return -1;
922 }
923
924 vote_parsed_command = vote_command;
925 vote_parsed_display = strzone(strcat("^1", vote_command));
926 break;
927 }
928
929 default:
930 {
931 vote_parsed_command = vote_command;
932 vote_parsed_display = strzone(strcat("^1", vote_command));
933
934 break;
935 }
936 }
937
938 return 1;
939}
940
941
942// =======================
943// Command Sub-Functions
944// =======================
945
946void VoteCommand_abstain(int request, entity caller) // CLIENT ONLY
947{
948 switch (request)
949 {
951 {
952 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
953 {
954 print_to(caller, "^1You are banned from voting.");
955 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN);
956 return;
957 }
958
959 if (!vote_called) { print_to(caller, "^1No vote called."); }
960 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
961 {
962 print_to(caller, "^1You have already voted.");
963 }
964
965 else // everything went okay, continue changing vote
966 {
967 print_to(caller, "^1You abstained from your vote.");
968 caller.vote_selection = VOTE_SELECT_ABSTAIN;
969 msg_entity = caller;
971 VoteCount(false);
972 }
973
974 return;
975 }
976
977 default:
979 {
980 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
981 print_to(caller, " No arguments required.");
982 return;
983 }
984 }
985}
986
988{
989 print_to(caller, strcat("You can call a vote for or execute these commands: ^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7"));
990}
991
992void VoteCommand_call(int request, entity caller, int argc, string vote_command) // BOTH
993{
994 switch (request)
995 {
997 {
998 float tmp_playercount = 0;
999 int parse_error;
1000
1001 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
1002 {
1003 print_to(caller, "^1You are banned from calling a vote.");
1004 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBAN);
1005 return;
1006 }
1007
1008 vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
1009
1010 if (!autocvar_sv_vote_call && caller)
1011 {
1012 print_to(caller, "^1Vote calling is not allowed.");
1013 }
1015 {
1016 print_to(caller, "^1Vote calling is not allowed before the match has started.");
1017 }
1018 else if (vote_called)
1019 {
1020 print_to(caller, "^1There is already a vote called.");
1021 }
1022 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
1023 {
1024 print_to(caller, "^1Only players can call a vote.");
1025 }
1026 else if (caller && !IS_CLIENT(caller))
1027 {
1028 print_to(caller, "^1Only connected clients can vote.");
1029 }
1030 else if (timeout_status && vote_command != "timein")
1031 {
1032 print_to(caller, "^1You can not call a vote while a timeout is active.");
1033 }
1034 else if (caller && (time < caller.vote_waittime))
1035 {
1036 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
1037 }
1038 else if (!VoteCommand_checknasty(vote_command))
1039 {
1040 print_to(caller, "^1Syntax error in command.");
1041 }
1042 else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
1043 {
1044 if(parse_error == 0)
1045 {
1046 if (vote_called_command == "")
1047 VoteCommand_call(CMD_REQUEST_USAGE, caller, argc, vote_command);
1048 else
1049 print_to(caller, "^1This command is not acceptable or not available.");
1050 }
1051 }
1052 else // everything went okay, continue with calling the vote
1053 {
1054 vote_caller = caller; // remember who called the vote
1060
1061 if (caller)
1062 {
1063 caller.vote_selection = VOTE_SELECT_ACCEPT;
1064 caller.vote_waittime = time + autocvar_sv_vote_wait;
1065 msg_entity = caller;
1066 }
1067
1068 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
1069
1070 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
1072 bprint("\{1}^2* ^3", "^6DEBUG MODE ACTIVE: bots can vote too\n"); // so servers don't forget it on
1074 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
1076 VoteCount(true); // needed if you are the only one
1077
1078 if (tmp_playercount > 1 && vote_called != VOTE_NULL)
1079 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
1080 }
1081
1082 return;
1083 }
1084
1085 default:
1086 case CMD_REQUEST_USAGE:
1087 {
1088 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call <command>"));
1089 print_to(caller, " Where <command> is the command to request a vote upon.");
1090 print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
1091 print_to(caller, strcat(" ", GetCommandPrefix(caller), " vote call endmatch"));
1093 print_to(caller, "Shortcuts: ^2vcall <command>, vend, vmap, vkick, ...");
1094 return;
1095 }
1096 }
1097}
1098
1099void VoteCommand_master(int request, entity caller, int argc, string vote_command) // CLIENT ONLY
1100{
1101 switch (request)
1102 {
1104 {
1106 {
1107 switch (strtolower(argv(2)))
1108 {
1109 case "do":
1110 {
1111 int parse_error;
1112 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
1113
1114 if (!caller.vote_master)
1115 print_to(caller, "^1You do not have vote master privileges.");
1116 else if (!VoteCommand_checknasty(vote_command))
1117 {
1118 print_to(caller, "^1Syntax error in command.");
1119 }
1120 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
1121 {
1122 if(parse_error == 0)
1123 {
1124 if (vote_called_command == "")
1125 VoteCommand_master(CMD_REQUEST_USAGE, caller, argc, vote_command);
1126 else
1127 print_to(caller, "^1This command is not acceptable or not available.");
1128 }
1129 }
1130 else // everything went okay, proceed with command
1131 {
1133 print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
1134 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
1136 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
1137 }
1138
1139 return;
1140 }
1141
1142 case "login":
1143 {
1144 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
1145 else if (caller.vote_master)
1146 {
1147 print_to(caller, "^1You are already logged in as vote master.");
1148 }
1150 {
1151 print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
1152 }
1153 else // everything went okay, proceed with giving this player master privilages
1154 {
1155 caller.vote_master = true;
1156 print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
1157 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
1159 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
1160 }
1161
1162 return;
1163 }
1164
1165 default: // calling a vote for master
1166 {
1167 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
1168 else if (vote_called)
1169 {
1170 print_to(caller, "^1There is already a vote called.");
1171 }
1172 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
1173 {
1174 print_to(caller, "^1Only players can call a vote.");
1175 }
1176 else if (timeout_status)
1177 {
1178 print_to(caller, "^1You can not call a vote while a timeout is active.");
1179 }
1180 else // everything went okay, continue with creating vote
1181 {
1182 vote_caller = caller;
1186 vote_called_display = strzone("^3master");
1188
1189 caller.vote_selection = VOTE_SELECT_ACCEPT;
1190 caller.vote_waittime = time + autocvar_sv_vote_wait;
1191
1192 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
1194 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
1196 VoteCount(true); // needed if you are the only one
1197 }
1198
1199 return;
1200 }
1201 }
1202 }
1203 else { print_to(caller, "^1Master control of voting is not allowed."); }
1204
1205 return;
1206 }
1207
1208 default:
1209 case CMD_REQUEST_USAGE:
1210 {
1211 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [<action> [<command> | <password>]]"));
1212 print_to(caller, " If <action> is left blank, it calls a vote for you to become master.");
1213 print_to(caller, " Otherwise it can be either 'do' (to run <command>) or 'login' as master.");
1214 return;
1215 }
1216 }
1217}
1218
1219void VoteCommand_no(int request, entity caller) // CLIENT ONLY
1220{
1221 switch (request)
1222 {
1224 {
1225 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
1226 {
1227 print_to(caller, "^1You are banned from voting.");
1228 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN);
1229 return;
1230 }
1231
1232 if (!vote_called) { print_to(caller, "^1No vote called."); }
1233 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1234 {
1235 print_to(caller, "^1You have already voted.");
1236 }
1237 else if (caller == vote_caller && autocvar_sv_vote_no_stops_vote)
1238 {
1239 VoteStop(caller, false);
1240 }
1241
1242 else // everything went okay, continue changing vote
1243 {
1244 print_to(caller, "^1You rejected the vote.");
1245 caller.vote_selection = VOTE_SELECT_REJECT;
1246 msg_entity = caller;
1248 VoteCount(false);
1249 }
1250
1251 return;
1252 }
1253
1254 default:
1255 case CMD_REQUEST_USAGE:
1256 {
1257 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1258 print_to(caller, " No arguments required.");
1259 return;
1260 }
1261 }
1262}
1263
1264void VoteCommand_status(int request, entity caller) // BOTH
1265{
1266 switch (request)
1267 {
1269 {
1270 if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1271 else print_to(caller, "^1No vote called.");
1272
1273 return;
1274 }
1275
1276 default:
1277 case CMD_REQUEST_USAGE:
1278 {
1279 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1280 print_to(caller, " No arguments required.");
1281 return;
1282 }
1283 }
1284}
1285
1286void VoteCommand_stop(int request, entity caller) // BOTH
1287{
1288 switch (request)
1289 {
1291 {
1292 if (!vote_called) print_to(caller, "^1No vote called.");
1293 else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller, false);
1294 else print_to(caller, "^1You are not allowed to stop that vote.");
1295 return;
1296 }
1297
1298 default:
1299 case CMD_REQUEST_USAGE:
1300 {
1301 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1302 print_to(caller, " No arguments required.");
1303 return;
1304 }
1305 }
1306}
1307
1308void VoteCommand_yes(int request, entity caller) // CLIENT ONLY
1309{
1310 switch (request)
1311 {
1313 {
1314 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
1315 {
1316 print_to(caller, "^1You are banned from voting.");
1317 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN);
1318 return;
1319 }
1320
1321 if (!vote_called) { print_to(caller, "^1No vote called."); }
1322 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1323 {
1324 print_to(caller, "^1You have already voted.");
1325 }
1326 else // everything went okay, continue changing vote
1327 {
1328 print_to(caller, "^1You accepted the vote.");
1329 caller.vote_selection = VOTE_SELECT_ACCEPT;
1330 msg_entity = caller;
1332 VoteCount(false);
1333 }
1334
1335 return;
1336 }
1337
1338 default:
1339 case CMD_REQUEST_USAGE:
1340 {
1341 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1342 print_to(caller, " No arguments required.");
1343 return;
1344 }
1345 }
1346}
1347
1348/* use this when creating a new command, making sure to place it in alphabetical order... also,
1349** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1350void VoteCommand_(int request)
1351{
1352 switch(request)
1353 {
1354 case CMD_REQUEST_COMMAND:
1355 {
1356
1357 return;
1358 }
1359
1360 default:
1361 case CMD_REQUEST_USAGE:
1362 {
1363 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1364 print_to(caller, " No arguments required.");
1365 return;
1366 }
1367 }
1368}
1369*/
1370
1371
1372// ================================
1373// Macro system for vote commands
1374// ================================
1375
1376// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1377#define VOTE_COMMANDS(request, caller, arguments, command) \
1378 VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1379 VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1380 VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1381 VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1382 VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1383 VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1384 VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1385 VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1386 /* nothing */
1387
1388void VoteCommand_macro_help(entity caller, int argc)
1389{
1390 string command_origin = GetCommandPrefix(caller);
1391
1392 if (argc == 2 || argv(2) == "help") // help display listing all commands
1393 {
1394 print_to(caller, "\nVoting commands:\n");
1395 #define VOTE_COMMAND(name, function, description, assignment) \
1396 { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } }
1397
1398 VOTE_COMMANDS(0, caller, 0, "");
1399 #undef VOTE_COMMAND
1400
1401 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote <command>^7, where possible commands are listed above.\n"));
1402 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help <command>"));
1404 }
1405 else // usage for individual command
1406 {
1407 #define VOTE_COMMAND(name, function, description, assignment) \
1408 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1409
1410 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1411 #undef VOTE_COMMAND
1412
1413 string cvarname = strcat("sv_vote_command_help_", argv(2));
1414 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1415 wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1416 else if (argv(2) != "")
1417 print_to(caller, "No documentation exists for this vote");
1418 }
1419}
1420
1421float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1422{
1423 #define VOTE_COMMAND(name, function, description, assignment) \
1424 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1425
1426 VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1427 #undef VOTE_COMMAND
1428
1429 return false;
1430}
1431
1432
1433// ======================================
1434// Main function handling vote commands
1435// ======================================
1436
1437void VoteCommand(int request, entity caller, int argc, string vote_command)
1438{
1439 // Guide for working with argc arguments by example:
1440 // argc: 1 - 2 - 3 - 4
1441 // argv: 0 - 1 - 2 - 3
1442 // cmd vote - master - login - password
1443
1444 switch (request)
1445 {
1447 {
1448 if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1449 }
1450
1451 default:
1452 print_to(caller, strcat(((argv(1) != "") ? strcat("Unknown vote command \"", argv(1), "\"") : "No command provided"), ". For a list of supported commands, try ", GetCommandPrefix(caller), " vote help.\n"));
1453 case CMD_REQUEST_USAGE:
1454 {
1455 VoteCommand_macro_help(caller, argc);
1456 return;
1457 }
1458 }
1459}
void accuracy_reset(entity e)
Definition accuracy.qc:63
string autocvar_g_voteban_list
Definition banning.qh:16
float autocvar_g_ban_default_bantime
Definition banning.qh:3
float autocvar_g_ban_default_masksize
Definition banning.qh:4
#define MUTATOR_CALLHOOK(name,...)
Definition base.qh:186
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
bool ready
Definition main.qh:88
bool warmup_stage
Definition main.qh:120
const int CMD_REQUEST_COMMAND
Definition command.qh:3
const int CMD_REQUEST_USAGE
Definition command.qh:4
#define IS_NOT_A_CLIENT(s)
Definition player.qh:243
#define IS_CLIENT(s)
Definition player.qh:241
#define IS_PLAYER(s)
Definition player.qh:242
float warmup_limit
Definition stats.qh:377
int timeout_status
Definition stats.qh:87
#define autocvar_timelimit
Definition stats.qh:92
float game_starttime
Definition stats.qh:82
float game_stopped
Definition stats.qh:81
int overtimes
Definition stats.qh:86
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2180
void wordwrap_sprint(entity to, string s, float l)
Definition util.qc:191
string strtolower(string s)
const int FL_PROJECTILE
Definition constants.qh:77
float maxclients
float CVAR_TYPEFLAG_EXISTS
float time
#define strstrofs
#define argv_end_index
#define strlen
#define argv_start_index
WriteString(chan, ent.netname)
WriteByte(chan, ent.angles.y/DEC_FACTOR)
void GameLogEcho(string s)
Definition gamelog.qc:15
bool autocvar_sv_eventlog
Definition gamelog.qh:3
bool Map_IsRecent(string m)
bool intermission_running
void Inventory_update(entity e)
Definition inventory.qh:159
void Inventory_clear(entity store)
Definition inventory.qh:170
#define FOREACH_ENTITY_ORDERED(cond, body)
Definition iter.qh:137
#define FOREACH_ENTITY_FLOAT_ORDERED(fld, match, body)
Definition iter.qh:175
#define PutClientInServer
Definition _all.inc:251
const int MSG_ENTITY
Definition net.qh:156
#define WriteHeader(to, id)
Definition net.qh:265
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
#define LOG_INFO(...)
Definition log.qh:62
float MapInfo_CheckMap(string s)
Definition mapinfo.qc:1507
string MapInfo_FixName(string s)
Definition mapinfo.qc:1471
int map_minplayers
Definition mapinfo.qh:190
bool autocvar_g_campaign
Definition menu.qc:752
void localcmd(string command,...)
void cvar_set(string name, string value)
float ceil(float f)
float stof(string val,...)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float cvar(string name)
void WriteChar(float data, float dest, float desto)
void bprint(string text,...)
const string cvar_string(string name)
float min(float f,...)
float rint(float f)
string ftos(float f)
float floor(float f)
string strzone(string s)
string argv(float n)
#define etof(e)
Definition misc.qh:25
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1500
#define TRANSMUTE(cname, this,...)
Definition oo.qh:139
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:66
bool pure_data
Definition oo.qh:9
void PlayerStats_GameReport_Reset_All()
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
entity msg_entity
Definition progsdefs.qc:63
entity result
Definition promise.qc:45
void round_handler_Reset(float next_think)
#define round_handler_IsActive()
void Score_ClearAll()
Clear ALL scores (for ready-restart).
Definition scores.qc:303
void W_NextWeapon(entity this, int list,.entity weaponentity)
Goto next weapon.
Definition selection.qc:310
#define setthink(e, f)
void player_powerups_remove_all(entity this, bool allow_poweroff_sound)
Definition client.qc:1540
void GiveWarmupResources(entity this)
Definition client.qc:568
bool PlayerInList(entity player, string list)
Definition client.qc:1048
void Join(entity this, bool queued_join)
it's assumed this isn't called for bots (campaign_bots_may_start, centreprints)
Definition client.qc:2073
void ClientData_Touch(entity e, bool to_spectators_too)
Definition client.qc:185
bool autocvar_sv_teamnagger
Definition client.qh:58
bool autocvar_sv_spectate
Definition client.qh:57
void print_to(entity to, string input)
Definition common.qc:171
string GetCommandPrefix(entity caller)
Definition common.qc:26
entity GetIndexedEntity(int argc, int start_index)
Definition common.qc:82
int VerifyClientEntity(entity client, bool must_be_real, bool must_be_bots)
Definition common.qc:47
string GetCallerName(entity caller)
Definition common.qc:33
bool autocvar_sv_timeout
Definition common.qh:5
#define GetClientErrorString(clienterror, original_input)
Definition common.qh:87
int next_token
Definition common.qh:71
int autocvar_sv_timeout_number
Definition common.qh:8
#define spectators_allowed
Definition vote.qc:218
void ReadyCount()
Definition vote.qc:559
void VoteReject()
Definition vote.qc:184
float VoteCommand_checkargs(float startpos, int argc)
Definition vote.qc:706
void VoteThink()
Definition vote.qc:336
void Nagger_Init()
Definition vote.qc:97
void VoteCommand_call(int request, entity caller, int argc, string vote_command)
Definition vote.qc:992
string OriginalCallerName()
Definition vote.qc:118
void VoteCommand_yes(int request, entity caller)
Definition vote.qc:1308
void print_available_commands_to(entity caller)
Definition vote.qc:987
void reset_map(bool is_fake_round_start)
Definition vote.qc:351
string ValidateMap(string validated_map, entity caller)
Definition vote.qc:678
#define VOTE_COMMANDS(request, caller, arguments, command)
Definition vote.qc:1377
bool Votecommand_check_assignment(entity caller, float assignment)
Definition vote.qc:629
float VoteCommand_macro_command(entity caller, int argc, string vote_command)
Definition vote.qc:1421
void VoteCount(float first_count)
Definition vote.qc:220
string VoteCommand_checkreplacements(string input)
Definition vote.qc:659
void Nagger_VoteCountChanged()
Definition vote.qc:107
void ReadyRestart(bool forceWarmupEnd)
Definition vote.qc:532
void VoteTimeout()
Definition vote.qc:191
void Nagger_ReadyCounted()
Definition vote.qc:112
void VoteCommand_no(int request, entity caller)
Definition vote.qc:1219
void VoteSpam(float notvoters, float mincount, string result)
Definition vote.qc:198
void VoteReset(bool verbose)
Definition vote.qc:129
void VoteCommand(int request, entity caller, int argc, string vote_command)
Definition vote.qc:1437
void VoteCommand_abstain(int request, entity caller)
Definition vote.qc:946
void Nagger_VoteChanged()
Definition vote.qc:102
string VoteCommand_extractcommand(string input, float startpos, int argc)
Definition vote.qc:640
bool VoteCommand_checkinlist(string vote_command, string list)
Definition vote.qc:669
void VoteStop(entity stopper, bool show_name)
Definition vote.qc:156
void VoteCommand_macro_help(entity caller, int argc)
Definition vote.qc:1388
void ReadyRestart_think(entity this)
Definition vote.qc:439
void VoteCommand_master(int request, entity caller, int argc, string vote_command)
Definition vote.qc:1099
void VoteCommand_stop(int request, entity caller)
Definition vote.qc:1286
int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
Definition vote.qc:780
bool Nagger_SendEntity(entity this, entity to, float sendflags)
Definition vote.qc:37
void VoteCommand_status(int request, entity caller)
Definition vote.qc:1264
bool VoteCommand_checknasty(string vote_command)
Definition vote.qc:650
void ReadyRestart_force(bool is_fake_round_start)
Definition vote.qc:447
void VoteAccept()
Definition vote.qc:171
string vote_caller_name
Definition vote.qh:44
const float VOTE_NORMAL
Definition vote.qh:39
string vote_called_command
Definition vote.qh:54
string vote_parsed_display
Definition vote.qh:57
int vote_needed_overall
Definition vote.qh:50
bool autocvar_sv_vote_master_callable
Definition vote.qh:12
bool autocvar_sv_vote_no_stops_vote
Definition vote.qh:17
const float VC_ASGNMNT_BOTH
Definition vote.qh:27
float vote_endtime
Definition vote.qh:46
const float VOTE_MASTER
Definition vote.qh:40
const float RESTART_COUNTDOWN
Definition vote.qh:66
int autocvar_sv_vote_limit
Definition vote.qh:8
bool autocvar_sv_vote_change
Definition vote.qh:4
const float VOTE_SELECT_REJECT
Definition vote.qh:33
entity nagger
Definition vote.qh:67
bool autocvar_sv_vote_master
Definition vote.qh:11
bool autocvar_sv_vote_call
Definition vote.qh:3
const float VC_ASGNMNT_CLIENTONLY
Definition vote.qh:28
const float VOTE_NULL
Definition vote.qh:38
int vote_accept_count
Definition vote.qh:47
string vote_parsed_command
Definition vote.qh:56
int readycount
Definition vote.qh:68
string autocvar_sv_vote_commands
Definition vote.qh:5
string vote_called_display
Definition vote.qh:55
string autocvar_sv_vote_master_commands
Definition vote.qh:13
float autocvar_sv_vote_majority_factor_of_voted
Definition vote.qh:10
bool autocvar_sv_vote_debug
Definition vote.qh:6
entity vote_caller
Definition vote.qh:43
string autocvar_sv_vote_master_password
Definition vote.qh:15
const float VOTE_SELECT_ACCEPT
Definition vote.qh:35
const float VOTE_SELECT_NULL
Definition vote.qh:34
float autocvar_sv_vote_stop
Definition vote.qh:22
float autocvar_sv_vote_timeout
Definition vote.qh:23
const float VC_ASGNMNT_SERVERONLY
Definition vote.qh:29
int autocvar_sv_vote_master_playerlimit
Definition vote.qh:16
float autocvar_sv_vote_wait
Definition vote.qh:24
const float VOTE_SELECT_ABSTAIN
Definition vote.qh:32
int vote_called
Definition vote.qh:45
bool autocvar_sv_vote_singlecount
Definition vote.qh:21
bool autocvar_sv_vote_override_mostrecent
Definition vote.qh:20
int vote_reject_count
Definition vote.qh:48
int vote_abstain_count
Definition vote.qh:49
float autocvar_sv_vote_majority_factor
Definition vote.qh:9
bool autocvar_sv_vote_gamestart
Definition vote.qh:7
@ MUT_VOTEPARSE_CONTINUE
Definition events.qh:1365
@ MUT_VOTEPARSE_UNACCEPTABLE
Definition events.qh:1368
@ MUT_VOTEPARSE_INVALID
Definition events.qh:1367
@ MUT_VOTEPARSE_SUCCESS
Definition events.qh:1366
int g_race_qualifying
Definition race.qh:11
float race_completing
Definition race.qh:26
#define PS(this)
Definition state.qh:18
ClientState CS(Client this)
Definition state.qh:47
#define strfree(this)
Definition string.qh:57
void shuffleteams()
Definition sv_cmd.qc:1535
void balanceteams()
Definition sv_cmd.qc:386
bool shuffleteams_on_reset_map
Definition sv_cmd.qh:9
bool balanceteams_on_reset_map
Definition sv_cmd.qh:6
#define INGAME(it)
Definition sv_rules.qh:24
#define INGAME_JOINED(it)
Definition sv_rules.qh:25
int total_players
Definition sv_rules.qh:12
int TeamBalance_SizeDifference(entity ignore)
Returns the size difference between the largest and smallest team (bots included).
Definition teamplay.qc:562
bool autocvar_teamplay_lockonrestart
Definition teamplay.qh:5
bool lockteams
Definition teamplay.qh:20
bool teamplay
Definition teams.qh:59
#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
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
float autocvar_timelimit_max
Definition world.qh:27
float checkrules_suddendeathwarning
Definition world.qh:36
float autocvar_timelimit_min
Definition world.qh:26
int checkrules_overtimesadded
Definition world.qh:38
float checkrules_suddendeathend
Definition world.qh:37
float autocvar_timelimit_overtime
Definition world.qh:28
int autocvar_g_warmup
Definition world.qh:9
bool sv_ready_restart_after_countdown
Definition world.qh:115