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 shuffleteams();
372 }
373
374 FOREACH_CLIENT(true, {
375 if (time <= game_starttime)
376 accuracy_reset(it); // for spectators too because weapon accuracy is persistent
377 if (!IS_PLAYER(it))
378 continue;
380 entity store = PS(it);
381 if (store)
382 {
383 Inventory_clear(store.inventory);
384 Inventory_update(store);
385 }
386 });
387
388 MUTATOR_CALLHOOK(reset_map_global);
389
391 {
392 if(IS_CLIENT(it))
393 continue;
394 if (it.reset)
395 {
396 it.reset(it);
397 continue;
398 }
399 if (it.team_saved) it.team = it.team_saved;
400 if (it.flags & FL_PROJECTILE) delete(it); // remove any projectiles left
401 });
402
403 // Waypoints and assault start come LAST
405 if (it.reset2) it.reset2(it);
406 });
407
408 // Moving the player reset code here since the player-reset depends
409 // on spawnpoint entities which have to be reset first --blub
410 if (!MUTATOR_CALLHOOK(reset_map_players))
411 {
413 {
414 // PlayerScore_Clear(it);
415 CS(it).killcount = 0;
416 // stop the player from moving so that they stand still once they get respawned
417 it.velocity = '0 0 0';
418 it.avelocity = '0 0 0';
419 CS(it).movement = '0 0 0';
421
422 if(IS_BOT_CLIENT(it))
423 {
424 .entity weaponentity = weaponentities[0];
425 if(it.(weaponentity).m_weapon == WEP_Null)
426 W_NextWeapon(it, 0, weaponentity);
427 }
428 });
429 }
430}
431
432// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
434{
435 if (!warmup_stage) // if the countdown was not aborted
436 reset_map(false);
437 delete(this);
438}
439
440// Forces a restart of the game without actually reloading the map // this is a mess...
441void ReadyRestart_force(bool is_fake_round_start)
442{
444 return;
445 if (!is_fake_round_start && !autocvar_g_campaign)
446 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_COUNTDOWN_RESTART);
447
448 VoteReset(true);
449
450 // clear overtime, we have to decrease timelimit to its original value again.
454
455 if(warmup_stage)
456 game_starttime = time; // Warmup: No countdown in warmup
457 else if (autocvar_g_campaign)
458 game_starttime = time + 3;
459 else
460 game_starttime = time + RESTART_COUNTDOWN; // Go into match mode
461
462 // clear player attributes
464 it.alivetime_start = 0;
465 CS(it).killcount = 0;
466 });
467
468 // if we're ending the warmup stage call the corresponding hook
469 if(!is_fake_round_start && !warmup_stage)
470 localcmd("\nsv_hook_warmupend\n");
471
472 // reset the .ready status of all clients (including spectators and bots)
473 FOREACH_CLIENT(true, { it.ready = false; });
474 readycount = 0;
475 Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
476
477 // lock teams with lockonrestart
480
481 // initiate the restart-countdown-announcer entity
482 if (!is_fake_round_start && sv_ready_restart_after_countdown && !warmup_stage)
483 {
484 entity restart_timer = new_pure(restart_timer);
485 setthink(restart_timer, ReadyRestart_think);
486 restart_timer.nextthink = game_starttime;
487 }
488
489 // If we're returning to warmup join/switch all queued players for consistency:
490 // there's no queueing in warmup and imbalances are handled by sv_teamnagger blocking end of warmup.
491 if (warmup_stage)
492 {
493 // First pass: join player(s) queued for specific team(s)
494 FOREACH_CLIENT(it.wants_join > 0, Join(it, false));
495 // Second pass: join player(s) queued for team autoselection
496 FOREACH_CLIENT(it.wants_join < 0, Join(it, false));
497 }
498
499 // after a restart every players number of allowed timeouts gets reset, too
501 {
502 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
503 }
504
506 {
508 {
509 if (it.vote_master)
510 ClientData_Touch(it.enemy, true);
511 else
512 {
513 Send_Notification(NOTIF_ONE_ONLY, it, MSG_MULTI, SPECTATE_SPEC_NOTALLOWED);
514 TRANSMUTE(Observer, it);
516 }
517 });
518 }
519
521 reset_map(is_fake_round_start);
522
523 if (autocvar_sv_eventlog) GameLogEcho(":restart");
524}
525
526void ReadyRestart(bool forceWarmupEnd)
527{
528 if (MUTATOR_CALLHOOK(ReadyRestart_Deny))
529 {
530 // NOTE: ReadyRestart support is mandatory in campaign
532 error("ReadyRestart must be supported in campaign mode!");
533 localcmd("restart\n"); // if ReadyRestart is denied, restart the server
534 }
535 else if (intermission_running || race_completing) // game is over, ReadyRestart no longer available
536 localcmd("restart\n");
537 else
538 localcmd("\nsv_hook_readyrestart\n");
539
540 if(forceWarmupEnd || autocvar_g_campaign)
541 warmup_stage = 0; // forcefully end warmup and go to match stage
542 else
543 warmup_stage = autocvar_g_warmup; // go into warmup if it's enabled, otherwise restart into match stage
544
545 ReadyRestart_force(false);
546}
547
548/* Count the players who are ready and determine whether or not to restart the match when:
549 * a player presses F4 server/command/cmd.qc ClientCommand_ready()
550 * a player switches from players to specs server/client.qc PutObserverInServer()
551 * a player joins (from specs or directly) server/client.qc PutPlayerInServer()
552 * a player disconnects server/client.qc ClientDisconnect() */
554{
555 // cannot reset the game while a timeout is active or pending
556 if (timeout_status) return;
557
558 int total_players = 0, human_players = 0, humans_ready = 0;
559 readycount = 0;
560
563 if (it.ready) ++readycount;
564 if (IS_REAL_CLIENT(it))
565 {
566 ++human_players;
567 if (it.ready) ++humans_ready;
568 }
569 });
570
572
573 // can't read warmup_stage here as it could have been set to 0 by ReadyRestart()
574 // and we need to use this when checking if we should abort the countdown
575 // map_minplayers can only be > 0 if g_warmup was -1 at worldspawn
576 int minplayers = autocvar_g_warmup > 1 ? autocvar_g_warmup : map_minplayers;
577
578 // This allows warmup to end with zero players to prevent complaints
579 // of server never changing map with legacy config (sv_autopause 0).
580 bool badteams = (teamplay && total_players && autocvar_sv_teamnagger)
582 : false;
583
584 if (total_players < minplayers || badteams)
585 {
586 if (game_starttime > time) // someone bailed during countdown, back to warmup
587 {
588 warmup_stage = autocvar_g_warmup; // CAN change it AFTER calling Nagger_ReadyCounted() this frame
590 if (total_players < minplayers)
591 Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP_MINPLAYERS, minplayers);
592 else
593 Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP_BADTEAMS);
594 if (!sv_ready_restart_after_countdown) // if we ran reset_map() at start of countdown
596 }
597 warmup_limit = -1;
598 return; // don't ReadyRestart if players are ready but too few or teams are bad
599 }
600 else if (warmup_limit <= 0
601 && game_starttime <= time) // No countdown in progress, check prevents early countdown end if only player leaves
602 {
603 // there's enough players now and teams are ok
604 // but we're still in infinite warmup and may need to switch to timed warmup
605 warmup_limit = cvar("g_warmup_limit");
606 if (warmup_limit == 0)
608 if (warmup_limit > 0)
610 // implicit else: g_warmup -1 && g_warmup_limit -1 means
611 // warmup continues until enough players AND enough RUPs (no time limit)
612 }
613
614 if (humans_ready && humans_ready >= rint(human_players * bound(0.5, cvar("g_warmup_majority_factor"), 1)))
615 ReadyRestart(true);
616}
617
618
619// ======================================
620// Supporting functions for VoteCommand
621// ======================================
622
623bool Votecommand_check_assignment(entity caller, float assignment)
624{
625 bool from_server = (!caller);
626
627 if ((assignment == VC_ASGNMNT_BOTH)
628 || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
629 || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
630
631 return false;
632}
633
634string VoteCommand_extractcommand(string input, float startpos, int argc)
635{
636 string output;
637
638 if ((argc - 1) < startpos) output = "";
639 else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
640
641 return output;
642}
643
644bool VoteCommand_checknasty(string vote_command)
645{
646 return !((strstrofs(vote_command, ";", 0) >= 0)
647 || (strstrofs(vote_command, "\n", 0) >= 0)
648 || (strstrofs(vote_command, "\r", 0) >= 0)
649 || (strstrofs(vote_command, "$", 0) >= 0));
650}
651
652// NOTE: requires input to be surrounded by spaces
654{
655 // add a space around the input so the start and end of the list is captured
656 string output = strcat(" ", input, " ");
657 // allow gotomap replacements
658 output = strreplace(" map ", " gotomap ", output);
659 output = strreplace(" chmap ", " gotomap ", output);
660 return output;
661}
662
663bool VoteCommand_checkinlist(string vote_command, string list)
664{
665 if (vote_command == "" || list == "")
666 return false;
667
668 string l = VoteCommand_checkreplacements(list);
669 return (strstrofs(l, VoteCommand_checkreplacements(vote_command), 0) >= 0);
670}
671
672string ValidateMap(string validated_map, entity caller)
673{
674 validated_map = MapInfo_FixName(validated_map);
675
676 if (!validated_map)
677 {
678 print_to(caller, "This map is not available on this server.");
679 return string_null;
680 }
681
683 {
684 if (Map_IsRecent(validated_map))
685 {
686 print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
687 return string_null;
688 }
689 }
690
691 if (!MapInfo_CheckMap(validated_map))
692 {
693 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current gametype."));
694 return string_null;
695 }
696
697 return validated_map;
698}
699
700float VoteCommand_checkargs(float startpos, int argc)
701{
702 float p, q, check, minargs;
703 string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
704 string cmdrestriction = ""; // No we don't.
705 string charlist, arg;
706 float checkmate;
707
708 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
709 cmdrestriction = cvar_string(cvarname);
710 else
711 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
712
713 if (cmdrestriction == "") return true;
714
715 ++startpos; // skip command name
716
717 // check minimum arg count
718
719 // 0 args: argc == startpos
720 // 1 args: argc == startpos + 1
721 // ...
722
723 minargs = stof(cmdrestriction);
724 if (argc - startpos < minargs) return false;
725
726 p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
727
728 for ( ; ; )
729 {
730 // we know that at any time, startpos <= argc - minargs
731 // so this means: argc-minargs >= startpos >= argc, thus
732 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
733 // have been seen already
734
735 if (startpos >= argc) // all args checked? GOOD
736 break;
737
738 if (p < 0) // no more args? FAIL
739 {
740 // exception: exactly minargs left, this one included
741 if (argc - startpos == minargs) break;
742
743 // otherwise fail
744 return false;
745 }
746
747 // cut to next semicolon
748 q = strstrofs(cmdrestriction, ";", p + 1); // find next semicolon
749 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
750 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
751
752 // in case we ever want to allow semicolons in VoteCommand_checknasty
753 // charlist = strreplace("^^", ";", charlist);
754
755 if (charlist != "")
756 {
757 // verify the arg only contains allowed chars
758 arg = argv(startpos);
759 checkmate = strlen(arg);
760 for (check = 0; check < checkmate; ++check)
761 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
762 // not allowed character
763 // all characters are allowed. FINE.
764 }
765
766 ++startpos;
767 --minargs;
768 p = q;
769 }
770
771 return true;
772}
773
774int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
775{
776 string first_command = argv(startpos);
777 int missing_chars = argv_start_index(startpos);
778
779 if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
780 return 0;
781
782 if (caller && !VoteCommand_checkinlist(first_command, vote_list)) return 0;
783
784 if (!VoteCommand_checkargs(startpos, argc)) return 0;
785
786 switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
787 {
788 case MUT_VOTEPARSE_CONTINUE: { break; }
789 case MUT_VOTEPARSE_SUCCESS: { return 1; }
790 case MUT_VOTEPARSE_INVALID: { return -1; }
791 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
792 }
793
794 switch (first_command) // now go through and parse the proper commands to adjust as needed.
795 {
796 case "movetoauto":
797 case "movetored":
798 case "movetoblue":
799 case "movetoyellow":
800 case "movetopink":
801 case "movetospec":
802 {
803 entity victim = GetIndexedEntity(argc, (startpos + 1));
804 float accepted = VerifyClientEntity(victim, true, false);
805 if (accepted > 0)
806 {
807 vote_parsed_command = vote_command;
808 vote_parsed_display = sprintf("^1%s #%d ^7%s", first_command, etof(victim), victim.netname);
809 }
810 else
811 {
812 print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n"));
813 return 0;
814 }
815
816 break;
817 }
818
819 case "kick":
820 case "kickban": // catch all kick/kickban commands
821 {
822 entity victim = GetIndexedEntity(argc, (startpos + 1));
823 float accepted = VerifyClientEntity(victim, true, false);
824
825 if (accepted > 0)
826 {
827 string reason = "No reason provided";
828 if(argc > next_token)
829 reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
830
831 string command_arguments = reason;
832 if (first_command == "kickban")
834
835 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
836 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
837 }
838 else
839 {
840 print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n"));
841 return 0;
842 }
843
844 break;
845 }
846
847 case "map":
848 case "chmap":
849 case "gotomap": // re-direct all map selection commands to gotomap
850 {
851 vote_command = ValidateMap(argv(startpos + 1), caller);
852 if (!vote_command) return -1;
853 vote_parsed_command = strcat("gotomap ", vote_command);
855
856 break;
857 }
858
859 // TODO: replicate the old behaviour of being able to vote for maps from different modes on multimode servers (possibly support it in gotomap too)
860 // maybe fallback instead of aborting if map name is invalid?
861 case "nextmap":
862 {
863 vote_command = ValidateMap(argv(startpos + 1), caller);
864 if (!vote_command) return -1;
865 vote_parsed_command = strcat("nextmap ", vote_command);
867
868 break;
869 }
870
871 case "fraglimit": // include restrictions on the maximum votable frag limit
872 {
873 float fraglimit_vote = stof(argv(startpos + 1));
874 float fraglimit_min = 0;
875 float fraglimit_max = 999999;
876 if(fraglimit_vote > fraglimit_max || fraglimit_vote < fraglimit_min)
877 {
878 print_to(caller, strcat("Invalid fraglimit vote, accepted values are between ", ftos(fraglimit_min), " and ", ftos(fraglimit_max), "."));
879 return -1;
880 }
881 vote_parsed_command = strcat("fraglimit ", ftos(fraglimit_vote));
883
884 break;
885 }
886
887 case "timelimit": // include restrictions on the maximum votable time limit
888 {
889 float timelimit_vote = stof(argv(startpos + 1));
890 if(timelimit_vote > autocvar_timelimit_max || timelimit_vote < autocvar_timelimit_min)
891 {
892 print_to(caller, strcat("Invalid timelimit vote, accepted values are between ", ftos(autocvar_timelimit_min), " and ", ftos(autocvar_timelimit_max), "."));
893 return -1;
894 }
895 vote_parsed_command = strcat("timelimit ", ftos(timelimit_vote));
897
898 break;
899 }
900
901 case "restart":
902 {
903 // add a delay so that vote result can be seen and announcer can be heard
904 // if the vote is accepted
905 vote_parsed_command = strcat("defer 1 ", vote_command);
906 vote_parsed_display = strzone(strcat("^1", vote_command));
907
908 break;
909 }
910
911 case "allready":
912 {
913 if(!warmup_stage) {
914 print_to(caller, "Game already started. Use the resetmatch command to restart the match.");
915 return -1;
916 }
917
918 vote_parsed_command = vote_command;
919 vote_parsed_display = strzone(strcat("^1", vote_command));
920 break;
921 }
922
923 default:
924 {
925 vote_parsed_command = vote_command;
926 vote_parsed_display = strzone(strcat("^1", vote_command));
927
928 break;
929 }
930 }
931
932 return 1;
933}
934
935
936// =======================
937// Command Sub-Functions
938// =======================
939
940void VoteCommand_abstain(int request, entity caller) // CLIENT ONLY
941{
942 switch (request)
943 {
945 {
946 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
947 {
948 print_to(caller, "^1You are banned from voting.");
949 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN);
950 return;
951 }
952
953 if (!vote_called) { print_to(caller, "^1No vote called."); }
954 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
955 {
956 print_to(caller, "^1You have already voted.");
957 }
958
959 else // everything went okay, continue changing vote
960 {
961 print_to(caller, "^1You abstained from your vote.");
962 caller.vote_selection = VOTE_SELECT_ABSTAIN;
963 msg_entity = caller;
965 VoteCount(false);
966 }
967
968 return;
969 }
970
971 default:
973 {
974 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
975 print_to(caller, " No arguments required.");
976 return;
977 }
978 }
979}
980
982{
983 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"));
984}
985
986void VoteCommand_call(int request, entity caller, int argc, string vote_command) // BOTH
987{
988 switch (request)
989 {
991 {
992 float tmp_playercount = 0;
993 int parse_error;
994
995 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
996 {
997 print_to(caller, "^1You are banned from calling a vote.");
998 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBAN);
999 return;
1000 }
1001
1002 vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
1003
1004 if (!autocvar_sv_vote_call && caller)
1005 {
1006 print_to(caller, "^1Vote calling is not allowed.");
1007 }
1009 {
1010 print_to(caller, "^1Vote calling is not allowed before the match has started.");
1011 }
1012 else if (vote_called)
1013 {
1014 print_to(caller, "^1There is already a vote called.");
1015 }
1016 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
1017 {
1018 print_to(caller, "^1Only players can call a vote.");
1019 }
1020 else if (caller && !IS_CLIENT(caller))
1021 {
1022 print_to(caller, "^1Only connected clients can vote.");
1023 }
1024 else if (timeout_status && vote_command != "timein")
1025 {
1026 print_to(caller, "^1You can not call a vote while a timeout is active.");
1027 }
1028 else if (caller && (time < caller.vote_waittime))
1029 {
1030 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
1031 }
1032 else if (!VoteCommand_checknasty(vote_command))
1033 {
1034 print_to(caller, "^1Syntax error in command.");
1035 }
1036 else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
1037 {
1038 if(parse_error == 0)
1039 {
1040 if (vote_called_command == "")
1041 VoteCommand_call(CMD_REQUEST_USAGE, caller, argc, vote_command);
1042 else
1043 print_to(caller, "^1This command is not acceptable or not available.");
1044 }
1045 }
1046 else // everything went okay, continue with calling the vote
1047 {
1048 vote_caller = caller; // remember who called the vote
1054
1055 if (caller)
1056 {
1057 caller.vote_selection = VOTE_SELECT_ACCEPT;
1058 caller.vote_waittime = time + autocvar_sv_vote_wait;
1059 msg_entity = caller;
1060 }
1061
1062 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
1063
1064 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
1066 bprint("\{1}^2* ^3", "^6DEBUG MODE ACTIVE: bots can vote too\n"); // so servers don't forget it on
1068 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
1070 VoteCount(true); // needed if you are the only one
1071
1072 if (tmp_playercount > 1 && vote_called != VOTE_NULL)
1073 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
1074 }
1075
1076 return;
1077 }
1078
1079 default:
1080 case CMD_REQUEST_USAGE:
1081 {
1082 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call <command>"));
1083 print_to(caller, " Where <command> is the command to request a vote upon.");
1084 print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
1085 print_to(caller, strcat(" ", GetCommandPrefix(caller), " vote call endmatch"));
1087 print_to(caller, "Shortcuts: ^2vcall <command>, vend, vmap, vkick, ...");
1088 return;
1089 }
1090 }
1091}
1092
1093void VoteCommand_master(int request, entity caller, int argc, string vote_command) // CLIENT ONLY
1094{
1095 switch (request)
1096 {
1098 {
1100 {
1101 switch (strtolower(argv(2)))
1102 {
1103 case "do":
1104 {
1105 int parse_error;
1106 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
1107
1108 if (!caller.vote_master)
1109 print_to(caller, "^1You do not have vote master privileges.");
1110 else if (!VoteCommand_checknasty(vote_command))
1111 {
1112 print_to(caller, "^1Syntax error in command.");
1113 }
1114 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
1115 {
1116 if(parse_error == 0)
1117 {
1118 if (vote_called_command == "")
1119 VoteCommand_master(CMD_REQUEST_USAGE, caller, argc, vote_command);
1120 else
1121 print_to(caller, "^1This command is not acceptable or not available.");
1122 }
1123 }
1124 else // everything went okay, proceed with command
1125 {
1127 print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
1128 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
1130 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
1131 }
1132
1133 return;
1134 }
1135
1136 case "login":
1137 {
1138 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
1139 else if (caller.vote_master)
1140 {
1141 print_to(caller, "^1You are already logged in as vote master.");
1142 }
1144 {
1145 print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
1146 }
1147 else // everything went okay, proceed with giving this player master privilages
1148 {
1149 caller.vote_master = true;
1150 print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
1151 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
1153 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
1154 }
1155
1156 return;
1157 }
1158
1159 default: // calling a vote for master
1160 {
1161 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
1162 else if (vote_called)
1163 {
1164 print_to(caller, "^1There is already a vote called.");
1165 }
1166 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
1167 {
1168 print_to(caller, "^1Only players can call a vote.");
1169 }
1170 else if (timeout_status)
1171 {
1172 print_to(caller, "^1You can not call a vote while a timeout is active.");
1173 }
1174 else // everything went okay, continue with creating vote
1175 {
1176 vote_caller = caller;
1180 vote_called_display = strzone("^3master");
1182
1183 caller.vote_selection = VOTE_SELECT_ACCEPT;
1184 caller.vote_waittime = time + autocvar_sv_vote_wait;
1185
1186 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
1188 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
1190 VoteCount(true); // needed if you are the only one
1191 }
1192
1193 return;
1194 }
1195 }
1196 }
1197 else { print_to(caller, "^1Master control of voting is not allowed."); }
1198
1199 return;
1200 }
1201
1202 default:
1203 case CMD_REQUEST_USAGE:
1204 {
1205 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [<action> [<command> | <password>]]"));
1206 print_to(caller, " If <action> is left blank, it calls a vote for you to become master.");
1207 print_to(caller, " Otherwise it can be either 'do' (to run <command>) or 'login' as master.");
1208 return;
1209 }
1210 }
1211}
1212
1213void VoteCommand_no(int request, entity caller) // CLIENT ONLY
1214{
1215 switch (request)
1216 {
1218 {
1219 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
1220 {
1221 print_to(caller, "^1You are banned from voting.");
1222 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN);
1223 return;
1224 }
1225
1226 if (!vote_called) { print_to(caller, "^1No vote called."); }
1227 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1228 {
1229 print_to(caller, "^1You have already voted.");
1230 }
1231 else if (caller == vote_caller && autocvar_sv_vote_no_stops_vote)
1232 {
1233 VoteStop(caller, false);
1234 }
1235
1236 else // everything went okay, continue changing vote
1237 {
1238 print_to(caller, "^1You rejected the vote.");
1239 caller.vote_selection = VOTE_SELECT_REJECT;
1240 msg_entity = caller;
1242 VoteCount(false);
1243 }
1244
1245 return;
1246 }
1247
1248 default:
1249 case CMD_REQUEST_USAGE:
1250 {
1251 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1252 print_to(caller, " No arguments required.");
1253 return;
1254 }
1255 }
1256}
1257
1258void VoteCommand_status(int request, entity caller) // BOTH
1259{
1260 switch (request)
1261 {
1263 {
1264 if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1265 else print_to(caller, "^1No vote called.");
1266
1267 return;
1268 }
1269
1270 default:
1271 case CMD_REQUEST_USAGE:
1272 {
1273 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1274 print_to(caller, " No arguments required.");
1275 return;
1276 }
1277 }
1278}
1279
1280void VoteCommand_stop(int request, entity caller) // BOTH
1281{
1282 switch (request)
1283 {
1285 {
1286 if (!vote_called) print_to(caller, "^1No vote called.");
1287 else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller, false);
1288 else print_to(caller, "^1You are not allowed to stop that vote.");
1289 return;
1290 }
1291
1292 default:
1293 case CMD_REQUEST_USAGE:
1294 {
1295 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1296 print_to(caller, " No arguments required.");
1297 return;
1298 }
1299 }
1300}
1301
1302void VoteCommand_yes(int request, entity caller) // CLIENT ONLY
1303{
1304 switch (request)
1305 {
1307 {
1308 if (PlayerInList(caller, autocvar_g_voteban_list)) // voteban
1309 {
1310 print_to(caller, "^1You are banned from voting.");
1311 Send_Notification(NOTIF_ONE, caller, MSG_CENTER, CENTER_VOTEBANYN);
1312 return;
1313 }
1314
1315 if (!vote_called) { print_to(caller, "^1No vote called."); }
1316 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1317 {
1318 print_to(caller, "^1You have already voted.");
1319 }
1320 else // everything went okay, continue changing vote
1321 {
1322 print_to(caller, "^1You accepted the vote.");
1323 caller.vote_selection = VOTE_SELECT_ACCEPT;
1324 msg_entity = caller;
1326 VoteCount(false);
1327 }
1328
1329 return;
1330 }
1331
1332 default:
1333 case CMD_REQUEST_USAGE:
1334 {
1335 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1336 print_to(caller, " No arguments required.");
1337 return;
1338 }
1339 }
1340}
1341
1342/* use this when creating a new command, making sure to place it in alphabetical order... also,
1343** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1344void VoteCommand_(int request)
1345{
1346 switch(request)
1347 {
1348 case CMD_REQUEST_COMMAND:
1349 {
1350
1351 return;
1352 }
1353
1354 default:
1355 case CMD_REQUEST_USAGE:
1356 {
1357 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1358 print_to(caller, " No arguments required.");
1359 return;
1360 }
1361 }
1362}
1363*/
1364
1365
1366// ================================
1367// Macro system for vote commands
1368// ================================
1369
1370// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1371#define VOTE_COMMANDS(request, caller, arguments, command) \
1372 VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1373 VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1374 VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1375 VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1376 VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1377 VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1378 VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1379 VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1380 /* nothing */
1381
1382void VoteCommand_macro_help(entity caller, int argc)
1383{
1384 string command_origin = GetCommandPrefix(caller);
1385
1386 if (argc == 2 || argv(2) == "help") // help display listing all commands
1387 {
1388 print_to(caller, "\nVoting commands:\n");
1389 #define VOTE_COMMAND(name, function, description, assignment) \
1390 { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } }
1391
1392 VOTE_COMMANDS(0, caller, 0, "");
1393 #undef VOTE_COMMAND
1394
1395 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote <command>^7, where possible commands are listed above.\n"));
1396 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help <command>"));
1398 }
1399 else // usage for individual command
1400 {
1401 #define VOTE_COMMAND(name, function, description, assignment) \
1402 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1403
1404 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1405 #undef VOTE_COMMAND
1406
1407 string cvarname = strcat("sv_vote_command_help_", argv(2));
1408 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1409 wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1410 else if (argv(2) != "")
1411 print_to(caller, "No documentation exists for this vote");
1412 }
1413}
1414
1415float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1416{
1417 #define VOTE_COMMAND(name, function, description, assignment) \
1418 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1419
1420 VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1421 #undef VOTE_COMMAND
1422
1423 return false;
1424}
1425
1426
1427// ======================================
1428// Main function handling vote commands
1429// ======================================
1430
1431void VoteCommand(int request, entity caller, int argc, string vote_command)
1432{
1433 // Guide for working with argc arguments by example:
1434 // argc: 1 - 2 - 3 - 4
1435 // argv: 0 - 1 - 2 - 3
1436 // cmd vote - master - login - password
1437
1438 switch (request)
1439 {
1441 {
1442 if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1443 }
1444
1445 default:
1446 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"));
1447 case CMD_REQUEST_USAGE:
1448 {
1449 VoteCommand_macro_help(caller, argc);
1450 return;
1451 }
1452 }
1453}
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(id,...)
Definition base.qh:143
#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:244
#define IS_CLIENT(s)
Definition player.qh:242
#define IS_PLAYER(s)
Definition player.qh:243
float warmup_limit
Definition stats.qh:375
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:2082
void wordwrap_sprint(entity to, string s, float l)
Definition util.qc:192
string strtolower(string s)
const int FL_PROJECTILE
Definition constants.qh:85
float maxclients
float CVAR_TYPEFLAG_EXISTS
float time
#define strstrofs
#define argv_end_index
#define strlen
#define argv_start_index
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:169
#define FOREACH_ENTITY_ORDERED(cond, body)
Definition iter.qh:138
#define FOREACH_ENTITY_FLOAT_ORDERED(fld, match, body)
Definition iter.qh:175
#define PutClientInServer
Definition _all.inc:246
const int MSG_ENTITY
Definition net.qh:115
#define WriteHeader(to, id)
Definition net.qh:221
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
#define LOG_INFO(...)
Definition log.qh:65
float MapInfo_CheckMap(string s)
Definition mapinfo.qc:1510
string MapInfo_FixName(string s)
Definition mapinfo.qc:1474
int map_minplayers
Definition mapinfo.qh:190
bool autocvar_g_campaign
Definition menu.qc:747
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)
void WriteString(string data, float dest, float desto)
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)
void WriteByte(float data, float dest, float desto)
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:1573
#define TRANSMUTE(cname, this,...)
Definition oo.qh:136
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
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:43
void round_handler_Reset(float next_think)
#define round_handler_IsActive()
void Score_ClearAll()
Clear ALL scores (for ready-restart).
Definition scores.qc:306
void W_NextWeapon(entity this, int list,.entity weaponentity)
Definition selection.qc:322
#define setthink(e, f)
void player_powerups_remove_all(entity this, bool allow_poweroff_sound)
Definition client.qc:1535
void GiveWarmupResources(entity this)
Definition client.qc:568
bool PlayerInList(entity player, string list)
Definition client.qc:1045
void Join(entity this, bool queued_join)
it's assumed this isn't called for bots (campaign_bots_may_start, centreprints)
Definition client.qc:2068
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:553
void VoteReject()
Definition vote.qc:184
float VoteCommand_checkargs(float startpos, int argc)
Definition vote.qc:700
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:986
string OriginalCallerName()
Definition vote.qc:118
void VoteCommand_yes(int request, entity caller)
Definition vote.qc:1302
void print_available_commands_to(entity caller)
Definition vote.qc:981
void reset_map(bool is_fake_round_start)
Definition vote.qc:351
string ValidateMap(string validated_map, entity caller)
Definition vote.qc:672
#define VOTE_COMMANDS(request, caller, arguments, command)
Definition vote.qc:1371
bool Votecommand_check_assignment(entity caller, float assignment)
Definition vote.qc:623
float VoteCommand_macro_command(entity caller, int argc, string vote_command)
Definition vote.qc:1415
void VoteCount(float first_count)
Definition vote.qc:220
string VoteCommand_checkreplacements(string input)
Definition vote.qc:653
void Nagger_VoteCountChanged()
Definition vote.qc:107
void ReadyRestart(bool forceWarmupEnd)
Definition vote.qc:526
void VoteTimeout()
Definition vote.qc:191
void Nagger_ReadyCounted()
Definition vote.qc:112
void VoteCommand_no(int request, entity caller)
Definition vote.qc:1213
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:1431
void VoteCommand_abstain(int request, entity caller)
Definition vote.qc:940
void Nagger_VoteChanged()
Definition vote.qc:102
string VoteCommand_extractcommand(string input, float startpos, int argc)
Definition vote.qc:634
bool VoteCommand_checkinlist(string vote_command, string list)
Definition vote.qc:663
void VoteStop(entity stopper, bool show_name)
Definition vote.qc:156
void VoteCommand_macro_help(entity caller, int argc)
Definition vote.qc:1382
void ReadyRestart_think(entity this)
Definition vote.qc:433
void VoteCommand_master(int request, entity caller, int argc, string vote_command)
Definition vote.qc:1093
void VoteCommand_stop(int request, entity caller)
Definition vote.qc:1280
int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
Definition vote.qc:774
bool Nagger_SendEntity(entity this, entity to, float sendflags)
Definition vote.qc:37
void VoteCommand_status(int request, entity caller)
Definition vote.qc:1258
bool VoteCommand_checknasty(string vote_command)
Definition vote.qc:644
void ReadyRestart_force(bool is_fake_round_start)
Definition vote.qc:441
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:1248
@ MUT_VOTEPARSE_UNACCEPTABLE
Definition events.qh:1251
@ MUT_VOTEPARSE_INVALID
Definition events.qh:1250
@ MUT_VOTEPARSE_SUCCESS
Definition events.qh:1249
int g_race_qualifying
Definition race.qh:13
float race_completing
Definition race.qh:28
#define PS(this)
Definition state.qh:18
ClientState CS(Client this)
Definition state.qh:47
#define strfree(this)
Definition string.qh:59
void shuffleteams()
Definition sv_cmd.qc:1339
bool shuffleteams_on_reset_map
Definition sv_cmd.qh:7
#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:633
bool autocvar_teamplay_lockonrestart
Definition teamplay.qh:5
bool lockteams
Definition teamplay.qh:15
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:50
#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:116