From c45c0f3d1258c5ceebac2b98af106a47235259b4 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 15 Apr 2026 16:11:49 +0200 Subject: [PATCH 1/6] DecodeTargetEvaluationMode: Initialize id_0 and id_1 in all code paths --- src/game_interpreter_shared.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/game_interpreter_shared.cpp b/src/game_interpreter_shared.cpp index 6d8f417419..c2bf508cd0 100644 --- a/src/game_interpreter_shared.cpp +++ b/src/game_interpreter_shared.cpp @@ -78,6 +78,10 @@ inline bool Game_Interpreter_Shared::DecodeTargetEvaluationMode(lcf::rpg::EventC return true; } break; + default: + id_0 = 0; + id_1 = 0; + break; } if constexpr (validate_patches) { From e5c72be1a026a0a5f47228d6842e310596f68767 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 15 Apr 2026 16:11:54 +0200 Subject: [PATCH 2/6] ControlSwitches: Only handle values 0, 1 and 2. More compatible with RPG_RT. Prior we accepted any value > 1 for Flip (2) Fix #3535 --- src/game_interpreter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index be764c52e9..e93fb53363 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -1076,16 +1076,16 @@ bool Game_Interpreter::CommandControlSwitches(lcf::rpg::EventCommand const& com) int val = com.parameters[3]; if (start == end) { - if (val < 2) { + if (val == 0 || val == 1) { Main_Data::game_switches->Set(start, val == 0); - } else { + } else if (val == 2) { Main_Data::game_switches->Flip(start); } Game_Map::SetNeedRefreshForSwitchChange(start); } else { - if (val < 2) { + if (val == 0 || val == 1) { Main_Data::game_switches->SetRange(start, end, val == 0); - } else { + } else if (val == 2) { Main_Data::game_switches->FlipRange(start, end); } Game_Map::SetNeedRefresh(true); From e4e947c913b25a01f8440ff3a39bba91dfda8ada Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 15 Apr 2026 16:16:06 +0200 Subject: [PATCH 3/6] Strings ToNum: Use Base 10, otherwise leading 0 are interpreted as a octal number --- src/game_strings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 63eb5d66c3..2dbd0f8c06 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -92,7 +92,7 @@ int Game_Strings::ToNum(Str_Params params, int var_id, Game_Variables& variables if (params.hex) num = static_cast(std::strtol(it->second.c_str(), nullptr, 16)); else - num = static_cast(std::strtol(it->second.c_str(), nullptr, 0)); + num = static_cast(std::strtol(it->second.c_str(), nullptr, 10)); variables.Set(var_id, num); From fea9ed53444fb5280d5da1731e090a8e057aaecd Mon Sep 17 00:00:00 2001 From: Ghabry Date: Wed, 15 Apr 2026 16:29:47 +0200 Subject: [PATCH 4/6] Conditional Branch: Support Maniac Event Exist --- src/game_interpreter.cpp | 16 +++++++++++----- src/game_interpreter.h | 2 +- src/game_interpreter_shared.h | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index e93fb53363..f248d3873a 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -1545,19 +1545,20 @@ std::vector Game_Interpreter::GetActors(int mode, int id) { return actors; } -Game_Character* Game_Interpreter::GetCharacter(int event_id, std::string_view origin) const { +Game_Character* Game_Interpreter::GetCharacter(int event_id, std::string_view origin, bool silent) const { if (event_id == Game_Character::CharThisEvent) { event_id = GetThisEventId(); // Is a common event if (event_id == 0) { // With no map parent + // Still reported even when "silent" as this would hide a bug Output::Warning("{}: Can't use ThisEvent in common event: Not called from a map event", origin); return nullptr; } } Game_Character* ch = Game_Character::GetCharacter(event_id, event_id); - if (!ch) { + if (!ch && !silent) { Output::Warning("{}: Unknown event with id {}", origin, event_id); } return ch; @@ -3623,9 +3624,14 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co chara_id = ValueOrVariable(com.parameters[3], chara_id); } - character = GetCharacter(chara_id, "ConditionalBranch"); - if (character != NULL) { - result = character->GetFacing() == com.parameters[2]; + if (Player::IsPatchManiac() && com.parameters[4] == 1) { + // Existance check + result = GetCharacter(chara_id, "ConditionalBranch", true) != nullptr; + } else { + character = GetCharacter(chara_id, "ConditionalBranch"); + if (character) { + result = character->GetFacing() == com.parameters[2]; + } } break; } diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 47671b2000..c66bff0c2d 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -104,7 +104,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext lcf::rpg::SaveEventExecState GetSaveState(); /** @return Game_Character of the passed event_id */ - Game_Character* GetCharacter(int event_id, std::string_view origin) const override; + Game_Character* GetCharacter(int event_id, std::string_view origin, bool silent = false) const override; /** @return the event_id of the current frame */ int GetCurrentEventId() const; diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index a033aaad18..61ba66ca99 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -252,7 +252,7 @@ class Game_BaseInterpreterContext { virtual ~Game_BaseInterpreterContext() {} virtual int GetThisEventId() const = 0; - virtual Game_Character* GetCharacter(int event_id, std::string_view origin) const = 0; + virtual Game_Character* GetCharacter(int event_id, std::string_view origin, bool silent = false) const = 0; virtual const lcf::rpg::SaveEventExecState& GetState() const = 0; virtual const lcf::rpg::SaveEventExecFrame& GetFrame() const = 0; From 053edab3d6a398ae03842ac128b6d8a8ba17f86e Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 16 Apr 2026 12:15:10 +0200 Subject: [PATCH 5/6] ShowStringPic: Support Var(Indirect) for system name and font name --- src/game_interpreter.cpp | 24 +++++++++++++++++++++ src/maniac_patch.cpp | 45 ++++++++++++++++++++++++++++++++++++++++ src/maniac_patch.h | 11 ++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index f248d3873a..e9a9f44b5f 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4680,7 +4680,31 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con } params.system_name = components[2]; + uint32_t var_id = 0; + auto mode = delims[1] - 1; + if (mode > 0) { + if (!ManiacPatch::DecodeStringToInt(params.system_name, var_id)) { + Output::Warning("ShowStringPic: Bad system name arg (id={}, arg={})", pic_id, components[2]); + return true; + } + + params.system_name = Main_Data::game_strings->GetWithMode( + params.system_name, mode, var_id, *Main_Data::game_variables + ); + } + text.font_name = components[3]; + mode = delims[2] - 1; + if (mode > 0) { + if (!ManiacPatch::DecodeStringToInt(text.font_name, var_id)) { + Output::Warning("ShowStringPic: Bad font name arg (id={}, arg={})", pic_id, components[2]); + return true; + } + + text.font_name = Main_Data::game_strings->GetWithMode( + text.font_name, mode, var_id, *Main_Data::game_variables + ); + } params.texts = {text}; diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index e672e97429..a8693ec8ba 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -834,6 +834,51 @@ std::string_view ManiacPatch::GetLcfDescription(int data_type, int id, bool is_d return {}; } +bool ManiacPatch::DecodeStringToInt(std::string_view str, uint32_t& out) { + /* + Is a custom 5 bit encoding: + + For the rightmost character c the value is: + c - A + 1 + + For all other characters c with index i the value is: + (c - a + 1) << (i * 5) + + Due to integer overflow the max string length is 6. + */ + + out = 0; + + if (!(str.size() > 0 && str.size() <= 6)) { + return false; + } + + auto in_range = [](uint32_t value) { + return value >= 0 && value < 32; + }; + + out = (str.back() - 'A' + 1); + + if (!in_range(out)) { + return false; + } + + str.remove_suffix(1); + + for (size_t i = 0; i < str.size(); ++i) { + uint32_t result = (str[i] - 'a' + 1); + + if (!in_range(result)) { + return false; + } + + int chidx = str.size() - i; + out += (result << (chidx * 5)); + } + + return out; +} + bool ManiacPatch::GlobalSave::Load() { if (!Player::IsPatchManiac()) { return true; diff --git a/src/maniac_patch.h b/src/maniac_patch.h index cf62d453ca..15b89a0ec3 100644 --- a/src/maniac_patch.h +++ b/src/maniac_patch.h @@ -38,6 +38,17 @@ namespace ManiacPatch { std::string_view GetLcfName(int data_type, int id, bool is_dynamic); std::string_view GetLcfDescription(int data_type, int id, bool is_dynamic); + /** + * Decodes an encoded string as used by the ShowStringPicture to the number + * representation. + * + * @param str String to decode + * @param out The number represented by the string + * + * @return Whether the decoding was successful (this function will reject malformed strings) + */ + bool DecodeStringToInt(std::string_view str, uint32_t& out); + namespace GlobalSave { /** * Attempts to load Save.lgs from the save directory. From 1bd0dd0d63f2a9346512dd9aa18200eadd41099e Mon Sep 17 00:00:00 2001 From: Ghabry Date: Fri, 1 May 2026 18:47:47 +0200 Subject: [PATCH 6/6] StringToInt: Use signed variable --- src/game_interpreter.cpp | 2 +- src/maniac_patch.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index e9a9f44b5f..c260d30acc 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4697,7 +4697,7 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con mode = delims[2] - 1; if (mode > 0) { if (!ManiacPatch::DecodeStringToInt(text.font_name, var_id)) { - Output::Warning("ShowStringPic: Bad font name arg (id={}, arg={})", pic_id, components[2]); + Output::Warning("ShowStringPic: Bad font name arg (id={}, arg={})", pic_id, components[3]); return true; } diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index a8693ec8ba..50cee3f2f4 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -853,27 +853,29 @@ bool ManiacPatch::DecodeStringToInt(std::string_view str, uint32_t& out) { return false; } - auto in_range = [](uint32_t value) { + auto in_range = [](int32_t value) { return value >= 0 && value < 32; }; - out = (str.back() - 'A' + 1); + int32_t result = (str.back() - 'A' + 1); - if (!in_range(out)) { + if (!in_range(result)) { return false; } + out = static_cast(result); + str.remove_suffix(1); for (size_t i = 0; i < str.size(); ++i) { - uint32_t result = (str[i] - 'a' + 1); + result = (str[i] - 'a' + 1); if (!in_range(result)) { return false; } int chidx = str.size() - i; - out += (result << (chidx * 5)); + out += static_cast(result << (chidx * 5)); } return out;