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 MUTATOR_CALLHOOK(reset_map_global);
350 FOREACH_ENTITY_FLOAT_ORDERED(pure_data, false,
359 if (it.team_saved) it.team = it.team_saved;
360 if (it.flags & FL_PROJECTILE) delete(it); // remove any projectiles left
363 // Waypoints and assault start come LAST
364 FOREACH_ENTITY_ORDERED(IS_NOT_A_CLIENT(it), {
365 if (it.reset2) it.reset2(it);
368 FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it); });
370 // Moving the player reset code here since the player-reset depends
371 // on spawnpoint entities which have to be reset first --blub
374 if (!MUTATOR_CALLHOOK(reset_map_players))
376 if (restart_mapalreadyrestarted || (time < game_starttime))
378 FOREACH_CLIENT(IS_PLAYER(it),
381 only reset players if a restart countdown is active
382 this can either be due to cvar sv_ready_restart_after_countdown having set
383 restart_mapalreadyrestarted to 1 after the countdown ended or when
384 sv_ready_restart_after_countdown is not used and countdown is still running
386 // NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
387 // PlayerScore_Clear(it);
388 CS(it).killcount = 0;
389 // stop the player from moving so that he stands still once he gets respawned
390 it.velocity = '0 0 0';
391 it.avelocity = '0 0 0';
392 CS(it).movement = '0 0 0';
393 PutClientInServer(it);
400 // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
401 void ReadyRestart_think(entity this)
403 restart_mapalreadyrestarted = true;
409 // Forces a restart of the game without actually reloading the map // this is a mess...
410 void ReadyRestart_force()
412 if (time <= game_starttime && game_stopped)
415 bprint("^1Server is restarting...\n");
419 // clear overtime, we have to decrease timelimit to its original value again.
420 if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
421 cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime)));
422 checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
424 readyrestart_happened = true;
425 game_starttime = time + RESTART_COUNTDOWN;
427 // clear player attributes
428 FOREACH_CLIENT(IS_PLAYER(it), {
430 CS(it).killcount = 0;
431 float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
432 PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
435 restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
437 // disable the warmup global for the server
438 warmup_stage = 0; // once the game is restarted the game is in match stage
440 // reset the .ready status of all players (also spectators)
441 FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
443 Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
445 // lock teams with lockonrestart
446 if (autocvar_teamplay_lockonrestart && teamplay)
449 bprint("^1The teams are now locked.\n");
452 // initiate the restart-countdown-announcer entity
453 if (sv_ready_restart_after_countdown)
455 entity restart_timer = new_pure(restart_timer);
456 setthink(restart_timer, ReadyRestart_think);
457 restart_timer.nextthink = game_starttime;
460 // after a restart every players number of allowed timeouts gets reset, too
461 if (autocvar_sv_timeout)
463 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
466 if (!sv_ready_restart_after_countdown) reset_map(true);
467 if (autocvar_sv_eventlog) GameLogEcho(":restart");
472 if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || game_stopped || race_completing) localcmd("restart\n");
473 else localcmd("\nsv_hook_gamerestart\n");
475 // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off!
476 // Otherwise scores could be manipulated during the countdown.
477 if (!sv_ready_restart_after_countdown) Score_ClearAll();
478 ReadyRestart_force();
481 // Count the players who are ready and determine whether or not to restart the match
484 float ready_needed_factor, ready_needed_count;
485 float t_ready = 0, t_players = 0;
487 FOREACH_CLIENT(IS_REAL_CLIENT(it) && (IS_PLAYER(it) || it.caplayer == 1), {
489 if (it.ready) ++t_ready;
492 readycount = t_ready;
494 Nagger_ReadyCounted();
496 ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999);
497 ready_needed_count = floor(t_players * ready_needed_factor) + 1;
499 if (readycount >= ready_needed_count) ReadyRestart();
503 // ======================================
504 // Supporting functions for VoteCommand
505 // ======================================
507 float Votecommand_check_assignment(entity caller, float assignment)
509 float from_server = (!caller);
511 if ((assignment == VC_ASGNMNT_BOTH)
512 || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY)
513 || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true;
518 string VoteCommand_extractcommand(string input, float startpos, int argc)
522 if ((argc - 1) < startpos) output = "";
523 else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos));
528 float VoteCommand_checknasty(string vote_command)
530 if ((strstrofs(vote_command, ";", 0) >= 0)
531 || (strstrofs(vote_command, "\n", 0) >= 0)
532 || (strstrofs(vote_command, "\r", 0) >= 0)
533 || (strstrofs(vote_command, "$", 0) >= 0)) return false;
538 float VoteCommand_checkinlist(string vote_command, string list)
540 string l = strcat(" ", list, " ");
542 if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true;
547 string ValidateMap(string validated_map, entity caller)
549 validated_map = MapInfo_FixName(validated_map);
553 print_to(caller, "This map is not available on this server.");
557 if (!autocvar_sv_vote_override_mostrecent && caller)
559 if (Map_IsRecent(validated_map))
561 print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds.");
566 if (!MapInfo_CheckMap(validated_map))
568 print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode."));
572 return validated_map;
575 float VoteCommand_checkargs(float startpos, int argc)
577 float p, q, check, minargs;
578 string cvarname = strcat("sv_vote_command_restriction_", argv(startpos));
579 string cmdrestriction = ""; // No we don't.
580 string charlist, arg;
583 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
584 cmdrestriction = cvar_string(cvarname);
586 LOG_INFO("NOTE: ", cvarname, " does not exist, no restrictions will be applied.");
588 if (cmdrestriction == "") return true;
590 ++startpos; // skip command name
592 // check minimum arg count
594 // 0 args: argc == startpos
595 // 1 args: argc == startpos + 1
598 minargs = stof(cmdrestriction);
599 if (argc - startpos < minargs) return false;
601 p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
605 // we know that at any time, startpos <= argc - minargs
606 // so this means: argc-minargs >= startpos >= argc, thus
607 // argc-minargs >= argc, thus minargs <= 0, thus all minargs
608 // have been seen already
610 if (startpos >= argc) // all args checked? GOOD
613 if (p < 0) // no more args? FAIL
615 // exception: exactly minargs left, this one included
616 if (argc - startpos == minargs) break;
622 // cut to next semicolon
623 q = strstrofs(cmdrestriction, ";", p + 1); // find next semicolon
624 if (q < 0) charlist = substring(cmdrestriction, p + 1, -1);
625 else charlist = substring(cmdrestriction, p + 1, q - (p + 1));
627 // in case we ever want to allow semicolons in VoteCommand_checknasty
628 // charlist = strreplace("^^", ";", charlist);
632 // verify the arg only contains allowed chars
633 arg = argv(startpos);
634 checkmate = strlen(arg);
635 for (check = 0; check < checkmate; ++check)
636 if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false;
637 // not allowed character
638 // all characters are allowed. FINE.
649 int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc)
651 string first_command = argv(startpos);
652 int missing_chars = argv_start_index(startpos);
654 if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
657 if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
659 if (!VoteCommand_checkargs(startpos, argc)) return 0;
661 switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc))
663 case MUT_VOTEPARSE_CONTINUE: { break; }
664 case MUT_VOTEPARSE_SUCCESS: { return 1; }
665 case MUT_VOTEPARSE_INVALID: { return -1; }
666 case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
669 switch (first_command) // now go through and parse the proper commands to adjust as needed.
672 case "kickban": // catch all kick/kickban commands
674 entity victim = GetIndexedEntity(argc, (startpos + 1));
675 float accepted = VerifyClientEntity(victim, true, false);
679 string reason = "No reason provided";
680 if(argc > next_token)
681 reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
683 string command_arguments = reason;
684 if (first_command == "kickban")
685 command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
687 vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
688 vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
690 else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
697 case "gotomap": // re-direct all map selection commands to gotomap
699 vote_command = ValidateMap(argv(startpos + 1), caller);
700 if (!vote_command) return -1;
701 vote_parsed_command = strcat("gotomap ", vote_command);
702 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
707 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?
709 vote_command = ValidateMap(argv(startpos + 1), caller);
710 if (!vote_command) return -1;
711 vote_parsed_command = strcat("nextmap ", vote_command);
712 vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
719 // add a delay so that vote result can be seen and announcer can be heard
720 // if the vote is accepted
721 vote_parsed_command = strcat("defer 1 ", vote_command);
722 vote_parsed_display = strzone(strcat("^1", vote_command));
729 vote_parsed_command = vote_command;
730 vote_parsed_display = strzone(strcat("^1", vote_command));
740 // =======================
741 // Command Sub-Functions
742 // =======================
744 void VoteCommand_abstain(int request, entity caller) // CLIENT ONLY
748 case CMD_REQUEST_COMMAND:
750 if (!vote_called) { print_to(caller, "^1No vote called."); }
751 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
753 print_to(caller, "^1You have already voted.");
756 else // everything went okay, continue changing vote
758 print_to(caller, "^1You abstained from your vote.");
759 caller.vote_selection = VOTE_SELECT_ABSTAIN;
761 if (!autocvar_sv_vote_singlecount) VoteCount(false); }
767 case CMD_REQUEST_USAGE:
769 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote abstain"));
770 print_to(caller, " No arguments required.");
776 void VoteCommand_call(int request, entity caller, int argc, string vote_command) // BOTH
780 case CMD_REQUEST_COMMAND:
782 float tmp_playercount = 0;
785 vote_command = VoteCommand_extractcommand(vote_command, 2, argc);
787 if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); }
788 else if (!autocvar_sv_vote_gamestart && time < game_starttime)
790 print_to(caller, "^1Vote calling is not allowed before the match has started.");
792 else if (vote_called)
794 print_to(caller, "^1There is already a vote called.");
796 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
798 print_to(caller, "^1Only players can call a vote.");
800 else if (caller && !IS_CLIENT(caller))
802 print_to(caller, "^1Only connected clients can vote.");
804 else if (timeout_status)
806 print_to(caller, "^1You can not call a vote while a timeout is active.");
808 else if (caller && (time < caller.vote_waittime))
810 print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote."));
812 else if (!VoteCommand_checknasty(vote_command))
814 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
816 else if ((parse_error = VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) <= 0)
819 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
821 else // everything went okay, continue with calling the vote
823 vote_caller = caller; // remember who called the vote
824 vote_caller_name = strzone(GetCallerName(vote_caller));
825 vote_called = VOTE_NORMAL;
826 vote_called_command = strzone(vote_parsed_command);
827 vote_called_display = strzone(vote_parsed_display);
828 vote_endtime = time + autocvar_sv_vote_timeout;
832 caller.vote_selection = VOTE_SELECT_ACCEPT;
833 caller.vote_waittime = time + autocvar_sv_vote_wait;
837 FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
838 if (tmp_playercount > 1)
839 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
841 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
842 if (autocvar_sv_eventlog)
843 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
844 Nagger_VoteChanged();
845 VoteCount(true); // needed if you are the only one
852 case CMD_REQUEST_USAGE:
854 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote call command"));
855 print_to(caller, " Where 'command' is the command to request a vote upon.");
856 print_to(caller, strcat("Examples: ", GetCommandPrefix(caller), " vote call gotomap dance"));
857 print_to(caller, strcat(" ", GetCommandPrefix(caller), " vote call endmatch"));
863 void VoteCommand_master(int request, entity caller, int argc, string vote_command) // CLIENT ONLY
867 case CMD_REQUEST_COMMAND:
869 if (autocvar_sv_vote_master)
871 switch (strtolower(argv(2)))
876 vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
878 if (!caller.vote_master)
879 print_to(caller, "^1You do not have vote master privileges.");
880 else if (!VoteCommand_checknasty(vote_command))
882 print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
884 else if ((parse_error = VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) <= 0)
887 print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
889 else // everything went okay, proceed with command
891 localcmd(strcat(vote_parsed_command, "\n"));
892 print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
893 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
894 if (autocvar_sv_eventlog)
895 GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
903 if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); }
904 else if (caller.vote_master)
906 print_to(caller, "^1You are already logged in as vote master.");
908 else if (autocvar_sv_vote_master_password != argv(3))
910 print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
912 else // everything went okay, proceed with giving this player master privilages
914 caller.vote_master = true;
915 print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
916 bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
917 if (autocvar_sv_eventlog)
918 GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
924 default: // calling a vote for master
926 if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); }
927 else if (vote_called)
929 print_to(caller, "^1There is already a vote called.");
931 else if (!spectators_allowed && (caller && !IS_PLAYER(caller)))
933 print_to(caller, "^1Only players can call a vote.");
935 else if (timeout_status)
937 print_to(caller, "^1You can not call a vote while a timeout is active.");
939 else // everything went okay, continue with creating vote
941 vote_caller = caller;
942 vote_caller_name = strzone(GetCallerName(vote_caller));
943 vote_called = VOTE_MASTER;
944 vote_called_command = strzone("XXX");
945 vote_called_display = strzone("^3master");
946 vote_endtime = time + autocvar_sv_vote_timeout;
948 caller.vote_selection = VOTE_SELECT_ACCEPT;
949 caller.vote_waittime = time + autocvar_sv_vote_wait;
951 bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
952 if (autocvar_sv_eventlog)
953 GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
954 Nagger_VoteChanged();
955 VoteCount(true); // needed if you are the only one
962 else { print_to(caller, "^1Master control of voting is not allowed."); }
968 case CMD_REQUEST_USAGE:
970 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote master [action [command | password]]"));
971 print_to(caller, " If action is left blank, it calls a vote for you to become master.");
972 print_to(caller, " Otherwise the actions are either 'do' a command or 'login' as master.");
978 void VoteCommand_no(int request, entity caller) // CLIENT ONLY
982 case CMD_REQUEST_COMMAND:
984 if (!vote_called) { print_to(caller, "^1No vote called."); }
985 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
987 print_to(caller, "^1You have already voted.");
989 else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
994 else // everything went okay, continue changing vote
996 print_to(caller, "^1You rejected the vote.");
997 caller.vote_selection = VOTE_SELECT_REJECT;
999 if (!autocvar_sv_vote_singlecount)
1007 case CMD_REQUEST_USAGE:
1009 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote no"));
1010 print_to(caller, " No arguments required.");
1016 void VoteCommand_status(int request, entity caller) // BOTH
1020 case CMD_REQUEST_COMMAND:
1022 if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7."));
1023 else print_to(caller, "^1No vote called.");
1029 case CMD_REQUEST_USAGE:
1031 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote status"));
1032 print_to(caller, " No arguments required.");
1038 void VoteCommand_stop(int request, entity caller) // BOTH
1042 case CMD_REQUEST_COMMAND:
1044 if (!vote_called) print_to(caller, "^1No vote called.");
1045 else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller);
1046 else print_to(caller, "^1You are not allowed to stop that vote.");
1051 case CMD_REQUEST_USAGE:
1053 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote stop"));
1054 print_to(caller, " No arguments required.");
1060 void VoteCommand_yes(int request, entity caller) // CLIENT ONLY
1064 case CMD_REQUEST_COMMAND:
1066 if (!vote_called) { print_to(caller, "^1No vote called."); }
1067 else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change)
1069 print_to(caller, "^1You have already voted.");
1071 else // everything went okay, continue changing vote
1073 print_to(caller, "^1You accepted the vote.");
1074 caller.vote_selection = VOTE_SELECT_ACCEPT;
1075 msg_entity = caller;
1076 if (!autocvar_sv_vote_singlecount)
1084 case CMD_REQUEST_USAGE:
1086 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote yes"));
1087 print_to(caller, " No arguments required.");
1093 /* use this when creating a new command, making sure to place it in alphabetical order... also,
1094 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1095 void VoteCommand_(int request)
1099 case CMD_REQUEST_COMMAND:
1106 case CMD_REQUEST_USAGE:
1108 print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote ");
1109 print_to(caller, " No arguments required.");
1117 // ================================
1118 // Macro system for vote commands
1119 // ================================
1121 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1122 #define VOTE_COMMANDS(request, caller, arguments, command) \
1123 VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \
1124 VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \
1125 VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \
1126 VOTE_COMMAND("master", VoteCommand_master(request, caller, arguments, command), "Full control over all voting and vote commands", VC_ASGNMNT_CLIENTONLY) \
1127 VOTE_COMMAND("no", VoteCommand_no(request, caller), "Select no in current vote", VC_ASGNMNT_CLIENTONLY) \
1128 VOTE_COMMAND("status", VoteCommand_status(request, caller), "Prints information about current vote", VC_ASGNMNT_BOTH) \
1129 VOTE_COMMAND("stop", VoteCommand_stop(request, caller), "Immediately end a vote", VC_ASGNMNT_BOTH) \
1130 VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \
1133 void VoteCommand_macro_help(entity caller, int argc)
1135 string command_origin = GetCommandPrefix(caller);
1137 if (argc == 2 || argv(2) == "help") // help display listing all commands
1139 print_to(caller, "\nVoting commands:\n");
1140 #define VOTE_COMMAND(name, function, description, assignment) \
1141 { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } }
1143 VOTE_COMMANDS(0, caller, 0, "");
1146 print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n"));
1147 print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND"));
1148 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"));
1150 else // usage for individual command
1152 #define VOTE_COMMAND(name, function, description, assignment) \
1153 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } }
1155 VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, "");
1158 string cvarname = strcat("sv_vote_command_help_", argv(2));
1159 if(cvar_type(cvarname) & CVAR_TYPEFLAG_EXISTS)
1160 wordwrap_sprint(caller, cvar_string(cvarname), 1000);
1162 print_to(caller, "No documentation exists for this vote");
1166 float VoteCommand_macro_command(entity caller, int argc, string vote_command)
1168 #define VOTE_COMMAND(name, function, description, assignment) \
1169 { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } }
1171 VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command);
1178 // ======================================
1179 // Main function handling vote commands
1180 // ======================================
1182 void VoteCommand(int request, entity caller, int argc, string vote_command)
1184 // Guide for working with argc arguments by example:
1185 // argc: 1 - 2 - 3 - 4
1186 // argv: 0 - 1 - 2 - 3
1187 // cmd vote - master - login - password
1191 case CMD_REQUEST_COMMAND:
1193 if (VoteCommand_macro_command(caller, argc, vote_command)) return;
1197 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"));
1198 case CMD_REQUEST_USAGE:
1200 VoteCommand_macro_help(caller, argc);