From 33a9db2e491e1042d1ae828113c9f434cba18c84 Mon Sep 17 00:00:00 2001 From: jdflyer Date: Fri, 14 Aug 2026 18:26:37 -0700 Subject: [PATCH] Finish Implementing Entrance Rando --- .../generator/data/entrance_shuffle_data.yaml | 2 +- mods/randomizer/generator/logic/entrance.hpp | 5 ++ .../generator/logic/entrance_shuffle.cpp | 2 +- mods/randomizer/src/hooks.cpp | 18 ++++++ mods/randomizer/src/randomizer_context.cpp | 57 ++++++++++-------- mods/randomizer/src/randomizer_context.hpp | 3 +- mods/randomizer/src/session.cpp | 60 ++++++++++++++++++- mods/randomizer/src/session.hpp | 3 +- 8 files changed, 118 insertions(+), 32 deletions(-) diff --git a/mods/randomizer/generator/data/entrance_shuffle_data.yaml b/mods/randomizer/generator/data/entrance_shuffle_data.yaml index 4634003d31..d967cc0e48 100644 --- a/mods/randomizer/generator/data/entrance_shuffle_data.yaml +++ b/mods/randomizer/generator/data/entrance_shuffle_data.yaml @@ -1222,7 +1222,7 @@ Forward: Connection: Kakariko Renados Sanctuary -> Kakariko Renados Sanctuary Basement Stage: 75 - Room: 5 + Room: 7 Spawn: "00" Spawn Type: "FF" Parameters: "FFFF" diff --git a/mods/randomizer/generator/logic/entrance.hpp b/mods/randomizer/generator/logic/entrance.hpp index d9d27695f1..78c1df9bf2 100644 --- a/mods/randomizer/generator/logic/entrance.hpp +++ b/mods/randomizer/generator/logic/entrance.hpp @@ -159,6 +159,8 @@ namespace randomizer::logic::entrance int8_t GetLayerNo() const { return _layerNo; } int16_t GetPointNo() const { return _pointNo; } bool SetGameInfo(const YAML::Node& node); + void SetCoupledDoor(int16_t entrance) { _coupledEntrance = entrance; } + int16_t getCoupledDoor() { return _coupledEntrance; } private: int _id = -1; @@ -219,6 +221,9 @@ namespace randomizer::logic::entrance // to this one. So if the entrance is North Faron Woods -> Forest Temple Entrance, // then _assumed is the target entrance Root -> Forest Temple Entrance Entrance* _assumed = nullptr; + + // If the entrance is a coupled door, this is the other door's point to override + int16_t _coupledEntrance = -1; }; using EntrancePool = std::vector; diff --git a/mods/randomizer/generator/logic/entrance_shuffle.cpp b/mods/randomizer/generator/logic/entrance_shuffle.cpp index 7dfc5dee54..d042f2d264 100644 --- a/mods/randomizer/generator/logic/entrance_shuffle.cpp +++ b/mods/randomizer/generator/logic/entrance_shuffle.cpp @@ -114,7 +114,7 @@ namespace randomizer::logic::entrance_shuffle [&](const auto& door) { return door->IsPrimary() == mainDoor->IsPrimary(); }); auto coupledDoor = *coupledDoorItr; - // TODO: Add the coupled door's info to the main door + mainDoor->SetCoupledDoor(coupledDoor->GetPointNo()); // Completely remove the coupled door from the world graph doors.erase(coupledDoorItr); diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 6bc2a2ebe8..ff15469e6e 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -33,6 +33,9 @@ #include "d/d_msg_object.h" #include "d/d_save.h" #include "d/d_shop_system.h" +#include "d/d_s_name.h" +#include "f_op/f_op_overlap_mng.h" +#include "m_Do/m_Do_Reset.h" #include "c/c_damagereaction.h" DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove); @@ -119,6 +122,8 @@ DEFINE_HOOK_SYMBOL("daE_MD_Create", int(fopAc_ac_c*), daE_MD_c__create); DEFINE_HOOK(&daObjMasterSword_c::executeWait, daObjMasterSword_c__executeWait); DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMasterSword_c__execute); +DEFINE_HOOK(&dScnName_c::changeGameScene, dScnName_c__changeGameScene); + namespace randomizer::ui { dialogSelectModeState g_dialogSelectModeState = SelectReady; } @@ -1525,6 +1530,9 @@ HookAction hookPreNpcFOrderEvent(ModContext*, void* args, void* retval, void*) { u16 i_priority = mods::arg(args, 4); // kinda hacky way to check for the state where Bo is trying to talk after getting Iron Boots + if (i_this->eventInfo.getArchiveName() == nullptr) { + return HOOK_CONTINUE; + } const std::string arcName = i_this->eventInfo.getArchiveName(); if (arcName == "Bou4" && i_priority == 40) { i_forceSpeak = FALSE; @@ -1698,6 +1706,12 @@ HookAction hookPreMasterSwordExecute(ModContext*, void* args, void* retval, void return HOOK_CONTINUE; } +void hookPost_dScnName_c__changeGameScene(ModContext* ctx, void* args, void* retval, void* userdata) { + if (!mDoRst::isReset() && !fopOvlpM_IsPeek()) { + randomizer::session::registerStartingLocation(); + } +} + } ModResult initialize() { @@ -1798,6 +1812,8 @@ ModResult initialize() { ADD_HOOK_REPLACE(daObjMasterSword_c__executeWait, hookReplaceMasterSwordExecuteWait); ADD_HOOK_PRE(daObjMasterSword_c__execute, hookPreMasterSwordExecute); + ADD_HOOK_POST(dScnName_c__changeGameScene, hookPost_dScnName_c__changeGameScene); + return MOD_OK; } @@ -1880,6 +1896,8 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(); + return MOD_OK; } } diff --git a/mods/randomizer/src/randomizer_context.cpp b/mods/randomizer/src/randomizer_context.cpp index 84c775e623..55edcd5ab9 100644 --- a/mods/randomizer/src/randomizer_context.cpp +++ b/mods/randomizer/src/randomizer_context.cpp @@ -867,17 +867,17 @@ void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, // Debugging: Dump mEntranceOverrides // for (const auto& [key,val] : randomizer_GetContext().mEntranceOverrides) { - // mods::log::info("({},{},{},{}) -> ({},{},{},{})",key.stageId,key.roomNo,key.mapLayer,key.pointNo,val.stageId,val.roomNo,val.mapLayer,val.pointNo); + // mods::log::debug("({},{},{},{}) -> ({},{},{},{})",key.stageId,key.roomNo,key.mapLayer,key.pointNo,val.stageId,val.roomNo,val.mapLayer,val.pointNo); // } + // mods::log::debug("Original Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); if (randomizer_GetContext().mEntranceOverrides.contains(override)) { auto& newOverride = randomizer_GetContext().mEntranceOverrides[override]; - mods::log::debug("Original Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); stageName = allStages[newOverride.stageId]; pointNo = newOverride.pointNo; roomNo = newOverride.roomNo; mapLayer = newOverride.mapLayer; - mods::log::debug("New Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); + // mods::log::debug("New Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer); } } @@ -1729,27 +1729,6 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { } } - // Entrance Overrides - if (world->Setting("Mirror Chamber Access") == "Closed") { - // Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance - // if mirror chamber access is closed - RandomizerContext::EntranceOverride original = { - .stageId = StageIDs::Mirror_Chamber, - .roomNo = 4, - .mapLayer = -1, - .pointNo = 0, - }; - - RandomizerContext::EntranceOverride override = { - .stageId = StageIDs::Bulblin_Camp, - .roomNo = 3, - .mapLayer = -1, - .pointNo = 3, - }; - - randoData.mEntranceOverrides[original] = override; - } - // Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented static const std::vector, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{ {{Forest_Temple, Ook, Diababa}, {.stageId = Forest_Temple, .roomNo = 22, .mapLayer = -1, .pointNo = 0}}, @@ -1781,10 +1760,38 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) { if (forward.stageId == 0xFF || replaces.stageId == 0xFF || forward.roomNo == -1 || replaces.roomNo == -1) { continue; } - randoData.mEntranceOverrides[replaces] = forward; + randoData.mEntranceOverrides[forward] = replaces; + + // Set an override for the second door, if the entrance is coupled + if (entrance->getCoupledDoor() != -1) { + RandomizerContext::EntranceOverride coupled = {.stageId = entrance->GetStageId(), .roomNo = entrance->GetRoomNo(), .mapLayer = entrance->GetLayerNo(), .pointNo = entrance->getCoupledDoor()}; + randoData.mEntranceOverrides[coupled] = replaces; + } } } + if (world->Setting("Mirror Chamber Access") == "Closed") { + // Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance + // if mirror chamber access is closed + RandomizerContext::EntranceOverride original = { + .stageId = StageIDs::Mirror_Chamber, + .roomNo = 4, + .mapLayer = -1, + .pointNo = 0, + }; + + RandomizerContext::EntranceOverride override = { + .stageId = StageIDs::Bulblin_Camp, + .roomNo = 3, + .mapLayer = -1, + .pointNo = 3, + }; + + // Check if we are already overriding the bulblin camp entrance, and correctly override the entrance + const auto& it = randoData.mEntranceOverrides.find(override); + randoData.mEntranceOverrides[original] = (it != randoData.mEntranceOverrides.end()) ? it->second : override; + } + return std::move(randoData); } diff --git a/mods/randomizer/src/randomizer_context.hpp b/mods/randomizer/src/randomizer_context.hpp index 75bc918ec3..7e789a500c 100644 --- a/mods/randomizer/src/randomizer_context.hpp +++ b/mods/randomizer/src/randomizer_context.hpp @@ -85,8 +85,7 @@ public: // Packs an EntranceOverride into a uint64_t struct EntranceOverrideHash { size_t operator()(const EntranceOverride& x) const noexcept { - return std::hash{}( - ((uint64_t)x.stageId << 32) | (x.roomNo << 24) | (x.mapLayer << 16) | x.pointNo); + return std::hash{}(std::bit_cast(x)); } }; diff --git a/mods/randomizer/src/session.cpp b/mods/randomizer/src/session.cpp index f4bec82ba6..dc5c74218f 100644 --- a/mods/randomizer/src/session.cpp +++ b/mods/randomizer/src/session.cpp @@ -135,7 +135,7 @@ bool activateSeed(const char* hash) { svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver); svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer); - registerStageEdits(); + registerStageEdits(); mods::log::info("activated seed {}", ctx.mHash); return true; } @@ -292,6 +292,62 @@ void registerStageEdits() { } } +// Set link to spawn outside of his house if we are playing with entrance rando and aren't already spawning in a dungeon +void registerStartingLocation() { + auto& ctx = randomizer_GetContext(); + + if (ctx.mEntranceOverrides.empty()) { + return; + } + + // Check that we aren't playing only with the setting "Mirror Chamber Access") == "Closed" + bool isExclusivelyMirrorChamber = false; + const auto& mirrorChamberIt = ctx.mEntranceOverrides.find(RandomizerContext::EntranceOverride{ + .stageId = StageIDs::Mirror_Chamber, + .roomNo = 4, + .mapLayer = -1, + .pointNo = 0 + }); + if (ctx.mEntranceOverrides.size() == 1 && mirrorChamberIt != ctx.mEntranceOverrides.end()) { + if (mirrorChamberIt->second == RandomizerContext::EntranceOverride{ + .stageId = StageIDs::Bulblin_Camp, + .roomNo = 3, + .mapLayer = -1, + .pointNo = 3, + }) { + isExclusivelyMirrorChamber = true; + } + } + + if (isExclusivelyMirrorChamber) { + return; + } + + constexpr std::array kDungeonNames = { + "D_MN01","D_MN04","D_MN05","D_MN06","D_MN07","D_MN08","D_MN09","D_MN10","D_MN11" + }; + + dSv_player_return_place_c& returnPlace = dComIfGs_getSaveData()->mPlayer.getPlayerReturnPlace(); + bool isDungeon = false; + for (const auto& name : kDungeonNames) { + if (returnPlace.getName() == name) { + isDungeon = true; + break; + } + } + + if (isDungeon) { + // Force-set the entrance without the override + g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set(returnPlace.getName(), returnPlace.getRoomNo(), returnPlace.mPlayerStatus, -1); + return; + } + + // Set the spawn to outside of link's house, which will get overrided later if we are replacing it + returnPlace.set("F_SP103",1,1); + // g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set("F_SP103", 1, 1, -1); + dComIfGp_setNextStage("F_SP103", 1, 1, -1); +} + void onNewSave(ModContext*, uint32_t, void*) { const std::string hash = g_pending_seed_hash; if (hash.empty()) @@ -351,4 +407,4 @@ void update() { g_randomizerState.execute(); } -} \ No newline at end of file +} diff --git a/mods/randomizer/src/session.hpp b/mods/randomizer/src/session.hpp index ebe9ce0516..002bba97d3 100644 --- a/mods/randomizer/src/session.hpp +++ b/mods/randomizer/src/session.hpp @@ -35,4 +35,5 @@ void update(); void deactivateSeed(); void setupRandomizerFile(); void registerStageEdits(); -} \ No newline at end of file +void registerStartingLocation(); +}