mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-25 14:06:03 -04:00
custom flow nodes and initial custom midna choices
This commit is contained in:
@@ -1632,6 +1632,7 @@ set(DUSK_FILES
|
||||
src/dusk/cosmetics/texture_utils.hpp
|
||||
src/dusk/cosmetics/texture_utils.cpp
|
||||
# Randomizer files
|
||||
src/dusk/randomizer/game/custom_flow_ids.hpp
|
||||
src/dusk/randomizer/game/flags.cpp
|
||||
src/dusk/randomizer/game/flags.h
|
||||
src/dusk/randomizer/game/messages.cpp
|
||||
|
||||
@@ -37,6 +37,26 @@ public:
|
||||
/* 0x12 */ BE(u16) unk_0x12;
|
||||
};
|
||||
|
||||
#if TARGET_PC
|
||||
// Default attributes for custom text ids
|
||||
static JMSMesgEntry_c defaultEntry{
|
||||
.string_offset = 0,
|
||||
.message_id = 0,
|
||||
.event_label_id = 0,
|
||||
.se_speaker = 0x15,
|
||||
.fuki_kind = 0x0D,
|
||||
.output_type = 0x01,
|
||||
.fuki_pos_type = 0x00,
|
||||
.unk_0xc = 0xFF,
|
||||
.unk_0xd = 0x00,
|
||||
.se_mood = 0x0D,
|
||||
.camera_id = 0x0,
|
||||
.base_anm_id = 0x1F,
|
||||
.face_anm_id = 0x1F,
|
||||
.unk_0x12 = 0x400,
|
||||
};
|
||||
#endif
|
||||
|
||||
class JMSMesgInfo_c {
|
||||
public:
|
||||
/* 0x00 */ bmg_section_t header; // section header
|
||||
@@ -44,6 +64,9 @@ public:
|
||||
/* 0x0A */ BE(u16) entry_size; // size of an entry
|
||||
/* 0x0C */ u8 padding[4]; // padding
|
||||
/* 0x10 */ JMSMesgEntry_c entries[0];
|
||||
#if TARGET_PC
|
||||
JMSMesgEntry_c& getEntry(u16 index);
|
||||
#endif
|
||||
};
|
||||
|
||||
class COutFont_c;
|
||||
|
||||
@@ -133,6 +133,8 @@ public:
|
||||
u16 query053(mesg_flow_node_branch*, fopAc_ac_c*, int);
|
||||
#if TARGET_PC
|
||||
u16 query054(mesg_flow_node_branch*, fopAc_ac_c*, int);
|
||||
u16 query055(mesg_flow_node_branch*, fopAc_ac_c*, int);
|
||||
u16 query056(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*);
|
||||
@@ -181,6 +183,7 @@ public:
|
||||
// events for rando
|
||||
int event043(mesg_flow_node_event*, fopAc_ac_c*);
|
||||
int event044(mesg_flow_node_event*, fopAc_ac_c*);
|
||||
int event045(mesg_flow_node_event*, fopAc_ac_c*);
|
||||
#endif
|
||||
|
||||
void initWord(fopAc_ac_c*, const char*, u8, int, fopAc_ac_c**);
|
||||
@@ -196,12 +199,14 @@ public:
|
||||
void setMsg(u32 msg) { mMsg = msg; }
|
||||
bool checkEndFlow() { return (u32)field_0x26 == 1; }
|
||||
|
||||
static DUSK_GAME_DATA queryFunc mQueryList[DUSK_IF_ELSE(54, 53)];
|
||||
static DUSK_GAME_DATA eventFunc mEventList[DUSK_IF_ELSE(45, 43)];
|
||||
static DUSK_GAME_DATA queryFunc mQueryList[DUSK_IF_ELSE(56, 53)];
|
||||
static DUSK_GAME_DATA eventFunc mEventList[DUSK_IF_ELSE(46, 43)];
|
||||
|
||||
#if TARGET_PC
|
||||
// patch funcs for rando
|
||||
u32 getKeyForIndex(u16 nodeIdx);
|
||||
void randoPatchNodeType(u8& type, u16 nodeIdx);
|
||||
void randoPatchMessageNode(mesg_flow_node*& message_node, u16 nodeIdx);
|
||||
void randoPatchBranchNode(mesg_flow_node_branch*& branch_node, u16 nodeIdx);
|
||||
void randoPatchEventNode(mesg_flow_node_event*& event_node, u16 nodeIdx);
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "JSystem/JMessage/control.h"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/randomizer/game/custom_flow_ids.hpp"
|
||||
#include "dusk/randomizer/game/messages.hpp"
|
||||
#endif
|
||||
|
||||
@@ -75,6 +76,12 @@ int JMessage::TControl::setMessageID(u32 uMsgID, u32 param_1, bool* pbValid) {
|
||||
|
||||
u32 uCode = pProcessor->toMessageCode_messageID(uMsgID, param_1, pbValid);
|
||||
if (uCode == 0xFFFFFFFF) {
|
||||
#if TARGET_PC
|
||||
// If this is a custom text id
|
||||
if (uMsgID >= BASE_CUSTOM_MSG_AND_FLOW_ID)
|
||||
uCode = CUSTOM_BMG_GROUP << 16 | uMsgID;
|
||||
else // return 0
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -84,6 +91,16 @@ int JMessage::TControl::setMessageID(u32 uMsgID, u32 param_1, bool* pbValid) {
|
||||
bool JMessage::TControl::setMessageCode_inSequence_(JMessage::TProcessor const* pProcessor, u16 u16GroupID, u16 u16Index) {
|
||||
pEntry_ = pProcessor->getMessageEntry_messageCode(u16GroupID, u16Index);
|
||||
if (pEntry_ == NULL) {
|
||||
#if TARGET_PC
|
||||
if (HandleCustomText(this, u16Index)) {
|
||||
uMessageGroupID_ = u16GroupID;
|
||||
uMessageID_ = u16Index;
|
||||
pResourceCache_ = pProcessor->getResourceCache();
|
||||
pMessageText_current_ = pMessageText_begin_;
|
||||
oStack_renderingProcessor_.clear();
|
||||
return true;
|
||||
} else
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#if TARGET_PC
|
||||
#include "dusk/menu_pointer.h"
|
||||
#include "dusk/scope_guard.hpp"
|
||||
#include "dusk/randomizer/game/randomizer_context.hpp"
|
||||
#include "dusk/randomizer/game/custom_flow_ids.hpp"
|
||||
#endif
|
||||
|
||||
#if REGION_JPN
|
||||
@@ -37,6 +39,34 @@
|
||||
#define CHAR_CODE_THIN_DOWN_ARROW 0xBE
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
// Custom function to return custom entries if applicable
|
||||
JMSMesgEntry_c& JMSMesgInfo_c::getEntry(u16 index) {
|
||||
u32 key{};
|
||||
auto& attributeOverrides = randomizer_GetContext().mAttributeOverrides;
|
||||
if (index < BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
// Regular entry
|
||||
u8 group = dComIfGp_getMsgObjectClass()->getMessageGroup(index);
|
||||
key = group << 16 | index;
|
||||
// Override attributes if we modified them for this index
|
||||
if (attributeOverrides.contains(key)) {
|
||||
return *reinterpret_cast<JMSMesgEntry_c*>(&attributeOverrides[key]);
|
||||
}
|
||||
return entries[index];
|
||||
}
|
||||
|
||||
// We have a custom index, use the custom index group
|
||||
key = CUSTOM_BMG_GROUP << 16 | index;
|
||||
if (attributeOverrides.contains(key)) {
|
||||
return *reinterpret_cast<JMSMesgEntry_c*>(&attributeOverrides[key]);
|
||||
}
|
||||
|
||||
// If we didn't specify attributes for the custom index, use the default
|
||||
defaultEntry.message_id = index;
|
||||
return defaultEntry;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool checkCharInfoCharactor(int c) {
|
||||
if (c != 0x8140 && c != 0x8141 && c != 0x8142 && c != 0x0020 && c != 0x0022 && c != 0x0027 &&
|
||||
c != 0x002C && c != 0x002E)
|
||||
|
||||
+157
-40
@@ -16,6 +16,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/randomizer/game/custom_flow_ids.hpp"
|
||||
#include "dusk/randomizer/game/flags.h"
|
||||
#include "dusk/randomizer/game/tools.h"
|
||||
#include "dusk/randomizer/game/stages.h"
|
||||
@@ -23,6 +24,21 @@
|
||||
#include "dusk/randomizer/game/verify_item_functions.h"
|
||||
#endif
|
||||
|
||||
// Macros for somewhat gracefully handling custom flow ids
|
||||
// The game likes to directly index into the flow node table a lot, so this macro keeps
|
||||
// reletively the same syntax in all the places that happens
|
||||
#if TARGET_PC
|
||||
#define M_FLOW_NODE_TBL(index) \
|
||||
(*(index >= BASE_CUSTOM_MSG_AND_FLOW_ID && index != 0xFFFF && randomizer_IsActive() ? \
|
||||
reinterpret_cast<mesg_flow*>(&randomizer_GetContext().mFlowPatches.at((CUSTOM_BMG_GROUP << 16) | (index))) : \
|
||||
&mFlowNodeTBL[index]))
|
||||
// If we're indexing into the flow index table, just return the custom index directly
|
||||
#define M_FLOW_IDX_TBL(index) index >= BASE_CUSTOM_MSG_AND_FLOW_ID && randomizer_IsActive() ? index : mFlowIdxTBL[index]
|
||||
#else
|
||||
#define M_FLOW_NODE_TBL(index) mFlowNodeTBL[index]
|
||||
#define M_FLOW_IDX_TBL(index) mFlowIdxTBL[index]
|
||||
#endif
|
||||
|
||||
dMsgFlow_c::dMsgFlow_c() {
|
||||
mNonStopJunpFlowFlag = 0;
|
||||
setInitValue(1);
|
||||
@@ -115,7 +131,7 @@ int dMsgFlow_c::checkOpenDoor(fopAc_ac_c* i_speaker_p, int* param_2) {
|
||||
mesg_flow_node_event* event_node = NULL;
|
||||
|
||||
while ((nodeIdx != 0xFFFF && !var_r27) && !var_r25) {
|
||||
u8 type = mFlowNodeTBL[nodeIdx].message.type;
|
||||
u8 type = M_FLOW_NODE_TBL(nodeIdx).message.type;
|
||||
|
||||
#if TARGET_PC
|
||||
randoPatchNodeType(type, nodeIdx);
|
||||
@@ -123,13 +139,13 @@ int dMsgFlow_c::checkOpenDoor(fopAc_ac_c* i_speaker_p, int* param_2) {
|
||||
|
||||
switch(type) {
|
||||
case NODETYPE_MESSAGE_e: {
|
||||
msg_node = &mFlowNodeTBL[nodeIdx].message;
|
||||
msg_node = &M_FLOW_NODE_TBL(nodeIdx).message;
|
||||
nodeIdx = msg_node->next_node_idx;
|
||||
var_r26++;
|
||||
break;
|
||||
}
|
||||
case NODETYPE_BRANCH_e: {
|
||||
branch_node = (mesg_flow_node_branch*)&mFlowNodeTBL[nodeIdx].branch;
|
||||
branch_node = (mesg_flow_node_branch*)&M_FLOW_NODE_TBL(nodeIdx).branch;
|
||||
|
||||
#if TARGET_PC
|
||||
randoPatchBranchNode(branch_node, nodeIdx);
|
||||
@@ -149,10 +165,20 @@ int dMsgFlow_c::checkOpenDoor(fopAc_ac_c* i_speaker_p, int* param_2) {
|
||||
u16 query_ret = (this->*mQueryList[branch_node->query_idx])(branch_node, i_speaker_p, 0);
|
||||
u16 spE = branch_node->next_node_idx + query_ret;
|
||||
nodeIdx = mFlowIdxTBL[spE];
|
||||
#if TARGET_PC
|
||||
// Overwrite the node we branch to if applicable
|
||||
if (randomizer_IsActive()) {
|
||||
auto& branchOverrides = randomizer_GetContext().mFlowPatchesBranchOverrides;
|
||||
auto key = getKeyForIndex(nodeIdx);
|
||||
if (branchOverrides.contains(key)) {
|
||||
nodeIdx = branchOverrides[key][query_ret];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case NODETYPE_EVENT_e: {
|
||||
event_node = &mFlowNodeTBL[nodeIdx].event;
|
||||
event_node = &M_FLOW_NODE_TBL(nodeIdx).event;
|
||||
|
||||
#if TARGET_PC
|
||||
randoPatchEventNode(event_node, nodeIdx);
|
||||
@@ -171,7 +197,7 @@ int dMsgFlow_c::checkOpenDoor(fopAc_ac_c* i_speaker_p, int* param_2) {
|
||||
var_r25 = TRUE;
|
||||
break;
|
||||
default:
|
||||
nodeIdx = mFlowIdxTBL[event_node->next_node_idx];
|
||||
nodeIdx = M_FLOW_IDX_TBL(event_node->next_node_idx);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -413,7 +439,7 @@ void dMsgFlow_c::setNodeIndex(u16 i_nodeIdx, fopAc_ac_c** i_talkPartners) {
|
||||
dMsgObject_endFlowGroup();
|
||||
field_0x26 = 1;
|
||||
} else {
|
||||
switch (mFlowNodeTBL[i_nodeIdx].message.type) {
|
||||
switch (M_FLOW_NODE_TBL(i_nodeIdx).message.type) {
|
||||
case 0:
|
||||
break;
|
||||
case NODETYPE_MESSAGE_e:
|
||||
@@ -423,7 +449,11 @@ void dMsgFlow_c::setNodeIndex(u16 i_nodeIdx, fopAc_ac_c** i_talkPartners) {
|
||||
break;
|
||||
case NODETYPE_EVENT_e:
|
||||
mesg_flow_node_event* node = NULL;
|
||||
node = &mFlowNodeTBL[i_nodeIdx].event;
|
||||
node = &M_FLOW_NODE_TBL(i_nodeIdx).event;
|
||||
|
||||
#if TARGET_PC
|
||||
randoPatchEventNode(node, i_nodeIdx);
|
||||
#endif
|
||||
|
||||
if (node->event_idx == 21 || node->event_idx == 32 || node->event_idx == 33) {
|
||||
if (node->event_idx == 21) {
|
||||
@@ -476,9 +506,21 @@ int dMsgFlow_c::setSelectMsg(mesg_flow_node* i_flowNode_p, mesg_flow_node* param
|
||||
inf_p = (BE(u16)*)getMsgDataBlock("INF1");
|
||||
|
||||
var_r29 = param_2;
|
||||
#if TARGET_PC
|
||||
// Don't index our msg index if it's a custom one (the custom one is already the correct value)
|
||||
if (randomizer_IsActive() && var_r29->msg_index >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
temp_r25 = var_r29->msg_index;
|
||||
} else
|
||||
#endif
|
||||
temp_r25 = ((inf_p + (var_r29->msg_index) * 10))[10];
|
||||
|
||||
var_r29 = i_flowNode_p;
|
||||
#if TARGET_PC
|
||||
// Same as above
|
||||
if (randomizer_IsActive() && var_r29->msg_index >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
msg_no = var_r29->msg_index;
|
||||
} else
|
||||
#endif
|
||||
msg_no = ((inf_p + (var_r29->msg_index) * 10))[10];
|
||||
|
||||
// "Message Set (Select)"
|
||||
@@ -523,6 +565,12 @@ int dMsgFlow_c::setNormalMsg(mesg_flow_node* i_flowNode_p, fopAc_ac_c* i_speaker
|
||||
|
||||
var_r29 = i_flowNode_p;
|
||||
inf_p = (BE(u16)*)getMsgDataBlock("INF1");
|
||||
#if TARGET_PC
|
||||
// Don't index our msg index if it's a custom one (the custom one is already the correct value)
|
||||
if (randomizer_IsActive() && var_r29->msg_index >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
msg_no = var_r29->msg_index;
|
||||
} else
|
||||
#endif
|
||||
msg_no = (inf_p + (var_r29->msg_index) * 10)[10];
|
||||
|
||||
#if TARGET_PC
|
||||
@@ -581,31 +629,42 @@ int dMsgFlow_c::setNormalMsg(mesg_flow_node* i_flowNode_p, fopAc_ac_c* i_speaker
|
||||
|
||||
int dMsgFlow_c::messageNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPartners) {
|
||||
mesg_flow_node* flowNode_p = NULL;
|
||||
flowNode_p = &mFlowNodeTBL[mNodeIdx].message;
|
||||
flowNode_p = &M_FLOW_NODE_TBL(mNodeIdx).message;
|
||||
|
||||
#if TARGET_PC
|
||||
randoPatchMessageNode(flowNode_p, mNodeIdx);
|
||||
#endif
|
||||
|
||||
if (field_0x25 != 0) {
|
||||
if (mSelType != SELTYPE_NONE_e) {
|
||||
u16 aNextNodeIndex = flowNode_p->next_node_idx;
|
||||
JUT_ASSERT(1051, 0xFFFF != aNextNodeIndex);
|
||||
|
||||
if (mSelType == SELTYPE_VERTICAL_e && mFlowNodeTBL[aNextNodeIndex].message.type == NODETYPE_MESSAGE_e) {
|
||||
JUT_ASSERT(1056, NODETYPE_MESSAGE_e == mFlowNodeTBL[aNextNodeIndex].message.type);
|
||||
if (setSelectMsg(&mFlowNodeTBL[mNodeIdx].message, &mFlowNodeTBL[aNextNodeIndex].message, i_speaker_p)) {
|
||||
#if TARGET_PC
|
||||
mesg_flow_node* nextFlowNode_p = &M_FLOW_NODE_TBL(aNextNodeIndex).message;
|
||||
randoPatchMessageNode(nextFlowNode_p, aNextNodeIndex);
|
||||
// The code really likes directly indexing over and over again here, so we have to put
|
||||
// in some ugly DUSK_IF_ELSEs
|
||||
#endif
|
||||
|
||||
if (mSelType == SELTYPE_VERTICAL_e && DUSK_IF_ELSE(nextFlowNode_p->, mFlowNodeTBL[aNextNodeIndex].message.)type == NODETYPE_MESSAGE_e) {
|
||||
JUT_ASSERT(1056, NODETYPE_MESSAGE_e == DUSK_IF_ELSE(nextFlowNode_p->, mFlowNodeTBL[aNextNodeIndex].message.)type);
|
||||
if (setSelectMsg(DUSK_IF_ELSE(flowNode_p, &mFlowNodeTBL[mNodeIdx].message), DUSK_IF_ELSE(nextFlowNode_p, &mFlowNodeTBL[aNextNodeIndex].message), i_speaker_p)) {
|
||||
mNodeIdx = aNextNodeIndex;
|
||||
mSelType = SELTYPE_NONE_e;
|
||||
field_0x25 = 0;
|
||||
}
|
||||
} else if (mSelType == SELTYPE_HORIZONTAL_e && mFlowNodeTBL[aNextNodeIndex].message.type == NODETYPE_BRANCH_e) {
|
||||
if (setNormalMsg(&mFlowNodeTBL[mNodeIdx].message, i_speaker_p)) {
|
||||
} else if (mSelType == SELTYPE_HORIZONTAL_e && DUSK_IF_ELSE(nextFlowNode_p->, mFlowNodeTBL[aNextNodeIndex].message.)type == NODETYPE_BRANCH_e) {
|
||||
if (setNormalMsg(DUSK_IF_ELSE(flowNode_p, &mFlowNodeTBL[mNodeIdx].message), i_speaker_p)) {
|
||||
mSelType = SELTYPE_NONE_e;
|
||||
field_0x25 = 0;
|
||||
}
|
||||
} else {
|
||||
OS_REPORT("★sel select mesg ===> %d, %d, %d\n", mSelType, aNextNodeIndex, mFlowNodeTBL[aNextNodeIndex].message.type);
|
||||
OS_REPORT("★sel select mesg ===> %d, %d, %d\n", mSelType, aNextNodeIndex, DUSK_IF_ELSE(nextFlowNode_p->, mFlowNodeTBL[aNextNodeIndex].message.)type);
|
||||
JUT_ASSERT(1077, FALSE);
|
||||
}
|
||||
} else {
|
||||
if (setNormalMsg(&mFlowNodeTBL[mNodeIdx].message, i_speaker_p)) {
|
||||
if (setNormalMsg(DUSK_IF_ELSE(flowNode_p, &mFlowNodeTBL[mNodeIdx].message), i_speaker_p)) {
|
||||
field_0x25 = 0;
|
||||
}
|
||||
}
|
||||
@@ -654,7 +713,7 @@ int dMsgFlow_c::messageNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPart
|
||||
case 18:
|
||||
setNodeIndex(flowNode_p->next_node_idx, i_talkPartners);
|
||||
|
||||
mesg_flow_node* var_r26 = &mFlowNodeTBL[flowNode_p->next_node_idx].message;
|
||||
mesg_flow_node* var_r26 = &M_FLOW_NODE_TBL(flowNode_p->next_node_idx).message;
|
||||
if (var_r26->field_0x1 == 0x15 || var_r26->field_0x1 == 0x20 || var_r26->field_0x1 == 0x21) {
|
||||
return 0;
|
||||
}
|
||||
@@ -668,7 +727,7 @@ 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;
|
||||
node = &M_FLOW_NODE_TBL(mNodeIdx).branch;
|
||||
|
||||
#if TARGET_PC
|
||||
randoPatchBranchNode(node, mNodeIdx);
|
||||
@@ -677,13 +736,25 @@ int dMsgFlow_c::branchNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPartn
|
||||
u16 proc_status = (this->*mQueryList[node->query_idx])(node, i_speaker_p, 1);
|
||||
|
||||
u16 var_r28 = node->next_node_idx + proc_status;
|
||||
#if TARGET_PC
|
||||
// Overwrite the node we branch to if applicable
|
||||
if (randomizer_IsActive()) {
|
||||
auto& branchOverrides = randomizer_GetContext().mFlowPatchesBranchOverrides;
|
||||
auto key = getKeyForIndex(mNodeIdx);
|
||||
u16 nextIndex = mFlowIdxTBL[var_r28];
|
||||
if (branchOverrides.contains(key)) {
|
||||
nextIndex = branchOverrides[key][proc_status];
|
||||
}
|
||||
setNodeIndex(nextIndex, i_talkPartners);
|
||||
} else
|
||||
#endif
|
||||
setNodeIndex(mFlowIdxTBL[var_r28], i_talkPartners);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dMsgFlow_c::eventNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPartners) {
|
||||
mesg_flow_node_event* node = NULL;
|
||||
node = &mFlowNodeTBL[mNodeIdx].event;
|
||||
node = &M_FLOW_NODE_TBL(mNodeIdx).event;
|
||||
#if TARGET_PC
|
||||
randoPatchEventNode(node, mNodeIdx);
|
||||
#endif
|
||||
@@ -692,7 +763,7 @@ int dMsgFlow_c::eventNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPartne
|
||||
switch (node->event_idx) {
|
||||
case 8: {
|
||||
getParam(&mEventId, &field_0x30, node->params);
|
||||
setNodeIndex(mFlowIdxTBL[node->next_node_idx], i_talkPartners);
|
||||
setNodeIndex(M_FLOW_IDX_TBL(node->next_node_idx), i_talkPartners);
|
||||
|
||||
if (field_0x26 != 0) {
|
||||
break;
|
||||
@@ -737,7 +808,7 @@ int dMsgFlow_c::eventNodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPartne
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
setNodeIndex(mFlowIdxTBL[node->next_node_idx], i_talkPartners);
|
||||
setNodeIndex(M_FLOW_IDX_TBL(node->next_node_idx), i_talkPartners);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -758,7 +829,7 @@ int dMsgFlow_c::nodeProc(fopAc_ac_c* i_speaker_p, fopAc_ac_c** i_talkPartners) {
|
||||
aSpeaker_p = i_talkPartners[field_0x38];
|
||||
}
|
||||
|
||||
u8 type = mFlowNodeTBL[mNodeIdx].message.type;
|
||||
u8 type = M_FLOW_NODE_TBL(mNodeIdx).message.type;
|
||||
#if TARGET_PC
|
||||
randoPatchNodeType(type, mNodeIdx);
|
||||
#endif
|
||||
@@ -801,7 +872,7 @@ int dMsgFlow_c::getParam(u8* params) {
|
||||
return *(BE(int)*)params;
|
||||
}
|
||||
|
||||
DUSK_GAME_DATA queryFunc dMsgFlow_c::mQueryList[DUSK_IF_ELSE(54, 53)] = {
|
||||
DUSK_GAME_DATA queryFunc dMsgFlow_c::mQueryList[DUSK_IF_ELSE(56, 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,
|
||||
@@ -817,7 +888,7 @@ DUSK_GAME_DATA queryFunc dMsgFlow_c::mQueryList[DUSK_IF_ELSE(54, 53)] = {
|
||||
&dMsgFlow_c::query049, &dMsgFlow_c::query050, &dMsgFlow_c::query051, &dMsgFlow_c::query052,
|
||||
&dMsgFlow_c::query053,
|
||||
#if TARGET_PC
|
||||
&dMsgFlow_c::query054,
|
||||
&dMsgFlow_c::query054, &dMsgFlow_c::query055, &dMsgFlow_c::query056
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1836,6 +1907,7 @@ u16 dMsgFlow_c::query053(mesg_flow_node_branch* i_flowNode_p, fopAc_ac_c* i_spea
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
// 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;
|
||||
@@ -1850,7 +1922,33 @@ u16 dMsgFlow_c::query054(mesg_flow_node_branch* i_flowNode_p, fopAc_ac_c* i_spea
|
||||
return ret;
|
||||
}
|
||||
|
||||
DUSK_GAME_DATA eventFunc dMsgFlow_c::mEventList[DUSK_IF_ELSE(45, 43)] = {
|
||||
// Return 0 if can change ToD, else 1 if cannot.
|
||||
u16 dMsgFlow_c::query055(mesg_flow_node_branch* i_flowNode_p, fopAc_ac_c* i_speaker_p, int param_2) {
|
||||
// This function is based on query044 which is used to determine whether to show the Midna menu which includes
|
||||
// "Warp" or not. We also add a check to ensure the current environment is not twilight. "R_SP161" is STAR tent.
|
||||
if (!dKy_darkworld_check() && !daAlink_c::checkForestOldCentury() &&
|
||||
(daAlink_c::checkField() || daAlink_c::checkCastleTown()) &&
|
||||
!daAlink_c::checkStageName("R_SP161"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Return 0 if can return to dungeon entrance, 1 if in dungeon but can only return to spawn, or 2 if not in dungeon.
|
||||
u16 dMsgFlow_c::query056(mesg_flow_node_branch* i_flowNode_p, fopAc_ac_c* i_speaker_p, int param_2) {
|
||||
// Darkhammer is highest numbered dungeon map stage id
|
||||
if (getStageID() <= Darkhammer) {
|
||||
if (randomizer_GetContext().mReturnToPlaceOverrides.contains(getStageID())) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
DUSK_GAME_DATA eventFunc dMsgFlow_c::mEventList[DUSK_IF_ELSE(46, 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,
|
||||
&dMsgFlow_c::event008, &dMsgFlow_c::event009, &dMsgFlow_c::event010, &dMsgFlow_c::event011,
|
||||
@@ -1863,7 +1961,7 @@ DUSK_GAME_DATA eventFunc dMsgFlow_c::mEventList[DUSK_IF_ELSE(45, 43)] = {
|
||||
&dMsgFlow_c::event036, &dMsgFlow_c::event037, &dMsgFlow_c::event038, &dMsgFlow_c::event039,
|
||||
&dMsgFlow_c::event040, &dMsgFlow_c::event041, &dMsgFlow_c::event042,
|
||||
#if TARGET_PC
|
||||
&dMsgFlow_c::event043, &dMsgFlow_c::event044,
|
||||
&dMsgFlow_c::event043, &dMsgFlow_c::event044, &dMsgFlow_c::event045
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -2733,29 +2831,52 @@ int dMsgFlow_c::event044(mesg_flow_node_event* i_flowNode_p, fopAc_ac_c* i_speak
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dMsgFlow_c::event045(mesg_flow_node_event* i_flowNode_p, fopAc_ac_c* i_speaker_p) {
|
||||
int prm0 = getParam(i_flowNode_p->params);
|
||||
randomizer_returnToSpawn(prm0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Rando patch funcs
|
||||
|
||||
u32 dMsgFlow_c::getKeyForIndex(u16 nodeIdx) {
|
||||
u32 key = (dMsgObject_getGroupID() << 16) | nodeIdx;
|
||||
// Change group to custom group if custom index
|
||||
if (nodeIdx >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
key = (CUSTOM_BMG_GROUP << 16) | nodeIdx;
|
||||
}
|
||||
// Dirty check to see if this node is part of bmg 0
|
||||
else if (*(reinterpret_cast<u16*>(&mFlowNodeTBL[0])) == 0x0803) {
|
||||
key &= 0x0000FFFF;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void dMsgFlow_c::randoPatchNodeType(u8& type, u16 nodeIdx) {
|
||||
// Overwrite the type if we have a patch for this index
|
||||
if (randomizer_IsActive()) {
|
||||
u32 key = (dMsgObject_getGroupID() << 16) | nodeIdx;
|
||||
// Dirty check to see if this node is part of bmg 0
|
||||
if (*(reinterpret_cast<u16*>(&mFlowNodeTBL[0])) == 0x0803) {
|
||||
key &= 0x0000FFFF;
|
||||
}
|
||||
auto key = getKeyForIndex(nodeIdx);
|
||||
if (randomizer_GetContext().mFlowPatches.contains(key)) {
|
||||
type = reinterpret_cast<mesg_flow_node*>(&randomizer_GetContext().mFlowPatches[key])->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dMsgFlow_c::randoPatchMessageNode(mesg_flow_node*& message_node, u16 nodeIdx) {
|
||||
// Overwrite this node if we have a patch for it
|
||||
if (randomizer_IsActive()) {
|
||||
auto key = getKeyForIndex(nodeIdx);
|
||||
if (randomizer_GetContext().mFlowPatches.contains(key)) {
|
||||
message_node = reinterpret_cast<mesg_flow_node*>(&randomizer_GetContext().mFlowPatches[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dMsgFlow_c::randoPatchBranchNode(mesg_flow_node_branch*& branch_node, u16 nodeIdx) {
|
||||
// Overwrite this node if we have a patch for it
|
||||
if (randomizer_IsActive()) {
|
||||
u32 key = (dMsgObject_getGroupID() << 16) | nodeIdx;
|
||||
// Dirty check to see if this node is part of bmg 0
|
||||
if (*(reinterpret_cast<u16*>(&mFlowNodeTBL[0])) == 0x0803) {
|
||||
key &= 0x0000FFFF;
|
||||
}
|
||||
auto key = getKeyForIndex(nodeIdx);
|
||||
if (randomizer_GetContext().mFlowPatches.contains(key)) {
|
||||
branch_node = reinterpret_cast<mesg_flow_node_branch*>(&randomizer_GetContext().mFlowPatches[key]);
|
||||
}
|
||||
@@ -2765,11 +2886,7 @@ void dMsgFlow_c::randoPatchBranchNode(mesg_flow_node_branch*& branch_node, u16 n
|
||||
void dMsgFlow_c::randoPatchEventNode(mesg_flow_node_event*& event_node, u16 nodeIdx) {
|
||||
// Overwrite this node if we have a patch for it
|
||||
if (randomizer_IsActive()) {
|
||||
u32 key = (dMsgObject_getGroupID() << 16) | nodeIdx;
|
||||
// Dirty check to see if this node is part of bmg 0
|
||||
if (*(reinterpret_cast<u16*>(&mFlowNodeTBL[0])) == 0x0803) {
|
||||
key &= 0x0000FFFF;
|
||||
}
|
||||
auto key = getKeyForIndex(nodeIdx);
|
||||
if (randomizer_GetContext().mFlowPatches.contains(key)) {
|
||||
event_node = reinterpret_cast<mesg_flow_node_event*>(&randomizer_GetContext().mFlowPatches[key]);
|
||||
}
|
||||
|
||||
+26
-2
@@ -31,6 +31,7 @@
|
||||
|
||||
#if TARGET_PC
|
||||
#include "d/d_item.h"
|
||||
#include "dusk/randomizer/game/custom_flow_ids.hpp"
|
||||
#include "dusk/randomizer/game/messages.hpp"
|
||||
#include "dusk/randomizer/game/randomizer_context.hpp"
|
||||
#include "dusk/randomizer/game/stages.h"
|
||||
@@ -45,6 +46,13 @@
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
// Macro to call our custom function for getting attributes
|
||||
#if TARGET_PC
|
||||
#define ENTRIES(i) getEntry(i)
|
||||
#else
|
||||
#define ENTRIES(i) entries[i]
|
||||
#endif
|
||||
|
||||
static void dMsgObject_addFundRaising(s16 param_0);
|
||||
static void dMsgObject_addTotalPayment(s16 param_0);
|
||||
|
||||
@@ -701,7 +709,7 @@ void dMsgObject_c::setMessageIndex(u32 revoIndex, u32 param_2, bool param_3) {
|
||||
JMSMesgInfo_c* pMsg = (JMSMesgInfo_c*)((char*)mpMsgDt + 0x20);
|
||||
u8* iVar2 = (u8*)pMsg + pMsg->header.size;
|
||||
u32 msg_id = getMessageIndex(revoIndex);
|
||||
dComIfGp_setMesgCameraAttrInfo(pMsg->entries[msg_id].camera_id);
|
||||
dComIfGp_setMesgCameraAttrInfo(pMsg->ENTRIES(msg_id).camera_id);
|
||||
if (field_0x15c == 1000) {
|
||||
mpRefer->setSelMsgPtr(NULL);
|
||||
} else {
|
||||
@@ -714,6 +722,10 @@ void dMsgObject_c::setMessageIndex(u32 revoIndex, u32 param_2, bool param_3) {
|
||||
// This is where the game sets the pointer to the string for message choices.
|
||||
// If any of our custom messages are for message choices, override them here
|
||||
if (randomizer_IsActive()) {
|
||||
// Change to custom group if we have a custom message index
|
||||
if (msgIndex >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
groupID = CUSTOM_BMG_GROUP;
|
||||
}
|
||||
auto override = GetTextOverride(groupID, field_0x15c);
|
||||
if (override != NULL) {
|
||||
my_ptr = override;
|
||||
@@ -750,7 +762,7 @@ void dMsgObject_c::setMessageIndexDemo(u32 revoMsgIndex, bool param_2) {
|
||||
JMSMesgInfo_c* info_header_p = (JMSMesgInfo_c*)((char*)mpMsgDt + 0x20);
|
||||
JMSMesgInfo_c* reg_25 = (JMSMesgInfo_c*)((char*) info_header_p + info_header_p->header.size);
|
||||
int ind = getMessageIndex(revoMsgIndex);
|
||||
dComIfGp_setMesgCameraAttrInfo(info_header_p->entries[ind].camera_id);
|
||||
dComIfGp_setMesgCameraAttrInfo(info_header_p->ENTRIES(ind).camera_id);
|
||||
mpRefer->setSelMsgPtr(NULL);
|
||||
if (param_2) {
|
||||
mpCtrl->setMessageID(mMessageID, 0, NULL);
|
||||
@@ -758,6 +770,13 @@ void dMsgObject_c::setMessageIndexDemo(u32 revoMsgIndex, bool param_2) {
|
||||
}
|
||||
|
||||
u32 dMsgObject_c::getMessageIndex(u32 param_0) {
|
||||
#if TARGET_PC
|
||||
// Directly return our index if it's above the custom id base
|
||||
if (randomizer_IsActive() && param_0 >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
return param_0;
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 i = 0;
|
||||
JMSMesgInfo_c* pMsg = (JMSMesgInfo_c*)((char*)mpMsgDt + 0x20);
|
||||
u32 msgIndexCount = *((BE(u16)*)((char*)mpMsgDt + 0x28));
|
||||
@@ -778,6 +797,11 @@ u32 dMsgObject_c::getMessageIndex(u32 param_0) {
|
||||
u32 dMsgObject_c::getRevoMessageIndex(u32 param_1) {
|
||||
#if TARGET_PC
|
||||
if (randomizer_IsActive()) {
|
||||
// Directly return our index if it's custom
|
||||
if (param_1 >= BASE_CUSTOM_MSG_AND_FLOW_ID) {
|
||||
return param_1;
|
||||
}
|
||||
|
||||
// Special case for Ilia Memory Reward Text (param_1 is msgId)
|
||||
// If we're in the sanctuary cutscene where we get the reward, override the text.
|
||||
// Otherwise, the regular item text for the horse call would be overridden if we find it
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "dolphin/types.h"
|
||||
|
||||
inline constexpr u16 BASE_CUSTOM_MSG_AND_FLOW_ID = 21000;
|
||||
inline constexpr u16 CUSTOM_BMG_GROUP = 9;
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "JSystem/JMessage/control.h"
|
||||
#include "d/d_msg_class.h"
|
||||
#include "randomizer_context.hpp"
|
||||
#include "custom_flow_ids.hpp"
|
||||
|
||||
#include "dusk/version.hpp"
|
||||
|
||||
@@ -78,6 +79,21 @@ void HandleTextOverrides(JMessage::TControl* control, JMessage::TProcessor const
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleCustomText(JMessage::TControl* control, u16 msgId) {
|
||||
if (randomizer_IsActive()) {
|
||||
u32 key = (CUSTOM_BMG_GROUP << 16) | msgId;
|
||||
auto& textOverrides = randomizer_GetContext().mTextOverrides;
|
||||
u8 language = getLanguageForOverride();
|
||||
if (textOverrides.at(language).contains(key)) {
|
||||
control->pMessageText_begin_ = GetFormatedTextOverride(key, textOverrides[language][key]);
|
||||
defaultEntry.message_id = msgId;
|
||||
control->pEntry_ = &defaultEntry;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Used in special cases
|
||||
char* GetTextOverride(s16 groupID, u32 messageId) {
|
||||
u32 key = (groupID << 16) | messageId;
|
||||
|
||||
@@ -8,4 +8,6 @@ struct TControl;
|
||||
|
||||
void HandleTextOverrides(JMessage::TControl* control, JMessage::TProcessor const* pProcessor, int groupID, int index);
|
||||
|
||||
bool HandleCustomText(JMessage::TControl* control, u16 msgId);
|
||||
|
||||
char* GetTextOverride(s16 groupID, u32 messageId);
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/data.hpp"
|
||||
#include "dusk/ui/rando_config.hpp"
|
||||
#include "dusk/randomizer/game/flags.h"
|
||||
#include "dusk/randomizer/game/tools.h"
|
||||
@@ -13,14 +12,17 @@
|
||||
#include "dusk/randomizer/generator/utility/yaml.hpp"
|
||||
#include "dusk/randomizer/generator/randomizer.hpp"
|
||||
#include "dusk/randomizer/generator/utility/text.hpp"
|
||||
#include "dusk/randomizer/generator/utility/string.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "custom_flow_ids.hpp"
|
||||
#include "d/actor/d_a_alink.h"
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "d/d_meter2.h"
|
||||
#include "d/d_meter2_draw.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
#include "d/d_msg_class.h"
|
||||
#include "d/d_msg_flow.h"
|
||||
|
||||
std::optional<std::string> RandomizerContext::WriteToFile() {
|
||||
@@ -103,6 +105,12 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
|
||||
|
||||
out["mFlowPatches"] = this->mFlowPatches;
|
||||
|
||||
for (const auto& [key, branchOverrides]: this->mFlowPatchesBranchOverrides) {
|
||||
for (auto override : branchOverrides) {
|
||||
out["mFlowPatchesBranchOverrides"][key].push_back(override);
|
||||
}
|
||||
}
|
||||
|
||||
// Dump text overrides as binary to avoid losing intentional null characters
|
||||
YAML::Emitter textData;
|
||||
textData << YAML::BeginMap;
|
||||
@@ -125,6 +133,10 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
|
||||
out["mEntranceOverrides"][key] = std::bit_cast<int>(override);
|
||||
}
|
||||
|
||||
for (const auto& [key, override] : mReturnToPlaceOverrides) {
|
||||
out["mReturnToPlaceOverrides"][key] = std::bit_cast<int>(override);
|
||||
}
|
||||
|
||||
seedData << YAML::Dump(out);
|
||||
seedData << '\n' << textData.c_str();
|
||||
seedData.close();
|
||||
@@ -270,6 +282,15 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
|
||||
this->mFlowPatches[key] = value;
|
||||
}
|
||||
|
||||
// Flow Patch Branch Overrides
|
||||
for (const auto& flowNode : in["mFlowPatchesBranchOverrides"]) {
|
||||
auto key = flowNode.first.as<u32>();
|
||||
for (const auto& branchNode : flowNode.second) {
|
||||
auto override = branchNode.as<u16>();
|
||||
this->mFlowPatchesBranchOverrides[key].push_back(override);
|
||||
}
|
||||
}
|
||||
|
||||
// Text Overrides
|
||||
for (const auto& languageNode: in["mTextOverrides"]) {
|
||||
const auto& languageStr = languageNode.first.as<std::string>();
|
||||
@@ -289,6 +310,13 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
|
||||
this->mEntranceOverrides[key] = override;
|
||||
}
|
||||
|
||||
// Return to Place Overrides
|
||||
for (const auto& entranceNode : in["mReturnToPlaceOverrides"]) {
|
||||
auto key = entranceNode.first.as<int>();
|
||||
auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<int>());
|
||||
this->mReturnToPlaceOverrides[key] = override;
|
||||
}
|
||||
|
||||
dusk::ui::push_toast(dusk::ui::Toast{
|
||||
.title = "Randomizer",
|
||||
.content = fmt::format("Loaded Randomizer Seed {}", this->mHash),
|
||||
@@ -896,6 +924,46 @@ bool randomizer_mirrorChamberWallShouldExist() {
|
||||
(mirrorChamberAccess == RandomizerContext::BARRIER && !dComIfGs_isStageBossEnemy(0x13));
|
||||
}
|
||||
|
||||
void randomizer_returnToSpawn(bool tryOverride) {
|
||||
|
||||
auto& placeOverrides = randomizer_GetContext().mReturnToPlaceOverrides;
|
||||
auto stageId = getStageID();
|
||||
|
||||
// If we're trying to override the default return to spawn
|
||||
if (tryOverride && placeOverrides.contains(stageId)) {
|
||||
auto entrance = placeOverrides[stageId];
|
||||
|
||||
// If in lakebed temple, spawn on land if shadow crystal is obtained like vanilla
|
||||
if (entrance.stageId == Lakebed_Temple && dComIfGs_isEventBit(TRANSFORMING_UNLOCKED)) {
|
||||
entrance.pointNo = 2;
|
||||
}
|
||||
|
||||
dComIfGp_setNextStage(allStages[entrance.stageId], entrance.pointNo, entrance.roomNo, entrance.mapLayer);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a player hasn't completed a twilight/MDH, we want to unset the transform flag so they aren't forced to be wolf
|
||||
// unnecessarily.
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
if (!dComIfGs_isDarkClearLV(i)) {
|
||||
dComIfGs_offTransformLV(i);
|
||||
}
|
||||
}
|
||||
|
||||
// If Midna's Desperate Hour is not complete, unset the flags that trigger it incase the player
|
||||
// used return to spawn while MDH was active
|
||||
if (!dComIfGs_isEventBit(MIDNAS_DESPERATE_HOUR_COMPLETED)) {
|
||||
dComIfGs_offStageSwitch(4, 0xE);
|
||||
dComIfGs_offEventBit(MIDNAS_DESPERATE_HOUR_STARTED);
|
||||
}
|
||||
|
||||
// Turn the player back into Link if they are currently wolf
|
||||
dComIfGs_setTransformStatus(TF_STATUS_HUMAN);
|
||||
|
||||
// Return to spawn. If the spawn has been randomized, that's taken care of within the function
|
||||
dComIfGp_setNextStage("F_SP103", 1, 1, -1);
|
||||
}
|
||||
|
||||
u8 randomizer_getRandomFoolishItemModelID() {
|
||||
static constexpr auto foolishItemModels = std::to_array<u8>({
|
||||
dItemNo_Randomizer_ARMOR_e,
|
||||
@@ -1299,12 +1367,42 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
||||
}
|
||||
}
|
||||
|
||||
// Give custom flows and message new indices as we read them in
|
||||
std::unordered_map<std::string, u16> customMessageIDs{};
|
||||
std::unordered_map<std::string, u16> customFlowIDs{};
|
||||
u16 curCustomMessageID = BASE_CUSTOM_MSG_AND_FLOW_ID;
|
||||
u16 curCustomFlowID = BASE_CUSTOM_MSG_AND_FLOW_ID;
|
||||
|
||||
// Flow Patches
|
||||
auto flowPatches = LOAD_EMBED_YAML(RANDO_DATA_PATH "flow_patches.yaml");
|
||||
for (const auto& groupNode : flowPatches) {
|
||||
u8 groupNo = groupNode.first.as<u8>();
|
||||
for (const auto& flowNode : groupNode.second) {
|
||||
u16 index = flowNode["index"].as<u16>();
|
||||
std::string name{};
|
||||
std::list<u16> indices{};
|
||||
if (flowNode["index"]) {
|
||||
if (flowNode["index"].IsSequence()) {
|
||||
for (const auto& indexNode : flowNode["index"]) {
|
||||
indices.push_back(indexNode.as<u16>());
|
||||
}
|
||||
name = std::to_string(indices.front());
|
||||
}
|
||||
else if (flowNode["index"].IsScalar()) {
|
||||
auto index = flowNode["index"].as<u16>();
|
||||
indices.push_back(index);
|
||||
name = std::to_string(index);
|
||||
}
|
||||
} else {
|
||||
name = flowNode["name"].as<std::string>();
|
||||
if (customFlowIDs.contains(name)) {
|
||||
indices.push_back(customFlowIDs[name]);
|
||||
} else {
|
||||
auto newFlowIndex = curCustomFlowID++;
|
||||
indices.push_back(newFlowIndex);
|
||||
customFlowIDs[name] = newFlowIndex;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& type = flowNode["type"].as<std::string>();
|
||||
u64 value{};
|
||||
if (type == "branch") {
|
||||
@@ -1314,21 +1412,97 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
||||
branch->query_idx = flowNode["query"].as<u16>();
|
||||
branch->param = flowNode["parameters"].as<u16>();
|
||||
branch->next_node_idx = flowNode["next node index"].as<u16>();
|
||||
if (flowNode["results"]) {
|
||||
if (flowNode["results"].size() != branch->field_0x1) {
|
||||
throw std::runtime_error(fmt::format("Flow results size for {} "
|
||||
"do not match num results. (expected: {}. size: {})", name, branch->field_0x1, flowNode["results"].size()));
|
||||
}
|
||||
for (const auto& resultNode : flowNode["results"]) {
|
||||
auto resultStr = resultNode.as<std::string>();
|
||||
auto resultInt = randomizer::utility::str::toInt(resultStr);
|
||||
u16 resultIndex{};
|
||||
if (resultInt.has_value()) {
|
||||
resultIndex = resultInt.value();
|
||||
} else {
|
||||
if (customFlowIDs.contains(resultStr)) {
|
||||
resultIndex = customFlowIDs[resultStr];
|
||||
} else {
|
||||
auto newFlowIndex = curCustomFlowID++;
|
||||
resultIndex = newFlowIndex;
|
||||
customFlowIDs[resultStr] = newFlowIndex;
|
||||
}
|
||||
}
|
||||
for (auto index : indices) {
|
||||
u32 key = (groupNo << 16) | index;
|
||||
randoData.mFlowPatchesBranchOverrides[key].push_back(resultIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == "event") {
|
||||
auto event = reinterpret_cast<mesg_flow_node_event*>(&value);
|
||||
event->type = 3;
|
||||
event->event_idx = flowNode["event"].as<u8>();
|
||||
event->next_node_idx = flowNode["next node index"].as<u16>();
|
||||
// Check to see if we're setting a custom node as our next node
|
||||
auto nextNodeIndexStr = flowNode["next node index"].as<std::string>();
|
||||
auto nextNodeIndex = randomizer::utility::str::toInt(nextNodeIndexStr);
|
||||
// If we have a regular index, then assume we're using an existing node index
|
||||
if (nextNodeIndex.has_value()) {
|
||||
event->next_node_idx = nextNodeIndex.value();
|
||||
// If we don't, assume we're setting the next node to be a custom flow node
|
||||
} else {
|
||||
if (customFlowIDs.contains(nextNodeIndexStr)) {
|
||||
event->next_node_idx = customFlowIDs.at(nextNodeIndexStr);
|
||||
} else {
|
||||
auto newFlowIndex = curCustomFlowID++;
|
||||
event->next_node_idx = newFlowIndex;
|
||||
customFlowIDs[nextNodeIndexStr] = newFlowIndex;
|
||||
}
|
||||
}
|
||||
u32 params = flowNode["parameters"].as<u32>();
|
||||
event->params[0] = (params >> 24) & 0xFF;
|
||||
event->params[1] = (params >> 16) & 0xFF;
|
||||
event->params[2] = (params >> 8) & 0xFF;
|
||||
event->params[3] = params & 0xFF;
|
||||
} else if (type == "message") {
|
||||
auto message = reinterpret_cast<mesg_flow_node*>(&value);
|
||||
message->type = 1;
|
||||
// Check to see if we have a custom index (identified by string)
|
||||
auto infIndexStr = flowNode["inf index"].as<std::string>();
|
||||
auto infIndex = randomizer::utility::str::toInt(infIndexStr);
|
||||
if (infIndex.has_value()) {
|
||||
message->msg_index = infIndex.value();
|
||||
} else {
|
||||
if (customMessageIDs.contains(infIndexStr)) {
|
||||
message->msg_index = customMessageIDs.at(infIndexStr);
|
||||
} else {
|
||||
auto newMsgIndex = curCustomMessageID++;
|
||||
message->msg_index = newMsgIndex;
|
||||
customMessageIDs[infIndexStr] = newMsgIndex;
|
||||
}
|
||||
}
|
||||
// message->next_node_idx = flowNode["next flow index"].as<u16>();
|
||||
// Check to see if we're setting a custom node as our next node
|
||||
auto nextNodeIndexStr = flowNode["next flow index"].as<std::string>();
|
||||
auto nextNodeIndex = randomizer::utility::str::toInt(nextNodeIndexStr);
|
||||
// If we have a regular index, then assume we're using an existing node index
|
||||
if (nextNodeIndex.has_value()) {
|
||||
message->next_node_idx = nextNodeIndex.value();
|
||||
// If we don't, assume we're setting the next node to be a custom flow node
|
||||
} else {
|
||||
if (customFlowIDs.contains(nextNodeIndexStr)) {
|
||||
message->next_node_idx = customFlowIDs.at(nextNodeIndexStr);
|
||||
} else {
|
||||
auto newFlowIndex = curCustomFlowID++;
|
||||
message->next_node_idx = newFlowIndex;
|
||||
customFlowIDs[nextNodeIndexStr] = newFlowIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto index : indices) {
|
||||
u32 key = (groupNo << 16) | index;
|
||||
randoData.mFlowPatches[key] = value;
|
||||
}
|
||||
|
||||
u32 key = (groupNo << 16) | index;
|
||||
randoData.mFlowPatches[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1336,6 +1510,27 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
||||
auto textOverrides = LOAD_EMBED_YAML(RANDO_DATA_PATH "text/text_overrides.yaml");
|
||||
for (const auto& overrideNode : textOverrides) {
|
||||
const auto& name = overrideNode["Name"].as<std::string>();
|
||||
u8 group;
|
||||
u16 messageId;
|
||||
if (overrideNode["Group"]) {
|
||||
group = overrideNode["Group"].as<u8>();
|
||||
} else {
|
||||
// If no group specified, assume custom bmg group
|
||||
group = CUSTOM_BMG_GROUP;
|
||||
}
|
||||
if (overrideNode["Message Id"]) {
|
||||
messageId = overrideNode["Message Id"].as<u16>();
|
||||
} else {
|
||||
// If no message id specified, assign custom message id based on name
|
||||
if (customMessageIDs.contains(name)) {
|
||||
messageId = customMessageIDs[name];
|
||||
} else {
|
||||
auto newMessageId = curCustomMessageID++;
|
||||
messageId = newMessageId;
|
||||
customMessageIDs[name] = newMessageId;
|
||||
}
|
||||
}
|
||||
u32 key = (group << 16) | messageId;
|
||||
for (auto language : randomizer::supportedLanguages) {
|
||||
std::string text;
|
||||
if (world->GetTextDatabase().contains(name)) {
|
||||
@@ -1343,12 +1538,30 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
||||
} else {
|
||||
text = randomizer::getTextStr(name, randomizer::Text::STANDARD, language);
|
||||
}
|
||||
u8 group = overrideNode["Group"].as<u8>();
|
||||
u16 messageId = overrideNode["Message Id"].as<u16>();
|
||||
u32 key = (group << 16) | messageId;
|
||||
|
||||
randomizer::applyMessageCodes(text);
|
||||
randoData.mTextOverrides[language][key] = text;
|
||||
}
|
||||
|
||||
// If we have custom attributes
|
||||
if (overrideNode["Attributes"]) {
|
||||
auto attributesStr = overrideNode["Attributes"].as<std::string>();
|
||||
auto attributesVec = HexToBytes(attributesStr);
|
||||
if (attributesVec.size() != 16) {
|
||||
throw std::runtime_error(fmt::format("Attributes for Text Override {} "
|
||||
"are the wrong length. (Expected: 16, Actual: {}", name, attributesVec.size()));
|
||||
}
|
||||
|
||||
std::array<u8, 20> attributes{};
|
||||
for (size_t i = 0; i < attributesVec.size(); ++i) {
|
||||
attributes[i + 4] = attributesVec[i];
|
||||
}
|
||||
|
||||
attributes[4] = messageId >> 8;
|
||||
attributes[5] = messageId & 0xFF;
|
||||
|
||||
randoData.mAttributeOverrides[key] = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
// Entrance Overrides
|
||||
@@ -1372,6 +1585,27 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
||||
randoData.mEntranceOverrides[std::bit_cast<int>(original)] = override;
|
||||
}
|
||||
|
||||
// Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented
|
||||
static const std::list<std::pair<std::list<int>, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{
|
||||
{{Forest_Temple, Ook, Diababa}, {Forest_Temple, 22, 0, -1}},
|
||||
{{Goron_Mines, Dangoro, Fyrus}, {Goron_Mines, 1, 0, -1}},
|
||||
{{Lakebed_Temple, Deku_Toad, Morpheel}, {Lakebed_Temple, 0, 0, -1}},
|
||||
{{Arbiters_Grounds, Death_Sword, Stallord}, {Arbiters_Grounds, 0, 0, -1}},
|
||||
{{Snowpeak_Ruins, Darkhammer, Blizzeta}, {Snowpeak_Ruins, 0, 0, -1}},
|
||||
{{Temple_of_Time, Darknut, Armogohma}, {Temple_of_Time, 0, 0, -1}},
|
||||
{{City_in_the_Sky, Aeralfos, Argorok}, {City_in_the_Sky, 0, 3, -1}},
|
||||
{{Palace_of_Twilight, Phantom_Zant_1,
|
||||
Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {Palace_of_Twilight, 0, 0, -1}},
|
||||
{{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {Hyrule_Castle, 11, 0, -1}},
|
||||
};
|
||||
|
||||
// Return to Place Overrides
|
||||
for (const auto& [stages, returnPlace] : defaultPlaceOverrides) {
|
||||
for (auto stage : stages) {
|
||||
randoData.mReturnToPlaceOverrides[stage] = returnPlace;
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(randoData);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ public:
|
||||
std::unordered_map<u32, std::list<std::vector<u8>>> mObjectAdditions{};
|
||||
// std::unordered_map<u32, std::unordered_set<u32>> mTgscDeletions{};
|
||||
std::unordered_map<u32, u64> mFlowPatches{};
|
||||
std::unordered_map<u32, std::vector<u16>> mFlowPatchesBranchOverrides{};
|
||||
std::unordered_map<u32, std::array<u8, 20>> mAttributeOverrides{};
|
||||
|
||||
// struct TextOverride {
|
||||
// std::array<u8, 16> mAttributes{};
|
||||
@@ -80,6 +82,9 @@ public:
|
||||
// keyed by stageId << 24 | pointNo << 16 | roomNo << 8 | mapLayer
|
||||
std::unordered_map<int, EntranceOverride> mEntranceOverrides{};
|
||||
|
||||
// Overrides for returning to spawn. Keyed by stageId
|
||||
std::unordered_map<int, EntranceOverride> mReturnToPlaceOverrides{};
|
||||
|
||||
std::optional<std::string> WriteToFile();
|
||||
std::optional<std::string> LoadFromHash(const std::string& hash);
|
||||
std::filesystem::path GetSeedDataPath() const;
|
||||
@@ -227,6 +232,8 @@ bool randomizer_checkTempleOfTimeRequirement();
|
||||
|
||||
bool randomizer_mirrorChamberWallShouldExist();
|
||||
|
||||
void randomizer_returnToSpawn(bool tryDungeon);
|
||||
|
||||
u8 randomizer_getRandomFoolishItemModelID();
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,19 +3,24 @@
|
||||
# NOTE: All data is expressed in little endian
|
||||
|
||||
0: # zel_00.bmg
|
||||
# Change Talk to Midna options to use custom event 44
|
||||
# which changes the time of day
|
||||
- index: 0x18F
|
||||
# Change the following indices of Midna's flows to jump to our custom choices
|
||||
# instead. These are all the flow jumps that normally determine which "Talk to Midna"
|
||||
# text is displayed.
|
||||
- index:
|
||||
- 0x1
|
||||
- 0x9
|
||||
- 0xC
|
||||
- 0x8F
|
||||
- 0x192
|
||||
- 0x1A4
|
||||
- 0x18F
|
||||
- 0x197
|
||||
- 0x1BC
|
||||
- 0x1C9
|
||||
type: event
|
||||
event: 44
|
||||
event: 42
|
||||
parameters: 0x00000000
|
||||
next node index: 0x116
|
||||
|
||||
- index: 0x192
|
||||
type: event
|
||||
event: 44
|
||||
parameters: 0x00000000
|
||||
next node index: 0x110
|
||||
next node index: Custom Midna Call Begin
|
||||
|
||||
#1: # zel_01.bmg
|
||||
2: # zel_02.bmg
|
||||
@@ -84,3 +89,190 @@
|
||||
#6: # zel_06.bmg
|
||||
#7: # zel_07.bmg
|
||||
#8: # zel_08.bmg
|
||||
|
||||
# Custom Flow Nodes
|
||||
9:
|
||||
# Start of custom midna call tree. Begin by checking if we can change time
|
||||
# of day to see if we show that choice
|
||||
- name: Custom Midna Call Begin
|
||||
type: branch
|
||||
num results: 2
|
||||
query: 54
|
||||
parameters: 0x0000
|
||||
next node index: 0xFFFF
|
||||
results:
|
||||
- Custom Midna Call 3 Choice Select Setup # Can change time of day
|
||||
- Custom Midna Call 2 Choice Select Setup # Can not change time of day
|
||||
|
||||
#################################################
|
||||
# CUSTOM MIDNA CALL 2 CHOICE FLOW #
|
||||
#################################################
|
||||
|
||||
- name: Custom Midna Call 2 Choice Select Setup
|
||||
type: event
|
||||
event: 13
|
||||
parameters: 0x00000003
|
||||
next node index: Custom Midna Call 2 Choice Intro
|
||||
|
||||
- name: Custom Midna Call 2 Choice Intro
|
||||
type: message
|
||||
inf index: Custom Midna Call Need Something Text
|
||||
next flow index: Custom Midna Call 2 Choice
|
||||
|
||||
- name: Custom Midna Call 2 Choice
|
||||
type: message
|
||||
inf index: Custom Midna Call 2 Choice Text
|
||||
next flow index: Custom Midna Call 2 Choice Branch
|
||||
|
||||
- name: Custom Midna Call 2 Choice Branch
|
||||
type: branch
|
||||
num results: 3
|
||||
query: 35
|
||||
parameters: 0x0000
|
||||
next node index: 0xFFFF
|
||||
results:
|
||||
- Custom Midna Call Hints # Hints
|
||||
- Custom Midna Call Return to Spawn Branch # Return to Spawn
|
||||
- 0xFFFF # (B button pressed)
|
||||
|
||||
#################################################
|
||||
# CUSTOM MIDNA CALL 3 CHOICE FLOW #
|
||||
#################################################
|
||||
|
||||
- name: Custom Midna Call 3 Choice Select Setup
|
||||
type: event
|
||||
event: 13
|
||||
parameters: 0x00000004
|
||||
next node index: Custom Midna Call 3 Choice Intro
|
||||
|
||||
- name: Custom Midna Call 3 Choice Intro
|
||||
type: message
|
||||
inf index: Custom Midna Call Need Something Text
|
||||
next flow index: Custom Midna Call 3 Choice
|
||||
|
||||
- name: Custom Midna Call 3 Choice
|
||||
type: message
|
||||
inf index: Custom Midna Call 3 Choice Text
|
||||
next flow index: Custom Midna Call 3 Choice Branch
|
||||
|
||||
- name: Custom Midna Call 3 Choice Branch
|
||||
type: branch
|
||||
num results: 4
|
||||
query: 36
|
||||
parameters: 0x0000
|
||||
next node index: 0xFFFF
|
||||
results:
|
||||
- Custom Midna Call Hints # Hints
|
||||
- Custom Midna Call Change Time # Change time of day
|
||||
- Custom Midna Call Return to Spawn Branch # Return to Spawn
|
||||
- 0xFFFF # (B button pressed)
|
||||
|
||||
#################################################
|
||||
# CUSTOM MIDNA CALL CHOICE OPTIONS #
|
||||
#################################################
|
||||
|
||||
- name: Custom Midna Call Hints
|
||||
type: message
|
||||
inf index: Custom Midna Call Hints Text
|
||||
next flow index: 0xFFFF
|
||||
|
||||
- name: Custom Midna Call Change Time
|
||||
type: event
|
||||
event: 44
|
||||
parameters: 0x00000000
|
||||
next node index: 0x116
|
||||
|
||||
- name: Custom Midna Call Return to Spawn Branch
|
||||
type: branch
|
||||
num results: 3
|
||||
query: 55
|
||||
parameters: 0x0000
|
||||
next node index: 0xFFFF
|
||||
results:
|
||||
- Return to Spawn Dungeon Choice Select Setup
|
||||
- Return to Spawn Dungeon No Choice Select Setup
|
||||
- Return to Base Spawn
|
||||
|
||||
#################################################
|
||||
# RETURN TO SPAWN DUNGEON CHOICE FLOW #
|
||||
#################################################
|
||||
|
||||
- name: Return to Spawn Dungeon Choice Select Setup
|
||||
type: event
|
||||
event: 13
|
||||
parameters: 0x00000004
|
||||
next node index: Return to Spawn Dungeon Choice Intro
|
||||
|
||||
- name: Return to Spawn Dungeon Choice Intro
|
||||
type: message
|
||||
inf index: Return to Spawn Dungeon Intro Text
|
||||
next flow index: Return to Spawn Dungeon Choice
|
||||
|
||||
- name: Return to Spawn Dungeon Choice
|
||||
type: message
|
||||
inf index: Return to Spawn Dungeon Choice Text
|
||||
next flow index: Return to Spawn Dungeon Choice Branch
|
||||
|
||||
- name: Return to Spawn Dungeon Choice Branch
|
||||
type: branch
|
||||
num results: 4
|
||||
query: 36
|
||||
parameters: 0x0000
|
||||
next node index: 0xFFFF
|
||||
results:
|
||||
- Return to Dungeon Spawn # Dungeon entrance
|
||||
- 0xFFFF # Nevermind
|
||||
- Return to Base Spawn # Spawn
|
||||
- 0xFFFF # (B button pressed)
|
||||
|
||||
#################################################
|
||||
# RETURN TO SPAWN DUNGEON NO CHOICE FLOW #
|
||||
#################################################
|
||||
|
||||
- name: Return to Spawn Dungeon No Choice Select Setup
|
||||
type: event
|
||||
event: 13
|
||||
parameters: 0x00000003
|
||||
next node index: Return to Spawn Dungeon No Choice Intro
|
||||
|
||||
- name: Return to Spawn Dungeon No Choice Intro
|
||||
type: message
|
||||
inf index: Return to Spawn Dungeon Intro Text
|
||||
next flow index: Return to Spawn No Dungeon Choice
|
||||
|
||||
- name: Return to Spawn Dungeon No Choice
|
||||
type: message
|
||||
inf index: Return to Spawn Dungeon No Choice Text
|
||||
next flow index: Return to Spawn Dungeon No Choice Branch
|
||||
|
||||
- name: Return to Spawn Dungeon No Choice Branch
|
||||
type: branch
|
||||
num results: 3
|
||||
query: 35
|
||||
parameters: 0x0000
|
||||
next node index: 0xFFFF
|
||||
results:
|
||||
- 0xFFFF # Nevermind
|
||||
- Return to Base Spawn # Spawn
|
||||
- 0xFFFF # (B button pressed)
|
||||
|
||||
#################################################
|
||||
# RETURN TO SPAWN EVENTS #
|
||||
#################################################
|
||||
|
||||
# Use custom event index 45 to setup returning to spawn. A non-zero
|
||||
# parameter value indicates to try and use a place override if one
|
||||
# exists for the current stage
|
||||
- name: Return to Dungeon Spawn
|
||||
type: event
|
||||
event: 45
|
||||
parameters: 0x00000001
|
||||
next node index: 0x116
|
||||
|
||||
# Use custom event index 45 to setup returning to spawn. A parameter
|
||||
# value of zero indicates to always return to the starting spawn
|
||||
- name: Return to Base Spawn
|
||||
type: event
|
||||
event: 45
|
||||
parameters: 0x00000000
|
||||
next node index: 0x116
|
||||
@@ -1935,37 +1935,37 @@ Partially Filled Sky Book Get Item Text:
|
||||
<fast>You got a <red>Sky Character<white>!
|
||||
You've collected <red>{}<white> so far.
|
||||
|
||||
#Midna Call As Human Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into wolf
|
||||
# <choice 2>Change time
|
||||
#
|
||||
#Midna Call As Wolf Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into human
|
||||
# <choice 2>Change time
|
||||
Midna Call As Human Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into wolf
|
||||
<2 way choice 2>Something else
|
||||
|
||||
#Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Warp
|
||||
# <choice 2>Change time
|
||||
Midna Call As Wolf Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into human
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Warp
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Human Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into wolf
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into wolf
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Midna Call As Wolf Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into human
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into human
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Slingshot Shop Text Template:
|
||||
Standard:
|
||||
@@ -2117,9 +2117,9 @@ Castle Town Malo Mart Magic Armor Sold Out Text Template:
|
||||
Charlo Donation Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>100 Rupees
|
||||
<choice 2>50 Rupees
|
||||
<choice 3>Sorry...
|
||||
<3 way choice 1>100 Rupees
|
||||
<3 way choice 2>50 Rupees
|
||||
<3 way choice 3>Sorry...
|
||||
|
||||
Charlo Donation Ask Text Template:
|
||||
Standard:
|
||||
@@ -2159,3 +2159,45 @@ Fishing Hole Sign Text Template:
|
||||
<Item Pretty Name> here! The fish are CRYING!
|
||||
|
||||
Keep the fishing hole clean!
|
||||
|
||||
Custom Midna Call Need Something Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
Need Something?<begin choice>
|
||||
|
||||
Custom Midna Call 3 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Hints
|
||||
<3 way choice 2>Change time of day
|
||||
<3 way choice 3>Return to spawn
|
||||
|
||||
Custom Midna Call 2 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Hints
|
||||
<2 way choice 2>Return to spawn
|
||||
|
||||
Custom Midna Call Hints Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I have no hints to give.
|
||||
|
||||
Return to Spawn Dungeon Intro Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I'll get you out of here.
|
||||
Where do you want to go?<begin choice>
|
||||
|
||||
Return to Spawn Dungeon Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Dungeon entrance
|
||||
<3 way choice 2>Nevermind
|
||||
<3 way choice 3>Spawn
|
||||
|
||||
Return to Spawn Dungeon No Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Nevermind
|
||||
<2 way choice 2>Spawn
|
||||
@@ -1935,37 +1935,37 @@ Partially Filled Sky Book Get Item Text:
|
||||
<fast>You got a <red>Sky Character<white>!
|
||||
You've collected <red>{}<white> so far.
|
||||
|
||||
#Midna Call As Human Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into wolf
|
||||
# <choice 2>Change time
|
||||
#
|
||||
#Midna Call As Wolf Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into human
|
||||
# <choice 2>Change time
|
||||
Midna Call As Human Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into wolf
|
||||
<2 way choice 2>Something else
|
||||
|
||||
#Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Warp
|
||||
# <choice 2>Change time
|
||||
Midna Call As Wolf Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into human
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Warp
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Human Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into wolf
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into wolf
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Midna Call As Wolf Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into human
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into human
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Slingshot Shop Text Template:
|
||||
Standard:
|
||||
@@ -2117,9 +2117,9 @@ Castle Town Malo Mart Magic Armor Sold Out Text Template:
|
||||
Charlo Donation Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>100 Rupees
|
||||
<choice 2>50 Rupees
|
||||
<choice 3>Sorry...
|
||||
<3 way choice 1>100 Rupees
|
||||
<3 way choice 2>50 Rupees
|
||||
<3 way choice 3>Sorry...
|
||||
|
||||
Charlo Donation Ask Text Template:
|
||||
Standard:
|
||||
@@ -2159,3 +2159,45 @@ Fishing Hole Sign Text Template:
|
||||
<Item Pretty Name> here! The fish are CRYING!
|
||||
|
||||
Keep the fishing hole clean!
|
||||
|
||||
Custom Midna Call Need Something Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
Need Something?<begin choice>
|
||||
|
||||
Custom Midna Call 3 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Hints
|
||||
<3 way choice 2>Change time of day
|
||||
<3 way choice 3>Return to spawn
|
||||
|
||||
Custom Midna Call 2 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Hints
|
||||
<2 way choice 2>Return to spawn
|
||||
|
||||
Custom Midna Call Hints Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I have no hints to give.
|
||||
|
||||
Return to Spawn Dungeon Intro Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I'll get you out of here.
|
||||
Where do you want to go?<begin choice>
|
||||
|
||||
Return to Spawn Dungeon Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Dungeon entrance
|
||||
<3 way choice 2>Nevermind
|
||||
<3 way choice 3>Spawn
|
||||
|
||||
Return to Spawn Dungeon No Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Nevermind
|
||||
<2 way choice 2>Spawn
|
||||
@@ -1935,37 +1935,37 @@ Partially Filled Sky Book Get Item Text:
|
||||
<fast>You got a <red>Sky Character<white>!
|
||||
You've collected <red>{}<white> so far.
|
||||
|
||||
#Midna Call As Human Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into wolf
|
||||
# <choice 2>Change time
|
||||
#
|
||||
#Midna Call As Wolf Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into human
|
||||
# <choice 2>Change time
|
||||
Midna Call As Human Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into wolf
|
||||
<2 way choice 2>Something else
|
||||
|
||||
#Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Warp
|
||||
# <choice 2>Change time
|
||||
Midna Call As Wolf Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into human
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Warp
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Human Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into wolf
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into wolf
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Midna Call As Wolf Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into human
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into human
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Slingshot Shop Text Template:
|
||||
Standard:
|
||||
@@ -2117,9 +2117,9 @@ Castle Town Malo Mart Magic Armor Sold Out Text Template:
|
||||
Charlo Donation Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>100 Rupees
|
||||
<choice 2>50 Rupees
|
||||
<choice 3>Sorry...
|
||||
<3 way choice 1>100 Rupees
|
||||
<3 way choice 2>50 Rupees
|
||||
<3 way choice 3>Sorry...
|
||||
|
||||
Charlo Donation Ask Text Template:
|
||||
Standard:
|
||||
@@ -2159,3 +2159,45 @@ Fishing Hole Sign Text Template:
|
||||
<Item Pretty Name> here! The fish are CRYING!
|
||||
|
||||
Keep the fishing hole clean!
|
||||
|
||||
Custom Midna Call Need Something Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
Need Something?<begin choice>
|
||||
|
||||
Custom Midna Call 3 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Hints
|
||||
<3 way choice 2>Change time of day
|
||||
<3 way choice 3>Return to spawn
|
||||
|
||||
Custom Midna Call 2 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Hints
|
||||
<2 way choice 2>Return to spawn
|
||||
|
||||
Custom Midna Call Hints Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I have no hints to give.
|
||||
|
||||
Return to Spawn Dungeon Intro Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I'll get you out of here.
|
||||
Where do you want to go?<begin choice>
|
||||
|
||||
Return to Spawn Dungeon Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Dungeon entrance
|
||||
<3 way choice 2>Nevermind
|
||||
<3 way choice 3>Spawn
|
||||
|
||||
Return to Spawn Dungeon No Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Nevermind
|
||||
<2 way choice 2>Spawn
|
||||
@@ -1935,37 +1935,37 @@ Partially Filled Sky Book Get Item Text:
|
||||
<fast>You got a <red>Sky Character<white>!
|
||||
You've collected <red>{}<white> so far.
|
||||
|
||||
#Midna Call As Human Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into wolf
|
||||
# <choice 2>Change time
|
||||
#
|
||||
#Midna Call As Wolf Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into human
|
||||
# <choice 2>Change time
|
||||
Midna Call As Human Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into wolf
|
||||
<2 way choice 2>Something else
|
||||
|
||||
#Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Warp
|
||||
# <choice 2>Change time
|
||||
Midna Call As Wolf Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into human
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Warp
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Human Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into wolf
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into wolf
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Midna Call As Wolf Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into human
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into human
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Slingshot Shop Text Template:
|
||||
Standard:
|
||||
@@ -2117,9 +2117,9 @@ Castle Town Malo Mart Magic Armor Sold Out Text Template:
|
||||
Charlo Donation Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>100 Rupees
|
||||
<choice 2>50 Rupees
|
||||
<choice 3>Sorry...
|
||||
<3 way choice 1>100 Rupees
|
||||
<3 way choice 2>50 Rupees
|
||||
<3 way choice 3>Sorry...
|
||||
|
||||
Charlo Donation Ask Text Template:
|
||||
Standard:
|
||||
@@ -2159,3 +2159,45 @@ Fishing Hole Sign Text Template:
|
||||
<Item Pretty Name> here! The fish are CRYING!
|
||||
|
||||
Keep the fishing hole clean!
|
||||
|
||||
Custom Midna Call Need Something Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
Need Something?<begin choice>
|
||||
|
||||
Custom Midna Call 3 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Hints
|
||||
<3 way choice 2>Change time of day
|
||||
<3 way choice 3>Return to spawn
|
||||
|
||||
Custom Midna Call 2 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Hints
|
||||
<2 way choice 2>Return to spawn
|
||||
|
||||
Custom Midna Call Hints Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I have no hints to give.
|
||||
|
||||
Return to Spawn Dungeon Intro Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I'll get you out of here.
|
||||
Where do you want to go?<begin choice>
|
||||
|
||||
Return to Spawn Dungeon Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Dungeon entrance
|
||||
<3 way choice 2>Nevermind
|
||||
<3 way choice 3>Spawn
|
||||
|
||||
Return to Spawn Dungeon No Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Nevermind
|
||||
<2 way choice 2>Spawn
|
||||
@@ -1935,37 +1935,37 @@ Partially Filled Sky Book Get Item Text:
|
||||
<fast>You got a <red>Sky Character<white>!
|
||||
You've collected <red>{}<white> so far.
|
||||
|
||||
#Midna Call As Human Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into wolf
|
||||
# <choice 2>Change time
|
||||
#
|
||||
#Midna Call As Wolf Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Transform into human
|
||||
# <choice 2>Change time
|
||||
Midna Call As Human Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into wolf
|
||||
<2 way choice 2>Something else
|
||||
|
||||
#Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
# Standard:
|
||||
# Text: |-
|
||||
# <choice 1>Warp
|
||||
# <choice 2>Change time
|
||||
Midna Call As Wolf Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Transform into human
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Wolf No Shadow Crystal Two Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Warp
|
||||
<2 way choice 2>Something else
|
||||
|
||||
Midna Call As Human Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into wolf
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into wolf
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Midna Call As Wolf Three Choice:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>Transform into human
|
||||
<choice 2>Warp
|
||||
<choice 3>Change time of day
|
||||
<3 way choice 1>Transform into human
|
||||
<3 way choice 2>Warp
|
||||
<3 way choice 3>Something else
|
||||
|
||||
Slingshot Shop Text Template:
|
||||
Standard:
|
||||
@@ -2117,9 +2117,9 @@ Castle Town Malo Mart Magic Armor Sold Out Text Template:
|
||||
Charlo Donation Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<choice 1>100 Rupees
|
||||
<choice 2>50 Rupees
|
||||
<choice 3>Sorry...
|
||||
<3 way choice 1>100 Rupees
|
||||
<3 way choice 2>50 Rupees
|
||||
<3 way choice 3>Sorry...
|
||||
|
||||
Charlo Donation Ask Text Template:
|
||||
Standard:
|
||||
@@ -2159,3 +2159,45 @@ Fishing Hole Sign Text Template:
|
||||
<Item Pretty Name> here! The fish are CRYING!
|
||||
|
||||
Keep the fishing hole clean!
|
||||
|
||||
Custom Midna Call Need Something Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
Need Something?<begin choice>
|
||||
|
||||
Custom Midna Call 3 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Hints
|
||||
<3 way choice 2>Change time of day
|
||||
<3 way choice 3>Return to spawn
|
||||
|
||||
Custom Midna Call 2 Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Hints
|
||||
<2 way choice 2>Return to spawn
|
||||
|
||||
Custom Midna Call Hints Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I have no hints to give.
|
||||
|
||||
Return to Spawn Dungeon Intro Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
I'll get you out of here.
|
||||
Where do you want to go?<begin choice>
|
||||
|
||||
Return to Spawn Dungeon Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<3 way choice 1>Dungeon entrance
|
||||
<3 way choice 2>Nevermind
|
||||
<3 way choice 3>Spawn
|
||||
|
||||
Return to Spawn Dungeon No Choice Text:
|
||||
Standard:
|
||||
Text: |-
|
||||
<2 way choice 1>Nevermind
|
||||
<2 way choice 2>Spawn
|
||||
@@ -223,17 +223,17 @@
|
||||
Group: 0
|
||||
Message Id: 335
|
||||
|
||||
#- Name: Midna Call As Human Two Choice
|
||||
# Group: 0
|
||||
# Message Id: 2004
|
||||
#
|
||||
#- Name: Midna Call As Wolf Two Choice
|
||||
# Group: 0
|
||||
# Message Id: 2005
|
||||
- Name: Midna Call As Human Two Choice
|
||||
Group: 0
|
||||
Message Id: 2004
|
||||
|
||||
#- Name: Midna Call As Wolf No Shadow Crystal Two Choice
|
||||
# Group: 0
|
||||
# Message Id: 2039
|
||||
- Name: Midna Call As Wolf Two Choice
|
||||
Group: 0
|
||||
Message Id: 2005
|
||||
|
||||
- Name: Midna Call As Wolf No Shadow Crystal Two Choice
|
||||
Group: 0
|
||||
Message Id: 2039
|
||||
|
||||
- Name: Midna Call As Human Three Choice
|
||||
Group: 0
|
||||
@@ -374,3 +374,25 @@
|
||||
- Name: Fishing Hole Sign Text
|
||||
Group: 7
|
||||
Message Id: 9502
|
||||
|
||||
# Completely Custom Text IDs
|
||||
- Name: Custom Midna Call Need Something Text
|
||||
Attributes: 0x07F60000150D0000FF00000000000400
|
||||
|
||||
- Name: Custom Midna Call 3 Choice Text
|
||||
Attributes: 0x07F8000015000000FF00000000000400
|
||||
|
||||
- Name: Custom Midna Call 2 Choice Text
|
||||
Attributes: 0x07F8000015000000FF00000000000400
|
||||
|
||||
- Name: Custom Midna Call Hints Text
|
||||
Attributes: 0x07F60000150D0000FF00000000000400
|
||||
|
||||
- Name: Return to Spawn Dungeon Intro Text
|
||||
Attributes: 0x07F60000150D0000FF00000000000400
|
||||
|
||||
- Name: Return to Spawn Dungeon Choice Text
|
||||
Attributes: 0x07F60000150D0000FF00000000000400
|
||||
|
||||
- Name: Return to Spawn Dungeon No Choice Text
|
||||
Attributes: 0x07F60000150D0000FF00000000000400
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <cstdarg>
|
||||
#include <charconv>
|
||||
|
||||
namespace randomizer::utility::str {
|
||||
//can't use codecvt on Wii U, deprecated in c++17 and g++ hates it
|
||||
@@ -49,4 +47,46 @@ namespace randomizer::utility::str {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Takes in a string and returns an optional integer if the string
|
||||
// could be turned into one
|
||||
std::optional<int> toInt(std::string_view str) {
|
||||
// Trim leading/trailing whitespace
|
||||
auto start = str.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string_view::npos) {
|
||||
return std::nullopt;
|
||||
}
|
||||
str.remove_prefix(start);
|
||||
|
||||
auto end = str.find_last_not_of(" \t\r\n");
|
||||
if (end != std::string_view::npos) {
|
||||
str = str.substr(0, end + 1);
|
||||
}
|
||||
|
||||
if (str.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Identify base (only decimal/hexadecimal handled)
|
||||
int base = 10;
|
||||
if (str.size() > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
|
||||
base = 16;
|
||||
str.remove_prefix(2);
|
||||
}
|
||||
|
||||
if (str.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Parse the remaining absolute string
|
||||
int value = 0;
|
||||
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value, base);
|
||||
|
||||
// Ensure conversion succeeded and consumed the entire trimmed string
|
||||
if (ec == std::errc{} && ptr == str.data() + str.size()) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,4 +113,6 @@ namespace randomizer::utility::str {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> toInt(std::string_view str);
|
||||
}
|
||||
|
||||
@@ -330,25 +330,28 @@ namespace randomizer {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
static const std::unordered_map<std::string_view, std::string_view> messageCodes = {
|
||||
{"<fast>", "\x1A\x05\x00\x00\x01"sv},
|
||||
{"<slow>", "\x1A\x05\x00\x00\x02"sv},
|
||||
{"<begin choice>", "\x1A\x05\x00\x00\x20"sv},
|
||||
{"<male>", "\x1A\x05\x06\x00\x02"sv},
|
||||
{"<female>", "\x1A\x05\x06\x00\x03"sv},
|
||||
{"<choice 1>", "\x1A\x06\x00\x00\x09\x01"sv},
|
||||
{"<choice 2>", "\x1A\x06\x00\x00\x09\x02"sv},
|
||||
{"<choice 3>", "\x1A\x06\x00\x00\x09\x03"sv},
|
||||
{"<white>", "\x1A\x06\xFF\x00\x00\x00"sv},
|
||||
{"<red>", "\x1A\x06\xFF\x00\x00\x01"sv},
|
||||
{"<green>", "\x1A\x06\xFF\x00\x00\x02"sv},
|
||||
{"<light blue>", "\x1A\x06\xFF\x00\x00\x03"sv},
|
||||
{"<yellow>", "\x1A\x06\xFF\x00\x00\x04"sv},
|
||||
{"<purple>", "\x1A\x06\xFF\x00\x00\x06"sv},
|
||||
{"<orange>", "\x1A\x06\xFF\x00\x00\x08"sv},
|
||||
{"<player name>", "\x1A\x05\x00\x00\x00"sv},
|
||||
{"<fast>", "\x1A\x05\x00\x00\x01"sv},
|
||||
{"<slow>", "\x1A\x05\x00\x00\x02"sv},
|
||||
{"<begin choice>", "\x1A\x05\x00\x00\x20"sv},
|
||||
{"<male>", "\x1A\x05\x06\x00\x02"sv},
|
||||
{"<female>", "\x1A\x05\x06\x00\x03"sv},
|
||||
{"<2 way choice 1>", "\x1A\x06\x00\x00\x08\x01"sv},
|
||||
{"<2 way choice 2>", "\x1A\x06\x00\x00\x08\x02"sv},
|
||||
{"<3 way choice 1>", "\x1A\x06\x00\x00\x09\x01"sv},
|
||||
{"<3 way choice 2>", "\x1A\x06\x00\x00\x09\x02"sv},
|
||||
{"<3 way choice 3>", "\x1A\x06\x00\x00\x09\x03"sv},
|
||||
{"<white>", "\x1A\x06\xFF\x00\x00\x00"sv},
|
||||
{"<red>", "\x1A\x06\xFF\x00\x00\x01"sv},
|
||||
{"<green>", "\x1A\x06\xFF\x00\x00\x02"sv},
|
||||
{"<light blue>", "\x1A\x06\xFF\x00\x00\x03"sv},
|
||||
{"<yellow>", "\x1A\x06\xFF\x00\x00\x04"sv},
|
||||
{"<purple>", "\x1A\x06\xFF\x00\x00\x06"sv},
|
||||
{"<orange>", "\x1A\x06\xFF\x00\x00\x08"sv},
|
||||
// custom colors
|
||||
{"<dark green>", "\x1A\x06\xFF\x00\x00\x09"sv},
|
||||
{"<blue>", "\x1A\x06\xFF\x00\x00\x0A"sv},
|
||||
{"<silver>", "\x1A\x06\xFF\x00\x00\x0B"sv},
|
||||
{"<dark green>", "\x1A\x06\xFF\x00\x00\x09"sv},
|
||||
{"<blue>", "\x1A\x06\xFF\x00\x00\x0A"sv},
|
||||
{"<silver>", "\x1A\x06\xFF\x00\x00\x0B"sv},
|
||||
};
|
||||
|
||||
void breakLines(std::string& str, int maxLineWidth) {
|
||||
|
||||
Reference in New Issue
Block a user