| 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 | |
| 12 | struct CNetObj_Character; |
| 13 | |
| 14 | enum |
| 15 | { |
| 16 | GHOSTDATA_TYPE_SKIN = 0, |
| 17 | GHOSTDATA_TYPE_CHARACTER_NO_TICK, |
| 18 | GHOSTDATA_TYPE_CHARACTER, |
| 19 | GHOSTDATA_TYPE_START_TICK |
| 20 | }; |
| 21 | |
| 22 | struct CGhostSkin |
| 23 | { |
| 24 | int m_aSkin[6]; |
| 25 | int m_UseCustomColor; |
| 26 | int m_ColorBody; |
| 27 | int m_ColorFeet; |
| 28 | }; |
| 29 | |
| 30 | struct 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 | |
| 45 | struct CGhostCharacter : public CGhostCharacter_NoTick |
| 46 | { |
| 47 | int m_Tick; |
| 48 | }; |
| 49 | |
| 50 | class CGhost : public CComponent |
| 51 | { |
| 52 | private: |
| 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 | |
| 143 | public: |
| 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 (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 | |