add mirror chamber access setting

This commit is contained in:
gymnast86
2026-06-23 09:16:39 -07:00
parent e57c952deb
commit 6592871fa2
10 changed files with 148 additions and 23 deletions
+12
View File
@@ -12,6 +12,11 @@
#include "d/d_com_inf_game.h"
#include <cstring>
#if TARGET_PC
#include "dusk/randomizer/game/stages.h"
#include "dusk/randomizer/game/tools.h"
#endif
static int daObj_Gb_Draw(obj_gb_class* i_this) {
g_env_light.settingTevStruct(0x10, &i_this->current.pos, &i_this->tevStr);
g_env_light.setLightTevColorType_MAJI(i_this->mModel, &i_this->tevStr);
@@ -169,6 +174,13 @@ static int useHeapInit(fopAc_ac_c* actor) {
static int daObj_Gb_Create(fopAc_ac_c* actor) {
fopAcM_ct(actor, obj_gb_class);
obj_gb_class* i_this = (obj_gb_class*)actor;
#if TARGET_PC
// Only spawn the added wall in randomizer if it should exist
if (randomizer_IsActive() && getStageID() == StageIDs::Mirror_Chamber &&
!randomizer_mirrorChamberWallShouldExist()) {
return cPhs_ERROR_e;
}
#endif
int rv = dComIfG_resLoad(&i_this->mPhase, "Obj_gb");
if (rv == cPhs_COMPLEATE_e) {
+7
View File
@@ -2013,6 +2013,13 @@ stage_arrow_class* dComIfGp_getRoomArrow(int i_roomNo) {
void dComIfGp_setNextStage(char const* i_stage, s16 i_point, s8 i_roomNo, s8 i_layer,
f32 i_lastSpeed, u32 i_lastMode, int i_setPoint, s8 i_wipe,
s16 i_lastAngle, int param_9, int i_wipeSpeedT) {
#if TARGET_PC
// In rando, override this entrance if applicable
if (randomizer_IsActive()) {
randomizer_checkAndOverrideEntranceData(i_stage, i_roomNo, i_point, i_layer);
}
#endif
if (i_layer >= 15) {
i_layer = -1;
}
@@ -121,6 +121,10 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
textData << YAML::EndMap;
textData << YAML::EndMap;
for (const auto& [key, override] : mEntranceOverrides) {
out["mEntranceOverrides"][key] = std::bit_cast<int>(override);
}
seedData << YAML::Dump(out);
seedData << '\n' << textData.c_str();
seedData.close();
@@ -278,6 +282,13 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
}
}
// Entrance Overrides
for (const auto& entranceNode : in["mEntranceOverrides"]) {
auto key = entranceNode.first.as<int>();
auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<int>());
this->mEntranceOverrides[key] = override;
}
dusk::ui::push_toast(dusk::ui::Toast{
.title = "Randomizer",
.content = fmt::format("Loaded Randomizer Seed {}", this->mHash),
@@ -291,7 +302,7 @@ std::filesystem::path RandomizerContext::GetSeedDataPath() const {
}
int RandomizerContext::SettingToEnum(const std::string& settingName) {
static const std::unordered_map<std::string, int> nameToEnum = {
static const std::map<std::string, int> nameToEnum = {
{"Hyrule Barrier Dungeons", HYRULE_BARRIER_DUNGEONS},
{"Hyrule Barrier Requirements", HYRULE_BARRIER_REQUIREMENTS},
{"Hyrule Barrier Fused Shadows", HYRULE_BARRIER_FUSED_SHADOWS},
@@ -309,6 +320,7 @@ int RandomizerContext::SettingToEnum(const std::string& settingName) {
{"Skip Minor Cutscenes", SKIP_MINOR_CUTSCENES},
{"Skip Major Cutscenes", SKIP_MAJOR_CUTSCENES},
{"Skip Bridge Donation", SKIP_BRIDGE_DONATION},
{"Mirror Chamber Access", MIRROR_CHAMBER_ACCESS},
};
if (nameToEnum.contains(settingName)) {
@@ -319,7 +331,7 @@ int RandomizerContext::SettingToEnum(const std::string& settingName) {
}
int RandomizerContext::OptionToEnum(const std::string& optionName) {
static const std::unordered_map<std::string, int> nameToEnum = {
static const std::map<std::string, int> nameToEnum = {
{"On", ON},
{"Off", OFF},
{"None", NONE},
@@ -334,6 +346,8 @@ int RandomizerContext::OptionToEnum(const std::string& optionName) {
{"Ordon Sword", ORDON_SWORD},
{"Master Sword", MASTER_SWORD},
{"Light Sword", LIGHT_SWORD},
{"Closed", CLOSED},
{"Barrier", BARRIER},
};
if (nameToEnum.contains(optionName)) {
@@ -531,7 +545,7 @@ static void updateGoalFlags() {
}
}
// Palace of Twlight Access
// Palace of Twilight Access
if (!dComIfGs_isEventBit(FIXED_THE_MIRROR_OF_TWILIGHT)) {
bool openPalace = false;
switch (settings[RandomizerContext::PALACE_OF_TWILIGHT_REQUIREMENTS]) {
@@ -798,6 +812,21 @@ int randomizer_getItemAtLocation(const std::string& locationName) {
return randomizer_GetContext().mItemLocations[locationName].itemId;
}
void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, s16& pointNo, s8& mapLayer) {
RandomizerContext::EntranceOverride override = {
static_cast<u8>(getStageID(stageName)), roomNo, static_cast<s8>(pointNo), mapLayer
};
int key = std::bit_cast<int>(override);
if (randomizer_GetContext().mEntranceOverrides.contains(key)) {
auto& newOverride = randomizer_GetContext().mEntranceOverrides[key];
stageName = allStages[newOverride.stageId];
pointNo = newOverride.pointNo;
roomNo = newOverride.roomNo;
mapLayer = newOverride.mapLayer;
}
}
static void randomizer_setTempFlag(RandomizerContext::itemLocationData data) {
// If stage is 0xFF, then this is an event flag
if (data.stage == 0xFF) {
@@ -861,6 +890,12 @@ bool randomizer_checkTempleOfTimeRequirement() {
return false;
}
bool randomizer_mirrorChamberWallShouldExist() {
auto mirrorChamberAccess = randomizer_GetContext().mSettings[RandomizerContext::MIRROR_CHAMBER_ACCESS];
return mirrorChamberAccess == RandomizerContext::CLOSED ||
(mirrorChamberAccess == RandomizerContext::BARRIER && !dComIfGs_isStageBossEnemy(0x13));
}
u8 randomizer_getRandomFoolishItemModelID() {
static constexpr auto foolishItemModels = std::to_array<u8>({
dItemNo_Randomizer_ARMOR_e,
@@ -1204,7 +1239,7 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
const auto& stageName = stageNode.first.as<std::string>();
for (const auto& roomNode : stageNode.second) {
u8 roomNo{};
// Special value for
// Special value for actors always on the stage and not just one specific room
if (roomNode.first.as<std::string>() == "Stage") {
roomNo = RandomizerContext::ROOM_STAGE;
} else {
@@ -1316,6 +1351,27 @@ 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 = {
StageIDs::Mirror_Chamber,
4,
0,
-1
};
RandomizerContext::EntranceOverride override = {
StageIDs::Bulblin_Camp,
3,
3,
-1
};
randoData.mEntranceOverrides[std::bit_cast<int>(original)] = override;
}
return std::move(randoData);
}
@@ -70,14 +70,15 @@ public:
// Map of language -> map of key -> string
std::unordered_map<int, std::unordered_map<u32, std::string>> mTextOverrides{};
// TODO: hook this up to generator data
struct {
// for now use hardcoded values for this
std::string mapName = "F_SP103"; // (Ordon) Outside Link's House
int pointNo = 1;
int roomNo = 1;
int mapLayer = -1;
} mStartLocation;
struct EntranceOverride {
u8 stageId = 0xFF;
s8 roomNo = -1;
s8 pointNo = -1;
s8 mapLayer = -1;
};
// keyed by stageId << 24 | pointNo << 16 | roomNo << 8 | mapLayer
std::unordered_map<int, EntranceOverride> mEntranceOverrides{};
std::optional<std::string> WriteToFile();
std::optional<std::string> LoadFromHash(const std::string& hash);
@@ -101,6 +102,7 @@ public:
SKIP_MINOR_CUTSCENES,
SKIP_MAJOR_CUTSCENES,
SKIP_BRIDGE_DONATION,
MIRROR_CHAMBER_ACCESS,
};
enum Options {
@@ -118,6 +120,8 @@ public:
ORDON_SWORD,
MASTER_SWORD,
LIGHT_SWORD,
BARRIER,
CLOSED,
};
static int SettingToEnum(const std::string& settingName);
@@ -206,6 +210,10 @@ bool randomizer_IsActive();
int randomizer_getItemAtLocation(const std::string& locationName);
/*
* @brief Overrides the given entrance paramaters if an override exists for them
*/
void randomizer_checkAndOverrideEntranceData(const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer);
/*
* @brief Puts the associated flag into the randomizer state's temporary flag
* variable. This allows the tracker/Archipelago to know a location has been checked
@@ -217,6 +225,8 @@ void randomizer_setTempFlagForFLWOverride(u32 key);
bool randomizer_checkTempleOfTimeRequirement();
bool randomizer_mirrorChamberWallShouldExist();
u8 randomizer_getRandomFoolishItemModelID();
/**
@@ -5,9 +5,9 @@
- Type: Spawn
Forward:
Connection: Links Spawn -> Outside Links House
Stage: -1
Room: -1
Spawn: ""
Stage: 43
Room: 1
Spawn: "01"
Spawn Type: ""
Parameters: ""
State: "FF"
@@ -449,11 +449,11 @@
Return:
Connection: Arbiters Grounds Boss Room -> Mirror Chamber Lower
Alias: Arbiters Grounds Boss Room -> Mirror Chamber
Stage: -1
Room: -1
Spawn: ""
Spawn Type: ""
Parameters: ""
Stage: 60
Room: 4
Spawn: "00"
Spawn Type: "10"
Parameters: "501F"
State: "FF"
- Type: Boss
@@ -3470,6 +3470,28 @@ F_SP124:
layers:
- 0
# Mirror Chamber
F_SP125:
# Room 4 - Main Chamber
4:
# Add barrier to prevent players from going back to the Arbiters Boss Room
# depending on settings
- action: add
name: Obj_gb
parameters: 0x800F0601
position:
x: 1794.0
y: 2523.0
z: -17400.0
angle:
x: 0xFF7F
y: 0x0000
z: 0x0000
set id: 0xFFFF
layers:
- 0
- 1
# Upper Zora's River
F_SP126:
# Room 0 - Main area
@@ -79,6 +79,15 @@
- Closed: "Midna will block the player from leaving Faron Woods until Forest Temple is completed."
- Open: "Midna will not prevent the player from leaving Faron Woods."
- Name: Mirror Chamber Access
Need In Game: True
Tracker Important: True
Default Option: Open
Options:
- Open: "The entrance is open and operates like normal. If you start with the Mirror Chamber Portal, you can access the Stallord boss fight without going through Arbiter's Grounds."
- Barrier: "A barrier is placed in front of the Mirror Chamber entrance and goes away once Stallord is defeated."
- Closed: "The Mirror Chamber is isolated from the world and cannot be reached from the Stallord boss room. To access it, players will either need the portal or access from Palace of Twilight."
######################
## Item Pool ##
######################
@@ -126,7 +126,7 @@
Can Warp: True
Exits:
Mirror Chamber Upper: Nothing
Arbiters Grounds Boss Room: Nothing
Arbiters Grounds Boss Room: Mirror_Chamber_Access == Open or (Mirror_Chamber_Access == Barrier and 'Can_Complete_Arbiters_Grounds')
- Name: Mirror Chamber Upper
Map Sector: Desert Province
@@ -423,6 +423,15 @@ namespace randomizer::logic::item_pool
startingItems["Castle Town Portal"] = 1;
}
// Automatically give players the Mirror Chamber Portal if Mirror Chamber Access is closed
// and they aren't both randomizing and decoupling dungeon entrances. Otherwise, there's no
// way to access the chamber
if (world->Setting("Mirror Chamber Access") == "Closed" &&
!(world->Setting("Randomize Dungeon Entrances") == "On" && world->Setting("Decouple Entrances") == "On"))
{
startingItems["Mirror Chamber Portal"] = 1;
}
// Add each item to the world's _startingItemPool and erase it from the regular _itemPool
for (const auto& [itemName, count] : startingItems)
{
+2 -2
View File
@@ -926,6 +926,7 @@ RandomizerWindow::RandomizerWindow(dFile_select_c* fileSelect /*= nullptr*/) : m
});
rando_config_group(leftPane, rightPane, "Palace of Twilight Requirements");
rando_config_group(leftPane, rightPane, "Faron Woods Logic");
rando_config_group(leftPane, rightPane, "Mirror Chamber Access");
// leftPane.add_section("World (TODO)");
@@ -1220,8 +1221,7 @@ RandomizerWindow::RandomizerWindow(dFile_select_c* fileSelect /*= nullptr*/) : m
leftPane.register_control(leftPane.add_button("Warp to Start").on_pressed([] {
mDoAud_seStartMenu(kSoundClick);
auto& locData = randomizer_GetContext().mStartLocation;
dComIfGp_setNextStage(locData.mapName.c_str(), locData.pointNo, locData.roomNo, locData.mapLayer);
dComIfGp_setNextStage("F_SP103", 1, 1, -1);
}), rightPane, [](Pane& pane) {
pane.clear();
pane.add_rml("Respawns the player at their appropriate starting location.");