diff --git a/src/cache.cpp b/src/cache.cpp index 4d54b1eb70..b9d36d5e4a 100644 --- a/src/cache.cpp +++ b/src/cache.cpp @@ -463,8 +463,16 @@ BitmapRef Cache::Tile(StringView filename, int tile_id) { } BitmapRef Cache::SpriteEffect(const BitmapRef& src_bitmap, const Rect& rect, bool flip_x, bool flip_y, const Tone& tone, const Color& blend) { + std::string id = ToString(src_bitmap->GetId()); + + if (id.empty()) { + // assert caused too many regressions, use the pointer as the unique key and log instead + Output::Debug("Bitmap has no ID. Please report a bug!"); + id = fmt::format("{}", (void*)(src_bitmap.get())); + } + const effect_key_type key { - src_bitmap->GetId(), + id, src_bitmap->GetTransparent(), rect, flip_x, @@ -473,8 +481,6 @@ BitmapRef Cache::SpriteEffect(const BitmapRef& src_bitmap, const Rect& rect, boo blend }; - assert(!src_bitmap->GetId().empty()); - const auto it = cache_effects.find(key); if (it == cache_effects.end() || it->second.expired()) { diff --git a/src/game_commonevent.cpp b/src/game_commonevent.cpp index 8a7d8d1682..1f74ffe7d1 100644 --- a/src/game_commonevent.cpp +++ b/src/game_commonevent.cpp @@ -63,6 +63,10 @@ AsyncOp Game_CommonEvent::Update(bool resume_async) { return {}; } +int Game_CommonEvent::GetId() const { + return common_event_id; +} + int Game_CommonEvent::GetIndex() const { return common_event_id; } diff --git a/src/game_commonevent.h b/src/game_commonevent.h index c009707543..f8069a3a0d 100644 --- a/src/game_commonevent.h +++ b/src/game_commonevent.h @@ -54,6 +54,13 @@ class Game_CommonEvent { */ AsyncOp Update(bool resume_async); + /** + * Gets common event ID. + * + * @return ID of the common event + */ + int GetId() const; + /** * Gets common event index. * diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 98b9edef57..c40bc20915 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -3407,21 +3407,33 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co result = (Main_Data::game_party->GetGold() <= com.parameters[1]); } break; - case 4: + case 4: { // Item + int item_id = com.parameters[1]; + + if (Player::IsPatchManiac()) { + item_id = ValueOrVariable(com.parameters[3], item_id); + } + if (com.parameters[2] == 0) { // Having - result = Main_Data::game_party->GetItemCount(com.parameters[1]) - + Main_Data::game_party->GetEquippedItemCount(com.parameters[1]) > 0; + result = Main_Data::game_party->GetItemCount(item_id) + + Main_Data::game_party->GetEquippedItemCount(item_id) > 0; } else { // Not having - result = Main_Data::game_party->GetItemCount(com.parameters[1]) - + Main_Data::game_party->GetEquippedItemCount(com.parameters[1]) == 0; + result = Main_Data::game_party->GetItemCount(item_id) + + Main_Data::game_party->GetEquippedItemCount(item_id) == 0; } break; + } case 5: // Hero actor_id = com.parameters[1]; + + if (Player::IsPatchManiac()) { + actor_id = ValueOrVariable(com.parameters[4], actor_id); + } + actor = Main_Data::game_actors->GetActor(actor_id); if (!actor) { @@ -3471,13 +3483,20 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co ; } break; - case 6: + case 6: { // Orientation of char - character = GetCharacter(com.parameters[1]); + int chara_id = com.parameters[1]; + + if (Player::IsPatchManiac()) { + chara_id = ValueOrVariable(com.parameters[3], chara_id); + } + + character = GetCharacter(chara_id); if (character != NULL) { result = character->GetFacing() == com.parameters[2]; } break; + } case 7: { // Vehicle in use Game_Vehicle::Type vehicle_id = (Game_Vehicle::Type) (com.parameters[1] + 1); @@ -3574,7 +3593,7 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co } break; case 15: - // Maniac: string comparison + // Maniac: String comparison if (Player::IsPatchManiac()) { int modes[] = { (com.parameters[1] ) & 15, //str_l mode: 0 = direct, 1 = indirect @@ -4271,9 +4290,8 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con // x03 -> indirect reference // for the displayed string, the id argument is in com.parameters[22] // here we are capturing all the delimiters, but currently only need to support reading the first one - int i = 0; std::vector delims; - auto components = Utils::Tokenize(com.string, [p = &delims, &i](char32_t ch) { + auto components = Utils::Tokenize(com.string, [p = &delims](char32_t ch) { if (ch == '\x01' || ch == '\x02' || ch == '\x03') { p->push_back(static_cast(ch)); return true; diff --git a/src/game_map.cpp b/src/game_map.cpp index 5056405de4..46684779bf 100644 --- a/src/game_map.cpp +++ b/src/game_map.cpp @@ -104,9 +104,6 @@ void Game_Map::OnContinueFromBattle() { static Game_Map::Parallax::Params GetParallaxParams(); void Game_Map::Init() { - screen_width = (Player::screen_width / 16) * SCREEN_TILE_SIZE; - screen_height = (Player::screen_height / 16) * SCREEN_TILE_SIZE; - Dispose(); map_info = {}; @@ -156,8 +153,8 @@ int Game_Map::GetMapSaveCount() { void Game_Map::Setup(std::unique_ptr map_in) { Dispose(); - screen_width = (Player::screen_width / 16) * SCREEN_TILE_SIZE; - screen_height = (Player::screen_height / 16) * SCREEN_TILE_SIZE; + screen_width = (Player::screen_width / 16.0) * SCREEN_TILE_SIZE; + screen_height = (Player::screen_height / 16.0) * SCREEN_TILE_SIZE; map = std::move(map_in); @@ -637,7 +634,7 @@ void Game_Map::Scroll(int dx, int dy) { // that acc changed by. static void ClampingAdd(int low, int high, int& acc, int& inc) { int original_acc = acc; - acc = std::max(low, std::min(high, acc + inc)); + acc = std::clamp(acc + inc, low, high); inc = acc - original_acc; } @@ -1651,7 +1648,7 @@ void Game_Map::SetPositionX(int x, bool reset_panorama) { if (LoopHorizontal()) { x = Utils::PositiveModulo(x, map_width); } else { - x = std::max(0, std::min(map_width - screen_width, x)); + x = std::clamp(x, 0, map_width - screen_width); } map_info.position_x = x; if (reset_panorama) { @@ -1673,7 +1670,7 @@ void Game_Map::SetPositionY(int y, bool reset_panorama) { if (LoopVertical()) { y = Utils::PositiveModulo(y, map_height); } else { - y = std::max(0, std::min(map_height - screen_height, y)); + y = std::clamp(y, 0, map_height - screen_height); } map_info.position_y = y; if (reset_panorama) { @@ -2037,19 +2034,19 @@ void Game_Map::Parallax::ResetPositionX() { parallax_fake_x = false; if (!params.scroll_horz && !LoopHorizontal()) { - int screen_width = Player::screen_width; + int pan_screen_width = Player::screen_width; if (Player::game_config.fake_resolution.Get()) { - screen_width = SCREEN_TARGET_WIDTH; + pan_screen_width = SCREEN_TARGET_WIDTH; } - int tiles_per_screen = screen_width / TILE_SIZE; - if (screen_width % TILE_SIZE != 0) { + int tiles_per_screen = pan_screen_width / TILE_SIZE; + if (pan_screen_width % TILE_SIZE != 0) { ++tiles_per_screen; } - if (GetTilesX() > tiles_per_screen && parallax_width > screen_width) { + if (GetTilesX() > tiles_per_screen && parallax_width > pan_screen_width) { const int w = (GetTilesX() - tiles_per_screen) * TILE_SIZE; - const int ph = 2 * std::min(w, parallax_width - screen_width) * map_info.position_x / w; + const int ph = 2 * std::min(w, parallax_width - pan_screen_width) * map_info.position_x / w; if (Player::IsRPG2k()) { SetPositionX(ph); } else { @@ -2075,19 +2072,19 @@ void Game_Map::Parallax::ResetPositionY() { parallax_fake_y = false; if (!params.scroll_vert && !Game_Map::LoopVertical()) { - int screen_height = Player::screen_height; + int pan_screen_height = Player::screen_height; if (Player::game_config.fake_resolution.Get()) { - screen_height = SCREEN_TARGET_HEIGHT; + pan_screen_height = SCREEN_TARGET_HEIGHT; } - int tiles_per_screen = screen_height / TILE_SIZE; - if (screen_height % TILE_SIZE != 0) { + int tiles_per_screen = pan_screen_height / TILE_SIZE; + if (pan_screen_height % TILE_SIZE != 0) { ++tiles_per_screen; } - if (GetTilesY() > tiles_per_screen && parallax_height > screen_height) { + if (GetTilesY() > tiles_per_screen && parallax_height > pan_screen_height) { const int h = (GetTilesY() - tiles_per_screen) * TILE_SIZE; - const int pv = 2 * std::min(h, parallax_height - screen_height) * map_info.position_y / h; + const int pv = 2 * std::min(h, parallax_height - pan_screen_height) * map_info.position_y / h; SetPositionY(pv); } else { panorama.pan_y = 0; diff --git a/src/game_pictures.cpp b/src/game_pictures.cpp index 3fc5f26010..19e6591672 100644 --- a/src/game_pictures.cpp +++ b/src/game_pictures.cpp @@ -498,7 +498,10 @@ void Game_Pictures::Picture::AttachWindow(const Window_Base& window) { CreateSprite(); - sprite->SetBitmap(std::make_shared(window.GetWidth(), window.GetHeight(), data.use_transparent_color)); + auto bmp = std::make_shared(window.GetWidth(), window.GetHeight(), data.use_transparent_color); + bmp->SetId(fmt::format("Window:addr={},w={},h={}", (void*)&window, window.GetWidth(), window.GetHeight())); + + sprite->SetBitmap(bmp); sprite->OnPictureShow(); sprite->SetVisible(true); diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 52e74c1cea..c16d988a33 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -106,12 +106,15 @@ int Game_Strings::Split(Str_Params params, const std::string& delimiter, int str return -1; } - int splits = 0; std::string str = ToString(Get(params.string_id)); params.string_id = string_out_id; + int components = 0; + if (delimiter.empty()) { + // Count the characters (or the codepoints in our case) + components = 0; const char* iter = str.data(); const auto end = str.data() + str.size(); @@ -127,19 +130,20 @@ int Game_Strings::Split(Str_Params params, const std::string& delimiter, int str Set(params, std::string(start_copy, iter - start_copy)); params.string_id++; - splits++; + components++; } } else { + components = 1; + if (str.find(delimiter) == std::string::npos) { - // token not found -> 1 split - splits = 1; + // token not found } else { std::string token; for (auto index = str.find(delimiter); index != std::string::npos; index = str.find(delimiter)) { token = str.substr(0, index); Set(params, token); params.string_id++; - splits++; + components++; str.erase(0, index + delimiter.length()); } } @@ -147,8 +151,8 @@ int Game_Strings::Split(Str_Params params, const std::string& delimiter, int str // set the remaining string Set(params, str); - variables.Set(var_id, splits); - return splits; + variables.Set(var_id, components); + return components; } std::string Game_Strings::FromFile(StringView filename, int encoding, bool& do_yield) { @@ -171,6 +175,11 @@ std::string Game_Strings::FromFile(StringView filename, int encoding, bool& do_y if (encoding == 0) { lcf::Encoder enc(Player::encoding); enc.Encode(file_content); + } else { + // UTF-8: Remove Byte Order Mask + if (file_content.size() >= 3 && file_content[0] == '\xEF' && file_content[1] == '\xBB' && file_content[2] == '\xBF') { + file_content.erase(0, 3); + } } return file_content; diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index 7619adc554..d307e66270 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -27,6 +27,7 @@ #include "game_variables.h" #include "main_data.h" #include "output.h" +#include "player.h" #include #include @@ -38,6 +39,7 @@ 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. */ @@ -117,7 +119,62 @@ namespace { }; } -int process(std::vector::iterator& it, std::vector::iterator end, const Game_BaseInterpreterContext& ip) { +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; + } + } + + 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(); + } + + 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; + } + } +}; + +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; @@ -166,108 +223,169 @@ int process(std::vector::iterator& it, std::vector::iterator e value = (value << 24) + (imm3 << 16) + (imm2 << 8) + imm; return value; case Op::Var: - imm = process(it, end, ip); + imm = Process(it, end, ip); return Main_Data::game_variables->Get(imm); case Op::Switch: - imm = process(it, end, ip); + imm = Process(it, end, ip); return Main_Data::game_switches->GetInt(imm); case Op::VarIndirect: - imm = process(it, end, ip); + imm = Process(it, end, ip); return Main_Data::game_variables->GetIndirect(imm); case Op::SwitchIndirect: - imm = process(it, end, ip); + 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); + imm = Process(it, end, ip); return -imm; case Op::Not: - imm = process(it, end, ip); + imm = Process(it, end, ip); return !imm ? 0 : 1; case Op::Flip: - imm = process(it, end, ip); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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 @@ -285,125 +403,125 @@ int process(std::vector::iterator& it, std::vector::iterator e Output::Warning("Maniac: Expression rnd args {} != 2", imm2); return 0; } - imm3 = process(it, end, ip); - return ControlVariables::Random(process(it, end, ip), imm3); + 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); + 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); + 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; } - return ControlVariables::Actor(process(it, end, ip), imm3); + 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); + 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); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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); + Process(it, end, ip); } return 0; } @@ -413,6 +531,32 @@ int process(std::vector::iterator& it, std::vector::iterator e } } +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; + + if (it == end) { + return {Op::Null, 0}; + } + + auto op = static_cast(*it); + ++it; + + // 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}; + default: + --it; // back on the op as op is fetched again by Process + imm = Process(it, end, ip); + return {op, imm}; + } +} + int32_t ManiacPatch::ParseExpression(Span op_codes, const Game_BaseInterpreterContext& interpreter) { std::vector ops; for (auto &o: op_codes) { @@ -423,7 +567,7 @@ 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); + return Process(beg, ops.end(), interpreter); } std::array ManiacPatch::GetKeyRange() { diff --git a/src/sprite_enemy.cpp b/src/sprite_enemy.cpp index 3214316051..1db6398cca 100644 --- a/src/sprite_enemy.cpp +++ b/src/sprite_enemy.cpp @@ -79,6 +79,7 @@ void Sprite_Enemy::OnMonsterSpriteReady(FileRequestResult* result) { if (hue_change) { BitmapRef new_graphic = Bitmap::Create(graphic->GetWidth(), graphic->GetHeight()); new_graphic->HueChangeBlit(0, 0, *graphic, graphic->GetRect(), hue); + new_graphic->SetId(fmt::format("{},hue={}", graphic->GetId(), hue)); graphic = new_graphic; }