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 if (vote_called_command == "restart")
164 // add a delay so that vote result can be seen and announcer can be heard
165 localcmd(strcat("defer 1 ", vote_called_command, "\n"));
167 else localcmd(strcat(vote_called_command, "\n"));
169 if (vote_caller) vote_caller.vote_waittime = 0; // people like your votes, you don't need to wait to vote again
172 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_ACCEPT);
177 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 was rejected\n");
179 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
184 bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 timed out\n");
186 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
189 void VoteSpam(float notvoters, float mincount, string result)
192 strcat("\{1}^2* vote results: ^1", ftos(vote_accept_count)),
193 strcat("^2:^1", ftos(vote_reject_count)),
194 ((mincount >= 0) ? strcat("^2 (^1", ftos(mincount), "^2 needed)") : "^2"),
195 strcat(", ^1", ftos(vote_abstain_count), "^2 didn't care"),
196 strcat(", ^1", ftos(notvoters), strcat("^2 didn't ", ((mincount >= 0) ? "" : "have to "), "vote\n"))));
198 if (autocvar_sv_eventlog)
201 strcat(":vote:v", result, ":", ftos(vote_accept_count)),
202 strcat(":", ftos(vote_reject_count)),
203 strcat(":", ftos(vote_abstain_count)),
204 strcat(":", ftos(notvoters)),
205 strcat(":", ftos(mincount))));
209 #define spectators_allowed (!autocvar_sv_vote_nospectators || (autocvar_sv_vote_nospectators == 1 && (warmup_stage || intermission_running)))
211 void VoteCount(float first_count)
214 vote_accept_count = vote_reject_count = vote_abstain_count = 0;
216 float vote_player_count = 0, notvoters = 0;
217 float vote_real_player_count = 0, vote_real_accept_count = 0;
218 float vote_real_reject_count = 0, vote_real_abstain_count = 0;
219 float vote_needed_of_voted, final_needed_votes;
220 float vote_factor_overall, vote_factor_of_voted;
222 Nagger_VoteCountChanged();
224 // add up all the votes from each connected client
225 FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_CLIENT(it), {
227 if (IS_PLAYER(it)) ++vote_real_player_count;
228 switch (it.vote_selection)
230 case VOTE_SELECT_REJECT:
231 { ++vote_reject_count;
232 { if (IS_PLAYER(it)) ++vote_real_reject_count; } break;
234 case VOTE_SELECT_ACCEPT:
235 { ++vote_accept_count;
236 { if (IS_PLAYER(it)) ++vote_real_accept_count; } break;
238 case VOTE_SELECT_ABSTAIN:
239 { ++vote_abstain_count;
240 { if (IS_PLAYER(it)) ++vote_real_abstain_count; } break;
246 // Check to see if there are enough players on the server to allow master voting... otherwise, vote master could be used for evil.
247 if ((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count)
249 if (vote_caller) vote_caller.vote_waittime = 0;
250 print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master.");
255 // 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.
256 if (!spectators_allowed && (vote_real_player_count > 0))
258 vote_accept_count = vote_real_accept_count;
259 vote_reject_count = vote_real_reject_count;
260 vote_abstain_count = vote_real_abstain_count;
261 vote_player_count = vote_real_player_count;
264 // people who have no opinion in any way :D
265 notvoters = (vote_player_count - vote_accept_count - vote_reject_count - vote_abstain_count);
267 // determine the goal for the vote to be passed or rejected normally
268 vote_factor_overall = bound(0.5, autocvar_sv_vote_majority_factor, 0.999);
269 vote_needed_overall = floor((vote_player_count - vote_abstain_count) * vote_factor_overall) + 1;
271 // if the vote times out, determine the amount of votes needed of the people who actually already voted
272 vote_factor_of_voted = bound(0.5, autocvar_sv_vote_majority_factor_of_voted, 0.999);
273 vote_needed_of_voted = floor((vote_accept_count + vote_reject_count) * vote_factor_of_voted) + 1;
275 // are there any players at all on the server? it could be an admin vote
276 if (vote_player_count == 0 && first_count)
278 VoteSpam(0, -1, "yes"); // no players at all, just accept it
283 // since there ARE players, finally calculate the result of the vote
284 if (vote_accept_count >= vote_needed_overall)
286 VoteSpam(notvoters, -1, "yes"); // there is enough acceptions to pass the vote
291 if (vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall)
293 VoteSpam(notvoters, -1, "no"); // there is enough rejections to deny the vote
298 // there is not enough votes in either direction, now lets just calculate what the voters have said
299 if (time > vote_endtime)
301 final_needed_votes = vote_needed_overall;
303 if (autocvar_sv_vote_majority_factor_of_voted)
305 if (vote_accept_count >= vote_needed_of_voted)
307 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "yes");
312 if (vote_accept_count + vote_reject_count > 0)
314 VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "no");
319 final_needed_votes = min(vote_needed_overall, vote_needed_of_voted);
322 // it didn't pass or fail, so not enough votes to even make a decision.
323 VoteSpam(notvoters, final_needed_votes, "timeout");
330 if (vote_endtime > 0) // a vote was called
332 if (time > vote_endtime) // time is up
338 // =======================
339 // Game logic for warmup
340 // =======================
342 // Resets the state of all clients, items, weapons, waypoints, ... of the map.
343 void reset_map(bool dorespawn)
345 if (time <= game_starttime)
349 if (round_handler_IsActive())
350 round_handler_Reset(game_starttime);
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); });
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
443 warmup_stage = 0; // once the game is restarted the game is in match stage
445 // reset the .ready status of all players (also spectators)
446 FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
448 Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
450 // lock teams with lockonrestart
451 if (autocvar_teamplay_lockonrestart && teamplay)
454 bprint("^1The teams are now locked.\n");
457 // initiate the restart-countdown-announcer entity
458 if (sv_ready_restart_after_countdown)
460 entity restart_timer = new_pure(restart_timer);
461 setthink(restart_timer, ReadyRestart_think);
462 restart_timer.nextthink = game_starttime;
465 // after a restart every players number of allowed timeouts gets reset, too
466 if (autocvar_sv_timeout)
468 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
471 if (!sv_ready_restart_after_countdown) reset_map(true);
472 if (autocvar_sv_eventlog) GameLogEcho(":restart");
477 if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || game_stopped || race_completing) localcmd("restart\n");
478 else localcmd("\nsv_hook_gamerestart\n");
480 // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
481 // Otherwise scores could be manipulated during the countdown.
482 if (!sv_ready_restart_after_countdown) Score_ClearAll();
483 ReadyRestart_force();
486 // Count the players who are ready and determine whether or not to restart the match
489 float ready_needed_factor, ready_needed_count;
490 float t_ready = 0, t_players = 0;
492 FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), {
494 if (it.ready) ++t_ready;
497 readycount = t_ready;
499 Nagger_ReadyCounted();
501 ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
502 ready_needed_count = floor(t_players * ready_needed_factor) + 1;
504 if (readycount >= ready_needed_count) ReadyRestart();
508 // ======================================
509 // Supporting functions for VoteCommand
510 // ======================================
512 float Votecommand_check_assignment(entity caller, float assignment)
514 float from_server = (!caller);
516 if ((assignment == VC_ASGNMNT_BOTH)
517 || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
518 || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
523 string VoteCommand_extractcommand(string input, float startpos, int argc)
527 if ((argc - 1) < startpos) output = "";
528 else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
533 float VoteCommand_checknasty(string vote_command)
535 if ((strstrofs(vote_command, ";", 0) >= 0)
536 || (strstrofs(vote_command, "\n", 0) >= 0)
537 || (strstrofs(vote_command, "\r", 0) >= 0)
538 || (strstrofs(vote_command, "$", 0) >= 0)) return false;
543 float VoteCommand_checkinlist(string vote_command, string list)
545 string l = strcat(" ", list, " ");
547 if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true;
552 string ValidateMap(string validated_map, entity caller)
554 validated_map = MapInfo_FixName(validated_map);
558 print_to(caller, "This map is not available on this server.");
562 if (!autocvar_sv_vote_override_mostrecent && caller)
564 if (Map_IsRecent(validated_map))
566 print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
571 if (!MapInfo_CheckMap(validated_map))
573 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode."));
577 return validated_map;
580 float VoteCommand_checkargs(float startpos, int argc)
582 float p, q, check, minargs;
583 string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
584 string cmdrestriction = ""; // No we don't.
585 string charlist, arg;
588 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
589 cmdrestriction = cvar_string(cvarname);
591 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
593 if (cmdrestriction == "") return true;
595 ++startpos; // skip command name
597 // check minimum arg count
599 // 0 args: argc == startpos
600 // 1 args: argc == startpos + 1
603 minargs = stof(cmdrestriction);
604 if (argc - startpos < minargs) return false;
606 p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
610 // we know that at any time, startpos <= argc - minargs
611 // so this means: argc-minargs >= startpos >= argc, thus
612 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
613 // have been seen already
615 if (startpos >= argc) // all args checked? GOOD
618 if (p < 0) // no more args? FAIL
620 // exception: exactly minargs left, this one included
621 if (argc - startpos == minargs) break;
627 // cut to next semicolon
628 q = strstrofs(cmdrestriction, ";", p + 1); // find next semicolon
629 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
630 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
632 // in case we ever want to allow semicolons in VoteCommand_checknasty
633 // charlist = strreplace("^^", ";", charlist);
637 // verify the arg only contains allowed chars
638 arg = argv(startpos);
639 checkmate = strlen(arg);
640 for (check = 0; check < checkmate; ++check)
641 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
642 // not allowed character
643 // all characters are allowed. FINE.
654 int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
656 string first_command = argv(startpos);
657 int missing_chars = argv_start_index(startpos);
659 if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
662 if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
664 if (!VoteCommand_checkargs(startpos, argc)) return 0;
666 switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
668 case MUT_VOTEPARSE_CONTINUE: { break; }
669 case MUT_VOTEPARSE_SUCCESS: { return 1; }
670 case MUT_VOTEPARSE_INVALID: { return -1; }
671 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
674 switch (first_command) // now go through and parse the proper commands to adjust as needed.
677 case "kickban": // catch all kick/kickban commands
679 entity victim = GetIndexedEntity(argc, (startpos + 1));
680 float accepted = VerifyClientEntity(victim, true, false);
684 string reason = "No reason provided";
685 if(argc > next_token)
686 reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
688 string command_arguments = reason;
689 if (first_command == "kickban")
690 command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
692 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
693 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
695 else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
702 case "gotomap": // re-direct all map selection commands to gotomap
704 vote_command = ValidateMap(argv(startpos + 1), caller);
705 if (!vote_command) return -1;
706 vote_parsed_command = strcat("gotomap ", vote_command);
707 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
712 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?
714 vote_command = ValidateMap(argv(startpos + 1), caller);
715 if (!vote_command) return -1;
716 vote_parsed_command = strcat("nextmap ", vote_command);
717 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
724 vote_parsed_command = vote_command;
725 vote_parsed_display = strzone(strcat("^1", vote_command));
735 // =======================
736 // Command Sub-Functions
737 // =======================
739 void VoteCommand_abstain(int request, entity caller) // CLIENT ONLY
743 case CMD_REQUEST_COMMAND:
745 if (!vote_called) { print_to(caller, "^1No vote called."); }
746 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
748 print_to(caller, "^1You have already voted.");
751 else // everything went okay, continue changing vote
753 print_to(caller, "^1You abstained from your vote.");
754 caller.vote_selection = VOTE_SELECT_ABSTAIN;
756 if (!autocvar_sv_vote_singlecount) VoteCount(false); }
762 case CMD_REQUEST_USAGE:
764 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
765 print_to(caller, " No arguments required.");
771 void VoteCommand_call(int request, entity caller, int argc, string vote_command) // BOTH
775 case CMD_REQUEST_COMMAND:
777 float tmp_playercount = 0;
780 vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
782 if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
783 else if (!autocvar_sv_vote_gamestart && time < game_starttime)
785 print_to(caller, "^1Vote calling is not allowed before the match has started.");
787 else if (vote_called)
789 print_to(caller, "^1There is already a vote called.");
791 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
793 print_to(caller, "^1Only players can call a vote.");
795 else if (caller && !IS_CLIENT(caller))
797 print_to(caller, "^1Only connected clients can vote.");
799 else if (timeout_status)
801 print_to(caller, "^1You can not call a vote while a timeout is active.");
803 else if (caller && (time < caller.vote_waittime))
805 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
807 else if (!VoteCommand_checknasty(vote_command))
809 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
811 else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
814 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
816 else // everything went okay, continue with calling the vote
818 vote_caller = caller; // remember who called the vote
819 vote_caller_name = strzone(GetCallerName(vote_caller));
820 vote_called = VOTE_NORMAL;
821 vote_called_command = strzone(vote_parsed_command);
822 vote_called_display = strzone(vote_parsed_display);
823 vote_endtime = time + autocvar_sv_vote_timeout;
827 caller.vote_selection = VOTE_SELECT_ACCEPT;
828 caller.vote_waittime = time + autocvar_sv_vote_wait;
832 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
833 if (tmp_playercount > 1)
834 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
836 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
837 if (autocvar_sv_eventlog)
838 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
839 Nagger_VoteChanged();
840 VoteCount(true); // needed if you are the only one
847 case CMD_REQUEST_USAGE:
849 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
850 print_to(caller, " Where 'command' is the command to request a vote upon.");
851 print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
852 print_to(caller, strcat(" ", GetCommandPrefix(caller), " vote call endmatch"));
858 void VoteCommand_master(int request, entity caller, int argc, string vote_command) // CLIENT ONLY
862 case CMD_REQUEST_COMMAND:
864 if (autocvar_sv_vote_master)
866 switch (strtolower(argv(2)))
871 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
873 if (!caller.vote_master)
874 print_to(caller, "^1You do not have vote master privileges.");
875 else if (!VoteCommand_checknasty(vote_command))
877 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
879 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
882 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
884 else // everything went okay, proceed with command
886 localcmd(strcat(vote_parsed_command, "\n"));
887 print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
888 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
889 if (autocvar_sv_eventlog)
890 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
898 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
899 else if (caller.vote_master)
901 print_to(caller, "^1You are already logged in as vote master.");
903 else if (autocvar_sv_vote_master_password != argv(3))
905 print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
907 else // everything went okay, proceed with giving this player master privilages
909 caller.vote_master = true;
910 print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
911 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
912 if (autocvar_sv_eventlog)
913 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
919 default: // calling a vote for master
921 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
922 else if (vote_called)
924 print_to(caller, "^1There is already a vote called.");
926 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
928 print_to(caller, "^1Only players can call a vote.");
930 else if (timeout_status)
932 print_to(caller, "^1You can not call a vote while a timeout is active.");
934 else // everything went okay, continue with creating vote
936 vote_caller = caller;
937 vote_caller_name = strzone(GetCallerName(vote_caller));
938 vote_called = VOTE_MASTER;
939 vote_called_command = strzone("XXX");
940 vote_called_display = strzone("^3master");
941 vote_endtime = time + autocvar_sv_vote_timeout;
943 caller.vote_selection = VOTE_SELECT_ACCEPT;
944 caller.vote_waittime = time + autocvar_sv_vote_wait;
946 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
947 if (autocvar_sv_eventlog)
948 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
949 Nagger_VoteChanged();
950 VoteCount(true); // needed if you are the only one
957 else { print_to(caller, "^1Master control of voting is not allowed."); }
963 case CMD_REQUEST_USAGE:
965 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
966 print_to(caller, " If action is left blank, it calls a vote for you to become master.");
967 print_to(caller, " Otherwise the actions are either 'do' a command or 'login' as master.");
973 void VoteCommand_no(int request, entity caller) // CLIENT ONLY
977 case CMD_REQUEST_COMMAND:
979 if (!vote_called) { print_to(caller, "^1No vote called."); }
980 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
982 print_to(caller, "^1You have already voted.");
984 else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
989 else // everything went okay, continue changing vote
991 print_to(caller, "^1You rejected the vote.");
992 caller.vote_selection = VOTE_SELECT_REJECT;
994 if (!autocvar_sv_vote_singlecount)
1002 case CMD_REQUEST_USAGE:
1004 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1005 print_to(caller, " No arguments required.");
1011 void VoteCommand_status(int request, entity caller) // BOTH
1015 case CMD_REQUEST_COMMAND:
1017 if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1018 else print_to(caller, "^1No vote called.");
1024 case CMD_REQUEST_USAGE:
1026 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1027 print_to(caller, " No arguments required.");
1033 void VoteCommand_stop(int request, entity caller) // BOTH
1037 case CMD_REQUEST_COMMAND:
1039 if (!vote_called) print_to(caller, "^1No vote called.");
1040 else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller);
1041 else print_to(caller, "^1You are not allowed to stop that vote.");
1046 case CMD_REQUEST_USAGE:
1048 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1049 print_to(caller, " No arguments required.");
1055 void VoteCommand_yes(int request, entity caller) // CLIENT ONLY
1059 case CMD_REQUEST_COMMAND:
1061 if (!vote_called) { print_to(caller, "^1No vote called."); }
1062 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1064 print_to(caller, "^1You have already voted.");
1066 else // everything went okay, continue changing vote
1068 print_to(caller, "^1You accepted the vote.");
1069 caller.vote_selection = VOTE_SELECT_ACCEPT;
1070 msg_entity = caller;
1071 if (!autocvar_sv_vote_singlecount)
1079 case CMD_REQUEST_USAGE:
1081 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1082 print_to(caller, " No arguments required.");
1088 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1089 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1090 void VoteCommand_(int request)
1094 case CMD_REQUEST_COMMAND:
1101 case CMD_REQUEST_USAGE:
1103 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1104 print_to(caller, " No arguments required.");
1112 // ================================
1113 // Macro system for vote commands
1114 // ================================
1116 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1117 #define VOTE_COMMANDS(request, caller, arguments, command) \
1118 VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1119 VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1120 VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1121 VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1122 VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1123 VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1124 VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1125 VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1128 void VoteCommand_macro_help(entity caller, int argc)
1130 string command_origin = GetCommandPrefix(caller);
1132 if (argc == 2 || argv(2) == "help") // help display listing all commands
1134 print_to(caller, "\nVoting commands:\n");
1135 #define VOTE_COMMAND(name, function, description, assignment) \
1136 { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } }
1138 VOTE_COMMANDS(0, caller, 0, "");
1141 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1142 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1143 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"));
1145 else // usage for individual command
1147 #define VOTE_COMMAND(name, function, description, assignment) \
1148 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1150 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1153 string cvarname = strcat("sv_vote_command_help_", argv(2));
1154 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1155 wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1157 print_to(caller, "No documentation exists for this vote");
1161 float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1163 #define VOTE_COMMAND(name, function, description, assignment) \
1164 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1166 VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1173 // ======================================
1174 // Main function handling vote commands
1175 // ======================================
1177 void VoteCommand(int request, entity caller, int argc, string vote_command)
1179 // Guide for working with argc arguments by example:
1180 // argc: 1 - 2 - 3 - 4
1181 // argv: 0 - 1 - 2 - 3
1182 // cmd vote - master - login - password
1186 case CMD_REQUEST_COMMAND:
1188 if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1192 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"));
1193 case CMD_REQUEST_USAGE:
1195 VoteCommand_macro_help(caller, argc);