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