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_CLIENT_GAMECLIENT_H
4#define GAME_CLIENT_GAMECLIENT_H
5
6#include "render.h"
7
8#include <base/color.h>
9#include <base/types.h>
10#include <base/vmath.h>
11
12#include <engine/client.h>
13#include <engine/client/enums.h>
14#include <engine/console.h>
15#include <engine/shared/config.h>
16#include <engine/shared/snapshot.h>
17
18#include <generated/protocol7.h>
19#include <generated/protocolglue.h>
20
21#include <game/client/prediction/gameworld.h>
22#include <game/client/race.h>
23#include <game/collision.h>
24#include <game/gamecore.h>
25#include <game/layers.h>
26#include <game/map/render_map.h>
27#include <game/mapbugs.h>
28#include <game/teamscore.h>
29
30// components
31#include "components/background.h"
32#include "components/binds.h"
33#include "components/broadcast.h"
34#include "components/camera.h"
35#include "components/censor.h"
36#include "components/chat.h"
37#include "components/console.h"
38#include "components/controls.h"
39#include "components/countryflags.h"
40#include "components/damageind.h"
41#include "components/debughud.h"
42#include "components/effects.h"
43#include "components/emoticon.h"
44#include "components/flow.h"
45#include "components/freezebars.h"
46#include "components/ghost.h"
47#include "components/hud.h"
48#include "components/important_alert.h"
49#include "components/infomessages.h"
50#include "components/items.h"
51#include "components/key_binder.h"
52#include "components/local_server.h"
53#include "components/mapimages.h"
54#include "components/maplayers.h"
55#include "components/mapsounds.h"
56#include "components/menu_background.h"
57#include "components/menus.h"
58#include "components/motd.h"
59#include "components/nameplates.h"
60#include "components/particles.h"
61#include "components/players.h"
62#include "components/race_demo.h"
63#include "components/scoreboard.h"
64#include "components/skins.h"
65#include "components/skins7.h"
66#include "components/sounds.h"
67#include "components/spectator.h"
68#include "components/statboard.h"
69#include "components/tooltips.h"
70#include "components/touch_controls.h"
71#include "components/voting.h"
72
73#include <memory>
74#include <vector>
75
76class IMap;
77
78class CGameInfo
79{
80public:
81 bool m_FlagStartsRace;
82 bool m_TimeScore;
83 bool m_UnlimitedAmmo;
84 bool m_DDRaceRecordMessage;
85 bool m_RaceRecordMessage;
86 bool m_RaceSounds;
87
88 bool m_AllowEyeWheel;
89 bool m_AllowHookColl;
90 bool m_AllowZoom;
91
92 bool m_BugDDRaceGhost;
93 bool m_BugDDRaceInput;
94 bool m_BugFNGLaserRange;
95 bool m_BugVanillaBounce;
96
97 bool m_PredictFNG;
98 bool m_PredictDDRace;
99 bool m_PredictDDRaceTiles;
100 bool m_PredictVanilla;
101
102 bool m_EntitiesDDNet;
103 bool m_EntitiesDDRace;
104 bool m_EntitiesRace;
105 bool m_EntitiesFNG;
106 bool m_EntitiesVanilla;
107 bool m_EntitiesBW;
108 bool m_EntitiesFDDrace;
109
110 bool m_Race;
111 bool m_Pvp;
112
113 bool m_DontMaskEntities;
114 bool m_AllowXSkins;
115
116 bool m_HudHealthArmor;
117 bool m_HudAmmo;
118 bool m_HudDDRace;
119
120 bool m_NoWeakHookAndBounce;
121 bool m_NoSkinChangeForFrozen;
122
123 bool m_DDRaceTeam;
124
125 bool m_PredictEvents;
126
127 bool m_Supports128Teams;
128};
129
130class CSnapEntities
131{
132public:
133 IClient::CSnapItem m_Item;
134 const CNetObj_EntityEx *m_pDataEx;
135};
136
137enum class EClientIdFormat
138{
139 NO_INDENT,
140 INDENT_AUTO,
141 INDENT_FORCE, // for rendering settings preview
142};
143
144class CGameClient : public IGameClient
145{
146public:
147 // all components
148 CInfoMessages m_InfoMessages;
149 CCamera m_Camera;
150 CChat m_Chat;
151 CCensor m_Censor;
152 CMotd m_Motd;
153 CBroadcast m_Broadcast;
154 CGameConsole m_GameConsole;
155 CBinds m_Binds;
156 CKeyBinder m_KeyBinder;
157 CParticles m_Particles;
158 CMenus m_Menus;
159 CSkins m_Skins;
160 CSkins7 m_Skins7;
161 CCountryFlags m_CountryFlags;
162 CFlow m_Flow;
163 CHud m_Hud;
164 CImportantAlert m_ImportantAlert;
165 CDebugHud m_DebugHud;
166 CControls m_Controls;
167 CEffects m_Effects;
168 CScoreboard m_Scoreboard;
169 CStatboard m_Statboard;
170 CSounds m_Sounds;
171 CEmoticon m_Emoticon;
172 CDamageInd m_DamageInd;
173 CTouchControls m_TouchControls;
174 CVoting m_Voting;
175 CSpectator m_Spectator;
176
177 CPlayers m_Players;
178 CNamePlates m_NamePlates;
179 CFreezeBars m_FreezeBars;
180 CItems m_Items;
181 CMapImages m_MapImages;
182
183 CMapLayers m_MapLayersBackground = CMapLayers{ERenderType::RENDERTYPE_BACKGROUND};
184 CMapLayers m_MapLayersForeground = CMapLayers{ERenderType::RENDERTYPE_FOREGROUND};
185 CBackground m_Background;
186 CMenuBackground m_MenuBackground;
187
188 CMapSounds m_MapSounds;
189
190 CRaceDemo m_RaceDemo;
191 CGhost m_Ghost;
192
193 CTooltips m_Tooltips;
194
195 CLocalServer m_LocalServer;
196
197private:
198 std::vector<class CComponent *> m_vpAll;
199 std::vector<class CComponent *> m_vpInput;
200 CNetObjHandler m_NetObjHandler;
201 protocol7::CNetObjHandler m_NetObjHandler7;
202
203 class IEngine *m_pEngine;
204 class IInput *m_pInput;
205 class IGraphics *m_pGraphics;
206 class ITextRender *m_pTextRender;
207 class IClient *m_pClient;
208 class ISound *m_pSound;
209 class IConfigManager *m_pConfigManager;
210 class CConfig *m_pConfig;
211 class IConsole *m_pConsole;
212 class IStorage *m_pStorage;
213 class IDemoPlayer *m_pDemoPlayer;
214 class IFavorites *m_pFavorites;
215 class IServerBrowser *m_pServerBrowser;
216 class IEditor *m_pEditor;
217 class IFriends *m_pFriends;
218 class IFriends *m_pFoes;
219 class IDiscord *m_pDiscord;
220#if defined(CONF_AUTOUPDATE)
221 class IUpdater *m_pUpdater;
222#endif
223 class IHttp *m_pHttp;
224
225 CLayers m_Layers;
226 CCollision m_Collision;
227 CUi m_UI;
228 CRaceHelper m_RaceHelper;
229
230 void ProcessEvents();
231 void UpdatePositions();
232
233 int m_EditorMovementDelay = 5;
234 void UpdateEditorIngameMoved();
235
236 int m_PredictedTick;
237 int m_aLastNewPredictedTick[NUM_DUMMIES];
238
239 int m_LastRoundStartTick;
240 int m_LastRaceTick;
241
242 int m_LastFlagCarrierRed;
243 int m_LastFlagCarrierBlue;
244
245 int m_aCheckInfo[NUM_DUMMIES];
246
247 char m_aDDNetVersionStr[64];
248
249 static void ConTeam(IConsole::IResult *pResult, void *pUserData);
250 static void ConKill(IConsole::IResult *pResult, void *pUserData);
251 static void ConReadyChange7(IConsole::IResult *pResult, void *pUserData);
252
253 static void ConchainLanguageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
254 static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
255 static void ConchainSpecialDummyInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
256 static void ConchainRefreshSkins(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
257 static void ConchainRefreshEventSkins(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
258 static void ConchainSpecialDummy(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
259
260 static void ConTuneParam(IConsole::IResult *pResult, void *pUserData);
261 static void ConTuneZone(IConsole::IResult *pResult, void *pUserData);
262 static void ConMapbug(IConsole::IResult *pResult, void *pUserData);
263
264 static void ConchainMenuMap(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
265
266 static std::function<bool(int, int, int, int)> GetScoreComparator(bool TimeScore, bool ReceivedMillisecondFinishTimes, bool Race7);
267
268 // only used in OnPredict
269 vec2 m_aLastPos[MAX_CLIENTS];
270 bool m_aLastActive[MAX_CLIENTS];
271
272 // only used in OnNewSnapshot
273 bool m_GameOver = false;
274 bool m_GamePaused = false;
275
276public:
277 IKernel *Kernel() { return IInterface::Kernel(); }
278 IEngine *Engine() const { return m_pEngine; }
279 class IGraphics *Graphics() const { return m_pGraphics; }
280 class IClient *Client() const { return m_pClient; }
281 class CUi *Ui() { return &m_UI; }
282 class ISound *Sound() const { return m_pSound; }
283 class IInput *Input() const { return m_pInput; }
284 class IStorage *Storage() const { return m_pStorage; }
285 class IConfigManager *ConfigManager() const { return m_pConfigManager; }
286 class CConfig *Config() const { return m_pConfig; }
287 class IConsole *Console() { return m_pConsole; }
288 class ITextRender *TextRender() const { return m_pTextRender; }
289 class IDemoPlayer *DemoPlayer() const { return m_pDemoPlayer; }
290 class IDemoRecorder *DemoRecorder(int Recorder) const { return Client()->DemoRecorder(Recorder); }
291 class IFavorites *Favorites() const { return m_pFavorites; }
292 class IServerBrowser *ServerBrowser() const { return m_pServerBrowser; }
293 class CRenderTools *RenderTools() { return &m_RenderTools; }
294 class CRenderMap *RenderMap() { return &m_RenderMap; }
295 class CLayers *Layers() { return &m_Layers; }
296 CCollision *Collision() { return &m_Collision; }
297 const CCollision *Collision() const { return &m_Collision; }
298 const CRaceHelper *RaceHelper() const { return &m_RaceHelper; }
299 class IEditor *Editor() { return m_pEditor; }
300 class IFriends *Friends() { return m_pFriends; }
301 class IFriends *Foes() { return m_pFoes; }
302#if defined(CONF_AUTOUPDATE)
303 class IUpdater *Updater()
304 {
305 return m_pUpdater;
306 }
307#endif
308 class IHttp *Http()
309 {
310 return m_pHttp;
311 }
312
313 int NetobjNumCorrections()
314 {
315 return m_NetObjHandler.NumObjCorrections();
316 }
317 const char *NetobjCorrectedOn() { return m_NetObjHandler.CorrectedObjOn(); }
318
319 bool m_SuppressEvents;
320 bool m_NewTick;
321 bool m_NewPredictedTick;
322 int m_aFlagDropTick[2];
323
324 enum
325 {
326 SERVERMODE_PURE = 0,
327 SERVERMODE_MOD,
328 SERVERMODE_PUREMOD,
329 };
330 int m_ServerMode;
331 CGameInfo m_GameInfo;
332
333 int m_DemoSpecId;
334
335 vec2 m_LocalCharacterPos;
336
337 /**
338 * Our prediction for the local character at tick
339 * `IClient::PredGameTick() - 1`.
340 */
341 CCharacterCore m_PredictedPrevChar;
342 /**
343 * Our prediction for the local character at tick
344 * `IClient::PredGameTick()`.
345 */
346 CCharacterCore m_PredictedChar;
347
348 // snap pointers
349 class CSnapState
350 {
351 public:
352 const CNetObj_Character *m_pLocalCharacter;
353 const CNetObj_Character *m_pLocalPrevCharacter;
354 const CNetObj_PlayerInfo *m_pLocalInfo;
355 const CNetObj_SpectatorInfo *m_pSpectatorInfo;
356 const CNetObj_SpectatorInfo *m_pPrevSpectatorInfo;
357 const CNetObj_SpectatorCount *m_pSpectatorCount;
358 int m_NumFlags;
359 const CNetObj_Flag *m_apFlags[CSnapshot::MAX_ITEMS];
360 const CNetObj_Flag *m_apPrevFlags[CSnapshot::MAX_ITEMS];
361 const CNetObj_GameInfo *m_pGameInfoObj;
362 const CNetObj_GameData *m_pGameDataObj;
363 const CNetObj_GameData *m_pPrevGameDataObj;
364
365 const CNetObj_PlayerInfo *m_apPlayerInfos[MAX_CLIENTS];
366 const CNetObj_PlayerInfo *m_apPrevPlayerInfos[MAX_CLIENTS];
367
368 const CNetObj_PlayerInfo *m_apInfoByScore[MAX_CLIENTS];
369 const CNetObj_PlayerInfo *m_apInfoByName[MAX_CLIENTS];
370 const CNetObj_PlayerInfo *m_apInfoByDDTeamScore[MAX_CLIENTS];
371 const CNetObj_PlayerInfo *m_apInfoByDDTeamName[MAX_CLIENTS];
372
373 int m_LocalClientId;
374 int m_NumPlayers;
375 int m_aTeamSize[2];
376 int m_HighestClientId;
377
378 class CSpectateInfo
379 {
380 public:
381 bool m_Active;
382 int m_SpectatorId;
383 bool m_UsePosition;
384 vec2 m_Position;
385
386 bool m_HasCameraInfo;
387 float m_Zoom;
388 int m_Deadzone;
389 int m_FollowFactor;
390 };
391 CSpectateInfo m_SpecInfo;
392
393 class CCharacterInfo
394 {
395 public:
396 bool m_Active;
397
398 // snapshots
399 CNetObj_Character m_Prev;
400 CNetObj_Character m_Cur;
401
402 CNetObj_DDNetCharacter m_ExtendedData;
403 const CNetObj_DDNetCharacter *m_pPrevExtendedData;
404 bool m_HasExtendedData;
405 bool m_HasExtendedDisplayInfo;
406 };
407 CCharacterInfo m_aCharacters[MAX_CLIENTS];
408 };
409
410 CSnapState m_Snap;
411 int m_aLocalTuneZone[NUM_DUMMIES]; // current tunezone (0-255)
412 bool m_aReceivedTuning[NUM_DUMMIES]; // was tuning message received after zone change
413 int m_aExpectingTuningForZone[NUM_DUMMIES]; // tunezone changed, waiting for tuning for that zone
414 int m_aExpectingTuningSince[NUM_DUMMIES]; // how many snaps received since tunezone changed
415 CTuningParams m_aTuning[NUM_DUMMIES]; // current local player tuning, only what the player/dummy has
416
417 std::bitset<RECORDER_MAX> m_ActiveRecordings;
418
419 // spectate cursor data
420 class CCursorInfo
421 {
422 friend class CGameClient;
423 static constexpr int CURSOR_SAMPLES = 8; // how many samples to keep
424 static constexpr int SAMPLE_FRAME_WINDOW = 3; // how many samples should be used for polynomial interpolation
425 static constexpr int SAMPLE_FRAME_OFFSET = 2; // how many samples in the past should be included
426 static constexpr double INTERP_DELAY = 4.25; // how many ticks in the past to show, enables extrapolation with smaller value (<= SAMPLE_FRAME_WINDOW - SAMPLE_FRAME_OFFSET + 3)
427 static constexpr double REST_THRESHOLD = 3.0; // how many ticks of the same samples are considered to be resting
428
429 int m_CursorOwnerId;
430 double m_aTargetSamplesTime[CURSOR_SAMPLES];
431 vec2 m_aTargetSamplesData[CURSOR_SAMPLES];
432 int m_NumSamples;
433
434 bool m_Available;
435 int m_Weapon;
436 vec2 m_Target;
437 vec2 m_WorldTarget;
438 vec2 m_Position;
439
440 public:
441 bool IsAvailable() const { return m_Available; }
442 int Weapon() const { return m_Weapon; }
443 vec2 Target() const { return m_Target; }
444 vec2 WorldTarget() const { return m_WorldTarget; }
445 vec2 Position() const { return m_Position; }
446 } m_CursorInfo;
447
448 // client data
449 class CClientData
450 {
451 friend class CGameClient;
452 CGameClient *m_pGameClient;
453 int m_ClientId;
454
455 public:
456 int m_UseCustomColor;
457 int m_ColorBody;
458 int m_ColorFeet;
459
460 char m_aName[MAX_NAME_LENGTH];
461 char m_aClan[MAX_CLAN_LENGTH];
462 /**
463 * Country code in ISO 3166-1 numeric.
464 */
465 int m_Country;
466 char m_aSkinName[MAX_SKIN_LENGTH];
467 int m_Team;
468 int m_Emoticon;
469 float m_EmoticonStartFraction;
470 int m_EmoticonStartTick;
471
472 bool m_Solo;
473 bool m_Jetpack;
474 bool m_CollisionDisabled;
475 bool m_EndlessHook;
476 bool m_EndlessJump;
477 bool m_HammerHitDisabled;
478 bool m_GrenadeHitDisabled;
479 bool m_LaserHitDisabled;
480 bool m_ShotgunHitDisabled;
481 bool m_HookHitDisabled;
482 bool m_Super;
483 bool m_Invincible;
484 bool m_HasTelegunGun;
485 bool m_HasTelegunGrenade;
486 bool m_HasTelegunLaser;
487 int m_FreezeEnd;
488 bool m_DeepFrozen;
489 bool m_LiveFrozen;
490
491 CCharacterCore m_Predicted;
492 CCharacterCore m_PrevPredicted;
493
494 std::shared_ptr<CManagedTeeRenderInfo> m_pSkinInfo = nullptr; // this is what the server reports
495 CTeeRenderInfo m_RenderInfo; // this is what we use
496
497 float m_Angle;
498 bool m_Active;
499 bool m_ChatIgnore;
500 bool m_EmoticonIgnore;
501 bool m_Friend;
502 bool m_Foe;
503
504 int m_AuthLevel;
505 bool m_Afk;
506 bool m_Paused;
507 bool m_Spec;
508
509 int m_FinishTimeSeconds;
510 int m_FinishTimeMillis;
511
512 // Editor allows 256 switches for now.
513 bool m_aSwitchStates[256];
514
515 CNetObj_Character m_Snapped;
516 CNetObj_Character m_Evolved;
517
518 CNetMsg_Sv_PreInput m_aPreInputs[200];
519
520 // rendered characters
521 CNetObj_Character m_RenderCur;
522 CNetObj_Character m_RenderPrev;
523 vec2 m_RenderPos;
524 bool m_IsPredicted;
525 bool m_IsPredictedLocal;
526 int64_t m_aSmoothStart[2];
527 int64_t m_aSmoothLen[2];
528 vec2 m_aPredPos[200];
529 int m_aPredTick[200];
530 bool m_SpecCharPresent;
531 vec2 m_SpecChar;
532
533 void UpdateSkinInfo();
534 void UpdateSkin7HatSprite(int Dummy);
535 void UpdateSkin7BotDecoration(int Dummy);
536 void UpdateRenderInfo();
537 void Reset();
538 CSkinDescriptor ToSkinDescriptor() const;
539
540 int ClientId() const { return m_ClientId; }
541
542 class CSixup
543 {
544 public:
545 void Reset();
546
547 char m_aaSkinPartNames[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_LENGTH];
548 int m_aUseCustomColors[protocol7::NUM_SKINPARTS];
549 int m_aSkinPartColors[protocol7::NUM_SKINPARTS];
550 };
551
552 // 0.7 Skin
553 CSixup m_aSixup[NUM_DUMMIES];
554 };
555
556 CClientData m_aClients[MAX_CLIENTS];
557
558 class CClientStats
559 {
560 int m_IngameTicks;
561 int m_JoinTick;
562 bool m_Active;
563
564 public:
565 CClientStats();
566
567 int m_aFragsWith[NUM_WEAPONS];
568 int m_aDeathsFrom[NUM_WEAPONS];
569 int m_Frags;
570 int m_Deaths;
571 int m_Suicides;
572 int m_BestSpree;
573 int m_CurrentSpree;
574
575 int m_FlagGrabs;
576 int m_FlagCaptures;
577
578 void Reset();
579
580 bool IsActive() const { return m_Active; }
581 void JoinGame(int Tick)
582 {
583 m_Active = true;
584 m_JoinTick = Tick;
585 }
586 void JoinSpec(int Tick)
587 {
588 m_Active = false;
589 m_IngameTicks += Tick - m_JoinTick;
590 }
591 int GetIngameTicks(int Tick) const { return m_IngameTicks + Tick - m_JoinTick; }
592 float GetFPM(int Tick, int TickSpeed) const { return (float)(m_Frags * TickSpeed * 60) / GetIngameTicks(Tick); }
593 };
594
595 CClientStats m_aStats[MAX_CLIENTS];
596
597 CRenderTools m_RenderTools;
598 CRenderMap m_RenderMap;
599
600 bool m_BackButtonHandledKeyBind = false;
601
602 void OnReset();
603
604 size_t ComponentCount() const { return m_vpAll.size(); }
605
606 // hooks
607 void OnConnected() override;
608 void OnRender() override;
609 void OnUpdate() override;
610 void OnDummyDisconnect() override;
611 virtual void OnRelease();
612 void OnInit() override;
613 void OnConsoleInit() override;
614 void OnStateChange(int NewState, int OldState) override;
615 template<typename T>
616 void ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId, int Conn);
617 void ApplySkin7InfoFromSnapObj(const protocol7::CNetObj_De_ClientInfo *pObj, int ClientId) override;
618 int OnDemoRecSnap7(CSnapshot *pFrom, CSnapshotBuffer *pTo, int Conn) override;
619 void *TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn);
620 int TranslateSnap(CSnapshotBuffer *pSnapDstSix, CSnapshot *pSnapSrcSeven, int Conn, bool Dummy) override;
621 void OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dummy) override;
622 void InvalidateSnapshot() override;
623 void OnNewSnapshot(bool DummySwapped) override;
624 void OnPredict() override;
625 void OnActivateEditor() override;
626 void OnDummySwap() override;
627 int OnSnapInput(int *pData, bool Dummy, bool Force) override;
628 void OnShutdown() override;
629 void OnEnterGame() override;
630 void OnRconType(bool UsernameReq) override;
631 void OnRconLine(const char *pLine) override;
632 virtual void OnGameOver();
633 virtual void OnStartGame();
634 virtual void OnStartRound();
635 virtual void OnFlagGrab(int TeamId);
636 void OnWindowResize() override;
637
638 void InitializeLanguage() override;
639 bool m_LanguageChanged = false;
640 void OnLanguageChange();
641 void HandleLanguageChanged();
642
643 void ForceUpdateConsoleRemoteCompletionSuggestions() override;
644
645 void RefreshSkin(const std::shared_ptr<CManagedTeeRenderInfo> &pManagedTeeRenderInfo);
646 void RefreshSkins(int SkinDescriptorFlags);
647 void OnSkinUpdate(const char *pSkinName);
648 std::shared_ptr<CManagedTeeRenderInfo> CreateManagedTeeRenderInfo(const CTeeRenderInfo &TeeRenderInfo, const CSkinDescriptor &SkinDescriptor);
649 std::shared_ptr<CManagedTeeRenderInfo> CreateManagedTeeRenderInfo(const CClientData &Client);
650 void CollectManagedTeeRenderInfos(const std::function<void(const char *pSkinName)> &ActiveSkinAcceptor);
651
652 void RenderShutdownMessage() override;
653 void ProcessDemoSnapshot(CSnapshot *pSnap) override;
654
655 const char *GetItemName(int Type) const override;
656 const char *Version() const override;
657 const char *NetVersion() const override;
658 const char *NetVersion7() const override;
659 int DDNetVersion() const override;
660 const char *DDNetVersionStr() const override;
661 int ClientVersion7() const override;
662
663 void DoTeamChangeMessage7(const char *pName, int ClientId, int Team, const char *pPrefix = "");
664
665 // actions
666 // TODO: move these
667 void SendSwitchTeam(int Team) const;
668 void SendStartInfo7(bool Dummy);
669 void SendSkinChange7(bool Dummy);
670 // Returns true if the requested skin change got applied by the server
671 bool GotWantedSkin7(bool Dummy);
672 void SendInfo(bool Start);
673 void SendDummyInfo(bool Start) override;
674 void SendKill() const;
675 void SendReadyChange7(); // NOLINT(readability-make-member-function-const)
676
677 void ApplyPreInputs(int Tick, bool Direct, CGameWorld &GameWorld);
678
679 int m_aNextChangeInfo[NUM_DUMMIES];
680
681 // DDRace
682
683 int m_aLocalIds[NUM_DUMMIES];
684 CNetObj_PlayerInput m_DummyInput;
685 CNetObj_PlayerInput m_HammerInput;
686 unsigned int m_DummyFire;
687 bool m_ReceivedDDNetPlayer;
688 bool m_ReceivedDDNetPlayerFinishTimes;
689 bool m_ReceivedDDNetPlayerFinishTimesMillis;
690
691 CTeamsCore m_Teams;
692
693 int IntersectCharacter(vec2 HookPos, vec2 NewPos, vec2 &NewPos2, int OwnId, vec2 *pPlayerPosition = nullptr);
694
695 int LastRaceTick() const;
696 int CurrentRaceTime() const;
697
698 bool IsTeamPlay() const;
699 bool IsWorldPaused() const;
700 bool IsDemoPlaybackPaused() const;
701 float GetAnimationPlaybackSpeed() const;
702
703 int AntiPingPlayers() const;
704 bool AntiPingGrenade() const;
705 bool AntiPingWeapons() const;
706 bool AntiPingGunfire() const;
707 bool Predict() const;
708 bool PredictDummy() const;
709
710 const CTuningParams *GetTuning(int i) const { return &m_aTuningList[i]; }
711 ColorRGBA GetDDTeamColor(int DDTeam, float Lightness = 0.5f) const;
712 void FormatClientId(int ClientId, char (&aClientId)[16], EClientIdFormat Format) const;
713
714 CGameWorld m_GameWorld;
715 CGameWorld m_PredictedWorld;
716 CGameWorld m_PrevPredictedWorld;
717
718 std::vector<SSwitchers> &Switchers() { return m_GameWorld.m_Core.m_vSwitchers; }
719 std::vector<SSwitchers> &PredSwitchers() { return m_PredictedWorld.m_Core.m_vSwitchers; }
720
721 void DummyResetInput() override;
722 void Echo(const char *pString) override;
723 bool IsOtherTeam(int ClientId) const;
724 int SwitchStateTeam() const;
725 bool IsLocalCharSuper() const;
726 bool CanDisplayWarning() const override;
727
728 IMap *Map() override { return m_pMap.get(); }
729 const IMap *Map() const override { return m_pMap.get(); }
730 CNetObjHandler *GetNetObjHandler() override;
731 protocol7::CNetObjHandler *GetNetObjHandler7() override;
732
733 void LoadGameSkin(const char *pPath, bool AsDir = false);
734 void LoadEmoticonsSkin(const char *pPath, bool AsDir = false);
735 void LoadParticlesSkin(const char *pPath, bool AsDir = false);
736 void LoadHudSkin(const char *pPath, bool AsDir = false);
737 void LoadExtrasSkin(const char *pPath, bool AsDir = false);
738
739 struct SClientGameSkin
740 {
741 // health armor hud
742 IGraphics::CTextureHandle m_SpriteHealthFull;
743 IGraphics::CTextureHandle m_SpriteHealthEmpty;
744 IGraphics::CTextureHandle m_SpriteArmorFull;
745 IGraphics::CTextureHandle m_SpriteArmorEmpty;
746
747 // cursors
748 IGraphics::CTextureHandle m_SpriteWeaponHammerCursor;
749 IGraphics::CTextureHandle m_SpriteWeaponGunCursor;
750 IGraphics::CTextureHandle m_SpriteWeaponShotgunCursor;
751 IGraphics::CTextureHandle m_SpriteWeaponGrenadeCursor;
752 IGraphics::CTextureHandle m_SpriteWeaponNinjaCursor;
753 IGraphics::CTextureHandle m_SpriteWeaponLaserCursor;
754
755 IGraphics::CTextureHandle m_aSpriteWeaponCursors[6];
756
757 // weapons and hook
758 IGraphics::CTextureHandle m_SpriteHookChain;
759 IGraphics::CTextureHandle m_SpriteHookHead;
760 IGraphics::CTextureHandle m_SpriteWeaponHammer;
761 IGraphics::CTextureHandle m_SpriteWeaponGun;
762 IGraphics::CTextureHandle m_SpriteWeaponShotgun;
763 IGraphics::CTextureHandle m_SpriteWeaponGrenade;
764 IGraphics::CTextureHandle m_SpriteWeaponNinja;
765 IGraphics::CTextureHandle m_SpriteWeaponLaser;
766
767 IGraphics::CTextureHandle m_aSpriteWeapons[6];
768
769 // particles
770 IGraphics::CTextureHandle m_aSpriteParticles[9];
771
772 // stars
773 IGraphics::CTextureHandle m_aSpriteStars[3];
774
775 // projectiles
776 IGraphics::CTextureHandle m_SpriteWeaponGunProjectile;
777 IGraphics::CTextureHandle m_SpriteWeaponShotgunProjectile;
778 IGraphics::CTextureHandle m_SpriteWeaponGrenadeProjectile;
779 IGraphics::CTextureHandle m_SpriteWeaponHammerProjectile;
780 IGraphics::CTextureHandle m_SpriteWeaponNinjaProjectile;
781 IGraphics::CTextureHandle m_SpriteWeaponLaserProjectile;
782
783 IGraphics::CTextureHandle m_aSpriteWeaponProjectiles[6];
784
785 // muzzles
786 IGraphics::CTextureHandle m_aSpriteWeaponGunMuzzles[3];
787 IGraphics::CTextureHandle m_aSpriteWeaponShotgunMuzzles[3];
788 IGraphics::CTextureHandle m_aaSpriteWeaponNinjaMuzzles[3];
789
790 IGraphics::CTextureHandle m_aaSpriteWeaponsMuzzles[6][3];
791
792 // pickups
793 IGraphics::CTextureHandle m_SpritePickupHealth;
794 IGraphics::CTextureHandle m_SpritePickupArmor;
795 IGraphics::CTextureHandle m_SpritePickupArmorShotgun;
796 IGraphics::CTextureHandle m_SpritePickupArmorGrenade;
797 IGraphics::CTextureHandle m_SpritePickupArmorNinja;
798 IGraphics::CTextureHandle m_SpritePickupArmorLaser;
799 IGraphics::CTextureHandle m_SpritePickupGrenade;
800 IGraphics::CTextureHandle m_SpritePickupShotgun;
801 IGraphics::CTextureHandle m_SpritePickupLaser;
802 IGraphics::CTextureHandle m_SpritePickupNinja;
803 IGraphics::CTextureHandle m_SpritePickupGun;
804 IGraphics::CTextureHandle m_SpritePickupHammer;
805
806 IGraphics::CTextureHandle m_aSpritePickupWeapons[6];
807 IGraphics::CTextureHandle m_aSpritePickupWeaponArmor[4];
808
809 // flags
810 IGraphics::CTextureHandle m_SpriteFlagBlue;
811 IGraphics::CTextureHandle m_SpriteFlagRed;
812
813 // ninja bar (0.7)
814 IGraphics::CTextureHandle m_SpriteNinjaBarFullLeft;
815 IGraphics::CTextureHandle m_SpriteNinjaBarFull;
816 IGraphics::CTextureHandle m_SpriteNinjaBarEmpty;
817 IGraphics::CTextureHandle m_SpriteNinjaBarEmptyRight;
818
819 bool IsSixup() const
820 {
821 return m_SpriteNinjaBarFullLeft.IsValid();
822 }
823 };
824
825 SClientGameSkin m_GameSkin;
826 bool m_GameSkinLoaded = false;
827
828 struct SClientParticlesSkin
829 {
830 IGraphics::CTextureHandle m_SpriteParticleSlice;
831 IGraphics::CTextureHandle m_SpriteParticleBall;
832 IGraphics::CTextureHandle m_aSpriteParticleSplat[3];
833 IGraphics::CTextureHandle m_SpriteParticleSmoke;
834 IGraphics::CTextureHandle m_SpriteParticleShell;
835 IGraphics::CTextureHandle m_SpriteParticleExpl;
836 IGraphics::CTextureHandle m_SpriteParticleAirJump;
837 IGraphics::CTextureHandle m_SpriteParticleHit;
838 IGraphics::CTextureHandle m_aSpriteParticles[10];
839 };
840
841 SClientParticlesSkin m_ParticlesSkin;
842 bool m_ParticlesSkinLoaded = false;
843
844 struct SClientEmoticonsSkin
845 {
846 IGraphics::CTextureHandle m_aSpriteEmoticons[16];
847 };
848
849 SClientEmoticonsSkin m_EmoticonsSkin;
850 bool m_EmoticonsSkinLoaded = false;
851
852 struct SClientHudSkin
853 {
854 IGraphics::CTextureHandle m_SpriteHudAirjump;
855 IGraphics::CTextureHandle m_SpriteHudAirjumpEmpty;
856 IGraphics::CTextureHandle m_SpriteHudSolo;
857 IGraphics::CTextureHandle m_SpriteHudCollisionDisabled;
858 IGraphics::CTextureHandle m_SpriteHudEndlessJump;
859 IGraphics::CTextureHandle m_SpriteHudEndlessHook;
860 IGraphics::CTextureHandle m_SpriteHudJetpack;
861 IGraphics::CTextureHandle m_SpriteHudFreezeBarFullLeft;
862 IGraphics::CTextureHandle m_SpriteHudFreezeBarFull;
863 IGraphics::CTextureHandle m_SpriteHudFreezeBarEmpty;
864 IGraphics::CTextureHandle m_SpriteHudFreezeBarEmptyRight;
865 IGraphics::CTextureHandle m_SpriteHudNinjaBarFullLeft;
866 IGraphics::CTextureHandle m_SpriteHudNinjaBarFull;
867 IGraphics::CTextureHandle m_SpriteHudNinjaBarEmpty;
868 IGraphics::CTextureHandle m_SpriteHudNinjaBarEmptyRight;
869 IGraphics::CTextureHandle m_SpriteHudHookHitDisabled;
870 IGraphics::CTextureHandle m_SpriteHudHammerHitDisabled;
871 IGraphics::CTextureHandle m_SpriteHudShotgunHitDisabled;
872 IGraphics::CTextureHandle m_SpriteHudGrenadeHitDisabled;
873 IGraphics::CTextureHandle m_SpriteHudLaserHitDisabled;
874 IGraphics::CTextureHandle m_SpriteHudGunHitDisabled;
875 IGraphics::CTextureHandle m_SpriteHudDeepFrozen;
876 IGraphics::CTextureHandle m_SpriteHudLiveFrozen;
877 IGraphics::CTextureHandle m_SpriteHudTeleportGrenade;
878 IGraphics::CTextureHandle m_SpriteHudTeleportGun;
879 IGraphics::CTextureHandle m_SpriteHudTeleportLaser;
880 IGraphics::CTextureHandle m_SpriteHudPracticeMode;
881 IGraphics::CTextureHandle m_SpriteHudLockMode;
882 IGraphics::CTextureHandle m_SpriteHudTeam0Mode;
883 IGraphics::CTextureHandle m_SpriteHudDummyHammer;
884 IGraphics::CTextureHandle m_SpriteHudDummyCopy;
885 };
886
887 SClientHudSkin m_HudSkin;
888 bool m_HudSkinLoaded = false;
889
890 struct SClientExtrasSkin
891 {
892 IGraphics::CTextureHandle m_SpriteParticleSnowflake;
893 IGraphics::CTextureHandle m_SpriteParticleSparkle;
894 IGraphics::CTextureHandle m_SpritePulley;
895 IGraphics::CTextureHandle m_SpriteHectagon;
896 IGraphics::CTextureHandle m_aSpriteParticles[4];
897 };
898
899 SClientExtrasSkin m_ExtrasSkin;
900 bool m_ExtrasSkinLoaded = false;
901
902 const std::vector<CSnapEntities> &SnapEntities() { return m_vSnapEntities; }
903
904 int m_MultiViewTeam;
905 float m_MultiViewPersonalZoom;
906 bool m_MultiViewShowHud;
907 bool m_MultiViewActivated;
908 bool m_aMultiViewId[MAX_CLIENTS];
909
910 void ResetMultiView();
911 int FindFirstMultiViewId();
912 void CleanMultiViewId(int ClientId);
913 int m_MapBestTimeSeconds;
914 int m_MapBestTimeMillis;
915 char m_aMapDescription[512];
916
917private:
918 std::unique_ptr<IMap> m_pMap;
919
920 std::vector<CSnapEntities> m_vSnapEntities;
921 void SnapCollectEntities();
922
923 bool m_aDDRaceMsgSent[NUM_DUMMIES];
924 int m_aShowOthers[NUM_DUMMIES];
925 int m_aEnableSpectatorCount[NUM_DUMMIES]; // current setting as sent to the server, -1 if not yet sent
926
927 class CImageAsset
928 {
929 public:
930 bool IsLoaded() const { return m_ImageInfo.m_pData != nullptr; }
931
932 char m_aPath[IO_MAX_PATH_LENGTH];
933 bool m_IsDefault;
934 CImageInfo m_ImageInfo;
935 std::optional<CImageInfo> m_FallbackImageInfo;
936 };
937
938 CImageAsset LoadAssetFromPath(const char *pPath, bool AsDir, int AssetId, const char *pDirectory) const;
939
940 std::vector<std::shared_ptr<CManagedTeeRenderInfo>> m_vpManagedTeeRenderInfos;
941 void UpdateManagedTeeRenderInfos();
942
943 void UpdateLocalTuning();
944 void UpdatePrediction();
945 void UpdateSpectatorCursor();
946 void UpdateRenderedCharacters();
947 void HandlePredictedEvents(int Tick);
948
949 void OnInput(const IInput::CEvent &Event);
950
951 int m_aLastUpdateTick[MAX_CLIENTS] = {0};
952 void DetectStrongHook();
953
954 vec2 GetSmoothPos(int ClientId);
955
956 int m_IsDummySwapping;
957 CCharOrder m_CharOrder;
958 int m_aSwitchStateTeam[NUM_DUMMIES];
959
960 void LoadMapSettings();
961 CMapBugs m_MapBugs;
962
963 // tunings for every zone on the map, 0 is a global tune
964 CTuningParams m_aTuningList[TuneZone::NUM];
965 CTuningParams *TuningList() { return m_aTuningList; }
966
967 float m_LastShowDistanceZoom;
968 float m_LastZoom;
969 float m_LastScreenAspect;
970 float m_LastDeadzone;
971 float m_LastFollowFactor;
972 bool m_LastDummyConnected;
973
974 void HandleMultiView();
975 bool IsMultiViewIdSet();
976 void CleanMultiViewIds();
977 bool InitMultiView(int Team);
978 float CalculateMultiViewMultiplier(vec2 TargetPos);
979 float CalculateMultiViewZoom(vec2 MinPos, vec2 MaxPos, float Vel);
980 float MapValue(float MaxValue, float MinValue, float MaxRange, float MinRange, float Value);
981
982 struct SMultiView
983 {
984 bool m_Solo;
985 bool m_IsInit;
986 bool m_Teleported;
987 bool m_aVanish[MAX_CLIENTS];
988 vec2 m_OldPos;
989 int m_OldPersonalZoom;
990 float m_SecondChance;
991 float m_OldCameraDistance;
992 float m_aLastFreeze[MAX_CLIENTS];
993 };
994
995 SMultiView m_MultiView;
996
997 void OnSaveCodeNetMessage(const CNetMsg_Sv_SaveCode *pMsg);
998 void StoreSave(const char *pTeamMembers, const char *pGeneratedCode) const;
999};
1000
1001ColorRGBA CalculateNameColor(ColorHSLA TextColorHSL);
1002
1003#endif
1004