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"
14 #include "../round_handler.qh"
15 #include "../scores.qh"
17 #include "../mutators/_mod.qh"
19 #include <common/constants.qh>
20 #include <common/net_linked.qh>
21 #include <common/mapinfo.qh>
22 #include <common/notifications/all.qh>
23 #include <common/playerstats.qh>
24 #include <common/util.qh>
26 // =============================================
27 // Server side voting code, reworked by Samual
28 // Last updated: December 27th, 2011
29 // =============================================
31 // Nagger for players to know status of voting
32 bool Nagger_SendEntity(entity this, entity to, float sendflags)
36 WriteHeader(MSG_ENTITY, ENT_CLIENT_NAGGER);
40 // 2 = player needs to ready up
42 // 8 = player needs to vote
52 if (to.ready == 0) nags |= BIT(1);
57 if (to.vote_selection == 0) nags |= BIT(3);
59 if (warmup_stage) nags |= BIT(4);
61 if (sendflags & BIT(6)) nags |= BIT(6);
63 if (sendflags & BIT(7)) nags |= BIT(7);
65 if (!(nags & 4)) // no vote called? send no string
66 nags &= ~(BIT(6) | BIT(7));
68 WriteByte(MSG_ENTITY, nags);
72 WriteByte(MSG_ENTITY, vote_accept_count);
73 WriteByte(MSG_ENTITY, vote_reject_count);
74 WriteByte(MSG_ENTITY, vote_needed_overall);
75 WriteChar(MSG_ENTITY, to.vote_selection);
78 if (nags & BIT(7)) WriteString(MSG_ENTITY, vote_called_display);
82 for (i = 1; i <= maxclients; i += 8)
84 for (f = 0, e = edict_num(i), b = BIT(0); b < BIT(8); b <<= 1, e = nextent(e))
85 if (!IS_REAL_CLIENT(e) || e.ready)
87 WriteByte(MSG_ENTITY, f);
96 Net_LinkEntity(nagger = new_pure(nagger), false, 0, Nagger_SendEntity);
99 void Nagger_VoteChanged()
101 if (nagger) nagger.SendFlags |= BIT(7);
104 void Nagger_VoteCountChanged()
106 if (nagger) nagger.SendFlags |= BIT(6);
109 void Nagger_ReadyCounted()
111 if (nagger) nagger.SendFlags |= BIT(0);
114 // If the vote_caller is still here, return their name, otherwise vote_caller_name
115 string OriginalCallerName()
117 if (IS_REAL_CLIENT(vote_caller)) return playername(vote_caller, false);
118 return vote_caller_name;
121 // =======================
122 // Game logic for voting
123 // =======================
127 FOREACH_CLIENT(true, { it.vote_selection = 0; });
131 strunzone(vote_called_command);
132 strunzone(vote_called_display);
133 strunzone(vote_caller_name);
136 vote_called = VOTE_NULL;
138 vote_caller_name = string_null;
141 vote_called_command = string_null;
142 vote_called_display = string_null;
144 vote_parsed_command = string_null;
145 vote_parsed_display = string_null;
147 Nagger_VoteChanged();
150 void VoteStop(entity stopper)
152 bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n");
153 if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));
154 // Don't force them to wait for next vote, this way they can e.g. correct their vote.
155 if ((vote_caller) && (stopper == vote_caller)) vote_caller.vote_waittime = time + autocvar_sv_vote_stop;
161 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ^1", vote_called_display, "^2 was accepted\n");
163 if ((vote_called == VOTE_MASTER) && vote_caller) vote_caller.vote_master = 1;
164 else localcmd(strcat(vote_called_command, "\n"));
166 if (vote_caller) vote_caller.vote_waittime = 0; // people like your votes, you don't need to wait to vote again
169 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_ACCEPT);
174 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 was rejected\n");
176 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
181 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 timed out\n");
183 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
186 void VoteSpam(float notvoters, float mincount, string result)
189 strcat("\{1}^2* vote results: ^1", ftos(vote_accept_count)),
190 strcat("^2:^1", ftos(vote_reject_count)),
191 ((mincount >= 0) ? strcat("^2 (^1", ftos(mincount), "^2 needed)") : "^2"),
192 strcat(", ^1", ftos(vote_abstain_count), "^2 didn't care"),
193 strcat(", ^1", ftos(notvoters), strcat("^2 didn't ", ((mincount >= 0) ? "" : "have to "), "vote\n"))));
195 if (autocvar_sv_eventlog)
198 strcat(":vote:v", result, ":", ftos(vote_accept_count)),
199 strcat(":", ftos(vote_reject_count)),
200 strcat(":", ftos(vote_abstain_count)),
201 strcat(":", ftos(notvoters)),
202 strcat(":", ftos(mincount))));
206 #define spectators_allowed (!autocvar_sv_vote_nospectators || (autocvar_sv_vote_nospectators == 1 && (warmup_stage || intermission_running)))
208 void VoteCount(float first_count)
211 vote_accept_count = vote_reject_count = vote_abstain_count = 0;
213 float vote_player_count = 0, notvoters = 0;
214 float vote_real_player_count = 0, vote_real_accept_count = 0;
215 float vote_real_reject_count = 0, vote_real_abstain_count = 0;
216 float vote_needed_of_voted, final_needed_votes;
217 float vote_factor_overall, vote_factor_of_voted;
219 Nagger_VoteCountChanged();
221 // add up all the votes from each connected client
222 FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_CLIENT(it), {
224 if (IS_PLAYER(it)) ++vote_real_player_count;
225 switch (it.vote_selection)
227 case VOTE_SELECT_REJECT:
228 { ++vote_reject_count;
229 { if (IS_PLAYER(it)) ++vote_real_reject_count; } break;
231 case VOTE_SELECT_ACCEPT:
232 { ++vote_accept_count;
233 { if (IS_PLAYER(it)) ++vote_real_accept_count; } break;
235 case VOTE_SELECT_ABSTAIN:
236 { ++vote_abstain_count;
237 { if (IS_PLAYER(it)) ++vote_real_abstain_count; } break;
243 // Check to see if there are enough players on the server to allow master voting... otherwise, vote master could be used for evil.
244 if ((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count)
246 if (vote_caller) vote_caller.vote_waittime = 0;
247 print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master.");
252 // 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.
253 if (!spectators_allowed && (vote_real_player_count > 0))
255 vote_accept_count = vote_real_accept_count;
256 vote_reject_count = vote_real_reject_count;
257 vote_abstain_count = vote_real_abstain_count;
258 vote_player_count = vote_real_player_count;
261 // people who have no opinion in any way :D
262 notvoters = (vote_player_count - vote_accept_count - vote_reject_count - vote_abstain_count);
264 // determine the goal for the vote to be passed or rejected normally
265 vote_factor_overall = bound(0.5, autocvar_sv_vote_majority_factor, 0.999);
266 vote_needed_overall = floor((vote_player_count - vote_abstain_count) * vote_factor_overall) + 1;
268 // if the vote times out, determine the amount of votes needed of the people who actually already voted
269 vote_factor_of_voted = bound(0.5, autocvar_sv_vote_majority_factor_of_voted, 0.999);
270 vote_needed_of_voted = floor((vote_accept_count + vote_reject_count) * vote_factor_of_voted) + 1;
272 // are there any players at all on the server? it could be an admin vote
273 if (vote_player_count == 0 && first_count)
275 VoteSpam(0, -1, "yes"); // no players at all, just accept it
280 // since there ARE players, finally calculate the result of the vote
281 if (vote_accept_count >= vote_needed_overall)
283 VoteSpam(notvoters, -1, "yes"); // there is enough acceptions to pass the vote
288 if (vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall)
290 VoteSpam(notvoters, -1, "no"); // there is enough rejections to deny the vote
295 // there is not enough votes in either direction, now lets just calculate what the voters have said
296 if (time > vote_endtime)
298 final_needed_votes = vote_needed_overall;
300 if (autocvar_sv_vote_majority_factor_of_voted)
302 if (vote_accept_count >= vote_needed_of_voted)
304 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "yes");
309 if (vote_accept_count + vote_reject_count > 0)
311 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "no");
316 final_needed_votes = min(vote_needed_overall, vote_needed_of_voted);
319 // it didn't pass or fail, so not enough votes to even make a decision.
320 VoteSpam(notvoters, final_needed_votes, "timeout");
327 if (vote_endtime > 0) // a vote was called
329 if (time > vote_endtime) // time is up
335 // =======================
336 // Game logic for warmup
337 // =======================
339 // Resets the state of all clients, items, weapons, waypoints, ... of the map.
340 void reset_map(bool dorespawn)
342 if (time <= game_starttime)
346 if (round_handler_IsActive())
347 round_handler_Reset(game_starttime);
350 MUTATOR_CALLHOOK(reset_map_global);
352 FOREACH_ENTITY_FLOAT_ORDERED(pure_data, false,
361 if (it.team_saved) it.team = it.team_saved;
362 if (it.flags & FL_PROJECTILE) delete(it); // remove any projectiles left
365 // Waypoints and assault start come LAST
366 FOREACH_ENTITY_ORDERED(IS_NOT_A_CLIENT(it), {
367 if (it.reset2) it.reset2(it);
370 FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it); });
372 // Moving the player reset code here since the player-reset depends
373 // on spawnpoint entities which have to be reset first --blub
376 if (!MUTATOR_CALLHOOK(reset_map_players))
378 if (restart_mapalreadyrestarted || (time < game_starttime))
380 FOREACH_CLIENT(IS_PLAYER(it),
383 only reset players if a restart countdown is active
384 this can either be due to cvar sv_ready_restart_after_countdown having set
385 restart_mapalreadyrestarted to 1 after the countdown ended or when
386 sv_ready_restart_after_countdown is not used and countdown is still running
388 // NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
389 // PlayerScore_Clear(it);
390 CS(it).killcount = 0;
391 // stop the player from moving so that he stands still once he gets respawned
392 it.velocity = '0 0 0';
393 it.avelocity = '0 0 0';
394 CS(it).movement = '0 0 0';
395 PutClientInServer(it);
402 // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
403 void ReadyRestart_think(entity this)
405 restart_mapalreadyrestarted = true;
411 // Forces a restart of the game without actually reloading the map // this is a mess...
412 void ReadyRestart_force()
414 if (time <= game_starttime && game_stopped)
417 bprint("^1Server is restarting...\n");
421 // clear overtime, we have to decrease timelimit to its original value again.
422 if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
423 cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
424 checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
426 readyrestart_happened = true;
427 game_starttime = time + RESTART_COUNTDOWN;
429 // clear player attributes
430 FOREACH_CLIENT(IS_PLAYER(it), {
432 CS(it).killcount = 0;
433 float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
434 PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
437 restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
439 // disable the warmup global for the server
440 warmup_stage = 0; // once the game is restarted the game is in match stage
442 // reset the .ready status of all players (also spectators)
443 FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
445 Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
447 // lock teams with lockonrestart
448 if (autocvar_teamplay_lockonrestart && teamplay)
451 bprint("^1The teams are now locked.\n");
454 // initiate the restart-countdown-announcer entity
455 if (sv_ready_restart_after_countdown)
457 entity restart_timer = new_pure(restart_timer);
458 setthink(restart_timer, ReadyRestart_think);
459 restart_timer.nextthink = game_starttime;
462 // after a restart every players number of allowed timeouts gets reset, too
463 if (autocvar_sv_timeout)
465 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
468 if (!sv_ready_restart_after_countdown) reset_map(true);
469 if (autocvar_sv_eventlog) GameLogEcho(":restart");
474 if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || game_stopped || race_completing) localcmd("restart\n");
475 else localcmd("\nsv_hook_gamerestart\n");
477 // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
478 // Otherwise scores could be manipulated during the countdown.
479 if (!sv_ready_restart_after_countdown) Score_ClearAll();
480 ReadyRestart_force();
483 // Count the players who are ready and determine whether or not to restart the match
486 float ready_needed_factor, ready_needed_count;
487 float t_ready = 0, t_players = 0;
489 FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), {
491 if (it.ready) ++t_ready;
494 readycount = t_ready;
496 Nagger_ReadyCounted();
498 ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
499 ready_needed_count = floor(t_players * ready_needed_factor) + 1;
501 if (readycount >= ready_needed_count) ReadyRestart();
505 // ======================================
506 // Supporting functions for VoteCommand
507 // ======================================
509 float Votecommand_check_assignment(entity caller, float assignment)
511 float from_server = (!caller);
513 if ((assignment == VC_ASGNMNT_BOTH)
514 || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
515 || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
520 string VoteCommand_extractcommand(string input, float startpos, float argc)
524 if ((argc - 1) < startpos) output = "";
525 else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
530 float VoteCommand_checknasty(string vote_command)
532 if ((strstrofs(vote_command, ";", 0) >= 0)
533 || (strstrofs(vote_command, "\n", 0) >= 0)
534 || (strstrofs(vote_command, "\r", 0) >= 0)
535 || (strstrofs(vote_command, "$", 0) >= 0)) return false;
540 float VoteCommand_checkinlist(string vote_command, string list)
542 string l = strcat(" ", list, " ");
544 if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true;
549 string ValidateMap(string validated_map, entity caller)
551 validated_map = MapInfo_FixName(validated_map);
555 print_to(caller, "This map is not available on this server.");
559 if (!autocvar_sv_vote_override_mostrecent && caller)
561 if (Map_IsRecent(validated_map))
563 print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
568 if (!MapInfo_CheckMap(validated_map))
570 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode."));
574 return validated_map;
577 float VoteCommand_checkargs(float startpos, float argc)
579 float p, q, check, minargs;
580 string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
581 string cmdrestriction = ""; // No we don't.
582 string charlist, arg;
585 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
586 cmdrestriction = cvar_string(cvarname);
588 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
590 if (cmdrestriction == "") return true;
592 ++startpos; // skip command name
594 // check minimum arg count
596 // 0 args: argc == startpos
597 // 1 args: argc == startpos + 1
600 minargs = stof(cmdrestriction);
601 if (argc - startpos < minargs) return false;
603 p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
607 // we know that at any time, startpos <= argc - minargs
608 // so this means: argc-minargs >= startpos >= argc, thus
609 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
610 // have been seen already
612 if (startpos >= argc) // all args checked? GOOD
615 if (p < 0) // no more args? FAIL
617 // exception: exactly minargs left, this one included
618 if (argc - startpos == minargs) break;
624 // cut to next semicolon
625 q = strstrofs(cmdrestriction, ";", p + 1); // find next semicolon
626 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
627 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
629 // in case we ever want to allow semicolons in VoteCommand_checknasty
630 // charlist = strreplace("^^", ";", charlist);
634 // verify the arg only contains allowed chars
635 arg = argv(startpos);
636 checkmate = strlen(arg);
637 for (check = 0; check < checkmate; ++check)
638 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
639 // not allowed character
640 // all characters are allowed. FINE.
651 int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc)
653 string first_command = argv(startpos);
654 int missing_chars = argv_start_index(startpos);
656 if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
659 if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
661 if (!VoteCommand_checkargs(startpos, argc)) return 0;
663 switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
665 case MUT_VOTEPARSE_CONTINUE: { break; }
666 case MUT_VOTEPARSE_SUCCESS: { return 1; }
667 case MUT_VOTEPARSE_INVALID: { return -1; }
668 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
671 switch (first_command) // now go through and parse the proper commands to adjust as needed.
674 case "kickban": // catch all kick/kickban commands
676 entity victim = GetIndexedEntity(argc, (startpos + 1));
677 float accepted = VerifyClientEntity(victim, true, false);
681 string reason = "No reason provided";
682 if(argc > next_token)
683 reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
685 string command_arguments = reason;
686 if (first_command == "kickban")
687 command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
689 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
690 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
692 else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
699 case "gotomap": // re-direct all map selection commands to gotomap
701 vote_command = ValidateMap(argv(startpos + 1), caller);
702 if (!vote_command) return -1;
703 vote_parsed_command = strcat("gotomap ", vote_command);
704 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
709 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?
711 vote_command = ValidateMap(argv(startpos + 1), caller);
712 if (!vote_command) return -1;
713 vote_parsed_command = strcat("nextmap ", vote_command);
714 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
721 vote_parsed_command = vote_command;
722 vote_parsed_display = strzone(strcat("^1", vote_command));
732 // =======================
733 // Command Sub-Functions
734 // =======================
736 void VoteCommand_abstain(float request, entity caller) // CLIENT ONLY
740 case CMD_REQUEST_COMMAND:
742 if (!vote_called) { print_to(caller, "^1No vote called."); }
743 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
745 print_to(caller, "^1You have already voted.");
748 else // everything went okay, continue changing vote
750 print_to(caller, "^1You abstained from your vote.");
751 caller.vote_selection = VOTE_SELECT_ABSTAIN;
753 if (!autocvar_sv_vote_singlecount) VoteCount(false); }
759 case CMD_REQUEST_USAGE:
761 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
762 print_to(caller, " No arguments required.");
768 void VoteCommand_call(float request, entity caller, float argc, string vote_command) // BOTH
772 case CMD_REQUEST_COMMAND:
774 float tmp_playercount = 0;
777 vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
779 if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
780 else if (!autocvar_sv_vote_gamestart && time < game_starttime)
782 print_to(caller, "^1Vote calling is not allowed before the match has started.");
784 else if (vote_called)
786 print_to(caller, "^1There is already a vote called.");
788 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
790 print_to(caller, "^1Only players can call a vote.");
792 else if (caller && !IS_CLIENT(caller))
794 print_to(caller, "^1Only connected clients can vote.");
796 else if (timeout_status)
798 print_to(caller, "^1You can not call a vote while a timeout is active.");
800 else if (caller && (time < caller.vote_waittime))
802 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
804 else if (!VoteCommand_checknasty(vote_command))
806 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
808 else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
811 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
813 else // everything went okay, continue with calling the vote
815 vote_caller = caller; // remember who called the vote
816 vote_caller_name = strzone(GetCallerName(vote_caller));
817 vote_called = VOTE_NORMAL;
818 vote_called_command = strzone(vote_parsed_command);
819 vote_called_display = strzone(vote_parsed_display);
820 vote_endtime = time + autocvar_sv_vote_timeout;
824 caller.vote_selection = VOTE_SELECT_ACCEPT;
825 caller.vote_waittime = time + autocvar_sv_vote_wait;
829 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
830 if (tmp_playercount > 1)
831 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
833 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
834 if (autocvar_sv_eventlog)
835 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
836 Nagger_VoteChanged();
837 VoteCount(true); // needed if you are the only one
844 case CMD_REQUEST_USAGE:
846 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
847 print_to(caller, " Where 'command' is the command to request a vote upon.");
848 print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
849 print_to(caller, strcat(" ", GetCommandPrefix(caller), " vote call endmatch"));
855 void VoteCommand_master(float request, entity caller, float argc, string vote_command) // CLIENT ONLY
859 case CMD_REQUEST_COMMAND:
861 if (autocvar_sv_vote_master)
863 switch (strtolower(argv(2)))
868 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
870 if (!caller.vote_master)
871 print_to(caller, "^1You do not have vote master privileges.");
872 else if (!VoteCommand_checknasty(vote_command))
874 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
876 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
879 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
881 else // everything went okay, proceed with command
883 localcmd(strcat(vote_parsed_command, "\n"));
884 print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
885 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
886 if (autocvar_sv_eventlog)
887 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
895 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
896 else if (caller.vote_master)
898 print_to(caller, "^1You are already logged in as vote master.");
900 else if (autocvar_sv_vote_master_password != argv(3))
902 print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
904 else // everything went okay, proceed with giving this player master privilages
906 caller.vote_master = true;
907 print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
908 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
909 if (autocvar_sv_eventlog)
910 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
916 default: // calling a vote for master
918 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
919 else if (vote_called)
921 print_to(caller, "^1There is already a vote called.");
923 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
925 print_to(caller, "^1Only players can call a vote.");
927 else if (timeout_status)
929 print_to(caller, "^1You can not call a vote while a timeout is active.");
931 else // everything went okay, continue with creating vote
933 vote_caller = caller;
934 vote_caller_name = strzone(GetCallerName(vote_caller));
935 vote_called = VOTE_MASTER;
936 vote_called_command = strzone("XXX");
937 vote_called_display = strzone("^3master");
938 vote_endtime = time + autocvar_sv_vote_timeout;
940 caller.vote_selection = VOTE_SELECT_ACCEPT;
941 caller.vote_waittime = time + autocvar_sv_vote_wait;
943 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
944 if (autocvar_sv_eventlog)
945 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
946 Nagger_VoteChanged();
947 VoteCount(true); // needed if you are the only one
954 else { print_to(caller, "^1Master control of voting is not allowed."); }
960 case CMD_REQUEST_USAGE:
962 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
963 print_to(caller, " If action is left blank, it calls a vote for you to become master.");
964 print_to(caller, " Otherwise the actions are either 'do' a command or 'login' as master.");
970 void VoteCommand_no(float request, entity caller) // CLIENT ONLY
974 case CMD_REQUEST_COMMAND:
976 if (!vote_called) { print_to(caller, "^1No vote called."); }
977 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
979 print_to(caller, "^1You have already voted.");
981 else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
986 else // everything went okay, continue changing vote
988 print_to(caller, "^1You rejected the vote.");
989 caller.vote_selection = VOTE_SELECT_REJECT;
991 if (!autocvar_sv_vote_singlecount)
999 case CMD_REQUEST_USAGE:
1001 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1002 print_to(caller, " No arguments required.");
1008 void VoteCommand_status(float request, entity caller) // BOTH
1012 case CMD_REQUEST_COMMAND:
1014 if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1015 else print_to(caller, "^1No vote called.");
1021 case CMD_REQUEST_USAGE:
1023 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1024 print_to(caller, " No arguments required.");
1030 void VoteCommand_stop(float request, entity caller) // BOTH
1034 case CMD_REQUEST_COMMAND:
1036 if (!vote_called) print_to(caller, "^1No vote called.");
1037 else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller);
1038 else print_to(caller, "^1You are not allowed to stop that vote.");
1043 case CMD_REQUEST_USAGE:
1045 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1046 print_to(caller, " No arguments required.");
1052 void VoteCommand_yes(float request, entity caller) // CLIENT ONLY
1056 case CMD_REQUEST_COMMAND:
1058 if (!vote_called) { print_to(caller, "^1No vote called."); }
1059 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1061 print_to(caller, "^1You have already voted.");
1063 else // everything went okay, continue changing vote
1065 print_to(caller, "^1You accepted the vote.");
1066 caller.vote_selection = VOTE_SELECT_ACCEPT;
1067 msg_entity = caller;
1068 if (!autocvar_sv_vote_singlecount)
1076 case CMD_REQUEST_USAGE:
1078 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1079 print_to(caller, " No arguments required.");
1085 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1086 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1087 void VoteCommand_(float request)
1091 case CMD_REQUEST_COMMAND:
1098 case CMD_REQUEST_USAGE:
1100 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1101 print_to(caller, " No arguments required.");
1109 // ================================
1110 // Macro system for vote commands
1111 // ================================
1113 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1114 #define VOTE_COMMANDS(request, caller, arguments, command) \
1115 VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1116 VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1117 VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1118 VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1119 VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1120 VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1121 VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1122 VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1125 void VoteCommand_macro_help(entity caller, float argc)
1127 string command_origin = GetCommandPrefix(caller);
1129 if (argc == 2 || argv(2) == "help") // help display listing all commands
1131 print_to(caller, "\nVoting commands:\n");
1132 #define VOTE_COMMAND(name, function, description, assignment) \
1133 { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } }
1135 VOTE_COMMANDS(0, caller, 0, "");
1138 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1139 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1140 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"));
1142 else // usage for individual command
1144 #define VOTE_COMMAND(name, function, description, assignment) \
1145 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1147 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1150 string cvarname = strcat("sv_vote_command_help_", argv(2));
1151 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1152 wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1154 print_to(caller, "No documentation exists for this vote");
1158 float VoteCommand_macro_command(entity caller, float argc, string vote_command)
1160 #define VOTE_COMMAND(name, function, description, assignment) \
1161 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1163 VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1170 // ======================================
1171 // Main function handling vote commands
1172 // ======================================
1174 void VoteCommand(float request, entity caller, float argc, string vote_command)
1176 // Guide for working with argc arguments by example:
1177 // argc: 1 - 2 - 3 - 4
1178 // argv: 0 - 1 - 2 - 3
1179 // cmd vote - master - login - password
1183 case CMD_REQUEST_COMMAND:
1185 if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1189 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"));
1190 case CMD_REQUEST_USAGE:
1192 VoteCommand_macro_help(caller, argc);