diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 90afb131b..f10d9fa03 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -659,18 +659,12 @@ void CGameContext::ConPractice(IConsole::IResult *pResult, void *pUserData) char aBuf[512]; str_format(aBuf, sizeof(aBuf), "'%s' voted to %s /practice mode for your team, which means you can use /r, but you can't earn a rank. Type /practice to vote (%d/%d required votes)", pSelf->Server()->ClientName(pResult->m_ClientID), VotedForPractice ? "enable" : "disable", NumCurrentVotes, NumRequiredVotes); - - for(int i = 0; i < MAX_CLIENTS; i++) - if(Teams.m_Core.Team(i) == Team) - pSelf->SendChatTarget(i, aBuf); + pSelf->SendChatTeam(Team, aBuf); if(NumCurrentVotes >= NumRequiredVotes) { Teams.EnablePractice(Team); - - for(int i = 0; i < MAX_CLIENTS; i++) - if(Teams.m_Core.Team(i) == Team) - pSelf->SendChatTarget(i, "Practice mode enabled for your team, happy practicing!"); + pSelf->SendChatTeam(Team, "Practice mode enabled for your team, happy practicing!"); } } @@ -879,10 +873,7 @@ void CGameContext::ConLockTeam(IConsole::IResult *pResult, void *pUserData) ((CGameControllerDDRace *)pSelf->m_pController)->m_Teams.SetTeamLock(Team, true); str_format(aBuf, sizeof(aBuf), "'%s' locked your team. After the race starts, killing will kill everyone in your team.", pSelf->Server()->ClientName(pResult->m_ClientID)); - - for(int i = 0; i < MAX_CLIENTS; i++) - if(((CGameControllerDDRace *)pSelf->m_pController)->m_Teams.m_Core.Team(i) == Team) - pSelf->SendChatTarget(i, aBuf); + pSelf->SendChatTeam(Team, aBuf); } } @@ -916,10 +907,7 @@ void CGameContext::UnlockTeam(int ClientID, int Team) char aBuf[512]; str_format(aBuf, sizeof(aBuf), "'%s' unlocked your team.", Server()->ClientName(ClientID)); - - for(int i = 0; i < MAX_CLIENTS; i++) - if(((CGameControllerDDRace *)m_pController)->m_Teams.m_Core.Team(i) == Team) - SendChatTarget(i, aBuf); + SendChatTeam(Team, aBuf); } void CGameContext::ConInviteTeam(IConsole::IResult *pResult, void *pUserData) @@ -980,10 +968,7 @@ void CGameContext::ConInviteTeam(IConsole::IResult *pResult, void *pUserData) pSelf->SendChatTarget(Target, aBuf); str_format(aBuf, sizeof aBuf, "'%s' invited '%s' to your team.", pSelf->Server()->ClientName(pResult->m_ClientID), pSelf->Server()->ClientName(Target)); - ; - for(int i = 0; i < MAX_CLIENTS; i++) - if(((CGameControllerDDRace *)pSelf->m_pController)->m_Teams.m_Core.Team(i) == Team) - pSelf->SendChatTarget(i, aBuf); + pSelf->SendChatTeam(Team, aBuf); } else pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "invite", "Can't invite players to this team"); diff --git a/src/game/server/gamemodes/DDRace.cpp b/src/game/server/gamemodes/DDRace.cpp index 916e2f819..78055762f 100644 --- a/src/game/server/gamemodes/DDRace.cpp +++ b/src/game/server/gamemodes/DDRace.cpp @@ -103,10 +103,7 @@ void CGameControllerDDRace::HandleCharacterTiles(CCharacter *pChr, int MapIndex) else if(((m_TileIndex == TILE_UNLOCK_TEAM) || (m_TileFIndex == TILE_UNLOCK_TEAM)) && m_Teams.TeamLocked(GetPlayerTeam(ClientID))) { m_Teams.SetTeamLock(GetPlayerTeam(ClientID), false); - - for(int i = 0; i < MAX_CLIENTS; i++) - if(GetPlayerTeam(i) == GetPlayerTeam(ClientID)) - GameServer()->SendChatTarget(i, "Your team was unlocked by an unlock team tile"); + GameServer()->SendChatTeam(GetPlayerTeam(ClientID), "Your team was unlocked by an unlock team tile"); } // solo part diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index f7bbd7b63..22558ba7a 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -29,6 +29,7 @@ void CGameTeams::Reset() m_Practice[i] = false; m_LastSwap[i] = 0; m_TeamSentStartWarning[i] = false; + m_TeamUnfinishableKillTick[i] = -1; } } @@ -38,7 +39,8 @@ void CGameTeams::ResetRoundState(int Team) ResetSwitchers(Team); m_LastSwap[Team] = 0; - m_Practice[Team] = 0; + m_Practice[Team] = false; + m_TeamUnfinishableKillTick[Team] = -1; for(int i = 0; i < MAX_CLIENTS; i++) { if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) @@ -127,6 +129,7 @@ void CGameTeams::OnCharacterStart(int ClientID) { ChangeTeamState(m_Core.Team(ClientID), TEAMSTATE_STARTED); m_TeamSentStartWarning[m_Core.Team(ClientID)] = false; + m_TeamUnfinishableKillTick[m_Core.Team(ClientID)] = -1; int NumPlayers = Count(m_Core.Team(ClientID)); @@ -205,6 +208,25 @@ void CGameTeams::OnCharacterFinish(int ClientID) void CGameTeams::Tick() { int Now = Server()->Tick(); + + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_TeamUnfinishableKillTick[i] == -1 || m_TeamState[i] != TEAMSTATE_STARTED_UNFINISHABLE) + { + continue; + } + if(Now >= m_TeamUnfinishableKillTick[i]) + { + if(m_Practice[i]) + { + m_TeamUnfinishableKillTick[i] = -1; + continue; + } + KillTeam(i, -1); + GameServer()->SendChatTeam(i, "Your team was killed because it couldn't finish anymore and hasn't entered /practice mode"); + } + } + int Frequency = Server()->TickSpeed() * 60; int Remainder = Server()->TickSpeed() * 30; uint64_t TeamHasWantedStartTime = 0; @@ -263,14 +285,7 @@ void CGameTeams::Tick() NumPlayersNotStarted, NumPlayersNotStarted == 1 ? "player that has" : "players that have", aPlayerNames); - - for(int j = 0; j < MAX_CLIENTS; j++) - { - if(m_Core.Team(j) == i) - { - GameServer()->SendChatTarget(j, aBuf); - } - } + GameServer()->SendChatTeam(i, aBuf); } } @@ -312,14 +327,7 @@ void CGameTeams::CheckTeamFinished(int Team) str_format(aBuf, sizeof(aBuf), "Your team would've finished in: %d minute(s) %5.2f second(s). Since you had practice mode enabled your rank doesn't count.", (int)Time / 60, Time - ((int)Time / 60 * 60)); - - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) - { - GameServer()->SendChatTarget(i, aBuf); - } - } + GameServer()->SendChatTeam(Team, aBuf); for(unsigned int i = 0; i < PlayersCount; ++i) { @@ -434,6 +442,22 @@ void CGameTeams::ChangeTeamState(int Team, int State) m_TeamState[Team] = State; } +void CGameTeams::KillTeam(int Team, int NewStrongID) +{ + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) + { + GameServer()->m_apPlayers[i]->m_VotedForPractice = false; + GameServer()->m_apPlayers[i]->KillCharacter(WEAPON_SELF); + if(NewStrongID != -1 && i != NewStrongID) + { + GameServer()->m_apPlayers[i]->Respawn(true); // spawn the rest of team with weak hook on the killer + } + } + } +} + bool CGameTeams::TeamFinished(int Team) { if(m_TeamState[Team] != TEAMSTATE_STARTED) @@ -982,20 +1006,11 @@ void CGameTeams::OnCharacterDeath(int ClientID, int Weapon) m_Practice[Team] = false; - for(int i = 0; i < MAX_CLIENTS; i++) - if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) - { - GameServer()->m_apPlayers[i]->m_VotedForPractice = false; - - if(i != ClientID) - { - GameServer()->m_apPlayers[i]->KillCharacter(WEAPON_SELF); - if(Weapon == WEAPON_SELF) - GameServer()->m_apPlayers[i]->Respawn(true); // spawn the rest of team with weak hook on the killer - } - if(Count(Team) > 1) - GameServer()->SendChatTarget(i, aBuf); - } + KillTeam(Team, Weapon == WEAPON_SELF ? ClientID : -1); + if(Count(Team) > 1) + { + GameServer()->SendChatTeam(Team, aBuf); + } } } else @@ -1003,16 +1018,10 @@ void CGameTeams::OnCharacterDeath(int ClientID, int Weapon) if(m_TeamState[m_Core.Team(ClientID)] == CGameTeams::TEAMSTATE_STARTED && !m_TeeStarted[ClientID]) { char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "This team cannot finish anymore because '%s' left the team before hitting the start", Server()->ClientName(ClientID)); - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(m_Core.Team(i) != Team || i == ClientID) - { - continue; - } - GameServer()->SendChatTarget(i, aBuf); - } + str_format(aBuf, sizeof(aBuf), "This team cannot finish anymore because '%s' left the team before hitting the start, enter /practice mode to avoid being killed in 60 seconds", Server()->ClientName(ClientID)); + GameServer()->SendChatTeam(Team, aBuf); + m_TeamUnfinishableKillTick[Team] = Server()->Tick() + 60 * Server()->TickSpeed(); ChangeTeamState(Team, CGameTeams::TEAMSTATE_STARTED_UNFINISHABLE); } SetForceCharacterTeam(ClientID, TEAM_FLOCK); @@ -1044,14 +1053,7 @@ void CGameTeams::SetClientInvited(int Team, int ClientID, bool Invited) void CGameTeams::KillSavedTeam(int ClientID, int Team) { - for(int i = 0; i < MAX_CLIENTS; i++) - { - if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) - { - GameServer()->m_apPlayers[i]->m_VotedForPractice = false; - GameServer()->m_apPlayers[i]->KillCharacter(WEAPON_SELF); - } - } + KillTeam(Team, -1); } void CGameTeams::ResetSavedTeam(int ClientID, int Team) diff --git a/src/game/server/teams.h b/src/game/server/teams.h index 7d5dd0c86..c380a9ea0 100644 --- a/src/game/server/teams.h +++ b/src/game/server/teams.h @@ -27,9 +27,18 @@ class CGameTeams std::shared_ptr m_pSaveTeamResult[MAX_CLIENTS]; uint64_t m_LastSwap[MAX_CLIENTS]; bool m_TeamSentStartWarning[MAX_CLIENTS]; + // `m_TeamUnfinishableKillTick` is -1 by default and gets set when a + // team becomes unfinishable. If the team hasn't entered practice mode + // by that time, it'll get killed to prevent people not understanding + // the message from playing for a long time in an unfinishable team. + int m_TeamUnfinishableKillTick[MAX_CLIENTS]; class CGameContext *m_pGameContext; + // Kill the whole team, making the player `NewStrongID` have strong + // hook on everyone else. `NewStrongID` can be -1 to get the normal + // spawning order. + void KillTeam(int Team, int NewStrongID); bool TeamFinished(int Team); void OnTeamFinish(CPlayer **Players, unsigned int Size, float Time, const char *pTimestamp); void OnFinish(CPlayer *Player, float Time, const char *pTimestamp);