#include "game/engine/events.h" #include #include "re/hook.h" namespace game { namespace { std::vector& TickCallbacks() { static std::vector callbacks; return callbacks; } std::vector& LoadedCallbacks() { static std::vector callbacks; return callbacks; } std::vector& GameEndCallbacks() { static std::vector callbacks; return callbacks; } std::vector& GameStartCallbacks() { static std::vector callbacks; return callbacks; } } // namespace void OnEngineTick(EngineTickFn callback) { TickCallbacks().push_back(callback); } void OnSaveGameLoaded(SaveGameLoadedFn callback) { LoadedCallbacks().push_back(callback); } void OnGameEnd(GameEndFn callback) { GameEndCallbacks().push_back(callback); } void OnGameStart(GameStartFn callback) { GameStartCallbacks().push_back(callback); } void NotifySaveGameLoaded() { for (SaveGameLoadedFn callback : LoadedCallbacks()) { callback(); } } // cEngine::Tick (0x60f1d0, 10 Hz): processes queued events, then the mouse's // hold-to-move re-aim orders. void __fastcall Engine_Tick(void* self, void* edx, void* event); RE_HOOK(0x0060f1d0, Engine_Tick); void __fastcall Engine_Tick(void* self, void* edx, void* event) { Engine_Tick_original(self, edx, event); if (self) { for (EngineTickFn callback : TickCallbacks()) { callback(static_cast(self)); } } } // cEngine::cEngine (0x60ac70, ret 4, called once at 0x60d7a3): the save game // and the hero are loaded inside it. void* __fastcall Engine_Construct(void* self, void* edx, uint32_t arg); RE_HOOK(0x0060ac70, Engine_Construct); void* __fastcall Engine_Construct(void* self, void* edx, uint32_t arg) { void* result = Engine_Construct_original(self, edx, arg); for (GameStartFn callback : GameStartCallbacks()) { callback(); } return result; } // cEngine::~cEngine (0x60d5e0, plain ret). void __fastcall Engine_Destruct(void* self, void* edx); RE_HOOK(0x0060d5e0, Engine_Destruct); void __fastcall Engine_Destruct(void* self, void* edx) { for (GameEndFn callback : GameEndCallbacks()) { callback(); } Engine_Destruct_original(self, edx); } } // namespace game