3 #include <server/defs.qh>
4 #include <server/miscfunctions.qh>
6 #include <common/command/_mod.qh>
11 #include "../g_damage.qh"
12 #include "../g_world.qh"
13 #include "../teamplay.qh"
15 #include "../round_handler.qh"
16 #include "../scores.qh"
18 #include <server/mutators/_mod.qh>
19 #include <common/gamemodes/_mod.qh>
21 #include <common/constants.qh>
22 #include <common/net_linked.qh>
23 #include <common/mapinfo.qh>
24 #include <common/notifications/all.qh>
25 #include <common/playerstats.qh>
26 #include <common/util.qh>
28 // =============================================
29 // Server side voting code, reworked by Samual
30 // Last updated: December 27th, 2011
31 // =============================================
33 // Nagger for players to know status of voting
34 bool Nagger_SendEntity(entity this, entity to, float sendflags)
38 WriteHeader(MSG_ENTITY, ENT_CLIENT_NAGGER);
42 // 2 = player needs to ready up
44 // 8 = player needs to vote
54 if (to.ready == 0) nags |= BIT(1);
59 if (to.vote_selection == 0) nags |= BIT(3);
61 if (warmup_stage) nags |= BIT(4);
63 if (sendflags & BIT(6)) nags |= BIT(6);
65 if (sendflags & BIT(7)) nags |= BIT(7);
67 if (!(nags & 4)) // no vote called? send no string
68 nags &= ~(BIT(6) | BIT(7));
70 WriteByte(MSG_ENTITY, nags);
74 WriteByte(MSG_ENTITY, vote_accept_count);
75 WriteByte(MSG_ENTITY, vote_reject_count);
76 WriteByte(MSG_ENTITY, vote_needed_overall);
77 WriteChar(MSG_ENTITY, to.vote_selection);
80 if (nags & BIT(7)) WriteString(MSG_ENTITY, vote_called_display);
84 for (i = 1; i <= maxclients; i += 8)
86 for (f = 0, e = edict_num(i), b = BIT(0); b < BIT(8); b <<= 1, e = nextent(e))
87 if (!IS_REAL_CLIENT(e) || e.ready)
89 WriteByte(MSG_ENTITY, f);
98 Net_LinkEntity(nagger = new_pure(nagger), false, 0, Nagger_SendEntity);
101 void Nagger_VoteChanged()
103 if (nagger) nagger.SendFlags |= BIT(7);
106 void Nagger_VoteCountChanged()
108 if (nagger) nagger.SendFlags |= BIT(6);
111 void Nagger_ReadyCounted()
113 if (nagger) nagger.SendFlags |= BIT(0);
116 // If the vote_caller is still here, return their name, otherwise vote_caller_name
117 string OriginalCallerName()
119 if (IS_REAL_CLIENT(vote_caller)) return playername(vote_caller, false);
120 return vote_caller_name;
123 // =======================
124 // Game logic for voting
125 // =======================
129 FOREACH_CLIENT(true, { it.vote_selection = 0; });
133 strfree(vote_called_command);
134 strfree(vote_called_display);
135 strfree(vote_caller_name);
138 vote_called = VOTE_NULL;
142 vote_parsed_command = string_null;
143 vote_parsed_display = string_null;
145 Nagger_VoteChanged();
148 void VoteStop(entity stopper)
150 bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n");
151 if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));
152 // Don't force them to wait for next vote, this way they can e.g. correct their vote.
153 if ((vote_caller) && (stopper == vote_caller)) vote_caller.vote_waittime = time + autocvar_sv_vote_stop;
159 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ^1", vote_called_display, "^2 was accepted\n");
161 if ((vote_called == VOTE_MASTER) && vote_caller) vote_caller.vote_master = 1;
162 else localcmd(strcat(vote_called_command, "\n"));
164 if (vote_caller) vote_caller.vote_waittime = 0; // people like your votes, you don't need to wait to vote again
167 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_ACCEPT);
172 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 was rejected\n");
174 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
179 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 timed out\n");
181 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
184 void VoteSpam(float notvoters, float mincount, string result)
187 strcat("\{1}^2* vote results: ^1", ftos(vote_accept_count)),
188 strcat("^2:^1", ftos(vote_reject_count)),
189 ((mincount >= 0) ? strcat("^2 (^1", ftos(mincount), "^2 needed)") : "^2"),
190 strcat(", ^1", ftos(vote_abstain_count), "^2 didn't care"),
191 strcat(", ^1", ftos(notvoters), strcat("^2 didn't ", ((mincount >= 0) ? "" : "have to "), "vote\n"))));
193 if (autocvar_sv_eventlog)
196 strcat(":vote:v", result, ":", ftos(vote_accept_count)),
197 strcat(":", ftos(vote_reject_count)),
198 strcat(":", ftos(vote_abstain_count)),
199 strcat(":", ftos(notvoters)),
200 strcat(":", ftos(mincount))));
204 #define spectators_allowed (!autocvar_sv_vote_nospectators || (autocvar_sv_vote_nospectators == 1 && (warmup_stage || intermission_running)))
206 void VoteCount(float first_count)
209 vote_accept_count = vote_reject_count = vote_abstain_count = 0;
211 float vote_player_count = 0, notvoters = 0;
212 float vote_real_player_count = 0, vote_real_accept_count = 0;
213 float vote_real_reject_count = 0, vote_real_abstain_count = 0;
214 float vote_needed_of_voted, final_needed_votes;
215 float vote_factor_overall, vote_factor_of_voted;
217 Nagger_VoteCountChanged();
219 // add up all the votes from each connected client
220 FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_CLIENT(it), {
222 if (IS_PLAYER(it)) ++vote_real_player_count;
223 switch (it.vote_selection)
225 case VOTE_SELECT_REJECT:
226 { ++vote_reject_count;
227 { if (IS_PLAYER(it)) ++vote_real_reject_count; } break;
229 case VOTE_SELECT_ACCEPT:
230 { ++vote_accept_count;
231 { if (IS_PLAYER(it)) ++vote_real_accept_count; } break;
233 case VOTE_SELECT_ABSTAIN:
234 { ++vote_abstain_count;
235 { if (IS_PLAYER(it)) ++vote_real_abstain_count; } break;
241 // Check to see if there are enough players on the server to allow master voting... otherwise, vote master could be used for evil.
242 if ((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count)
244 if (vote_caller) vote_caller.vote_waittime = 0;
245 print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master.");
250 // 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.
251 if (!spectators_allowed && (vote_real_player_count > 0))
253 vote_accept_count = vote_real_accept_count;
254 vote_reject_count = vote_real_reject_count;
255 vote_abstain_count = vote_real_abstain_count;
256 vote_player_count = vote_real_player_count;
259 // people who have no opinion in any way :D
260 notvoters = (vote_player_count - vote_accept_count - vote_reject_count - vote_abstain_count);
262 // determine the goal for the vote to be passed or rejected normally
263 vote_factor_overall = bound(0.5, autocvar_sv_vote_majority_factor, 0.999);
264 vote_needed_overall = floor((vote_player_count - vote_abstain_count) * vote_factor_overall) + 1;
266 // if the vote times out, determine the amount of votes needed of the people who actually already voted
267 vote_factor_of_voted = bound(0.5, autocvar_sv_vote_majority_factor_of_voted, 0.999);
268 vote_needed_of_voted = floor((vote_accept_count + vote_reject_count) * vote_factor_of_voted) + 1;
270 // are there any players at all on the server? it could be an admin vote
271 if (vote_player_count == 0 && first_count)
273 VoteSpam(0, -1, "yes"); // no players at all, just accept it
278 // since there ARE players, finally calculate the result of the vote
279 if (vote_accept_count >= vote_needed_overall)
281 VoteSpam(notvoters, -1, "yes"); // there is enough acceptions to pass the vote
286 if (vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall)
288 VoteSpam(notvoters, -1, "no"); // there is enough rejections to deny the vote
293 // there is not enough votes in either direction, now lets just calculate what the voters have said
294 if (time > vote_endtime)
296 final_needed_votes = vote_needed_overall;
298 if (autocvar_sv_vote_majority_factor_of_voted)
300 if (vote_accept_count >= vote_needed_of_voted)
302 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "yes");
307 if (vote_accept_count + vote_reject_count > 0)
309 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "no");
314 final_needed_votes = min(vote_needed_overall, vote_needed_of_voted);
317 // it didn't pass or fail, so not enough votes to even make a decision.
318 VoteSpam(notvoters, final_needed_votes, "timeout");
325 if (vote_endtime > 0) // a vote was called
327 if (time > vote_endtime) // time is up
333 // =======================
334 // Game logic for warmup
335 // =======================
337 // Resets the state of all clients, items, weapons, waypoints, ... of the map.
338 void reset_map(bool dorespawn)
340 if (time <= game_starttime)
344 if (round_handler_IsActive())
345 round_handler_Reset(game_starttime);
348 if (shuffleteams_on_reset_map)
351 shuffleteams_on_reset_map = false;
353 MUTATOR_CALLHOOK(reset_map_global);
355 FOREACH_ENTITY_FLOAT_ORDERED(pure_data, false,
364 if (it.team_saved) it.team = it.team_saved;
365 if (it.flags & FL_PROJECTILE) delete(it); // remove any projectiles left
368 // Waypoints and assault start come LAST
369 FOREACH_ENTITY_ORDERED(IS_NOT_A_CLIENT(it), {
370 if (it.reset2) it.reset2(it);
373 FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it, false); });
375 // Moving the player reset code here since the player-reset depends
376 // on spawnpoint entities which have to be reset first --blub
379 if (!MUTATOR_CALLHOOK(reset_map_players))
381 if (restart_mapalreadyrestarted || (time < game_starttime))
383 FOREACH_CLIENT(IS_PLAYER(it),
386 only reset players if a restart countdown is active
387 this can either be due to cvar sv_ready_restart_after_countdown having set
388 restart_mapalreadyrestarted to 1 after the countdown ended or when
389 sv_ready_restart_after_countdown is not used and countdown is still running
391 // NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
392 // PlayerScore_Clear(it);
393 CS(it).killcount = 0;
394 // stop the player from moving so that he stands still once he gets respawned
395 it.velocity = '0 0 0';
396 it.avelocity = '0 0 0';
397 CS(it).movement = '0 0 0';
398 PutClientInServer(it);
405 // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
406 void ReadyRestart_think(entity this)
408 restart_mapalreadyrestarted = true;
414 // Forces a restart of the game without actually reloading the map // this is a mess...
415 void ReadyRestart_force()
417 if (time <= game_starttime && game_stopped)
420 bprint("^1Server is restarting...\n");
424 // clear overtime, we have to decrease timelimit to its original value again.
425 if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
426 cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
427 checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
429 readyrestart_happened = true;
430 game_starttime = time + RESTART_COUNTDOWN;
432 // clear player attributes
433 FOREACH_CLIENT(IS_PLAYER(it), {
435 CS(it).killcount = 0;
436 float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
437 PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
440 restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
442 // disable the warmup global for the server
444 localcmd("\nsv_hook_warmupend\n");
445 warmup_stage = 0; // once the game is restarted the game is in match stage
447 // reset the .ready status of all players (also spectators)
448 FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
450 Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
452 // lock teams with lockonrestart
453 if (autocvar_teamplay_lockonrestart && teamplay)
456 bprint("^1The teams are now locked.\n");
459 // initiate the restart-countdown-announcer entity
460 if (sv_ready_restart_after_countdown)
462 entity restart_timer = new_pure(restart_timer);
463 setthink(restart_timer, ReadyRestart_think);
464 restart_timer.nextthink = game_starttime;
467 // after a restart every players number of allowed timeouts gets reset, too
468 if (autocvar_sv_timeout)
470 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
473 if (!sv_ready_restart_after_countdown) reset_map(true);
474 if (autocvar_sv_eventlog) GameLogEcho(":restart");
479 if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || game_stopped || race_completing) localcmd("restart\n");
480 else localcmd("\nsv_hook_gamerestart\n");
482 // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
483 // Otherwise scores could be manipulated during the countdown.
484 if (!sv_ready_restart_after_countdown) Score_ClearAll();
485 ReadyRestart_force();
488 // Count the players who are ready and determine whether or not to restart the match
491 float ready_needed_factor, ready_needed_count;
492 float t_ready = 0, t_players = 0;
494 FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), {
496 if (it.ready) ++t_ready;
499 readycount = t_ready;
501 Nagger_ReadyCounted();
503 ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
504 ready_needed_count = floor(t_players * ready_needed_factor) + 1;
506 if (readycount >= ready_needed_count) ReadyRestart();
510 // ======================================
511 // Supporting functions for VoteCommand
512 // ======================================
514 float Votecommand_check_assignment(entity caller, float assignment)
516 float from_server = (!caller);
518 if ((assignment == VC_ASGNMNT_BOTH)
519 || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
520 || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
525 string VoteCommand_extractcommand(string input, float startpos, int argc)
529 if ((argc - 1) < startpos) output = "";
530 else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
535 float VoteCommand_checknasty(string vote_command)
537 if ((strstrofs(vote_command, ";", 0) >= 0)
538 || (strstrofs(vote_command, "\n", 0) >= 0)
539 || (strstrofs(vote_command, "\r", 0) >= 0)
540 || (strstrofs(vote_command, "$", 0) >= 0)) return false;
545 float VoteCommand_checkinlist(string vote_command, string list)
547 string l = strcat(" ", list, " ");
549 if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true;
554 string ValidateMap(string validated_map, entity caller)
556 validated_map = MapInfo_FixName(validated_map);
560 print_to(caller, "This map is not available on this server.");
564 if (!autocvar_sv_vote_override_mostrecent && caller)
566 if (Map_IsRecent(validated_map))
568 print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
573 if (!MapInfo_CheckMap(validated_map))
575 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode."));
579 return validated_map;
582 float VoteCommand_checkargs(float startpos, int argc)
584 float p, q, check, minargs;
585 string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
586 string cmdrestriction = ""; // No we don't.
587 string charlist, arg;
590 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
591 cmdrestriction = cvar_string(cvarname);
593 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
595 if (cmdrestriction == "") return true;
597 ++startpos; // skip command name
599 // check minimum arg count
601 // 0 args: argc == startpos
602 // 1 args: argc == startpos + 1
605 minargs = stof(cmdrestriction);
606 if (argc - startpos < minargs) return false;
608 p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
612 // we know that at any time, startpos <= argc - minargs
613 // so this means: argc-minargs >= startpos >= argc, thus
614 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
615 // have been seen already
617 if (startpos >= argc) // all args checked? GOOD
620 if (p < 0) // no more args? FAIL
622 // exception: exactly minargs left, this one included
623 if (argc - startpos == minargs) break;
629 // cut to next semicolon
630 q = strstrofs(cmdrestriction, ";", p + 1); // find next semicolon
631 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
632 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
634 // in case we ever want to allow semicolons in VoteCommand_checknasty
635 // charlist = strreplace("^^", ";", charlist);
639 // verify the arg only contains allowed chars
640 arg = argv(startpos);
641 checkmate = strlen(arg);
642 for (check = 0; check < checkmate; ++check)
643 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
644 // not allowed character
645 // all characters are allowed. FINE.
656 int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
658 string first_command = argv(startpos);
659 int missing_chars = argv_start_index(startpos);
661 if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
664 if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
666 if (!VoteCommand_checkargs(startpos, argc)) return 0;
668 switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
670 case MUT_VOTEPARSE_CONTINUE: { break; }
671 case MUT_VOTEPARSE_SUCCESS: { return 1; }
672 case MUT_VOTEPARSE_INVALID: { return -1; }
673 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
676 switch (first_command) // now go through and parse the proper commands to adjust as needed.
679 case "kickban": // catch all kick/kickban commands
681 entity victim = GetIndexedEntity(argc, (startpos + 1));
682 float accepted = VerifyClientEntity(victim, true, false);
686 string reason = "No reason provided";
687 if(argc > next_token)
688 reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
690 string command_arguments = reason;
691 if (first_command == "kickban")
692 command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
694 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
695 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
697 else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
704 case "gotomap": // re-direct all map selection commands to gotomap
706 vote_command = ValidateMap(argv(startpos + 1), caller);
707 if (!vote_command) return -1;
708 vote_parsed_command = strcat("gotomap ", vote_command);
709 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
714 case "nextmap": // TODO: replicate the old behaviour of being able to vote for maps from different modes on multimode servers (possibly support it in gotomap too), maybe fallback instead of aborting if map name is invalid?
716 vote_command = ValidateMap(argv(startpos + 1), caller);
717 if (!vote_command) return -1;
718 vote_parsed_command = strcat("nextmap ", vote_command);
719 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
726 // add a delay so that vote result can be seen and announcer can be heard
727 // if the vote is accepted
728 vote_parsed_command = strcat("defer 1 ", vote_command);
729 vote_parsed_display = strzone(strcat("^1", vote_command));
736 vote_parsed_command = vote_command;
737 vote_parsed_display = strzone(strcat("^1", vote_command));
747 // =======================
748 // Command Sub-Functions
749 // =======================
751 void VoteCommand_abstain(int request, entity caller) // CLIENT ONLY
755 case CMD_REQUEST_COMMAND:
757 if (!vote_called) { print_to(caller, "^1No vote called."); }
758 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
760 print_to(caller, "^1You have already voted.");
763 else // everything went okay, continue changing vote
765 print_to(caller, "^1You abstained from your vote.");
766 caller.vote_selection = VOTE_SELECT_ABSTAIN;
768 if (!autocvar_sv_vote_singlecount) VoteCount(false); }
774 case CMD_REQUEST_USAGE:
776 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
777 print_to(caller, " No arguments required.");
783 void VoteCommand_call(int request, entity caller, int argc, string vote_command) // BOTH
787 case CMD_REQUEST_COMMAND:
789 float tmp_playercount = 0;
792 vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
794 if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
795 else if (!autocvar_sv_vote_gamestart && time < game_starttime)
797 print_to(caller, "^1Vote calling is not allowed before the match has started.");
799 else if (vote_called)
801 print_to(caller, "^1There is already a vote called.");
803 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
805 print_to(caller, "^1Only players can call a vote.");
807 else if (caller && !IS_CLIENT(caller))
809 print_to(caller, "^1Only connected clients can vote.");
811 else if (timeout_status)
813 print_to(caller, "^1You can not call a vote while a timeout is active.");
815 else if (caller && (time < caller.vote_waittime))
817 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
819 else if (!VoteCommand_checknasty(vote_command))
821 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
823 else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
826 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
828 else // everything went okay, continue with calling the vote
830 vote_caller = caller; // remember who called the vote
831 vote_caller_name = strzone(GetCallerName(vote_caller));
832 vote_called = VOTE_NORMAL;
833 vote_called_command = strzone(vote_parsed_command);
834 vote_called_display = strzone(vote_parsed_display);
835 vote_endtime = time + autocvar_sv_vote_timeout;
839 caller.vote_selection = VOTE_SELECT_ACCEPT;
840 caller.vote_waittime = time + autocvar_sv_vote_wait;
844 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
846 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
847 if (autocvar_sv_eventlog)
848 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
849 Nagger_VoteChanged();
850 VoteCount(true); // needed if you are the only one
852 if (tmp_playercount > 1 && vote_called != VOTE_NULL)
853 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
860 case CMD_REQUEST_USAGE:
862 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
863 print_to(caller, " Where 'command' is the command to request a vote upon.");
864 print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
865 print_to(caller, strcat(" ", GetCommandPrefix(caller), " vote call endmatch"));
871 void VoteCommand_master(int request, entity caller, int argc, string vote_command) // CLIENT ONLY
875 case CMD_REQUEST_COMMAND:
877 if (autocvar_sv_vote_master)
879 switch (strtolower(argv(2)))
884 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
886 if (!caller.vote_master)
887 print_to(caller, "^1You do not have vote master privileges.");
888 else if (!VoteCommand_checknasty(vote_command))
890 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
892 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
895 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
897 else // everything went okay, proceed with command
899 localcmd(strcat(vote_parsed_command, "\n"));
900 print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
901 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
902 if (autocvar_sv_eventlog)
903 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
911 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
912 else if (caller.vote_master)
914 print_to(caller, "^1You are already logged in as vote master.");
916 else if (autocvar_sv_vote_master_password != argv(3))
918 print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
920 else // everything went okay, proceed with giving this player master privilages
922 caller.vote_master = true;
923 print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
924 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
925 if (autocvar_sv_eventlog)
926 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
932 default: // calling a vote for master
934 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
935 else if (vote_called)
937 print_to(caller, "^1There is already a vote called.");
939 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
941 print_to(caller, "^1Only players can call a vote.");
943 else if (timeout_status)
945 print_to(caller, "^1You can not call a vote while a timeout is active.");
947 else // everything went okay, continue with creating vote
949 vote_caller = caller;
950 vote_caller_name = strzone(GetCallerName(vote_caller));
951 vote_called = VOTE_MASTER;
952 vote_called_command = strzone("XXX");
953 vote_called_display = strzone("^3master");
954 vote_endtime = time + autocvar_sv_vote_timeout;
956 caller.vote_selection = VOTE_SELECT_ACCEPT;
957 caller.vote_waittime = time + autocvar_sv_vote_wait;
959 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
960 if (autocvar_sv_eventlog)
961 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
962 Nagger_VoteChanged();
963 VoteCount(true); // needed if you are the only one
970 else { print_to(caller, "^1Master control of voting is not allowed."); }
976 case CMD_REQUEST_USAGE:
978 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
979 print_to(caller, " If action is left blank, it calls a vote for you to become master.");
980 print_to(caller, " Otherwise the actions are either 'do' a command or 'login' as master.");
986 void VoteCommand_no(int request, entity caller) // CLIENT ONLY
990 case CMD_REQUEST_COMMAND:
992 if (!vote_called) { print_to(caller, "^1No vote called."); }
993 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
995 print_to(caller, "^1You have already voted.");
997 else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
1002 else // everything went okay, continue changing vote
1004 print_to(caller, "^1You rejected the vote.");
1005 caller.vote_selection = VOTE_SELECT_REJECT;
1006 msg_entity = caller;
1007 if (!autocvar_sv_vote_singlecount)
1015 case CMD_REQUEST_USAGE:
1017 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1018 print_to(caller, " No arguments required.");
1024 void VoteCommand_status(int request, entity caller) // BOTH
1028 case CMD_REQUEST_COMMAND:
1030 if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1031 else print_to(caller, "^1No vote called.");
1037 case CMD_REQUEST_USAGE:
1039 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1040 print_to(caller, " No arguments required.");
1046 void VoteCommand_stop(int request, entity caller) // BOTH
1050 case CMD_REQUEST_COMMAND:
1052 if (!vote_called) print_to(caller, "^1No vote called.");
1053 else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller);
1054 else print_to(caller, "^1You are not allowed to stop that vote.");
1059 case CMD_REQUEST_USAGE:
1061 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1062 print_to(caller, " No arguments required.");
1068 void VoteCommand_yes(int request, entity caller) // CLIENT ONLY
1072 case CMD_REQUEST_COMMAND:
1074 if (!vote_called) { print_to(caller, "^1No vote called."); }
1075 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1077 print_to(caller, "^1You have already voted.");
1079 else // everything went okay, continue changing vote
1081 print_to(caller, "^1You accepted the vote.");
1082 caller.vote_selection = VOTE_SELECT_ACCEPT;
1083 msg_entity = caller;
1084 if (!autocvar_sv_vote_singlecount)
1092 case CMD_REQUEST_USAGE:
1094 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1095 print_to(caller, " No arguments required.");
1101 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1102 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1103 void VoteCommand_(int request)
1107 case CMD_REQUEST_COMMAND:
1114 case CMD_REQUEST_USAGE:
1116 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1117 print_to(caller, " No arguments required.");
1125 // ================================
1126 // Macro system for vote commands
1127 // ================================
1129 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1130 #define VOTE_COMMANDS(request, caller, arguments, command) \
1131 VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1132 VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1133 VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1134 VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1135 VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1136 VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1137 VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1138 VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1141 void VoteCommand_macro_help(entity caller, int argc)
1143 string command_origin = GetCommandPrefix(caller);
1145 if (argc == 2 || argv(2) == "help") // help display listing all commands
1147 print_to(caller, "\nVoting commands:\n");
1148 #define VOTE_COMMAND(name, function, description, assignment) \
1149 { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } }
1151 VOTE_COMMANDS(0, caller, 0, "");
1154 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1155 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1156 print_to(caller, strcat("\n^7You can call a vote for or execute these commands: ^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7"));
1158 else // usage for individual command
1160 #define VOTE_COMMAND(name, function, description, assignment) \
1161 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1163 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1166 string cvarname = strcat("sv_vote_command_help_", argv(2));
1167 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1168 wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1170 print_to(caller, "No documentation exists for this vote");
1174 float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1176 #define VOTE_COMMAND(name, function, description, assignment) \
1177 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1179 VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1186 // ======================================
1187 // Main function handling vote commands
1188 // ======================================
1190 void VoteCommand(int request, entity caller, int argc, string vote_command)
1192 // Guide for working with argc arguments by example:
1193 // argc: 1 - 2 - 3 - 4
1194 // argv: 0 - 1 - 2 - 3
1195 // cmd vote - master - login - password
1199 case CMD_REQUEST_COMMAND:
1201 if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1205 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"));
1206 case CMD_REQUEST_USAGE:
1208 VoteCommand_macro_help(caller, argc);