diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 8fd253b5d1..a823e25bf8 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -9,6 +9,10 @@ #include "soh/Enhancements/randomizer/randomizer_entrance.h" #include "soh/Enhancements/boss-rush/BossRush.h" +#define FULL_HEART_HEALTH 0x10 +#define STARTING_HEALTH (3 * FULL_HEART_HEALTH) +#define MAX_HEALTH (20 * FULL_HEART_HEALTH) + typedef enum { /* 0x0 */ MAGIC_STATE_IDLE, // Regular gameplay /* 0x1 */ MAGIC_STATE_CONSUME_SETUP, // Sets the speed at which magic border flashes diff --git a/soh/soh/Enhancements/Difficulty/HyperEnemies.cpp b/soh/soh/Enhancements/Difficulty/HyperEnemies.cpp new file mode 100644 index 0000000000..b2e0c933b2 --- /dev/null +++ b/soh/soh/Enhancements/Difficulty/HyperEnemies.cpp @@ -0,0 +1,33 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "functions.h" +#include "macros.h" + +extern "C" PlayState* gPlayState; + +static constexpr int32_t CVAR_HYPER_ENEMIES_DEFAULT = 0; +#define CVAR_HYPER_ENEMIES_NAME CVAR_ENHANCEMENT("HyperEnemies") +#define CVAR_HYPER_ENEMIES_VALUE CVarGetInteger(CVAR_HYPER_ENEMIES_NAME, CVAR_HYPER_ENEMIES_DEFAULT) + +static void MakeHyperEnemies(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); + } +} + +static void UpdateHyperEnemiesState() { + COND_HOOK(OnActorUpdate, CVAR_HYPER_ENEMIES_VALUE, MakeHyperEnemies); +} + +static RegisterShipInitFunc initFunc(UpdateHyperEnemiesState, { CVAR_HYPER_ENEMIES_NAME }); diff --git a/soh/soh/Enhancements/Difficulty/PermanentLosses.cpp b/soh/soh/Enhancements/Difficulty/PermanentLosses.cpp index 32b00057e2..cab0aa7d43 100644 --- a/soh/soh/Enhancements/Difficulty/PermanentLosses.cpp +++ b/soh/soh/Enhancements/Difficulty/PermanentLosses.cpp @@ -1,5 +1,4 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" -#include "soh/Enhancements/mods.h" #include "soh/OTRGlobals.h" #include "soh/SaveManager.h" #include "soh/ShipInit.hpp" @@ -23,9 +22,14 @@ static constexpr int32_t CVAR_DELETE_FILE_DEFAULT = 0; static bool hasAffectedHealth = false; -void UpdatePermanentHeartLossState() { - if (!GameInteractor::IsSaveLoaded() || !hasAffectedHealth || CVAR_PERM_HEART_LOSS_VALUE) +static void UpdatePermanentHeartLossState() { + // Reset Link's hearts to the normal value without permanent losses. Only applies if all of the following are true: + // - A saved game is playing + // - The "Permanent Heart Loss" setting is turned off + // - The player has lost at least one Heart Container + if (!GameInteractor::IsSaveLoaded() || !hasAffectedHealth || CVAR_PERM_HEART_LOSS_VALUE) { return; + } 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 @@ -39,8 +43,10 @@ void UpdatePermanentHeartLossState() { } static void UpdateHealthCapacity() { - if (!GameInteractor::IsSaveLoaded()) + // Applies permanent losses of Heart Containers to Link's health. Only applies when a saved game is playing. + if (!GameInteractor::IsSaveLoaded()) { return; + } if (gSaveContext.healthCapacity > 16 && gSaveContext.healthCapacity - gSaveContext.health >= 16) { gSaveContext.healthCapacity -= 16; @@ -50,8 +56,9 @@ static void UpdateHealthCapacity() { } static void DeleteFileOnDeath() { - if (!GameInteractor::IsSaveLoaded() || gPlayState == NULL) + if (!GameInteractor::IsSaveLoaded() || gPlayState == NULL) { return; + } if (gPlayState->gameOverCtx.state == GAMEOVER_DEATH_MENU && gPlayState->pauseCtx.state == 9) { SaveManager::Instance->DeleteZeldaFile(gSaveContext.fileNum); @@ -63,6 +70,7 @@ static void DeleteFileOnDeath() { } static void RegisterPermanentHeartLoss() { + UpdatePermanentHeartLossState(); COND_HOOK(OnPlayerUpdate, CVAR_PERM_HEART_LOSS_VALUE, UpdateHealthCapacity); } diff --git a/soh/soh/Enhancements/ExtraModes/HurtContainer.cpp b/soh/soh/Enhancements/ExtraModes/HurtContainer.cpp new file mode 100644 index 0000000000..ecf39ef315 --- /dev/null +++ b/soh/soh/Enhancements/ExtraModes/HurtContainer.cpp @@ -0,0 +1,42 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "variables.h" +extern SaveContext gSaveContext; +} + +static constexpr int32_t CVAR_HURT_CONTAINER_DEFAULT = 0; +#define CVAR_HURT_CONTAINER_NAME CVAR_ENHANCEMENT("HurtContainer") +#define CVAR_HURT_CONTAINER_VALUE CVarGetInteger(CVAR_HURT_CONTAINER_NAME, CVAR_HURT_CONTAINER_DEFAULT) + +static bool hurtEnabled = false; + +static void UpdateHurtContainerModeState() { + hurtEnabled = CVAR_HURT_CONTAINER_VALUE; + uint16_t heartPieceContainers = gSaveContext.ship.stats.heartPieces / 4; + uint16_t heartContainers = gSaveContext.ship.stats.heartContainers; + uint16_t healthCapacityMod = (heartPieceContainers + heartContainers) * FULL_HEART_HEALTH; + + if (hurtEnabled != CVAR_HURT_CONTAINER_DEFAULT) { + gSaveContext.healthCapacity = MAX_HEALTH - healthCapacityMod; + } else { + gSaveContext.healthCapacity = STARTING_HEALTH + healthCapacityMod; + } +} + +static void RegisterHurtContainer() { + if (GameInteractor::IsSaveLoaded(false)) { + UpdateHurtContainerModeState(); + } + + COND_HOOK(OnLoadGame, hurtEnabled != CVAR_HURT_CONTAINER_VALUE, [](int32_t) { UpdateHurtContainerModeState(); }); + + COND_VB_SHOULD(VB_HEARTS_INCREASE_WITH_CONTAINERS, CVAR_HURT_CONTAINER_VALUE, { + *should = false; + gSaveContext.healthCapacity -= FULL_HEART_HEALTH; + gSaveContext.health -= FULL_HEART_HEALTH; + }); +} + +static RegisterShipInitFunc initFunc(RegisterHurtContainer, { CVAR_HURT_CONTAINER_NAME }); diff --git a/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp b/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp index 2f4f9e4d00..723a5f45e5 100644 --- a/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp +++ b/soh/soh/Enhancements/ExtraModes/MirroredWorld.cpp @@ -3,13 +3,12 @@ #include "soh/Enhancements/randomizer/3drando/random.hpp" #include "soh/Enhancements/randomizer/context.h" #include "soh/Enhancements/enhancementTypes.h" -#include "soh/Enhancements/mods.h" #include "soh/ResourceManagerHelpers.h" #include "soh/ShipInit.hpp" extern "C" { -#include "variables.h" extern SaveContext gSaveContext; +extern PlayState* gPlayState; } static constexpr MirroredWorldMode CVAR_MIRRORED_WORLD_DEFAULT = MIRRORED_WORLD_OFF; @@ -55,13 +54,7 @@ static bool MirroredWorld_ShouldApply(int32_t sceneNum) { } } -static void RegisterMirroredWorld() { - COND_HOOK(OnSceneInit, CVAR_MIRRORED_WORLD_MODE_VALUE, UpdateMirrorModeState); -} - -static RegisterShipInitFunc initFunc(RegisterMirroredWorld, { CVAR_MIRRORED_WORLD_MODE_NAME }); - -void UpdateMirrorModeState(int32_t sceneNum) { +static void UpdateMirrorModeState(int32_t sceneNum) { bool nextMirroredWorld = MirroredWorld_ShouldApply(sceneNum); if (prevMirroredWorld == nextMirroredWorld) { @@ -73,8 +66,17 @@ void UpdateMirrorModeState(int32_t sceneNum) { CVarSetInteger(CVAR_MIRRORED_WORLD_NAME, 1); } else { CVarClear(CVAR_MIRRORED_WORLD_NAME); - RegisterMirroredWorld(); } ApplyMirrorWorldGfxPatches(); } + +static void RegisterMirroredWorld() { + if (gPlayState != NULL) { + UpdateMirrorModeState(gPlayState->sceneNum); + } + + COND_HOOK(OnSceneInit, CVAR_MIRRORED_WORLD_MODE_VALUE, UpdateMirrorModeState); +} + +static RegisterShipInitFunc initFunc(RegisterMirroredWorld, { CVAR_MIRRORED_WORLD_MODE_NAME }); diff --git a/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp b/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp index 20bdc16bc7..2fc224152d 100644 --- a/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp +++ b/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp @@ -17,7 +17,7 @@ static constexpr int32_t CVAR_RUPEE_DASH_INTERVAL_DEFAULT = 5; #define CVAR_RUPEE_DASH_INTERVAL_TIME \ CVarGetInteger(CVAR_RUPEE_DASH_INTERVAL_NAME, CVAR_RUPEE_DASH_INTERVAL_DEFAULT) * 20 -void UpdateRupeeDash() { +static void UpdateRupeeDash() { // Initialize Timer static uint16_t rupeeDashTimer = 0; @@ -36,7 +36,7 @@ void UpdateRupeeDash() { } } -void RegisterRupeeDash() { +static void RegisterRupeeDash() { COND_HOOK(OnPlayerUpdate, CVAR_RUPEE_DASH_VALUE, UpdateRupeeDash); } diff --git a/soh/soh/Enhancements/ExtraModes/ShadowTag.cpp b/soh/soh/Enhancements/ExtraModes/ShadowTag.cpp index 1b03b6c77f..6dc93bfe17 100644 --- a/soh/soh/Enhancements/ExtraModes/ShadowTag.cpp +++ b/soh/soh/Enhancements/ExtraModes/ShadowTag.cpp @@ -16,7 +16,7 @@ static constexpr s8 ROOM_GREEN_POE = 16; static constexpr s8 ROOM_BLUE_POE = 13; static constexpr s8 ROOM_RED_POE = 12; -void OnPlayerUpdateShadowTag() { +static void OnPlayerUpdateShadowTag() { if (gPlayState->sceneNum == SCENE_FOREST_TEMPLE) { switch (gPlayState->roomCtx.curRoom.num) { case ROOM_GREEN_POE: @@ -36,12 +36,12 @@ void OnPlayerUpdateShadowTag() { } } -void ResetShadowTagSpawnTimer() { +static void ResetShadowTagSpawnTimer() { shouldSpawn = true; delayTimer = 60; } -void RegisterShadowTag() { +static void RegisterShadowTag() { COND_HOOK(OnPlayerUpdate, CVAR_SHADOW_TAG_VALUE, OnPlayerUpdateShadowTag); COND_HOOK(OnSceneSpawnActors, true, []() { ResetShadowTagSpawnTimer(); }); COND_HOOK(OnSceneInit, true, [](int16_t) { ResetShadowTagSpawnTimer(); }); diff --git a/soh/soh/Enhancements/Fixes/BrokenGiantsKnife.cpp b/soh/soh/Enhancements/Fixes/BrokenGiantsKnife.cpp index 1bde4e50c7..1d4a55f5d8 100644 --- a/soh/soh/Enhancements/Fixes/BrokenGiantsKnife.cpp +++ b/soh/soh/Enhancements/Fixes/BrokenGiantsKnife.cpp @@ -13,7 +13,7 @@ static constexpr int32_t CVAR_BGS_FIX_DEFAULT = 0; #define CVAR_BGS_FIX_NAME CVAR_ENHANCEMENT("FixBrokenGiantsKnife") #define CVAR_BGS_FIX_VALUE CVarGetInteger(CVAR_BGS_FIX_NAME, CVAR_BGS_FIX_DEFAULT) -void OnReceiveBrokenGiantsKnife(GetItemEntry itemEntry) { +static void OnReceiveBrokenGiantsKnife(GetItemEntry itemEntry) { if (itemEntry.itemId != ITEM_SWORD_BGS) { return; } @@ -39,7 +39,7 @@ void OnReceiveBrokenGiantsKnife(GetItemEntry itemEntry) { } } -void RegisterBrokenGiantsKnifeFix() { +static void RegisterBrokenGiantsKnifeFix() { // If enhancement is off, flag should be handled exclusively by vanilla behaviour COND_HOOK(OnItemReceive, CVAR_BGS_FIX_VALUE || IS_RANDO, OnReceiveBrokenGiantsKnife); } diff --git a/soh/soh/Enhancements/Fixes/DekuNutUpgradeFix.cpp b/soh/soh/Enhancements/Fixes/DekuNutUpgradeFix.cpp index 8096f20757..23e0a1fd63 100644 --- a/soh/soh/Enhancements/Fixes/DekuNutUpgradeFix.cpp +++ b/soh/soh/Enhancements/Fixes/DekuNutUpgradeFix.cpp @@ -13,7 +13,7 @@ static constexpr int32_t CVAR_NUT_UPGRADE_FIX_DEFAULT = 0; #define CVAR_NUT_UPGRADE_FIX_NAME CVAR_ENHANCEMENT("DekuNutUpgradeFix") #define CVAR_NUT_UPGRADE_FIX_VALUE CVarGetInteger(CVAR_NUT_UPGRADE_FIX_NAME, CVAR_NUT_UPGRADE_FIX_DEFAULT) -void DekuNutUpgradeFixAtForestStage(bool* should) { +static void DekuNutUpgradeFixAtForestStage(bool* should) { // This check is needed because of an intentional fallthrough at the source if (Player_GetMask(gPlayState) == PLAYER_MASK_SKULL) { return; @@ -30,11 +30,11 @@ void DekuNutUpgradeFixAtForestStage(bool* should) { } } -void DekuNutUpgradeSetByPoachersSaw(bool* should) { +static void DekuNutUpgradeSetByPoachersSaw(bool* should) { *should = false; } -void RegisterDekuNutUpgradeFix() { +static void RegisterDekuNutUpgradeFix() { COND_VB_SHOULD(VB_POACHERS_SAW_SET_DEKU_NUT_UPGRADE_FLAG, CVAR_NUT_UPGRADE_FIX_VALUE || IS_RANDO, { DekuNutUpgradeSetByPoachersSaw(should); }); COND_VB_SHOULD(VB_DEKU_SCRUBS_REACT_TO_MASK_OF_TRUTH, CVAR_NUT_UPGRADE_FIX_VALUE && !IS_RANDO, diff --git a/soh/soh/Enhancements/Fixes/DirtPathFix.cpp b/soh/soh/Enhancements/Fixes/DirtPathFix.cpp index 0ceafc9c1c..ae49f45bfa 100644 --- a/soh/soh/Enhancements/Fixes/DirtPathFix.cpp +++ b/soh/soh/Enhancements/Fixes/DirtPathFix.cpp @@ -1,13 +1,14 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/enhancementTypes.h" -#include "soh/Enhancements/mods.h" #include "soh/ShipInit.hpp" +extern "C" PlayState* gPlayState; + static constexpr ZFightingFixType CVAR_DIRT_PATH_DEFAULT = ZFIGHT_FIX_DISABLED; #define CVAR_DIRT_PATH_NAME CVAR_ENHANCEMENT("SceneSpecificDirtPathFix") #define CVAR_DIRT_PATH_VALUE CVarGetInteger(CVAR_DIRT_PATH_NAME, CVAR_DIRT_PATH_DEFAULT) -void DirtPathFix_UpdateZFightingMode(int32_t sceneNum) { +static void DirtPathFix_UpdateZFightingMode(int32_t sceneNum) { switch (sceneNum) { case SCENE_HYRULE_FIELD: case SCENE_KOKIRI_FOREST: @@ -20,6 +21,10 @@ void DirtPathFix_UpdateZFightingMode(int32_t sceneNum) { } static void RegisterDirtPathFix() { + if (gPlayState != NULL) { + DirtPathFix_UpdateZFightingMode(gPlayState->sceneNum); + } + COND_HOOK(OnTransitionEnd, CVAR_DIRT_PATH_VALUE, DirtPathFix_UpdateZFightingMode); } diff --git a/soh/soh/Enhancements/Fixes/HammerHandFix.cpp b/soh/soh/Enhancements/Fixes/HammerHandFix.cpp new file mode 100644 index 0000000000..5cee8c30de --- /dev/null +++ b/soh/soh/Enhancements/Fixes/HammerHandFix.cpp @@ -0,0 +1,38 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "macros.h" +#include "soh/ResourceManagerHelpers.h" +#include "objects/object_link_boy/object_link_boy.h" +extern SaveContext gSaveContext; +} + +static constexpr int32_t CVAR_HAMMER_HAND_DEFAULT = 0; +#define CVAR_HAMMER_HAND_NAME CVAR_ENHANCEMENT("FixHammerHand") +#define CVAR_HAMMER_HAND_VALUE CVarGetInteger(CVAR_HAMMER_HAND_NAME, CVAR_HAMMER_HAND_DEFAULT) + +static void FixHammerHand() { + if (LINK_IS_ADULT) { + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand1", 92, + gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand2", 93, gsSPEndDisplayList()); + } +} + +static void ResetHammerHand() { + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand2"); +} + +static void RegisterHammerHandFix() { + if (CVAR_HAMMER_HAND_VALUE) { + FixHammerHand(); + } else { + ResetHammerHand(); + } + + COND_HOOK(OnSceneInit, CVAR_HAMMER_HAND_VALUE, [](int32_t) { FixHammerHand(); }); +} + +static RegisterShipInitFunc initFunc(RegisterHammerHandFix, { CVAR_HAMMER_HAND_NAME }); diff --git a/soh/soh/Enhancements/Graphics/AgeDependentEquipment.cpp b/soh/soh/Enhancements/Graphics/AgeDependentEquipment.cpp new file mode 100644 index 0000000000..e8b0d12704 --- /dev/null +++ b/soh/soh/Enhancements/Graphics/AgeDependentEquipment.cpp @@ -0,0 +1,86 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "macros.h" +#include "soh/ResourceManagerHelpers.h" +#include "objects/object_link_boy/object_link_boy.h" +#include "objects/object_link_child/object_link_child.h" +extern SaveContext gSaveContext; +} + +static constexpr int32_t CVAR_AGE_EQUIPMENT_DEFAULT = 0; +#define CVAR_AGE_EQUIPMENT_NAME CVAR_ENHANCEMENT("EquipmentAlwaysVisible") +#define CVAR_AGE_EQUIPMENT_VALUE CVarGetInteger(CVAR_AGE_EQUIPMENT_NAME, CVAR_AGE_EQUIPMENT_DEFAULT) + +static void ResetAdultHands() { + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2"); + ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1"); + ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2"); +} + +static void ResetChildHands() { + ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword"); + ResourceMgr_UnpatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot"); + ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang"); + ResourceMgr_UnpatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield"); +} + +static void MakeEquipmentAlwaysVisible() { + if (LINK_IS_CHILD) { + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1", 92, + gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2", 93, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1", 84, + gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2", 85, + gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1", 51, + gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2", 52, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1", 104, + gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2", 105, + gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1", 79, + gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2", 80, gsSPEndDisplayList()); + ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1", 76, + gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); + ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2", 77, + gsSPEndDisplayList()); + ResetChildHands(); + } else { + ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword", 13, + gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot", 13, + gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang", 50, + gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); + ResourceMgr_PatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield", 49, + gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL)); + ResetAdultHands(); + } +} + +static void RegisterAgeDependentEquipmentHook() { + if (CVAR_AGE_EQUIPMENT_VALUE) { + MakeEquipmentAlwaysVisible(); + } else { + ResetAdultHands(); + ResetChildHands(); + } + + COND_HOOK(OnSceneInit, CVAR_AGE_EQUIPMENT_VALUE, [](int32_t) { MakeEquipmentAlwaysVisible(); }); +} + +static RegisterShipInitFunc initFunc(RegisterAgeDependentEquipmentHook, { CVAR_AGE_EQUIPMENT_NAME }); diff --git a/soh/soh/Enhancements/Graphics/ToTMedallions.cpp b/soh/soh/Enhancements/Graphics/ToTMedallions.cpp index 13c752a622..5a7809836c 100644 --- a/soh/soh/Enhancements/Graphics/ToTMedallions.cpp +++ b/soh/soh/Enhancements/Graphics/ToTMedallions.cpp @@ -1,5 +1,4 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" -#include "soh/Enhancements/mods.h" #include "soh/ShipInit.hpp" extern "C" { @@ -102,14 +101,6 @@ static void ResetToTMedallions() { endGrayscale.RevertPatch(); } -void UpdateToTMedallions() { - if (CVAR_TOT_MEDALLION_COLORS_VALUE) { - PatchToTMedallions(); - } else { - ResetToTMedallions(); - } -} - static void CheckTempleOfTime(int16_t sceneNum) { if (sceneNum != SCENE_TEMPLE_OF_TIME) { return; @@ -118,6 +109,12 @@ static void CheckTempleOfTime(int16_t sceneNum) { } static void RegisterToTMedallions() { + if (CVAR_TOT_MEDALLION_COLORS_VALUE) { + PatchToTMedallions(); + } else { + ResetToTMedallions(); + } + COND_HOOK(OnItemReceive, CVAR_TOT_MEDALLION_COLORS_VALUE, [](GetItemEntry) { if (gPlayState) { CheckTempleOfTime(gPlayState->sceneNum); diff --git a/soh/soh/Enhancements/Presets/Presets.cpp b/soh/soh/Enhancements/Presets/Presets.cpp index 876ce1449b..4a601f6b76 100644 --- a/soh/soh/Enhancements/Presets/Presets.cpp +++ b/soh/soh/Enhancements/Presets/Presets.cpp @@ -129,6 +129,7 @@ void applyPreset(std::string presetName, std::vector includeSecti } } ShipInit::InitAll(); + OTRGlobals::Instance->ScaleImGui(); } void DrawPresetSelector(std::vector includeSections, std::string presetLoc, bool disabled) { diff --git a/soh/soh/Enhancements/QoL/DaytimeGS.cpp b/soh/soh/Enhancements/QoL/DaytimeGS.cpp index 6d44742741..f1aa322cee 100644 --- a/soh/soh/Enhancements/QoL/DaytimeGS.cpp +++ b/soh/soh/Enhancements/QoL/DaytimeGS.cpp @@ -21,7 +21,7 @@ struct DayTimeGoldSkulltulas { using DayTimeGoldSkulltulasList = std::vector; -void OnSpawnNighttimeGoldSkulltula() { +static void OnSpawnNighttimeGoldSkulltula() { // 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 = { @@ -62,7 +62,7 @@ void OnSpawnNighttimeGoldSkulltula() { } } -void RegisterDaytimeGoldSkultullas() { +static void RegisterDaytimeGoldSkultullas() { COND_HOOK(OnSceneSpawnActors, CVAR_DAYTIME_GS_VALUE, OnSpawnNighttimeGoldSkulltula); } diff --git a/soh/soh/Enhancements/QoL/OpenAllHours.cpp b/soh/soh/Enhancements/QoL/OpenAllHours.cpp index 5b6d39ad97..b46db53c37 100644 --- a/soh/soh/Enhancements/QoL/OpenAllHours.cpp +++ b/soh/soh/Enhancements/QoL/OpenAllHours.cpp @@ -24,12 +24,10 @@ static constexpr int32_t DOOR_NIGHT_KAK_POTION_SHOP = 7822; static constexpr int32_t DOOR_NIGHT_KAK_POTION_SHOP_BACK = 8846; static void OpenAllHours(void* refActor) { - Actor* actor = static_cast(refActor); - if (actor->id != ACTOR_EN_DOOR) { - return; - } + EnDoor* enDoor = static_cast(refActor); + s16* params = &enDoor->actor.params; - switch (actor->params) { + switch (*params) { case DOOR_DAY_CHEST_GAME: case DOOR_DAY_BOMBCHU_SHOP: case DOOR_NIGHT_POTION_SHOP: @@ -40,8 +38,7 @@ static void OpenAllHours(void* refActor) { case DOOR_NIGHT_KAK_BAZAAR: case DOOR_NIGHT_KAK_POTION_SHOP: case DOOR_NIGHT_KAK_POTION_SHOP_BACK: { - actor->params = (actor->params & 0xFC00) | (DOOR_SCENEEXIT << 7) | 0x3F; - EnDoor* enDoor = static_cast(refActor); + *params = (*params & 0xFC00) | (DOOR_SCENEEXIT << 7) | 0x3F; EnDoor_SetupType(enDoor, gPlayState); break; } @@ -54,7 +51,7 @@ static void RegisterOpenAllHours() { bool overworldDoorsOpen = !IS_RANDO || !OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LOCK_OVERWORLD_DOORS); - COND_HOOK(OnActorInit, CVAR_OPEN_ALL_HOURS_VALUE && overworldDoorsOpen, OpenAllHours); + COND_ID_HOOK(OnActorInit, ACTOR_EN_DOOR, CVAR_OPEN_ALL_HOURS_VALUE && overworldDoorsOpen, OpenAllHours); } static RegisterShipInitFunc initFunc(RegisterOpenAllHours, { CVAR_OPEN_ALL_HOURS_NAME, "IS_RANDO" }); diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index be6acae739..fa07272183 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include "../randomizer/3drando/random.hpp" diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index b01ffc5f05..c0ac6bbc1d 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -5,12 +5,11 @@ #include #include -#include #include -#include #include #include "soh/SohGui/UIWidgets.hpp" +#include "soh/SohGui/SohMenu.h" #include "soh/SohGui/SohGui.hpp" #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" @@ -22,7 +21,6 @@ extern "C" { #include "objects/object_link_boy/object_link_boy.h" #include "objects/object_link_child/object_link_child.h" #include "objects/object_gi_shield_3/object_gi_shield_3.h" -#include "objects/object_gi_heart/object_gi_heart.h" #include "objects/object_gi_bow/object_gi_bow.h" #include "objects/object_gi_bracelet/object_gi_bracelet.h" #include "objects/object_gi_rupy/object_gi_rupy.h" @@ -64,6 +62,12 @@ void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName); u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); } +static WidgetInfo goronNeck; + +namespace SohGui { +extern std::shared_ptr mSohMenu; +} + #define PATCH_GFX(path, name, cvar, index, instruction) \ if (CVarGetInteger(cvar, 0)) { \ ResourceMgr_PatchGfxByName(path, name, index, instruction); \ @@ -2026,15 +2030,7 @@ void DrawSillyTab() { UIWidgets::Separator(true, true, 2.0f, 2.0f); - UIWidgets::CVarSliderFloat("Goron Neck Length", CVAR_COSMETIC("Goron.NeckLength"), - UIWidgets::FloatSliderOptions() - .Format("%.0f") - .Min(0.0f) - .Max(5000.0f) - .DefaultValue(0.0f) - .Step(10.0f) - .Size(ImVec2(300.0f, 0.0f)) - .Color(THEME_COLOR)); + SohGui::mSohMenu->MenuDrawItem(goronNeck, ImGui::GetContentRegionAvail().x, THEME_COLOR); Reset_Option_Single("Reset##Goron_NeckLength", CVAR_COSMETIC("Goron.NeckLength")); UIWidgets::Separator(true, true, 2.0f, 2.0f); @@ -2648,22 +2644,6 @@ void RegisterOnGameFrameUpdateHook() { GameInteractor::Instance->RegisterGameHook([]() { CosmeticsUpdateTick(); }); } -void Cosmetics_RegisterOnSceneInitHook() { - GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { - if (CVarGetInteger(CVAR_COSMETIC("RandomizeAllOnNewScene"), 0)) { - CosmeticsEditor_RandomizeAll(); - } - }); -} - -void CosmeticsEditorRegisterOnGenerationCompletionHook() { - GameInteractor::Instance->RegisterGameHook([]() { - if (CVarGetInteger(CVAR_COSMETIC("RandomizeAllOnRandoGen"), 0)) { - CosmeticsEditor_RandomizeAll(); - } - }); -} - void CosmeticsEditorWindow::InitElement() { // Convert the `current color` into the format that the ImGui color picker expects for (auto& [id, cosmeticOption] : cosmeticOptions) { @@ -2679,11 +2659,6 @@ void CosmeticsEditorWindow::InitElement() { Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); ApplyOrResetCustomGfxPatches(); ApplyAuthenticGfxPatches(); - - RegisterOnLoadGameHook(); - RegisterOnGameFrameUpdateHook(); - Cosmetics_RegisterOnSceneInitHook(); - CosmeticsEditorRegisterOnGenerationCompletionHook(); } void CosmeticsEditor_RandomizeAll() { @@ -2732,3 +2707,33 @@ void CosmeticsEditor_ResetGroup(CosmeticGroup group) { Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); ApplyOrResetCustomGfxPatches(); } + +void RegisterCosmeticHooks() { + COND_HOOK(OnSceneInit, CVarGetInteger(CVAR_COSMETIC("RandomizeAllOnNewScene"), 0), + [](s16 sceneNum) { CosmeticsEditor_RandomizeAll(); }); + + COND_HOOK(OnGenerationCompletion, CVarGetInteger(CVAR_COSMETIC("RandomizeAllOnRandoGen"), 0), + []() { CosmeticsEditor_RandomizeAll(); }); + + COND_HOOK(OnGameFrameUpdate, true, CosmeticsUpdateTick); +} + +void RegisterCosmeticWidgets() { + goronNeck = { .name = "Goron Neck Length", .type = WidgetType::WIDGET_CVAR_SLIDER_FLOAT }; + goronNeck.CVar(CVAR_COSMETIC("Goron.NeckLength")) + .Options(UIWidgets::FloatSliderOptions() + .Format("%.0f") + .Min(0.0f) + .Max(5000.0f) + .DefaultValue(0.0f) + .Step(10.0f) + .Size(ImVec2(300.0f, 0.0f)) + .Color(THEME_COLOR)); + SohGui::mSohMenu->AddSearchWidget({ goronNeck, "Enhancements", "Cosmetics Editor", "Silly" }); +} + +static RegisterShipInitFunc initFunc(RegisterCosmeticHooks, { + CVAR_COSMETIC("RandomizeAllOnNewScene"), + CVAR_COSMETIC("RandomizeAllOnRandoGen"), + }); +static RegisterMenuInitFunc menuInitFunc(RegisterCosmeticWidgets); diff --git a/soh/soh/Enhancements/CustomSkeletons.cpp b/soh/soh/Enhancements/cosmetics/CustomSkeletons.cpp similarity index 100% rename from soh/soh/Enhancements/CustomSkeletons.cpp rename to soh/soh/Enhancements/cosmetics/CustomSkeletons.cpp diff --git a/soh/soh/Enhancements/debugger/hookDebugger.cpp b/soh/soh/Enhancements/debugger/hookDebugger.cpp index 146db2ddc9..709bbb1d42 100644 --- a/soh/soh/Enhancements/debugger/hookDebugger.cpp +++ b/soh/soh/Enhancements/debugger/hookDebugger.cpp @@ -1,4 +1,5 @@ #include "hookDebugger.h" +#include "soh/SohGui/SohGui.hpp" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/SohGui/UIWidgets.hpp" #include "soh/OTRGlobals.h" @@ -7,6 +8,9 @@ static std::map*> hookData; +static bool hookOptCollapseAll; // A bool that will collapse all hook group once +static bool hookOptExpandAll; // A bool that will expand all hook group once + const ImVec4 grey = ImVec4(0.75, 0.75, 0.75, 1); const ImVec4 yellow = ImVec4(1, 1, 0, 1); const ImVec4 red = ImVec4(1, 0, 0, 1); @@ -77,6 +81,9 @@ void DrawHookRegisteringInfos(const char* hookName) { } void HookDebuggerWindow::DrawElement() { + bool collapseLogic = false; + bool doingCollapseOrExpand = hookOptExpandAll || hookOptCollapseAll; + ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); #ifndef __cpp_lib_source_location ImGui::TextColored(yellow, "Some features of the Hook Debugger are unavailable because SoH was compiled " @@ -84,9 +91,29 @@ void HookDebuggerWindow::DrawElement() { "(\"__cpp_lib_source_location\" not defined in \"\")."); #endif + if (UIWidgets::Button("Expand All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { + hookOptCollapseAll = false; + hookOptExpandAll = true; + } + ImGui::SameLine(); + if (UIWidgets::Button("Collapse All", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { + hookOptExpandAll = false; + hookOptCollapseAll = true; + } + ImGui::PushFont(OTRGlobals::Instance->fontMonoLarger); for (auto& [hookName, _] : hookData) { + if (doingCollapseOrExpand) { + if (hookOptExpandAll) { + collapseLogic = true; + } else if (hookOptCollapseAll) { + collapseLogic = false; + } + ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always); + } + if (ImGui::TreeNode(hookName)) { DrawHookRegisteringInfos(hookName); ImGui::TreePop(); @@ -95,9 +122,17 @@ void HookDebuggerWindow::DrawElement() { ImGui::PopFont(); ImGui::EndDisabled(); + + if (doingCollapseOrExpand) { + hookOptExpandAll = false; + hookOptCollapseAll = false; + } } void HookDebuggerWindow::InitElement() { + hookOptExpandAll = false; + hookOptCollapseAll = false; + #define DEFINE_HOOK(name, _) hookData.insert({ #name, GameInteractor::Instance->GetHookData() }); #include "../game-interactor/GameInteractor_HookTable.h" diff --git a/soh/soh/Enhancements/debugger/hookDebugger.h b/soh/soh/Enhancements/debugger/hookDebugger.h index 1a586a09cf..c1f439f302 100644 --- a/soh/soh/Enhancements/debugger/hookDebugger.h +++ b/soh/soh/Enhancements/debugger/hookDebugger.h @@ -1,3 +1,6 @@ +#ifndef hookDebugger_h +#define hookDebugger_h + #include class HookDebuggerWindow final : public Ship::GuiWindow { @@ -8,3 +11,5 @@ class HookDebuggerWindow final : public Ship::GuiWindow { void DrawElement() override; void UpdateElement() override{}; }; + +#endif // hookDebugger_h diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index 682a1271ec..7c762ca663 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -101,8 +101,10 @@ void UnsetFlag::_Apply() { GameInteractionEffectQueryResult ModifyHeartContainers::CanBeApplied() { if (!GameInteractor::IsSaveLoaded(true)) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; - } else if ((parameters[0] > 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) > 0x140)) || - (parameters[0] < 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) < 0x10))) { + } else if ((parameters[0] > 0 && + (gSaveContext.healthCapacity + (parameters[0] * FULL_HEART_HEALTH) > MAX_HEALTH)) || + (parameters[0] < 0 && + (gSaveContext.healthCapacity + (parameters[0] * FULL_HEART_HEALTH) < FULL_HEART_HEALTH))) { return GameInteractionEffectQueryResult::NotPossible; } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 44296118e8..f779001c17 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -28,6 +28,7 @@ DEFINE_HOOK(OnPlayerDeath, ()); DEFINE_HOOK(OnSetDoAction, (uint16_t action)); DEFINE_HOOK(OnPlayerSfx, (u16 sfxId)); DEFINE_HOOK(OnOcarinaSongAction, ()); +DEFINE_HOOK(OnOcarinaNote, (uint8_t note, float modulator, int8_t bend)); DEFINE_HOOK(OnCuccoOrChickenHatch, ()); DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price)); DEFINE_HOOK(OnDungeonKeyUsed, (uint16_t mapIndex)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 1ab72fe815..87c59c0716 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -110,6 +110,10 @@ void GameInteractor_ExecuteOnOcarinaSongAction() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnOcarinaNote(uint8_t note, float modulator, int8_t bend) { + GameInteractor::Instance->ExecuteHooks(note, modulator, bend); +} + void GameInteractor_ExecuteOnCuccoOrChickenHatch() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 3a94c07483..f3ef7c2a3f 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -31,6 +31,7 @@ void GameInteractor_ExecuteOnPlayerDeath(); void GameInteractor_ExecuteOnSetDoAction(uint16_t action); void GameInteractor_ExecuteOnPlayerSfx(u16 sfxId); void GameInteractor_ExecuteOnOcarinaSongAction(); +void GameInteractor_ExecuteOnOcarinaNote(uint8_t note, float modulator, int8_t bend); void GameInteractor_ExecuteOnCuccoOrChickenHatch(); bool GameInteractor_ShouldActorInit(void* actor); void GameInteractor_ExecuteOnActorInit(void* actor); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 793466c193..0e82ac7c3e 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -17,7 +17,7 @@ extern PlayState* gPlayState; #include "overlays/actors/ovl_En_Bom/z_en_bom.h" void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { - gSaveContext.healthCapacity += amount * 0x10; + gSaveContext.healthCapacity += amount * FULL_HEART_HEALTH; } void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { @@ -46,17 +46,17 @@ void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { void GameInteractor::RawAction::HealOrDamagePlayer(int16_t hearts) { if (hearts > 0) { - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * FULL_HEART_HEALTH); } else if (hearts < 0) { Player* player = GET_PLAYER(gPlayState); - Health_ChangeBy(gPlayState, hearts * 0x10); + Health_ChangeBy(gPlayState, hearts * FULL_HEART_HEALTH); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); player->invincibilityTimer = 28; } } void GameInteractor::RawAction::SetPlayerHealth(int16_t hearts) { - gSaveContext.health = hearts * 0x10; + gSaveContext.health = hearts * FULL_HEART_HEALTH; } void GameInteractor::RawAction::SetLinkInvisibility(bool active) { diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 83924aed15..928565338a 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1183,6 +1183,14 @@ typedef enum { // - None VB_HEALTH_METER_BE_CRITICAL, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_HEARTS_INCREASE_WITH_CONTAINERS, + // #### `result` // ```c // (respawnFlag == 1) || (respawnFlag == -1) @@ -2343,6 +2351,15 @@ typedef enum { // - `*BgHidanKowarerukabe` VB_FIRE_TEMPLE_BOMBABLE_WALL_BREAK, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*Player` + // - `*Color_RGB8` + VB_APPLY_TUNIC_COLOR, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/kaleido.cpp b/soh/soh/Enhancements/kaleido.cpp index 2b95522633..5edf050f30 100644 --- a/soh/soh/Enhancements/kaleido.cpp +++ b/soh/soh/Enhancements/kaleido.cpp @@ -1,5 +1,7 @@ #include "kaleido.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/frame_interpolation.h" #include "soh/ShipUtils.h" @@ -45,7 +47,7 @@ void KaleidoEntryIcon::LoadIconTex(std::vector* mEntryDl) { } } -KaleidoEntry::KaleidoEntry(int16_t x, int16_t y, std::string text) : mX(x), mY(y), mText(std::move(text)) { +KaleidoEntry::KaleidoEntry(std::string text) : mText(std::move(text)) { mHeight = 0; mWidth = 0; vtx = nullptr; @@ -55,7 +57,12 @@ void KaleidoEntry::SetYOffset(int yOffset) { mY = yOffset; } +void KaleidoEntry::SetSelected(bool val) { + mSelected = val; +} + void KaleidoEntryIcon::Draw(PlayState* play, std::vector* mEntryDl) { + PauseContext* pauseCtx = &play->pauseCtx; if (vtx == nullptr) { return; } @@ -74,13 +81,24 @@ void KaleidoEntryIcon::Draw(PlayState* play, std::vector* mEntryDl) { mEntryDl->push_back(gsSPMatrix(Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW)); + // cursor (if selected) + if (mSelected) { + mEntryDl->push_back(gsDPSetPrimColor(0, 0, 255, 255, 255, 255)); + mEntryDl->push_back(gsSPVertex(vtx, 4, 0)); + Gfx cursorIconTex[] = { gsDPLoadTextureBlock(gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD) }; + mEntryDl->insert(mEntryDl->end(), std::begin(cursorIconTex), std::end(cursorIconTex)); + mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0)); + } + // icon if (!mAchieved) { mEntryDl->push_back(gsDPSetGrayscaleColor(109, 109, 109, 255)); mEntryDl->push_back(gsSPGrayscale(true)); } mEntryDl->push_back(gsDPSetPrimColor(0, 0, mIconColor.r, mIconColor.g, mIconColor.b, mIconColor.a)); - mEntryDl->push_back(gsSPVertex(vtx, 4, 0)); + mEntryDl->push_back(gsSPVertex(&vtx[4], 4, 0)); LoadIconTex(mEntryDl); mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0)); mEntryDl->push_back(gsSPGrayscale(false)); @@ -90,10 +108,10 @@ void KaleidoEntryIcon::Draw(PlayState* play, std::vector* mEntryDl) { for (size_t i = 0, vtxGroup = 0; i < numChar; i++) { // A maximum of 64 Vtx can be loaded at once by gSPVertex, or basically 16 characters // handle loading groups of 16 chars at a time until there are no more left to load. - // By this point 4 vertices have already been loaded for the preceding icon. + // By this point 8 vertices have already been loaded for the preceding icon and cursor. if (i % 16 == 0) { size_t numVtxToLoad = std::min(numChar - i, 16) * 4; - mEntryDl->push_back(gsSPVertex(&vtx[4 + (vtxGroup * 16 * 4)], numVtxToLoad, 0)); + mEntryDl->push_back(gsSPVertex(&vtx[8 + (vtxGroup * 16 * 4)], numVtxToLoad, 0)); vtxGroup++; } @@ -111,22 +129,29 @@ void KaleidoEntryIcon::Draw(PlayState* play, std::vector* mEntryDl) { Kaleido::Kaleido() { const auto ctx = Rando::Context::GetInstance(); - int yOffset = 2; + int yOffset = 0; mEntries.push_back(std::make_shared( gRupeeCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 0xC8, 0xFF, 0x64, 255 }, - FlagType::FLAG_RANDOMIZER_INF, static_cast(RAND_INF_GREG_FOUND), 0, yOffset, "Greg")); - yOffset += 18; + FlagType::FLAG_RANDOMIZER_INF, static_cast(RAND_INF_GREG_FOUND), "Greg")); + if (ctx->GetOption(RSK_SHUFFLE_FISHING_POLE)) { + mEntries.push_back(std::make_shared( + gItemIconFishingPoleTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, + FlagType::FLAG_RANDOMIZER_INF, static_cast(RAND_INF_FISHING_POLE_FOUND), "Fishing Pole")); + } if (ctx->GetOption(RSK_TRIFORCE_HUNT)) { mEntries.push_back(std::make_shared( - gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, 0, yOffset, + gTriforcePieceTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, reinterpret_cast(&gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected), ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_REQUIRED).Get() + 1, ctx->GetOption(RSK_TRIFORCE_HUNT_PIECES_TOTAL).Get() + 1)); - yOffset += 18; + } + if (ctx->GetOption(RSK_SKELETON_KEY)) { + mEntries.push_back(std::make_shared( + gSmallKeyCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 255, 255, 255, 255 }, + FlagType::FLAG_RANDOMIZER_INF, static_cast(RAND_INF_HAS_SKELETON_KEY), "Skeleton Key")); } if (ctx->GetOption(RSK_SHUFFLE_OCARINA_BUTTONS)) { - mEntries.push_back(std::make_shared(0, yOffset)); - yOffset += 18; + mEntries.push_back(std::make_shared()); } if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).IsNot(RO_BOSS_SOULS_OFF)) { static const char* bossSoulNames[] = { @@ -136,15 +161,22 @@ Kaleido::Kaleido() { for (int i = RAND_INF_GOHMA_SOUL; i < RAND_INF_GANON_SOUL; i++) { mEntries.push_back(std::make_shared( gBossSoulTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, - FlagType::FLAG_RANDOMIZER_INF, i, 0, yOffset, bossSoulNames[i - RAND_INF_GOHMA_SOUL])); - yOffset += 18; + FlagType::FLAG_RANDOMIZER_INF, i, bossSoulNames[i - RAND_INF_GOHMA_SOUL])); } } if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS).Is(RO_BOSS_SOULS_ON_PLUS_GANON)) { mEntries.push_back(std::make_shared( gBossSoulTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, Color_RGBA8{ 255, 255, 255, 255 }, - FlagType::FLAG_RANDOMIZER_INF, RAND_INF_GANON_SOUL, 0, yOffset, "Ganon's Soul")); - yOffset += 18; + FlagType::FLAG_RANDOMIZER_INF, RAND_INF_GANON_SOUL, "Ganon's Soul")); + } + if (ctx->GetOption(RSK_LOCK_OVERWORLD_DOORS)) { + int rg = RG_GUARD_HOUSE_KEY; + for (int i = RAND_INF_GUARD_HOUSE_KEY_OBTAINED; i <= RAND_INF_FISHING_HOLE_KEY_OBTAINED; i += 2, rg++) { + mEntries.push_back(std::make_shared( + gSmallKeyCounterIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, Color_RGBA8{ 255, 255, 255, 255 }, + FlagType::FLAG_RANDOMIZER_INF, i, + Rando::StaticData::RetrieveItem(static_cast(rg)).GetName().english)); + } } } @@ -162,6 +194,7 @@ void Kaleido::Draw(PlayState* play) { mEntryDl.clear(); OPEN_DISPS(play->state.gfxCtx); mEntryDl.push_back(gsDPPipeSync()); + Gfx_SetupDL_39Opa(play->state.gfxCtx); Gfx_SetupDL_42Opa(play->state.gfxCtx); mEntryDl.push_back(gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM)); @@ -179,13 +212,23 @@ void Kaleido::Draw(PlayState* play) { if (!((pauseCtx->state != 6) || ((pauseCtx->stickRelX == 0) && (pauseCtx->stickRelY == 0)))) { if (pauseCtx->cursorSpecialPos == 0) { if ((pauseCtx->stickRelY > 30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DUP))) { - if (mTopIndex > 0) { - mTopIndex--; + if (mCursorPos > 0) { + mCursorPos--; + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + if (mCursorPos < mTopIndex) { + mTopIndex = mCursorPos; shouldScroll = true; } } else if ((pauseCtx->stickRelY < -30) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DDOWN))) { - if (mTopIndex + mNumVisible < mEntries.size()) { - mTopIndex++; + if (mCursorPos < mEntries.size() - 1) { + mCursorPos++; + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + if (mCursorPos >= mTopIndex + mNumVisible && mTopIndex + mNumVisible < mEntries.size()) { + mTopIndex = mCursorPos - mNumVisible + 1; shouldScroll = true; } } @@ -213,16 +256,14 @@ void Kaleido::Draw(PlayState* play) { pauseCtx->cursorSpecialPos = 0; } } - int yOffset = 2; + int yOffset = 1; for (int i = mTopIndex; i < (mTopIndex + mNumVisible) && i < mEntries.size(); i++) { auto& entry = mEntries[i]; - if (shouldScroll) { - entry->SetYOffset(yOffset); - yOffset += 18; - Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, - &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); - } + entry->SetYOffset(yOffset); + yOffset += 9; Matrix_Push(); + entry->SetSelected((i == mCursorPos) && !(pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT || + pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT)); entry->Draw(play, &mEntryDl); Matrix_Pop(); } @@ -252,9 +293,9 @@ extern "C" void RandoKaleido_UpdateMiscCollectibles(int16_t inDungeonScene) { KaleidoEntryIconFlag::KaleidoEntryIconFlag(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth, int iconHeight, Color_RGBA8 iconColor, FlagType flagType, int flag, - int16_t x, int16_t y, std::string name) - : mFlagType(flagType), mFlag(flag), KaleidoEntryIcon(iconResourceName, iconFormat, iconSize, iconWidth, iconHeight, - iconColor, x, y, std::move(name)) { + std::string name) + : mFlagType(flagType), mFlag(flag), + KaleidoEntryIcon(iconResourceName, iconFormat, iconSize, iconWidth, iconHeight, iconColor, std::move(name)) { BuildVertices(); } @@ -264,9 +305,9 @@ void KaleidoEntryIconFlag::Update(PlayState* play) { KaleidoEntryIconCountRequired::KaleidoEntryIconCountRequired(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth, int iconHeight, Color_RGBA8 iconColor, - int16_t x, int16_t y, int* watch, int required, int total) + int* watch, int required, int total) : mWatch(watch), mRequired(required), mTotal(total), - KaleidoEntryIcon(iconResourceName, iconFormat, iconSize, iconWidth, iconHeight, iconColor, x, y) { + KaleidoEntryIcon(iconResourceName, iconFormat, iconSize, iconWidth, iconHeight, iconColor) { mCount = *mWatch; BuildText(); BuildVertices(); @@ -287,31 +328,54 @@ void KaleidoEntryIconCountRequired::BuildText() { void KaleidoEntryIcon::BuildVertices() { int offsetY = 0; int offsetX = 0; - // 4 vertices per character, plus one for the preceding icon. - Vtx* vertices = (Vtx*)calloc(sizeof(Vtx[4]), mText.length() + 1); + // 4 vertices per character, plus one for the preceding icon, plus one for the cursor. + Vtx* vertices = (Vtx*)calloc(sizeof(Vtx[4]), mText.length() + 2); + // Vertex for the cursor. + Ship_CreateQuadVertexGroup(vertices, offsetX, offsetY, 16, 24, 0); + offsetX += 18; // Vertex for the preceding icon. - Ship_CreateQuadVertexGroup(vertices, offsetX, offsetY, mIconWidth, mIconHeight, 0); + Ship_CreateQuadVertexGroup(&vertices[4], offsetX, offsetY, mIconWidth, mIconHeight, 0); offsetX += 18; for (size_t i = 0; i < mText.length(); i++) { int charWidth = static_cast(Ship_GetCharFontWidth(mText[i])); - Ship_CreateQuadVertexGroup(&(vertices)[(i + 1) * 4], offsetX, offsetY, charWidth, 16, 0); + Ship_CreateQuadVertexGroup(&(vertices)[((i + 1) * 4) + 4], offsetX, offsetY, charWidth, 16, 0); offsetX += charWidth; } offsetY += FONT_CHAR_TEX_HEIGHT; - mWidth = static_cast(offsetX); - mHeight = static_cast(offsetY); + // mWidth = static_cast(offsetX); + // mHeight = static_cast(offsetY); + + vertices[1].v.ob[0] = 15; // top-right x + vertices[2].v.ob[1] = 15; // bottom-left y + vertices[3].v.ob[0] = 15; // bottom-right x + vertices[3].v.ob[1] = 15; // bottom-right y + vertices[5].v.ob[0] = 32; // top-right x + vertices[6].v.ob[1] = 16; // bottom-left-y + vertices[7].v.ob[0] = 32; // bottom-right x + vertices[7].v.ob[1] = 16; // bottom-right y + + for (size_t i = 0; i < mText.length() + 2; i++) { + size_t j = i * 4; + vertices[j].v.ob[0] = vertices[j].v.ob[0] / 2; + vertices[j].v.ob[1] = vertices[j].v.ob[1] / 2; + vertices[j + 1].v.ob[0] = vertices[j + 1].v.ob[0] / 2; + vertices[j + 1].v.ob[1] = vertices[j + 1].v.ob[1] / 2; + vertices[j + 2].v.ob[0] = vertices[j + 2].v.ob[0] / 2; + vertices[j + 2].v.ob[1] = vertices[j + 2].v.ob[1] / 2; + vertices[j + 3].v.ob[0] = vertices[j + 3].v.ob[0] / 2; + vertices[j + 3].v.ob[1] = vertices[j + 3].v.ob[1] / 2; + } + + mWidth = static_cast(offsetX / 2); + mHeight = static_cast(8); - vertices[1].v.ob[0] = 16; - vertices[2].v.ob[1] = 16; - vertices[3].v.ob[0] = 16; - vertices[3].v.ob[1] = 16; vtx = vertices; } KaleidoEntryIcon::KaleidoEntryIcon(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth, - int iconHeight, Color_RGBA8 iconColor, int16_t x, int16_t y, std::string text) + int iconHeight, Color_RGBA8 iconColor, std::string text) : mIconResourceName(iconResourceName), mIconFormat(iconFormat), mIconSize(iconSize), mIconWidth(iconWidth), - mIconHeight(iconHeight), mIconColor(iconColor), KaleidoEntry(x, y, std::move(text)) { + mIconHeight(iconHeight), mIconColor(iconColor), KaleidoEntry(std::move(text)) { } void KaleidoEntryIcon::RebuildVertices() { @@ -329,9 +393,9 @@ void KaleidoEntryIconCountRequired::Update(PlayState* play) { } } -KaleidoEntryOcarinaButtons::KaleidoEntryOcarinaButtons(int16_t x, int16_t y) +KaleidoEntryOcarinaButtons::KaleidoEntryOcarinaButtons() : KaleidoEntryIcon(gItemIconOcarinaOfTimeTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, - Color_RGBA8{ 255, 255, 255, 255 }, x, y, "\x9F\xA5\xA6\xA7\xA8") { + Color_RGBA8{ 255, 255, 255, 255 }, "\x9F\xA5\xA6\xA7\xA8") { CalculateColors(); BuildVertices(); } @@ -405,13 +469,24 @@ void KaleidoEntryOcarinaButtons::Draw(PlayState* play, std::vector* mEntryD mEntryDl->push_back(gsSPMatrix(Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW)); + // cursor (if selected) + if (mSelected) { + mEntryDl->push_back(gsDPSetPrimColor(0, 0, 255, 255, 255, 255)); + mEntryDl->push_back(gsSPVertex(vtx, 4, 0)); + Gfx cursorIconTex[] = { gsDPLoadTextureBlock(gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD) }; + mEntryDl->insert(mEntryDl->end(), std::begin(cursorIconTex), std::end(cursorIconTex)); + mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0)); + } + // icon if (!mAchieved) { mEntryDl->push_back(gsDPSetGrayscaleColor(109, 109, 109, 255)); mEntryDl->push_back(gsSPGrayscale(true)); } mEntryDl->push_back(gsDPSetPrimColor(0, 0, mIconColor.r, mIconColor.g, mIconColor.b, mIconColor.a)); - mEntryDl->push_back(gsSPVertex(vtx, 4, 0)); + mEntryDl->push_back(gsSPVertex(&vtx[4], 4, 0)); LoadIconTex(mEntryDl); mEntryDl->push_back(gsSP1Quadrangle(0, 2, 3, 1, 0)); mEntryDl->push_back(gsSPGrayscale(false)); @@ -426,7 +501,7 @@ void KaleidoEntryOcarinaButtons::Draw(PlayState* play, std::vector* mEntryD // By this point 4 vertices have already been loaded for the preceding icon. if (i % 16 == 0) { size_t numVtxToLoad = std::min(numChar - i, 16) * 4; - mEntryDl->push_back(gsSPVertex(&vtx[4 + (vtxGroup * 16 * 4)], numVtxToLoad, 0)); + mEntryDl->push_back(gsSPVertex(&vtx[8 + (vtxGroup * 16 * 4)], numVtxToLoad, 0)); vtxGroup++; } diff --git a/soh/soh/Enhancements/kaleido.h b/soh/soh/Enhancements/kaleido.h index 7776afb663..605bae226d 100644 --- a/soh/soh/Enhancements/kaleido.h +++ b/soh/soh/Enhancements/kaleido.h @@ -26,18 +26,20 @@ class KaleidoEntry { * @param text the initial value of the line of text. Can be omitted for an * empty string. */ - KaleidoEntry(int16_t x, int16_t y, std::string text = ""); + KaleidoEntry(std::string text = ""); virtual void Draw(PlayState* play, std::vector* mEntryDl) = 0; virtual void Update(PlayState* play) = 0; void SetYOffset(int yOffset); + void SetSelected(bool val); protected: - int16_t mX; - int16_t mY; + int16_t mX = 0; + int16_t mY = 0; int16_t mHeight; int16_t mWidth; Vtx* vtx; std::string mText; + bool mSelected = false; bool mAchieved = false; }; @@ -59,7 +61,7 @@ class KaleidoEntryIcon : public KaleidoEntry { * @param text text to draw to the right of the icon. */ KaleidoEntryIcon(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth, int iconHeight, - Color_RGBA8 iconColor, int16_t x, int16_t y, std::string text = ""); + Color_RGBA8 iconColor, std::string text = ""); void Draw(PlayState* play, std::vector* mEntryDl) override; void RebuildVertices(); @@ -95,8 +97,7 @@ class KaleidoEntryIconFlag : public KaleidoEntryIcon { * @param mName name to draw to the right of the icon. Leave blank to omit. */ KaleidoEntryIconFlag(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth, int iconHeight, - Color_RGBA8 iconColor, FlagType flagType, int flag, int16_t x, int16_t y, - std::string name = ""); + Color_RGBA8 iconColor, FlagType flagType, int flag, std::string name = ""); void Update(PlayState* play) override; private: @@ -128,8 +129,7 @@ class KaleidoEntryIconCountRequired : public KaleidoEntryIcon { * @param total The amount of this collectible available in the seed. Set to 0 to not render. */ KaleidoEntryIconCountRequired(const char* iconResourceName, int iconFormat, int iconSize, int iconWidth, - int iconHeight, Color_RGBA8 iconColor, int16_t x, int16_t y, int* watch, - int required = 0, int total = 0); + int iconHeight, Color_RGBA8 iconColor, int* watch, int required = 0, int total = 0); void Update(PlayState* play) override; private: @@ -143,7 +143,7 @@ class KaleidoEntryIconCountRequired : public KaleidoEntryIcon { class KaleidoEntryOcarinaButtons : public KaleidoEntryIcon { public: - KaleidoEntryOcarinaButtons(int16_t x, int16_t y); + KaleidoEntryOcarinaButtons(); void Update(PlayState* play) override; void Draw(PlayState* play, std::vector* mEntryDl) override; @@ -164,7 +164,8 @@ class Kaleido { std::vector> mEntries; std::vector mEntryDl; int mTopIndex = 0; - int mNumVisible = 7; + int mCursorPos = 0; + int mNumVisible = 14; }; } // namespace Rando diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index c300171833..bea55f1850 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -27,8 +27,6 @@ #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_link_boy/object_link_boy.h" -#include "objects/object_link_child/object_link_child.h" #include "soh_assets.h" #include "kaleido.h" @@ -203,102 +201,6 @@ void RegisterHyperBosses() { [](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 UpdatePatchHand() { - if ((CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) && LINK_IS_CHILD) { - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1", 92, - gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2", 93, gsSPEndDisplayList()); - ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1", 84, - gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL)); - ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2", 85, - gsSPEndDisplayList()); - ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1", 51, - gsSPDisplayListOTRFilePath(gLinkChildRightHandClosedNearDL)); - ResourceMgr_PatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2", 52, gsSPEndDisplayList()); - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1", 104, - gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2", 105, - gsSPEndDisplayList()); - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1", 79, - gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2", 80, gsSPEndDisplayList()); - ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1", 76, - gsSPDisplayListOTRFilePath(gLinkChildLeftFistNearDL)); - ResourceMgr_PatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2", 77, - gsSPEndDisplayList()); - - } else { - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer1"); - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "childHammer2"); - ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot1"); - ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingHookshotNearDL, "childHookshot2"); - ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow1"); - ResourceMgr_UnpatchGfxByName(gLinkAdultRightHandHoldingBowNearDL, "childBow2"); - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword1"); - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingMasterSwordNearDL, "childMasterSword2"); - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword1"); - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingBgsNearDL, "childBiggoronSword2"); - ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife1"); - ResourceMgr_UnpatchGfxByName(gLinkAdultHandHoldingBrokenGiantsKnifeDL, "childBrokenGiantsKnife2"); - } - if ((CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) && LINK_IS_ADULT) { - ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword", 13, - gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); - ResourceMgr_PatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot", 13, - gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL)); - ResourceMgr_PatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang", 50, - gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); - ResourceMgr_PatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield", 49, - gsSPDisplayListOTRFilePath(gLinkAdultRightHandClosedNearDL)); - } else { - ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndKokiriSwordNearDL, "adultKokiriSword"); - ResourceMgr_UnpatchGfxByName(gLinkChildRightHandHoldingSlingshotNearDL, "adultSlingshot"); - ResourceMgr_UnpatchGfxByName(gLinkChildLeftFistAndBoomerangNearDL, "adultBoomerang"); - ResourceMgr_UnpatchGfxByName(gLinkChildRightFistAndDekuShieldNearDL, "adultDekuShield"); - } - if (CVarGetInteger("gEnhancements.FixHammerHand", 0) && LINK_IS_ADULT) { - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand1", 92, - gsSPDisplayListOTRFilePath(gLinkAdultLeftHandClosedNearDL)); - ResourceMgr_PatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand2", 93, gsSPEndDisplayList()); - } else { - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand1"); - ResourceMgr_UnpatchGfxByName(gLinkAdultLeftHandHoldingHammerNearDL, "hammerHand2"); - } -} - -void RegisterPatchHandHandler() { - GameInteractor::Instance->RegisterGameHook( - [](int32_t sceneNum) { UpdatePatchHand(); }); -} - // this map is used for enemies that can be uniquely identified by their id // and that are always counted // enemies that can't be uniquely identified by their id @@ -489,28 +391,6 @@ void RegisterEnemyDefeatCounts() { }); } -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 @@ -652,10 +532,7 @@ void InitMods() { RegisterTTS(); RegisterOcarinaTimeTravel(); RegisterHyperBosses(); - UpdateHyperEnemiesState(); RegisterEnemyDefeatCounts(); RegisterRandomizedEnemySizes(); - RegisterPatchHandHandler(); - RegisterHurtContainerModeHandler(); RandoKaleido_RegisterHooks(); } diff --git a/soh/soh/Enhancements/mods.h b/soh/soh/Enhancements/mods.h index 24710d5b20..9a0c6794bf 100644 --- a/soh/soh/Enhancements/mods.h +++ b/soh/soh/Enhancements/mods.h @@ -7,15 +7,8 @@ extern "C" { #endif -void DirtPathFix_UpdateZFightingMode(int32_t sceneNum); -void UpdateMirrorModeState(int32_t sceneNum); -void UpdateHurtContainerModeState(bool newState); -void UpdateToTMedallions(); -void UpdatePermanentHeartLossState(); -void UpdateHyperEnemiesState(); void UpdateHyperBossesState(); void InitMods(); -void UpdatePatchHand(); void SwitchAge(); #ifdef __cplusplus diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index 16205424e4..751f3b07bd 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -486,11 +486,11 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) { if (receivedItemEntry.modIndex == MOD_NONE && (receivedItemEntry.itemId == ITEM_HEART_PIECE || receivedItemEntry.itemId == ITEM_HEART_PIECE_2 || receivedItemEntry.itemId == ITEM_HEART_CONTAINER)) { - gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts + gSaveContext.healthAccumulator = MAX_HEALTH; // Refill 20 hearts if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; - gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; + gSaveContext.healthCapacity += FULL_HEART_HEALTH; + gSaveContext.health += FULL_HEART_HEALTH; } } @@ -1723,12 +1723,6 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l } break; } - case VB_RENDER_KEY_COUNTER: { - if (Flags_GetRandomizerInf(RAND_INF_HAS_SKELETON_KEY)) { - *should = false; - } - break; - } case VB_RENDER_RUPEE_COUNTER: { if (!Flags_GetRandomizerInf(RAND_INF_HAS_WALLET) || Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MONEY)) { *should = false; diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index c21a7a33eb..8ec64078a9 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -25,6 +25,7 @@ void RegionTable_Init_DodongosCavern() { areaTable[RR_DODONGOS_CAVERN_LOBBY] = Region("Dodongos Cavern Lobby", SCENE_DODONGOS_CAVERN, { //Events EventAccess(LOGIC_GOSSIP_STONE_FAIRY, []{return (Here(RR_DODONGOS_CAVERN_LOBBY, []{return logic->CanBreakMudWalls();}) || logic->HasItem(RG_GORONS_BRACELET)) && logic->CallGossipFairy();}), + EventAccess(LOGIC_DC_EYES_LIT, []{return ctx->GetTrickOption(RT_DC_EYES_CHU) && logic->CanUse(RG_BOMBCHU_5);}), }, { //Locations LOCATION(RC_DODONGOS_CAVERN_MAP_CHEST, logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);), @@ -282,7 +283,10 @@ void RegionTable_Init_DodongosCavern() { Entrance(RR_DODONGOS_CAVERN_MQ_LOBBY, []{return Here(RR_DODONGOS_CAVERN_MQ_BEGINNING, []{return logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET);});}), }); - areaTable[RR_DODONGOS_CAVERN_MQ_LOBBY] = Region("Dodongos Cavern MQ Lobby", SCENE_DODONGOS_CAVERN, {}, { + areaTable[RR_DODONGOS_CAVERN_MQ_LOBBY] = Region("Dodongos Cavern MQ Lobby", SCENE_DODONGOS_CAVERN, { + //Events + EventAccess(LOGIC_DC_EYES_LIT, []{return ctx->GetTrickOption(RT_DC_EYES_CHU) && logic->CanUse(RG_BOMBCHU_5);}), + }, { //Locations LOCATION(RC_DODONGOS_CAVERN_MQ_MAP_CHEST, logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET)), LOCATION(RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_REAR, logic->CanStunDeku()), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp index 6d75dffb66..04b9e5474a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp @@ -20,19 +20,29 @@ void RegionTable_Init_GerudoTrainingGround() { //Locations LOCATION(RC_GERUDO_TRAINING_GROUND_LOBBY_LEFT_CHEST, logic->CanHitEyeTargets()), LOCATION(RC_GERUDO_TRAINING_GROUND_LOBBY_RIGHT_CHEST, logic->CanHitEyeTargets()), - LOCATION(RC_GERUDO_TRAINING_GROUND_STALFOS_CHEST, logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true)), - LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_CHEST, logic->CanKillEnemy(RE_BEAMOS) && logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true)), LOCATION(RC_GERUDO_TRAINING_GROUND_ENTRANCE_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_SOUTH_HEART, true), - LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_EAST_HEART, true), }, { //Exits Entrance(RR_GERUDO_TRAINING_GROUND_ENTRYWAY, []{return true;}), - Entrance(RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM, []{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true) && (logic->CanUse(RG_HOOKSHOT) || ctx->GetTrickOption(RT_GTG_WITHOUT_HOOKSHOT));}), - Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_LOBBY, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true);});}), + Entrance(RR_GERUDO_TRAINING_GROUND_SAND_ROOM, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_DINALFOS, []{return true;}), Entrance(RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE, []{return true;}), }); + areaTable[RR_GERUDO_TRAINING_GROUND_SAND_ROOM] = Region("Gerudo Training Ground Sand Room", SCENE_GERUDO_TRAINING_GROUND, {}, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_STALFOS_CHEST, logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true)), + }, { + //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_LOBBY, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_BOULDER_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_SAND_ROOM, []{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true);});}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_BOULDER_ROOM] = Region("Gerudo Training Ground Boulder Room", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + Entrance(RR_GERUDO_TRAINING_GROUND_SAND_ROOM, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_BOULDER_ROOM, []{return logic->CanUse(logic->IsAdult ? RG_HOOKSHOT : RG_LONGSHOT) || ctx->GetTrickOption(RT_GTG_WITHOUT_HOOKSHOT);});}), + }); + areaTable[RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE] = Region("Gerudo Training Ground Central Maze", SCENE_GERUDO_TRAINING_GROUND, {}, { //Locations LOCATION(RC_GERUDO_TRAINING_GROUND_HIDDEN_CEILING_CHEST, logic->SmallKeys(SCENE_GERUDO_TRAINING_GROUND, 3) && (ctx->GetTrickOption(RT_LENS_GTG) || logic->CanUse(RG_LENS_OF_TRUTH))), @@ -42,6 +52,7 @@ void RegionTable_Init_GerudoTrainingGround() { LOCATION(RC_GERUDO_TRAINING_GROUND_MAZE_PATH_FINAL_CHEST, logic->SmallKeys(SCENE_GERUDO_TRAINING_GROUND, 9)), }, { //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_LOBBY, []{return true;}), Entrance(RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE_RIGHT, []{return logic->SmallKeys(SCENE_GERUDO_TRAINING_GROUND, 9);}), }); @@ -52,17 +63,74 @@ void RegionTable_Init_GerudoTrainingGround() { LOCATION(RC_GERUDO_TRAINING_GROUND_FREESTANDING_KEY, true), }, { //Exits - Entrance(RR_GERUDO_TRAINING_GROUND_HAMMER_ROOM, []{return logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM_UPPER_LEDGE, []{return logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE, []{return logic->SmallKeys(SCENE_GERUDO_TRAINING_GROUND, 9);}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_LAVA_ROOM] = Region("Gerudo Training Ground Lava Room", SCENE_GERUDO_TRAINING_GROUND, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM] = Region("Gerudo Training Ground Heavy Block Room", SCENE_GERUDO_TRAINING_GROUND, { + //Events + EventAccess(LOGIC_GTG_PUSHED_HEAVY_BLOCK, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), + }, { //Locations - LOCATION(RC_GERUDO_TRAINING_GROUND_UNDERWATER_SILVER_RUPEE_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 24), + LOCATION(RC_GERUDO_TRAINING_GROUND_BEFORE_HEAVY_BLOCK_CHEST, logic->CanKillEnemy(RE_WOLFOS, ED_CLOSE, true, 4, true)), }, { //Exits - Entrance(RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE_RIGHT, []{return logic->CanUse(RG_SONG_OF_TIME) || logic->IsChild;}), - Entrance(RR_GERUDO_TRAINING_GROUND_HAMMER_ROOM, []{return logic->CanUse(RG_LONGSHOT) || (logic->CanUse(RG_HOVER_BOOTS) && logic->CanUse(RG_HOOKSHOT));}), + Entrance(RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM_UPPER, []{return (ctx->GetTrickOption(RT_LENS_GTG) || logic->CanUse(RG_LENS_OF_TRUTH)) && (logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_GTG_FAKE_WALL) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)) || (logic->IsAdult && logic->CanGroundJump()));}), + Entrance(RR_GERUDO_TRAINING_GROUND_BEHIND_HEAVY_BLOCK, []{return logic->Get(LOGIC_GTG_PUSHED_HEAVY_BLOCK);}), + Entrance(RR_GERUDO_TRAINING_GROUND_BOULDER_ROOM, []{return true;}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM_UPPER] = Region("Gerudo Training Ground Heavy Block Room Upper", SCENE_GERUDO_TRAINING_GROUND, { + //Events + EventAccess(LOGIC_GTG_UNLOCKED_DOOR_BEHIND_HEAVY_BLOCK, []{return true;}), + }, {}, { + //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM, []{return ctx->GetTrickOption(RT_LENS_GTG) || logic->CanUse(RG_LENS_OF_TRUTH);}), + Entrance(RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER, []{return true;}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_BEHIND_HEAVY_BLOCK] = Region("Gerudo Training Ground Behind Heavy Block", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM, []{return logic->Get(LOGIC_GTG_PUSHED_HEAVY_BLOCK);}), + Entrance(RR_GERUDO_TRAINING_GROUND_LIKE_LIKE_ROOM, []{return logic->Get(LOGIC_GTG_UNLOCKED_DOOR_BEHIND_HEAVY_BLOCK);}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_LIKE_LIKE_ROOM] = Region("Gerudo Training Ground Like Like Room", SCENE_GERUDO_TRAINING_GROUND, {}, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_FIRST_CHEST, logic->CanKillEnemy(RE_LIKE_LIKE)), + LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_SECOND_CHEST, logic->CanKillEnemy(RE_LIKE_LIKE)), + LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_THIRD_CHEST, (ctx->GetTrickOption(RT_LENS_GTG) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanPassEnemy(RE_LIKE_LIKE)), + LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_FOURTH_CHEST, true), + }, { + //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_BEHIND_HEAVY_BLOCK, []{return true;}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER] = Region("Gerudo Training Ground Eye Statue Upper", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM_UPPER, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_ABOVE_MAZE, []{return logic->Get(LOGIC_GTG_CLEARED_EYE_STATUE);}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_ABOVE_MAZE] = Region("Gerudo Training Ground Above Eye", SCENE_GERUDO_TRAINING_GROUND, {}, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_NEAR_SCARECROW_CHEST, true), + }, { + //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER, []{return true;}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER] = Region("Gerudo Training Ground Eye Statue Lower", SCENE_GERUDO_TRAINING_GROUND, { + //Events + EventAccess(LOGIC_GTG_CLEARED_EYE_STATUE, []{return logic->CanUse(RG_FAIRY_BOW);}), + }, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_EYE_STATUE_CHEST, logic->Get(LOGIC_GTG_CLEARED_EYE_STATUE)), + }, { + //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_HAMMER_ROOM, []{return true;}), }); areaTable[RR_GERUDO_TRAINING_GROUND_HAMMER_ROOM] = Region("Gerudo Training Ground Hammer Room", SCENE_GERUDO_TRAINING_GROUND, {}, { @@ -75,39 +143,43 @@ void RegionTable_Init_GerudoTrainingGround() { Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, []{return true;}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER] = Region("Gerudo Training Ground Eye Statue Lower", SCENE_GERUDO_TRAINING_GROUND, {}, { - //Locations - LOCATION(RC_GERUDO_TRAINING_GROUND_EYE_STATUE_CHEST, logic->CanUse(RG_FAIRY_BOW)), - }, { + areaTable[RR_GERUDO_TRAINING_GROUND_LAVA_ROOM] = Region("Gerudo Training Ground Lava Room", SCENE_GERUDO_TRAINING_GROUND, { + EventAccess(LOGIC_GTG_PLATFORM_SILVER_RUPEES, []{return logic->CanUse(RG_HOOKSHOT) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME));}), + }, {}, { //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_DINALFOS, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE_RIGHT, []{return logic->CanUse(RG_SONG_OF_TIME) || logic->IsChild;}), + // possible to make across with adult's rolling jump, only requiring hookshot + Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM_UPPER_LEDGE, []{return logic->CanUse(RG_LONGSHOT) || (logic->CanUse(RG_HOOKSHOT) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME)));}), + Entrance(RR_GERUDO_TRAINING_GROUND_UNDERWATER, []{return logic->Get(LOGIC_GTG_PLATFORM_SILVER_RUPEES);}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_LAVA_ROOM_UPPER_LEDGE] = Region("Gerudo Training Ground Lava Room", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + //Exits + // possible to make across with as adult's rolling jump, no hookshot necessary + Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME);}), Entrance(RR_GERUDO_TRAINING_GROUND_HAMMER_ROOM, []{return true;}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER] = Region("Gerudo Training Ground Eye Statue Upper", SCENE_GERUDO_TRAINING_GROUND, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_UNDERWATER] = Region("Gerudo Training Ground Underwater", SCENE_GERUDO_TRAINING_GROUND, {}, { //Locations - LOCATION(RC_GERUDO_TRAINING_GROUND_NEAR_SCARECROW_CHEST, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_GERUDO_TRAINING_GROUND_UNDERWATER_SILVER_RUPEE_CHEST, logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_IRON_BOOTS) && logic->HasItem(RG_BRONZE_SCALE) && logic->WaterTimer() >= 24), }, { //Exits - Entrance(RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, []{return true;}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM] = Region("Gerudo Training Ground Heavy Block Room", SCENE_GERUDO_TRAINING_GROUND, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_DINALFOS] = Region("Gerudo Training Dinalfos", SCENE_GERUDO_TRAINING_GROUND, {}, { //Locations - LOCATION(RC_GERUDO_TRAINING_GROUND_BEFORE_HEAVY_BLOCK_CHEST, logic->CanKillEnemy(RE_WOLFOS, ED_CLOSE, true, 4, true)), + LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_CHEST, logic->CanKillEnemy(RE_BEAMOS) && logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true)), + LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_SOUTH_HEART, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_EAST_HEART, true), }, { //Exits - Entrance(RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER, []{return (ctx->GetTrickOption(RT_LENS_GTG) || logic->CanUse(RG_LENS_OF_TRUTH)) && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && (ctx->GetTrickOption(RT_GTG_FAKE_WALL) && logic->CanUse(RG_HOVER_BOOTS)) || logic->CanGroundJump()));}), - Entrance(RR_GERUDO_TRAINING_GROUND_LIKE_LIKE_ROOM, []{return (ctx->GetTrickOption(RT_LENS_GTG) || logic->CanUse(RG_LENS_OF_TRUTH)) && (logic->CanUse(RG_HOOKSHOT) || (logic->IsAdult && (ctx->GetTrickOption(RT_GTG_FAKE_WALL) && logic->CanUse(RG_HOVER_BOOTS)) || logic->CanGroundJump())) && logic->CanUse(RG_SILVER_GAUNTLETS);}), + Entrance(RR_GERUDO_TRAINING_GROUND_LOBBY, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_DINALFOS, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true);});}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_LIKE_LIKE_ROOM] = Region("Gerudo Training Ground Like Like Room", SCENE_GERUDO_TRAINING_GROUND, {}, { - //Locations - LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_FIRST_CHEST, logic->CanJumpslashExceptHammer()), - LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_SECOND_CHEST, logic->CanJumpslashExceptHammer()), - LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_THIRD_CHEST, logic->CanJumpslashExceptHammer()), - LOCATION(RC_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_FOURTH_CHEST, logic->CanJumpslashExceptHammer()), - }, {}); - #pragma endregion #pragma region MQ @@ -116,8 +188,6 @@ void RegionTable_Init_GerudoTrainingGround() { //Locations LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_LOBBY_LEFT_CHEST, true), LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_LOBBY_RIGHT_CHEST, true), - LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_FIRST_CHEST, true), - LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_SECOND_CHEST, true), LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_LOBBY_LEFT_POT_1, logic->CanBreakPots()), LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_LOBBY_LEFT_POT_2, logic->CanBreakPots()), LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_LOBBY_RIGHT_POT_1, logic->CanBreakPots()), @@ -125,19 +195,21 @@ void RegionTable_Init_GerudoTrainingGround() { }, { //Exits Entrance(RR_GERUDO_TRAINING_GROUND_ENTRYWAY, []{return true;}), - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_HIDDEN_ROOM, []{return ctx->GetTrickOption(RT_LENS_GTG_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}), - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_FIRST_LOCK, []{return logic->SmallKeys(SCENE_GERUDO_TRAINING_GROUND, 1);}), - //It's possible to use the torch in RR_GERUDO_TRAINING_GROUND_MQ_MAZE_HIDDEN_ROOM with flame storage to light these + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_BY_LOBBY, []{return true;}), + //It's possible to use the torch in hidden room of maze with flame storage to light these Entrance(RR_GERUDO_TRAINING_GROUND_MQ_SAND_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return logic->HasFireSource();});}), - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_DINOLFOS_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW)) || (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT));});}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_DINOLFOS_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return logic->CanHitEyeTargets();});}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_HIDDEN_ROOM] = Region("Gerudo Training Ground MQ Maze Hidden Room", SCENE_GERUDO_TRAINING_GROUND, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_BY_LOBBY] = Region("Gerudo Training Ground MQ Maze By Lobby", SCENE_GERUDO_TRAINING_GROUND, {}, { //Locations - LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_HIDDEN_CEILING_CHEST, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_FIRST_CHEST, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_MAZE_PATH_SECOND_CHEST, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_HIDDEN_CEILING_CHEST, ctx->GetTrickOption(RT_LENS_GTG_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_FIRST_LOCK, []{return logic->SmallKeys(SCENE_GERUDO_TRAINING_GROUND, 1);}), }); areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAZE_FIRST_LOCK] = Region("Gerudo Training Ground MQ Maze First Lock", SCENE_GERUDO_TRAINING_GROUND, {}, { @@ -166,34 +238,44 @@ void RegionTable_Init_GerudoTrainingGround() { LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_FIRST_IRON_KNUCKLE_CHEST, logic->CanKillEnemy(RE_IRON_KNUCKLE)), }, { //Exits - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return true;}), - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_LEFT_SIDE, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_SAND_ROOM, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_SAND_ROOM, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_LEFT_SIDE] = Region("Gerudo Training Ground MQ Left Side", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM] = Region("Gerudo Training Ground MQ Left Side", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { //Exits Entrance(RR_GERUDO_TRAINING_GROUND_MQ_SAND_ROOM, []{return true;}), - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_LEFT_SIDE, []{return logic->CanUse(RG_LONGSHOT) || ctx->GetTrickOption(RT_GTG_MQ_WITHOUT_HOOKSHOT) || (ctx->GetTrickOption(RT_GTG_MQ_WITH_HOOKSHOT) && logic->IsAdult && logic->CanJumpslash() && logic->CanUse(RG_HOOKSHOT));});}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM, []{return logic->CanUse(RG_LONGSHOT) || ctx->GetTrickOption(RT_GTG_MQ_WITHOUT_HOOKSHOT) || (ctx->GetTrickOption(RT_GTG_MQ_WITH_HOOKSHOT) && logic->IsAdult && logic->CanJumpslash() && logic->CanUse(RG_HOOKSHOT));});}), }); areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM] = Region("Gerudo Training Ground MQ Stalfos Room", SCENE_GERUDO_TRAINING_GROUND, { //Events - EventAccess(LOGIC_BLUE_FIRE_ACCESS, []{return true;}), + EventAccess(LOGIC_BLUE_FIRE_ACCESS, []{return true;}), + EventAccess(LOGIC_GTG_UNLOCKED_DOOR_BEHIND_HEAVY_BLOCK, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true);});}), + EventAccess(LOGIC_GTG_PUSHED_HEAVY_BLOCK, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanAvoidEnemy(RE_STALFOS, true, 2);}), }, { //Locations //implies logic->CanKillEnemy(RE_BIG_SKULLTULA) LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BEFORE_HEAVY_BLOCK_CHEST, logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true)), }, { //Exits - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true);}) && logic->CanUse(RG_SILVER_GAUNTLETS);}), - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE, []{return logic->IsAdult && Here(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true);}) && (ctx->GetTrickOption(RT_LENS_GTG_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->BlueFire() && logic->IsAdult && (logic->CanUse(RG_SONG_OF_TIME) || (ctx->GetTrickOption(RT_GTG_FAKE_WALL) && logic->CanUse(RG_HOVER_BOOTS)) || logic->CanGroundJump());}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK, []{return logic->Get(LOGIC_GTG_PUSHED_HEAVY_BLOCK);}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE, []{return logic->IsAdult && Here(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, []{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true);}) && (ctx->GetTrickOption(RT_LENS_GTG_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->BlueFire() && (logic->CanUse(RG_SONG_OF_TIME) || (ctx->GetTrickOption(RT_GTG_FAKE_WALL) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)) || (logic->IsAdult && logic->CanGroundJump()));}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK] = Region("Gerudo Training Ground MQ Behind Block", SCENE_GERUDO_TRAINING_GROUND, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK] = Region("Gerudo Training Ground MQ Behind Block", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, []{return logic->Get(LOGIC_GTG_PUSHED_HEAVY_BLOCK);}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_ROOM_BEHIND_BLOCK, []{return logic->Get(LOGIC_GTG_UNLOCKED_DOOR_BEHIND_HEAVY_BLOCK);}), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_ROOM_BEHIND_BLOCK] = Region("Gerudo Training Ground MQ Room Behind Block", SCENE_GERUDO_TRAINING_GROUND, {}, { //Locations //implies logic->CanKillEnemy(RE_SPIKE) LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_HEAVY_BLOCK_CHEST, logic->CanKillEnemy(RE_FREEZARD)), - }, {}); + }, { + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_ROOM_BEHIND_BLOCK, []{return true;}), + }); areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE] = Region("Gerudo Training Ground MQ Statue Room Ledge", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { //Exits @@ -211,7 +293,7 @@ void RegionTable_Init_GerudoTrainingGround() { Entrance(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE, []{return true;}), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM] = Region("Gerudo Training Ground MQ Statue ROom", SCENE_GERUDO_TRAINING_GROUND, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM] = Region("Gerudo Training Ground MQ Statue Room", SCENE_GERUDO_TRAINING_GROUND, {}, { //Locations LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_EYE_STATUE_CHEST, logic->CanUse(RG_FAIRY_BOW)), }, { @@ -241,6 +323,7 @@ void RegionTable_Init_GerudoTrainingGround() { //the fire bubble here is a jerk if you are aiming for the nearest hook platform, you have to aim to the right hand side with hook to dodge it Entrance(RR_GERUDO_TRAINING_GROUND_MQ_PLATFORMS_UNLIT_TORCH, []{return logic->CanUse(RG_LONGSHOT) || (logic->Get(LOGIC_GTG_PLATFORM_SILVER_RUPEES) && logic->CanUse(RG_HOOKSHOT)) || ((logic->CanUse(RG_FIRE_ARROWS) && logic->Get(LOGIC_GTG_PLATFORM_SILVER_RUPEES)) && logic->CanUse(RG_HOVER_BOOTS));}), Entrance(RR_GERUDO_TRAINING_GROUND_MQ_MAZE_RIGHT, []{return logic->Get(LOGIC_GTG_MQ_RIGHT_SIDE_SWITCH) && logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_TORCH_SLUG_ROOM, []{return true;}), }); //this region exists to place silver rupee items on later, normally it's all on fire and cannot be stood on without access from another area @@ -312,16 +395,17 @@ void RegionTable_Init_GerudoTrainingGround() { areaTable[RR_GERUDO_TRAINING_GROUND_MQ_DINOLFOS_ROOM] = Region("Gerudo Training Ground MQ Dinolfos Room", SCENE_GERUDO_TRAINING_GROUND, { //Events - //EventAccess(&WallFairy, []{return WallFairy || (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW));}), + //EventAccess(&WallFairy, []{return logic->IsAdult && logic->CanUse(RG_FAIRY_BOW);}), }, { //Locations //implies logic->CanKillEnemy(RE_LIZALFOS and logic->CanKillEnemy(RE_DODONGO) - //is logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true) && logic->CanKillEnemy(RE_ARMOS, ED_CLOSE, true, 1, true) broken down to exclude sticks, as it take too many to clear the room + //is logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true) && logic->CanKillEnemy(RE_ARMOS, ED_CLOSE, true, 1, true) broken down to exclude sticks, as it takes too many to clear the room //Proper enemy kill room ammo logic is needed to handle this room //some combinations may be impossible without taking damage, keep an eye out for issues here LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_DINOLFOS_CHEST, logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MEGATON_HAMMER) || logic->CanUse(RG_FAIRY_BOW) || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT)))), }, { //Exits + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, []{return true;}), Entrance(RR_GERUDO_TRAINING_GROUND_MQ_TORCH_SIDE_PLATFORMS, []{return Here(RR_GERUDO_TRAINING_GROUND_MQ_DINOLFOS_ROOM, []{return logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MEGATON_HAMMER) || logic->CanUse(RG_FAIRY_BOW) || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT)));});}), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp index 15f75ab8dd..bad0027bb4 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp @@ -13,7 +13,7 @@ void RegionTable_Init_GerudoFortress() { }, { //Locations LOCATION(RC_GF_OUTSKIRTS_NE_CRATE, (logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)) && logic->CanBreakCrates()), - LOCATION(RC_GF_OUTSKIRTS_NW_CRATE, logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)), + LOCATION(RC_GF_OUTSKIRTS_NW_CRATE, (logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)) && logic->CanBreakCrates()), }, { //Exits Entrance(RR_GV_FORTRESS_SIDE, []{return true;}), @@ -260,4 +260,4 @@ void RegionTable_Init_GerudoFortress() { Entrance(RR_GF_NEAR_GROTTO, []{return true;}), }); } -// clang-format on \ No newline at end of file +// clang-format on diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index f29b726a9c..aa5df35a09 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -6074,7 +6074,6 @@ std::map randomizerGetToRandInf = { { RG_MAGIC_INF, RAND_INF_HAS_INFINITE_MAGIC_METER }, { RG_BOMBCHU_INF, RAND_INF_HAS_INFINITE_BOMBCHUS }, { RG_WALLET_INF, RAND_INF_HAS_INFINITE_MONEY }, - { RG_SKELETON_KEY, RAND_INF_HAS_SKELETON_KEY }, { RG_OCARINA_A_BUTTON, RAND_INF_HAS_OCARINA_A }, { RG_OCARINA_C_UP_BUTTON, RAND_INF_HAS_OCARINA_C_UP }, { RG_OCARINA_C_DOWN_BUTTON, RAND_INF_HAS_OCARINA_C_DOWN }, @@ -6280,6 +6279,21 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { } gSaveContext.inventory.dungeonItems[mapIndex] |= bitmask; + return Return_Item_Entry(giEntry, RG_NONE); + } else if (item == RG_SKELETON_KEY) { + Flags_SetRandomizerInf(RAND_INF_HAS_SKELETON_KEY); + // This isn't technically necessary, because keys will no longer be consumed, + // but for the player's sanity we display that they _have_ keys. + gSaveContext.inventory.dungeonKeys[SCENE_FOREST_TEMPLE] = FOREST_TEMPLE_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_FIRE_TEMPLE] = FIRE_TEMPLE_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_WATER_TEMPLE] = WATER_TEMPLE_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_SPIRIT_TEMPLE] = SPIRIT_TEMPLE_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_SHADOW_TEMPLE] = SHADOW_TEMPLE_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_BOTTOM_OF_THE_WELL] = BOTTOM_OF_THE_WELL_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_GERUDO_TRAINING_GROUND] = GERUDO_TRAINING_GROUND_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_THIEVES_HIDEOUT] = GERUDO_FORTRESS_SMALL_KEY_MAX; + gSaveContext.inventory.dungeonKeys[SCENE_INSIDE_GANONS_CASTLE] = GANONS_CASTLE_SMALL_KEY_MAX; + return Return_Item_Entry(giEntry, RG_NONE); } else if (item >= RG_GUARD_HOUSE_KEY && item <= RG_FISHING_HOLE_KEY) { Flags_SetRandomizerInf( @@ -6311,7 +6325,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { case RG_DOUBLE_DEFENSE: gSaveContext.isDoubleDefenseAcquired = true; gSaveContext.inventory.defenseHearts = 20; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; break; case RG_TYCOON_WALLET: Inventory_ChangeUpgrade(UPG_WALLET, 3); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 7f09a6ba39..461a86a2e6 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -331,6 +331,9 @@ typedef enum { LOGIC_GTG_MQ_MAZE_SWITCH, LOGIC_GTG_MQ_RIGHT_SIDE_SWITCH, LOGIC_GTG_PLATFORM_SILVER_RUPEES, + LOGIC_GTG_UNLOCKED_DOOR_BEHIND_HEAVY_BLOCK, + LOGIC_GTG_PUSHED_HEAVY_BLOCK, + LOGIC_GTG_CLEARED_EYE_STATUE, LOGIC_SHADOW_TRIAL_FIRST_CHEST, LOGIC_MAX } LogicVal; @@ -1201,23 +1204,32 @@ typedef enum { RR_ICE_CAVERN_MQ_ABOVE_BEGINNING, RR_GERUDO_TRAINING_GROUND_LOBBY, + RR_GERUDO_TRAINING_GROUND_SAND_ROOM, + RR_GERUDO_TRAINING_GROUND_BOULDER_ROOM, RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE, RR_GERUDO_TRAINING_GROUND_CENTRAL_MAZE_RIGHT, - RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, - RR_GERUDO_TRAINING_GROUND_HAMMER_ROOM, - RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER, - RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER, RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM, + RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM_UPPER, + RR_GERUDO_TRAINING_GROUND_BEHIND_HEAVY_BLOCK, RR_GERUDO_TRAINING_GROUND_LIKE_LIKE_ROOM, + RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER, + RR_GERUDO_TRAINING_GROUND_ABOVE_MAZE, + RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER, + RR_GERUDO_TRAINING_GROUND_HAMMER_ROOM, + RR_GERUDO_TRAINING_GROUND_LAVA_ROOM, + RR_GERUDO_TRAINING_GROUND_LAVA_ROOM_UPPER_LEDGE, + RR_GERUDO_TRAINING_GROUND_UNDERWATER, + RR_GERUDO_TRAINING_GROUND_DINALFOS, RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, - RR_GERUDO_TRAINING_GROUND_MQ_MAZE_HIDDEN_ROOM, + RR_GERUDO_TRAINING_GROUND_MQ_MAZE_BY_LOBBY, RR_GERUDO_TRAINING_GROUND_MQ_MAZE_FIRST_LOCK, RR_GERUDO_TRAINING_GROUND_MQ_MAZE_CENTER, RR_GERUDO_TRAINING_GROUND_MQ_SAND_ROOM, - RR_GERUDO_TRAINING_GROUND_MQ_LEFT_SIDE, + RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM, RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK, + RR_GERUDO_TRAINING_GROUND_MQ_ROOM_BEHIND_BLOCK, RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE, RR_GERUDO_TRAINING_GROUND_MQ_MAGENTA_FIRE_ROOM, RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM, @@ -3856,6 +3868,7 @@ typedef enum { RT_DC_MQ_CHILD_EYES, RT_DC_MQ_ADULT_EYES, RT_DC_DODONGO_CHU, + RT_DC_EYES_CHU, RT_JABU_ALCOVE_JUMP_DIVE, RT_JABU_BOSS_HOVER, RT_JABU_NEAR_BOSS_RANGED, diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 022303aa32..9363ace39b 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -1042,16 +1042,17 @@ void CheckTrackerWindow::DrawElement() { #else float headerHeight = 20.0f; #endif - ImVec2 size = ImGui::GetContentRegionMax(); - size.y -= headerHeight; - if (!ImGui::BeginTable("Check Tracker", 1, 0, size)) { + if (!ImGui::BeginTable("Check Tracker", 1, 0)) { EndFloatWindows(); return; } - ImGui::TableNextRow(0, headerHeight); + ImGui::SetWindowFontScale(CVarGetFloat(CVAR_TRACKER_CHECK("FontSize"), 1.0f)); + + ImGui::TableNextRow(0, 0); ImGui::TableNextColumn(); - if (UIWidgets::CVarCheckbox( + if (CVarGetInteger(CVAR_TRACKER_CHECK("HiddenItemsToggleVisible"), 1) && + UIWidgets::CVarCheckbox( "Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions( { { .tooltip = "When active, items will show hidden checks by default when updated to this state." } }) @@ -1060,7 +1061,7 @@ void CheckTrackerWindow::DrawElement() { showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0); RecalculateAllAreaTotals(); } - if (enableAvailableChecks) { + if (enableAvailableChecks && CVarGetInteger(CVAR_TRACKER_CHECK("AvailableChecksToggleVisible"), 1)) { if (UIWidgets::CVarCheckbox( "Only Show Available Checks", CVAR_TRACKER_CHECK("OnlyShowAvailable"), UIWidgets::CheckboxOptions({ { .tooltip = "When active, unavailable checks will be hidden." } }) @@ -1069,49 +1070,61 @@ void CheckTrackerWindow::DrawElement() { RecalculateAllAreaTotals(); } } - UIWidgets::PaddedSeparator(); - if (UIWidgets::Button("Expand All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { - optCollapseAll = false; - optExpandAll = true; - doAreaScroll = true; - } - ImGui::SameLine(); - if (UIWidgets::Button("Collapse All", - UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) { - optExpandAll = false; - optCollapseAll = true; - } - ImGui::SameLine(); - if (UIWidgets::Button("Clear", UIWidgets::ButtonOptions({ { .tooltip = "Clear the search field" } }) - .Color(THEME_COLOR) - .Size(UIWidgets::Sizes::Inline))) { - checkSearch.Clear(); - UpdateFilters(); - doAreaScroll = true; + if (CVarGetInteger(CVAR_TRACKER_CHECK("ExpandCollapseButtonsVisible"), 0)) { + if (UIWidgets::Button( + "Expand All", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size({ ImGui::GetContentRegionAvail().x / 2 - 6, 0 }))) { + optCollapseAll = false; + optExpandAll = true; + doAreaScroll = true; + } + ImGui::SameLine(); + if (UIWidgets::Button( + "Collapse All", + UIWidgets::ButtonOptions().Color(THEME_COLOR).Size({ ImGui::GetContentRegionAvail().x - 6, 0 }))) { + optExpandAll = false; + optCollapseAll = true; + } } UIWidgets::PushStyleCombobox(THEME_COLOR); - if (checkSearch.Draw()) { - UpdateFilters(); + if (CVarGetInteger(CVAR_TRACKER_CHECK("SearchInputVisible"), 1)) { + if (checkSearch.Draw("", ImGui::GetContentRegionAvail().x - 6)) { + UpdateFilters(); + } + std::string checkSearchText = ""; + checkSearchText = checkSearch.InputBuf; + checkSearchText.erase(std::remove(checkSearchText.begin(), checkSearchText.end(), ' '), checkSearchText.end()); + if (checkSearchText.length() < 1) { + ImGui::SameLine(20.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search..."); + } } UIWidgets::PopStyleCombobox(); - ImGui::Separator(); - - std::ostringstream totalChecksSS; - totalChecksSS << "Total Checks: "; - if (enableAvailableChecks) { - totalChecksSS << totalChecksAvailable << " Available / "; + if (CVarGetInteger(CVAR_TRACKER_CHECK("CheckTotalsVisible"), 1)) { + std::ostringstream totalChecksSS; + totalChecksSS << ""; + if (enableAvailableChecks) { + totalChecksSS << totalChecksAvailable << " Available / "; + } + totalChecksSS << totalChecksGotten << " Checked / " << totalChecks << " Total"; + ImGui::Text("%s", totalChecksSS.str().c_str()); } - totalChecksSS << totalChecksGotten << " Checked / " << totalChecks << " Total"; - ImGui::Text("%s", totalChecksSS.str().c_str()); - UIWidgets::PaddedSeparator(); + bool headerPresent = + CVarGetInteger(CVAR_TRACKER_CHECK("HiddenItemsToggleVisible"), 1) || + (enableAvailableChecks && CVarGetInteger(CVAR_TRACKER_CHECK("AvailableChecksToggleVisible"), 1)) || + CVarGetInteger(CVAR_TRACKER_CHECK("ExpandCollapseButtonsVisible"), 0) || + CVarGetInteger(CVAR_TRACKER_CHECK("SearchInputVisible"), 1) || + CVarGetInteger(CVAR_TRACKER_CHECK("CheckTotalsVisible"), 1); + if (headerPresent) { + ImGui::Separator(); + } // Checks Section Lead-in ImGui::TableNextRow(); ImGui::TableNextColumn(); - size = ImGui::GetContentRegionAvail(); - if (!ImGui::BeginTable("CheckTracker##Checks", 1, ImGuiTableFlags_ScrollY, size)) { + if (!ImGui::BeginTable("CheckTracker##Checks", 1, ImGuiTableFlags_ScrollY)) { ImGui::EndTable(); EndFloatWindows(); return; @@ -1181,7 +1194,7 @@ void CheckTrackerWindow::DrawElement() { } else { ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once); } - doDraw = ImGui::TreeNode(stemp.c_str()); + doDraw = ImGui::TreeNodeEx(stemp.c_str(), ImGuiTreeNodeFlags_NoTreePushOnOpen); ImGui::PopStyleColor(); ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, @@ -1230,10 +1243,6 @@ void CheckTrackerWindow::DrawElement() { DrawLocation(rc); } } - - if (doDraw) { - ImGui::TreePop(); - } } } ImGui::PopStyleVar(); @@ -2130,6 +2139,16 @@ void CheckTrackerSettingsWindow::DrawElement() { SohGui::mSohMenu->MenuDrawItem(windowTypeWidget, ImGui::GetContentRegionAvail().x, THEME_COLOR); + UIWidgets::CVarSliderFloat("Font Size", CVAR_TRACKER_CHECK("FontSize"), + UIWidgets::FloatSliderOptions() + .Tooltip("Sets the font size used in the check tracker.") + .Format("%.1f") + .Step(0.1f) + .Min(0.3f) + .Max(2.0f) + .Color(THEME_COLOR) + .DefaultValue(1.0f)); + if (CVarGetInteger(CVAR_TRACKER_CHECK("WindowType"), TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { UIWidgets::CVarCheckbox("Enable Dragging", CVAR_TRACKER_CHECK("Draggable"), UIWidgets::CheckboxOptions().Color(THEME_COLOR)); @@ -2172,7 +2191,6 @@ void CheckTrackerSettingsWindow::DrawElement() { ImGui::EndDisabled(); // Filtering settings - UIWidgets::PaddedSeparator(); UIWidgets::CVarCheckbox( "Filter Empty Areas", CVAR_TRACKER_CHECK("HideFilteredAreas"), UIWidgets::CheckboxOptions() @@ -2180,6 +2198,18 @@ void CheckTrackerSettingsWindow::DrawElement() { .Color(THEME_COLOR) .DefaultValue(true)); + ImGui::SeparatorText("Tracker Header Visibility"); + UIWidgets::CVarCheckbox("Hidden Items Toggle", CVAR_TRACKER_CHECK("HiddenItemsToggleVisible"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + UIWidgets::CVarCheckbox("Available Checks Toggle", CVAR_TRACKER_CHECK("AvailableChecksToggleVisible"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + UIWidgets::CVarCheckbox("Expand/Collapse Buttons", CVAR_TRACKER_CHECK("ExpandCollapseButtonsVisible"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR).DefaultValue(false)); + UIWidgets::CVarCheckbox("Search Input", CVAR_TRACKER_CHECK("SearchInputVisible"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + UIWidgets::CVarCheckbox("Check Totals", CVAR_TRACKER_CHECK("CheckTotalsVisible"), + UIWidgets::CheckboxOptions().Color(THEME_COLOR).DefaultValue(true)); + ImGui::TableNextColumn(); CheckTracker::ImGuiDrawTwoColorPickerSection("Area Incomplete", CVAR_TRACKER_CHECK("AreaIncomplete.MainColor"), diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index ec46209fe0..8f3182353d 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -706,6 +706,11 @@ void Settings::CreateOptions() { RT_DC_MQ_ADULT_EYES, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "Dodongo\'s Cavern MQ Light the Eyes with Strength as Adult", "If you move very quickly, it is possible to use the bomb flower at the top of the room to light the eyes."); + OPT_TRICK( + RT_DC_EYES_CHU, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, + "Dodongo\'s Cavern Light the Eyes with Bombchus", + "You can light the dodongo head's eyes with bombchus from the main room, allowing instant access to the end " + "of the dungeon."); OPT_TRICK(RT_JABU_ALCOVE_JUMP_DIVE, RCQUEST_BOTH, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, "Jabu Underwater Alcove as Adult with Jump Dive", "Standing above the underwater tunnel leading to the scrub, jump down and swim through the tunnel. This " diff --git a/soh/soh/Enhancements/timesaver_hook_handlers.cpp b/soh/soh/Enhancements/timesaver_hook_handlers.cpp index fe91c67b70..f0aa329843 100644 --- a/soh/soh/Enhancements/timesaver_hook_handlers.cpp +++ b/soh/soh/Enhancements/timesaver_hook_handlers.cpp @@ -773,7 +773,7 @@ void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_li (IS_RANDO || CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipMiscInteractions"), IS_RANDO))) { if (IS_RANDO || *should) { Flags_SetRandomizerInf(flag); - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; Magic_Fill(gPlayState); } *should = false; diff --git a/soh/soh/Network/Anchor/Anchor.cpp b/soh/soh/Network/Anchor/Anchor.cpp index e996ac6a09..5852e62e07 100644 --- a/soh/soh/Network/Anchor/Anchor.cpp +++ b/soh/soh/Network/Anchor/Anchor.cpp @@ -110,6 +110,8 @@ void Anchor::ProcessIncomingPacketQueue() { HandlePacket_GameComplete(payload); else if (packetType == GIVE_ITEM) HandlePacket_GiveItem(payload); + else if (packetType == OCARINA_SFX) + HandlePacket_OcarinaSfx(payload); else if (packetType == PLAYER_SFX) HandlePacket_PlayerSfx(payload); else if (packetType == UPDATE_TEAM_STATE) diff --git a/soh/soh/Network/Anchor/Anchor.h b/soh/soh/Network/Anchor/Anchor.h index 93c4046171..9eb1032309 100644 --- a/soh/soh/Network/Anchor/Anchor.h +++ b/soh/soh/Network/Anchor/Anchor.h @@ -35,6 +35,8 @@ typedef struct { s32 linkAge; PosRot posRot; Vec3s jointTable[24]; + u8 movementFlags; + Vec3s prevTransl; Vec3s upperLimbRot; s8 currentBoots; s8 currentShield; @@ -46,8 +48,12 @@ typedef struct { s8 heldItemAction; u8 modelGroup; s8 invincibilityTimer; + f32 unk_85C; s16 unk_862; s8 actionVar1; + u8 ocarinaNote; + f32 ocarinaModulator; + s8 ocarinaBend; // Ptr to the dummy player Player* player; @@ -84,6 +90,7 @@ class Anchor : public Network { void HandlePacket_EntranceDiscovered(nlohmann::json payload); void HandlePacket_GameComplete(nlohmann::json payload); void HandlePacket_GiveItem(nlohmann::json payload); + void HandlePacket_OcarinaSfx(nlohmann::json payload); void HandlePacket_PlayerSfx(nlohmann::json payload); void HandlePacket_PlayerUpdate(nlohmann::json payload); void HandlePacket_RequestTeamState(nlohmann::json payload); @@ -111,6 +118,7 @@ class Anchor : public Network { inline static const std::string GAME_COMPLETE = "GAME_COMPLETE"; inline static const std::string GIVE_ITEM = "GIVE_ITEM"; inline static const std::string HANDSHAKE = "HANDSHAKE"; + inline static const std::string OCARINA_SFX = "OCARINA_SFX"; inline static const std::string PLAYER_SFX = "PLAYER_SFX"; inline static const std::string PLAYER_UPDATE = "PLAYER_UPDATE"; inline static const std::string REQUEST_TEAM_STATE = "REQUEST_TEAM_STATE"; @@ -148,6 +156,7 @@ class Anchor : public Network { void SendPacket_GameComplete(); void SendPacket_GiveItem(u16 modId, s16 getItemId); void SendPacket_Handshake(); + void SendPacket_OcarinaSfx(uint8_t note, float modulator, int8_t bend); void SendPacket_PlayerSfx(u16 sfxId); void SendPacket_PlayerUpdate(); void SendPacket_RequestTeamState(); diff --git a/soh/soh/Network/Anchor/DummyPlayer.cpp b/soh/soh/Network/Anchor/DummyPlayer.cpp index 3bc0854134..8e5df3c8e5 100644 --- a/soh/soh/Network/Anchor/DummyPlayer.cpp +++ b/soh/soh/Network/Anchor/DummyPlayer.cpp @@ -122,6 +122,8 @@ void DummyPlayer_Update(Actor* actor, PlayState* play) { Math_Vec3s_Copy(&actor->shape.rot, &client.posRot.rot); Math_Vec3f_Copy(&actor->world.pos, &client.posRot.pos); player->skelAnime.jointTable = client.jointTable; + player->skelAnime.movementFlags = client.movementFlags; + Math_Vec3s_Copy(&player->skelAnime.prevTransl, &client.prevTransl); player->currentBoots = client.currentBoots; player->currentShield = client.currentShield; player->currentTunic = client.currentTunic; @@ -131,15 +133,38 @@ void DummyPlayer_Update(Actor* actor, PlayState* play) { player->heldItemAction = client.heldItemAction; player->invincibilityTimer = client.invincibilityTimer; player->unk_862 = client.unk_862; + player->unk_85C = client.unk_85C; player->av1.actionVar1 = client.actionVar1; - if (player->modelGroup != client.modelGroup) { + // Apply animation movement (Copied from Player_ApplyAnimMovementScaledByAge) + Vec3f diff; + SkelAnime_UpdateTranslation(&player->skelAnime, &diff, player->actor.shape.rot.y); + + if (player->skelAnime.movementFlags & 1) { + if (!LINK_IS_ADULT) { + diff.x *= 0.64f; + diff.z *= 0.64f; + } + + player->actor.world.pos.x += diff.x * player->actor.scale.x; + player->actor.world.pos.z += diff.z * player->actor.scale.z; + } + + if (player->skelAnime.movementFlags & 2) { + if (!(player->skelAnime.movementFlags & 4)) { + diff.y *= player->ageProperties->unk_08; + } + + player->actor.world.pos.y += diff.y * player->actor.scale.y; + } + + if (player->modelGroup != Player_ActionToModelGroup(player, player->itemAction)) { // Hack to account for usage of gSaveContext s32 originalAge = gSaveContext.linkAge; gSaveContext.linkAge = client.linkAge; u8 originalButtonItem0 = gSaveContext.equips.buttonItems[0]; gSaveContext.equips.buttonItems[0] = client.buttonItem0; - Player_SetModelGroup(player, client.modelGroup); + Player_SetModelGroup(player, Player_ActionToModelGroup(player, player->itemAction)); gSaveContext.linkAge = originalAge; gSaveContext.equips.buttonItems[0] = originalButtonItem0; } diff --git a/soh/soh/Network/Anchor/HookHandlers.cpp b/soh/soh/Network/Anchor/HookHandlers.cpp index 9a4622c293..c620ee30e1 100644 --- a/soh/soh/Network/Anchor/HookHandlers.cpp +++ b/soh/soh/Network/Anchor/HookHandlers.cpp @@ -98,6 +98,8 @@ void Anchor::RegisterHooks() { COND_HOOK(OnGameFrameUpdate, isConnected, [&]() { ProcessIncomingPacketQueue(); }); COND_HOOK(OnPlayerSfx, isConnected, [&](u16 sfxId) { SendPacket_PlayerSfx(sfxId); }); + COND_HOOK(OnOcarinaNote, isConnected, + [&](uint8_t note, float modulator, int8_t bend) { SendPacket_OcarinaSfx(note, modulator, bend); }); COND_HOOK(OnLoadGame, isConnected, [&](s16 fileNum) { justLoadedSave = true; }); @@ -152,6 +154,31 @@ void Anchor::RegisterHooks() { SendPacket_UpdateDungeonItems(); }); + COND_VB_SHOULD(VB_APPLY_TUNIC_COLOR, isConnected, { + Actor* myPlayer = (Actor*)GET_PLAYER(gPlayState); + Actor* actor = va_arg(args, Actor*); + Color_RGB8* color = va_arg(args, Color_RGB8*); + + if (actor == myPlayer) { + Color_RGBA8 ownColor = CVarGetColor(CVAR_REMOTE_ANCHOR("Color.Value"), { 100, 255, 100 }); + color->r = ownColor.r; + color->g = ownColor.g; + color->b = ownColor.b; + return; + } + + uint32_t clientId = Anchor::Instance->GetDummyPlayerClientId(actor); + + if (!Anchor::Instance->clients.contains(clientId)) { + return; + } + + AnchorClient& client = Anchor::Instance->clients[clientId]; + color->r = client.color.r; + color->g = client.color.g; + color->b = client.color.b; + }); + // #endregion // #region Hooks that are purely to sync actor states across the clients, not super essential diff --git a/soh/soh/Network/Anchor/Menu.cpp b/soh/soh/Network/Anchor/Menu.cpp index d0e74167c0..47cd102d2a 100644 --- a/soh/soh/Network/Anchor/Menu.cpp +++ b/soh/soh/Network/Anchor/Menu.cpp @@ -46,7 +46,10 @@ void AnchorMainMenu(WidgetInfo& info) { } UIWidgets::PopStyleInput(); - ImGui::Text("Name"); + ImGui::Text("Name & Color"); + static Color_RGBA8 defaultColor = { 100, 255, 100, 255 }; + UIWidgets::CVarColorPicker("##Color", CVAR_REMOTE_ANCHOR("Color"), defaultColor); + ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (UIWidgets::InputString("##Name", &anchorName, UIWidgets::InputOptions().Color(THEME_COLOR))) { CVarSetString(CVAR_REMOTE_ANCHOR("Name"), anchorName.c_str()); diff --git a/soh/soh/Network/Anchor/Packets/OcarinaSfx.cpp b/soh/soh/Network/Anchor/Packets/OcarinaSfx.cpp new file mode 100644 index 0000000000..72eee3c5c0 --- /dev/null +++ b/soh/soh/Network/Anchor/Packets/OcarinaSfx.cpp @@ -0,0 +1,65 @@ +#include "soh/Network/Anchor/Anchor.h" +#include "soh/Network/Anchor/JsonConversions.hpp" +#include +#include + +extern "C" { +#include "macros.h" +#include "functions.h" +#include "variables.h" +extern PlayState* gPlayState; +extern f32 D_80130F28; +} + +/** + * OCARINA_SFX + * + * Ocarina effects, only sent to other clients in the same scene as the player + */ + +void Anchor::SendPacket_OcarinaSfx(uint8_t note, float modulator, int8_t bend) { + if (!IsSaveLoaded()) { + return; + } + + nlohmann::json payload; + + payload["type"] = OCARINA_SFX; + payload["note"] = note; + payload["modulator"] = modulator; + payload["bend"] = bend; + payload["quiet"] = true; + + for (auto& [clientId, client] : clients) { + if (client.sceneNum == gPlayState->sceneNum && client.online && client.isSaveLoaded && !client.self) { + payload["targetClientId"] = clientId; + SendJsonToRemote(payload); + } + } +} + +void Anchor::HandlePacket_OcarinaSfx(nlohmann::json payload) { + uint32_t clientId = payload["clientId"].get(); + uint8_t note = payload["note"].get(); + float modulator = payload["modulator"].get(); + int8_t bend = payload["bend"].get(); + + if (!clients.contains(clientId) || !clients[clientId].player) { + return; + } + + auto& client = clients[clientId]; + client.ocarinaModulator = modulator; + client.ocarinaBend = bend; + + if ((note != 0xFF) && (client.ocarinaNote != note)) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD07, client.ocarinaBend - 1); + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD05, note); + Audio_PlaySoundGeneral(NA_SE_OC_OCARINA, &client.player->actor.projectedPos, 4, &client.ocarinaModulator, + &D_80130F28, &gSfxDefaultReverb); + } else if ((client.ocarinaNote != 0xFF) && (note == 0xFF)) { + Audio_StopSfxById(NA_SE_OC_OCARINA); + } + + client.ocarinaNote = note; +} diff --git a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp index a4ee17b76c..c05de0a23c 100644 --- a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp +++ b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp @@ -50,6 +50,8 @@ void Anchor::SendPacket_PlayerUpdate() { jointArray.push_back(joint.y); jointArray.push_back(joint.z); } + payload["prevTransl"] = player->skelAnime.prevTransl; + payload["movementFlags"] = player->skelAnime.movementFlags; payload["jointTable"] = jointArray; payload["upperLimbRot"] = player->upperLimbRot; payload["currentBoots"] = player->currentBoots; @@ -63,6 +65,7 @@ void Anchor::SendPacket_PlayerUpdate() { payload["modelGroup"] = player->modelGroup; payload["invincibilityTimer"] = player->invincibilityTimer; payload["unk_862"] = player->unk_862; + payload["unk_85C"] = player->unk_85C; payload["actionVar1"] = player->av1.actionVar1; payload["quiet"] = true; @@ -94,6 +97,8 @@ void Anchor::HandlePacket_PlayerUpdate(nlohmann::json payload) { client.jointTable[i].y = jointArray[i * 3 + 1]; client.jointTable[i].z = jointArray[i * 3 + 2]; } + client.movementFlags = payload["movementFlags"].get(); + client.prevTransl = payload["prevTransl"].get(); client.upperLimbRot = payload["upperLimbRot"].get(); client.currentBoots = payload["currentBoots"].get(); client.currentShield = payload["currentShield"].get(); @@ -106,6 +111,7 @@ void Anchor::HandlePacket_PlayerUpdate(nlohmann::json payload) { client.modelGroup = payload["modelGroup"].get(); client.invincibilityTimer = payload["invincibilityTimer"].get(); client.unk_862 = payload["unk_862"].get(); + client.unk_85C = payload["unk_85C"].get(); client.actionVar1 = payload["actionVar1"].get(); } } diff --git a/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp b/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp index 7b3c180bbb..3099da84a4 100644 --- a/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp +++ b/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp @@ -23,7 +23,7 @@ extern PlayState* gPlayState; nlohmann::json Anchor::PrepClientState() { nlohmann::json payload; payload["name"] = CVarGetString(CVAR_REMOTE_ANCHOR("Name"), ""); - payload["color"] = CVarGetColor24(CVAR_REMOTE_ANCHOR("Color"), { 100, 255, 100 }); + payload["color"] = CVarGetColor24(CVAR_REMOTE_ANCHOR("Color.Value"), { 100, 255, 100 }); payload["clientVersion"] = clientVersion; payload["teamId"] = CVarGetString(CVAR_REMOTE_ANCHOR("TeamId"), "default"); payload["online"] = true; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index d34970d4d7..8b0cce20cc 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -154,6 +154,7 @@ Color_RGB8 kokiriColor = { 0x1E, 0x69, 0x1B }; Color_RGB8 goronColor = { 0x64, 0x14, 0x00 }; Color_RGB8 zoraColor = { 0x00, 0xEC, 0x64 }; +int32_t previousImGuiScaleIndex; float previousImGuiScale; bool prevAltAssets = false; @@ -433,6 +434,7 @@ void OTRGlobals::Initialize() { hasMasterQuest = hasOriginal = false; + previousImGuiScaleIndex = -1; previousImGuiScale = defaultImGuiScale; fontMonoSmall = CreateFontWithSize(14.0f, "fonts/Inconsolata-Regular.ttf"); @@ -501,11 +503,17 @@ OTRGlobals::~OTRGlobals() { } void OTRGlobals::ScaleImGui() { - float scale = imguiScaleOptionToValue[CVarGetInteger(CVAR_SETTING("ImGuiScale"), defaultImGuiScale)]; + int32_t imGuiScaleIndex = CVarGetInteger(CVAR_SETTING("ImGuiScale"), defaultImGuiScale); + if (imGuiScaleIndex == previousImGuiScaleIndex) { + return; + } + + float scale = imguiScaleOptionToValue[imGuiScaleIndex]; float newScale = scale / previousImGuiScale; ImGui::GetStyle().ScaleAllSizes(newScale); ImGui::GetIO().FontGlobalScale = scale; previousImGuiScale = scale; + previousImGuiScaleIndex = imGuiScaleIndex; } ImFont* OTRGlobals::CreateDefaultFontWithSize(float size) { diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 3c6a25dc7b..f0d6e06c46 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -669,10 +669,10 @@ void SaveManager::InitFileNormal() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } - gSaveContext.healthCapacity = 0x30; - gSaveContext.health = 0x30; + gSaveContext.healthCapacity = STARTING_HEALTH; + gSaveContext.health = STARTING_HEALTH; gSaveContext.magicLevel = 0; - gSaveContext.magic = 0x30; + gSaveContext.magic = MAGIC_NORMAL_METER; gSaveContext.rupees = 0; gSaveContext.swordHealth = 0; gSaveContext.naviTimer = 0; @@ -964,10 +964,10 @@ void SaveManager::InitFileMaxed() { gSaveContext.ship.filenameLanguage = (gSaveContext.language == LANGUAGE_JPN) ? NAME_LANGUAGE_NTSC_JPN : NAME_LANGUAGE_NTSC_ENG; } - gSaveContext.healthCapacity = 0x140; - gSaveContext.health = 0x140; + gSaveContext.healthCapacity = MAX_HEALTH; + gSaveContext.health = MAX_HEALTH; gSaveContext.magicLevel = 2; - gSaveContext.magic = 0x60; + gSaveContext.magic = MAGIC_DOUBLE_METER; gSaveContext.rupees = 500; gSaveContext.swordHealth = 8; gSaveContext.naviTimer = 0; diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index 149eb6577c..c32b117558 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -581,7 +581,6 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Show Age-Dependent Equipment", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("EquipmentAlwaysVisible")) .RaceDisable(false) - .Callback([](WidgetInfo& info) { UpdatePatchHand(); }) .Options(CheckboxOptions().Tooltip("Makes all equipment visible, regardless of age.")); AddWidget(path, "Scale Adult Equipment as Child", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ScaleAdultEquipmentAsChild")) @@ -602,7 +601,6 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Color Temple of Time's Medallions", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("ToTMedallionsColors")) .RaceDisable(false) - .Callback([](WidgetInfo& info) { UpdateToTMedallions(); }) .Options(CheckboxOptions().Tooltip( "When Medallions are collected, the Medallion imprints around the Master Sword Pedestal in the Temple " "of Time will become colored-in.")); @@ -1074,17 +1072,11 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Fix Hand Holding Hammer", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("FixHammerHand")) .RaceDisable(false) - .Callback([](WidgetInfo& info) { UpdatePatchHand(); }) .Options(CheckboxOptions().Tooltip( "Fixes Adult Link having a backwards Left hand when holding the Megaton Hammer.")); AddWidget(path, "Fix Vanishing Paths", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_ENHANCEMENT("SceneSpecificDirtPathFix")) .RaceDisable(false) - .Callback([](WidgetInfo& info) { - if (gPlayState != NULL) { - DirtPathFix_UpdateZFightingMode(gPlayState->sceneNum); - } - }) .Options( ComboboxOptions() .ComboMap(zFightingOptions) @@ -1181,7 +1173,6 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Health", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Permanent Heart Loss", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("PermanentHeartLoss")) - .Callback([](WidgetInfo& info) { UpdatePermanentHeartLossState(); }) .Options(CheckboxOptions().Tooltip( "When you lose 4 quarters of a heart you will permanently lose that Heart Container.\n\n" "Disabling this after the fact will restore your Heart Containers.")); @@ -1300,7 +1291,6 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip("All Major Bosses move and act twice as fast.")); AddWidget(path, "Hyper Enemies", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("HyperEnemies")) - .Callback([](WidgetInfo& info) { UpdateHyperEnemiesState(); }) .Options(CheckboxOptions().Tooltip("All Regular Enemies and Mini-Bosses move and act twice as fast.")); AddWidget(path, "Enable Visual Guard Vision", WIDGET_CVAR_CHECKBOX).CVar(CVAR_ENHANCEMENT("GuardVision")); AddWidget(path, "Leever Spawn Rate: %d seconds", WIDGET_CVAR_SLIDER_INT) @@ -1524,11 +1514,6 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Mirrored World", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_ENHANCEMENT("MirroredWorldMode")) - .Callback([](WidgetInfo& info) { - if (gPlayState != NULL) { - UpdateMirrorModeState(gPlayState->sceneNum); - } - }) .Options( ComboboxOptions() .DefaultIndex(MIRRORED_WORLD_OFF) @@ -1568,9 +1553,6 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip("A Wallmaster follows Link everywhere, don't get caught!")); AddWidget(path, "Hurt Container Mode", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("HurtContainer")) - .Callback([](WidgetInfo& info) { - UpdateHurtContainerModeState(CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)); - }) .Options(CheckboxOptions().Tooltip("Changes Heart Piece and Heart Container functionality.\n\n" " - Each Heart Container or full Heart Piece reduces Link's Hearts by 1.\n" " - Can be enabled retroactively after a File has already started.")); diff --git a/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c index 2da5b4d2b1..0cc9debe89 100644 --- a/soh/src/code/code_800EC960.c +++ b/soh/src/code/code_800EC960.c @@ -1677,6 +1677,7 @@ void func_800ED458(s32 arg0) { } else if ((sPrevOcarinaNoteVal != 0xFF) && (sCurOcarinaBtnVal == 0xFF)) { Audio_StopSfxById(NA_SE_OC_OCARINA); } + GameInteractor_ExecuteOnOcarinaNote(sCurOcarinaBtnVal, D_80130F24, D_80130F10); } } diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index b6bf5ecdf9..28459ba5ee 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -1707,7 +1707,7 @@ void Item_DropCollectibleRandom(PlayState* play, Actor* fromActor, Vec3f* spawnP } if (dropId == ITEM00_FLEXIBLE) { - if (gSaveContext.health <= 0x10) { // 1 heart or less + if (gSaveContext.health <= FULL_HEART_HEALTH) { // 1 heart or less Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ELF, spawnPos->x, spawnPos->y + 40.0f, spawnPos->z, 0, 0, 0, FAIRY_HEAL_TIMED, true); EffectSsDeadSound_SpawnStationary(play, spawnPos, NA_SE_EV_BUTTERFRY_TO_FAIRY, true, diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index 451bdf9ab5..cd6de967ea 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -393,9 +393,9 @@ void HealthMeter_Draw(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; GraphicsContext* gfxCtx = play->state.gfxCtx; Vtx* sp154 = interfaceCtx->beatingHeartVtx; - s32 curHeartFraction = gSaveContext.health % 0x10; - s16 totalHeartCount = gSaveContext.healthCapacity / 0x10; - s16 fullHeartCount = gSaveContext.health / 0x10; + s32 curHeartFraction = gSaveContext.health % FULL_HEART_HEALTH; + s16 totalHeartCount = gSaveContext.healthCapacity / FULL_HEART_HEALTH; + s16 fullHeartCount = gSaveContext.health / FULL_HEART_HEALTH; s32 pad2; f32 sp144 = interfaceCtx->unk_22A * 0.1f; s32 curCombineModeSet = 0; @@ -410,7 +410,7 @@ void HealthMeter_Draw(PlayState* play) { OPEN_DISPS(gfxCtx); - if (!(gSaveContext.health % 0x10)) { + if (!(gSaveContext.health % FULL_HEART_HEALTH)) { fullHeartCount--; } diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index efdf29627b..7a59e44094 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -4656,7 +4656,7 @@ void Message_Update(PlayState* play) { } if ((msgCtx->textId >= 0xC2 && msgCtx->textId < 0xC7) || (msgCtx->textId >= 0xFA && msgCtx->textId < 0xFE)) { - gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts + gSaveContext.healthAccumulator = MAX_HEALTH; // Refill 20 hearts } if (msgCtx->textId == 0x301F || msgCtx->textId == 0xA || msgCtx->textId == 0xC || msgCtx->textId == 0xCF || msgCtx->textId == 0x21C || msgCtx->textId == 9 || msgCtx->textId == 0x4078 || @@ -4694,12 +4694,9 @@ void Message_Update(PlayState* play) { } if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { gSaveContext.inventory.questItems ^= 0x40000000; - if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { - gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; - } else { - gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + if (GameInteractor_Should(VB_HEARTS_INCREASE_WITH_CONTAINERS, true)) { + gSaveContext.healthCapacity += FULL_HEART_HEALTH; + gSaveContext.health += FULL_HEART_HEALTH; } } if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 5eabe0e5dc..0e9373b436 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -2325,19 +2325,16 @@ u8 Item_Give(PlayState* play, u8 item) { gSaveContext.ship.stats.heartPieces++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART_CONTAINER) { - if (!CVarGetInteger(CVAR_ENHANCEMENT("HurtContainer"), 0)) { - gSaveContext.healthCapacity += 0x10; - gSaveContext.health += 0x10; - } else { - gSaveContext.healthCapacity -= 0x10; - gSaveContext.health -= 0x10; + if (GameInteractor_Should(VB_HEARTS_INCREASE_WITH_CONTAINERS, true)) { + gSaveContext.healthCapacity += FULL_HEART_HEALTH; + gSaveContext.health += FULL_HEART_HEALTH; } gSaveContext.ship.stats.heartContainers++; return Return_Item(item, MOD_NONE, ITEM_NONE); } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" if (play != NULL) { - Health_ChangeBy(play, 0x10); + Health_ChangeBy(play, FULL_HEART_HEALTH); } return Return_Item(item, MOD_NONE, item); } else if (item == ITEM_MAGIC_SMALL) { @@ -2917,7 +2914,7 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { gSaveContext.health = gSaveContext.healthCapacity; } - heartCount = gSaveContext.health % 0x10; + heartCount = gSaveContext.health % FULL_HEART_HEALTH; healthLevel = heartCount; if (heartCount != 0) { @@ -3574,7 +3571,7 @@ void Interface_DrawMagicBar(PlayState* play) { R_MAGIC_FILL_X - 1; } } else { - if ((gSaveContext.healthCapacity - 1) / 0x10 >= lineLength && lineLength != 0) { + if ((gSaveContext.healthCapacity - 1) / FULL_HEART_HEALTH >= lineLength && lineLength != 0) { magicBarY = magicBarY_original_l + magicDrop * (lineLength == 0 ? 0 : ((gSaveContext.healthCapacity - 1) / (0x10 * lineLength) - 1)); diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index d458563244..9c6dbc1826 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -7,6 +7,7 @@ #include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/randomizer/draw.h" #include "soh/Enhancements/Holiday/Fredomato.h" #include "soh/ResourceManagerHelpers.h" @@ -1087,7 +1088,9 @@ void Player_DrawImpl(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dL color = &sTemp; } - gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 0); + if (GameInteractor_Should(VB_APPLY_TUNIC_COLOR, true, data, color)) { + gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 0); + } // If we have a custom link model, always use the most detailed LOD if (Player_IsCustomLinkModel()) { diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 023ee02df4..a47b6facbc 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -151,9 +151,9 @@ void Sram_OpenSave() { osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); osSyncPrintf(VT_RST); - if (gSaveContext.health < 0x30) { + if (gSaveContext.health < STARTING_HEALTH) { gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : STARTING_HEALTH; } if (gSaveContext.scarecrowLongSongSet) { diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c index 2e3ad78884..a1fe242c61 100644 --- a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c @@ -475,7 +475,7 @@ void BgDyYoseizo_HealPlayer_NoReward(BgDyYoseizo* this, PlayState* play) { } if (this->healingTimer == 110) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; Magic_Fill(play); this->refillTimer = 200; } @@ -743,7 +743,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } if (!this->healing) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; this->healing = true; if (actionIndex == 2) { Magic_Fill(play); @@ -771,7 +771,7 @@ void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, PlayState* play) { } this->itemSpawned = true; - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; Interface_ChangeAlpha(9); gSaveContext.itemGetInf[1] |= sItemGetFlags[actionIndex]; Item_Give(play, sItemIds[actionIndex]); diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index c57ddc5f7b..7e21b8e994 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -586,7 +586,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { this->unk_198 = 2; this->timers[2] = 110; if (GameInteractor_Should(VB_GANON_HEAL_BEFORE_FIGHT, true)) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; } Audio_QueueSeqCmd(NA_BGM_STOP); } else { @@ -802,7 +802,7 @@ void BossGanon_IntroCutscene(BossGanon* this, PlayState* play) { } if (this->csTimer == 25) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; } if (this->csTimer == 100) { diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c index 712a9008d8..129d948790 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c @@ -206,7 +206,7 @@ void EnBomBowlPit_Reset(EnBomBowlPit* this, PlayState* play) { // "Normal termination"/"completion" osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); if (this->getItemId == GI_HEART_PIECE) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; // "Ah recovery!" (?) osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ あぁ回復! ☆☆☆☆☆ \n" VT_RST); } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index ca85135662..442a2dfc32 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -9517,7 +9517,7 @@ void func_80843AE8(PlayState* play, Player* this) { LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_derth_rebirth, 1.0f, 99.0f, Animation_GetLastFrame(&gPlayerAnim_link_derth_rebirth), ANIMMODE_ONCE, 0.0f); } - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; this->av2.actionVar2 = -1; } } else if (gSaveContext.healthAccumulator == 0) { @@ -14599,20 +14599,20 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { rand = 3; } - if ((rand < 0) && (gSaveContext.health <= 0x10)) { + if ((rand < 0) && (gSaveContext.health <= FULL_HEART_HEALTH)) { rand = 3; } if (rand < 0) { - Health_ChangeBy(play, -0x10); + Health_ChangeBy(play, -FULL_HEART_HEALTH); } else { - gSaveContext.healthAccumulator = rand * 0x10; + gSaveContext.healthAccumulator = rand * FULL_HEART_HEALTH; } } else { s32 sp28 = D_808549FC[this->itemAction - PLAYER_IA_BOTTLE_POTION_RED]; if (sp28 & 1) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; } if (sp28 & 2) { @@ -14756,7 +14756,7 @@ void Player_Action_8084EED8(Player* this, PlayState* play) { Player_PlaySfx(this, NA_SE_EV_BOTTLE_CAP_OPEN); Player_PlaySfx(this, NA_SE_EV_FIATY_HEAL - SFX_FLAG); } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { - gSaveContext.healthAccumulator = 0x140; + gSaveContext.healthAccumulator = MAX_HEALTH; } } diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 0328a97f9d..7d747012c0 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -2401,7 +2401,7 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { gDPSetEnvColor(POLY_OPA_DISP++, heartBorder.r, heartBorder.g, heartBorder.b, 255); } - i = Save_GetSaveMetaInfo(fileIndex)->healthCapacity / 0x10; + i = Save_GetSaveMetaInfo(fileIndex)->healthCapacity / FULL_HEART_HEALTH; if (CVarGetInteger(CVAR_ENHANCEMENT("FileSelectMoreInfo"), 0) == 0 || this->menuMode != FS_MENU_MODE_SELECT || Save_GetSaveMetaInfo(this->selectedFileIndex)->archiSave) { diff --git a/soh/src/overlays/gamestates/ovl_select/z_select.c b/soh/src/overlays/gamestates/ovl_select/z_select.c index a057ebbd55..7f15d39711 100644 --- a/soh/src/overlays/gamestates/ovl_select/z_select.c +++ b/soh/src/overlays/gamestates/ovl_select/z_select.c @@ -50,6 +50,8 @@ void Select_LoadGame(SelectContext* this, s32 entranceIndex) { CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenCurrentScene"), this->currentScene); CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenTopDisplayedScene"), this->topDisplayedScene); CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenPageDownIndex"), this->pageDownIndex); + CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenLinkAge"), gSaveContext.linkAge); + CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenNightFlag"), gSaveContext.nightFlag); CVarSave(); if (ResourceMgr_GameHasMasterQuest() && ResourceMgr_GameHasOriginal()) { @@ -118,6 +120,8 @@ void Select_Grotto_LoadGame(SelectContext* this, s32 grottoIndex) { CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenCurrentScene"), this->currentScene); CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenTopDisplayedScene"), this->topDisplayedScene); CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenPageDownIndex"), this->pageDownIndex); + CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenLinkAge"), gSaveContext.linkAge); + CVarSetInteger(CVAR_GENERAL("BetterDebugWarpScreenNightFlag"), gSaveContext.nightFlag); CVarSave(); } @@ -1833,6 +1837,10 @@ void Select_SwitchBetterWarpMode(SelectContext* this, u8 isBetterWarpMode) { this->opt = 1; } } + + gSaveContext.linkAge = CVarGetInteger(CVAR_GENERAL("BetterDebugWarpScreenLinkAge"), 1); + gSaveContext.nightFlag = CVarGetInteger(CVAR_GENERAL("BetterDebugWarpScreenNightFlag"), 0); + gSaveContext.dayTime = gSaveContext.nightFlag ? 0x0000 : 0x8000; } else { this->count = ARRAY_COUNT(sScenes); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c index 2eb5e15bbd..b79da892f6 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c @@ -140,7 +140,7 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); // Current Health Quarter (X / 4) - KaleidoScope_DrawDigit(play, (gSaveContext.health % 0x10) / 4, 194, 15); + KaleidoScope_DrawDigit(play, (gSaveContext.health % FULL_HEART_HEALTH) / 4, 194, 15); gDPPipeSync(POLY_OPA_DISP++); gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); @@ -169,7 +169,7 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { // Health capacity spD8[2] = 0; - spD8[3] = gSaveContext.healthCapacity / 0x10; + spD8[3] = gSaveContext.healthCapacity / FULL_HEART_HEALTH; while (spD8[3] >= 10) { spD8[2]++; spD8[3] -= 10; @@ -180,7 +180,7 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { // Health spD8[2] = 0; - spD8[3] = gSaveContext.health / 0x10; + spD8[3] = gSaveContext.health / FULL_HEART_HEALTH; while (spD8[3] >= 10) { spD8[2]++; spD8[3] -= 10; @@ -368,15 +368,15 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { case 1: if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { - gSaveContext.healthCapacity -= 0x10; - if (gSaveContext.healthCapacity < 0x30) { - gSaveContext.healthCapacity = 0x30; + gSaveContext.healthCapacity -= FULL_HEART_HEALTH; + if (gSaveContext.healthCapacity < STARTING_HEALTH) { + gSaveContext.healthCapacity = STARTING_HEALTH; } } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { - gSaveContext.healthCapacity += 0x10; - if (gSaveContext.healthCapacity >= 0x140) { - gSaveContext.healthCapacity = 0x140; + gSaveContext.healthCapacity += FULL_HEART_HEALTH; + if (gSaveContext.healthCapacity >= MAX_HEALTH) { + gSaveContext.healthCapacity = MAX_HEALTH; } } break; @@ -387,9 +387,9 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { Health_ChangeBy(play, 4); } else if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { - Health_ChangeBy(play, -0x10); + Health_ChangeBy(play, -FULL_HEART_HEALTH); } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { - Health_ChangeBy(play, 0x10); + Health_ChangeBy(play, FULL_HEART_HEALTH); } break; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 14f749d43d..1e53d8180a 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -1633,8 +1633,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); if (pauseCtx->randoQuestMode) { - POLY_OPA_DISP = - KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->saveVtx, sSaveTexs[gSaveContext.language]); + POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->saveVtx, sGameOverTexs); RandoKaleido_DrawMiscCollectibles(play); } else { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->questPageVtx, @@ -1729,8 +1728,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); if (pauseCtx->randoQuestMode) { - POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->saveVtx, - sSaveTexs[gSaveContext.language]); + POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->saveVtx, sGameOverTexs); RandoKaleido_DrawMiscCollectibles(play); } else { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->questPageVtx, @@ -4773,8 +4771,9 @@ void KaleidoScope_Update(PlayState* play) { // Reset frame counter to prevent autosave on respawn play->gameplayFrames = 0; gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK; - gSaveContext.health = - CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : 0x30; + gSaveContext.health = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) + ? gSaveContext.healthCapacity + : STARTING_HEALTH; Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); gSaveContext.healthAccumulator = 0; gSaveContext.magicState = MAGIC_STATE_IDLE;