From ded0719995eea2476b80a76e9516a994486e4546 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Fri, 12 Dec 2025 06:39:46 -0300 Subject: [PATCH] Maniac CallCommand - Add ResolveEventCommand Introduces the ResolveEventCommand method to process Maniac Patch @cmd (Call Command) event commands, resolving them into their actual form. Refactors interpreter logic to use this method where event commands are accessed, ensuring correct handling of Maniac Patch commands and improving compatibility. Updates function signatures and documentation accordingly. --- src/game_interpreter.cpp | 90 +++++++++++++++++++++++----------------- src/game_interpreter.h | 7 ++++ 2 files changed, 60 insertions(+), 37 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 3e4842ba67..56145d0f4c 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -586,7 +586,7 @@ void Game_Interpreter::SkipToNextConditional(std::initializer_list codes, i } for (++index; index < static_cast(list.size()); ++index) { - const auto& com = list[index]; + const auto& com = ResolveEventCommand(list[index]); if (com.indent > indent) { continue; } @@ -870,17 +870,22 @@ std::vector Game_Interpreter::GetChoices(int max_num_choices) { auto& index = frame.current_command; // Let's find the choices - int current_indent = list[index + 1].indent; + if (index + 1 >= static_cast(list.size())) return {}; + + auto first_opt = ResolveEventCommand(list[index + 1]); + int current_indent = first_opt.indent; + std::vector s_choices; for (int index_temp = index + 1; index_temp < static_cast(list.size()); ++index_temp) { - const auto& com = list[index_temp]; + auto com = ResolveEventCommand(list[index_temp]); + if (com.indent != current_indent) { continue; } if (static_cast(com.code) == Cmd::ShowChoiceOption && com.parameters.size() > 0 && com.parameters[0] < max_num_choices) { // Choice found - s_choices.push_back(ToString(list[index_temp].string)); + s_choices.push_back(ToString(com.string)); } if (static_cast(com.code) == Cmd::ShowChoiceEnd) { @@ -920,29 +925,37 @@ bool Game_Interpreter::CommandShowMessage(lcf::rpg::EventCommand const& com) { / ++index; // Check for continued lines via ShowMessage_2 - while (index < static_cast(list.size()) && static_cast(list[index].code) == Cmd::ShowMessage_2) { - // Add second (another) line - pm.PushLine(ToString(list[index].string)); - ++index; + while (index < static_cast(list.size())) { + auto next_cmd = ResolveEventCommand(list[index]); + + if (static_cast(next_cmd.code) == Cmd::ShowMessage_2) { + pm.PushLine(ToString(next_cmd.string)); + ++index; + } + else { + break; + } } // Handle Choices or number if (index < static_cast(list.size())) { - // If next event command is show choices - if (static_cast(list[index].code) == Cmd::ShowChoice) { + auto next_cmd = ResolveEventCommand(list[index]); + + if (static_cast(next_cmd.code) == Cmd::ShowChoice) { std::vector s_choices = GetChoices(4); // If choices fit on screen if (static_cast(s_choices.size()) <= (4 - pm.NumLines())) { - pm.SetChoiceCancelType(list[index].parameters[0]); - SetupChoices(s_choices, com.indent, pm); + pm.SetChoiceCancelType(next_cmd.parameters[0]); + SetupChoices(s_choices, next_cmd.indent, pm); ++index; } - } else if (static_cast(list[index].code) == Cmd::InputNumber) { // If next event command is input number // If input number fits on screen + } + else if (static_cast(next_cmd.code) == Cmd::InputNumber) { if (pm.NumLines() < 4) { - int digits = list[index].parameters[0]; - int variable_id = list[index].parameters[1]; + int digits = next_cmd.parameters[0]; + int variable_id = next_cmd.parameters[1]; pm.PushNumInput(variable_id, digits); ++index; } @@ -2060,7 +2073,7 @@ std::optional Game_Interpreter::HandleDynRpgScript(const lcf::rpg::EventCo // Concat everything that is not another command or a new comment block for (size_t i = index + 1; i < list.size(); ++i) { - const auto& cmd = list[i]; + const auto& cmd = ResolveEventCommand(list[i]); if (cmd.code == static_cast(Cmd::Comment_2) && !cmd.string.empty() && cmd.string[0] != '@') { command += ToString(cmd.string); @@ -3771,9 +3784,11 @@ bool Game_Interpreter::CommandJumpToLabel(lcf::rpg::EventCommand const& com) { / int label_id = com.parameters[0]; for (int idx = 0; (size_t)idx < list.size(); idx++) { - if (static_cast(list[idx].code) != Cmd::Label) + auto scanned_cmd = ResolveEventCommand(list[idx]); + + if (static_cast(scanned_cmd.code) != Cmd::Label) continue; - if (list[idx].parameters.empty() || list[idx].parameters[0] != label_id) + if (scanned_cmd.parameters.empty() || scanned_cmd.parameters[0] != label_id) continue; index = idx; break; @@ -3853,19 +3868,19 @@ bool Game_Interpreter::CommandBreakLoop(lcf::rpg::EventCommand const& /* com */) bool has_bug = !Player::IsPatchManiac(); if (!has_bug) { - SkipToNextConditional({ Cmd::EndLoop }, list[index].indent - 1); + SkipToNextConditional({ Cmd::EndLoop }, ResolveEventCommand(list[index]).indent - 1); ++index; return true; } // This emulates an RPG_RT bug where break loop ignores scopes and // unconditionally jumps to the next EndLoop command. - auto pcode = static_cast(list[index].code); + auto pcode = static_cast(ResolveEventCommand(list[index]).code); for (++index; index < (int)list.size(); ++index) { if (pcode == Cmd::EndLoop) { break; } - pcode = static_cast(list[index].code); + pcode = static_cast(ResolveEventCommand(list[index]).code); } return true; @@ -3931,11 +3946,13 @@ bool Game_Interpreter::CommandEndLoop(lcf::rpg::EventCommand const& com) { // co // Restart the loop for (int idx = index; idx >= 0; idx--) { - if (list[idx].indent > indent) + auto scanned_cmd = ResolveEventCommand(list[idx]); + + if (scanned_cmd.indent > indent) continue; - if (list[idx].indent < indent) + if (scanned_cmd.indent < indent) return false; - if (static_cast(list[idx].code) != Cmd::Loop) + if (static_cast(scanned_cmd.code) != Cmd::Loop) continue; index = idx; break; @@ -5288,9 +5305,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& return true; } -bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& com) { - if (!Player::IsPatchManiac()) { - return true; +lcf::rpg::EventCommand Game_Interpreter::ResolveEventCommand(const lcf::rpg::EventCommand& com) { + if (static_cast(com.code) != Cmd::Maniac_CallCommand || !Player::IsPatchManiac()) { + return com; } enum class ProcessingMode { @@ -5346,22 +5363,21 @@ bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& co } default: Output::Warning("Call Command: Unsupported Processing Mode: {}", static_cast(processing_mode)); - return true; + return com; } // Finalize command parameters cmd.parameters = lcf::DBArray(values.begin(), values.end()); - // Debug output - /*Output::Warning("Processing mode: {}", static_cast(processing_mode)); - Output::Warning("Command code: {}", cmd.code); - Output::Warning("Command string: {}", cmd.string); - std::string params_str; - for (const auto& param : values) { - params_str += " " + std::to_string(param); + return cmd; +} + +bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& com) { + if (!Player::IsPatchManiac()) { + return true; } - Output::Warning("Command parameters:{}", params_str); - Output::Info("--------------------\n");*/ + + const auto& cmd = ResolveEventCommand(com); // Our implementation pushes a new frame containing the command instead of invoking it directly. // This is incompatible to Maniacs but has a better compatibility with our code. diff --git a/src/game_interpreter.h b/src/game_interpreter.h index e42c44462f..33297cc0d3 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -86,6 +86,13 @@ class Game_Interpreter : public Game_BaseInterpreterContext void InputButton(); void SetupChoices(const std::vector& choices, int indent, PendingMessage& pm); + /* + * Resolves a Maniac Patch @cmd (Call Command) into the actual EventCommand it represents. + * If the input is not a @cmd, it returns the input copy. + * This handles variables, pointers, and expressions to build the final command. + */ + lcf::rpg::EventCommand ResolveEventCommand(const lcf::rpg::EventCommand& com); + bool ExecuteCommand(); virtual bool ExecuteCommand(lcf::rpg::EventCommand const& com);