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