Finish Implementing Entrance Rando

This commit is contained in:
jdflyer
2026-08-14 18:26:37 -07:00
parent e5a4323056
commit 33a9db2e49
8 changed files with 118 additions and 32 deletions
@@ -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"
@@ -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<Entrance*>;
@@ -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);
+18
View File
@@ -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<u16>(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<daObjMasterSword_c__executeWait>(svc_hook);
mods::hook::uninstall<daObjMasterSword_c__execute>(svc_hook);
mods::hook::uninstall<dScnName_c__changeGameScene>();
return MOD_OK;
}
}
+32 -25
View File
@@ -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<std::pair<std::vector<int>, 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);
}
+1 -2
View File
@@ -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>{}(
((uint64_t)x.stageId << 32) | (x.roomNo << 24) | (x.mapLayer << 16) | x.pointNo);
return std::hash<uint64_t>{}(std::bit_cast<uint64_t>(x));
}
};
+58 -2
View File
@@ -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<std::string_view,9> 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();
}
}
}
+2 -1
View File
@@ -35,4 +35,5 @@ void update();
void deactivateSeed();
void setupRandomizerFile();
void registerStageEdits();
}
void registerStartingLocation();
}