1/* (c) Rajh, Redix and Sushi. */
2
3#ifndef GAME_CLIENT_COMPONENTS_GHOST_H
4#define GAME_CLIENT_COMPONENTS_GHOST_H
5
6#include <generated/protocol.h>
7
8#include <game/client/component.h>
9#include <game/client/components/menus.h>
10#include <game/client/render.h>
11
12struct CNetObj_Character;
13
14enum
15{
16 GHOSTDATA_TYPE_SKIN = 0,
17 GHOSTDATA_TYPE_CHARACTER_NO_TICK,
18 GHOSTDATA_TYPE_CHARACTER,
19 GHOSTDATA_TYPE_START_TICK
20};
21
22struct CGhostSkin
23{
24 int m_aSkin[6];
25 int m_UseCustomColor;
26 int m_ColorBody;
27 int m_ColorFeet;
28};
29
30struct CGhostCharacter_NoTick
31{
32 int m_X;
33 int m_Y;
34 int m_VelX;
35 int m_VelY;
36 int m_Angle;
37 int m_Direction;
38 int m_Weapon;
39 int m_HookState;
40 int m_HookX;
41 int m_HookY;
42 int m_AttackTick;
43};
44
45struct CGhostCharacter : public CGhostCharacter_NoTick
46{
47 int m_Tick;
48};
49
50class CGhost : public CComponent
51{
52private:
53 enum
54 {
55 MAX_ACTIVE_GHOSTS = 256,
56 };
57
58 class CGhostPath
59 {
60 int m_ChunkSize;
61 int m_NumItems;
62
63 std::vector<CGhostCharacter *> m_vpChunks;
64
65 public:
66 CGhostPath() { Reset(); }
67 ~CGhostPath() { Reset(); }
68 CGhostPath(const CGhostPath &Other) = delete;
69 CGhostPath &operator=(const CGhostPath &Other) = delete;
70
71 CGhostPath(CGhostPath &&Other) noexcept;
72 CGhostPath &operator=(CGhostPath &&Other) noexcept;
73
74 void Reset(int ChunkSize = 25 * 60); // one minute with default snap rate
75 void SetSize(int Items);
76 int Size() const { return m_NumItems; }
77
78 void Add(const CGhostCharacter &Char);
79 CGhostCharacter *Get(int Index);
80 };
81
82 class CGhostItem
83 {
84 public:
85 std::shared_ptr<CManagedTeeRenderInfo> m_pManagedTeeRenderInfo;
86 CGhostSkin m_Skin;
87 CGhostPath m_Path;
88 int m_StartTick;
89 char m_aPlayer[MAX_NAME_LENGTH];
90 int m_PlaybackPos;
91
92 CGhostItem() { Reset(); }
93
94 bool Empty() const { return m_Path.Size() == 0; }
95 void Reset()
96 {
97 m_pManagedTeeRenderInfo = nullptr;
98 m_Path.Reset();
99 m_StartTick = -1;
100 m_PlaybackPos = -1;
101 }
102 };
103
104 static const char *ms_pGhostDir;
105
106 class IGhostLoader *m_pGhostLoader;
107 class IGhostRecorder *m_pGhostRecorder;
108
109 CGhostItem m_aActiveGhosts[MAX_ACTIVE_GHOSTS];
110 CGhostItem m_CurGhost;
111
112 char m_aTmpFilename[IO_MAX_PATH_LENGTH];
113
114 int m_NewRenderTick = -1;
115 int m_StartRenderTick = -1;
116 int m_LastDeathTick = -1;
117 bool m_Recording = false;
118 bool m_Rendering = false;
119 bool m_RenderingStartedByServer = false;
120
121 static void SetGhostSkinData(CGhostSkin *pSkin, const char *pSkinName, int UseCustomColor, int ColorBody, int ColorFeet);
122 static void GetGhostCharacter(CGhostCharacter *pGhostChar, const CNetObj_Character *pChar, const CNetObj_DDNetCharacter *pDDnetChar);
123 static void GetNetObjCharacter(CNetObj_Character *pChar, const CGhostCharacter *pGhostChar);
124
125 void GetPath(char *pBuf, int Size, const char *pPlayerName, int Time = -1) const;
126
127 void AddInfos(const CNetObj_Character *pChar, const CNetObj_DDNetCharacter *pDDnetChar);
128 int GetSlot() const;
129
130 void CheckStart();
131 void CheckStartLocal(bool Predicted);
132 void TryRenderStart(int Tick, bool ServerControl);
133
134 void StartRecord(int Tick);
135 void StopRecord(int Time = -1);
136 void StartRender(int Tick);
137 void StopRender();
138
139 void UpdateTeeRenderInfo(CGhostItem &Ghost);
140
141 static void ConGPlay(IConsole::IResult *pResult, void *pUserData);
142
143public:
144 bool m_AllowRestart;
145
146 int Sizeof() const override { return sizeof(*this); }
147
148 void OnRender() override;
149 void OnConsoleInit() override;
150 void OnReset() override;
151 void OnMessage(int MsgType, void *pRawMsg) override;
152 void OnMapLoad() override;
153 void OnShutdown() override;
154 void OnNewSnapshot() override;
155
156 void OnNewPredictedSnapshot();
157
158 int FreeSlots() const;
159 int Load(const char *pFilename);
160 void Unload(int Slot);
161 void UnloadAll();
162
163 void SaveGhost(CMenus::CGhostItem *pItem);
164
165 const char *GetGhostDir() const { return ms_pGhostDir; }
166
167 class IGhostLoader *GhostLoader() const { return m_pGhostLoader; }
168 class IGhostRecorder *GhostRecorder() const { return m_pGhostRecorder; }
169};
170
171#endif
172