1 | /* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ |
2 | /* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */ |
3 | #include "DDRace.h" |
4 | |
5 | #include <engine/server.h> |
6 | #include <engine/shared/config.h> |
7 | #include <game/mapitems.h> |
8 | #include <game/server/entities/character.h> |
9 | #include <game/server/gamecontext.h> |
10 | #include <game/server/player.h> |
11 | #include <game/server/score.h> |
12 | #include <game/version.h> |
13 | |
14 | #define GAME_TYPE_NAME "DDraceNetwork" |
15 | #define TEST_TYPE_NAME "TestDDraceNetwork" |
16 | |
17 | CGameControllerDDRace::CGameControllerDDRace(class CGameContext *pGameServer) : |
18 | IGameController(pGameServer) |
19 | { |
20 | m_pGameType = g_Config.m_SvTestingCommands ? TEST_TYPE_NAME : GAME_TYPE_NAME; |
21 | } |
22 | |
23 | CGameControllerDDRace::~CGameControllerDDRace() = default; |
24 | |
25 | CScore *CGameControllerDDRace::Score() |
26 | { |
27 | return GameServer()->Score(); |
28 | } |
29 | |
30 | void CGameControllerDDRace::HandleCharacterTiles(CCharacter *pChr, int MapIndex) |
31 | { |
32 | CPlayer *pPlayer = pChr->GetPlayer(); |
33 | const int ClientId = pPlayer->GetCid(); |
34 | |
35 | int m_TileIndex = GameServer()->Collision()->GetTileIndex(Index: MapIndex); |
36 | int m_TileFIndex = GameServer()->Collision()->GetFTileIndex(Index: MapIndex); |
37 | |
38 | //Sensitivity |
39 | int S1 = GameServer()->Collision()->GetPureMapIndex(Pos: vec2(pChr->GetPos().x + pChr->GetProximityRadius() / 3.f, pChr->GetPos().y - pChr->GetProximityRadius() / 3.f)); |
40 | int S2 = GameServer()->Collision()->GetPureMapIndex(Pos: vec2(pChr->GetPos().x + pChr->GetProximityRadius() / 3.f, pChr->GetPos().y + pChr->GetProximityRadius() / 3.f)); |
41 | int S3 = GameServer()->Collision()->GetPureMapIndex(Pos: vec2(pChr->GetPos().x - pChr->GetProximityRadius() / 3.f, pChr->GetPos().y - pChr->GetProximityRadius() / 3.f)); |
42 | int S4 = GameServer()->Collision()->GetPureMapIndex(Pos: vec2(pChr->GetPos().x - pChr->GetProximityRadius() / 3.f, pChr->GetPos().y + pChr->GetProximityRadius() / 3.f)); |
43 | int Tile1 = GameServer()->Collision()->GetTileIndex(Index: S1); |
44 | int Tile2 = GameServer()->Collision()->GetTileIndex(Index: S2); |
45 | int Tile3 = GameServer()->Collision()->GetTileIndex(Index: S3); |
46 | int Tile4 = GameServer()->Collision()->GetTileIndex(Index: S4); |
47 | int FTile1 = GameServer()->Collision()->GetFTileIndex(Index: S1); |
48 | int FTile2 = GameServer()->Collision()->GetFTileIndex(Index: S2); |
49 | int FTile3 = GameServer()->Collision()->GetFTileIndex(Index: S3); |
50 | int FTile4 = GameServer()->Collision()->GetFTileIndex(Index: S4); |
51 | |
52 | const int PlayerDDRaceState = pChr->m_DDRaceState; |
53 | bool IsOnStartTile = (m_TileIndex == TILE_START) || (m_TileFIndex == TILE_START) || FTile1 == TILE_START || FTile2 == TILE_START || FTile3 == TILE_START || FTile4 == TILE_START || Tile1 == TILE_START || Tile2 == TILE_START || Tile3 == TILE_START || Tile4 == TILE_START; |
54 | // start |
55 | if(IsOnStartTile && PlayerDDRaceState != DDRACE_CHEAT) |
56 | { |
57 | const int Team = GameServer()->GetDDRaceTeam(ClientId); |
58 | if(Teams().GetSaving(TeamId: Team)) |
59 | { |
60 | GameServer()->SendStartWarning(ClientId, pMessage: "You can't start while loading/saving of team is in progress" ); |
61 | pChr->Die(Killer: ClientId, Weapon: WEAPON_WORLD); |
62 | return; |
63 | } |
64 | if(g_Config.m_SvTeam == SV_TEAM_MANDATORY && (Team == TEAM_FLOCK || Teams().Count(Team) <= 1)) |
65 | { |
66 | GameServer()->SendStartWarning(ClientId, pMessage: "You have to be in a team with other tees to start" ); |
67 | pChr->Die(Killer: ClientId, Weapon: WEAPON_WORLD); |
68 | return; |
69 | } |
70 | if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && Team > TEAM_FLOCK && Team < TEAM_SUPER && Teams().Count(Team) < g_Config.m_SvMinTeamSize && !Teams().TeamFlock(Team)) |
71 | { |
72 | char aBuf[128]; |
73 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Your team has fewer than %d players, so your team rank won't count" , g_Config.m_SvMinTeamSize); |
74 | GameServer()->SendStartWarning(ClientId, pMessage: aBuf); |
75 | } |
76 | if(g_Config.m_SvResetPickups) |
77 | { |
78 | pChr->ResetPickups(); |
79 | } |
80 | |
81 | Teams().OnCharacterStart(ClientId); |
82 | pChr->m_LastTimeCp = -1; |
83 | pChr->m_LastTimeCpBroadcasted = -1; |
84 | for(float &CurrentTimeCp : pChr->m_aCurrentTimeCp) |
85 | { |
86 | CurrentTimeCp = 0.0f; |
87 | } |
88 | } |
89 | |
90 | // finish |
91 | if(((m_TileIndex == TILE_FINISH) || (m_TileFIndex == TILE_FINISH) || FTile1 == TILE_FINISH || FTile2 == TILE_FINISH || FTile3 == TILE_FINISH || FTile4 == TILE_FINISH || Tile1 == TILE_FINISH || Tile2 == TILE_FINISH || Tile3 == TILE_FINISH || Tile4 == TILE_FINISH) && PlayerDDRaceState == DDRACE_STARTED) |
92 | Teams().OnCharacterFinish(ClientId); |
93 | |
94 | // unlock team |
95 | else if(((m_TileIndex == TILE_UNLOCK_TEAM) || (m_TileFIndex == TILE_UNLOCK_TEAM)) && Teams().TeamLocked(Team: GameServer()->GetDDRaceTeam(ClientId))) |
96 | { |
97 | Teams().SetTeamLock(Team: GameServer()->GetDDRaceTeam(ClientId), Lock: false); |
98 | GameServer()->SendChatTeam(Team: GameServer()->GetDDRaceTeam(ClientId), pText: "Your team was unlocked by an unlock team tile" ); |
99 | } |
100 | |
101 | // solo part |
102 | if(((m_TileIndex == TILE_SOLO_ENABLE) || (m_TileFIndex == TILE_SOLO_ENABLE)) && !Teams().m_Core.GetSolo(ClientId)) |
103 | { |
104 | GameServer()->SendChatTarget(To: ClientId, pText: "You are now in a solo part" ); |
105 | pChr->SetSolo(true); |
106 | } |
107 | else if(((m_TileIndex == TILE_SOLO_DISABLE) || (m_TileFIndex == TILE_SOLO_DISABLE)) && Teams().m_Core.GetSolo(ClientId)) |
108 | { |
109 | GameServer()->SendChatTarget(To: ClientId, pText: "You are now out of the solo part" ); |
110 | pChr->SetSolo(false); |
111 | } |
112 | } |
113 | |
114 | void CGameControllerDDRace::OnPlayerConnect(CPlayer *pPlayer) |
115 | { |
116 | IGameController::OnPlayerConnect(pPlayer); |
117 | int ClientId = pPlayer->GetCid(); |
118 | |
119 | // init the player |
120 | Score()->PlayerData(Id: ClientId)->Reset(); |
121 | |
122 | // Can't set score here as LoadScore() is threaded, run it in |
123 | // LoadScoreThreaded() instead |
124 | Score()->LoadPlayerData(ClientId); |
125 | |
126 | if(!Server()->ClientPrevIngame(ClientId)) |
127 | { |
128 | char aBuf[512]; |
129 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' entered and joined the %s" , Server()->ClientName(ClientId), GetTeamName(Team: pPlayer->GetTeam())); |
130 | GameServer()->SendChat(ClientId: -1, Team: TEAM_ALL, pText: aBuf, SpamProtectionClientId: -1, Flags: CGameContext::CHAT_SIX); |
131 | |
132 | GameServer()->SendChatTarget(To: ClientId, pText: "DDraceNetwork Mod. Version: " GAME_VERSION); |
133 | GameServer()->SendChatTarget(To: ClientId, pText: "please visit DDNet.org or say /info and make sure to read our /rules" ); |
134 | } |
135 | } |
136 | |
137 | void CGameControllerDDRace::OnPlayerDisconnect(CPlayer *pPlayer, const char *pReason) |
138 | { |
139 | int ClientId = pPlayer->GetCid(); |
140 | bool WasModerator = pPlayer->m_Moderating && Server()->ClientIngame(ClientId); |
141 | |
142 | IGameController::OnPlayerDisconnect(pPlayer, pReason); |
143 | |
144 | if(!GameServer()->PlayerModerating() && WasModerator) |
145 | GameServer()->SendChat(ClientId: -1, Team: TEAM_ALL, pText: "Server kick/spec votes are no longer actively moderated." ); |
146 | |
147 | if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO) |
148 | Teams().SetForceCharacterTeam(ClientId, Team: TEAM_FLOCK); |
149 | |
150 | for(int Team = TEAM_FLOCK + 1; Team < TEAM_SUPER; Team++) |
151 | if(Teams().IsInvited(Team, ClientId)) |
152 | Teams().SetClientInvited(Team, ClientId, Invited: false); |
153 | } |
154 | |
155 | void CGameControllerDDRace::OnReset() |
156 | { |
157 | IGameController::OnReset(); |
158 | Teams().Reset(); |
159 | } |
160 | |
161 | void CGameControllerDDRace::Tick() |
162 | { |
163 | IGameController::Tick(); |
164 | Teams().ProcessSaveTeam(); |
165 | Teams().Tick(); |
166 | } |
167 | |
168 | void CGameControllerDDRace::DoTeamChange(class CPlayer *pPlayer, int Team, bool DoChatMsg) |
169 | { |
170 | Team = ClampTeam(Team); |
171 | if(Team == pPlayer->GetTeam()) |
172 | return; |
173 | |
174 | CCharacter *pCharacter = pPlayer->GetCharacter(); |
175 | |
176 | if(Team == TEAM_SPECTATORS) |
177 | { |
178 | if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && pCharacter) |
179 | { |
180 | // Joining spectators should not kill a locked team, but should still |
181 | // check if the team finished by you leaving it. |
182 | int DDRTeam = pCharacter->Team(); |
183 | Teams().SetForceCharacterTeam(ClientId: pPlayer->GetCid(), Team: TEAM_FLOCK); |
184 | Teams().CheckTeamFinished(Team: DDRTeam); |
185 | } |
186 | } |
187 | |
188 | IGameController::DoTeamChange(pPlayer, Team, DoChatMsg); |
189 | } |
190 | |