diff --git a/src/game_config_game.cpp b/src/game_config_game.cpp index 0789bd17f9..930c56e9c7 100644 --- a/src/game_config_game.cpp +++ b/src/game_config_game.cpp @@ -87,6 +87,7 @@ void Game_ConfigGame::LoadFromArgs(CmdlineParser& cp) { patch_rpg2k3_commands.Lock(false); patch_anti_lag_switch.Lock(0); patch_direct_menu.Lock(0); + patch_powermode.Lock(0); RuntimePatches::LockPatchesAsDiabled(); patch_override = true; @@ -156,6 +157,11 @@ void Game_ConfigGame::LoadFromArgs(CmdlineParser& cp) { } continue; } + if (cp.ParseNext(arg, 0, { "--patch-powermode", "--no-patch-powermode" })) { + patch_powermode.Set(arg.ArgIsOn()); + patch_override = true; + continue; + } if (RuntimePatches::ParseFromCommandLine(cp)) { patch_override = true; continue; @@ -235,6 +241,10 @@ void Game_ConfigGame::LoadFromStream(Filesystem_Stream::InputStream& is) { patch_override = true; } + if (patch_powermode.FromIni(ini)) { + patch_override = true; + } + if (RuntimePatches::ParseFromIni(ini)) { patch_override = true; } diff --git a/src/game_config_game.h b/src/game_config_game.h index 354d1d9bb8..05fc701603 100644 --- a/src/game_config_game.h +++ b/src/game_config_game.h @@ -71,6 +71,8 @@ struct Game_ConfigGame { ConfigParam patch_guardrevamp_normal{ "GuardRevamp", "Changes damage calculation for defense situations (Normal)", "Patch", "GuardRevamp.NormalDefense", 0 }; ConfigParam patch_guardrevamp_strong{ "GuardRevamp", "Changes damage calculation for defense situations (Strong)", "Patch", "GuardRevamp.StrongDefense", 0 }; + ConfigParam patch_powermode{ "Power Mode 2003", "", "Patch", "PowerMode2003", 0 }; + // Command line only BoolConfigParam patch_support{ "Support patches", "When OFF all patch support is disabled", "", "", true }; diff --git a/src/game_ineluki.cpp b/src/game_ineluki.cpp index 7a8d660453..ad5c0c0a1e 100644 --- a/src/game_ineluki.cpp +++ b/src/game_ineluki.cpp @@ -25,6 +25,7 @@ #include "input.h" #include "player.h" #include "system.h" +#include "game_runtime_patches.h" #include @@ -180,17 +181,18 @@ bool Game_Ineluki::Execute(std::string_view ini_file) { } std::string arg_lower = Utils::LowerCase(cmd.arg); if (arg_lower == "left") { - mouse_decision_binding = MouseReturnMode::Left; + RuntimePatches::mouse_bindings.bind_decision = RuntimePatches::MouseButtonBindingMode::Left; } else if (arg_lower == "right") { - mouse_decision_binding = MouseReturnMode::Right; + RuntimePatches::mouse_bindings.bind_decision = RuntimePatches::MouseButtonBindingMode::Right; } else if (arg_lower == "both") { - mouse_decision_binding = MouseReturnMode::Both; + RuntimePatches::mouse_bindings.bind_decision = RuntimePatches::MouseButtonBindingMode::Both; } else if (arg_lower == "none") { - mouse_decision_binding = MouseReturnMode::None; + RuntimePatches::mouse_bindings.bind_decision = RuntimePatches::MouseButtonBindingMode::None; } else { Output::Warning("Ineluki: Invalid value for setMouseAsReturn"); - mouse_decision_binding = MouseReturnMode::None; + RuntimePatches::mouse_bindings.bind_decision = RuntimePatches::MouseButtonBindingMode::None; } + RuntimePatches::mouse_bindings.enabled = true; } else if (cmd.name == "setmousewheelaskeys") { // This command is only found in a few uncommon versions of the patch if (!mouse_support) { @@ -198,15 +200,16 @@ bool Game_Ineluki::Execute(std::string_view ini_file) { } std::string arg_lower = Utils::LowerCase(cmd.arg); if (arg_lower == "updown") { - mouse_wheel_binding = MouseWheelMode::UpDown; + RuntimePatches::mouse_bindings.bind_wheel = RuntimePatches::MouseWheelMode::UpDown; } else if (arg_lower == "leftright") { - mouse_wheel_binding = MouseWheelMode::LeftRight; + RuntimePatches::mouse_bindings.bind_wheel = RuntimePatches::MouseWheelMode::LeftRight; } else if (arg_lower == "none") { - mouse_wheel_binding = MouseWheelMode::None; + RuntimePatches::mouse_bindings.bind_wheel = RuntimePatches::MouseWheelMode::None; } else { Output::Warning("Ineluki: Invalid value for setMouseWheelAsKeys"); - mouse_wheel_binding = MouseWheelMode::None; + RuntimePatches::mouse_bindings.bind_wheel = RuntimePatches::MouseWheelMode::None; } + RuntimePatches::mouse_bindings.enabled = true; } } @@ -335,9 +338,6 @@ void Game_Ineluki::Update() { if (key_support) { UpdateKeys(); } - if (mouse_support) { - UpdateMouse(); - } } void Game_Ineluki::UpdateKeys() { @@ -375,34 +375,6 @@ void Game_Ineluki::UpdateKeys() { } } -void Game_Ineluki::UpdateMouse() { -#if defined(USE_MOUSE) && defined(SUPPORT_MOUSE) - if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_LEFT)) { - if ((mouse_decision_binding == MouseReturnMode::Left || mouse_decision_binding == MouseReturnMode::Both)) { - Input::SimulateButtonPress(Input::DECISION); - } - } else if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_RIGHT)) { - if ((mouse_decision_binding == MouseReturnMode::Right || mouse_decision_binding == MouseReturnMode::Both)) { - Input::SimulateButtonPress(Input::DECISION); - } - } - - if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_SCROLLUP)) { - if (mouse_wheel_binding == MouseWheelMode::UpDown) { - Input::SimulateButtonPress(Input::UP); - } else if (mouse_wheel_binding == MouseWheelMode::LeftRight) { - Input::SimulateButtonPress(Input::LEFT); - } - } else if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_SCROLLDOWN)) { - if (mouse_wheel_binding == MouseWheelMode::UpDown) { - Input::SimulateButtonPress(Input::DOWN); - } else if (mouse_wheel_binding == MouseWheelMode::LeftRight) { - Input::SimulateButtonPress(Input::RIGHT); - } - } -#endif -} - void Game_Ineluki::OnScriptFileReady(FileRequestResult* result) { auto it = std::find_if(async_scripts.begin(), async_scripts.end(), [&](const auto& a) { return a.script_name == result->file; diff --git a/src/game_ineluki.h b/src/game_ineluki.h index 80a46cbd38..150b03d972 100644 --- a/src/game_ineluki.h +++ b/src/game_ineluki.h @@ -85,11 +85,6 @@ class Game_Ineluki { */ void UpdateKeys(); - /** - * Handles virtual key bindings for mouse buttons. - */ - void UpdateMouse(); - /** * Parses and caches the script. * @@ -134,22 +129,6 @@ class Game_Ineluki { bool mouse_support = false; int mouse_id_prefix = 0; - enum class MouseReturnMode { - None, - Left, - Right, - Both - }; - - enum class MouseWheelMode { - None, - UpDown, - LeftRight - }; - - MouseReturnMode mouse_decision_binding = MouseReturnMode::None; - MouseWheelMode mouse_wheel_binding = MouseWheelMode::None; - struct Mapping { Input::Keys::InputKey key; const char* name; diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 95be239efe..f146bf8248 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -468,7 +468,10 @@ void Game_Interpreter::Update(bool reset_loop_count) { const int key = _keyinput.CheckInput(); Main_Data::game_variables->Set(_keyinput.variable, key); Game_Map::SetNeedRefreshForVarChange(_keyinput.variable); - if (key == 0) { + RuntimePatches::OnVariableChanged(_keyinput.variable); + // Content of KeyInput value might have changed due to a + // patch side effect -> Read it again + if (Main_Data::game_variables->Get(_keyinput.variable) == 0) { ++_keyinput.wait_frames; break; } @@ -477,6 +480,7 @@ void Game_Interpreter::Update(bool reset_loop_count) { Main_Data::game_variables->Set(_keyinput.time_variable, (_keyinput.wait_frames * 10) / Game_Clock::GetTargetGameFps()); Game_Map::SetNeedRefreshForVarChange(_keyinput.time_variable); + RuntimePatches::OnVariableChanged(_keyinput.time_variable); } _keyinput.wait = false; } @@ -1328,6 +1332,7 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com break; } Game_Map::SetNeedRefreshForVarChange(start); + RuntimePatches::OnVariableChanged(start); } else if (com.parameters[4] == 1) { // Multiple variables - Direct variable lookup int var_id = com.parameters[5]; @@ -1367,6 +1372,7 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com break; } Game_Map::SetNeedRefresh(true); + RuntimePatches::OnVariableRangeChanged(start, end); } else if (com.parameters[4] == 2) { // Multiple variables - Indirect variable lookup int var_id = com.parameters[5]; @@ -1406,6 +1412,7 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com break; } Game_Map::SetNeedRefresh(true); + RuntimePatches::OnVariableRangeChanged(start, end); } else if (com.parameters[4] == 3) { // Multiple variables - random int rmax = max(com.parameters[5], com.parameters[6]); @@ -1446,6 +1453,7 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com break; } Game_Map::SetNeedRefresh(true); + RuntimePatches::OnVariableRangeChanged(start, end); } else { // Multiple variables - constant switch (operation) { @@ -1484,6 +1492,7 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com break; } Game_Map::SetNeedRefresh(true); + RuntimePatches::OnVariableRangeChanged(start, end); } } @@ -1947,6 +1956,7 @@ bool Game_Interpreter::CommandSimulatedAttack(lcf::rpg::EventCommand const& com) if (com.parameters[6] != 0) { Main_Data::game_variables->Set(com.parameters[7], result); + RuntimePatches::OnVariableChanged(com.parameters[7]); Game_Map::SetNeedRefresh(true); } } @@ -2239,6 +2249,7 @@ bool Game_Interpreter::CommandMemorizeLocation(lcf::rpg::EventCommand const& com Main_Data::game_variables->Set(var_x, player->GetX()); Main_Data::game_variables->Set(var_y, player->GetY()); Game_Map::SetNeedRefreshForVarChange({var_map_id, var_x, var_y}); + RuntimePatches::OnVariableChanged({var_map_id, var_x, var_y}); return true; } @@ -2357,6 +2368,7 @@ bool Game_Interpreter::CommandStoreTerrainID(lcf::rpg::EventCommand const& com) int var_id = com.parameters[3]; Main_Data::game_variables->Set(var_id, Game_Map::GetTerrainTag(x, y)); Game_Map::SetNeedRefreshForVarChange(var_id); + RuntimePatches::OnVariableChanged(var_id); return true; } @@ -2367,6 +2379,7 @@ bool Game_Interpreter::CommandStoreEventID(lcf::rpg::EventCommand const& com) { auto* ev = Game_Map::GetEventAt(x, y, false); Main_Data::game_variables->Set(var_id, ev ? ev->GetId() : 0); Game_Map::SetNeedRefreshForVarChange(var_id); + RuntimePatches::OnVariableChanged(var_id); return true; } @@ -3252,6 +3265,7 @@ bool Game_Interpreter::CommandKeyInputProc(lcf::rpg::EventCommand const& com) { // While waiting the variable is reset to 0 each frame. Main_Data::game_variables->Set(var_id, 0); Game_Map::SetNeedRefreshForVarChange(var_id); + RuntimePatches::OnVariableChanged(var_id); } if (wait && Game_Message::IsMessageActive()) { @@ -3367,6 +3381,7 @@ bool Game_Interpreter::CommandKeyInputProc(lcf::rpg::EventCommand const& com) { int key = _keyinput.CheckInput(); Main_Data::game_variables->Set(_keyinput.variable, key); Game_Map::SetNeedRefreshForVarChange(_keyinput.variable); + RuntimePatches::OnVariableChanged(_keyinput.variable); return true; } diff --git a/src/game_pictures.cpp b/src/game_pictures.cpp index 21422f2c31..952f62b03a 100644 --- a/src/game_pictures.cpp +++ b/src/game_pictures.cpp @@ -29,6 +29,7 @@ #include "scene.h" #include "drawable_mgr.h" #include "sprite_picture.h" +#include "game_runtime_patches.h" static bool IsEmpty(const lcf::rpg::SavePicture& data, int frames) { lcf::rpg::SavePicture empty; @@ -570,7 +571,9 @@ void Game_Pictures::Picture::Update(bool is_battle) { // Update rotation if (data.effect_mode == lcf::rpg::SavePicture::Effect_rotation) { - data.current_rotation += data.current_effect_power; + if (!RuntimePatches::PowerMode2003::ApplyPictureRotation(data)) { + data.current_rotation += data.current_effect_power; + } } // Update waver phase diff --git a/src/game_runtime_patches.cpp b/src/game_runtime_patches.cpp index 8222f13077..e35566e221 100644 --- a/src/game_runtime_patches.cpp +++ b/src/game_runtime_patches.cpp @@ -18,6 +18,7 @@ // Headers #include "game_runtime_patches.h" +#include #include "game_map.h" #include "game_party.h" #include "game_switches.h" @@ -27,6 +28,11 @@ #include "game_enemy.h" #include "main_data.h" #include "player.h" +#include "output.h" +#include "input.h" +#include "scene.h" +#include "scene_load.h" +#include "baseui.h" namespace { template @@ -149,6 +155,84 @@ void RuntimePatches::DetermineActivePatches(std::vector& patches) { PrintPatch(patches, GuardRevamp::patch_args); } +void RuntimePatches::OnUpdate() { + // Handle virtual key bindings for mouse buttons. + if (mouse_bindings.enabled) { +#if defined(USE_MOUSE) && defined(SUPPORT_MOUSE) + if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_LEFT)) { + if ((mouse_bindings.bind_decision == MouseButtonBindingMode::Left || mouse_bindings.bind_decision == MouseButtonBindingMode::Both)) { + Input::SimulateButtonPress(Input::DECISION); + } + if ((mouse_bindings.bind_cancel == MouseButtonBindingMode::Left || mouse_bindings.bind_cancel == MouseButtonBindingMode::Both)) { + Input::SimulateButtonPress(Input::CANCEL); + } + } else if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_RIGHT)) { + if ((mouse_bindings.bind_decision == MouseButtonBindingMode::Right || mouse_bindings.bind_decision == MouseButtonBindingMode::Both)) { + Input::SimulateButtonPress(Input::DECISION); + } + if ((mouse_bindings.bind_cancel == MouseButtonBindingMode::Right || mouse_bindings.bind_cancel == MouseButtonBindingMode::Both)) { + Input::SimulateButtonPress(Input::CANCEL); + } + } + + if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_SCROLLUP)) { + if (mouse_bindings.bind_wheel == MouseWheelMode::UpDown) { + Input::SimulateButtonPress(Input::UP); + } else if (mouse_bindings.bind_wheel == MouseWheelMode::LeftRight) { + Input::SimulateButtonPress(Input::LEFT); + } + } else if (Input::IsRawKeyTriggered(Input::Keys::MOUSE_SCROLLDOWN)) { + if (mouse_bindings.bind_wheel == MouseWheelMode::UpDown) { + Input::SimulateButtonPress(Input::DOWN); + } else if (mouse_bindings.bind_wheel == MouseWheelMode::LeftRight) { + Input::SimulateButtonPress(Input::RIGHT); + } + } +#endif + } +} + +void RuntimePatches::OnResetGameObjects() { + mouse_bindings = {}; + + if (Player::game_config.patch_powermode.Get()) { + Player::game_config.new_game.Set(true); + Main_Data::game_variables->Set(PowerMode2003::PM_VAR_CR0, FileFinder::HasSavegame() ? 1 : 0); + + mouse_bindings.enabled = true; + mouse_bindings.bind_decision = MouseButtonBindingMode::Left; + mouse_bindings.bind_cancel = MouseButtonBindingMode::Right; + } +} + +void RuntimePatches::OnLoadSavegame() { + if (Player::game_config.patch_powermode.Get()) { + Main_Data::game_variables->Set(PowerMode2003::PM_VAR_CR0, FileFinder::HasSavegame() ? 1 : 0); + } +} + +void RuntimePatches::OnVariableChanged(int variable_id) { + if (EP_UNLIKELY(Player::game_config.patch_powermode.Get())) { + PowerMode2003::HandleVariableHooks(variable_id); + } +} + +void RuntimePatches::OnVariableChanged(std::initializer_list variable_ids) { + if (EP_UNLIKELY(Player::game_config.patch_powermode.Get())) { + for (int var_id : variable_ids) { + PowerMode2003::HandleVariableHooks(var_id); + } + } +} + +void RuntimePatches::OnVariableRangeChanged(int start_id, int end_id) { + if (EP_UNLIKELY(Player::game_config.patch_powermode.Get())) { + for (int var_id = start_id; var_id <= end_id; ++var_id) { + PowerMode2003::HandleVariableHooks(var_id); + } + } +} + bool RuntimePatches::EncounterRandomnessAlert::HandleEncounter(int troop_id) { if (auto var_id = Player::game_config.patch_encounter_random_alert_var.Get(); var_id > 0) { Main_Data::game_player->SetTotalEncounterRate(0); @@ -289,3 +373,162 @@ bool RuntimePatches::GuardRevamp::OverrideDamageAdjustment(int& dmg, const Game_ } return false; } + +namespace RuntimePatches::PowerMode2003 { + void HandleCommands() { + int op = Main_Data::game_variables->Get(PM_VAR_CR0); + if (op == 255 && FileFinder::HasSavegame()) { + Scene::instance->SetRequestedScene(std::make_shared()); + } else if (op == 254) { + Player::exit_flag = true; + } + Main_Data::game_variables->Set(PM_VAR_CR0, FileFinder::HasSavegame() ? 1 : 0); + } + + void HandleMouse() { +#if !defined(USE_MOUSE_OR_TOUCH) || !defined(SUPPORT_MOUSE_OR_TOUCH) + Output::Warning("PowerMode2003: Mouse input is not supported on this platform"); + return; +#endif + Point mouse_pos = Input::GetMousePosition(); + Main_Data::game_variables->Set(PM_VAR_MCOORDX, mouse_pos.x); + Main_Data::game_variables->Set(PM_VAR_MCOORDY, mouse_pos.y); + + } + + void HandleKeyboard() { +#if !defined(SUPPORT_KEYBOARD) + Output::Warning("PowerMode2003: Keyboard input is not supported on this platform"); + return; +#endif + int vk_id = Main_Data::game_variables->Get(PM_VAR_KEY); + if (vk_id == 0) { + if (Input::IsRawKeyPressed(Input::Keys::LSHIFT) || Input::IsRawKeyPressed(Input::Keys::RSHIFT)) { + vk_id = RuntimePatches::VirtualKeys::InputKeyToVirtualKey(Input::Keys::SHIFT); + } else if (Input::IsRawKeyPressed(Input::Keys::LCTRL) || Input::IsRawKeyPressed(Input::Keys::RCTRL)) { + vk_id = RuntimePatches::VirtualKeys::InputKeyToVirtualKey(Input::Keys::CTRL); + } else if (Input::IsRawKeyPressed(Input::Keys::LALT) || Input::IsRawKeyPressed(Input::Keys::RALT)) { + vk_id = RuntimePatches::VirtualKeys::InputKeyToVirtualKey(Input::Keys::ALT); + } else { + // check the whole keyboard for any key press + for (int i = 1; i < Input::Keys::KEYS_COUNT; ++i) { + auto input_key = static_cast(i); + if (Input::IsRawKeyPressed(input_key) && (vk_id = RuntimePatches::VirtualKeys::InputKeyToVirtualKey(input_key))) { + break; + } + } + } + + Main_Data::game_variables->Set(PM_VAR_KEY, vk_id); + } else if (vk_id > 0 && vk_id <= 255) { + // check a single key + auto input_key = RuntimePatches::VirtualKeys::VirtualKeyToInputKey(vk_id); + int result = 0; + if (input_key == Input::Keys::NONE) { + Output::Debug("PowerMode2003: Unsupported keycode {}", vk_id); + } + bool pressed = false; + if (input_key == Input::Keys::SHIFT) { + pressed = Input::IsRawKeyPressed(Input::Keys::LSHIFT) || Input::IsRawKeyPressed(Input::Keys::RSHIFT); + } else if (input_key == Input::Keys::CTRL) { + pressed = Input::IsRawKeyPressed(Input::Keys::LCTRL) || Input::IsRawKeyPressed(Input::Keys::RCTRL); + } else if (input_key == Input::Keys::ALT) { + pressed = Input::IsRawKeyPressed(Input::Keys::LALT) || Input::IsRawKeyPressed(Input::Keys::RALT); + } else { + pressed = Input::IsRawKeyPressed(input_key); + } + if (!pressed) { + Main_Data::game_variables->Set(PM_VAR_KEY, 0); + } + } else { + Main_Data::game_variables->Set(PM_VAR_KEY, 0); + } + } + + void HandleFloatComputation() { + auto input1 = static_cast(Main_Data::game_variables->Get(PM_VAR_FVALUE1)); + + // Some versions of the documentation for this patch have the target ids swapped. + // (SIN -> FVALUE1, COS -> FVALUE2, TAN -> FVALUE1) + // + // But the only known RPG_RT versions of this patch actually save them like follows..: + switch (Main_Data::game_variables->Get(PM_VAR_FCODE)) { + case 1: + { + auto v_sin = std::sin(input1 * M_PI / 180); + auto v_cos = std::cos(input1 * M_PI / 180); + Main_Data::game_variables->Set(PM_VAR_FVALUE2, static_cast(v_sin * 1'000'000)); + Main_Data::game_variables->Set(PM_VAR_FVALUE1, static_cast(v_cos * 1'000'000)); + break; + } + case 2: + { + auto v_tan = std::tan(input1 * M_PI / 180); + Main_Data::game_variables->Set(PM_VAR_FVALUE2, static_cast(v_tan * 1'000'000)); + break; + } + case 3: + { + float v_int; + float v_fract = std::modf(std::sqrt(input1), &v_int); + Main_Data::game_variables->Set(PM_VAR_FVALUE1, static_cast(v_int)); + Main_Data::game_variables->Set(PM_VAR_FVALUE2, static_cast(v_fract * 1'000'000)); + break; + } + case 4: + { + auto input2 = static_cast(Main_Data::game_variables->Get(PM_VAR_FVALUE2)); + float v_int; + float v_fract = std::modf(input1 / input2, &v_int); + Main_Data::game_variables->Set(PM_VAR_FVALUE1, static_cast(v_int)); + Main_Data::game_variables->Set(PM_VAR_FVALUE2, static_cast(v_fract * 1'000'000)); + break; + } + default: + break; + } + } +} + +void RuntimePatches::PowerMode2003::HandleVariableHooks(int var_id) { + switch (var_id) { + case PM_VAR_CR0: + HandleCommands(); + break; + case PM_VAR_MCOORDY: + HandleMouse(); + Game_Map::SetNeedRefreshForVarChange(PM_VAR_MCOORDX); + break; + case PM_VAR_KEY: + HandleKeyboard(); + break; + case PM_VAR_FCODE: + HandleFloatComputation(); + Game_Map::SetNeedRefreshForVarChange(PM_VAR_FVALUE1); + Game_Map::SetNeedRefreshForVarChange(PM_VAR_FVALUE2); + break; + default: + return; + } +} + +bool RuntimePatches::PowerMode2003::ApplyPictureRotation(lcf::rpg::SavePicture& pict) { + if (Player::game_config.patch_powermode.Get()) { + auto var_start_pict_rotations = Main_Data::game_variables->Get(PM_VAR_SPECIAL); + if (var_start_pict_rotations <= 10) { + // if the "SPECIAL" mode is off, then the value for "bottom transparency" + // is reused to specify a counter-clock-wise rotation + if (static_cast(pict.finish_bot_trans) >= 50) { + pict.current_rotation -= pict.current_effect_power; + return true; + } + return false; + } + if (pict.ID <= 50) { + pict.current_rotation = Main_Data::game_variables->Get(var_start_pict_rotations + pict.ID - 1); + return true; + } + } + + return false; +} diff --git a/src/game_runtime_patches.h b/src/game_runtime_patches.h index 08504b4322..eec866cd99 100644 --- a/src/game_runtime_patches.h +++ b/src/game_runtime_patches.h @@ -21,6 +21,8 @@ #include "game_config_game.h" #include "cmdline_parser.h" #include "player.h" +#include "input.h" +#include "lcf/rpg/savepicture.h" class Game_Actor; class Game_Battler; @@ -39,6 +41,26 @@ namespace RuntimePatches { } }; + enum class MouseButtonBindingMode { + None, + Left, + Right, + Both + }; + + enum class MouseWheelMode { + None, + UpDown, + LeftRight + }; + + inline struct { + bool enabled = false; + MouseButtonBindingMode bind_decision = MouseButtonBindingMode::None; + MouseButtonBindingMode bind_cancel = MouseButtonBindingMode::None; + MouseWheelMode bind_wheel = MouseWheelMode::None; + } mouse_bindings; + void LockPatchesAsDiabled(); bool ParseFromCommandLine(CmdlineParser& cp); @@ -47,6 +69,24 @@ namespace RuntimePatches { void DetermineActivePatches(std::vector& patches); + void OnUpdate(); + + void OnResetGameObjects(); + + void OnLoadSavegame(); + + // Handles patch side effects caused by Variable changes. This function is meant + // to be called only for operations that are possible in an unpatched RPG_RT! + void OnVariableChanged(int variable_id); + + // Handles patch side effects caused by Variable changes. This function is meant + // to be called only for operations that are possible in an unpatched RPG_RT! + void OnVariableChanged(std::initializer_list variable_ids); + + // Handles patch side effects caused by Variable changes. This function is meant + // to be called only for operations that are possible in an unpatched RPG_RT! + void OnVariableRangeChanged(int start_id, int end_id); + /** * Support for RPG_RT patch 'Encounter Randomness Alert'. * This patch skips the normal battle startup logic whenever a random @@ -196,6 +236,38 @@ namespace RuntimePatches { constexpr uint32_t InputKeyToVirtualKey(Input::Keys::InputKey input_key); } + + + /** + * Support for RPG_RT patch 'Power Mode 2003'. + * (aka. "Mega Patch 2003" in Italian scene) + * + * This patch adds some specialiced commands by hooking into + * the first 8 in-game variables, functionally using them + * as 'Control Registers'. + * + * These new commands include: + * - V[1]: Calling up the Load menu, Exiting the game + * & Checking for the existence of Savefiles + * - V[2-3]: Retrieving the current coordinates of the mouse cursor. + * - V[4]: Retrieving key inputs for entire keyboard. + * - V[5-7]: Floating-point operations + * - V[8]: Specifying custom rotation for Picture IDs 1 - 50 + * */ + namespace PowerMode2003 { + constexpr int PM_VAR_CR0 = 1; + constexpr int PM_VAR_MCOORDX = 2; + constexpr int PM_VAR_MCOORDY = 3; + constexpr int PM_VAR_KEY = 4; + constexpr int PM_VAR_FVALUE1 = 5; + constexpr int PM_VAR_FVALUE2 = 6; + constexpr int PM_VAR_FCODE = 7; + constexpr int PM_VAR_SPECIAL = 8; + + void HandleVariableHooks(int var_id); + + bool ApplyPictureRotation(lcf::rpg::SavePicture& pict); + } } constexpr Input::Keys::InputKey RuntimePatches::VirtualKeys::VirtualKeyToInputKey(uint32_t key_id) { diff --git a/src/player.cpp b/src/player.cpp index 24a250138e..847e8d896b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -378,7 +378,7 @@ void Player::Update(bool update_scene) { if (Main_Data::game_ineluki) { Main_Data::game_ineluki->Update(); } - + RuntimePatches::OnUpdate(); Scene::instance->Update(); } @@ -836,6 +836,11 @@ void Player::CreateGameObjects() { if (!FileFinder::Game().FindFile(DESTINY_DLL).empty()) { game_config.patch_destiny.Set(true); } + + if (!FileFinder::Game().FindFile("warp.dll").empty()) { + game_config.patch_powermode.Set(true); + } + } game_config.PrintActivePatches(); @@ -964,6 +969,8 @@ void Player::ResetGameObjects() { Main_Data::game_system->ReloadSystemGraphic(); + RuntimePatches::OnResetGameObjects(); + Input::ResetMask(); } @@ -1221,6 +1228,7 @@ void Player::LoadSavegame(const std::string& save_name, int save_id) { Game_Map::Dispose(); OnMapSaveFileReady(request, std::move(save)); + RuntimePatches::OnLoadSavegame(); if (load_on_map) { // Increment frame counter for consistency with a normal savegame load @@ -1477,6 +1485,7 @@ Engine options: version of the engine. --patch-rpg2k3-cmds Support all RPG Maker 2003 event commands in any version of the engine. + --patch-powermode Enable PowerMode 2003 Patch by Firesta. --no-patch Disable all engine patches. To disable a single patch, prefix any of the patch options with --no- --project-path PATH Instead of using the working directory, the game in PATH diff --git a/src/scene_debug.cpp b/src/scene_debug.cpp index 9206a73af0..33924ac77b 100644 --- a/src/scene_debug.cpp +++ b/src/scene_debug.cpp @@ -1022,6 +1022,7 @@ void Scene_Debug::DoVariable() { const auto value = GetFrame(0).value; Main_Data::game_variables->Set(var_id, value); Game_Map::SetNeedRefresh(true); + RuntimePatches::OnVariableChanged(var_id); Pop(); } diff --git a/src/sprite_picture.cpp b/src/sprite_picture.cpp index 3fadea8568..4c7c21dd47 100644 --- a/src/sprite_picture.cpp +++ b/src/sprite_picture.cpp @@ -30,7 +30,7 @@ Sprite_Picture::Sprite_Picture(int pic_id, Drawable::Flags flags) pic_id(pic_id), feature_spritesheet(Player::IsRPG2k3ECommands()), feature_priority_layers(Player::IsMajorUpdatedVersion()), - feature_bottom_trans(Player::IsRPG2k3() && !Player::IsRPG2k3E()) + feature_bottom_trans(Player::IsRPG2k3() && !Player::IsRPG2k3E() && !Player::game_config.patch_powermode.Get()) { // Initialize Z value for legacy pictures. Will be overriden in OnPictureShow if // priority layers feature is enabled. diff --git a/src/window_message.cpp b/src/window_message.cpp index 4d797a29de..f320bd11b8 100644 --- a/src/window_message.cpp +++ b/src/window_message.cpp @@ -27,6 +27,7 @@ #include "game_map.h" #include "game_message.h" #include "game_party.h" +#include "game_runtime_patches.h" #include "game_system.h" #include "game_variables.h" #include "input.h" @@ -837,6 +838,7 @@ void Window_Message::InputNumber() { Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision)); Main_Data::game_variables->Set(pending_message.GetNumberInputVariable(), number_input_window->GetNumber()); Game_Map::SetNeedRefresh(true); + RuntimePatches::OnVariableChanged(pending_message.GetNumberInputVariable()); number_input_window->SetNumber(0); number_input_window->SetActive(false); }