#ifndef GAME_INTERACTOR_H #define GAME_INTERACTOR_H #include #ifdef __cplusplus #include extern "C" { #endif #include "z64.h" #ifdef __cplusplus } #endif typedef enum { FLAG_NONE, FLAG_WEEK_EVENT_REG, FLAG_EVENT_INF, FLAG_SCENES_VISIBLE, FLAG_OWL_ACTIVATION, FLAG_PERM_SCENE_CHEST, FLAG_PERM_SCENE_SWITCH, FLAG_PERM_SCENE_CLEARED_ROOM, FLAG_PERM_SCENE_COLLECTIBLE, FLAG_PERM_SCENE_UNK_14, FLAG_PERM_SCENE_ROOMS, FLAG_CYCL_SCENE_CHEST, FLAG_CYCL_SCENE_SWITCH, FLAG_CYCL_SCENE_CLEARED_ROOM, FLAG_CYCL_SCENE_COLLECTIBLE, } FlagType; typedef enum { // Vanilla condition: gSaveContext.showTitleCard VB_SHOW_TITLE_CARD, VB_PLAY_ENTRANCE_CS, VB_DISABLE_FD_MASK, VB_DOGGY_RACE_SET_MAX_SPEED, VB_LOWER_RAZOR_SWORD_DURABILITY, VB_SET_BLAST_MASK_COOLDOWN_TIMER, VB_PATCH_POWER_CROUCH_STAB, VB_PATCH_SIDEROLL, VB_TATL_CONVERSATION_AVAILABLE, VB_PREVENT_MASK_TRANSFORMATION_CS, VB_RESET_PUTAWAY_TIMER, VB_SET_CLIMB_SPEED, VB_PREVENT_CLOCK_DISPLAY, VB_SONG_AVAILABLE_TO_PLAY, VB_USE_CUSTOM_CAMERA, VB_DELETE_OWL_SAVE, VB_MSG_SCRIPT_DEL_ITEM, VB_CONSIDER_BUNNY_HOOD_EQUIPPED, VB_USE_ITEM_EQUIP_MASK, VB_KALEIDO_DISPLAY_ITEM_TEXT, VB_USE_ITEM_CONSIDER_LINK_HUMAN, VB_DRAW_ITEM_EQUIPPED_OUTLINE, VB_PLAY_TRANSITION_CS, VB_TATL_INTERUPT_MSG3, VB_TATL_INTERUPT_MSG6, VB_ITEM_BE_RESTRICTED, VB_FLIP_HOP_VARIABLE, VB_DISABLE_LETTERBOX, VB_CLOCK_TOWER_OPENING_CONSIDER_THIS_FIRST_CYCLE, VB_DRAW_SLIME_BODY_ITEM, VB_ZTARGET_SPEED_CHECK, VB_THIEF_BIRD_STEAL, VB_PLAY_CREMIA_HUG_CUTSCENE, VB_FD_ALWAYS_WIELD_SWORD, VB_SHOULD_PUTAWAY, VB_ELEGY_CHECK_SCENE, VB_NEED_SCARECROW_SONG, VB_CHECK_HELD_ITEM_BUTTON_PRESS, VB_MAGIC_SPIN_ATTACK_CHECK_FORM, VB_TRANSFORM_THUNDER_MATRIX, } GIVanillaBehavior; typedef enum { GI_INVERT_CAMERA_RIGHT_STICK_X, GI_INVERT_CAMERA_RIGHT_STICK_Y, GI_INVERT_MOVEMENT_X, GI_INVERT_SHIELD_X, GI_INVERT_SHOP_X, GI_INVERT_HORSE_X, GI_INVERT_ZORA_SWIM_X, GI_INVERT_DEBUG_DPAD_X, GI_INVERT_TELESCOPE_X, GI_INVERT_FIRST_PERSON_AIM_X, GI_INVERT_FIRST_PERSON_AIM_Y, GI_INVERT_FIRST_PERSON_GYRO_X, GI_INVERT_FIRST_PERSON_GYRO_Y, GI_INVERT_FIRST_PERSON_RIGHT_STICK_X, GI_INVERT_FIRST_PERSON_RIGHT_STICK_Y, GI_INVERT_FIRST_PERSON_MOVING_X, } GIInvertType; typedef enum { GI_DPAD_OCARINA, GI_DPAD_EQUIP, } GIDpadType; #ifdef __cplusplus #include #include #include #include #include typedef uint32_t HOOK_ID; #define DEFINE_HOOK(name, args) \ struct name { \ typedef std::function fn; \ typedef std::function filter; \ } class GameInteractor { public: static GameInteractor* Instance; // Game State class State {}; // Game Hooks HOOK_ID nextHookId = 1; template struct RegisteredGameHooks { inline static std::unordered_map functions; inline static std::unordered_map> functionsForID; inline static std::unordered_map> functionsForPtr; inline static std::unordered_map> functionsForFilter; }; template struct HooksToUnregister { inline static std::vector hooks; inline static std::vector hooksForID; inline static std::vector hooksForPtr; inline static std::vector hooksForFilter; }; // General Hooks template HOOK_ID RegisterGameHook(typename H::fn h) { if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; while (RegisteredGameHooks::functions.find(this->nextHookId) != RegisteredGameHooks::functions.end()) { this->nextHookId++; } RegisteredGameHooks::functions[this->nextHookId] = h; return this->nextHookId++; } template void UnregisterGameHook(HOOK_ID hookId) { if (hookId == 0) return; HooksToUnregister::hooks.push_back(hookId); } template void ExecuteHooks(Args&&... args) { for (auto& hookId : HooksToUnregister::hooks) { RegisteredGameHooks::functions.erase(hookId); } HooksToUnregister::hooks.clear(); for (auto& hook : RegisteredGameHooks::functions) { hook.second(std::forward(args)...); } } // ID based Hooks template HOOK_ID RegisterGameHookForID(int32_t id, typename H::fn h) { if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; while (RegisteredGameHooks::functionsForID[id].find(this->nextHookId) != RegisteredGameHooks::functionsForID[id].end()) { this->nextHookId++; } RegisteredGameHooks::functionsForID[id][this->nextHookId] = h; return this->nextHookId++; } template void UnregisterGameHookForID(HOOK_ID hookId) { if (hookId == 0) return; HooksToUnregister::hooksForID.push_back(hookId); } template void ExecuteHooksForID(int32_t id, Args&&... args) { for (auto& hookId : HooksToUnregister::hooksForID) { for (auto it = RegisteredGameHooks::functionsForID[id].begin(); it != RegisteredGameHooks::functionsForID[id].end();) { if (it->first == hookId) { it = RegisteredGameHooks::functionsForID[id].erase(it); HooksToUnregister::hooksForID.erase(std::remove(HooksToUnregister::hooksForID.begin(), HooksToUnregister::hooksForID.end(), hookId), HooksToUnregister::hooksForID.end()); } else { ++it; } } } for (auto& hook : RegisteredGameHooks::functionsForID[id]) { hook.second(std::forward(args)...); } } // PTR based Hooks template HOOK_ID RegisterGameHookForPtr(uintptr_t ptr, typename H::fn h) { if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; while (RegisteredGameHooks::functionsForPtr[ptr].find(this->nextHookId) != RegisteredGameHooks::functionsForPtr[ptr].end()) { this->nextHookId++; } RegisteredGameHooks::functionsForPtr[ptr][this->nextHookId] = h; return this->nextHookId++; } template void UnregisterGameHookForPtr(HOOK_ID hookId) { if (hookId == 0) return; HooksToUnregister::hooksForPtr.push_back(hookId); } template void ExecuteHooksForPtr(uintptr_t ptr, Args&&... args) { for (auto& hookId : HooksToUnregister::hooksForPtr) { for (auto it = RegisteredGameHooks::functionsForPtr[ptr].begin(); it != RegisteredGameHooks::functionsForPtr[ptr].end();) { if (it->first == hookId) { it = RegisteredGameHooks::functionsForPtr[ptr].erase(it); HooksToUnregister::hooksForPtr.erase(std::remove(HooksToUnregister::hooksForPtr.begin(), HooksToUnregister::hooksForPtr.end(), hookId), HooksToUnregister::hooksForPtr.end()); } else { ++it; } } } for (auto& hook : RegisteredGameHooks::functionsForPtr[ptr]) { hook.second(std::forward(args)...); } } // Filter based Hooks template HOOK_ID RegisterGameHookForFilter(typename H::filter f, typename H::fn h) { if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1; while (RegisteredGameHooks::functionsForFilter.find(this->nextHookId) != RegisteredGameHooks::functionsForFilter.end()) { this->nextHookId++; } RegisteredGameHooks::functionsForFilter[this->nextHookId] = std::make_pair(f, h); return this->nextHookId++; } template void UnregisterGameHookForFilter(HOOK_ID hookId) { if (hookId == 0) return; HooksToUnregister::hooksForFilter.push_back(hookId); } template void ExecuteHooksForFilter(Args&&... args) { for (auto& hookId : HooksToUnregister::hooksForFilter) { RegisteredGameHooks::functionsForFilter.erase(hookId); } HooksToUnregister::hooksForFilter.clear(); for (auto& hook : RegisteredGameHooks::functionsForFilter) { if (hook.second.first(std::forward(args)...)) { hook.second.second(std::forward(args)...); } } } class HookFilter { public: static auto ActorNotPlayer(Actor* actor) { return actor->id != ACTOR_PLAYER; } // For use with Should hooks static auto SActorNotPlayer(Actor* actor, bool* result) { return actor->id != ACTOR_PLAYER; } static auto ActorMatchIdAndParams(int16_t id, int16_t params) { return [id, params](Actor* actor) { return actor->id == id && actor->params == params; }; } // For use with Should hooks static auto SActorMatchIdAndParams(int16_t id, int16_t params) { return [id, params](Actor* actor, bool* result) { return actor->id == id && actor->params == params; }; } }; DEFINE_HOOK(OnFileDropped, (std::string path)); DEFINE_HOOK(OnGameStateMainFinish, ()); DEFINE_HOOK(OnGameStateDrawFinish, ()); DEFINE_HOOK(OnGameStateUpdate, ()); DEFINE_HOOK(OnConsoleLogoUpdate, ()); DEFINE_HOOK(OnKaleidoUpdate, (PauseContext * pauseCtx)); DEFINE_HOOK(BeforeKaleidoDrawPage, (PauseContext * pauseCtx, u16 pauseIndex)); DEFINE_HOOK(AfterKaleidoDrawPage, (PauseContext * pauseCtx, u16 pauseIndex)); DEFINE_HOOK(OnSaveInit, (s16 fileNum)); DEFINE_HOOK(BeforeEndOfCycleSave, ()); DEFINE_HOOK(AfterEndOfCycleSave, ()); DEFINE_HOOK(BeforeMoonCrashSaveReset, ()); DEFINE_HOOK(OnSceneInit, (s8 sceneId, s8 spawnNum)); DEFINE_HOOK(OnRoomInit, (s8 sceneId, s8 roomNum)); DEFINE_HOOK(AfterRoomSceneCommands, (s8 sceneId, s8 roomNum)); DEFINE_HOOK(OnPlayDrawWorldEnd, ()); DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(ShouldActorInit, (Actor * actor, bool* should)); DEFINE_HOOK(OnActorInit, (Actor * actor)); DEFINE_HOOK(ShouldActorUpdate, (Actor * actor, bool* should)); DEFINE_HOOK(OnActorUpdate, (Actor * actor)); DEFINE_HOOK(ShouldActorDraw, (Actor * actor, bool* should)); DEFINE_HOOK(OnActorDraw, (Actor * actor)); DEFINE_HOOK(OnActorKill, (Actor * actor)); DEFINE_HOOK(OnActorDestroy, (Actor * actor)); DEFINE_HOOK(OnPlayerPostLimbDraw, (Player * player, s32 limbIndex)); DEFINE_HOOK(OnSceneFlagSet, (s16 sceneId, FlagType flagType, u32 flag)); DEFINE_HOOK(OnSceneFlagUnset, (s16 sceneId, FlagType flagType, u32 flag)); DEFINE_HOOK(OnFlagSet, (FlagType flagType, u32 flag)); DEFINE_HOOK(OnFlagUnset, (FlagType flagType, u32 flag)); DEFINE_HOOK(AfterCameraUpdate, (Camera * camera)); DEFINE_HOOK(OnCameraChangeModeFlags, (Camera * camera)); DEFINE_HOOK(OnCameraChangeSettingsFlags, (Camera * camera)); DEFINE_HOOK(OnPassPlayerInputs, (Input * input)); DEFINE_HOOK(OnOpenText, (u16 textId)); DEFINE_HOOK(ShouldItemGive, (u8 item, bool* should)); DEFINE_HOOK(OnItemGive, (u8 item)); DEFINE_HOOK(ShouldVanillaBehavior, (GIVanillaBehavior flag, bool* should, va_list originalArgs)); }; extern "C" { #endif // __cplusplus void GameInteractor_ExecuteOnGameStateMainFinish(); void GameInteractor_ExecuteOnGameStateDrawFinish(); void GameInteractor_ExecuteOnGameStateUpdate(); void GameInteractor_ExecuteOnConsoleLogoUpdate(); void GameInteractor_ExecuteOnKaleidoUpdate(PauseContext* pauseCtx); void GameInteractor_ExecuteBeforeKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex); void GameInteractor_ExecuteAfterKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex); void GameInteractor_ExecuteOnSaveInit(s16 fileNum); void GameInteractor_ExecuteBeforeEndOfCycleSave(); void GameInteractor_ExecuteAfterEndOfCycleSave(); void GameInteractor_ExecuteBeforeMoonCrashSaveReset(); void GameInteractor_ExecuteOnSceneInit(s16 sceneId, s8 spawnNum); void GameInteractor_ExecuteOnRoomInit(s16 sceneId, s8 roomNum); void GameInteractor_ExecuteAfterRoomSceneCommands(s16 sceneId, s8 roomNum); void GameInteractor_ExecuteOnPlayDrawWorldEnd(); void GameInteractor_ExecuteOnPlayDestroy(); bool GameInteractor_ShouldActorInit(Actor* actor); void GameInteractor_ExecuteOnActorInit(Actor* actor); bool GameInteractor_ShouldActorUpdate(Actor* actor); void GameInteractor_ExecuteOnActorUpdate(Actor* actor); bool GameInteractor_ShouldActorDraw(Actor* actor); void GameInteractor_ExecuteOnActorDraw(Actor* actor); void GameInteractor_ExecuteOnActorKill(Actor* actor); void GameInteractor_ExecuteOnActorDestroy(Actor* actor); void GameInteractor_ExecuteOnPlayerPostLimbDraw(Player* player, s32 limbIndex); void GameInteractor_ExecuteOnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag); void GameInteractor_ExecuteOnSceneFlagUnset(s16 sceneId, FlagType flagType, u32 flag); void GameInteractor_ExecuteOnFlagSet(FlagType flagType, u32 flag); void GameInteractor_ExecuteOnFlagUnset(FlagType flagType, u32 flag); void GameInteractor_ExecuteAfterCameraUpdate(Camera* camera); void GameInteractor_ExecuteOnCameraChangeModeFlags(Camera* camera); void GameInteractor_ExecuteOnCameraChangeSettingsFlags(Camera* camera); void GameInteractor_ExecuteOnPassPlayerInputs(Input* input); void GameInteractor_ExecuteOnOpenText(u16 textId); bool GameInteractor_ShouldItemGive(u8 item); void GameInteractor_ExecuteOnItemGive(u8 item); bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...); #define REGISTER_VB_SHOULD(flag, body) \ GameInteractor::Instance->RegisterGameHookForID( \ flag, [](GIVanillaBehavior _, bool* should, va_list originalArgs) { \ va_list args; \ va_copy(args, originalArgs); \ body; \ va_end(args); \ }) int GameInteractor_InvertControl(GIInvertType type); uint32_t GameInteractor_Dpad(GIDpadType type, uint32_t buttonCombo); #ifdef __cplusplus } #endif #endif // GAME_INTERACTOR_H