diff --git a/include/d/d_msg_flow.h b/include/d/d_msg_flow.h index 7a6a26f3f9..fb34767306 100644 --- a/include/d/d_msg_flow.h +++ b/include/d/d_msg_flow.h @@ -131,6 +131,9 @@ public: u16 query051(mesg_flow_node_branch*, fopAc_ac_c*, int); u16 query052(mesg_flow_node_branch*, fopAc_ac_c*, int); u16 query053(mesg_flow_node_branch*, fopAc_ac_c*, int); +#if TARGET_PC + u16 query054(mesg_flow_node_branch*, fopAc_ac_c*, int); +#endif int event000(mesg_flow_node_event*, fopAc_ac_c*); int event001(mesg_flow_node_event*, fopAc_ac_c*); int event002(mesg_flow_node_event*, fopAc_ac_c*); @@ -188,7 +191,7 @@ public: void setMsg(u32 msg) { mMsg = msg; } bool checkEndFlow() { return (u32)field_0x26 == 1; } - static queryFunc mQueryList[53]; + static queryFunc mQueryList[DUSK_IF_ELSE(54, 53)]; static eventFunc mEventList[43]; private: diff --git a/src/d/actor/d_a_npc.cpp b/src/d/actor/d_a_npc.cpp index 123bf66800..7c079cdab8 100644 --- a/src/d/actor/d_a_npc.cpp +++ b/src/d/actor/d_a_npc.cpp @@ -14,6 +14,11 @@ #include "m_Do/m_Do_lib.h" #include +#if TARGET_PC +#include "dusk/randomizer/game/stages.h" +#include "dusk/randomizer/game/tools.h" +#endif + #if DEBUG void daNpcT_cmnListenPropertyEvent(char* param_0, int* param_1, daNpcT_HIOParam* param_2) { sprintf(¶m_0[*param_1], "%.3ff,\t// 注目オフセット\n", param_2->attention_offset); @@ -2886,6 +2891,29 @@ void daNpcT_offEvtBit(u32 i_no) { } BOOL daNpcT_chkEvtBit(u32 i_no) { +#if TARGET_PC + if (randomizer_IsActive()) { + switch (i_no) { + case 0x153: // Checking if the player has Ending Blow + { + if (getStageID() == Hidden_Skill) { + return true; + } + break; + } + + case 0x40: // Checking if the player has completed Goron Mines + { + if (getStageID() == Kakariko_Village_Interiors) { + return true; // Return true so Barnes will sell bombs no matter what + } + break; + } + default: + break; + } + } +#endif return dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[i_no]); } diff --git a/src/d/d_msg_flow.cpp b/src/d/d_msg_flow.cpp index a316e588ea..d8b8c07c5f 100644 --- a/src/d/d_msg_flow.cpp +++ b/src/d/d_msg_flow.cpp @@ -635,6 +635,17 @@ int dMsgFlow_c::messageNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPart int dMsgFlow_c::branchNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPartners) { mesg_flow_node_branch* node = NULL; node = &mFlowNodeTBL[mNodeIdx].branch; + +#if TARGET_PC + // Overwrite this node if we have a patch for it + if (randomizer_IsActive()) { + u32 key = (dMsgObject_getGroupID() << 16) | mNodeIdx; + if (randomizer_GetContext().mFlowPatches.contains(key)) { + node = reinterpret_cast(&randomizer_GetContext().mFlowPatches[key]); + } + } +#endif + u16 proc_status = (this->*mQueryList[node->query_idx])(node, i_speaker_p, 1); u16 var_r28 = node->next_node_idx + proc_status; @@ -756,7 +767,7 @@ int dMsgFlow_c::getParam(u8* params) { return *(BE(int)*)params; } -queryFunc dMsgFlow_c::mQueryList[53] = { +queryFunc dMsgFlow_c::mQueryList[DUSK_IF_ELSE(54, 53)] = { &dMsgFlow_c::query005, &dMsgFlow_c::query001, &dMsgFlow_c::query002, &dMsgFlow_c::query003, &dMsgFlow_c::query006, &dMsgFlow_c::query007, &dMsgFlow_c::query004, &dMsgFlow_c::query008, &dMsgFlow_c::query009, &dMsgFlow_c::query010, &dMsgFlow_c::query011, &dMsgFlow_c::query012, @@ -771,6 +782,9 @@ queryFunc dMsgFlow_c::mQueryList[53] = { &dMsgFlow_c::query045, &dMsgFlow_c::query046, &dMsgFlow_c::query047, &dMsgFlow_c::query048, &dMsgFlow_c::query049, &dMsgFlow_c::query050, &dMsgFlow_c::query051, &dMsgFlow_c::query052, &dMsgFlow_c::query053, +#if TARGET_PC + &dMsgFlow_c::query054, +#endif }; #if DEBUG @@ -1765,6 +1779,20 @@ u16 dMsgFlow_c::query053(mesg_flow_node_branch* i_flowNode_p, fopAc_ac_c* i_spea return ret; } +// Like query 1, but instead returns 1 if true, and 0 if false +u16 dMsgFlow_c::query054(mesg_flow_node_branch* i_flowNode_p, fopAc_ac_c* i_speaker_p, int param_2) { + const u16 prm0 = i_flowNode_p->param; + u16 ret = dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[prm0]); + + if (param_2 != 0) { + // "Flag Check" + OS_REPORT("\x1B[44;33mフラグチェック           \x1B[m|:"); + OS_REPORT("flow:%d, ret:%d, prm0:%d\n", mFlow, ret, prm0); + } + + return ret; +} + eventFunc dMsgFlow_c::mEventList[43] = { &dMsgFlow_c::event000, &dMsgFlow_c::event001, &dMsgFlow_c::event002, &dMsgFlow_c::event003, &dMsgFlow_c::event004, &dMsgFlow_c::event005, &dMsgFlow_c::event006, &dMsgFlow_c::event007, diff --git a/src/dusk/randomizer/game/randomizer_context.cpp b/src/dusk/randomizer/game/randomizer_context.cpp index bb9472d797..da1b101fe7 100644 --- a/src/dusk/randomizer/game/randomizer_context.cpp +++ b/src/dusk/randomizer/game/randomizer_context.cpp @@ -20,6 +20,7 @@ #include "d/d_meter2_info.h" #include "d/d_meter2.h" #include "d/d_meter2_draw.h" +#include "d/d_msg_flow.h" std::optional RandomizerContext::WriteToFile() { @@ -92,6 +93,8 @@ std::optional RandomizerContext::WriteToFile() { } } + out["mFlowPatches"] = this->mFlowPatches; + seedData << YAML::Dump(out); seedData.close(); @@ -228,6 +231,13 @@ std::optional RandomizerContext::LoadFromHash(const std::string& ha } } + // Flow Patches + for (const auto& flowNode: in["mFlowPatches"]) { + auto key = flowNode.first.as(); + auto value = flowNode.second.as(); + this->mFlowPatches[key] = value; + } + DuskLog.debug("Loaded Randomizer Seed {}", this->mHash); return std::nullopt; @@ -860,6 +870,27 @@ void GenerateAndWriteSeed(std::string& generationStatusMsg) { } } + // Flow Patches + auto flowPatches = LoadYAML(RANDO_DATA_PATH "flow_patches.yaml"); + for (const auto& groupNode : flowPatches) { + u8 groupNo = groupNode.first.as(); + for (const auto& flowNode : groupNode.second) { + u16 index = flowNode["index"].as(); + const auto& type = flowNode["type"].as(); + u64 value{}; + if (type == "branch") { + auto branch = reinterpret_cast(&value); + branch->type = 2; + branch->field_0x1 = flowNode["num results"].as(); + branch->query_idx = flowNode["query"].as(); + branch->param = flowNode["parameters"].as(); + branch->next_node_idx = flowNode["next node index"].as(); + } + + u32 key = (groupNo << 16) | index; + randoData.mFlowPatches[key] = value; + } + } randoData.mHash = r.GetConfig().GetHash(); auto writeToFileResult = randoData.WriteToFile(); diff --git a/src/dusk/randomizer/game/randomizer_context.hpp b/src/dusk/randomizer/game/randomizer_context.hpp index d8cbd1cb4d..c21af22119 100644 --- a/src/dusk/randomizer/game/randomizer_context.hpp +++ b/src/dusk/randomizer/game/randomizer_context.hpp @@ -42,6 +42,7 @@ public: std::unordered_map>> mActorPatches{}; std::unordered_map>>> mActorAdditions{}; + std::unordered_map mFlowPatches{}; std::optional WriteToFile(); std::optional LoadFromHash(const std::string& hash); diff --git a/src/dusk/randomizer/generator/data/flow_patches.yaml b/src/dusk/randomizer/generator/data/flow_patches.yaml new file mode 100644 index 0000000000..e3264eb806 --- /dev/null +++ b/src/dusk/randomizer/generator/data/flow_patches.yaml @@ -0,0 +1,22 @@ +# Patches for NPC text flows + +# NOTE: All data is expressed in little endian + +#0: # zel_00.bmg +#1: # zel_01.bmg +2: # zel_02.bmg + # Patch Barnes branch to see if you can buy the bomb bag to use + # query 54 (custom event check) instead of query 22 (how many bomb bags the player has) + - index: 0x47C + type: branch + num results: 2 + query: 53 + parameters: 0x004D + next node index: 0x340 + +#3: # zel_03.bmg +#4: # zel_04.bmg +#5: # zel_05.bmg +#6: # zel_06.bmg +#7: # zel_07.bmg +#8: # zel_08.bmg diff --git a/src/dusk/randomizer/generator/data/locations.yaml b/src/dusk/randomizer/generator/data/locations.yaml index 9e416b47d6..1f3656c71d 100644 --- a/src/dusk/randomizer/generator/data/locations.yaml +++ b/src/dusk/randomizer/generator/data/locations.yaml @@ -1333,8 +1333,8 @@ - Kakariko Village - ARC Metadata: - - Stage: 0xFF - Item: 0xFF + - Stage: 68 + Item: 0x50 - Name: Kakariko Village Bomb Shop Poe Original Item: Poe Soul