From 8e3306f87f25f7c5e923c47a5faea924b67ec2af Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Sat, 25 Apr 2026 21:43:32 -0700 Subject: [PATCH] add master sword actor to grove layer 2 --- src/d/d_stage.cpp | 25 +++++++ .../randomizer/game/randomizer_context.cpp | 68 +++++++++++++++++++ .../randomizer/game/randomizer_context.hpp | 1 + .../generator/data/actor_additions.yaml | 23 +++++++ .../generator/data/actor_patches.yaml | 2 +- 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/dusk/randomizer/generator/data/actor_additions.yaml diff --git a/src/d/d_stage.cpp b/src/d/d_stage.cpp index 13b23c497c..dcb31d5976 100644 --- a/src/d/d_stage.cpp +++ b/src/d/d_stage.cpp @@ -1991,6 +1991,31 @@ static int dStage_actorCommonLayerInit(dStage_dt_c* i_stage, void* i_data, int e actor_data++; } +#if TARGET_PC + // For randomizer, add in our custom actors + if (randomizer_IsActive()) { + u32 type = static_cast(i_data)->m_tag; + u32 stageRoomLayer = getActorPatchesCurrentStageKey(i_stage->getRoomNo()); + const auto& actorAdditions = randomizer_GetContext().mActorAdditions; + if (actorAdditions.contains(type) && actorAdditions.at(type).contains(stageRoomLayer)) { + auto& newActors = actorAdditions.at(type).at(stageRoomLayer); + for (const auto& actorData : newActors) { + stage_actor_data_class actor{}; + std::memcpy(&actor, actorData.data(), actorData.size()); + actor.base.setID = 0xFFFF; + // Code copied from base game for loop above + fopAcM_prm_class* appen = fopAcM_CreateAppend(); + + if (appen != NULL) { + appen->base = actor.base; + appen->room_no = (int)i_stage->getRoomNo(); + dStage_actorCreate(&actor, appen); + } + } + } + } +#endif + return 1; } diff --git a/src/dusk/randomizer/game/randomizer_context.cpp b/src/dusk/randomizer/game/randomizer_context.cpp index b35c42c8a5..06ba6ae901 100644 --- a/src/dusk/randomizer/game/randomizer_context.cpp +++ b/src/dusk/randomizer/game/randomizer_context.cpp @@ -78,6 +78,14 @@ std::optional RandomizerContext::WriteToFile() { } } + for (const auto& [actorType, stages] : this->mActorAdditions) { + for (const auto& [stageRoomLayer, newActors] : stages) { + for (const auto& actor : newActors) { + out["mActorAdditions"][actorType][stageRoomLayer].push_back(ContainerToHexString(actor)); + } + } + } + seedData << YAML::Dump(out); seedData.close(); @@ -187,6 +195,19 @@ std::optional RandomizerContext::LoadFromHash(const std::string& ha } } + // Actor Additions + for (const auto& typeNode: in["mActorAdditions"]) { + u32 type = typeNode.first.as(); + for (const auto& stageNode : typeNode.second) { + u32 stageRoomLayer = stageNode.first.as(); + for (const auto& actorNode : stageNode.second) { + auto actorBytes = HexToBytes(actorNode.as()); + auto& patchedActor = this->mActorAdditions[type][stageRoomLayer].emplace_back(); + std::copy_n(actorBytes.begin(), actorBytes.size(), patchedActor.begin()); + } + } + } + DuskLog.debug("Loaded Randomizer Seed {}", this->mHash); return std::nullopt; @@ -753,6 +774,53 @@ void GenerateAndWriteSeed(std::string& generationStatusMsg) { } } + // Actor Additions + auto actorAdditions = LoadYAML(RANDO_DATA_PATH "actor_additions.yaml"); + for (const auto& typeNode : actorAdditions) { + const auto& actorTypeStr = typeNode.first.as(); + // Get the integer interpretation of the multi-char type literal + u32 actorType = *(reinterpret_cast(actorTypeStr.c_str())); + // For each stage + for (const auto& stageNode : typeNode.second) { + const auto& stageName = stageNode.first.as(); + // For each room + for (const auto& roomNode : stageNode.second) { + u8 roomNo = roomNode.first.as(); + // Get data on new actors + for (const auto& actorNode : roomNode.second) { + using namespace Utility::Endian; + // Get all the data for the actor (with endian shenanigans) + stage_actor_data_class actor{}; + const auto& actorName = actorNode["name"].as(); + strncpy(actor.name, actorName.c_str(), 8); + actor.base.parameters = toPlatform(target, actorNode["parameters"].as()); + actor.base.position.x = toPlatform(target, actorNode["position"]["x"].as()); + actor.base.position.y = toPlatform(target, actorNode["position"]["y"].as()); + actor.base.position.z = toPlatform(target, actorNode["position"]["z"].as()); + // Have to retrieve as u16 and then cast as s16 because otherwise yaml-cpp + // complains about values over 32767 not fitting in s16 + actor.base.angle.x = toPlatform(target, static_cast(actorNode["angle"]["x"].as())); + actor.base.angle.y = toPlatform(target, static_cast(actorNode["angle"]["y"].as())); + actor.base.angle.z = toPlatform(target, static_cast(actorNode["angle"]["z"].as())); + + // Insert the actor into the context keyed by type and the stage/layer/room combo + std::array newActorData{}; + std::memcpy(newActorData.data(), &actor, RandomizerContext::ACTOR_CRC_SIZE); + for (const auto& layerNode : actorNode["layers"]) { + u8 layerNo = layerNode.as(); + // Create key based off of stage index, room, and layer + u32 stageRoomLayerKey{}; + stageRoomLayerKey |= getStageID(stageName.c_str()) << 16; + stageRoomLayerKey |= roomNo << 8; + stageRoomLayerKey |= layerNo; + randoData.mActorAdditions[actorType][stageRoomLayerKey].push_back(newActorData); + } + } + } + } + } + + randoData.mHash = r.GetConfig().GetHash(); auto writeToFileResult = randoData.WriteToFile(); if (writeToFileResult.has_value()) { diff --git a/src/dusk/randomizer/game/randomizer_context.hpp b/src/dusk/randomizer/game/randomizer_context.hpp index 8e99d51c6c..1276fe5608 100644 --- a/src/dusk/randomizer/game/randomizer_context.hpp +++ b/src/dusk/randomizer/game/randomizer_context.hpp @@ -38,6 +38,7 @@ public: u8 mMapBits{}; std::unordered_map>> mActorPatches{}; + std::unordered_map>>> mActorAdditions{}; std::optional WriteToFile(); std::optional LoadFromHash(const std::string& hash); diff --git a/src/dusk/randomizer/generator/data/actor_additions.yaml b/src/dusk/randomizer/generator/data/actor_additions.yaml new file mode 100644 index 0000000000..dd72babb38 --- /dev/null +++ b/src/dusk/randomizer/generator/data/actor_additions.yaml @@ -0,0 +1,23 @@ +# New actors to spawn in + +# NOTE: All data is expressed in little endian format + +# Objects which have the ACTR dzx type +ACTR: + # Sacred Grove + F_SP117: + # Pedestal of Time + 1: + # Spawn in the Master Sword actor + - name: mstrsrd + parameters: 0x00020110 + position: + x: 0.0 + y: 1700.0 + z: -5435.0 + angle: + x: 0x0147 + y: 0x0000 + z: 0x0000 + layers: + - 2 \ No newline at end of file diff --git a/src/dusk/randomizer/generator/data/actor_patches.yaml b/src/dusk/randomizer/generator/data/actor_patches.yaml index 629a28c8c2..06de7d654c 100644 --- a/src/dusk/randomizer/generator/data/actor_patches.yaml +++ b/src/dusk/randomizer/generator/data/actor_patches.yaml @@ -1,4 +1,4 @@ -# Patches for actors that already exist on a given map +# Patches for actors that already exist # NOTE: Data is expressed in little endian format