From 2af952b1803088766bb2a2dbbddaa33ac32f999b Mon Sep 17 00:00:00 2001 From: Patrick12115 <115201185+Patrick12115@users.noreply.github.com> Date: Sun, 2 Apr 2023 21:11:08 -0400 Subject: [PATCH] [Time Saver Enhancement] Nighttime GS Always Spawn (#2611) * Nighttime GS Always Spawn * add hook onSceneSpawnActors * implement onSceneSpawnActors hook to spawn GS in various day scenes * handle the kak tree skull during the day * Update mods.cpp * Add the & * Fixes * Update soh/soh/Enhancements/mods.cpp Co-authored-by: Adam Bird --------- Co-authored-by: Adam Bird Co-authored-by: Adam Bird --- .../game-interactor/GameInteractor.h | 1 + .../game-interactor/GameInteractor_Hooks.cpp | 4 ++ .../game-interactor/GameInteractor_Hooks.h | 1 + soh/soh/Enhancements/mods.cpp | 59 +++++++++++++++++++ soh/soh/Enhancements/presets.h | 1 + soh/soh/GameMenuBar.cpp | 2 + soh/src/code/z_actor.c | 1 + soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c | 2 +- .../actors/ovl_En_Wood02/z_en_wood02.c | 9 +++ 9 files changed, 79 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 59e322ed9..c2dd2c05f 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -148,6 +148,7 @@ public: DEFINE_HOOK(OnSaleEnd, void(GetItemEntry itemEntry)); DEFINE_HOOK(OnTransitionEnd, void(int16_t sceneNum)); DEFINE_HOOK(OnSceneInit, void(int16_t sceneNum)); + DEFINE_HOOK(OnSceneSpawnActors, void()); DEFINE_HOOK(OnPlayerUpdate, void()); DEFINE_HOOK(OnOcarinaSongAction, void()); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 90074f4ff..e5159849e 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -30,6 +30,10 @@ void GameInteractor_ExecuteOnSceneInitHooks(int16_t sceneNum) { GameInteractor::Instance->ExecuteHooks(sceneNum); } +void GameInteractor_ExecuteOnSceneSpawnActors() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnPlayerUpdate() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 7c9661cf0..b8029914f 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -8,6 +8,7 @@ extern "C" void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry) extern "C" void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); extern "C" void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); +extern "C" void GameInteractor_ExecuteOnSceneSpawnActors(); extern "C" void GameInteractor_ExecuteOnPlayerUpdate(); extern "C" void GameInteractor_ExecuteOnOcarinaSongAction(); extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor); diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index cffd00ffe..cdbacdf06 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -14,6 +14,8 @@ extern PlayState* gPlayState; extern void Play_PerformSave(PlayState* play); extern s32 Health_ChangeBy(PlayState* play, s16 healthChange); extern void Rupees_ChangeBy(s16 rupeeChange); +extern Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 posX, f32 posY, f32 posZ, + s16 rotX, s16 rotY, s16 rotZ, s16 params, s16 canRandomize); extern void Inventory_ChangeEquipment(s16 equipment, u16 value); } bool performDelayedSave = false; @@ -350,6 +352,62 @@ void RegisterRupeeDash() { }); } +struct DayTimeGoldSkulltulas { + uint16_t scene; + uint16_t room; + bool forChild; + std::vector actorEntries; +}; + +using DayTimeGoldSkulltulasList = std::vector; + +void RegisterDaytimeGoldSkultullas() { + GameInteractor::Instance->RegisterGameHook([]() { + if (!CVarGetInteger("gNightGSAlwaysSpawn", 0)) { + return; + } + + // Gold Skulltulas that are not part of the scene actor list during the day + // Actor values copied from the night time scene actor list + static const DayTimeGoldSkulltulasList dayTimeGoldSkulltulas = { + // Graveyard + { SCENE_SPOT02, 1, true, { { ACTOR_EN_SW, { 156, 315, 795 }, { 16384, -32768, 0 }, -20096 } } }, + // ZF + { SCENE_SPOT08, 0, true, { { ACTOR_EN_SW, { -1891, 187, 1911 }, { 16384, 18022, 0 }, -19964 } } }, + // GF + { SCENE_SPOT12, 0, false, { { ACTOR_EN_SW, { 1598, 999, -2008 }, { 16384, -16384, 0 }, -19198 } } }, + { SCENE_SPOT12, 1, false, { { ACTOR_EN_SW, { 3377, 1734, -4935 }, { 16384, 0, 0 }, -19199 } } }, + // Kak + { SCENE_SPOT01, 0, false, { { ACTOR_EN_SW, { -18, 540, 1800 }, { 0, -32768, 0 }, -20160 } } }, + { SCENE_SPOT01, + 0, + true, + { { ACTOR_EN_SW, { -465, 377, -888 }, { 0, 28217, 0 }, -20222 }, + { ACTOR_EN_SW, { 5, 686, -171 }, { 0, -32768, 0 }, -20220 }, + { ACTOR_EN_SW, { 324, 270, 905 }, { 16384, 0, 0 }, -20216 }, + { ACTOR_EN_SW, { -602, 120, 1120 }, { 16384, 0, 0 }, -20208 } } }, + // LLR + { SCENE_SPOT20, + 0, + true, + { { ACTOR_EN_SW, { -2344, 180, 672 }, { 16384, 22938, 0 }, -29695 }, + { ACTOR_EN_SW, { 808, 48, 326 }, { 16384, 0, 0 }, -29694 }, + { ACTOR_EN_SW, { 997, 286, -2698 }, { 16384, -16384, 0 }, -29692 } } }, + }; + + for (const auto& dayTimeGS : dayTimeGoldSkulltulas) { + if (IS_DAY && dayTimeGS.forChild == LINK_IS_CHILD && dayTimeGS.scene == gPlayState->sceneNum && + dayTimeGS.room == gPlayState->roomCtx.curRoom.num) { + for (const auto& actorEntry : dayTimeGS.actorEntries) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, actorEntry.id, actorEntry.pos.x, actorEntry.pos.y, + actorEntry.pos.z, actorEntry.rot.x, actorEntry.rot.y, actorEntry.rot.z, + actorEntry.params, false); + } + } + } + }); +} + void RegisterHyperBosses() { GameInteractor::Instance->RegisterGameHook([](void* refActor) { // Run the update function a second time to make bosses move and act twice as fast. @@ -450,6 +508,7 @@ void InitMods() { RegisterSwitchAge(); RegisterOcarinaTimeTravel(); RegisterAutoSave(); + RegisterDaytimeGoldSkultullas(); RegisterRupeeDash(); RegisterHyperBosses(); RegisterBonkDamage(); diff --git a/soh/soh/Enhancements/presets.h b/soh/soh/Enhancements/presets.h index cf617021e..1a2319cdd 100644 --- a/soh/soh/Enhancements/presets.h +++ b/soh/soh/Enhancements/presets.h @@ -181,6 +181,7 @@ const std::vector enhancementsCvars = { "gStaticExplosionRadius", "gNoInputForCredits", "gFastFarores", + "gNightGSAlwaysSpawn", }; const std::vector randomizerCvars = { diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index 075ea4134..7b9cbb288 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -354,6 +354,8 @@ namespace GameMenuBar { "to the guard next to the gate."); UIWidgets::PaddedEnhancementCheckbox("Faster Farore's Wind", "gFastFarores", true, false); UIWidgets::Tooltip("Greatly decreases cast time of Farore's Wind magic spell."); + UIWidgets::PaddedEnhancementCheckbox("Nighttime GS Always Spawn", "gNightGSAlwaysSpawn", true, false); + UIWidgets::Tooltip("Nighttime Skulltulas will spawn during both day and night."); UIWidgets::PaddedEnhancementCheckbox("Dampe Appears All Night", "gDampeAllNight", true, false); UIWidgets::Tooltip("Makes Dampe appear anytime during it's night, not just his usual working hours."); UIWidgets::PaddedEnhancementCheckbox("Time Travel with the Song of Time", "gTimeTravel", true, false); diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index ef7708919..26e0b6dfe 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -2516,6 +2516,7 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { Actor_SpawnEntry(&play->actorCtx, actorEntry++, play); } play->numSetupActors = 0; + GameInteractor_ExecuteOnSceneSpawnActors(); } if (actorCtx->unk_02 != 0) { diff --git a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c index c26e017cc..db864d403 100644 --- a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c +++ b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -550,7 +550,7 @@ void func_80B0D590(EnSw* this, PlayState* play) { this->collider.elements[0].info.ocElemFlags = 1; } - Math_ApproachF(&this->actor.scale.x, !IS_DAY ? 0.02f : 0.0f, 0.2f, 0.01f); + Math_ApproachF(&this->actor.scale.x, !IS_DAY || CVarGetInteger("gNightGSAlwaysSpawn", 0) ? 0.02f : 0.0f, 0.2f, 0.01f); Actor_SetScale(&this->actor, this->actor.scale.x); } diff --git a/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c index abada7c12..d84e53756 100644 --- a/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c +++ b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c @@ -173,6 +173,15 @@ void EnWood02_Init(Actor* thisx, PlayState* play2) { f32 floorY; s16 extraRot; + // The tree in Kakariko's day scene does not have the same params to spawn the GS + // as the night scene, For the always spawn GS enhancement we apply the needed + // params to have the GS drop when bonking + if ((this->actor.params & 0xFF) == WOOD_TREE_CONICAL_MEDIUM && IS_DAY && + play->sceneNum == SCENE_SPOT01 && CVarGetInteger("gNightGSAlwaysSpawn", 0)) { + this->actor.params = 0x2001; + this->actor.home.rot.z = 0x71; + } + spawnType = WOOD_SPAWN_NORMAL; actorScale = 1.0f; this->unk_14C = (this->actor.params >> 8) & 0xFF;