1 | /* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ |
2 | #ifndef GAME_SERVER_TEAMS_H |
3 | #define GAME_SERVER_TEAMS_H |
4 | |
5 | #include <engine/shared/config.h> |
6 | #include <game/server/gamecontext.h> |
7 | #include <game/teamscore.h> |
8 | |
9 | class CCharacter; |
10 | class CPlayer; |
11 | struct CScoreSaveResult; |
12 | |
13 | class CGameTeams |
14 | { |
15 | // `m_TeeStarted` is used to keep track whether a given tee has hit the |
16 | // start of the map yet. If a tee that leaves hasn't hit the start line |
17 | // yet, the team will be marked as "not allowed to finish" |
18 | // (`TEAMSTATE_STARTED_UNFINISHABLE`). If this were not the case, tees |
19 | // could go around the startline on a map, leave one tee behind at |
20 | // start, go to the finish line, let the tee start and kill, allowing |
21 | // the team to finish instantly. |
22 | bool m_aTeeStarted[MAX_CLIENTS]; |
23 | bool m_aTeeFinished[MAX_CLIENTS]; |
24 | int m_aLastChat[MAX_CLIENTS]; |
25 | |
26 | int m_aTeamState[NUM_DDRACE_TEAMS]; |
27 | bool m_aTeamLocked[NUM_DDRACE_TEAMS]; |
28 | bool m_aTeamFlock[NUM_DDRACE_TEAMS]; |
29 | CClientMask m_aInvited[NUM_DDRACE_TEAMS]; |
30 | bool m_aPractice[NUM_DDRACE_TEAMS]; |
31 | std::shared_ptr<CScoreSaveResult> m_apSaveTeamResult[NUM_DDRACE_TEAMS]; |
32 | uint64_t m_aLastSwap[MAX_CLIENTS]; // index is id of player who initiated swap |
33 | bool m_aTeamSentStartWarning[NUM_DDRACE_TEAMS]; |
34 | // `m_aTeamUnfinishableKillTick` is -1 by default and gets set when a |
35 | // team becomes unfinishable. If the team hasn't entered practice mode |
36 | // by that time, it'll get killed to prevent people not understanding |
37 | // the message from playing for a long time in an unfinishable team. |
38 | int m_aTeamUnfinishableKillTick[NUM_DDRACE_TEAMS]; |
39 | |
40 | class CGameContext *m_pGameContext; |
41 | |
42 | /** |
43 | * Kill the whole team. |
44 | * @param Team The team id to kill |
45 | * @param NewStrongId The player with that id will get strong hook on everyone else, -1 will set the normal spawning order |
46 | * @param ExceptId The player that should not get killed |
47 | */ |
48 | void KillTeam(int Team, int NewStrongId, int ExceptId = -1); |
49 | bool TeamFinished(int Team); |
50 | void OnTeamFinish(int Team, CPlayer **Players, unsigned int Size, int TimeTicks, const char *pTimestamp); |
51 | void OnFinish(CPlayer *Player, int TimeTicks, const char *pTimestamp); |
52 | |
53 | public: |
54 | enum |
55 | { |
56 | TEAMSTATE_EMPTY, |
57 | TEAMSTATE_OPEN, |
58 | TEAMSTATE_STARTED, |
59 | // Happens when a tee that hasn't hit the start tiles leaves |
60 | // the team. |
61 | TEAMSTATE_STARTED_UNFINISHABLE, |
62 | TEAMSTATE_FINISHED |
63 | }; |
64 | |
65 | CTeamsCore m_Core; |
66 | |
67 | CGameTeams(CGameContext *pGameContext); |
68 | |
69 | // helper methods |
70 | CCharacter *Character(int ClientId) |
71 | { |
72 | return GameServer()->GetPlayerChar(ClientId); |
73 | } |
74 | CPlayer *GetPlayer(int ClientId) |
75 | { |
76 | return GameServer()->m_apPlayers[ClientId]; |
77 | } |
78 | |
79 | class CGameContext *GameServer() |
80 | { |
81 | return m_pGameContext; |
82 | } |
83 | class IServer *Server() |
84 | { |
85 | return m_pGameContext->Server(); |
86 | } |
87 | |
88 | void OnCharacterStart(int ClientId); |
89 | void OnCharacterFinish(int ClientId); |
90 | void OnCharacterSpawn(int ClientId); |
91 | void OnCharacterDeath(int ClientId, int Weapon); |
92 | void Tick(); |
93 | |
94 | // returns nullptr if successful, error string if failed |
95 | const char *SetCharacterTeam(int ClientId, int Team); |
96 | void CheckTeamFinished(int Team); |
97 | |
98 | void ChangeTeamState(int Team, int State); |
99 | |
100 | CClientMask TeamMask(int Team, int ExceptId = -1, int Asker = -1); |
101 | |
102 | int Count(int Team) const; |
103 | |
104 | // need to be very careful using this method. SERIOUSLY... |
105 | void SetForceCharacterTeam(int ClientId, int Team); |
106 | |
107 | void Reset(); |
108 | void ResetRoundState(int Team); |
109 | void ResetSwitchers(int Team); |
110 | |
111 | void SendTeamsState(int ClientId); |
112 | void SetTeamLock(int Team, bool Lock); |
113 | void SetTeamFlock(int Team, bool Mode); |
114 | void ResetInvited(int Team); |
115 | void SetClientInvited(int Team, int ClientId, bool Invited); |
116 | |
117 | int GetDDRaceState(CPlayer *Player); |
118 | int GetStartTime(CPlayer *Player); |
119 | float *GetCurrentTimeCp(CPlayer *Player); |
120 | void SetDDRaceState(CPlayer *Player, int DDRaceState); |
121 | void SetStartTime(CPlayer *Player, int StartTime); |
122 | void SetLastTimeCp(CPlayer *Player, int LastTimeCp); |
123 | void KillSavedTeam(int ClientId, int Team); |
124 | void ResetSavedTeam(int ClientId, int Team); |
125 | void RequestTeamSwap(CPlayer *pPlayer, CPlayer *pTargetPlayer, int Team); |
126 | void SwapTeamCharacters(CPlayer *pPrimaryPlayer, CPlayer *pTargetPlayer, int Team); |
127 | void ProcessSaveTeam(); |
128 | |
129 | int GetFirstEmptyTeam() const; |
130 | |
131 | bool TeeStarted(int ClientId) |
132 | { |
133 | return m_aTeeStarted[ClientId]; |
134 | } |
135 | |
136 | bool TeeFinished(int ClientId) |
137 | { |
138 | return m_aTeeFinished[ClientId]; |
139 | } |
140 | |
141 | int GetTeamState(int Team) |
142 | { |
143 | return m_aTeamState[Team]; |
144 | } |
145 | |
146 | bool TeamLocked(int Team) |
147 | { |
148 | if(Team <= TEAM_FLOCK || Team >= TEAM_SUPER) |
149 | return false; |
150 | |
151 | return m_aTeamLocked[Team]; |
152 | } |
153 | |
154 | bool TeamFlock(int Team) |
155 | { |
156 | if(Team <= TEAM_FLOCK || Team >= TEAM_SUPER) |
157 | return false; |
158 | |
159 | return m_aTeamFlock[Team]; |
160 | } |
161 | |
162 | bool IsInvited(int Team, int ClientId) |
163 | { |
164 | return m_aInvited[Team].test(position: ClientId); |
165 | } |
166 | |
167 | bool IsStarted(int Team) |
168 | { |
169 | return m_aTeamState[Team] == CGameTeams::TEAMSTATE_STARTED; |
170 | } |
171 | |
172 | void SetStarted(int ClientId, bool Started) |
173 | { |
174 | m_aTeeStarted[ClientId] = Started; |
175 | } |
176 | |
177 | void SetFinished(int ClientId, bool Finished) |
178 | { |
179 | m_aTeeFinished[ClientId] = Finished; |
180 | } |
181 | |
182 | void SetSaving(int TeamId, std::shared_ptr<CScoreSaveResult> &SaveResult) |
183 | { |
184 | m_apSaveTeamResult[TeamId] = SaveResult; |
185 | } |
186 | |
187 | bool GetSaving(int TeamId) |
188 | { |
189 | if(TeamId < TEAM_FLOCK || TeamId >= TEAM_SUPER) |
190 | return false; |
191 | if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && TeamId == TEAM_FLOCK) |
192 | return false; |
193 | |
194 | return m_apSaveTeamResult[TeamId] != nullptr; |
195 | } |
196 | |
197 | void SetPractice(int Team, bool Enabled) |
198 | { |
199 | if(Team < TEAM_FLOCK || Team >= TEAM_SUPER) |
200 | return; |
201 | if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && Team == TEAM_FLOCK) |
202 | return; |
203 | |
204 | m_aPractice[Team] = Enabled; |
205 | } |
206 | |
207 | bool IsPractice(int Team) |
208 | { |
209 | if(Team < TEAM_FLOCK || Team >= TEAM_SUPER) |
210 | return false; |
211 | if(g_Config.m_SvTeam != SV_TEAM_FORCED_SOLO && Team == TEAM_FLOCK) |
212 | return false; |
213 | |
214 | return m_aPractice[Team]; |
215 | } |
216 | }; |
217 | |
218 | #endif |
219 | |