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_GAMECORE_H
4#define GAME_GAMECORE_H
5
6#include "prng.h"
7
8#include <base/vmath.h>
9
10#include <engine/shared/protocol.h>
11
12#include <generated/protocol.h>
13
14#include <game/teamscore.h>
15
16#include <set>
17#include <vector>
18
19class CCollision;
20class CTeamsCore;
21
22class CTuneParam
23{
24 int m_Value;
25
26public:
27 void Set(int v) { m_Value = v; }
28 int Get() const { return m_Value; }
29 CTuneParam &operator=(int v)
30 {
31 m_Value = (int)(v * 100.0f);
32 return *this;
33 }
34 CTuneParam &operator=(float v)
35 {
36 m_Value = (int)(v * 100.0f);
37 return *this;
38 }
39 operator float() const { return m_Value / 100.0f; }
40};
41
42class CTuningParams
43{
44 static const char *ms_apNames[];
45
46public:
47 CTuningParams()
48 {
49#define MACRO_TUNING_PARAM(Name, ScriptName, Value, Description) m_##Name.Set((int)((Value) * 100.0f));
50#include "tuning.h"
51#undef MACRO_TUNING_PARAM
52 }
53
54#define MACRO_TUNING_PARAM(Name, ScriptName, Value, Description) CTuneParam m_##Name;
55#include "tuning.h"
56#undef MACRO_TUNING_PARAM
57
58 static int Num()
59 {
60 return sizeof(CTuningParams) / sizeof(int);
61 }
62 int *NetworkArray() { return (int *)this; }
63 const int *NetworkArray() const { return (const int *)this; }
64 bool Set(int Index, float Value);
65 bool Set(const char *pName, float Value);
66 bool Get(int Index, float *pValue) const;
67 bool Get(const char *pName, float *pValue) const;
68 static const char *Name(int Index) { return ms_apNames[Index]; }
69 float GetWeaponFireDelay(int Weapon) const;
70
71 static const CTuningParams DEFAULT;
72};
73
74// Do not use these function unless for legacy code!
75void StrToInts(int *pInts, size_t NumInts, const char *pStr);
76bool IntsToStr(const int *pInts, size_t NumInts, char *pStr, size_t StrSize);
77
78inline vec2 CalcPos(vec2 Pos, vec2 Velocity, float Curvature, float Speed, float Time)
79{
80 vec2 n;
81 Time *= Speed;
82 n.x = Pos.x + Velocity.x * Time;
83 n.y = Pos.y + Velocity.y * Time + Curvature / 10000 * (Time * Time);
84 return n;
85}
86
87template<typename T>
88inline T SaturatedAdd(T Min, T Max, T Current, T Modifier)
89{
90 if(Modifier < 0)
91 {
92 if(Current < Min)
93 return Current;
94 Current += Modifier;
95 if(Current < Min)
96 Current = Min;
97 return Current;
98 }
99 else
100 {
101 if(Current > Max)
102 return Current;
103 Current += Modifier;
104 if(Current > Max)
105 Current = Max;
106 return Current;
107 }
108}
109
110float VelocityRamp(float Value, float Start, float Range, float Curvature);
111
112// hooking stuff
113enum
114{
115 HOOK_RETRACTED = -1,
116 HOOK_IDLE = 0,
117 HOOK_RETRACT_START = 1,
118 HOOK_RETRACT_END = 3,
119 HOOK_FLYING = 4,
120 HOOK_GRABBED = 5,
121
122 COREEVENT_GROUND_JUMP = 0x01,
123 COREEVENT_AIR_JUMP = 0x02,
124 COREEVENT_HOOK_LAUNCH = 0x04,
125 COREEVENT_HOOK_ATTACH_PLAYER = 0x08,
126 COREEVENT_HOOK_ATTACH_GROUND = 0x10,
127 COREEVENT_HOOK_HIT_NOHOOK = 0x20,
128 COREEVENT_HOOK_RETRACT = 0x40,
129};
130
131// show others values - do not change them
132enum
133{
134 SHOW_OTHERS_NOT_SET = -1, // show others value before it is set
135 SHOW_OTHERS_OFF = 0, // show no other players in solo or other teams
136 SHOW_OTHERS_ON = 1, // show all other players in solo and other teams
137 SHOW_OTHERS_ONLY_TEAM = 2 // show players that are in solo and are in the same team
138};
139
140struct SSwitchers
141{
142 bool m_aStatus[NUM_DDRACE_TEAMS];
143 bool m_Initial;
144 int m_aEndTick[NUM_DDRACE_TEAMS];
145 int m_aType[NUM_DDRACE_TEAMS];
146 int m_aLastUpdateTick[NUM_DDRACE_TEAMS];
147};
148
149class CWorldCore
150{
151public:
152 CWorldCore()
153 {
154 for(auto &pCharacter : m_apCharacters)
155 {
156 pCharacter = nullptr;
157 }
158 m_pPrng = nullptr;
159 }
160
161 int RandomOr0(int BelowThis) // NOLINT(readability-make-member-function-const)
162 {
163 if(BelowThis <= 1 || !m_pPrng)
164 {
165 return 0;
166 }
167 // This makes the random number slightly biased if `BelowThis`
168 // is not a power of two, but we have decided that this is not
169 // significant for DDNet and favored the simple implementation.
170 return m_pPrng->RandomBits() % BelowThis;
171 }
172
173 class CCharacterCore *m_apCharacters[MAX_CLIENTS];
174 CPrng *m_pPrng;
175
176 void InitSwitchers(int HighestSwitchNumber);
177 std::vector<SSwitchers> m_vSwitchers;
178};
179
180typedef std::function<void(int ClientId, bool DisallowReset)> FAntiPingInterfereCallback;
181
182class CCharacterCore
183{
184 CWorldCore *m_pWorld = nullptr;
185 CCollision *m_pCollision;
186
187public:
188 static constexpr float PhysicalSize() { return 28.0f; }
189 static constexpr vec2 PhysicalSizeVec2() { return vec2(28.0f, 28.0f); }
190 vec2 m_Pos;
191 vec2 m_Vel;
192
193 vec2 m_HookPos;
194 vec2 m_HookDir;
195 vec2 m_HookTeleBase;
196 int m_HookTick;
197 int m_HookState;
198 std::set<int> m_AttachedPlayers;
199 int HookedPlayer() const { return m_HookedPlayer; }
200 void SetHookedPlayer(int HookedPlayer);
201
202 int m_ActiveWeapon;
203 class CWeaponStat
204 {
205 public:
206 int m_AmmoRegenStart;
207 int m_Ammo;
208 int m_Ammocost;
209 bool m_Got;
210 } m_aWeapons[NUM_WEAPONS];
211
212 // ninja
213 struct
214 {
215 vec2 m_ActivationDir;
216 int m_ActivationTick;
217 int m_CurrentMoveTime;
218 int m_OldVelAmount;
219 } m_Ninja;
220
221 bool m_NewHook;
222
223 int m_Jumped;
224 // m_JumpedTotal counts the jumps performed in the air
225 int m_JumpedTotal;
226 int m_Jumps;
227
228 int m_Direction;
229 int m_Angle;
230 CNetObj_PlayerInput m_Input;
231
232 int m_TriggeredEvents;
233
234 void Init(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams = nullptr);
235 void SetCoreWorld(CWorldCore *pWorld, CCollision *pCollision, CTeamsCore *pTeams);
236 void Reset();
237 void TickDeferred();
238 void Tick(bool UseInput, bool DoDeferredTick = true);
239 void Move();
240
241 void Read(const CNetObj_CharacterCore *pObjCore);
242 void Write(CNetObj_CharacterCore *pObjCore) const;
243 void Quantize();
244
245 // DDRace
246 int m_Id;
247 bool m_Reset;
248 CCollision *Collision() { return m_pCollision; }
249
250 int m_Colliding;
251 bool m_LeftWall;
252
253 // DDNet Character
254 void SetTeamsCore(CTeamsCore *pTeams);
255 void ReadDDNet(const CNetObj_DDNetCharacter *pObjDDNet);
256 bool m_Solo;
257 bool m_Jetpack;
258 bool m_CollisionDisabled;
259 bool m_EndlessHook;
260 bool m_EndlessJump;
261 bool m_HammerHitDisabled;
262 bool m_GrenadeHitDisabled;
263 bool m_LaserHitDisabled;
264 bool m_ShotgunHitDisabled;
265 bool m_HookHitDisabled;
266 bool m_Super;
267 bool m_Invincible;
268 bool m_HasTelegunGun;
269 bool m_HasTelegunGrenade;
270 bool m_HasTelegunLaser;
271 int m_FreezeStart;
272 int m_FreezeEnd;
273 bool m_IsInFreeze;
274 bool m_DeepFrozen;
275 bool m_LiveFrozen;
276 CTuningParams m_Tuning;
277
278 // clientside only: antiping
279 void SetAntiPingInterfereCallback(FAntiPingInterfereCallback Callback);
280
281private:
282 CTeamsCore *m_pTeams;
283 int m_MoveRestrictions;
284 int m_HookedPlayer;
285 static bool IsSwitchActiveCb(unsigned char Number, void *pUser);
286
287 FAntiPingInterfereCallback m_AntiPingInterfereCallback = [](int ClientId, bool DisallowReset) {};
288};
289
290// input count
291struct CInputCount
292{
293 int m_Presses;
294 int m_Releases;
295};
296
297inline CInputCount CountInput(int Prev, int Cur)
298{
299 CInputCount c = {.m_Presses: 0, .m_Releases: 0};
300 Prev &= INPUT_STATE_MASK;
301 Cur &= INPUT_STATE_MASK;
302 int i = Prev;
303
304 while(i != Cur)
305 {
306 i = (i + 1) & INPUT_STATE_MASK;
307 if(i & 1)
308 c.m_Presses++;
309 else
310 c.m_Releases++;
311 }
312
313 return c;
314}
315
316#endif
317