1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef GAME_SERVER_GAMECONTROLLER_H
4#define GAME_SERVER_GAMECONTROLLER_H
5
6#include <base/dbg.h>
7#include <base/vmath.h>
8
9#include <engine/map.h>
10#include <engine/shared/protocol.h>
11
12#include <generated/protocol.h>
13
14#include <game/server/teams.h>
15
16struct CScoreLoadBestTimeResult;
17
18/*
19 Class: Game Controller
20 Controls the main game logic. Keeping track of team and player score,
21 winning conditions and specific game logic.
22*/
23class IGameController
24{
25 friend class CSaveTeam; // need access to GameServer() and Server()
26
27protected:
28 enum ESpawnType
29 {
30 SPAWNTYPE_DEFAULT = 0,
31 SPAWNTYPE_RED,
32 SPAWNTYPE_BLUE,
33
34 NUM_SPAWNTYPES
35 };
36
37private:
38 std::vector<vec2> m_avSpawnPoints[NUM_SPAWNTYPES];
39
40 class CGameContext *m_pGameServer;
41 class CConfig *m_pConfig;
42 class IServer *m_pServer;
43
44 CGameTeams m_Teams;
45
46protected:
47 CGameContext *GameServer() const { return m_pGameServer; }
48 CConfig *Config() { return m_pConfig; }
49 IServer *Server() const { return m_pServer; }
50
51 void DoActivityCheck();
52
53 struct CSpawnEval
54 {
55 CSpawnEval()
56 {
57 m_Got = false;
58 m_FriendlyTeam = -1;
59 m_Pos = vec2(100, 100);
60 }
61
62 vec2 m_Pos;
63 bool m_Got;
64 int m_FriendlyTeam;
65 float m_Score;
66 };
67
68 float EvaluateSpawnPos(CSpawnEval *pEval, vec2 Pos, int ClientId);
69 void EvaluateSpawnType(CSpawnEval *pEval, ESpawnType SpawnType, int ClientId);
70
71 void ResetGame();
72
73 char m_aMapWish[MAX_MAP_LENGTH];
74
75 int m_RoundStartTick;
76 int m_GameOverTick;
77 int m_SuddenDeath;
78
79 int m_Warmup;
80 int m_RoundCount;
81
82 int m_GameFlags;
83
84public:
85 const char *m_pGameType;
86
87 IGameController(class CGameContext *pGameServer);
88 virtual ~IGameController();
89
90 // event
91 /*
92 Function: OnCharacterDeath
93 Called when a CCharacter in the world dies.
94
95 Arguments:
96 victim - The CCharacter that died.
97 killer - The player that killed it.
98 weapon - What weapon that killed it. Can be -1 for undefined
99 weapon when switching team or player suicides.
100 */
101 virtual int OnCharacterDeath(class CCharacter *pVictim, class CPlayer *pKiller, int Weapon);
102 /*
103 Function: OnCharacterSpawn
104 Called when a CCharacter spawns into the game world.
105
106 Arguments:
107 chr - The CCharacter that was spawned.
108 */
109 virtual void OnCharacterSpawn(class CCharacter *pChr);
110
111 virtual void HandleCharacterTiles(class CCharacter *pChr, int MapIndex);
112 virtual void SetArmorProgress(CCharacter *pCharacter, int Progress) {}
113
114 /*
115 Function: OnEntity
116 Called when the map is loaded to process an entity
117 in the map.
118
119 Arguments:
120 index - Entity index.
121 pos - Where the entity is located in the world.
122
123 Returns:
124 bool?
125 */
126 virtual bool OnEntity(int Index, int x, int y, int Layer, int Flags, bool Initial, int Number = 0);
127
128 virtual void OnPlayerConnect(class CPlayer *pPlayer);
129 virtual void OnPlayerDisconnect(class CPlayer *pPlayer, const char *pReason);
130
131 virtual void OnReset();
132
133 // game
134 virtual void DoWarmup(int Seconds);
135
136 void StartRound();
137 void EndRound();
138 void ChangeMap(const char *pToMap);
139
140 /*
141
142 */
143 virtual void Tick();
144
145 virtual void Snap(int SnappingClient);
146
147 /**
148 * Sets the score value that will be shown in the scoreboard.
149 *
150 * @param SnappingClient Client ID of the player that will receive the snapshot.
151 * @param pPlayer Player that is being snapped.
152 *
153 * @return the score value that will be included in the snapshot.
154 */
155 virtual int SnapPlayerScore(int SnappingClient, CPlayer *pPlayer) { return 0; }
156
157 class CFinishTime
158 {
159 public:
160 CFinishTime(int Seconds, int Milliseconds) :
161 m_Seconds(Seconds), m_Milliseconds(Milliseconds)
162 {
163 dbg_assert(Seconds >= 0, "Invalid Seconds: %d", Seconds);
164 dbg_assert(Milliseconds >= 0 && Milliseconds < 1000, "Invalid Milliseconds: %d", Milliseconds);
165 }
166
167 int m_Seconds;
168 int m_Milliseconds;
169
170 static CFinishTime Unset() { return CFinishTime(FinishTime::UNSET); }
171 static CFinishTime NotFinished() { return CFinishTime(FinishTime::NOT_FINISHED_MILLIS); }
172
173 private:
174 CFinishTime(int Type)
175 {
176 m_Seconds = Type;
177 m_Milliseconds = 0;
178 }
179 };
180
181 /**
182 * Returns the finish time value that will be shown in the scoreboard.
183 *
184 * @param SnappingClient Client ID of the player that will receive the snapshot.
185 * @param pPlayer Player that is being snapped.
186 *
187 * @return The time split into seconds and the milliseconds remainder, use CFinishTime::Unset if you want the server to prefer scores.
188 */
189 virtual CFinishTime SnapPlayerTime(int SnappingClient, CPlayer *pPlayer) { return CFinishTime::Unset(); }
190
191 /**
192 * Snaps the current server record / best time of the current map.
193 *
194 * @param SnappingClient Client ID of the player that will receive the snapshot.
195 *
196 * @return The the map best time split into seconds and the milliseconds remainder, use CFinishTime::Unset if you want the server to prefer scores.
197 */
198 virtual CFinishTime SnapMapBestTime(int SnappingClient) { return CFinishTime::Unset(); }
199
200 // spawn
201 virtual bool CanSpawn(int Team, vec2 *pOutPos, int ClientId);
202
203 virtual void DoTeamChange(class CPlayer *pPlayer, int Team, bool DoChatMsg = true);
204
205 int TileFlagsToPickupFlags(int TileFlags) const;
206
207 /*
208
209 */
210 virtual bool IsValidTeam(int Team);
211 virtual const char *GetTeamName(int Team);
212 virtual int GetAutoTeam(int NotThisId);
213 virtual bool CanJoinTeam(int Team, int NotThisId, char *pErrorReason, int ErrorReasonSize);
214
215 CClientMask GetMaskForPlayerWorldEvent(int Asker, int ExceptID = -1);
216
217 bool IsTeamPlay() const { return m_GameFlags & GAMEFLAG_TEAMS; }
218 // DDRace
219
220 std::optional<float> m_CurrentRecord;
221 CGameTeams &Teams() { return m_Teams; }
222 std::shared_ptr<CScoreLoadBestTimeResult> m_pLoadBestTimeResult;
223};
224
225#endif
226