mySize -= '2 2 0' * panel_bg_padding;
}
- string timer;
- float timelimit, timeleft, minutesLeft;
+ string timer_sub = "";
+ float timelimit, timeleft, minutesLeft, overtimes, timeout_last;
timelimit = STAT(TIMELIMIT);
+ overtimes = STAT(OVERTIMESADDED);
+ timeout_last = STAT(TIMEOUT_LAST);
- timeleft = max(0, timelimit * 60 + STAT(GAMESTARTTIME) - time);
- timeleft = min(timeleft, timelimit * 60);
+ timeleft = bound(0, timelimit * 60 + STAT(GAMESTARTTIME) - time, timelimit * 60);
timeleft = ceil(timeleft);
+ // countdown sound
+ // if 3 use server dictated option, otherwise the client's
+ int countdown_type;
+ if(autocvar_cl_timer_countdown == 3)
+ countdown_type = sv_timer_countdown;
+ else
+ countdown_type = autocvar_cl_timer_countdown;
+
+ if(countdown_type && !warmup_stage && timeleft > 0 && timeleft != last_timeleft && timeleft <= 10)
+ {
+ if(countdown_type == 1 || (countdown_type == 2 && spectatee_status))
+ sound(NULL, CH_INFO, SND_ENDCOUNT, VOL_BASE, ATTN_NONE);
+
+ last_timeleft = timeleft;
+ }
+
minutesLeft = floor(timeleft / 60);
float warmup_timeleft = 0;
});
}
- int CA_GetWinnerTeam()
- {
- int winner_team = 0;
- if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1)) >= 1)
- {
- winner_team = NUM_TEAM_1;
- }
- for (int i = 2; i <= NUM_TEAMS; ++i)
- {
- if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) >= 1)
- {
- if (winner_team != 0)
- {
- return 0;
- }
- winner_team = Team_IndexToTeam(i);
- }
- }
- if (winner_team)
- {
- return winner_team;
- }
- return -1; // no player left
- }
-
void nades_Clear(entity player);
+entity ca_LastPlayer(float tm)
+{
+ entity last_pl = NULL;
+ FOREACH_CLIENT(IS_PLAYER(it) && it.team == tm, {
+ if (!IS_DEAD(it))
+ {
+ if (!last_pl)
+ last_pl = it;
+ else
+ return NULL;
+ }
+ });
+ return last_pl;
+}
+
+
float CA_CheckWinner()
{
if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
}
CA_count_alive_players();
- if (Team_GetNumberOfAliveTeams() > 1)
- {
+ int winner_team = Team_GetWinnerAliveTeam();
+ if (!winner_team)
return 0;
- }
- int winner_team = CA_GetWinnerTeam();
+ bool perfect = false;
if(winner_team > 0)
{
+ entity tm = Team_GetTeam(winner_team);
+ entity last_pl = ca_LastPlayer(winner_team);
+
+ if(last_pl && Team_GetNumberOfPlayers(tm) >= 3) {
+ Give_Medal(last_pl, DEFENSE);
+ }
+
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
+ if(fragsleft > 1) Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, APP_TEAM_NUM(winner_team, ANNCE_ROUND_TEAM_WIN));
TeamScore_AddToTeam(winner_team, ST_CA_ROUNDS, +1);
+
+ if (Team_GetNumberOfPlayers(tm) >= 3 &&
+ Team_GetNumberOfAlivePlayers(tm) == Team_GetNumberOfPlayers(tm))
+ perfect = true;
}
else if(winner_team == -1)
{
return true;
}
- if (Team_GetNumberOfAliveTeams() > 1)
- {
+ int winner_team = Team_GetWinnerAliveTeam();
+ if (!winner_team)
return false;
- }
- int winner_team = freezetag_getWinnerTeam();
if(winner_team > 0)
{
- Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
+ entity last_pl = freezetag_LastPlayer(winner_team);
+ if(last_pl) {
+ Give_Medal(last_pl, DEFENSE);
+ }
+
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_SCORES));
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_SCORES));
TeamScore_AddToTeam(winner_team, ST_FT_ROUNDS, +1);
+ if(fragsleft > 1) AnnounceScores(winner_team);
}
else if(winner_team == -1)
{
{
sprint(source, sourcemsgstr);
dedicated_print(msgstr); // send to server console too
- FOREACH_CLIENT(!(IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+ FOREACH_CLIENT(!(IS_PLAYER(it) || INGAME(it)) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
sprint(it, msgstr);
});
- event_log_msg = sprintf(":chat_spec:%d:%s", source.playerid, strreplace("\n", " ", msgin));
+
+ if(!play_chatsound(source, msgin))
+ event_log_msg = sprintf(":chat_spec:%d:%s", source.playerid, strreplace("\n", " ", msgin));
}
else
{
{
case CMD_REQUEST_COMMAND:
{
- if (IS_CLIENT(caller))
+ if (IS_CLIENT(caller) && caller.last_ready < time - 3) // anti-spam
{
- if (warmup_stage || autocvar_sv_ready_restart || g_race_qualifying == 2)
+ if (warmup_stage || g_race_qualifying == 2)
{
- if (!readyrestart_happened || autocvar_sv_ready_restart_repeatable)
+ if (time < game_starttime) // game is already restarting
+ return;
+ if (caller.ready) // toggle
{
- if (time < game_starttime) // game is already restarting
- return;
- if (caller.ready) // toggle
- {
- caller.ready = false;
- if(IS_PLAYER(caller) || caller.caplayer == 1)
- bprint("\{1}", playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
- }
- else
- {
- caller.ready = true;
- if(IS_PLAYER(caller) || caller.caplayer == 1)
- bprint("\{1}", playername(caller.netname, caller.team, false), "^2 is ready\n");
- }
-
- caller.last_ready = time;
-
- // cannot reset the game while a timeout is active!
- if (!timeout_status) ReadyCount();
+ caller.ready = false;
+ if (IS_PLAYER(caller) || INGAME_JOINED(caller))
- bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
++ bprint("\{1}", playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
}
else
{
- sprint(caller, "^1Game has already been restarted\n");
+ caller.ready = true;
+ if (IS_PLAYER(caller) || INGAME_JOINED(caller))
- bprint(playername(caller.netname, caller.team, false), "^2 is ready\n");
++ bprint("\{1}", playername(caller.netname, caller.team, false), "^2 is ready\n");
}
+
+ // cannot reset the game while a timeout is active!
+ if (!timeout_status) ReadyCount();
}
}
return; // never fall through to usage
SERVER_COMMAND(printstats, "Dump eventlog player stats and other score information") { GameCommand_printstats(request); }
SERVER_COMMAND(radarmap, "Generate a radar image of the map") { GameCommand_radarmap(request, arguments); }
SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); }
- SERVER_COMMAND(reset, "Soft restart the server and reset the players") { GameCommand_reset(request); }
+ SERVER_COMMAND(resetmatch, "Soft restart the game without changing teams; goes back to warmup if enabled") { GameCommand_resetmatch(request); }
SERVER_COMMAND(setbots, "Adjust how many bots are in the match") { GameCommand_setbots(request, arguments); }
+SERVER_COMMAND(setflag, "Set client flag") { GameCommand_setflag(request, arguments); }
SERVER_COMMAND(shuffleteams, "Randomly move players to different teams") { GameCommand_shuffleteams(request); }
SERVER_COMMAND(stuffto, "Send a command to be executed on a client") { GameCommand_stuffto(request, arguments); }
+SERVER_COMMAND(teamname, "Set team name") { GameCommand_teamname(request, arguments); }
SERVER_COMMAND(trace, "Various debugging tools with tracing") { GameCommand_trace(request, arguments); }
SERVER_COMMAND(unlockteams, "Enable the ability for players to switch or enter teams") { GameCommand_unlockteams(request); }
SERVER_COMMAND(warp, "Choose different level in campaign") { GameCommand_warp(request, arguments); }
// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
void ReadyRestart_think(entity this)
{
- restart_mapalreadyrestarted = true;
- reset_map(true);
+ reset_map(true, false);
Score_ClearAll();
+ Inventory_ClearAll();
delete(this);
}
break;
}
- case "restart":
- case "reset":
+ case "gg":
+ case "shuffleteams":
- case "allready":
+ 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 2 ", vote_command);
+ vote_parsed_display = strzone(strcat("^1", vote_command));
++
++ break;
++ }
++
++ case "reset":
+ case "restart":
+ case "resetmatch": // re-direct all match restarting to resetmatch
+ {
- vote_parsed_command = "resetmatch";
++ vote_parsed_command = "defer 2 resetmatch";
+ vote_parsed_display = strzone("^1resetmatch");
+
+ break;
+ }
+
+ case "allready":
+ {
+ if(!warmup_stage) {
+ print_to(caller, "Game already started. Use the resetmatch command to restart the match.");
+ return -1;
+ }
- vote_parsed_command = vote_command;
++ vote_parsed_command = strcat("defer 2 ", vote_command);
+ vote_parsed_display = strzone(strcat("^1", vote_command));
break;
}
const float RESTART_COUNTDOWN = 10;
entity nagger;
float readycount; // amount of players who are ready
- float readyrestart_happened; // keeps track of whether a restart has already happened
- float restart_mapalreadyrestarted; // bool, indicates whether reset_map() was already executed
.float ready; // flag for if a player is ready
+.float last_ready; // z411 time of the last readyup for anti-spam
.int team_saved; // team number to restore upon map reset
.void(entity this) reset; // if set, an entity is reset using this
.void(entity this) reset2; // if set, an entity is reset using this (after calling ALL the reset functions for other entities)
team_ent.m_num_players_alive = number;
}
+int Team_GetNumberOfPlayers(entity team_ent)
+{
+ return team_ent.m_num_players;
+}
+
+void Team_SetNumberOfPlayers(entity team_ent, int number)
+{
+ team_ent.m_num_players = number;
+}
+
+ int Team_GetWinnerAliveTeam()
+ {
+ int winner = 0;
+ for (int i = 0; i < NUM_TEAMS; ++i)
+ {
+ if (g_team_entities[i].m_num_players_alive > 0)
+ {
+ if (winner)
+ return 0;
+ winner = Team_IndexToTeam(i + 1);
+ }
+ }
+ return (winner ? winner : -1);
+ }
+
int Team_GetNumberOfAliveTeams()
{
int result = 0;
/// \param[in,out] team_ent Team entity.
/// \param[in] number Number of players to set.
void Team_SetNumberOfAlivePlayers(entity team_ent, int number);
+void Team_SetNumberOfPlayers(entity team_ent, int number);
+ /// \brief Returns the winner team.
+ /// \return Winner team or 0 if 2 or more teams have alive players or -1 if no team has any alive players.
+ int Team_GetWinnerAliveTeam();
+
/// \brief Returns the number of alive teams.
/// \return Number of alive teams.
int Team_GetNumberOfAliveTeams();
// clear the .winning flags
void ClearWinners()
{
- FOREACH_CLIENT(IS_PLAYER(it), { it.winning = 0; });
+ FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = 0; });
}
-int fragsleft_last;
+void AnnounceNewLeader()
+{
+ if(teamplay) {
+ if (WinningConditionHelper_equality)
+ Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_TEAM_LEADS_TIED);
+ else
+ FOREACH_CLIENT(IS_PLAYER(it), {
+ if(it.team == WinningConditionHelper_winnerteam)
+ Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_LEADS_TEAM);
+ else
+ Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_LEADS_ENEMY);
+ });
+ Send_Notification(NOTIF_ALL_SPEC, NULL, MSG_ANNCE, APP_TEAM_NUM(WinningConditionHelper_winnerteam, ANNCE_TEAM_LEADS));
+ } else {
+ if (WinningConditionHelper_equality)
+ {
+ Send_Notification(NOTIF_ONE, WinningConditionHelper_equality_one, MSG_ANNCE, ANNCE_LEAD_TIED);
+ Send_Notification(NOTIF_ONE, WinningConditionHelper_equality_two, MSG_ANNCE, ANNCE_LEAD_TIED);
+ }
+ else
+ {
+ Send_Notification(NOTIF_ONE, WinningConditionHelper_winner, MSG_ANNCE, ANNCE_LEAD_GAINED);
+ Send_Notification(NOTIF_ONE, WinningConditionHelper_second, MSG_ANNCE, ANNCE_LEAD_LOST);
+ }
+ }
+}
+
+void AnnounceScores(float tm)
+{
+ WinningConditionHelper(NULL);
+ if (Score_NewLeader()) {
+ AnnounceNewLeader();
+ } else {
+ FOREACH_CLIENT(IS_PLAYER(it), {
+ if(it.team == tm)
+ Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_SCORES_TEAM);
+ else
+ Send_Notification(NOTIF_ONE_ONLY, it, MSG_ANNCE, ANNCE_TEAM_SCORES_ENEMY);
+ });
+ Send_Notification(NOTIF_ALL_SPEC, NULL, MSG_ANNCE, APP_TEAM_NUM(tm, ANNCE_TEAM_SCORES));
+ }
+}
+
float WinningCondition_Scores(float limit, float leadlimit)
{
// TODO make everything use THIS winning condition (except LMS)