#include <server/scores.qh>
#include <server/teamplay.qh>
#include <server/weapons/accuracy.qh>
+#include <server/weapons/selection.qh>
#include <server/world.qh>
// =============================================
// Game logic for voting
// =======================
-void VoteReset()
+void VoteStop(entity stopper, bool show_name);
+void VoteReset(bool verbose)
{
+ if (verbose && vote_called)
+ {
+ VoteStop(NULL, true);
+ return;
+ }
+
FOREACH_CLIENT(true, { it.vote_selection = 0; });
if (vote_called)
Nagger_VoteChanged();
}
-void VoteStop(entity stopper)
+void VoteStop(entity stopper, bool canceled)
{
- bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n");
+ if (canceled)
+ bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote was canceled\n");
+ else
+ bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n");
if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid)));
// Don't force them to wait for next vote, this way they can e.g. correct their vote.
if ((vote_caller) && (stopper == vote_caller)) vote_caller.vote_waittime = time + autocvar_sv_vote_stop;
- VoteReset();
+ VoteReset(false);
}
void VoteAccept()
if (vote_caller) vote_caller.vote_waittime = 0; // people like your votes, you don't need to wait to vote again
- VoteReset();
+ VoteReset(false);
Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_ACCEPT);
}
void VoteReject()
{
bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 was rejected\n");
- VoteReset();
+ VoteReset(false);
Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
}
void VoteTimeout()
{
bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ", vote_called_display, "^2 timed out\n");
- VoteReset();
+ VoteReset(false);
Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_FAIL);
}
void VoteCount(float first_count)
{
- // declarations
vote_accept_count = vote_reject_count = vote_abstain_count = 0;
float vote_player_count = 0, notvoters = 0;
Nagger_VoteCountChanged();
// add up all the votes from each connected client
- FOREACH_CLIENT(IS_REAL_CLIENT(it), {
+ FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_CLIENT(it), {
+ // z411
+ if(vote_target_type == VOTE_TARGET_TEAM && it.team != vote_caller.team) continue;
+ if(vote_target_type == VOTE_TARGET_SINGLE && it != vote_target) continue;
+
++vote_player_count;
if (IS_PLAYER(it) || INGAME(it)) ++vote_real_player_count;
switch (it.vote_selection)
{
if (vote_caller) vote_caller.vote_waittime = 0;
print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master.");
- VoteReset();
+ VoteReset(false);
return;
}
if (round_handler_IsActive())
round_handler_Reset(game_starttime);
}
+
+ // for RJZ
+ if (autocvar_rjz_count_shards) {
+ total_shards = 0;
+ send_TotalShardsAll();
+ }
if (shuffleteams_on_reset_map)
{
it.avelocity = '0 0 0';
CS(it).movement = '0 0 0';
PutClientInServer(it);
+
+ if(IS_BOT_CLIENT(it))
+ {
+ .entity weaponentity = weaponentities[0];
+ if(it.(weaponentity).m_weapon == WEP_Null)
+ W_NextWeapon(it, 0, weaponentity);
+ }
});
}
}
if (!is_fake_round_start && !autocvar_g_campaign)
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_COUNTDOWN_RESTART);
- VoteReset();
+ VoteReset(true);
// clear overtime, we have to decrease timelimit to its original value again.
if (checkrules_overtimesadded > 0 && g_race_qualifying != 2)
if(!is_fake_round_start && !warmup_stage)
localcmd("\nsv_hook_warmupend\n");
- // reset the .ready status of all clients (including spectators and bots)
- FOREACH_CLIENT(true, { it.ready = false; });
+ // reset the .ready status of all players (also spectators)
+ FOREACH_CLIENT(IS_REAL_CLIENT(it), {
+ it.ready = false;
+ Kill_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CPID_MISSING_READY);
+ });
readycount = 0;
Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
}
+ round_handler_Activate(!warmup_stage);
if (!sv_ready_restart_after_countdown || warmup_stage)
reset_map(is_fake_round_start);
void ReadyRestart(bool forceWarmupEnd)
{
- if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || intermission_running || race_completing)
+ if (MUTATOR_CALLHOOK(ReadyRestart_Deny))
{
// NOTE: ReadyRestart support is mandatory in campaign
if (autocvar_g_campaign)
error("ReadyRestart must be supported in campaign mode!");
localcmd("restart\n"); // if ReadyRestart is denied, restart the server
}
- else localcmd("\nsv_hook_readyrestart\n");
+ else if (intermission_running || race_completing) // game is over, ReadyRestart no longer available
+ localcmd("restart\n");
+ else
+ localcmd("\nsv_hook_readyrestart\n");
if(forceWarmupEnd || autocvar_g_campaign)
warmup_stage = 0; // forcefully end warmup and go to match stage
game_starttime = time;
Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, COUNTDOWN_STOP, minplayers);
if (!sv_ready_restart_after_countdown) // if we ran reset_map() at start of countdown
- FOREACH_CLIENT(IS_PLAYER(it), { GiveWarmupResources(it); });
+ FOREACH_CLIENT(IS_PLAYER(it), { ResetPlayerResources(it); });
}
if (warmup_limit > 0)
warmup_limit = -1;
case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; }
}
+ vote_target_type = VOTE_TARGET_ALL;
+
switch (first_command) // now go through and parse the proper commands to adjust as needed.
{
+ case "movetoauto":
+ case "movetored":
+ case "movetoblue":
+ case "movetoyellow":
+ case "movetopink":
+ case "movetospec":
+ {
+ entity victim = GetIndexedEntity(argc, (startpos + 1));
+ float accepted = VerifyClientEntity(victim, true, false);
+ if (accepted > 0)
+ {
+ vote_parsed_command = vote_command;
+ vote_parsed_display = sprintf("^1%s #%d ^7%s", first_command, etof(victim), victim.netname);
+ }
+ else
+ {
+ print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n"));
+ return 0;
+ }
+
+ break;
+ }
+
case "kick":
case "kickban": // catch all kick/kickban commands
{
if (first_command == "kickban")
command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
- vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
+ if (first_command == "kick") // z411 : Use our kick implementation - kind of hacky...
+ vote_parsed_command = strcat("defer 2 \"sv_cmd kickkick # ", ftos(etof(victim)), " ", command_arguments, "\"");
+ else
+ vote_parsed_command = strcat("defer 2 \"", first_command, " # ", ftos(etof(victim)), " ", command_arguments, "\"");
+
vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
}
else
{
vote_command = ValidateMap(argv(startpos + 1), caller);
if (!vote_command) return -1;
- vote_parsed_command = strcat("gotomap ", vote_command);
- vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
+ vote_parsed_command = strcat("defer 2 \"gotomap ", vote_command, "\"");
+ vote_parsed_display = strzone(strcat("^1gotomap ", vote_command));
break;
}
+
+ // z411 team calls
+ case "teamname":
+ {
+ if (teamplay && Team_IsValidTeam(caller.team)) {
+ vote_target_type = VOTE_TARGET_TEAM;
+
+ string tmname = strtolower(Static_Team_ColorName(caller.team));
+ string newname = argv(startpos + 1);
+
+ vote_parsed_command = strcat(first_command, " ", tmname, " \"", newname, "\"");
+ vote_parsed_display = strzone(strcat("^3(Team) ^1", first_command, " ^2", newname));
+ } else { print_to(caller, "vcall: Not in a team\n"); return 0; }
+
+ break;
+ }
// 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?
break;
}
+ case "fraglimit": // include restrictions on the maximum votable frag limit
+ {
+ float fraglimit_vote = stof(argv(startpos + 1));
+ float fraglimit_min = 0;
+ float fraglimit_max = 999999;
+ if(fraglimit_vote > fraglimit_max || fraglimit_vote < fraglimit_min)
+ {
+ print_to(caller, strcat("Invalid fraglimit vote, accepted values are between ", ftos(fraglimit_min), " and ", ftos(fraglimit_max), "."));
+ return -1;
+ }
+ vote_parsed_command = strcat("fraglimit ", ftos(fraglimit_vote));
+ vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
+
+ break;
+ }
+
case "timelimit": // include restrictions on the maximum votable time limit
{
float timelimit_vote = stof(argv(startpos + 1));
print_to(caller, strcat("Invalid timelimit vote, accepted values are between ", ftos(autocvar_timelimit_min), " and ", ftos(autocvar_timelimit_max), "."));
return -1;
}
- timelimit_vote = bound(autocvar_timelimit_min, timelimit_vote, autocvar_timelimit_max);
vote_parsed_command = strcat("timelimit ", ftos(timelimit_vote));
vote_parsed_display = strzone(strcat("^1", vote_parsed_command));
break;
}
- case "restart":
+ case "gg":
+ case "shuffleteams":
+ case "endmatch":
{
// add a delay so that vote result can be seen and announcer can be heard
// if the vote is accepted
- vote_parsed_command = strcat("defer 1 ", vote_command);
+ vote_parsed_command = strcat("defer 2 ", vote_command);
+ vote_parsed_display = strzone(strcat("^1", vote_command));
+
+ break;
+ }
+
+ case "reset":
+ case "restart": // re-direct all match restarting to resetmatch
+ vote_command = "resetmatch"; // fall-through
+ case "resetmatch":
+ {
+ vote_parsed_command = strcat("defer 2 ", vote_command);
vote_parsed_display = strzone(strcat("^1", vote_command));
break;
return -1;
}
- vote_parsed_command = vote_command;
+ vote_parsed_command = strcat("defer 2 ", vote_command);
vote_parsed_display = strzone(strcat("^1", vote_command));
break;
}
FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
+ if (autocvar_sv_vote_debug)
+ bprint("\{1}^2* ^3", "^6DEBUG MODE ACTIVE: bots can vote too\n"); // so servers don't forget it on
if (autocvar_sv_eventlog)
GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
Nagger_VoteChanged();
}
else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote)
{
- VoteStop(caller);
+ VoteStop(caller, true);
}
else // everything went okay, continue changing vote
case CMD_REQUEST_COMMAND:
{
if (!vote_called) print_to(caller, "^1No vote called.");
- else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller);
+ else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller, false);
else print_to(caller, "^1You are not allowed to stop that vote.");
return;
}