diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 8fd253b5d..a823e25bf 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 000000000..b2e0c933b --- /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 32b00057e..cab0aa7d4 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 000000000..ecf39ef31 --- /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 2f4f9e4d0..723a5f45e 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 20bdc16bc..2fc224152 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 1b03b6c77..6dc93bfe1 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 1bde4e50c..1d4a55f5d 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 8096f2075..23e0a1fd6 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 0ceafc9c1..ae49f45bf 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 000000000..5cee8c30d --- /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 000000000..e8b0d1270 --- /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 13c752a62..5a7809836 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 876ce1449..4a601f6b7 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 6d4474274..f1aa322ce 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 5b6d39ad9..b46db53c3 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 be6acae73..fa0727218 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 b01ffc5f0..c0ac6bbc1 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 146db2ddc..709bbb1d4 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 1a586a09c..c1f439f30 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 682a1271e..7c762ca66 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 44296118e..f779001c1 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 1ab72fe81..87c59c071 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 3a94c0748..f3ef7c2a3 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 793466c19..0e82ac7c3 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 83924aed1..928565338 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 2b9552263..5edf050f3 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 7776afb66..605bae226 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 c30017183..bea55f185 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 24710d5b2..9a0c6794b 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 16205424e..751f3b07b 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 c21a7a33e..8ec64078a 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 6d75dffb6..04b9e5474 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 15f75ab8d..bad0027bb 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 f29b726a9..aa5df35a0 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 7f09a6ba3..461a86a2e 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 022303aa3..9363ace39 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 ec46209fe..8f3182353 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 fe91c67b7..f0aa32984 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 e996ac6a0..5852e62e0 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 93c404617..9eb103230 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 3bc085413..8e5df3c8e 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 9a4622c29..c620ee30e 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 d0e74167c..47cd102d2 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 000000000..72eee3c5c --- /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 a4ee17b76..c05de0a23 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 7b3c180bb..3099da84a 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 d34970d4d..8b0cce20c 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 3c6a25dc7..f0d6e06c4 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 149eb6577..c32b11755 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 2da5b4d2b..0cc9debe8 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 b6bf5ecdf..28459ba5e 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 451bdf9ab..cd6de967e 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 efdf29627..7a59e4409 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 5eabe0e5d..0e9373b43 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 d45856324..9c6dbc182 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 023ee02df..a47b6facb 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 2e3ad7888..a1fe242c6 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 c57ddc5f7..7e21b8e99 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 712a9008d..129d94879 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 ca8513566..442a2dfc3 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 0328a97f9..7d747012c 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 a057ebbd5..7f15d3971 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 2eb5e15bb..b79da892f 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 14f749d43..1e53d8180 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;