diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index deb29dd6d8..39b6763a8c 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -1198,7 +1198,7 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com case 10: { // Switch (Maniac) value = com.parameters[5]; - if (com.parameters[6] == 1) { + if (com.parameters[6] == 0) { value = Main_Data::game_switches->GetInt(value); } else { value = Main_Data::game_switches->GetInt(Main_Data::game_variables->Get(value)); @@ -1287,6 +1287,40 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com // Expression (Maniac) value = ManiacPatch::ParseExpression(MakeSpan(com.parameters).subspan(6, com.parameters[5]), *this); break; + case 22: { + // Lerp (Maniac) + int mode = com.parameters[5]; + int arg1 = ValueOrVariableBitfield(mode, 0, com.parameters[6]); + int arg2 = ValueOrVariableBitfield(mode, 1, com.parameters[7]); + int arg3 = ValueOrVariableBitfield(mode, 2, com.parameters[8]); + int arg4 = ValueOrVariableBitfield(mode, 3, com.parameters[9]); + value = ControlVariables::Lerp(arg1, arg2, arg3, arg4); + break; + } + case 23: { + // ArraySum (Maniac) + int mode = com.parameters[5]; + int arg1 = ValueOrVariableBitfield(mode, 0, com.parameters[6]); + int arg2 = ValueOrVariableBitfield(mode, 1, com.parameters[7]); + value = ControlVariables::ArraySum(arg1, arg2); + break; + } + case 24: { + // ArrayMin (Maniac) + int mode = com.parameters[5]; + int arg1 = ValueOrVariableBitfield(mode, 0, com.parameters[6]); + int arg2 = ValueOrVariableBitfield(mode, 1, com.parameters[7]); + value = ControlVariables::ArrayMin(arg1, arg2); + break; + } + case 25: { + // ArrayMax (Maniac) + int mode = com.parameters[5]; + int arg1 = ValueOrVariableBitfield(mode, 0, com.parameters[6]); + int arg2 = ValueOrVariableBitfield(mode, 1, com.parameters[7]); + value = ControlVariables::ArrayMax(arg1, arg2); + break; + } default: Output::Warning("ControlVariables: Unsupported operand {}", operand); return true; diff --git a/src/game_interpreter_control_variables.cpp b/src/game_interpreter_control_variables.cpp index e459695109..796f57322c 100644 --- a/src/game_interpreter_control_variables.cpp +++ b/src/game_interpreter_control_variables.cpp @@ -24,6 +24,7 @@ #include "game_party.h" #include "game_player.h" #include "game_system.h" +#include "game_variables.h" #include "main_data.h" #include "output.h" #include "player.h" @@ -33,6 +34,7 @@ #include #include #include +#include int ControlVariables::Random(int value, int value2) { int rmax = std::max(value, value2); @@ -138,6 +140,12 @@ int ControlVariables::Actor(int op, int actor_id) { return actor->GetAtbGauge(); } break; + case 17: + // Required Experience + if (Player::IsPatchManiac()) { + return actor->GetNextExp(); + } + break; } Output::Warning("ControlVariables::Actor: Unknown op {}", op); @@ -425,23 +433,23 @@ int ControlVariables::Binary(int op, int arg1, int arg2) { if (arg2_64 != 0) { result = arg1_64 % arg2_64; } else { - result = arg1_64; + result = 0; } break; case 6: - result = arg1_64 | arg2_64; + result = arg1 | arg2; break; case 7: - result = arg1_64 & arg2_64; + result = arg1 & arg2; break; case 8: - result = arg1_64 ^ arg2_64; + result = arg1 ^ arg2; break; case 9: - result = arg1_64 << arg2_64; + result = static_cast(static_cast(arg1) << arg2); break; case 10: - result = arg1_64 >> arg2_64; + result = static_cast(static_cast(arg1) >> arg2); break; default: Output::Warning("ControlVariables::Binary: Unknown op {}", op); @@ -480,5 +488,40 @@ int ControlVariables::Divmul(int arg1, int arg2, int arg3) { } int ControlVariables::Between(int arg1, int arg2, int arg3) { - return (arg1 >= arg2 && arg2 <= arg3) ? 0 : 1; + int min_v = std::min(arg2, arg3); + int max_v = std::max(arg2, arg3); + return (arg1 >= min_v && arg1 <= max_v) ? 1 : 0; +} + +int ControlVariables::Lerp(int a, int b, int num, int den) { + if (den == 0) return a; + int64_t diff = static_cast(b) - a; + return a + static_cast(diff * num / den); +} + +int ControlVariables::ArraySum(int var_id, int length) { + if (length <= 0) return 0; + int64_t sum = 0; + for (int i = 0; i < length; ++i) { + sum += Main_Data::game_variables->Get(var_id + i); + } + return static_cast(Utils::Clamp(sum, std::numeric_limits::min(), std::numeric_limits::max())); +} + +int ControlVariables::ArrayMin(int var_id, int length) { + if (length <= 0) return 0; + int min_val = Main_Data::game_variables->Get(var_id); + for (int i = 1; i < length; ++i) { + min_val = std::min(min_val, Main_Data::game_variables->Get(var_id + i)); + } + return min_val; +} + +int ControlVariables::ArrayMax(int var_id, int length) { + if (length <= 0) return 0; + int max_val = Main_Data::game_variables->Get(var_id); + for (int i = 1; i < length; ++i) { + max_val = std::max(max_val, Main_Data::game_variables->Get(var_id + i)); + } + return max_val; } diff --git a/src/game_interpreter_control_variables.h b/src/game_interpreter_control_variables.h index c1d7da11c4..cc7e58f4d1 100644 --- a/src/game_interpreter_control_variables.h +++ b/src/game_interpreter_control_variables.h @@ -45,6 +45,10 @@ namespace ControlVariables { int Muldiv(int arg1, int arg2, int arg3); int Divmul(int arg1, int arg2, int arg3); int Between(int arg1, int arg2, int arg3); + int Lerp(int a, int b, int num, int den); + int ArraySum(int var_id, int length); + int ArrayMin(int var_id, int length); + int ArrayMax(int var_id, int length); } #endif diff --git a/src/game_variables.cpp b/src/game_variables.cpp index 4f77f700bb..e74728f61e 100644 --- a/src/game_variables.cpp +++ b/src/game_variables.cpp @@ -125,7 +125,7 @@ constexpr Var_t VarBitShiftLeft(Var_t n, Var_t d) { }; constexpr Var_t VarBitShiftRight(Var_t n, Var_t d) { - return n >> d; + return static_cast(static_cast(n) >> d); }; } diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index 988ea12dd9..edf42c0662 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -35,18 +35,6 @@ #include #include -/* -The following operations are unsupported: - -All array functions (Array, Range and Subscript): -They could be implemented but are not very useful - -All Inplace functions: -These functions are disabled when EasyRpg Extensions are active. -Inplace assigns to variables while the ControlVariables event command is executed. -This violates how the command is supposed to work because more variables than the target variables can be set. -*/ - namespace { enum class Op { Null = 0, @@ -118,7 +106,11 @@ namespace { Clamp, Muldiv, Divmul, - Between + Between, + Lerp, + ArraySum, + ArrayMin, + ArrayMax }; bool global_save_opened = false; @@ -126,446 +118,656 @@ namespace { struct ProcessAssignmentRet { Op op = Op::Null; - int id = 0; - - int fetch() const { - switch (op) { - case Op::Var: - return Main_Data::game_variables->Get(id); - case Op::Switch: - return Main_Data::game_switches->Get(id); - case Op::VarIndirect: { - return Main_Data::game_variables->GetIndirect(id); - } - case Op::SwitchIndirect: { - int var = Main_Data::game_variables->GetIndirect(id); - return Main_Data::game_switches->Get(var); - } - default: - Output::Warning("Maniac: Expression assignment {} is not a lvalue", static_cast(op)); - return 0; + std::vector ids; + + std::vector fetch() const { + std::vector res; + res.reserve(ids.size()); + for (int id : ids) { + switch (op) { + case Op::Var: + res.push_back(Main_Data::game_variables->Get(id)); + break; + case Op::Switch: + res.push_back(Main_Data::game_switches->GetInt(id)); + break; + case Op::VarIndirect: + res.push_back(Main_Data::game_variables->GetIndirect(id)); + break; + case Op::SwitchIndirect: + res.push_back(Main_Data::game_switches->GetInt(Main_Data::game_variables->Get(id))); + break; + default: + Output::Warning("Maniac: Expression assignment {} is not a lvalue", static_cast(op)); + res.push_back(0); + break; + } } + return res; } - int assign(int value) const { - if (Player::HasEasyRpgExtensions()) { - Output::Warning("Maniac: Inplace assignments are not allowed in expressions when running in EasyRpg Mode"); - return fetch(); + std::vector assign(const std::vector& rhs) const { + std::vector res; + res.reserve(ids.size()); + for (size_t i = 0; i < ids.size(); ++i) { + int id = ids[i]; + int val = (i < rhs.size()) ? rhs[i] : 0; + switch (op) { + case Op::Var: + Game_Map::SetNeedRefreshForVarChange(id); + Main_Data::game_variables->Set(id, val); + res.push_back(val); + break; + case Op::Switch: + Game_Map::SetNeedRefreshForSwitchChange(id); + Main_Data::game_switches->Set(id, val > 0); + res.push_back(val); + break; + case Op::VarIndirect: { + int var = Main_Data::game_variables->GetIndirect(id); + Game_Map::SetNeedRefreshForVarChange(var); + Main_Data::game_variables->Set(var, val); + res.push_back(val); + break; + } + case Op::SwitchIndirect: { + int var = Main_Data::game_variables->GetIndirect(id); + Game_Map::SetNeedRefreshForSwitchChange(var); + Main_Data::game_switches->Set(var, val > 0); + res.push_back(val); + break; + } + default: + res.push_back(0); + break; + } } + return res; + } - switch (op) { - case Op::Var: - Game_Map::SetNeedRefreshForVarChange(id); - return Main_Data::game_variables->Set(id, value); - case Op::Switch: - Game_Map::SetNeedRefreshForSwitchChange(id); - return Main_Data::game_switches->Set(id, value > 0); - case Op::VarIndirect: { - int var = Main_Data::game_variables->GetIndirect(id); - Game_Map::SetNeedRefreshForVarChange(var); - return Main_Data::game_variables->Set(var, value); - } - case Op::SwitchIndirect: { - int var = Main_Data::game_variables->GetIndirect(id); - Game_Map::SetNeedRefreshForSwitchChange(var); - return Main_Data::game_switches->Set(var, value > 0); - } - default: - Output::Warning("Maniac: Expression assignment {} is not a lvalue", static_cast(op)); - return 0; + template + std::vector assign_op(const std::vector& rhs, F&& fn) const { + auto current = fetch(); + std::vector new_vals; + new_vals.reserve(current.size()); + for (size_t i = 0; i < current.size(); ++i) { + int val = (i < rhs.size()) ? rhs[i] : (rhs.empty() ? 0 : rhs.back()); + new_vals.push_back(fn(current[i], val)); } + return assign(new_vals); } }; -ProcessAssignmentRet ProcessAssignment(std::vector::iterator& it, std::vector::iterator end, const Game_BaseInterpreterContext& ip); - -int Process(std::vector::iterator& it, std::vector::iterator end, const Game_BaseInterpreterContext& ip) { - int value = 0; - int imm = 0; - int imm2 = 0; - int imm3 = 0; +std::vector Process(std::vector::iterator& it, std::vector::iterator end, const Game_BaseInterpreterContext& ip, bool execute); +ProcessAssignmentRet ProcessAssignment(std::vector::iterator& it, std::vector::iterator end, const Game_BaseInterpreterContext& ip, bool execute) { if (it == end) { - return 0; + return { Op::Null, {0} }; } auto op = static_cast(*it); ++it; - // When entering the switch it is on the first argument switch (op) { - case Op::Null: - it++; - return 0; - case Op::U8: - case Op::UX8: - value = *it++; - return value; - case Op::U16: - case Op::UX16: - imm = *it++; - if (it == end) { - return 0; - } - imm2 = *it++; - value = (imm2 << 8) + imm; - return value; - case Op::S32: - case Op::SX32: - imm = *it++; - if (it == end) { - return 0; - } - imm2 = *it++; - if (it == end) { - return 0; - } - imm3 = *it++; - if (it == end) { - return 0; - } - value = *it++; - value = (value << 24) + (imm3 << 16) + (imm2 << 8) + imm; - return value; - case Op::Var: - imm = Process(it, end, ip); - return Main_Data::game_variables->Get(imm); - case Op::Switch: - imm = Process(it, end, ip); - return Main_Data::game_switches->GetInt(imm); - case Op::VarIndirect: - imm = Process(it, end, ip); - return Main_Data::game_variables->GetIndirect(imm); - case Op::SwitchIndirect: - imm = Process(it, end, ip); - return Main_Data::game_switches->GetInt(Main_Data::game_variables->Get(imm)); - case Op::Negate: - imm = Process(it, end, ip); - return -imm; - case Op::Not: - imm = Process(it, end, ip); - return !imm ? 0 : 1; - case Op::Flip: - imm = Process(it, end, ip); - return ~imm; - case Op::AssignInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(imm2); - } - case Op::AddInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(static_cast(Utils::Clamp(static_cast(ret.fetch()) + imm2, std::numeric_limits::min(), std::numeric_limits::max()))); - } - case Op::SubInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(static_cast(Utils::Clamp(static_cast(ret.fetch()) - imm2, std::numeric_limits::min(), std::numeric_limits::max()))); - } - case Op::MulInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(static_cast(Utils::Clamp(static_cast(ret.fetch()) * imm2, std::numeric_limits::min(), std::numeric_limits::max()))); - } - case Op::DivInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - if (imm2 == 0) { - return ret.fetch(); - } - return ret.assign(ret.fetch() / imm2); - } - case Op::ModInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - if (imm2 == 0) { - return ret.fetch(); - } - return ret.assign(ret.fetch() % imm2); - } - case Op::BitOrInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(ret.fetch() | imm2); - } - case Op::BitAndInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(ret.fetch() & imm2); - } - case Op::BitXorInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(ret.fetch() ^ imm2); - } - case Op::BitShiftLeftInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(ret.fetch() << imm2); - } - case Op::BitShiftRightInplace: { - auto ret = ProcessAssignment(it, end, ip); - imm2 = Process(it, end, ip); - return ret.assign(ret.fetch() >> imm2); - } - case Op::Add: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return static_cast(Utils::Clamp(static_cast(imm) + imm2, std::numeric_limits::min(), std::numeric_limits::max())); - case Op::Sub: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return static_cast(Utils::Clamp(static_cast(imm) - imm2, std::numeric_limits::min(), std::numeric_limits::max())); - case Op::Mul: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return static_cast(Utils::Clamp(static_cast(imm) * imm2, std::numeric_limits::min(), std::numeric_limits::max())); - case Op::Div: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - if (imm2 == 0) { - return imm; - } - return imm / imm2; - case Op::Mod: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - if (imm2 == 0) { - return imm; - } - return imm % imm2; - case Op::BitOr: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm | imm2; - case Op::BitAnd: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm & imm2; - case Op::BitXor: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm ^ imm2; - case Op::BitShiftLeft: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm << imm2; - case Op::BitShiftRight: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm >> imm2; - case Op::Equal: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm == imm2 ? 1 : 0; - case Op::GreaterEqual: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm >= imm2 ? 1 : 0; - case Op::LessEqual: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm <= imm2 ? 1 : 0; - case Op::Greater: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm > imm2 ? 1 : 0; - case Op::Less: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm < imm2 ? 1 : 0; - case Op::NotEqual: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return imm != imm2 ? 1 : 0; - case Op::Or: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return !!imm || !!imm2 ? 1 : 0; - case Op::And: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - return !!imm && !!imm2 ? 1 : 0; - case Op::Ternary: - imm = Process(it, end, ip); - imm2 = Process(it, end, ip); - imm3 = Process(it, end, ip); - return imm != 0 ? imm2 : imm3; - case Op::Function: - imm = *it++; // function - imm2 = *it++; // arguments - - if ((imm2 & 0x80) != 0) { - // Argument count is 4 bytes, that mode is not supported - Output::Warning("Maniac: Expression func long args unsupported"); - return 0; - } - - switch (static_cast(imm)) { - case Fn::Rand: - if (imm2 != 2) { - Output::Warning("Maniac: Expression rnd args {} != 2", imm2); - return 0; - } - imm3 = Process(it, end, ip); - return ControlVariables::Random(Process(it, end, ip), imm3); - case Fn::Item: - if (imm2 != 2) { - Output::Warning("Maniac: Expression item args {} != 2", imm2); - return 0; - } - imm3 = Process(it, end, ip); - return ControlVariables::Item(Process(it, end, ip), imm3); - case Fn::Event: - if (imm2 != 2) { - Output::Warning("Maniac: Expression event args {} != 2", imm2); - return 0; - } - imm3 = Process(it, end, ip); - return ControlVariables::Event(Process(it, end, ip), imm3, ip); - case Fn::Actor: - if (imm2 != 2) { - 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) { - Output::Warning("Maniac: Expression member args {} != 2", imm2); - return 0; - } - imm3 = Process(it, end, ip); - return ControlVariables::Party(Process(it, end, ip), imm3); - case Fn::Enemy: - if (imm2 != 2) { - Output::Warning("Maniac: Expression enemy args {} != 2", imm2); - return 0; - } - imm3 = Process(it, end, ip); - return ControlVariables::Enemy(Process(it, end, ip), imm3); - break; - case Fn::Misc: - if (imm2 != 1) { - Output::Warning("Maniac: Expression misc args {} != 1", imm2); - return 0; - } - return ControlVariables::Other(Process(it, end, ip)); - case Fn::Pow: - if (imm2 != 2) { - Output::Warning("Maniac: Expression pow args {} != 2", imm2); - return 0; - } - return ControlVariables::Pow(Process(it, end, ip), Process(it, end, ip)); - case Fn::Sqrt: - if (imm2 != 2) { - Output::Warning("Maniac: Expression sqrt args {} != 2", imm2); - return 0; - } - return ControlVariables::Sqrt(Process(it, end, ip), Process(it, end, ip)); - case Fn::Sin: - if (imm2 != 3) { - Output::Warning("Maniac: Expression sin args {} != 3", imm2); - return 0; - } - return ControlVariables::Sin(Process(it, end, ip), Process(it, end, ip), Process(it, end, ip)); - case Fn::Cos: - if (imm2 != 3) { - Output::Warning("Maniac: Expression cos args {} != 3", imm2); - return 0; - } - return ControlVariables::Cos(Process(it, end, ip), Process(it, end, ip), Process(it, end, ip)); - case Fn::Atan2: - if (imm2 != 3) { - Output::Warning("Maniac: Expression atan2 args {} != 3", imm2); - return 0; - } - return ControlVariables::Atan2(Process(it, end, ip), Process(it, end, ip), Process(it, end, ip)); - case Fn::Min: - if (imm2 != 2) { - Output::Warning("Maniac: Expression min args {} != 2", imm2); - return 0; - } - return ControlVariables::Min(Process(it, end, ip), Process(it, end, ip)); - case Fn::Max: - if (imm2 != 2) { - Output::Warning("Maniac: Expression max args {} != 2", imm2); - return 0; - } - return ControlVariables::Max(Process(it, end, ip), Process(it, end, ip)); - case Fn::Abs: - if (imm2 != 1) { - Output::Warning("Maniac: Expression abs args {} != 1", imm2); - return 0; - } - return ControlVariables::Abs(Process(it, end, ip)); - case Fn::Clamp: - if (imm2 != 3) { - Output::Warning("Maniac: Expression clamp args {} != 3", imm2); - return 0; - } - return ControlVariables::Clamp(Process(it, end, ip), Process(it, end, ip), Process(it, end, ip)); - case Fn::Muldiv: - if (imm2 != 3) { - Output::Warning("Maniac: Expression muldiv args {} != 3", imm2); - return 0; - } - return ControlVariables::Muldiv(Process(it, end, ip), Process(it, end, ip), Process(it, end, ip)); - case Fn::Divmul: - if (imm2 != 3) { - Output::Warning("Maniac: Expression divmul args {} != 3", imm2); - return 0; - } - return ControlVariables::Divmul(Process(it, end, ip), Process(it, end, ip), Process(it, end, ip)); - case Fn::Between: - if (imm2 != 3) { - Output::Warning("Maniac: Expression between args {} != 3", imm2); - return 0; - } - return ControlVariables::Between(Process(it, end, ip), Process(it, end, ip), Process(it, end, ip)); - default: - Output::Warning("Maniac: Expression Unknown Func {}", imm); - for (int i = 0; i < imm2; ++i) { - Process(it, end, ip); - } - return 0; - } - default: - Output::Warning("Maniac: Expression contains unsupported operation {}", static_cast(op)); - return 0; + case Op::Var: + case Op::Switch: + case Op::VarIndirect: + case Op::SwitchIndirect: + return { op, Process(it, end, ip, execute) }; + default: + --it; // back on the op as op is fetched again by Process + return { Op::Null, Process(it, end, ip, execute) }; } } -ProcessAssignmentRet ProcessAssignment(std::vector::iterator& it, std::vector::iterator end, const Game_BaseInterpreterContext& ip) { - // Like process but it remembers the type (Variable or Switch) without evaluating it to allow assignments - int imm = 0; - +std::vector Process(std::vector::iterator& it, std::vector::iterator end, const Game_BaseInterpreterContext& ip, bool execute) { if (it == end) { - return {Op::Null, 0}; + return { 0 }; } auto op = static_cast(*it); ++it; + auto eval1 = [&]() { + auto r = Process(it, end, ip, execute); + return r.empty() ? 0 : r[0]; + }; + + auto eval2 = [&]() { + int a = eval1(); + int b = eval1(); + return std::make_tuple(a, b); + }; + + auto eval3 = [&]() { + int a = eval1(); + int b = eval1(); + int c = eval1(); + return std::make_tuple(a, b, c); + }; + // When entering the switch it is on the first argument switch (op) { - case Op::Var: - case Op::Switch: - case Op::VarIndirect: - case Op::SwitchIndirect: - imm = Process(it, end, ip); - return {op, imm}; + case Op::Null: + return { 0 }; + case Op::U8: + case Op::UX8: + return { *it++ }; + case Op::U16: + case Op::UX16: { + uint32_t u0 = *it++; + if (it == end) return { 0 }; + uint32_t u1 = *it++; + return { static_cast((u1 << 8) | u0) }; + } + case Op::S32: + case Op::SX32: { + uint32_t u0 = *it++; + if (it == end) return { 0 }; + uint32_t u1 = *it++; + if (it == end) return { 0 }; + uint32_t u2 = *it++; + if (it == end) return { 0 }; + uint32_t u3 = *it++; + uint32_t val = (u3 << 24) | (u2 << 16) | (u1 << 8) | u0; + return { static_cast(val) }; + } + case Op::Var: { + auto ids = Process(it, end, ip, execute); + std::vector res; + res.reserve(ids.size()); + for (int id : ids) { + res.push_back(Main_Data::game_variables->Get(id)); + } + return res; + } + case Op::Switch: { + auto ids = Process(it, end, ip, execute); + std::vector res; + res.reserve(ids.size()); + for (int id : ids) { + res.push_back(Main_Data::game_switches->GetInt(id)); + } + return res; + } + case Op::VarIndirect: { + auto ids = Process(it, end, ip, execute); + std::vector res; + res.reserve(ids.size()); + for (int id : ids) { + res.push_back(Main_Data::game_variables->GetIndirect(id)); + } + return res; + } + case Op::SwitchIndirect: { + auto ids = Process(it, end, ip, execute); + std::vector res; + res.reserve(ids.size()); + for (int id : ids) { + res.push_back(Main_Data::game_switches->GetInt(Main_Data::game_variables->Get(id))); + } + return res; + } + case Op::Array: { + int32_t n = *it++; + if ((n & 0x80) != 0) { + int32_t b0 = *it++; + int32_t b1 = *it++; + int32_t b2 = *it++; + int32_t b3 = *it++; + n = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + } + std::vector res; + for (int32_t i = 0; i < n; ++i) { + auto elem = Process(it, end, ip, execute); + res.insert(res.end(), elem.begin(), elem.end()); + } + return res; + } + case Op::Range: { + auto [first, second] = eval2(); + std::vector res; + if (first <= second) { + for (int i = first; i <= second; ++i) { + res.push_back(i); + } + } else { + for (int i = first; i >= second; --i) { + res.push_back(i); + } + } + return res; + } + case Op::Subscript: { + auto arr = Process(it, end, ip, execute); + int idx = eval1(); + if (idx >= 0 && idx < static_cast(arr.size())) { + return { arr[idx] }; + } else { + return { 0 }; + } + } + case Op::Negate: + return { -eval1() }; + case Op::Not: + return { !eval1() ? 1 : 0 }; + case Op::Flip: + return { ~eval1() }; + case Op::AssignInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign(rhs); + } + return rhs; + } + case Op::AddInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { + return static_cast(Utils::Clamp(static_cast(a) + b, std::numeric_limits::min(), std::numeric_limits::max())); + }); + } + return { 0 }; + } + case Op::SubInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { + return static_cast(Utils::Clamp(static_cast(a) - b, std::numeric_limits::min(), std::numeric_limits::max())); + }); + } + return { 0 }; + } + case Op::MulInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { + return static_cast(Utils::Clamp(static_cast(a) * b, std::numeric_limits::min(), std::numeric_limits::max())); + }); + } + return { 0 }; + } + case Op::DivInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { return b == 0 ? a : a / b; }); + } + return { 0 }; + } + case Op::ModInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { return b == 0 ? 0 : a % b; }); + } + return { 0 }; + } + case Op::BitOrInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { return a | b; }); + } + return { 0 }; + } + case Op::BitAndInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { return a & b; }); + } + return { 0 }; + } + case Op::BitXorInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { return a ^ b; }); + } + return { 0 }; + } + case Op::BitShiftLeftInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { return a << b; }); + } + return { 0 }; + } + case Op::BitShiftRightInplace: { + auto ret = ProcessAssignment(it, end, ip, execute); + auto rhs = Process(it, end, ip, execute); + if (execute) { + return ret.assign_op(rhs, [](int a, int b) { return static_cast(static_cast(a) >> b); }); + } + return { 0 }; + } + case Op::Add: { + auto [first, second] = eval2(); + return { + static_cast(Utils::Clamp(static_cast(first) + second, std::numeric_limits::min(), std::numeric_limits::max())) + }; + } + case Op::Sub: { + auto [first, second] = eval2(); + return { + static_cast(Utils::Clamp(static_cast(first) - second, std::numeric_limits::min(), std::numeric_limits::max())) + }; + } + case Op::Mul: { + auto [first, second] = eval2(); + return { + static_cast(Utils::Clamp(static_cast(first) * second, std::numeric_limits::min(), std::numeric_limits::max())) + }; + } + case Op::Div: { + auto [first, second] = eval2(); + return { + second == 0 ? first : first / second + }; + } + case Op::Mod: { + auto [first, second] = eval2(); + return { + second == 0 ? 0 : first % second + }; + } + case Op::BitOr: { + auto [first, second] = eval2(); + return { + first | second + }; + } + case Op::BitAnd: { + auto [first, second] = eval2(); + return { + first & second + }; + } + case Op::BitXor: { + auto [first, second] = eval2(); + return { + first ^ second + }; + } + case Op::BitShiftLeft: { + auto [first, second] = eval2(); + return { + first << second + }; + } + case Op::BitShiftRight: { + auto [first, second] = eval2(); + return { + static_cast(static_cast(first) >> second) + }; + } + case Op::Equal: { + auto [first, second] = eval2(); + return { + first == second ? 1 : 0 + }; + } + case Op::GreaterEqual: { + auto [first, second] = eval2(); + return { + first >= second ? 1 : 0 + }; + } + case Op::LessEqual: { + auto [first, second] = eval2(); + return { + first <= second ? 1 : 0 + }; + } + case Op::Greater: { + auto [first, second] = eval2(); + return { + first > second ? 1 : 0 + }; + } + case Op::Less: { + auto [first, second] = eval2(); + return { + first < second ? 1 : 0 + }; + } + case Op::NotEqual: { + auto [first, second] = eval2(); + return { + first != second ? 1 : 0 + }; + } + case Op::Or: { + auto res_first = Process(it, end, ip, execute); + int first = res_first.empty() ? 0 : res_first[0]; + bool next_execute = execute && (first == 0); + auto res_second = Process(it, end, ip, next_execute); + int second = res_second.empty() ? 0 : res_second[0]; + return { (first != 0 || second != 0) ? 1 : 0 }; + } + case Op::And: { + auto res_first = Process(it, end, ip, execute); + int first = res_first.empty() ? 0 : res_first[0]; + bool next_execute = execute && (first != 0); + auto res_second = Process(it, end, ip, next_execute); + int second = res_second.empty() ? 0 : res_second[0]; + return { (first != 0 && second != 0) ? 1 : 0 }; + } + case Op::Ternary: { + auto res_cond = Process(it, end, ip, execute); + int cond = res_cond.empty() ? 0 : res_cond[0]; + auto res_true = Process(it, end, ip, execute && (cond != 0)); + auto res_false = Process(it, end, ip, execute && (cond == 0)); + return cond != 0 ? res_true : res_false; + } + case Op::Function: { + int fn = *it++; + int argc = *it++; + if ((argc & 0x80) != 0) { + Output::Warning("Maniac: Expression func long args unsupported"); + return { 0 }; + } + switch (static_cast(fn)) { + case Fn::Rand: { + if (argc != 2) return { 0 }; + auto [first, second] = eval2(); + if (execute) { + return { + ControlVariables::Random(first, second) + }; + } + return { 0 }; + } + case Fn::Item: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Item(first, second) + }; + } + case Fn::Event: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Event(first, second, ip) + }; + } + case Fn::Actor: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Actor(first, second) + }; + } + case Fn::Party: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Party(first, second) + }; + } + case Fn::Enemy: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Enemy(first, second) + }; + } + case Fn::Misc: { + if (argc != 1) { return { 0 }; } + return { + ControlVariables::Other(eval1()) + }; + } + case Fn::Pow: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Pow(first, second) + }; + } + case Fn::Sqrt: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Sqrt(first, second) + }; + } + case Fn::Sin: { + if (argc != 3) { return { 0 }; } + auto [first, second, third] = eval3(); + return { + ControlVariables::Sin(first, second, third) + }; + } + case Fn::Cos: { + if (argc != 3) { return { 0 }; } + auto [first, second, third] = eval3(); + return { + ControlVariables::Cos(first, second, third) + }; + } + case Fn::Atan2: { + if (argc != 3) { return { 0 }; } + auto [first, second, third] = eval3(); + return { + ControlVariables::Atan2(first, second, third) + }; + } + case Fn::Min: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Min(first, second) + }; + } + case Fn::Max: { + if (argc != 2) { return { 0 }; } + auto [first, second] = eval2(); + return { + ControlVariables::Max(first, second) + }; + } + case Fn::Abs: { + if (argc != 1) { return { 0 }; } + return { + ControlVariables::Abs(eval1()) + }; + } + case Fn::Clamp: { + if (argc != 3) { return { 0 }; } + auto [first, second, third] = eval3(); + return { + ControlVariables::Clamp(first, second, third) + }; + } + case Fn::Muldiv: { + if (argc != 3) { return { 0 }; } + auto [first, second, third] = eval3(); + return { + ControlVariables::Muldiv(first, second, third) + }; + } + case Fn::Divmul: { + if (argc != 3) { return { 0 }; } + auto [first, second, third] = eval3(); + return { + ControlVariables::Divmul(first, second, third) + }; + } + case Fn::Between: { + if (argc != 3) { return { 0 }; } + auto [first, second, third] = eval3(); + return { + ControlVariables::Between(first, second, third) + }; + } + case Fn::Lerp: { + if (argc != 4) return { 0 }; + int a = eval1(); + int b = eval1(); + int num = eval1(); + int den = eval1(); + if (execute) { + return { + ControlVariables::Lerp(a, b, num, den) + }; + } + return { 0 }; + } + case Fn::ArraySum: { + if (argc != 2) return { 0 }; + auto [first, second] = eval2(); + if (execute) { + return { + ControlVariables::ArraySum(first, second) + }; + } + return { 0 }; + } + case Fn::ArrayMin: { + if (argc != 2) return { 0 }; + auto [first, second] = eval2(); + if (execute) { + return { + ControlVariables::ArrayMin(first, second) + }; + } + return { 0 }; + } + case Fn::ArrayMax: { + if (argc != 2) return { 0 }; + auto [first, second] = eval2(); + if (execute) { + return { + ControlVariables::ArrayMax(first, second) + }; + } + return { 0 }; + } default: - --it; // back on the op as op is fetched again by Process - imm = Process(it, end, ip); - return {op, imm}; + Output::Warning("Maniac: Expression Unknown Func {}", fn); + for (int i = 0; i < argc; ++i) { + eval1(); + } + return { 0 }; + } + } + default: + Output::Warning("Maniac: Expression contains unsupported operation {}", static_cast(op)); + return { 0 }; } } int32_t ManiacPatch::ParseExpression(Span op_codes, const Game_BaseInterpreterContext& interpreter) { std::vector ops; - for (auto &o: op_codes) { + 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)); @@ -573,7 +775,8 @@ int32_t ManiacPatch::ParseExpression(Span op_codes, const Game_Ba ops.push_back(static_cast((uo & 0xFF000000) >> 24)); } auto beg = ops.begin(); - return Process(beg, ops.end(), interpreter); + auto res = Process(beg, ops.end(), interpreter, true); + return res.empty() ? 0 : res[0]; } std::vector ManiacPatch::ParseExpressions(Span op_codes, const Game_BaseInterpreterContext& interpreter) { @@ -595,7 +798,10 @@ std::vector ManiacPatch::ParseExpressions(Span op_codes, std::vector results; while (true) { - results.push_back(Process(it, ops.end(), interpreter)); + auto res = Process(it, ops.end(), interpreter, true); + if (!res.empty()) { + results.push_back(res[0]); + } if (it == ops.end() || static_cast(*it) == Op::Null) { break;