diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index ca65119cec..35bb0d7a20 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -5052,36 +5052,110 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& return true; } -bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& com) { +bool Game_Interpreter::CommandManiacCallCommand(const lcf::rpg::EventCommand& com) { if (!Player::IsPatchManiac()) { return true; } + enum class ProcessingMode { + VariableSequence = 0, + VariableSequenceAlt = 1, + Dynamic = 3, + Expression = 4 + }; + + struct CmdArrayData { + int start_index = 0; + int length = 0; + std::vector values; + int interpretation_bits = 0; + bool has_interpretation_bits = false; + }; + + constexpr int INTERPRETATION_MODE_SHIFT = 2; + + // Create command with basic parameters lcf::rpg::EventCommand cmd; cmd.code = ValueOrVariableBitfield(com.parameters[0], 0, com.parameters[1]); - cmd.string = lcf::DBString(CommandStringOrVariableBitfield(com, 0, 3, 4)); - int arr_begin = ValueOrVariableBitfield(com.parameters[0], 1, com.parameters[2]); - int arr_length = ValueOrVariableBitfield(com.parameters[0], 2, com.parameters[3]); + // Determine processing mode + auto processing_mode = static_cast((com.parameters[0] >> 4) & 0b1111); + CmdArrayData arr_data; + + // Helper to populate values based on start_index and length + auto populate_values = [&](auto get_value) { + arr_data.values.reserve(arr_data.length); + for (int i = 0; i < arr_data.length; ++i) { + arr_data.values.push_back(get_value(i)); + } + }; + + switch (processing_mode) { + case ProcessingMode::VariableSequence: + case ProcessingMode::VariableSequenceAlt: { + arr_data.start_index = ValueOrVariable(static_cast(processing_mode), com.parameters[2]); + arr_data.length = ValueOrVariableBitfield(com.parameters[0], 2, com.parameters[3]); + + if (arr_data.length > 0) { + populate_values([&](int i) { + return Main_Data::game_variables->Get(arr_data.start_index + i); + }); + } + break; + } + case ProcessingMode::Dynamic: { + arr_data.start_index = com.parameters[2]; + arr_data.length = com.parameters[3]; + populate_values([&](int i) { return com.parameters[5 + i]; }); + + int interpretation_index = 5 + arr_data.length; + if (interpretation_index < static_cast(com.parameters.size())) { + arr_data.interpretation_bits = com.parameters[interpretation_index]; + arr_data.has_interpretation_bits = true; - std::vector output_args; - if (arr_length > 0) { - output_args.reserve(arr_length); - for (int i = 0; i < arr_length; ++i) { - output_args.push_back(Main_Data::game_variables->Get(arr_begin + i)); + for (int i = 0; i < arr_data.length; ++i) { + int mode = (arr_data.interpretation_bits >> (i * INTERPRETATION_MODE_SHIFT)) & 0x3; + arr_data.values[i] = ValueOrVariable(mode, arr_data.values[i]); + } } + break; } + case ProcessingMode::Expression: { + auto values = ManiacPatch::ParseExpressions( + MakeSpan(com.parameters).subspan(5), *this); + arr_data.values.insert(arr_data.values.end(), values.begin(), values.end()); + break; + } + default: + Output::Warning("Call Command - Unsupported Processing Mode: {}", + static_cast(processing_mode)); + return true; + } + + // Finalize command parameters + cmd.parameters = lcf::DBArray(arr_data.values.begin(), arr_data.values.end()); - cmd.parameters = lcf::DBArray(output_args.begin(), output_args.end()); + // Debug output + auto debug_callcmd_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 : arr_data.values) { + params_str += " " + std::to_string(param); + } + Output::Warning("Command parameters:{}", params_str); + Output::Info("--------------------\n"); + }; - // 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. - Push({ cmd }, GetCurrentEventId(), false); //FIXME: add some new flag, so the interpreter debug view (window_interpreter) can differentiate this frame from normal ones + debug_callcmd_output(); + Push({ cmd }, GetCurrentEventId(), false); return true; } + bool Game_Interpreter::CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com) { if (!Player::HasEasyRpgExtensions()) { return true; diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index 0ed805adf9..0926849e3c 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -424,6 +424,7 @@ int Process(std::vector::iterator& it, std::vector::iterator e Output::Warning("Maniac: Expression actor args {} != 2", imm2); return 0; } + imm3 = Process(it, end, ip); return ControlVariables::Actor(Process(it, end, ip), imm3); case Fn::Party: if (imm2 != 2) { @@ -570,6 +571,35 @@ int32_t ManiacPatch::ParseExpression(Span op_codes, const Game_Ba return Process(beg, ops.end(), interpreter); } +std::vector ManiacPatch::ParseExpressions(Span op_codes, const Game_BaseInterpreterContext& interpreter) { + std::vector ops; + for (auto& o : op_codes) { + auto uo = static_cast(o); + ops.push_back(static_cast(uo & 0x000000FF)); + ops.push_back(static_cast((uo & 0x0000FF00) >> 8)); + ops.push_back(static_cast((uo & 0x00FF0000) >> 16)); + ops.push_back(static_cast((uo & 0xFF000000) >> 24)); + } + + if (ops.empty()) { + return {}; + } + + auto it = ops.begin(); + + std::vector results; + + while (true) { + results.push_back(Process(it, ops.end(), interpreter)); + + if (it == ops.end() || static_cast(*it) == Op::Null) { + break; + } + } + + return results; +} + std::array ManiacPatch::GetKeyRange() { std::array keys = { Input::Keys::A, diff --git a/src/maniac_patch.h b/src/maniac_patch.h index 789834fcf9..2b5c2c4edd 100644 --- a/src/maniac_patch.h +++ b/src/maniac_patch.h @@ -21,6 +21,7 @@ #include #include +#include #include "span.h" #include "game_strings.h" @@ -29,6 +30,8 @@ class Game_BaseInterpreterContext; namespace ManiacPatch { int32_t ParseExpression(Span op_codes, const Game_BaseInterpreterContext& interpreter); + std::vector ParseExpressions(Span op_codes, const Game_BaseInterpreterContext& interpreter); + std::array GetKeyRange();