update data for flow node usage

This commit is contained in:
gymnast86
2026-08-03 14:15:35 -07:00
parent 2b489e1a7d
commit 36d31e2bb8
5 changed files with 298 additions and 58 deletions
@@ -42,18 +42,21 @@
query: 6
parameters: 0x0064
next node index: 0x222
# Patch Charlo to take 100 rupees from Link instead of 30
- index: 0x34E
type: event
event: 41
parameters: 0x00000064
next node index: 0x226
# Patch Charlo to add 100 rupees to his counter instead of 30
- index: 0x357
type: event
event: 3
parameters: 0x00000064
next node index: 0x22B
# Patch Malo Mart door person to not check for time of day.
# Change from branch node that checks time of day to event node
# that does nothing.
@@ -63,6 +66,22 @@
parameters: 0x00000000
next node index: 0x10C
# Patch Jovani to skip checking whether we've received the first
# or second reward and skip straight to the second reward if we're
# talking to him for the first time with 60 souls
- index: 0x844
type: message
inf index: 0x322
next flow index: 0x851
# Patch Jovani to check if we've received the 20 reward yet at
# the end of the 60 reward sequence.
- index: 0x81F
type: event
event: 14
parameters: 0x00000063
next node index: Jovani Check 20 Reward Obtained
5: # zel_05.bmg
# Patch Yeta flow to always give the map check even if the player has obtained the Ordon Cheese
- index: 0x8
@@ -86,6 +105,13 @@
parameters: 0x00000000
next node index: 0x1AC
# Patch Cave of Ordeals Great Fairy to skip over the check
# for an empty bottle the first time
- index: 0x850
type: message
inf index: 0x25B
next flow index: 0x202
#6: # zel_06.bmg
#7: # zel_07.bmg
#8: # zel_08.bmg
@@ -277,6 +303,18 @@
parameters: 0x00000000
next node index: 0x116
# Check to see if we've obtained the 20 Poe Soul Reward.
# If we haven't then branch to giving the reward
- name: Jovani Check 20 Reward Obtained
type: branch
num results: 2
query: 1
parameters: 0x0272
next node index: 0xFFFF
results:
- 0xFFFF # Yes, we've obtained the reward, do nothing
- Jovani Give 20 Reward
#################################################
# HINT SIGNS #
#################################################
@@ -33,6 +33,11 @@
- Ordona Province
- Npc
Metadata:
FLW Event:
- Group: 1
Index: 0x18F
Next Node Index: 0x116
Parameters: 0x0001004A
Name Lookup:
- Uli Cradle Delivery
Event Flag: 0x0301
@@ -44,6 +49,11 @@
- Npc
- Ordona Province
Metadata:
FLW Event:
- Group: 1
Index: 0x6DF
Next Node Index: 0x44A
Parameters: 0x00010065
Name Lookup:
- Ordon Cat Rescue
Event Flag: 0x1408
@@ -277,6 +287,11 @@
- Npc
- Ordona Province
Metadata:
FLW Event:
- Group: 1
Index: 0x345
Next Node Index: 0x216
Parameters: 0x00010021
Name Lookup:
- Herding Goats Reward
Event Flag: 0x4240
@@ -493,6 +508,11 @@
- Faron Woods
- ARC
Metadata:
FLW Event:
- Group: 6
Index: 0x72
Next Node Index: 0x54
Parameters: 0x0001009D
Name Lookup:
- Coro Bottle
Event Flag: 0x1A08
@@ -1471,6 +1491,17 @@
- ARC
- Remote Location
Metadata:
FLW Event:
# Win the game without ever using the hawkeye
- Group: 2
Index: 0x124
Next Node Index: 0xA6
Parameters: 0x00010021
# Win the game, but have used hawkeye before
- Group: 2
Index: 0xE0
Next Node Index: 0x71
Parameters: 0x00010021
Name Lookup:
- Talo Sharpshooting
Event Flag: 0x0920
@@ -1483,6 +1514,11 @@
- Kakariko Village
- ARC
Metadata:
FLW Event:
- Group: 2
Index: 0x19A
Next Node Index: 0xF5
Parameters: 0x00010080
Name Lookup:
- Renados Letter
Event Flag: 0x0F80
@@ -1507,6 +1543,11 @@
- Kakariko Graveyard
- ARC
Metadata:
FLW Event:
- Group: 2
Index: 0x55
Next Node Index: 0x25
Parameters: 0x00010031
Name Lookup:
- Rutelas Blessing
Event Flag: 0x0804
@@ -1519,6 +1560,11 @@
- Kakariko Graveyard
- ARC
Metadata:
FLW Event:
- Group: 2
Index: 0x339
Next Node Index: 0x1ED
Parameters: 0x0001003D
Name Lookup:
- Gift From Ralis
Event Flag: 0x3B80
@@ -1887,6 +1933,11 @@
- ARC
- Remote Location
Metadata:
FLW Event:
- Group: 6
Index: 0x610
Next Node Index: 0x439
Parameters: 0x000100E9
Name Lookup:
- Skybook From Impaz
Event Flag: 0x5F80
@@ -1922,6 +1973,11 @@
- Hidden Village
- ARC
Metadata:
FLW Event:
- Group: 6
Index: 0x32
Next Node Index: 0x2A
Parameters: 0x00010083
Name Lookup:
- Ilia Charm
Event Flag: 0x2280
@@ -2390,6 +2446,11 @@
- Castle Town
- ARC
Metadata:
FLW Event:
- Group: 4
Index: 0x276
Next Node Index: 0x1A5
Parameters: 0x00010055
Name Lookup:
- STAR Prize 1
Event Flag: 0x2308
@@ -2402,6 +2463,11 @@
- Castle Town
- ARC
Metadata:
FLW Event:
- Group: 4
Index: 0x269
Next Node Index: 0x1A1
Parameters: 0x00010056
Name Lookup:
- STAR Prize 2
Event Flag: 0x2301
@@ -2471,6 +2537,17 @@
- Castle Town
- ARC
Metadata:
FLW Event:
# Flow if less than 60 souls
- Group: 4
Index: 0x841
Next Node Index: 0x773
Parameters: 0x00010075
# Custom flow if 60 souls
- Group: 9
Index: Jovani Give 20 Reward
Next Node Index: 0x77E
Parameters: 0x00010075
Name Lookup:
- Jovani 20 Poe Soul Reward
Event Flag: 0x5510
@@ -2485,6 +2562,11 @@
- ARC
- Remote Location
Metadata:
FLW Event:
- Group: 4
Index: 0x852
Next Node Index: 0x781
Parameters: 0x00010007
Name Lookup:
- Jovani 60 Poe Soul Reward
Event Flag: 0x3820
@@ -2518,6 +2600,11 @@
- Castle Town
- ARC
Metadata:
FLW Event:
- Group: 4
Index: 0x14D
Next Node Index: 0xC6
Parameters: 0x00010081
Name Lookup:
- Telma Invoice
Event Flag: 0x2180
@@ -3040,6 +3127,11 @@
- Lake Hylia
- ARC
Metadata:
FLW Event:
- Group: 8
Index: 0x180
Next Node Index: 0xF7
Parameters: 0x00010090
Name Lookup:
- Auru Gift To Fyer
Event Flag: 0x2520
@@ -3564,6 +3656,11 @@
- ARC
- Remote Location
Metadata:
FLW Event:
- Group: 8
Index: 0x1FC
Next Node Index: 0x152
Parameters: 0x00010021
Name Lookup:
- Plumm Fruit Balloon Minigame
Event Flag: 0x2380
@@ -3653,6 +3750,11 @@
- ARC
- Remote Location
Metadata:
FLW Event:
- Group: 8
Index: 0x2E4
Next Node Index: 0x208
Parameters: 0x00010051
Name Lookup:
- Iza Helping Hand
Event Flag: 0x0B01
@@ -3666,6 +3768,11 @@
- ARC
- Remote Location
Metadata:
FLW Event:
- Group: 8
Index: 0x2EE
Next Node Index: 0x20F
Parameters: 0x0001004F
Name Lookup:
- Iza Raging Rapids Minigame
Event Flag: 0x5908
@@ -3898,6 +4005,11 @@
- Zoras Domain
- ARC
Metadata:
FLW Event:
- Group: 7
Index: 0x8
Next Node Index: 0x1
Parameters: 0x00010051
Name Lookup:
- Zoras Domain Underwater Goron
Event Flag: 0x3D10
@@ -3987,6 +4099,11 @@
- Snowpeak Province
- ARC
Metadata:
FLW Event:
- Group: 6
Index: 0x282
Next Node Index: 0x1C6
Parameters: 0x00010091
Name Lookup:
- Ashei Sketch
Event Flag: 0x2940
@@ -4077,6 +4194,11 @@
- ARC
- Remote Location
Metadata:
FLW Event:
- Group: 6
Index: 0x2F8
Next Node Index: 0x237
Parameters: 0x00010021
Name Lookup:
- Snowboard Racing Prize
Event Flag: 0x3B10
@@ -4724,6 +4846,11 @@
- ARC
- Remote Location
Metadata:
FLW Event:
- Group: 5
Index: 0x20A
Next Node Index: 0x18F
Parameters: 0x00010073
Name Lookup:
- Cave of Ordeals Great Fairy Reward
Event Flag: 0x3E40
@@ -4992,6 +5119,11 @@
- Big Key
- ARC
Metadata:
FLW Event:
- Group: 5
Index: 0x237
Next Node Index: 0x19B
Parameters: 0x000100F9
Name Lookup:
- Goron Mines Gor Amato Key Shard
Event Flag: 0x3008 # late
@@ -5111,6 +5243,11 @@
- Big Key
- ARC
Metadata:
FLW Event:
- Group: 5
Index: 0x259
Next Node Index: 0x1AD
Parameters: 0x000100FA
Name Lookup:
- Goron Mines Gor Ebizo Key Shard
Event Flag: 0x3702 # late
@@ -5171,6 +5308,11 @@
- Big Key
- ARC
Metadata:
FLW Event:
- Group: 5
Index: 0x248
Next Node Index: 0x1A4
Parameters: 0x000100FD
Name Lookup:
- Goron Mines Gor Liggs Key Shard
Event Flag: 0x3701 # late
@@ -5892,6 +6034,11 @@
- Dungeon Map
- ARC
Metadata:
FLW Event:
- Group: 5
Index: 0x3C8
Next Node Index: 0x265
Parameters: 0x00010023
Name Lookup:
- Snowpeak Ruins Mansion Map
Event Flag: 0x0B10 # late
@@ -635,6 +635,10 @@ namespace randomizer::logic::hints {
static void AssignHintSignHints(const location::LocationPool& hintSigns, std::vector<Hint> hints, world::World* world) {
size_t hintsPerSign = std::ceil(static_cast<double>(hints.size()) / static_cast<double>(hintSigns.size()));
// Don't bother placing hints if there are none
if (hintsPerSign == 0) {
return;
}
auto& worlds = world->GetRandomizer()->GetWorlds();
auto& hintSignHints = world->GetHintSignHints();
auto hintSignHintsOriginal = hintSignHints;
+107 -58
View File
@@ -873,7 +873,7 @@ void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo,
}
}
static void randomizer_setTempFlag(RandomizerContext::itemLocationData data) {
void randomizer_setTempFlag(RandomizerContext::itemLocationData data) {
// If stage is 0xFF, then this is an event flag
if (data.stage == 0xFF) {
g_randomizerState.mTrackerTempEventFlag = data.flag;
@@ -1113,6 +1113,50 @@ static std::array<u8, 20> CreateAttributeData(const YAML::Node& node, const std:
RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
RandomizerContext randoData{};
// Give custom flows and messages new indices as we read them in/create them
std::unordered_map<std::string, u16> customMessageIDs{};
std::unordered_map<std::string, u16> customFlowIDs{};
std::unordered_set<u16> usedMessageIDs{};
std::unordered_set<u16> usedFlowIDs{};
u16 curCustomMessageID = BASE_CUSTOM_MSG_AND_FLOW_ID;
u16 curCustomFlowID = BASE_CUSTOM_MSG_AND_FLOW_ID;
// Helper functions for assigning new custom flow IDs/message IDs
auto handleCustomID = [](const std::string& name, auto& customIds, auto& usedIds, u16& curCustomID) {
u16 resultIndex{};
// Check to see if we're setting a custom index
auto resultInt = randomizer::utility::str::toInt(name);
// If we have a regular index, then use that directly
if (resultInt.has_value()) {
resultIndex = resultInt.value();
} else {
// If we don't, assume we're setting the index as custom
if (customIds.contains(name)) {
resultIndex = customIds[name];
} else {
while (usedIds.contains(curCustomID)) {
++curCustomID;
}
auto newIndex = curCustomID++;
resultIndex = newIndex;
customIds[name] = newIndex;
}
}
usedIds.insert(resultIndex);
return resultIndex;
};
auto handleCustomFlowID = [&](const std::string& name) {
return handleCustomID(name, customFlowIDs, usedFlowIDs, curCustomFlowID);
};
auto handleCustomMessageID = [&](const std::string& name) {
return handleCustomID(name, customMessageIDs, usedMessageIDs, curCustomMessageID);
};
// Settings we need to check ingame
for (const auto& [setting, info] : *randomizer::seedgen::settings::GetAllSettingsInfo()) {
if (info->NeedInGame()) {
@@ -1254,6 +1298,60 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
}
}
// Items that are given by FLW events. Override the item in the existing event with the
// randomized item
if (location->HasCategories("FLW Event")) {
for (const auto& flwEventNode : metaData["FLW Event"]) {
u8 group = flwEventNode["Group"].as<u8>();
u16 index = handleCustomFlowID(flwEventNode["Index"].as<std::string>());
mesg_flow_node_event event{};
event.type = 3; // event type node
event.event_idx = 8;
event.next_node_idx = handleCustomFlowID(flwEventNode["Next Node Index"].as<std::string>());
auto params = flwEventNode["Parameters"].as<u32>();
// Zero out the spot for the item id
params &= 0xFFFFFF00;
// Put in the item id
params |= location->GetCurrentItem()->GetID();
// Set the params in the correct order
event.params[0] = (params >> 24) & 0xFF;
event.params[1] = (params >> 16) & 0xFF;
event.params[2] = (params >> 8) & 0xFF;
event.params[3] = params & 0xFF;
// Construct another FLW node to set the associated flag in a temporary variable
// right before we receive the item. This ensures that a tracker/AP can pick up
// on the fact that we've received the item.
auto newFlwIndex = handleCustomFlowID(location->GetName() + " Flag Set Node");
mesg_flow_node_event flagEvent{};
flagEvent.type = 3; // event type node
flagEvent.event_idx = 46; // Set temporary randomizer flag
u8 stage{0xFF};
u16 flag{0xFFFF};
if (metaData["Event Flag"]) {
flag = metaData["Event Flag"].as<u16>();
} else if (metaData["Switch Flag"]) {
stage = metaData["Switch Flag"]["Stage"].as<u8>();
flag = metaData["Switch Flag"]["Flag"].as<u8>();
}
flagEvent.params[0] = 0;
flagEvent.params[1] = stage;
flagEvent.params[2] = (flag >> 8) & 0xFF;
flagEvent.params[3] = flag & 0xFF;
// Store the modified FLW nodes. The flag event takes the place of the original
// index we're modifying and then leads into the custom index of the event that
// sets up the item id
flagEvent.next_node_idx = newFlwIndex;
u32 key = (group << 16) | index;
randoData.mFlowPatches[key] = std::bit_cast<u64>(flagEvent);
key = (CUSTOM_BMG_GROUP << 16) | newFlwIndex;
randoData.mFlowPatches[key] = std::bit_cast<u64>(event);
}
}
// Items that we lookup just by calling their location name
if (location->HasCategories("Name Lookup")) {
for (const auto& locationNameNode : metaData["Name Lookup"]) {
@@ -1280,7 +1378,6 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
} else if (flagNode.IsMap()) {
const auto& condition = flagNode.begin()->first.as<std::string>();
if (world->EvaluateSettingCondition(condition)) {
randomizer::session::LogDebug(fmt::format("Setting flags for {}", condition).c_str());
for (const auto& conditionalFlag : flagNode.begin()->second) {
const auto& flag = conditionalFlag.as<u16>();
randoData.mStartEventFlags.push_back(flag);
@@ -1294,7 +1391,6 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
const auto& region = regionNode.first.as<std::string>();
const auto& index = regionNode.second["Index"].as<int>();
const auto& flags = regionNode.second["Flags"];
randomizer::session::LogDebug(fmt::format("Setting region flags for {}", region).c_str());
// This seems kinda scuffed so maybe we change it later
for (const auto& flagNode : flags) {
if (flagNode.IsScalar()) {
@@ -1400,49 +1496,6 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
}
}
// Give custom flows and messages new indices as we read them in
std::unordered_map<std::string, u16> customMessageIDs{};
std::unordered_map<std::string, u16> customFlowIDs{};
std::unordered_set<u16> usedMessageIDs{};
std::unordered_set<u16> usedFlowIDs{};
u16 curCustomMessageID = BASE_CUSTOM_MSG_AND_FLOW_ID;
u16 curCustomFlowID = BASE_CUSTOM_MSG_AND_FLOW_ID;
// Helper functions for assigning new custom flow IDs/message IDs
auto handleCustomID = [](const YAML::Node& node, auto& customIds, auto& usedIds, u16& curCustomID) {
u16 resultIndex{};
// Check to see if we're setting a custom index
auto resultStr = node.as<std::string>();
auto resultInt = randomizer::utility::str::toInt(resultStr);
// If we have a regular index, then use that directly
if (resultInt.has_value()) {
resultIndex = resultInt.value();
} else {
// If we don't, assume we're setting the index as custom
if (customIds.contains(resultStr)) {
resultIndex = customIds[resultStr];
} else {
while (usedIds.contains(curCustomID)) {
++curCustomID;
}
auto newIndex = curCustomID++;
resultIndex = newIndex;
customIds[resultStr] = newIndex;
}
}
usedIds.insert(resultIndex);
return resultIndex;
};
auto handleCustomFlowID = [&](const YAML::Node& node) {
return handleCustomID(node, customFlowIDs, usedFlowIDs, curCustomFlowID);
};
auto handleCustomMessageID = [&](const YAML::Node& node) {
return handleCustomID(node, customMessageIDs, usedMessageIDs, curCustomMessageID);
};
// Flow Patches
auto flowPatches = LOAD_EMBED_YAML(RANDO_DATA_PATH "flow_patches.yaml");
for (const auto& groupNode : flowPatches) {
@@ -1476,7 +1529,7 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
}
} else {
name = flowNode["name"].as<std::string>();
indices.push_back(handleCustomFlowID(flowNode["name"]));
indices.push_back(handleCustomFlowID(flowNode["name"].as<std::string>()));
}
const auto& type = flowNode["type"].as<std::string>();
@@ -1496,7 +1549,7 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
"do not match num results. (expected: {}. size: {})", name, branch->field_0x1, results.size()));
}
for (const auto& resultNode : results) {
auto resultIndex = handleCustomFlowID(resultNode);
auto resultIndex = handleCustomFlowID(resultNode.as<std::string>());
for (auto index : indices) {
u32 key = (groupNo << 16) | index;
randoData.mFlowPatchesBranchOverrides[key].push_back(resultIndex);
@@ -1508,7 +1561,7 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
auto event = reinterpret_cast<mesg_flow_node_event*>(&value);
event->type = 3;
event->event_idx = flowNode["event"].as<u8>();
event->next_node_idx = handleCustomFlowID(flowNode["next node index"]);
event->next_node_idx = handleCustomFlowID(flowNode["next node index"].as<std::string>());
u32 params = flowNode["parameters"].as<u32>();
event->params[0] = (params >> 24) & 0xFF;
event->params[1] = (params >> 16) & 0xFF;
@@ -1517,8 +1570,8 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
} else if (type == "message") {
auto message = reinterpret_cast<mesg_flow_node*>(&value);
message->type = 1;
message->msg_index = handleCustomMessageID(flowNode["inf index"]);
message->next_node_idx = handleCustomFlowID(flowNode["next flow index"]);
message->msg_index = handleCustomMessageID(flowNode["inf index"].as<std::string>());
message->next_node_idx = handleCustomFlowID(flowNode["next flow index"].as<std::string>());
// If a custom message is too long, split it up among additional flow/message nodes
if (message->msg_index >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
@@ -1548,13 +1601,9 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
auto extraTextName = textName + std::to_string(i + 1);
world->AddNewText(extraTextName) = extraText[i];
// Kinda silly, but means we don't need to create another function
// to handle direct string names.
YAML::Node node{};
node["name"] = extraTextName;
// Create new Flow and Message Ids for the split text object
auto newCustomFlowIndex = handleCustomFlowID(node["name"]);
auto newCustomMessageIndex = handleCustomMessageID(node["name"]);
auto newCustomFlowIndex = handleCustomFlowID(extraTextName);
auto newCustomMessageIndex = handleCustomMessageID(extraTextName);
// Create the new flow node. We're storing its own flow index with
// itself for now, but we'll shift it back to the previous node later
@@ -1629,7 +1678,7 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
messageId = overrideNode["Message Id"].as<u16>();
} else {
// If no message id specified, assume a custom one
messageId = handleCustomMessageID(overrideNode["Name"]);
messageId = handleCustomMessageID(overrideNode["Name"].as<std::string>());
}
u32 key = (group << 16) | messageId;
for (auto language : randomizer::supportedLanguages) {
@@ -222,6 +222,8 @@ void randomizer_checkAndOverrideEntranceData(const char*& i_Name, s8& i_RoomNo,
* variable. This allows the tracker/Archipelago to know a location has been checked
* when the item is received instead of some indeterminate amount of time afterward.
*/
void randomizer_setTempFlag(RandomizerContext::itemLocationData);
void randomizer_setTempFlagForLocation(const std::string& locationName);
void randomizer_setTempFlagForFLWOverride(u32 key);