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/protocol.h>
6
7#include <game/race_state.h>
8#include <game/server/gamecontext.h>
9#include <game/team_state.h>
10#include <game/teamscore.h>
11
12class CCharacter;
13class CPlayer;
14struct CScoreSaveResult;
15
16class CGameTeams
17{
18 // `m_TeeStarted` is used to keep track whether a given tee has hit the
19 // start of the map yet. If a tee that leaves hasn't hit the start line
20 // yet, the team will be marked as "not allowed to finish"
21 // (`ETeamState::STARTED_UNFINISHABLE`). If this were not the case, tees
22 // could go around the startline on a map, leave one tee behind at
23 // start, go to the finish line, let the tee start and kill, allowing
24 // the team to finish instantly.
25 bool m_aTeeStarted[MAX_CLIENTS];
26 bool m_aTeeFinished[MAX_CLIENTS];
27 int m_aLastChat[MAX_CLIENTS];
28
29 ETeamState m_aTeamState[NUM_DDRACE_TEAMS];
30 bool m_aTeamLocked[NUM_DDRACE_TEAMS];
31 bool m_aTeamFlock[NUM_DDRACE_TEAMS];
32 CClientMask m_aInvited[NUM_DDRACE_TEAMS];
33 bool m_aPractice[NUM_DDRACE_TEAMS];
34 std::shared_ptr<CScoreSaveResult> m_apSaveTeamResult[NUM_DDRACE_TEAMS];
35 uint64_t m_aLastSwap[MAX_CLIENTS]; // index is id of player who initiated swap
36 bool m_aTeamSentStartWarning[NUM_DDRACE_TEAMS];
37 // `m_aTeamUnfinishableKillTick` is -1 by default and gets set when a
38 // team becomes unfinishable. If the team hasn't entered practice mode
39 // by that time, it'll get killed to prevent people not understanding
40 // the message from playing for a long time in an unfinishable team.
41 int m_aTeamUnfinishableKillTick[NUM_DDRACE_TEAMS];
42
43 CGameContext *m_pGameContext;
44
45 /**
46 * Kill the whole team.
47 * @param Team The team id to kill
48 * @param NewStrongId The player with that id will get strong hook on everyone else, -1 will set the normal spawning order
49 * @param ExceptId The player that should not get killed
50 */
51 void KillTeam(int Team, int NewStrongId, int ExceptId = -1);
52 bool TeamFinished(int Team);
53 void OnTeamFinish(int Team, CPlayer **Players, unsigned int Size, int TimeTicks, const char *pTimestamp);
54 void OnFinish(CPlayer *Player, int TimeTicks, const char *pTimestamp);
55
56public:
57 CTeamsCore m_Core;
58
59 CGameTeams(CGameContext *pGameContext);
60
61 // helper methods
62 CCharacter *Character(int ClientId);
63 const CCharacter *Character(int ClientId) const;
64 CPlayer *GetPlayer(int ClientId);
65 CGameContext *GameServer();
66 const CGameContext *GameServer() const;
67 class IServer *Server();
68
69 void OnCharacterStart(int ClientId);
70 void OnCharacterFinish(int ClientId);
71 void OnCharacterSpawn(int ClientId);
72 void OnCharacterDeath(int ClientId, int Weapon);
73 void Tick();
74
75 // sets pError to an empty string on success (true)
76 // and sets pError if it returns false
77 bool CanJoinTeam(int ClientId, int Team, char *pError, int ErrorSize) const;
78
79 // returns true if successful. Writes error into pError on failure
80 bool SetCharacterTeam(int ClientId, int Team, char *pError, int ErrorSize);
81 void CheckTeamFinished(int Team);
82
83 void ChangeTeamState(int Team, ETeamState State);
84
85 CClientMask TeamMask(int Team, int ExceptId = -1, int Asker = -1, int VersionFlags = CGameContext::FLAG_SIX | CGameContext::FLAG_SIXUP);
86
87 int Count(int Team) const;
88
89 // need to be very careful using this method. SERIOUSLY...
90 void SetForceCharacterTeam(int ClientId, int Team);
91
92 void Reset();
93 void ResetRoundState(int Team);
94 void ResetSwitchers(int Team);
95
96 void SendTeamsState(int ClientId);
97 void SetTeamLock(int Team, bool Lock);
98 void SetTeamFlock(int Team, bool Mode);
99 void ResetInvited(int Team);
100 void SetClientInvited(int Team, int ClientId, bool Invited);
101
102 ERaceState GetDDRaceState(const CPlayer *Player) const;
103 int GetStartTime(CPlayer *Player);
104 float *GetCurrentTimeCp(CPlayer *Player);
105 void SetDDRaceState(CPlayer *Player, ERaceState DDRaceState);
106 void SetStartTime(CPlayer *Player, int StartTime);
107 void SetLastTimeCp(CPlayer *Player, int LastTimeCp);
108 void KillCharacterOrTeam(int ClientId, int Team);
109 void ResetSavedTeam(int ClientId, int Team);
110 void RequestTeamSwap(CPlayer *pPlayer, CPlayer *pTargetPlayer, int Team);
111 void SwapTeamCharacters(CPlayer *pPrimaryPlayer, CPlayer *pTargetPlayer, int Team);
112 void CancelTeamSwap(CPlayer *pPlayer, int Team);
113 void ProcessSaveTeam();
114 std::optional<int> GetFirstEmptyTeam() const;
115 bool TeeStarted(int ClientId) const;
116 bool TeeFinished(int ClientId) const;
117 ETeamState GetTeamState(int Team) const;
118 bool TeamLocked(int Team) const;
119 bool TeamFlock(int Team) const;
120 bool IsInvited(int Team, int ClientId) const;
121 bool IsStarted(int Team) const;
122 void SetStarted(int ClientId, bool Started);
123 void SetFinished(int ClientId, bool Finished);
124 void SetSaving(int TeamId, std::shared_ptr<CScoreSaveResult> &SaveResult);
125 bool GetSaving(int TeamId) const;
126 void SetPractice(int Team, bool Enabled);
127 bool IsPractice(int Team);
128 bool IsValidTeamNumber(int Team) const;
129};
130
131#endif
132