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 = bound(0, timelimit * 60 + STAT(GAMESTARTTIME) - time, timelimit * 60);
timeleft = ceil(timeleft);
+ // countdown sound
+ if(autocvar_hud_panel_timer_count && timeleft > 0 && timeleft != last_timeleft && timeleft <= 10)
+ {
+ 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");
}
else
{
// ==================================
// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
+SERVER_COMMAND(setflag, "Set client flag") { GameCommand_setflag(request, arguments); }
+SERVER_COMMAND(teamname, "Set team name") { GameCommand_teamname(request, arguments); }
+
SERVER_COMMAND(adminmsg, "Send an admin message to a client directly") { GameCommand_adminmsg(request, arguments); }
- SERVER_COMMAND(allready, "Restart the server and reset the players") { GameCommand_allready(request); }
+ SERVER_COMMAND(allready, "Ends warmup and starts the match") { GameCommand_allready(request); }
SERVER_COMMAND(allspec, "Force all players to spectate") { GameCommand_allspec(request, arguments); }
SERVER_COMMAND(anticheat, "Create an anticheat report for a client") { GameCommand_anticheat(request, arguments); }
SERVER_COMMAND(animbench, "Benchmark model animation (LAGS)") { GameCommand_animbench(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);
}
FOREACH_CLIENT(IS_PLAYER(it), {
it.alivetime = 0;
CS(it).killcount = 0;
- float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
- PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
});
- restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
-
- // disable the warmup global for the server
- if(warmup_stage)
+ // if we're ending the warmup stage call the corresponding hook
+ if(!is_fake_round_start && !warmup_stage)
localcmd("\nsv_hook_warmupend\n");
- warmup_stage = 0; // once the game is restarted the game is in match stage
// reset the .ready status of all players (also spectators)
- FOREACH_CLIENT(IS_REAL_CLIENT(it), { it.ready = false; });
+ 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
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();
void accuracy_free(entity e)
{
delete(CS(e).accuracy);
+ delete(e.roundaccuracy);
}
+ void accuracy_reset(entity e)
+ {
+ entity a = CS(e).accuracy;
+ if (!a) return;
+
+ for (int i = 0; i < REGISTRY_MAX(Weapons); i++)
+ {
+ a.accuracy_frags[i] = 0;
+ a.accuracy_hit[i] = 0;
+ a.accuracy_fired[i] = 0;
+ a.accuracy_cnt_hit[i] = 0;
+ a.accuracy_cnt_fired[i] = 0;
+ }
+ }
+
// force a resend of a player's accuracy stats
void accuracy_resend(entity e)
{
// 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)