From 8073f9685611222c180e1407c0bd9612981e4150 Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Sat, 8 Nov 2025 15:51:09 -0600 Subject: [PATCH] Various hooks to support anchor (#5929) --- .../GameInteractor_HookTable.h | 11 +++++- .../game-interactor/GameInteractor_Hooks.cpp | 36 +++++++++++++++++-- .../game-interactor/GameInteractor_Hooks.h | 9 ++++- .../vanilla-behavior/GIVanillaBehavior.h | 17 +++++++++ .../Enhancements/randomizer/item_location.cpp | 2 ++ .../randomizer/randomizer_entrance.c | 3 ++ soh/soh/SaveManager.cpp | 2 +- soh/soh/util.cpp | 1 + soh/src/code/z_actor.c | 34 +++++++++++++----- .../ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c | 7 ++-- .../z_bg_hidan_kowarerukabe.c | 4 ++- .../actors/ovl_Door_Gerudo/z_door_gerudo.c | 2 ++ .../actors/ovl_Door_Shutter/z_door_shutter.c | 1 + .../overlays/actors/ovl_En_Door/z_en_door.c | 1 + 14 files changed, 114 insertions(+), 16 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 0f694660c..30aa16a2a 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -23,11 +23,15 @@ DEFINE_HOOK(OnFlagUnset, (int16_t flagType, int16_t flag)); DEFINE_HOOK(OnSceneSpawnActors, ()); DEFINE_HOOK(OnPlayerUpdate, ()); DEFINE_HOOK(OnSetDoAction, (uint16_t action)); +DEFINE_HOOK(OnPlayerSfx, (u16 sfxId)); DEFINE_HOOK(OnOcarinaSongAction, ()); DEFINE_HOOK(OnCuccoOrChickenHatch, ()); DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price)); +DEFINE_HOOK(OnDungeonKeyUsed, (uint16_t mapIndex)); +DEFINE_HOOK(ShouldActorInit, (void* actor, bool* result)); DEFINE_HOOK(OnActorInit, (void* actor)); DEFINE_HOOK(OnActorSpawn, (void* actor)); +DEFINE_HOOK(ShouldActorUpdate, (void* actor, bool* result)); DEFINE_HOOK(OnActorUpdate, (void* actor)); DEFINE_HOOK(OnActorKill, (void* actor)); DEFINE_HOOK(OnActorDestroy, (void* actor)); @@ -45,7 +49,7 @@ DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(OnPlayDrawBegin, ()); DEFINE_HOOK(OnPlayDrawEnd, ()); DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs)); -DEFINE_HOOK(OnSaveFile, (int32_t fileNum)); +DEFINE_HOOK(OnSaveFile, (int32_t fileNum, int32_t sectionID)); DEFINE_HOOK(OnLoadFile, (int32_t fileNum)); DEFINE_HOOK(OnDeleteFile, (int32_t fileNum)); @@ -77,3 +81,8 @@ DEFINE_HOOK(OnKaleidoUpdate, ()); // Audio DEFINE_HOOK(OnSeqPlayerInit, (int32_t playerIdx, int32_t seqId)); + +// Rando +DEFINE_HOOK(OnRandoSetCheckStatus, (RandomizerCheck rc, RandomizerCheckStatus status)); +DEFINE_HOOK(OnRandoSetIsSkipped, (RandomizerCheck rc, bool isSkipped)); +DEFINE_HOOK(OnRandoEntranceDiscovered, (u16 entranceIndex, u8 isReversedEntrance)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 7d5a243d2..fda2e4709 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -89,6 +89,10 @@ void GameInteractor_ExecuteOnSetDoAction(uint16_t action) { GameInteractor::Instance->ExecuteHooks(action); } +void GameInteractor_ExecuteOnPlayerSfx(u16 sfxId) { + GameInteractor::Instance->ExecuteHooks(sfxId); +} + void GameInteractor_ExecuteOnOcarinaSongAction() { GameInteractor::Instance->ExecuteHooks(); } @@ -101,6 +105,19 @@ void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t pr GameInteractor::Instance->ExecuteHooks(cursorIndex, price); } +void GameInteractor_ExecuteOnDungeonKeyUsedHooks(uint16_t mapIndex) { + GameInteractor::Instance->ExecuteHooks(mapIndex); +} + +bool GameInteractor_ShouldActorInit(void* actor) { + bool result = true; + GameInteractor::Instance->ExecuteHooks(actor, &result); + GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor, &result); + GameInteractor::Instance->ExecuteHooksForPtr((uintptr_t)actor, actor, &result); + GameInteractor::Instance->ExecuteHooksForFilter(actor, &result); + return result; +} + void GameInteractor_ExecuteOnActorInit(void* actor) { GameInteractor::Instance->ExecuteHooks(actor); GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor); @@ -115,6 +132,15 @@ void GameInteractor_ExecuteOnActorSpawn(void* actor) { GameInteractor::Instance->ExecuteHooksForFilter(actor); } +bool GameInteractor_ShouldActorUpdate(void* actor) { + bool result = true; + GameInteractor::Instance->ExecuteHooks(actor, &result); + GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor, &result); + GameInteractor::Instance->ExecuteHooksForPtr((uintptr_t)actor, actor, &result); + GameInteractor::Instance->ExecuteHooksForFilter(actor, &result); + return result; +} + void GameInteractor_ExecuteOnActorUpdate(void* actor) { GameInteractor::Instance->ExecuteHooks(actor); GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor); @@ -217,8 +243,8 @@ bool GameInteractor_Should(GIVanillaBehavior flag, u32 result, ...) { // MARK: - Save Files -void GameInteractor_ExecuteOnSaveFile(int32_t fileNum) { - GameInteractor::Instance->ExecuteHooks(fileNum); +void GameInteractor_ExecuteOnSaveFile(int32_t fileNum, int32_t sectionID) { + GameInteractor::Instance->ExecuteHooks(fileNum, sectionID); } void GameInteractor_ExecuteOnLoadFile(int32_t fileNum) { @@ -332,3 +358,9 @@ void GameInteractor_ExecuteOnKaleidoUpdate() { void GameInteractor_ExecuteOnSeqPlayerInit(int32_t playerIdx, int32_t seqId) { GameInteractor::Instance->ExecuteHooks(playerIdx, seqId); } + +// MARK: - Rando +void GameInteractor_ExecuteOnRandoEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) { + GameInteractor::Instance->ExecuteHooks(entranceIndex, + isReversedEntrance); +} diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 4c3a600e7..b39513195 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -26,10 +26,13 @@ void GameInteractor_ExecuteOnFlagUnset(int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnSceneSpawnActors(); void GameInteractor_ExecuteOnPlayerUpdate(); void GameInteractor_ExecuteOnSetDoAction(uint16_t action); +void GameInteractor_ExecuteOnPlayerSfx(u16 sfxId); void GameInteractor_ExecuteOnOcarinaSongAction(); void GameInteractor_ExecuteOnCuccoOrChickenHatch(); +bool GameInteractor_ShouldActorInit(void* actor); void GameInteractor_ExecuteOnActorInit(void* actor); void GameInteractor_ExecuteOnActorSpawn(void* actor); +bool GameInteractor_ShouldActorUpdate(void* actor); void GameInteractor_ExecuteOnActorUpdate(void* actor); void GameInteractor_ExecuteOnActorKill(void* actor); void GameInteractor_ExecuteOnActorDestroy(void* actor); @@ -44,13 +47,14 @@ void GameInteractor_ExecuteOnPlayerFirstPersonControl(Player* player); void GameInteractor_ExecuteOnPlayerShieldControl(float_t* sp50, float_t* sp54); void GameInteractor_ExecuteOnPlayerProcessStick(); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); +void GameInteractor_ExecuteOnDungeonKeyUsedHooks(uint16_t mapIndex); void GameInteractor_ExecuteOnPlayDestroy(); void GameInteractor_ExecuteOnPlayDrawBegin(); void GameInteractor_ExecuteOnPlayDrawEnd(); bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...); // MARK: - Save Files -void GameInteractor_ExecuteOnSaveFile(int32_t fileNum); +void GameInteractor_ExecuteOnSaveFile(int32_t fileNum, int32_t sectionID); void GameInteractor_ExecuteOnLoadFile(int32_t fileNum); void GameInteractor_ExecuteOnDeleteFile(int32_t fileNum); @@ -89,6 +93,9 @@ void GameInteractor_ExecuteOnKaleidoUpdate(); // Mark: - Audio void GameInteractor_ExecuteOnSeqPlayerInit(int32_t playerIdx, int32_t seqId); +// MARK: - Rando +void GameInteractor_ExecuteOnRandoEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance); + #ifdef __cplusplus } #endif diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 08d663c7e..fe437907a 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2318,6 +2318,23 @@ typedef enum { // - `*Player` VB_SET_STATIC_FLOOR_TYPE, + // #### `result` + // ```c + // (this->collider.base.acFlags & AC_HIT) && !Player_InCsMode(play) && + // (player->meleeWeaponAnimation == 22 || player->meleeWeaponAnimation == 23) + // ``` + // #### `args` + // - `*BgHidanDalm` + VB_HAMMER_TOTEM_BREAK, + + // #### `result` + // ```c + // Actor_GetCollidedExplosive(play, &this->collider.base) != NULL + // ``` + // #### `args` + // - `*BgHidanKowarerukabe` + VB_FIRE_TEMPLE_BOMBABLE_WALL_BREAK, + } GIVanillaBehavior; #endif diff --git a/soh/soh/Enhancements/randomizer/item_location.cpp b/soh/soh/Enhancements/randomizer/item_location.cpp index f7587ec5e..b5325c604 100644 --- a/soh/soh/Enhancements/randomizer/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/item_location.cpp @@ -134,6 +134,7 @@ bool ItemLocation::HasObtained() const { void ItemLocation::SetCheckStatus(RandomizerCheckStatus status_) { status = status_; + GameInteractor::Instance->ExecuteHooks(rc, status); } RandomizerCheckStatus ItemLocation::GetCheckStatus() { @@ -142,6 +143,7 @@ RandomizerCheckStatus ItemLocation::GetCheckStatus() { void ItemLocation::SetIsSkipped(bool isSkipped_) { isSkipped = isSkipped_; + GameInteractor::Instance->ExecuteHooks(rc, isSkipped); } bool ItemLocation::GetIsSkipped() { diff --git a/soh/soh/Enhancements/randomizer/randomizer_entrance.c b/soh/soh/Enhancements/randomizer/randomizer_entrance.c index 36e5dd983..82082a9d0 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_entrance.c +++ b/soh/soh/Enhancements/randomizer/randomizer_entrance.c @@ -16,6 +16,7 @@ #include "global.h" #include "entrance.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" extern PlayState* gPlayState; @@ -812,6 +813,8 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) { return; } + GameInteractor_ExecuteOnRandoEntranceDiscovered(entranceIndex, isReversedEntrance); + u16 bitsPerIndex = sizeof(u32) * 8; u32 idx = entranceIndex / bitsPerIndex; if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) { diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index d15c01086..c17ab0a54 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -1069,7 +1069,7 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se delete saveContext; InitMeta(fileNum); - GameInteractor::Instance->ExecuteHooks(fileNum); + GameInteractor::Instance->ExecuteHooks(fileNum, sectionID); SPDLOG_INFO("Save File Finish - fileNum: {}", fileNum); saveMtx.unlock(); } diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index c05f03f0c..159ccfbe2 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -120,6 +120,7 @@ std::vector sceneNames = { "Castle Hedge Maze (Early)", "Sasa Test", "Treasure Chest Room", + "Unknown", }; std::vector itemNamesEng = { diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index d8263f5de..d1386a48a 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1256,10 +1256,16 @@ void Actor_Init(Actor* actor, PlayState* play) { ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f); if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { Actor_SetObjectDependency(play, actor); - actor->init(actor, play); - actor->init = NULL; - GameInteractor_ExecuteOnActorInit(actor); + if (GameInteractor_ShouldActorInit(actor)) { + actor->init(actor, play); + actor->init = NULL; + + GameInteractor_ExecuteOnActorInit(actor); + } else { + actor->init = NULL; + Actor_Kill(actor); + } } } @@ -2244,6 +2250,10 @@ void Player_PlaySfx(Actor* actor, u16 sfxId) { Audio_PlaySoundGeneral(sfxId, &actor->projectedPos, 4, &freqMultiplier, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); } + + if (actor->id == ACTOR_PLAYER) { + GameInteractor_ExecuteOnPlayerSfx(sfxId); + } } void Audio_PlayActorSound2(Actor* actor, u16 sfxId) { @@ -2624,10 +2634,16 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { if (actor->init != NULL) { if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { Actor_SetObjectDependency(play, actor); - actor->init(actor, play); - actor->init = NULL; - GameInteractor_ExecuteOnActorInit(actor); + if (GameInteractor_ShouldActorInit(actor)) { + actor->init(actor, play); + actor->init = NULL; + + GameInteractor_ExecuteOnActorInit(actor); + } else { + actor->init = NULL; + Actor_Kill(actor); + } } actor = actor->next; } else if (!Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { @@ -2670,8 +2686,10 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { if (actor->colorFilterTimer != 0) { actor->colorFilterTimer--; } - actor->update(actor, play); - GameInteractor_ExecuteOnActorUpdate(actor); + if (GameInteractor_ShouldActorUpdate(actor)) { + actor->update(actor, play); + GameInteractor_ExecuteOnActorUpdate(actor); + } func_8003F8EC(play, &play->colCtx.dyna, actor); } diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c index 725bfacd6..b4e3ca3c3 100644 --- a/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c @@ -6,6 +6,7 @@ #include "z_bg_hidan_dalm.h" #include "objects/object_hidan_objects/object_hidan_objects.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -126,8 +127,10 @@ void BgHidanDalm_Destroy(Actor* thisx, PlayState* play) { void BgHidanDalm_Wait(BgHidanDalm* this, PlayState* play) { Player* player = GET_PLAYER(play); - if ((this->collider.base.acFlags & AC_HIT) && !Player_InCsMode(play) && - (player->meleeWeaponAnimation == 22 || player->meleeWeaponAnimation == 23)) { + if (GameInteractor_Should(VB_HAMMER_TOTEM_BREAK, + (this->collider.base.acFlags & AC_HIT) && !Player_InCsMode(play) && + (player->meleeWeaponAnimation == 22 || player->meleeWeaponAnimation == 23), + this)) { this->collider.base.acFlags &= ~AC_HIT; if ((this->collider.elements[0].info.bumperFlags & BUMP_HIT) || (this->collider.elements[1].info.bumperFlags & BUMP_HIT)) { diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.c index 328d1f4c0..0625c6ab8 100644 --- a/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.c +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.c @@ -8,6 +8,7 @@ #include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" #include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" #include "objects/object_hidan_objects/object_hidan_objects.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -303,7 +304,8 @@ void BgHidanKowarerukabe_Update(Actor* thisx, PlayState* play) { BgHidanKowarerukabe* this = (BgHidanKowarerukabe*)thisx; s32 pad; - if (Actor_GetCollidedExplosive(play, &this->collider.base) != NULL) { + if (GameInteractor_Should(VB_FIRE_TEMPLE_BOMBABLE_WALL_BREAK, + Actor_GetCollidedExplosive(play, &this->collider.base) != NULL, this)) { BgHidanKowarerukabe_Break(this, play); Flags_SetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F); diff --git a/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c index 34796f980..29eccfdd7 100644 --- a/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c +++ b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c @@ -6,6 +6,7 @@ #include "z_door_gerudo.h" #include "objects/object_door_gerudo/object_door_gerudo.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -103,6 +104,7 @@ void func_8099485C(DoorGerudo* this, PlayState* play) { gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] -= 1; Flags_SetSwitch(play, this->dyna.actor.params & 0x3F); Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_CHAIN_KEY_UNLOCK); + GameInteractor_ExecuteOnDungeonKeyUsedHooks(gSaveContext.mapIndex); } else { s32 direction = func_80994750(this, play); diff --git a/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c index ea983771f..307aee785 100644 --- a/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c +++ b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c @@ -391,6 +391,7 @@ void func_80996B0C(DoorShutter* this, PlayState* play) { if (this->doorType != SHUTTER_BOSS) { gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex]--; Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_CHAIN_KEY_UNLOCK); + GameInteractor_ExecuteOnDungeonKeyUsedHooks(gSaveContext.mapIndex); } else { Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_CHAIN_KEY_UNLOCK_B); } diff --git a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c index e714cb211..268355094 100644 --- a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c +++ b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c @@ -209,6 +209,7 @@ void EnDoor_Idle(EnDoor* this, PlayState* play) { Flags_SetSwitch(play, this->actor.params & 0x3F); } Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHAIN_KEY_UNLOCK); + GameInteractor_ExecuteOnDungeonKeyUsedHooks(gSaveContext.mapIndex); } } else if (!Player_InCsMode(play)) { if (fabsf(playerPosRelToDoor.y) < 20.0f && fabsf(playerPosRelToDoor.x) < 20.0f &&