From d18261e08c56ed165937c626f040b496c37fd5a2 Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Sun, 2 Mar 2025 21:11:13 +0100 Subject: [PATCH 1/2] Clean `mods.cpp` --- .../DifficultyOptions/BonkDamage.cpp | 62 +++ .../DifficultyOptions/DeleteFileOnDeath.cpp | 34 ++ .../DifficultyOptions/HurtContainerMode.cpp | 37 ++ .../DifficultyOptions/HyperBosses.cpp | 64 +++ .../DifficultyOptions/HyperEnemies.cpp | 34 ++ .../DifficultyOptions/PermanentHeartLoss.cpp | 55 +++ .../Enhancements/ExtraModes/MirrorMode.cpp | 68 +++ soh/soh/Enhancements/ExtraModes/RupeeDash.cpp | 43 ++ soh/soh/Enhancements/ExtraModes/ShadowTag.cpp | 56 +++ soh/soh/Enhancements/Fixes/DirtPathFix.cpp | 28 ++ .../Enhancements/TimeSavers/TimeSavers.cpp | 3 + soh/soh/Enhancements/TimeSavers/TimeSavers.h | 2 - soh/soh/Enhancements/boss-rush/BossRush.cpp | 2 + soh/soh/Enhancements/boss-rush/BossRush.h | 7 +- soh/soh/Enhancements/kaleido.cpp | 2 + soh/soh/Enhancements/kaleido.h | 1 - soh/soh/Enhancements/mods.cpp | 405 ++---------------- soh/soh/Enhancements/mods.h | 11 +- soh/soh/Enhancements/nametag.cpp | 2 + soh/soh/Enhancements/nametag.h | 3 - .../Enhancements/randomizer/hook_handlers.cpp | 2 + .../Enhancements/randomizer/hook_handlers.h | 6 - .../Enhancements/timesaver_hook_handlers.cpp | 2 + .../Enhancements/timesaver_hook_handlers.h | 6 - soh/soh/Enhancements/tts/tts.cpp | 2 + soh/soh/Enhancements/tts/tts.h | 6 - soh/soh/OTRGlobals.cpp | 1 - soh/soh/SohGui/SohMenuBar.cpp | 24 +- 28 files changed, 533 insertions(+), 435 deletions(-) create mode 100644 soh/soh/Enhancements/DifficultyOptions/BonkDamage.cpp create mode 100644 soh/soh/Enhancements/DifficultyOptions/DeleteFileOnDeath.cpp create mode 100644 soh/soh/Enhancements/DifficultyOptions/HurtContainerMode.cpp create mode 100644 soh/soh/Enhancements/DifficultyOptions/HyperBosses.cpp create mode 100644 soh/soh/Enhancements/DifficultyOptions/HyperEnemies.cpp create mode 100644 soh/soh/Enhancements/DifficultyOptions/PermanentHeartLoss.cpp create mode 100644 soh/soh/Enhancements/ExtraModes/MirrorMode.cpp create mode 100644 soh/soh/Enhancements/ExtraModes/RupeeDash.cpp create mode 100644 soh/soh/Enhancements/ExtraModes/ShadowTag.cpp create mode 100644 soh/soh/Enhancements/Fixes/DirtPathFix.cpp delete mode 100644 soh/soh/Enhancements/randomizer/hook_handlers.h delete mode 100644 soh/soh/Enhancements/timesaver_hook_handlers.h delete mode 100644 soh/soh/Enhancements/tts/tts.h diff --git a/soh/soh/Enhancements/DifficultyOptions/BonkDamage.cpp b/soh/soh/Enhancements/DifficultyOptions/BonkDamage.cpp new file mode 100644 index 00000000000..807f3cc1b20 --- /dev/null +++ b/soh/soh/Enhancements/DifficultyOptions/BonkDamage.cpp @@ -0,0 +1,62 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/enhancementTypes.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +#include "functions.h" +#include "macros.h" +} + +#define CVAR_BONK_DAMAGE_NAME CVAR_ENHANCEMENT("BonkDamageMult") +#define CVAR_BONK_DAMAGE_DEFAULT BONK_DAMAGE_NONE +#define CVAR_BONK_DAMAGE_VALUE CVarGetInteger(CVAR_BONK_DAMAGE_NAME, CVAR_BONK_DAMAGE_DEFAULT) + +void OnPlayerBonkDamage() { + uint8_t bonkOption = CVAR_BONK_DAMAGE_VALUE; + if (bonkOption == BONK_DAMAGE_NONE) { + return; + } + + if (bonkOption == BONK_DAMAGE_OHKO) { + gSaveContext.health = 0; + return; + } + + uint16_t bonkDamage = 0; + switch (bonkOption) { + case BONK_DAMAGE_QUARTER_HEART: + bonkDamage = 4; + break; + case BONK_DAMAGE_HALF_HEART: + bonkDamage = 8; + break; + case BONK_DAMAGE_1_HEART: + bonkDamage = 16; + break; + case BONK_DAMAGE_2_HEARTS: + bonkDamage = 32; + break; + case BONK_DAMAGE_4_HEARTS: + bonkDamage = 64; + break; + case BONK_DAMAGE_8_HEARTS: + bonkDamage = 128; + break; + default: + break; + } + + Health_ChangeBy(gPlayState, -bonkDamage); + // Set invincibility to make Link flash red as a visual damage indicator. + Player* player = GET_PLAYER(gPlayState); + player->invincibilityTimer = 28; +} + +void RegisterBonkDamage() { + COND_HOOK(OnPlayerBonk, CVAR_BONK_DAMAGE_VALUE, OnPlayerBonkDamage); +} + +static RegisterShipInitFunc initFunc(RegisterBonkDamage, { CVAR_BONK_DAMAGE_NAME }); diff --git a/soh/soh/Enhancements/DifficultyOptions/DeleteFileOnDeath.cpp b/soh/soh/Enhancements/DifficultyOptions/DeleteFileOnDeath.cpp new file mode 100644 index 00000000000..9236124c5ed --- /dev/null +++ b/soh/soh/Enhancements/DifficultyOptions/DeleteFileOnDeath.cpp @@ -0,0 +1,34 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/SaveManager.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +#include "macros.h" +} + +#define CVAR_DELETE_FILE_ON_DEATH_NAME CVAR_ENHANCEMENT("DeleteFileOnDeath") +#define CVAR_DELETE_FILE_ON_DEATH_DEFAULT 0 +#define CVAR_DELETE_FILE_ON_DEATH_VALUE CVarGetInteger(CVAR_DELETE_FILE_ON_DEATH_NAME, CVAR_DELETE_FILE_ON_DEATH_DEFAULT) + +void OnGameFrameUpdateDeleteFileOnDeath() { + if ( + !GameInteractor::IsSaveLoaded() || + gPlayState == NULL || + gPlayState->gameOverCtx.state != GAMEOVER_DEATH_MENU || + gPlayState->pauseCtx.state != 9 + ) { + return; + } + + SaveManager::Instance->DeleteZeldaFile(gSaveContext.fileNum); + std::reinterpret_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset"); +} + +void RegisterDeleteFileOnDeath() { + COND_HOOK(OnGameFrameUpdate, CVAR_DELETE_FILE_ON_DEATH_VALUE, OnGameFrameUpdateDeleteFileOnDeath); +} + +static RegisterShipInitFunc initFunc(RegisterDeleteFileOnDeath, { CVAR_DELETE_FILE_ON_DEATH_NAME }); diff --git a/soh/soh/Enhancements/DifficultyOptions/HurtContainerMode.cpp b/soh/soh/Enhancements/DifficultyOptions/HurtContainerMode.cpp new file mode 100644 index 00000000000..fbacea4c6e3 --- /dev/null +++ b/soh/soh/Enhancements/DifficultyOptions/HurtContainerMode.cpp @@ -0,0 +1,37 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +#include "macros.h" +} + +#define CVAR_BONK_DAMAGE_NAME CVAR_ENHANCEMENT("HurtContainer") +#define CVAR_BONK_DAMAGE_DEFAULT 0 +#define CVAR_BONK_DAMAGE_VALUE CVarGetInteger(CVAR_BONK_DAMAGE_NAME, CVAR_BONK_DAMAGE_DEFAULT) + +void OnLoadGameHurtContainerMode(int32_t _fileNum) { + static bool hurtEnabled = false; + if (hurtEnabled == CVAR_BONK_DAMAGE_VALUE) { + return; + } + + hurtEnabled = CVAR_BONK_DAMAGE_VALUE; + uint16_t getHeartPieces = gSaveContext.ship.stats.heartPieces / 4; + uint16_t getHeartContainers = gSaveContext.ship.stats.heartContainers; + + if (hurtEnabled) { + gSaveContext.healthCapacity = 320 - ((getHeartPieces + getHeartContainers) * 16); + } else { + gSaveContext.healthCapacity = 48 + ((getHeartPieces + getHeartContainers) * 16); + } +} + +void RegisterHurtContainerMode() { + COND_HOOK(OnLoadGame, true, OnLoadGameHurtContainerMode); + OnLoadGameHurtContainerMode(0); +} + +static RegisterShipInitFunc initFunc(RegisterHurtContainerMode, { CVAR_BONK_DAMAGE_NAME }); diff --git a/soh/soh/Enhancements/DifficultyOptions/HyperBosses.cpp b/soh/soh/Enhancements/DifficultyOptions/HyperBosses.cpp new file mode 100644 index 00000000000..40a3e2eae5e --- /dev/null +++ b/soh/soh/Enhancements/DifficultyOptions/HyperBosses.cpp @@ -0,0 +1,64 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +#include "functions.h" +#include "macros.h" +} + +#define CVAR_HYPER_BOSSES_NAME CVAR_ENHANCEMENT("HyperBosses") +#define CVAR_HYPER_BOSSES_DEFAULT 0 +#define CVAR_HYPER_BOSSES_VALUE CVarGetInteger(CVAR_HYPER_BOSSES_NAME, CVAR_HYPER_BOSSES_DEFAULT) + +bool IsHyperBossesActive() { + return CVAR_HYPER_BOSSES_VALUE || + (IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_HYPERBOSSES] == BR_CHOICE_HYPERBOSSES_YES); +} + +void OnActorUpdateHyperBosses(void* refActor) { + // Run the update function a second time to make bosses move and act twice as fast. + Player* player = GET_PLAYER(gPlayState); + Actor* actor = static_cast(refActor); + + uint8_t isBossActor = + actor->id == ACTOR_BOSS_GOMA || // Gohma + actor->id == ACTOR_BOSS_DODONGO || // King Dodongo + actor->id == ACTOR_EN_BDFIRE || // King Dodongo Fire Breath + actor->id == ACTOR_BOSS_VA || // Barinade + actor->id == ACTOR_BOSS_GANONDROF || // Phantom Ganon + actor->id == ACTOR_EN_FHG_FIRE || // Phantom Ganon/Ganondorf Energy Ball/Thunder + actor->id == ACTOR_EN_FHG || // Phantom Ganon's Horse + actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || // Volvagia (grounded/flying) + actor->id == ACTOR_EN_VB_BALL || // Volvagia Rocks + actor->id == ACTOR_BOSS_MO || // Morpha + actor->id == ACTOR_BOSS_SST || // Bongo Bongo + actor->id == ACTOR_BOSS_TW || // Twinrova + actor->id == ACTOR_BOSS_GANON || // Ganondorf + actor->id == ACTOR_BOSS_GANON2; // Ganon + + // Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses. + if (IsHyperBossesActive() && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) { + // Barinade needs to be updated in sequence to avoid unintended behaviour. + if (actor->id == ACTOR_BOSS_VA) { + // params -1 is BOSSVA_BODY + if (actor->params == -1) { + Actor* actorList = gPlayState->actorCtx.actorLists[ACTORCAT_BOSS].head; + while (actorList != NULL) { + GameInteractor::RawAction::UpdateActor(actorList); + actorList = actorList->next; + } + } + } else { + GameInteractor::RawAction::UpdateActor(actor); + } + } +} + +void RegisterHyperBosses() { + COND_HOOK(OnActorUpdate, IsHyperBossesActive(), OnActorUpdateHyperBosses); +} + +static RegisterShipInitFunc initFunc(RegisterHyperBosses, { CVAR_HYPER_BOSSES_NAME }); diff --git a/soh/soh/Enhancements/DifficultyOptions/HyperEnemies.cpp b/soh/soh/Enhancements/DifficultyOptions/HyperEnemies.cpp new file mode 100644 index 00000000000..44992976620 --- /dev/null +++ b/soh/soh/Enhancements/DifficultyOptions/HyperEnemies.cpp @@ -0,0 +1,34 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern PlayState* gPlayState; +#include "functions.h" +#include "macros.h" +} + +#define CVAR_HYPER_ENEMIES_NAME CVAR_ENHANCEMENT("HyperEnemies") +#define CVAR_HYPER_ENEMIES_DEFAULT 0 +#define CVAR_HYPER_ENEMIES_VALUE CVarGetInteger(CVAR_HYPER_ENEMIES_NAME, CVAR_HYPER_ENEMIES_DEFAULT) + +void OnActorUpdateHyperEnemies(void* refActor) { + // Run the update function a second time to make enemies and minibosses move and act twice as fast. + Player* player = GET_PLAYER(gPlayState); + Actor* actor = static_cast(refActor); + + // Some enemies are not in the ACTORCAT_ENEMY category, and some are that aren't really enemies. + bool isEnemy = actor->category == ACTORCAT_ENEMY || actor->id == ACTOR_EN_TORCH2; + bool isExcludedEnemy = actor->id == ACTOR_EN_FIRE_ROCK || actor->id == ACTOR_EN_ENCOUNT2; + + // Don't apply during cutscenes because it causes weird behaviour and/or crashes on some cutscenes. + if (isEnemy && !isExcludedEnemy && !Player_InBlockingCsMode(gPlayState, player)) { + GameInteractor::RawAction::UpdateActor(actor); + } +} + +void RegisterHyperEnemies() { + COND_HOOK(OnActorUpdate, CVAR_HYPER_ENEMIES_VALUE, OnActorUpdateHyperEnemies); +} + +static RegisterShipInitFunc initFunc(RegisterHyperEnemies, { CVAR_HYPER_ENEMIES_NAME }); diff --git a/soh/soh/Enhancements/DifficultyOptions/PermanentHeartLoss.cpp b/soh/soh/Enhancements/DifficultyOptions/PermanentHeartLoss.cpp new file mode 100644 index 00000000000..34a3894e3bb --- /dev/null +++ b/soh/soh/Enhancements/DifficultyOptions/PermanentHeartLoss.cpp @@ -0,0 +1,55 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "soh/OTRGlobals.h" + +extern "C" { +extern SaveContext gSaveContext; +#include "macros.h" +} + +#define CVAR_PERMANENT_HEART_LOSS_NAME CVAR_ENHANCEMENT("PermanentHeartLoss") +#define CVAR_PERMANENT_HEART_LOSS_DEFAULT 0 +#define CVAR_PERMANENT_HEART_LOSS_VALUE CVarGetInteger(CVAR_PERMANENT_HEART_LOSS_NAME, CVAR_PERMANENT_HEART_LOSS_DEFAULT) + +static bool hasAffectedHealth = false; + +void UpdatePermanentHeartLossState() { + if (!GameInteractor::IsSaveLoaded()) return; + + if (!CVAR_PERMANENT_HEART_LOSS_VALUE && hasAffectedHealth) { + uint8_t heartContainers = gSaveContext.ship.stats.heartContainers; // each worth 16 health + uint8_t heartPieces = gSaveContext.ship.stats.heartPieces; // each worth 4 health, but only in groups of 4 + uint8_t startingHealth = 16 * (IS_RANDO ? (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_HEARTS) + 1) : 3); + + uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); + gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + hasAffectedHealth = false; + } +} + +void OnPlayerUpdatePermanentHeartLoss() { + if (!GameInteractor::IsSaveLoaded()) { + return; + } + + if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { + gSaveContext.healthCapacity -= 16; + gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); + hasAffectedHealth = true; + } +} + +void OnLoadGamePermanentHeartLoss(int16_t fileNum) { + hasAffectedHealth = false; + UpdatePermanentHeartLossState(); +} + +void RegisterPermanentHeartLoss() { + COND_HOOK(OnLoadGame, CVAR_PERMANENT_HEART_LOSS_VALUE, OnLoadGamePermanentHeartLoss); + COND_HOOK(OnPlayerUpdate, CVAR_PERMANENT_HEART_LOSS_VALUE, OnPlayerUpdatePermanentHeartLoss); + UpdatePermanentHeartLossState(); +} + +static RegisterShipInitFunc initFunc(RegisterPermanentHeartLoss, { CVAR_PERMANENT_HEART_LOSS_NAME }); diff --git a/soh/soh/Enhancements/ExtraModes/MirrorMode.cpp b/soh/soh/Enhancements/ExtraModes/MirrorMode.cpp new file mode 100644 index 00000000000..227ab8999ec --- /dev/null +++ b/soh/soh/Enhancements/ExtraModes/MirrorMode.cpp @@ -0,0 +1,68 @@ +#include +#include "soh/Enhancements/cosmetics/authenticGfxPatches.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/randomizer/context.h" +#include "soh/Enhancements/enhancementTypes.h" +#include "soh/ResourceManagerHelpers.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +#include "macros.h" +} + +#define CVAR_MIRRORED_WORLD_MODE_NAME CVAR_ENHANCEMENT("MirroredWorldMode") +#define CVAR_MIRRORED_WORLD_MODE_DEFAULT MIRRORED_WORLD_OFF +#define CVAR_MIRRORED_WORLD_MODE_VALUE CVarGetInteger(CVAR_MIRRORED_WORLD_MODE_NAME, CVAR_MIRRORED_WORLD_MODE_DEFAULT) + +#define CVAR_MIRRORED_WORLD_NAME CVAR_ENHANCEMENT("MirroredWorld") +#define CVAR_MIRRORED_WORLD_DEFAULT false +#define CVAR_MIRRORED_WORLD_VALUE CVarGetInteger(CVAR_MIRRORED_WORLD_NAME, CVAR_MIRRORED_WORLD_DEFAULT) + +void OnSceneInitMirrorMode(s32 sceneNum) { + static bool prevMirroredWorld = false; + bool nextMirroredWorld = false; + + s16 mirroredMode = CVAR_MIRRORED_WORLD_MODE_VALUE; + s16 inDungeon = (sceneNum >= SCENE_DEKU_TREE && sceneNum <= SCENE_INSIDE_GANONS_CASTLE_COLLAPSE && sceneNum != SCENE_THIEVES_HIDEOUT) || + (sceneNum >= SCENE_DEKU_TREE_BOSS && sceneNum <= SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) || + (sceneNum == SCENE_GANON_BOSS); + + if (mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED || mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED) { + u32 seed = sceneNum + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() : gSaveContext.ship.stats.fileCreatedAt); + Random_Init(seed); + } + + bool randomMirror = Random(0, 2) == 1; + + if ( + mirroredMode == MIRRORED_WORLD_ALWAYS || + ((mirroredMode == MIRRORED_WORLD_RANDOM || mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED) && randomMirror) || + // Dungeon modes + (inDungeon && (mirroredMode == MIRRORED_WORLD_DUNGEONS_All || + (mirroredMode == MIRRORED_WORLD_DUNGEONS_VANILLA && !ResourceMgr_IsSceneMasterQuest(sceneNum)) || + (mirroredMode == MIRRORED_WORLD_DUNGEONS_MQ && ResourceMgr_IsSceneMasterQuest(sceneNum)) || + ((mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM || mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED) && randomMirror))) + ) { + nextMirroredWorld = true; + CVarSetInteger(CVAR_MIRRORED_WORLD_NAME, true); + } else { + nextMirroredWorld = false; + CVarClear(CVAR_MIRRORED_WORLD_NAME); + } + + if (prevMirroredWorld != nextMirroredWorld) { + prevMirroredWorld = nextMirroredWorld; + ApplyMirrorWorldGfxPatches(); + } +} + +void RegisterMirrorMode() { + COND_HOOK(OnSceneInit, CVAR_MIRRORED_WORLD_MODE_VALUE, OnSceneInitMirrorMode); + if (gPlayState != NULL) { + OnSceneInitMirrorMode(gPlayState->sceneNum); + } +} + +static RegisterShipInitFunc initFunc(RegisterMirrorMode, { CVAR_MIRRORED_WORLD_MODE_NAME }); diff --git a/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp b/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp new file mode 100644 index 00000000000..2daeead6983 --- /dev/null +++ b/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp @@ -0,0 +1,43 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +extern SaveContext gSaveContext; +extern PlayState* gPlayState; +#include "functions.h" +#include "macros.h" +#include "variables.h" +} + +#define CVAR_RUPEE_DASH_NAME CVAR_ENHANCEMENT("RupeeDash") +#define CVAR_RUPEE_DASH_DEFAULT 0 +#define CVAR_RUPEE_DASH_VALUE CVarGetInteger(CVAR_RUPEE_DASH_NAME, CVAR_RUPEE_DASH_DEFAULT) + +#define CVAR_RUPEE_DASH_INTERVAL_NAME CVAR_ENHANCEMENT("RupeeDashInterval") +#define CVAR_RUPEE_DASH_INTERVAL_DEFAULT 5 +#define CVAR_RUPEE_DASH_INTERVAL_VALUE CVarGetInteger(CVAR_RUPEE_DASH_INTERVAL_NAME, CVAR_RUPEE_DASH_INTERVAL_DEFAULT) + +void OnPlayerUpdateRupeeDash() { + // Initialize Timer + static u16 rupeeDashTimer = 0; + + // Did time change by DashInterval? + if (rupeeDashTimer >= CVAR_RUPEE_DASH_INTERVAL_VALUE * 20) { + rupeeDashTimer = 0; + if (gSaveContext.rupees > 0) { + u16 walletSize = (CUR_UPG_VALUE(UPG_WALLET) + 1) * -1; + Rupees_ChangeBy(walletSize); + } else { + Health_ChangeBy(gPlayState, -16); + } + } else { + rupeeDashTimer++; + } +} + +void RegisterRupeeDash() { + COND_HOOK(OnPlayerUpdate, CVAR_RUPEE_DASH_VALUE, OnPlayerUpdateRupeeDash); +} + +static RegisterShipInitFunc initFunc(RegisterRupeeDash, { CVAR_RUPEE_DASH_NAME }); diff --git a/soh/soh/Enhancements/ExtraModes/ShadowTag.cpp b/soh/soh/Enhancements/ExtraModes/ShadowTag.cpp new file mode 100644 index 00000000000..66b23123762 --- /dev/null +++ b/soh/soh/Enhancements/ExtraModes/ShadowTag.cpp @@ -0,0 +1,56 @@ +#include +#include "soh/Enhancements/cosmetics/authenticGfxPatches.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "soh/ResourceManagerHelpers.h" + +extern "C" { +extern PlayState* gPlayState; +#include "functions.h" +#include "macros.h" +} + +#define CVAR_SHADOW_TAG_NAME CVAR_ENHANCEMENT("ShadowTag") +#define CVAR_SHADOW_TAG_DEFAULT 0 +#define CVAR_SHADOW_TAG_VALUE CVarGetInteger(CVAR_SHADOW_TAG_NAME, CVAR_SHADOW_TAG_DEFAULT) + +static bool shouldSpawn = false; +static u16 delayTimer = 60; + +void OnPlayerUpdateShadowTag() { + if ( + gPlayState->sceneNum == SCENE_FOREST_TEMPLE && + ( + gPlayState->roomCtx.curRoom.num == 16 || // Green Poe Room + gPlayState->roomCtx.curRoom.num == 13 || // Blue Poe Room + gPlayState->roomCtx.curRoom.num == 12 // Red Poe Room + ) + ) { + return; + } + + if (shouldSpawn && delayTimer <= 0) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_WALLMAS, 0, 0, 0, 0, 0, 0, 3, false); + shouldSpawn = false; + } else { + delayTimer--; + } +} + +void OnSceneSpawnActorsShadowTag() { + shouldSpawn = true; + delayTimer = 60; +} + +void OnSceneInitShadowTag(s16 sceneNum) { + shouldSpawn = true; + delayTimer = 60; +} + +void RegisterShadowTag() { + COND_HOOK(OnPlayerUpdate, CVAR_SHADOW_TAG_VALUE, OnPlayerUpdateShadowTag); + COND_HOOK(OnSceneSpawnActors, CVAR_SHADOW_TAG_VALUE, OnSceneSpawnActorsShadowTag); + COND_HOOK(OnSceneInit, CVAR_SHADOW_TAG_VALUE, OnSceneInitShadowTag); +} + +static RegisterShipInitFunc initFunc(RegisterShadowTag, { CVAR_SHADOW_TAG_NAME }); diff --git a/soh/soh/Enhancements/Fixes/DirtPathFix.cpp b/soh/soh/Enhancements/Fixes/DirtPathFix.cpp new file mode 100644 index 00000000000..2468955fc05 --- /dev/null +++ b/soh/soh/Enhancements/Fixes/DirtPathFix.cpp @@ -0,0 +1,28 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +#define CVAR_DIRT_PATH_FIX_NAME CVAR_ENHANCEMENT("SceneSpecificDirtPathFix") +#define CVAR_DIRT_PATH_FIX_DEFAULT ZFIGHT_FIX_DISABLED +#define CVAR_DIRT_PATH_FIX_VALUE CVarGetInteger(CVAR_DIRT_PATH_FIX_NAME, CVAR_DIRT_PATH_FIX_DEFAULT) + +void OnTransitionEndDirtPathFix(s32 sceneNum) { + switch (sceneNum) { + case SCENE_HYRULE_FIELD: + case SCENE_KOKIRI_FOREST: + case SCENE_HYRULE_CASTLE: + CVarSetInteger(CVAR_Z_FIGHTING_MODE, CVAR_DIRT_PATH_FIX_VALUE); + return; + default: + CVarClear(CVAR_Z_FIGHTING_MODE); + } +} + +void RegisterDirtPathFix() { + COND_HOOK(OnTransitionEnd, CVAR_DIRT_PATH_FIX_VALUE, OnTransitionEndDirtPathFix); + if (gPlayState != NULL) { + OnTransitionEndDirtPathFix(gPlayState->sceneNum); + } +} + +static RegisterShipInitFunc initFunc(RegisterDirtPathFix, { CVAR_DIRT_PATH_FIX_NAME }); diff --git a/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp b/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp index a198f828904..e1e15151202 100644 --- a/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp +++ b/soh/soh/Enhancements/TimeSavers/TimeSavers.cpp @@ -1,4 +1,5 @@ #include "TimeSavers.h" +#include "soh/ShipInit.hpp" void TimeSavers_Register() { // SkipCutscene @@ -16,3 +17,5 @@ void TimeSavers_Register() { FasterHeavyBlockLift_Register(); FasterRupeeAccumulator_Register(); } + +static RegisterShipInitFunc initFunc(TimeSavers_Register); diff --git a/soh/soh/Enhancements/TimeSavers/TimeSavers.h b/soh/soh/Enhancements/TimeSavers/TimeSavers.h index ad521c6c2ba..7ce45a28b9f 100644 --- a/soh/soh/Enhancements/TimeSavers/TimeSavers.h +++ b/soh/soh/Enhancements/TimeSavers/TimeSavers.h @@ -1,8 +1,6 @@ #ifndef TIME_SAVERS_H #define TIME_SAVERS_H -void TimeSavers_Register(); - // SkipCutscene // Story void SkipBlueWarp_Register(); diff --git a/soh/soh/Enhancements/boss-rush/BossRush.cpp b/soh/soh/Enhancements/boss-rush/BossRush.cpp index aabe5d67ab2..e11906719b2 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.cpp +++ b/soh/soh/Enhancements/boss-rush/BossRush.cpp @@ -708,3 +708,5 @@ void BossRush_RegisterHooks() { onActorUpdate = GameInteractor::Instance->RegisterGameHookForID(ACTOR_DOOR_WARP1, BossRush_OnBlueWarpUpdate); }); } + +static RegisterShipInitFunc initFunc(BossRush_RegisterHooks); diff --git a/soh/soh/Enhancements/boss-rush/BossRush.h b/soh/soh/Enhancements/boss-rush/BossRush.h index 292cbaf26ad..7ad889fed2b 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.h +++ b/soh/soh/Enhancements/boss-rush/BossRush.h @@ -1,16 +1,17 @@ #pragma once -#include "z64.h" +#include + +struct PlayState; #ifdef __cplusplus extern "C" { #endif - void BossRush_HandleBlueWarpHeal(PlayState* play); + void BossRush_HandleBlueWarpHeal(struct PlayState* play); void BossRush_InitSave(); const char* BossRush_GetSettingName(u8 optionIndex, u8 language); const char* BossRush_GetSettingChoiceName(u8 optionIndex, u8 choiceIndex, u8 language); u8 BossRush_GetSettingOptionsAmount(u8 optionIndex); - void BossRush_RegisterHooks(); #ifdef __cplusplus }; #endif diff --git a/soh/soh/Enhancements/kaleido.cpp b/soh/soh/Enhancements/kaleido.cpp index 4cddeef43ed..88ba841e95c 100644 --- a/soh/soh/Enhancements/kaleido.cpp +++ b/soh/soh/Enhancements/kaleido.cpp @@ -467,3 +467,5 @@ namespace Rando { void RandoKaleido_RegisterHooks() { GameInteractor::Instance->RegisterGameHook(RandoKaleido_UpdateMiscCollectibles); } + +static RegisterShipInitFunc initFunc(RandoKaleido_RegisterHooks); diff --git a/soh/soh/Enhancements/kaleido.h b/soh/soh/Enhancements/kaleido.h index db86a817e90..3e0f0ec762a 100644 --- a/soh/soh/Enhancements/kaleido.h +++ b/soh/soh/Enhancements/kaleido.h @@ -170,7 +170,6 @@ void RandoKaleido_UpdateMiscCollectibles(int16_t inDungeonScene); #ifdef __cplusplus } #endif -void RandoKaleido_RegisterHooks(); #endif //KALEIDO_H diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index cd60f7d8c90..50ad5dd13d6 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -1,58 +1,43 @@ #include "mods.h" + #include -#include "game-interactor/GameInteractor.h" -#include "tts/tts.h" -#include "soh/OTRGlobals.h" -#include "soh/SaveManager.h" +#include "soh/Enhancements/enhancementTypes.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/ResourceManagerHelpers.h" #include "soh/resource/type/Skeleton.h" -#include "soh/Enhancements/boss-rush/BossRushTypes.h" -#include "soh/Enhancements/boss-rush/BossRush.h" -#include "soh/Enhancements/enhancementTypes.h" -#include "soh/Enhancements/randomizer/3drando/random.hpp" -#include "soh/Enhancements/cosmetics/authenticGfxPatches.h" -#include -#include "soh/Enhancements/nametag.h" -#include "soh/Enhancements/timesaver_hook_handlers.h" -#include "soh/Enhancements/TimeSavers/TimeSavers.h" -#include "soh/Enhancements/randomizer/hook_handlers.h" -#include "objects/object_gi_compass/object_gi_compass.h" +#include "soh/SaveManager.h" +#include "soh/OTRGlobals.h" +#include "soh_assets.h" + +extern "C" { +#include "z64.h" +#include "align_asset_macro.h" +#include "macros.h" +#include "soh/cvar_prefixes.h" +#include "variables.h" +#include "functions.h" +#include "src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h" +#include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h" #include "src/overlays/actors/ovl_En_Bb/z_en_bb.h" #include "src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h" +#include "src/overlays/actors/ovl_En_Door/z_en_door.h" +#include "src/overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "src/overlays/actors/ovl_En_Firefly/z_en_firefly.h" #include "src/overlays/actors/ovl_En_Mb/z_en_mb.h" -#include "src/overlays/actors/ovl_En_Tite/z_en_tite.h" -#include "src/overlays/actors/ovl_En_Zf/z_en_zf.h" -#include "src/overlays/actors/ovl_En_Wf/z_en_wf.h" -#include "src/overlays/actors/ovl_En_Reeba/z_en_reeba.h" #include "src/overlays/actors/ovl_En_Peehat/z_en_peehat.h" #include "src/overlays/actors/ovl_En_Po_Field/z_en_po_field.h" #include "src/overlays/actors/ovl_En_Poh/z_en_poh.h" +#include "src/overlays/actors/ovl_En_Reeba/z_en_reeba.h" +#include "src/overlays/actors/ovl_En_Tite/z_en_tite.h" #include "src/overlays/actors/ovl_En_Tp/z_en_tp.h" -#include "src/overlays/actors/ovl_En_Firefly/z_en_firefly.h" -#include "src/overlays/actors/ovl_En_Xc/z_en_xc.h" -#include "src/overlays/actors/ovl_Fishing/z_fishing.h" +#include "src/overlays/actors/ovl_En_Wf/z_en_wf.h" +#include "src/overlays/actors/ovl_En_Zf/z_en_zf.h" #include "src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h" -#include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h" -#include "src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h" -#include "src/overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "objects/object_gi_compass/object_gi_compass.h" #include "objects/object_link_boy/object_link_boy.h" #include "objects/object_link_child/object_link_child.h" -#include "soh_assets.h" -#include "kaleido.h" - -extern "C" { -#include -#include "align_asset_macro.h" -#include "macros.h" -#include "soh/cvar_prefixes.h" -#include "variables.h" -#include "functions.h" -#include "src/overlays/actors/ovl_En_Door/z_en_door.h" - -extern SaveContext gSaveContext; extern PlayState* gPlayState; -extern void Overlay_DisplayText(float duration, const char* text); } // GreyScaleEndDlist @@ -108,7 +93,6 @@ void SwitchAge() { /// Switches Link's age and respawns him at the last entrance he entered. void RegisterOcarinaTimeTravel() { - GameInteractor::Instance->RegisterGameHook([]() { if (!GameInteractor::IsSaveLoaded(true) || !CVarGetInteger(CVAR_ENHANCEMENT("TimeTravel"), 0)) { return; @@ -133,109 +117,6 @@ void RegisterOcarinaTimeTravel() { }); } -void RegisterRupeeDash() { - GameInteractor::Instance->RegisterGameHook([]() { - if (!CVarGetInteger(CVAR_ENHANCEMENT("RupeeDash"), 0)) { - return; - } - - // Initialize Timer - static uint16_t rupeeDashTimer = 0; - uint16_t rdmTime = CVarGetInteger(CVAR_ENHANCEMENT("RupeeDashInterval"), 5) * 20; - - // Did time change by DashInterval? - if (rupeeDashTimer >= rdmTime) { - rupeeDashTimer = 0; - if (gSaveContext.rupees > 0) { - uint16_t walletSize = (CUR_UPG_VALUE(UPG_WALLET) + 1) * -1; - Rupees_ChangeBy(walletSize); - } else { - Health_ChangeBy(gPlayState, -16); - } - } else { - rupeeDashTimer++; - } - }); -} - -void RegisterShadowTag() { - static bool shouldSpawn = false; - static uint16_t delayTimer = 60; - - GameInteractor::Instance->RegisterGameHook([]() { - if (!CVarGetInteger(CVAR_ENHANCEMENT("ShadowTag"), 0)) { - return; - } - if (gPlayState->sceneNum == SCENE_FOREST_TEMPLE && // Forest Temple Scene - gPlayState->roomCtx.curRoom.num == 16 || // Green Poe Room - gPlayState->roomCtx.curRoom.num == 13 || // Blue Poe Room - gPlayState->roomCtx.curRoom.num == 12) { // Red Poe Room - return; - } else { - if (shouldSpawn && (delayTimer <= 0)) { - Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_WALLMAS, 0, 0, 0, 0, 0, 0, 3, false); - shouldSpawn = false; - } else { - delayTimer--; - } - } - }); - GameInteractor::Instance->RegisterGameHook([]() { - shouldSpawn = true; - delayTimer = 60; - }); - GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { - shouldSpawn = true; - delayTimer = 60; - }); -} - -static bool hasAffectedHealth = false; -void UpdatePermanentHeartLossState() { - if (!GameInteractor::IsSaveLoaded()) return; - - if (!CVarGetInteger(CVAR_ENHANCEMENT("PermanentHeartLoss"), 0) && hasAffectedHealth) { - uint8_t heartContainers = gSaveContext.ship.stats.heartContainers; // each worth 16 health - uint8_t heartPieces = gSaveContext.ship.stats.heartPieces; // each worth 4 health, but only in groups of 4 - uint8_t startingHealth = 16 * (IS_RANDO ? (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_HEARTS) + 1) : 3); - - - uint8_t newCapacity = startingHealth + (heartContainers * 16) + ((heartPieces - (heartPieces % 4)) * 4); - gSaveContext.healthCapacity = MAX(newCapacity, gSaveContext.healthCapacity); - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); - hasAffectedHealth = false; - } -} - -void RegisterPermanentHeartLoss() { - GameInteractor::Instance->RegisterGameHook([](int16_t fileNum) { - hasAffectedHealth = false; - UpdatePermanentHeartLossState(); - }); - - GameInteractor::Instance->RegisterGameHook([]() { - if (!CVarGetInteger(CVAR_ENHANCEMENT("PermanentHeartLoss"), 0) || !GameInteractor::IsSaveLoaded()) return; - - if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { - gSaveContext.healthCapacity -= 16; - gSaveContext.health = MIN(gSaveContext.health, gSaveContext.healthCapacity); - hasAffectedHealth = true; - } - }); -}; - -void RegisterDeleteFileOnDeath() { - GameInteractor::Instance->RegisterGameHook([]() { - if (!CVarGetInteger(CVAR_ENHANCEMENT("DeleteFileOnDeath"), 0) || !GameInteractor::IsSaveLoaded() || gPlayState == NULL) return; - - if (gPlayState->gameOverCtx.state == GAMEOVER_DEATH_MENU && gPlayState->pauseCtx.state == 9) { - SaveManager::Instance->DeleteZeldaFile(gSaveContext.fileNum); - hasAffectedHealth = false; - std::reinterpret_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch("reset"); - } - }); -} - struct DayTimeGoldSkulltulas { uint16_t scene; uint16_t room; @@ -292,201 +173,6 @@ void RegisterDaytimeGoldSkultullas() { }); } -bool IsHyperBossesActive() { - return CVarGetInteger(CVAR_ENHANCEMENT("HyperBosses"), 0) || - (IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_HYPERBOSSES] == BR_CHOICE_HYPERBOSSES_YES); -} - -void UpdateHyperBossesState() { - static uint32_t actorUpdateHookId = 0; - if (actorUpdateHookId != 0) { - GameInteractor::Instance->UnregisterGameHook(actorUpdateHookId); - actorUpdateHookId = 0; - } - - if (IsHyperBossesActive()) { - actorUpdateHookId = GameInteractor::Instance->RegisterGameHook([](void* refActor) { - // Run the update function a second time to make bosses move and act twice as fast. - - Player* player = GET_PLAYER(gPlayState); - Actor* actor = static_cast(refActor); - - uint8_t isBossActor = - actor->id == ACTOR_BOSS_GOMA || // Gohma - actor->id == ACTOR_BOSS_DODONGO || // King Dodongo - actor->id == ACTOR_EN_BDFIRE || // King Dodongo Fire Breath - actor->id == ACTOR_BOSS_VA || // Barinade - actor->id == ACTOR_BOSS_GANONDROF || // Phantom Ganon - actor->id == ACTOR_EN_FHG_FIRE || // Phantom Ganon/Ganondorf Energy Ball/Thunder - actor->id == ACTOR_EN_FHG || // Phantom Ganon's Horse - actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || // Volvagia (grounded/flying) - actor->id == ACTOR_EN_VB_BALL || // Volvagia Rocks - actor->id == ACTOR_BOSS_MO || // Morpha - actor->id == ACTOR_BOSS_SST || // Bongo Bongo - actor->id == ACTOR_BOSS_TW || // Twinrova - actor->id == ACTOR_BOSS_GANON || // Ganondorf - actor->id == ACTOR_BOSS_GANON2; // Ganon - - // Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses. - if (IsHyperBossesActive() && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) { - // Barinade needs to be updated in sequence to avoid unintended behaviour. - if (actor->id == ACTOR_BOSS_VA) { - // params -1 is BOSSVA_BODY - if (actor->params == -1) { - Actor* actorList = gPlayState->actorCtx.actorLists[ACTORCAT_BOSS].head; - while (actorList != NULL) { - GameInteractor::RawAction::UpdateActor(actorList); - actorList = actorList->next; - } - } - } else { - GameInteractor::RawAction::UpdateActor(actor); - } - } - }); - } -} - -void RegisterHyperBosses() { - UpdateHyperBossesState(); - GameInteractor::Instance->RegisterGameHook([](int16_t fileNum) { - UpdateHyperBossesState(); - }); -} - -void UpdateHyperEnemiesState() { - static uint32_t actorUpdateHookId = 0; - if (actorUpdateHookId != 0) { - GameInteractor::Instance->UnregisterGameHook(actorUpdateHookId); - actorUpdateHookId = 0; - } - - if (CVarGetInteger(CVAR_ENHANCEMENT("HyperEnemies"), 0)) { - actorUpdateHookId = GameInteractor::Instance->RegisterGameHook([](void* refActor) { - // Run the update function a second time to make enemies and minibosses move and act twice as fast. - - Player* player = GET_PLAYER(gPlayState); - Actor* actor = static_cast(refActor); - - // Some enemies are not in the ACTORCAT_ENEMY category, and some are that aren't really enemies. - bool isEnemy = actor->category == ACTORCAT_ENEMY || actor->id == ACTOR_EN_TORCH2; - bool isExcludedEnemy = actor->id == ACTOR_EN_FIRE_ROCK || actor->id == ACTOR_EN_ENCOUNT2; - - // Don't apply during cutscenes because it causes weird behaviour and/or crashes on some cutscenes. - if (CVarGetInteger(CVAR_ENHANCEMENT("HyperEnemies"), 0) && isEnemy && !isExcludedEnemy && - !Player_InBlockingCsMode(gPlayState, player)) { - GameInteractor::RawAction::UpdateActor(actor); - } - }); - } -} - -void RegisterBonkDamage() { - GameInteractor::Instance->RegisterGameHook([]() { - uint8_t bonkOption = CVarGetInteger(CVAR_ENHANCEMENT("BonkDamageMult"), BONK_DAMAGE_NONE); - if (bonkOption == BONK_DAMAGE_NONE) { - return; - } - - if (bonkOption == BONK_DAMAGE_OHKO) { - gSaveContext.health = 0; - return; - } - - uint16_t bonkDamage = 0; - switch (bonkOption) { - case BONK_DAMAGE_QUARTER_HEART: - bonkDamage = 4; - break; - case BONK_DAMAGE_HALF_HEART: - bonkDamage = 8; - break; - case BONK_DAMAGE_1_HEART: - bonkDamage = 16; - break; - case BONK_DAMAGE_2_HEARTS: - bonkDamage = 32; - break; - case BONK_DAMAGE_4_HEARTS: - bonkDamage = 64; - break; - case BONK_DAMAGE_8_HEARTS: - bonkDamage = 128; - break; - default: - break; - } - - Health_ChangeBy(gPlayState, -bonkDamage); - // Set invincibility to make Link flash red as a visual damage indicator. - Player* player = GET_PLAYER(gPlayState); - player->invincibilityTimer = 28; - }); -} - -void UpdateDirtPathFixState(int32_t sceneNum) { - switch (sceneNum) { - case SCENE_HYRULE_FIELD: - case SCENE_KOKIRI_FOREST: - case SCENE_HYRULE_CASTLE: - CVarSetInteger(CVAR_Z_FIGHTING_MODE, CVarGetInteger(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix"), ZFIGHT_FIX_DISABLED)); - return; - default: - CVarClear(CVAR_Z_FIGHTING_MODE); - } -} - -void RegisterMenuPathFix() { - GameInteractor::Instance->RegisterGameHook([](int32_t sceneNum) { - UpdateDirtPathFixState(sceneNum); - }); -} - -void UpdateMirrorModeState(int32_t sceneNum) { - static bool prevMirroredWorld = false; - bool nextMirroredWorld = false; - - int16_t mirroredMode = CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorldMode"), MIRRORED_WORLD_OFF); - int16_t inDungeon = (sceneNum >= SCENE_DEKU_TREE && sceneNum <= SCENE_INSIDE_GANONS_CASTLE_COLLAPSE && sceneNum != SCENE_THIEVES_HIDEOUT) || - (sceneNum >= SCENE_DEKU_TREE_BOSS && sceneNum <= SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) || - (sceneNum == SCENE_GANON_BOSS); - - if (mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED || mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED) { - uint32_t seed = sceneNum + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed() - : gSaveContext.ship.stats.fileCreatedAt); - Random_Init(seed); - } - - bool randomMirror = Random(0, 2) == 1; - - if ( - mirroredMode == MIRRORED_WORLD_ALWAYS || - ((mirroredMode == MIRRORED_WORLD_RANDOM || mirroredMode == MIRRORED_WORLD_RANDOM_SEEDED) && randomMirror) || - // Dungeon modes - (inDungeon && (mirroredMode == MIRRORED_WORLD_DUNGEONS_All || - (mirroredMode == MIRRORED_WORLD_DUNGEONS_VANILLA && !ResourceMgr_IsSceneMasterQuest(sceneNum)) || - (mirroredMode == MIRRORED_WORLD_DUNGEONS_MQ && ResourceMgr_IsSceneMasterQuest(sceneNum)) || - ((mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM || mirroredMode == MIRRORED_WORLD_DUNGEONS_RANDOM_SEEDED) && randomMirror))) - ) { - nextMirroredWorld = true; - CVarSetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 1); - } else { - nextMirroredWorld = false; - CVarClear(CVAR_ENHANCEMENT("MirroredWorld")); - } - - if (prevMirroredWorld != nextMirroredWorld) { - prevMirroredWorld = nextMirroredWorld; - ApplyMirrorWorldGfxPatches(); - } -} - -void RegisterMirrorModeHandler() { - GameInteractor::Instance->RegisterGameHook([](int32_t sceneNum) { - UpdateMirrorModeState(sceneNum); - }); -} - void UpdatePatchHand() { if ((CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) && LINK_IS_CHILD) { ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1", 92, gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); @@ -780,29 +466,6 @@ void RegisterBossDefeatTimestamps() { }); } -void UpdateHurtContainerModeState(bool newState) { - static bool hurtEnabled = false; - if (hurtEnabled == newState) { - return; - } - - hurtEnabled = newState; - uint16_t getHeartPieces = gSaveContext.ship.stats.heartPieces / 4; - uint16_t getHeartContainers = gSaveContext.ship.stats.heartContainers; - - if (hurtEnabled) { - gSaveContext.healthCapacity = 320 - ((getHeartPieces + getHeartContainers) * 16); - } else { - gSaveContext.healthCapacity = 48 + ((getHeartPieces + getHeartContainers) * 16); - } -} - -void RegisterHurtContainerModeHandler() { - GameInteractor::Instance->RegisterGameHook([](int32_t fileNum) { - UpdateHurtContainerModeState(CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)); - }); -} - void RegisterRandomizedEnemySizes() { GameInteractor::Instance->RegisterGameHook([](void* refActor) { // Randomized Enemy Sizes @@ -964,7 +627,6 @@ void RegisterToTMedallions() { }); } - void RegisterFloorSwitchesHook() { GameInteractor::Instance->RegisterGameHook([](void* refActor) { Actor* actor = static_cast(refActor); @@ -1043,22 +705,8 @@ void RegisterCustomSkeletons() { } void InitMods() { - BossRush_RegisterHooks(); - RandomizerRegisterHooks(); - TimeSaverRegisterHooks(); - TimeSavers_Register(); - RegisterTTS(); RegisterOcarinaTimeTravel(); RegisterDaytimeGoldSkultullas(); - RegisterRupeeDash(); - RegisterShadowTag(); - RegisterPermanentHeartLoss(); - RegisterDeleteFileOnDeath(); - RegisterHyperBosses(); - UpdateHyperEnemiesState(); - RegisterBonkDamage(); - RegisterMenuPathFix(); - RegisterMirrorModeHandler(); RegisterResetNaviTimer(); RegisterEnemyDefeatCounts(); RegisterBossDefeatTimestamps(); @@ -1066,11 +714,10 @@ void InitMods() { RegisterOpenAllHours(); RegisterToTMedallions(); RegisterRandomizerCompasses(); - NameTag_RegisterHooks(); RegisterFloorSwitchesHook(); RegisterPatchHandHandler(); - RegisterHurtContainerModeHandler(); RegisterPauseMenuHooks(); - RandoKaleido_RegisterHooks(); RegisterCustomSkeletons(); } + +static RegisterShipInitFunc initFunc(InitMods); diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 9305222502a..b6b6b088fd7 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -1,5 +1,3 @@ -#include - #ifndef MODS_H #define MODS_H @@ -7,16 +5,9 @@ extern "C" { #endif -void UpdateDirtPathFixState(int32_t sceneNum); -void UpdateMirrorModeState(int32_t sceneNum); -void UpdateHurtContainerModeState(bool newState); void PatchToTMedallions(); void PatchCompasses(); -void UpdatePermanentHeartLossState(); -void UpdateHyperEnemiesState(); -void UpdateHyperBossesState(); -void InitMods(); -void UpdatePatchHand(); +void UpdatePatchHand(); void SwitchAge(); #ifdef __cplusplus diff --git a/soh/soh/Enhancements/nametag.cpp b/soh/soh/Enhancements/nametag.cpp index 49b0891fa3c..2143977cecf 100644 --- a/soh/soh/Enhancements/nametag.cpp +++ b/soh/soh/Enhancements/nametag.cpp @@ -313,3 +313,5 @@ void NameTag_RegisterHooks() { // Remove all name tags on play state destroy as all actors are removed anyways GameInteractor::Instance->RegisterGameHook([]() { RemoveAllNameTags(); }); } + +static RegisterShipInitFunc initFunc(NameTag_RegisterHooks); diff --git a/soh/soh/Enhancements/nametag.h b/soh/soh/Enhancements/nametag.h index 9fd26643e80..3b979678e29 100644 --- a/soh/soh/Enhancements/nametag.h +++ b/soh/soh/Enhancements/nametag.h @@ -8,9 +8,6 @@ typedef struct { Color_RGBA8 textColor; // Text color override. Global color is used if alpha is 0 } NameTagOptions; -// Register required hooks for nametags on startup -void NameTag_RegisterHooks(); - #ifdef __cplusplus extern "C" { #endif diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index f48b7da5a99..feee4b34128 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -2471,3 +2471,5 @@ void RandomizerRegisterHooks() { } }); } + +static RegisterShipInitFunc initFunc(RandomizerRegisterHooks); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.h b/soh/soh/Enhancements/randomizer/hook_handlers.h deleted file mode 100644 index 46abb30b72c..00000000000 --- a/soh/soh/Enhancements/randomizer/hook_handlers.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef RANDOMIZER_HOOK_HANDLERS_H -#define RANDOMIZER_HOOK_HANDLERS_H - -void RandomizerRegisterHooks(); - -#endif // RANDOMIZER_HOOK_HANDLERS_H \ No newline at end of file diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index eee2a0e9bf3..01540739977 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -1232,3 +1232,5 @@ void TimeSaverRegisterHooks() { onItemReceiveHook = GameInteractor::Instance->RegisterGameHook(TimeSaverOnItemReceiveHandler); }); } + +static RegisterShipInitFunc initFunc(TimeSaverRegisterHooks); diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.h b/soh/soh/Enhancements/timesaver_hook_handlers.h deleted file mode 100644 index 41bccd8d99d..00000000000 --- a/soh/soh/Enhancements/timesaver_hook_handlers.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef TIMESAVER_HOOK_HANDLERS_H -#define TIMESAVER_HOOK_HANDLERS_H - -void TimeSaverRegisterHooks(); - -#endif // TIMESAVER_HOOK_HANDLERS_H \ No newline at end of file diff --git a/soh/soh/Enhancements/tts/tts.cpp b/soh/soh/Enhancements/tts/tts.cpp index 9d58e2d62c7..64f3ffc1bb1 100644 --- a/soh/soh/Enhancements/tts/tts.cpp +++ b/soh/soh/Enhancements/tts/tts.cpp @@ -1077,3 +1077,5 @@ void RegisterTTS() { InitTTSBank(); RegisterTTSModHooks(); } + +static RegisterShipInitFunc initFunc(RegisterTTS); diff --git a/soh/soh/Enhancements/tts/tts.h b/soh/soh/Enhancements/tts/tts.h deleted file mode 100644 index 4276287044c..00000000000 --- a/soh/soh/Enhancements/tts/tts.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef TTS_H -#define TTS_H - -void RegisterTTS(); - -#endif diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 2acc01d7fae..69b8aa1b02c 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1192,7 +1192,6 @@ extern "C" void InitOTR() { VanillaItemTable_Init(); DebugConsole_Init(); - InitMods(); ActorDB::AddBuiltInCustomActors(); // #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer CVarClear(CVAR_GENERAL("RandomizerNewFileDropped")); diff --git a/soh/soh/SohGui/SohMenuBar.cpp b/soh/soh/SohGui/SohMenuBar.cpp index 5fd7324fbcb..68699f0c45a 100644 --- a/soh/soh/SohGui/SohMenuBar.cpp +++ b/soh/soh/SohGui/SohMenuBar.cpp @@ -1048,9 +1048,7 @@ void DrawEnhancementsMenu() { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); UIWidgets::Tooltip("Dying will delete your file\n\n " ICON_FA_EXCLAMATION_TRIANGLE " WARNING " ICON_FA_EXCLAMATION_TRIANGLE "\nTHIS IS NOT REVERSABLE\nUSE AT YOUR OWN RISK!"); ImGui::PopStyleColor(); - if (UIWidgets::PaddedEnhancementCheckbox("Permanent heart loss", CVAR_ENHANCEMENT("PermanentHeartLoss"), true, false)) { - UpdatePermanentHeartLossState(); - } + UIWidgets::PaddedEnhancementCheckbox("Permanent heart loss", CVAR_ENHANCEMENT("PermanentHeartLoss"), true, false); UIWidgets::Tooltip("When you lose 4 quarters of a heart you will permanently lose that heart container.\n\nDisabling this after the fact will restore your heart containers."); ImGui::Text("Damage Multiplier"); UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("DamageMult"), allPowers, 0); @@ -1106,13 +1104,9 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("Bonking into trees will have a chance to drop up to 3 sticks. Must already have obtained sticks."); UIWidgets::PaddedEnhancementCheckbox("No Heart Drops", CVAR_ENHANCEMENT("NoHeartDrops"), true, false); UIWidgets::Tooltip("Disables heart drops, but not heart placements, like from a Deku Scrub running off\nThis simulates Hero Mode from other games in the series"); - if (UIWidgets::PaddedEnhancementCheckbox("Hyper Bosses", CVAR_ENHANCEMENT("HyperBosses"), true, false)) { - UpdateHyperBossesState(); - } + UIWidgets::PaddedEnhancementCheckbox("Hyper Bosses", CVAR_ENHANCEMENT("HyperBosses"), true, false); UIWidgets::Tooltip("All major bosses move and act twice as fast."); - if (UIWidgets::PaddedEnhancementCheckbox("Hyper Enemies", CVAR_ENHANCEMENT("HyperEnemies"), true, false)) { - UpdateHyperEnemiesState(); - } + UIWidgets::PaddedEnhancementCheckbox("Hyper Enemies", CVAR_ENHANCEMENT("HyperEnemies"), true, false); UIWidgets::Tooltip("All regular enemies and mini-bosses move and act twice as fast."); UIWidgets::PaddedEnhancementCheckbox("Always Win Goron Pot", CVAR_ENHANCEMENT("GoronPot"), true, false); UIWidgets::Tooltip("Always get the heart piece/purple rupee from the spinning Goron pot"); @@ -1302,9 +1296,7 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("When medallions are collected, the medallion imprints around the Master Sword pedestal in the Temple of Time will become colored"); UIWidgets::PaddedEnhancementCheckbox("Show locked door chains on both sides of locked doors", CVAR_ENHANCEMENT("ShowDoorLocksOnBothSides"), true, false); UIWidgets::PaddedText("Fix Vanishing Paths", true, false); - if (UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix"), zFightingOptions, ZFIGHT_FIX_DISABLED) && gPlayState != NULL) { - UpdateDirtPathFixState(gPlayState->sceneNum); - } + UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix"), zFightingOptions, ZFIGHT_FIX_DISABLED); UIWidgets::Tooltip("Disabled: Paths vanish more the higher the resolution (Z-fighting is based on resolution)\n" "Consistent: Certain paths vanish the same way in all resolutions\n" "No Vanish: Paths do not vanish, Link seems to sink in to some paths\n" @@ -1449,9 +1441,7 @@ void DrawEnhancementsMenu() { if (ImGui::BeginMenu("Extra Modes")) { UIWidgets::PaddedText("Mirrored World", true, false); - if (UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("MirroredWorldMode"), mirroredWorldModes, MIRRORED_WORLD_OFF) && gPlayState != NULL) { - UpdateMirrorModeState(gPlayState->sceneNum); - } + UIWidgets::EnhancementCombobox(CVAR_ENHANCEMENT("MirroredWorldMode"), mirroredWorldModes, MIRRORED_WORLD_OFF); UIWidgets::Tooltip( "Mirrors the world horizontally\n\n" "- Always: Always mirror the world\n" @@ -1561,9 +1551,7 @@ void DrawEnhancementsMenu() { } UIWidgets::Spacer(0); - if (UIWidgets::PaddedEnhancementCheckbox("Hurt Container Mode", CVAR_ENHANCEMENT("HurtContainer"), true, false)) { - UpdateHurtContainerModeState(CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)); - } + UIWidgets::PaddedEnhancementCheckbox("Hurt Container Mode", CVAR_ENHANCEMENT("HurtContainer"), true, false); UIWidgets::Tooltip("Changes Heart Piece and Heart Container functionality.\n\n" "- Each Heart Container or full Heart Piece reduces Links hearts by 1.\n" "- Can be enabled retroactively after a File has already started."); From 80439e5cbec4429c269fc6d4b0ae991e78cc88da Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:09:06 +0100 Subject: [PATCH 2/2] Update DirtPathFix.cpp --- soh/soh/Enhancements/Fixes/DirtPathFix.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/soh/soh/Enhancements/Fixes/DirtPathFix.cpp b/soh/soh/Enhancements/Fixes/DirtPathFix.cpp index 2468955fc05..609a5b6c5ab 100644 --- a/soh/soh/Enhancements/Fixes/DirtPathFix.cpp +++ b/soh/soh/Enhancements/Fixes/DirtPathFix.cpp @@ -1,7 +1,10 @@ #include +#include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/ShipInit.hpp" +extern "C" PlayState* gPlayState; + #define CVAR_DIRT_PATH_FIX_NAME CVAR_ENHANCEMENT("SceneSpecificDirtPathFix") #define CVAR_DIRT_PATH_FIX_DEFAULT ZFIGHT_FIX_DISABLED #define CVAR_DIRT_PATH_FIX_VALUE CVarGetInteger(CVAR_DIRT_PATH_FIX_NAME, CVAR_DIRT_PATH_FIX_DEFAULT)