diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 77a25cd9c..fa6df1cc7 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -415,7 +415,7 @@ typedef enum { #define EVENTCHKINF_65 0x65 #define EVENTCHKINF_67 0x67 #define EVENTCHKINF_68 0x68 -#define EVENTCHKINF_69 0x69 +#define EVENTCHKINF_RAISED_LAKE_HYLIA_WATER 0x69 #define EVENTCHKINF_TALON_WOKEN_IN_KAKARIKO 0x6A // 0x6B diff --git a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h index 894dcb91f..659b5fbb3 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h @@ -36,6 +36,8 @@ typedef enum { TEXT_WARP_NOCTURNE_OF_SHADOW = 0x891, TEXT_WARP_PRELUDE_OF_LIGHT = 0x892, TEXT_WARP_RANDOM_REPLACED_TEXT = 0x9200, + TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range + TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range } TextIDs; #ifdef __cplusplus diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 341c99031..f45944f7d 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -344,6 +344,19 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { "Warp to&{{location}}?\x1B&%gOK&No%w\x02", "Warp to&{{location}}?\x1B&%gOK&No%w\x02", // TODO: German translation "Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02" }); + + CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN, + { TEXTBOX_TYPE_WOODEN, TEXTBOX_POS_BOTTOM, + "Water level control system.&Keep away!", + "Wasserstand Kontrollsystem&Finger weg!", + "Contrôle du niveau de l'eau.&Ne pas toucher!" + }); + CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI, + { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, + "This switch is rustier than you think.^Something must be wrong with the&pipe system in the Water Temple.", + "Dieser Schalter scheint rostiger zu&sein als er aussieht.^Etwas muss mit dem Leitungssystem&im Wassertempel nicht stimmen.", + "Cet interrupteur est très rouillé.^Quelque chose ne va pas avec&la tuyauterie du Temple de l'Eau." + }); } // Reference soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.h diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index b9ee30e92..8d963d46b 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2016,6 +2016,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { } else if (Randomizer_GetSettingValue(RSK_SHUFFLE_WARP_SONGS) && (textId >= TEXT_WARP_MINUET_OF_FOREST && textId <= TEXT_WARP_PRELUDE_OF_LIGHT)) { messageEntry = OTRGlobals::Instance->gRandomizer->GetWarpSongMessage(textId, false); + } else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) { + messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, textId); } } if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) { diff --git a/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.c b/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.c index 11b8a0afa..c9e37943e 100644 --- a/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.c +++ b/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.c @@ -6,6 +6,7 @@ #include "z_bg_spot06_objects.h" #include "objects/object_spot06_objects/object_spot06_objects.h" +#include "soh/Enhancements/custom-message/CustomMessageTypes.h" #define FLAGS ACTOR_FLAG_9 @@ -43,6 +44,7 @@ void BgSpot06Objects_LockSwimToSurface(BgSpot06Objects* this, PlayState* play); void BgSpot06Objects_LockFloat(BgSpot06Objects* this, PlayState* play); void BgSpot06Objects_WaterPlaneCutsceneWait(BgSpot06Objects* this, PlayState* play); void BgSpot06Objects_WaterPlaneCutsceneRise(BgSpot06Objects* this, PlayState* play); +void BgSpot06Objects_WaterPlaneCutsceneLower(BgSpot06Objects* this, PlayState* play); const ActorInit Bg_Spot06_Objects_InitVars = { ACTOR_BG_SPOT06_OBJECTS, @@ -150,9 +152,7 @@ void BgSpot06Objects_Init(Actor* thisx, PlayState* play) { Actor_ProcessInitChain(thisx, sInitChainWaterPlane); thisx->flags = ACTOR_FLAG_4 | ACTOR_FLAG_5; - if (LINK_IS_ADULT && - ((!gSaveContext.n64ddFlag && !(gSaveContext.eventChkInf[6] & 0x200)) || - (gSaveContext.n64ddFlag && !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE)))) { + if (LINK_IS_ADULT && !(Flags_GetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER))) { if (gSaveContext.sceneSetupIndex < 4) { this->lakeHyliaWaterLevel = -681.0f; play->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].ySurface = @@ -189,6 +189,12 @@ void BgSpot06Objects_Init(Actor* thisx, PlayState* play) { } } +static u8 actionCounter = 0; // Used to perform some actions on subsequent frames +static s8 waterMovement = 0; // Used to control the water change direction +static u8 switchPressed = 0; // Used to track when the water fill switch is pressed/depressed +static u8 prevSwitchState = 0; // Used to track the previous state of the water fill switch +static Actor* lakeControlFloorSwitch; + void BgSpot06Objects_Destroy(Actor* thisx, PlayState* play) { BgSpot06Objects* this = (BgSpot06Objects*)thisx; @@ -203,6 +209,17 @@ void BgSpot06Objects_Destroy(Actor* thisx, PlayState* play) { case LHO_WATER_PLANE: break; } + + if (gSaveContext.n64ddFlag && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE)) { + // For randomizer when leaving lake hylia while the water level is lowered, + // reset the "raise lake hylia water" flag back to on if the water temple is cleared + Flags_SetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER); + } + + actionCounter = 0; + waterMovement = 0; + switchPressed = 0; + prevSwitchState = 0; } /** @@ -425,6 +442,71 @@ void BgSpot06Objects_Update(Actor* thisx, PlayState* play) { if (thisx->params == LHO_WATER_TEMPLE_ENTRANCE_LOCK) { CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); } + + // Bail early for water control system for child or non-rando + if (LINK_IS_CHILD || !gSaveContext.n64ddFlag) { + return; + } + + // Begin setup for Lake Hylia water control system + if (actionCounter == 0) { + // Object containing floor switch data (and ice block data) + Object_Spawn(&play->objectCtx, OBJECT_GAMEPLAY_DANGEON_KEEP); + + s16 switchParams; + if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE)) { + // Toggle-able floor switch, + // linked to temp_switch 0x1E (room temporary, cleared when room unloads) + switchParams = 0x3E10; + } else { + // Frozen rusty switch, same flag as above. It's glitched and can't be pressed + switchParams = 0x3E81; + } + + // Spawn a floor switch + lakeControlFloorSwitch = Actor_Spawn(&play->actorCtx, play, ACTOR_OBJ_SWITCH, -896.0f, -1243.0f, 6953.0f, 0, 0, 0, switchParams, false); + // Spawn a sign + Actor_Spawn(&play->actorCtx, play, ACTOR_EN_KANBAN, -970.0f, -1242.0f, 6954.0f, 0, 0, 0, + 0x0000 | (TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN & 0xFF), false); + + // Spawn a Navi check spot when Water Temple isn't cleared + if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE)) { + Actor_Spawn(&play->actorCtx, play, ACTOR_ELF_MSG2, -896.0f, -1243.0f, 6953.0f, 0, 0, 0, + 0x3D00 | (TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI & 0xFF), false); + } + + actionCounter++; + return; + } else if (actionCounter == 1) { + if (!Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE)) { + // Remove the link to ice block so melting it doesn't set the flag + lakeControlFloorSwitch->params = 0x3E01; + } + + actionCounter++; + return; + } + + // Detect when the switch is pressed + if (prevSwitchState != (Flags_GetSwitch(play, 0x3E) != 0)) { + prevSwitchState = !prevSwitchState; + switchPressed = 1; + } + + // When pressed, assign the corresponding action func to the water plane and water movement direction + if (switchPressed == 1 && thisx->params == LHO_WATER_PLANE) { + // Lower water + if (waterMovement >= 0) { + waterMovement = -1; + this->actionFunc = BgSpot06Objects_WaterPlaneCutsceneLower; + // Raise water + } else { + waterMovement = 1; + this->actionFunc = BgSpot06Objects_WaterPlaneCutsceneRise; + } + + switchPressed = 0; + } } /** @@ -489,7 +571,7 @@ void BgSpot06Objects_Draw(Actor* thisx, PlayState* play) { * cleared. */ void BgSpot06Objects_WaterPlaneCutsceneWait(BgSpot06Objects* this, PlayState* play) { - if (gSaveContext.eventChkInf[6] & 0x200) { + if (Flags_GetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER)) { this->actionFunc = BgSpot06Objects_WaterPlaneCutsceneRise; } } @@ -505,6 +587,13 @@ void BgSpot06Objects_WaterPlaneCutsceneRise(BgSpot06Objects* this, PlayState* pl if (this->lakeHyliaWaterLevel >= 0.0001f) { this->dyna.actor.world.pos.y = WATER_LEVEL_RAISED; this->actionFunc = BgSpot06Objects_DoNothing; + + // On rando, this is used with the water control system switch to finalize raising the water + if (gSaveContext.n64ddFlag) { + this->lakeHyliaWaterLevel = 0; + Flags_SetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER); // Set the "raise lake hylia water" flag + play->roomCtx.unk_74[0] = 0; // Apply the moving under water texture to lake hylia ground + } } else { Math_SmoothStepToF(&this->lakeHyliaWaterLevel, 1.0f, 0.1f, 1.0f, 0.001f); play->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].ySurface = WATER_LEVEL_RIVER_LOWERED; @@ -514,3 +603,32 @@ void BgSpot06Objects_WaterPlaneCutsceneRise(BgSpot06Objects* this, PlayState* pl func_8002F948(&this->dyna.actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); } + +/** + * Custom action func to lower the Laker Hylia water plane from a switch. + */ +void BgSpot06Objects_WaterPlaneCutsceneLower(BgSpot06Objects* this, PlayState* play) { + f32 yPos = this->dyna.actor.world.pos.y = this->lakeHyliaWaterLevel + WATER_LEVEL_RAISED; + + // A slightly smaller number thatn -680 (which is when textures change) + // Then we change the position since the "low water" texture has a different height + if (this->lakeHyliaWaterLevel <= -679.9f) { + this->dyna.actor.world.pos.y = (this->lakeHyliaWaterLevel + 680.0f) + WATER_LEVEL_RAISED; + } + + gSaveContext.eventChkInf[6] &= ~0x200; // Unset the "raised lake hylia water" flag + play->roomCtx.unk_74[0] = 87; // Remove the moving under water texture from lake hylia ground + + if (this->lakeHyliaWaterLevel <= -681.0f) { + this->dyna.actor.world.pos.y = WATER_LEVEL_RAISED; + this->actionFunc = BgSpot06Objects_DoNothing; + } else { + // Go slightly beyond -681 so the smoothing doesn't slow down too much (matches the reverse of water rise func) + Math_SmoothStepToF(&this->lakeHyliaWaterLevel, -682.0f, 0.1f, 1.0f, 0.001f); + play->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].ySurface = WATER_LEVEL_RIVER_LOWERED; + play->colCtx.colHeader->waterBoxes[LHWB_MAIN_1].ySurface = yPos; + play->colCtx.colHeader->waterBoxes[LHWB_MAIN_2].ySurface = yPos; + } + + func_8002F948(&this->dyna.actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); +}