From cb5afa7cab1f4ac80a0fd59f57c1af96dac6e91b Mon Sep 17 00:00:00 2001 From: florianessl Date: Sun, 26 Jan 2025 20:07:42 +0100 Subject: [PATCH 1/6] Refactor & Extend EasyRPG feature "SetInterpreterFlag" (#3123), according to Ghabry's suggestions: - Overrides of interpreter features are now stored inside a new lcf field "easyrpg_runtime_flags", so the settings can be retrieved from save files. - These overrides are only valid for the current interpreter & the runtime flags will be cleared, once the last frame has been popped - A new global field "active_interpreter_flags" has been introduced, so the Player functions responsible for restricting use to these custom features can access & consider the state of the active interpreter flags for its check. - Implemented a very rudimentary display for active engine flag overrides in window_interpreter --- src/feature.cpp | 8 +- src/game_interpreter.cpp | 123 ++++++++++++++--- src/game_interpreter.h | 13 +- src/game_interpreter_shared.cpp | 18 +++ src/game_interpreter_shared.h | 20 +++ src/player.cpp | 1 + src/player.h | 70 ++++++++++ src/scene_debug.cpp | 7 +- src/window_interpreter.cpp | 231 +++++++++++++++++++++++++++++++- src/window_interpreter.h | 62 ++++++++- 10 files changed, 519 insertions(+), 34 deletions(-) diff --git a/src/feature.cpp b/src/feature.cpp index c7e5e2e576..dbac556a11 100644 --- a/src/feature.cpp +++ b/src/feature.cpp @@ -18,13 +18,19 @@ // Headers #include "feature.h" #include "player.h" +#include "game_interpreter_shared.h" #include bool Feature::HasRpg2kBattleSystem() { if (Player::IsRPG2k()) { return true; } - +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = Player::GetRuntimeFlag(&Flags::use_rpg2k_battle_system_on, &Flags::use_rpg2k_battle_system_off)) { + return *f; + } +#endif return lcf::Data::system.easyrpg_use_rpg2k_battle_system; } diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 06560db4fa..47c137fca5 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -314,7 +314,7 @@ bool Game_Interpreter::ReachedLoopLimit() const { int Game_Interpreter::GetThisEventId() const { auto event_id = GetCurrentEventId(); - if (event_id == 0 && (Player::IsRPG2k3E() || Player::game_config.patch_common_this_event.Get())) { + if (event_id == 0 && (Player::IsRPG2k3E() || Player::IsPatchCommonThisEvent())) { // RM2k3E allows "ThisEvent" commands to run from called // common events. It operates on the last map event in // the call stack. @@ -382,6 +382,10 @@ void Game_Interpreter::Update(bool reset_loop_count) { return; } +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + Player::active_interpreter_flags = &_state.easyrpg_runtime_flags; +#endif + for (; loop_count < loop_limit; ++loop_count) { // If something is calling a menu, we're allowed to execute only 1 command per interpreter. So we pass through if loop_count == 0, and stop at 1 or greater. // RPG_RT compatible behavior. @@ -526,6 +530,9 @@ void Game_Interpreter::Update(bool reset_loop_count) { if (Game_Map::GetNeedRefresh()) { Game_Map::Refresh(); } +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + Player::active_interpreter_flags = nullptr; +#endif } // Setup Starting Event @@ -829,6 +836,12 @@ bool Game_Interpreter::OnFinishStackFrame() { _state.stack.pop_back(); } + if (is_base_frame) { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + ClearStateRuntimeFlags(); +#endif + } + return !is_base_frame; } @@ -2700,7 +2713,7 @@ namespace PicPointerPatch { bool Game_Interpreter::CommandShowPicture(lcf::rpg::EventCommand const& com) { // code 11110 // Older versions of RPG_RT block pictures when message active. - if (!Player::IsEnglish() && !Player::game_config.patch_unlock_pics.Get() && Game_Message::IsMessageActive()) { + if (!Player::IsEnglish() && !Player::IsPatchUnlockPics() && Game_Message::IsMessageActive()) { return false; } @@ -2850,7 +2863,7 @@ bool Game_Interpreter::CommandShowPicture(lcf::rpg::EventCommand const& com) { / bool Game_Interpreter::CommandMovePicture(lcf::rpg::EventCommand const& com) { // code 11120 // Older versions of RPG_RT block pictures when message active. - if (!Player::IsEnglish() && !Player::game_config.patch_unlock_pics.Get() && Game_Message::IsMessageActive()) { + if (!Player::IsEnglish() && !Player::IsPatchUnlockPics() && Game_Message::IsMessageActive()) { return false; } @@ -3005,7 +3018,7 @@ bool Game_Interpreter::CommandMovePicture(lcf::rpg::EventCommand const& com) { / bool Game_Interpreter::CommandErasePicture(lcf::rpg::EventCommand const& com) { // code 11130 // Older versions of RPG_RT block pictures when message active. - if (!Player::IsEnglish() && !Player::game_config.patch_unlock_pics.Get() && Game_Message::IsMessageActive()) { + if (!Player::IsEnglish() && !Player::IsPatchUnlockPics() && Game_Message::IsMessageActive()) { return false; } @@ -5363,25 +5376,97 @@ bool Game_Interpreter::CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand c return true; } - // FIXME: Store them as part of the interpreter state +#ifndef ENABLE_DYNAMIC_INTERPRETER_CONFIG + Output::Warning("CommandEasyRpgSetInterpreterFlag: Not supported on this platform"); + return true; +#else + constexpr std::array, 9> config_names = {{ + { "destiny", 1 }, + { "dynrpg", 2 }, + { "maniac", 3 }, + { "common-this", 4 }, + { "pic-unlock", 5 }, + { "key-patch", 6 }, + { "rpg2k3-cmds", 7 }, + { "rpg2k3-commands", 7 }, + { "rpg2k-battle", 8 } + }}; std::string flag_name = Utils::LowerCase(ToString(com.string)); int flag_value = ValueOrVariable(com.parameters[0], com.parameters[1]); + int flag_id = 0; + + if (flag_name.empty() && com.parameters.size() > 2) { + flag_id = com.parameters[2]; + } else { + auto it = std::find_if(config_names.begin(), config_names.end(), [&flag_name](auto& p) { return p.first == flag_name; }); + if (it != config_names.end()) { + flag_id = it->second; + } + } - if (flag_name == "dynrpg") - Player::game_config.patch_dynrpg.Set(flag_value); - if (flag_name == "maniac") - Player::game_config.patch_maniac.Set(flag_value); - if (flag_name == "common-this") - Player::game_config.patch_common_this_event.Set(flag_value); - if (flag_name == "pic-unlock") - Player::game_config.patch_unlock_pics.Set(flag_value); - if (flag_name == "key-patch") - Player::game_config.patch_key_patch.Set(flag_value); - if (flag_name == "rpg2k3-cmds" || flag_name == "rpg2k3-commands") - Player::game_config.patch_rpg2k3_commands.Set(flag_value); - if (flag_name == "rpg2k-battle") - lcf::Data::system.easyrpg_use_rpg2k_battle_system = flag_value; + switch (flag_id) { + case 1: + if (flag_value) { + _state.easyrpg_runtime_flags.patch_destiny_on = true; + } else { + _state.easyrpg_runtime_flags.patch_destiny_off = true; + } + break; + case 2: + if (flag_value) { + _state.easyrpg_runtime_flags.patch_dynrpg_on = true; + } else { + _state.easyrpg_runtime_flags.patch_dynrpg_off = true; + } + break; + case 3: + if (flag_value) { + _state.easyrpg_runtime_flags.patch_maniac_on = true; + } else { + _state.easyrpg_runtime_flags.patch_maniac_off = true; + } + break; + case 4: + if (flag_value) { + _state.easyrpg_runtime_flags.patch_common_this_event_on = true; + } else { + _state.easyrpg_runtime_flags.patch_common_this_event_off = true; + } + break; + case 5: + if (flag_value) { + _state.easyrpg_runtime_flags.patch_unlock_pics_on = true; + } else { + _state.easyrpg_runtime_flags.patch_unlock_pics_off = true; + } + break; + case 6: + if (flag_value) { + _state.easyrpg_runtime_flags.patch_keypatch_on = true; + } else { + _state.easyrpg_runtime_flags.patch_keypatch_off = true; + } + break; + case 7: + if (flag_value) { + _state.easyrpg_runtime_flags.patch_rpg2k3_cmds_on = true; + } else { + _state.easyrpg_runtime_flags.patch_rpg2k3_cmds_off = true; + } + break; + case 8: + if (flag_value) { + _state.easyrpg_runtime_flags.use_rpg2k_battle_system_on = true; + } else { + _state.easyrpg_runtime_flags.use_rpg2k_battle_system_off = true; + } + break; + default: + return true; + } + _state.easyrpg_runtime_flags.conf_override_active = true; +#endif return true; } diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 8f5b40af34..720b7b736d 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -85,7 +85,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext * Returns the interpreters current state information. * For saving state into a save file, use GetSaveState instead. */ - const lcf::rpg::SaveEventExecState& GetState() const; + const lcf::rpg::SaveEventExecState& GetState() const override; /** * Returns a SaveEventExecState needed for the savefile. @@ -344,6 +344,10 @@ class Game_Interpreter : public Game_BaseInterpreterContext int ManiacBitmask(int value, int mask) const; +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + void ClearStateRuntimeFlags(); +#endif + lcf::rpg::SaveEventExecState _state; KeyInputState _keyinput; AsyncOp _async_op = {}; @@ -398,4 +402,11 @@ inline AsyncOp Game_Interpreter::GetAsyncOp() const { return _async_op; } +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG +inline void Game_Interpreter::ClearStateRuntimeFlags() { + _state.easyrpg_runtime_flags.conf_override_active = false; + _state.easyrpg_runtime_flags.flags.fill(false); +} +#endif + #endif diff --git a/src/game_interpreter_shared.cpp b/src/game_interpreter_shared.cpp index ee923ada5b..3b2ea92732 100644 --- a/src/game_interpreter_shared.cpp +++ b/src/game_interpreter_shared.cpp @@ -238,6 +238,24 @@ lcf::rpg::MoveCommand Game_Interpreter_Shared::DecodeMove(lcf::DBArray: return cmd; } +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + +std::optional Game_Interpreter_Shared::GetRuntimeFlag(lcf::rpg::SaveEventExecState const& state, StateRuntimeFlagRef const field_on, StateRuntimeFlagRef const field_off) { + return GetRuntimeFlag(state.easyrpg_runtime_flags, field_on, field_off); +} + +std::optional Game_Interpreter_Shared::GetRuntimeFlag(lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags const& state_runtime_flags, StateRuntimeFlagRef const field_on, StateRuntimeFlagRef const field_off) { + if (state_runtime_flags.conf_override_active) { + if (state_runtime_flags.*field_on) + return true; + if (state_runtime_flags.*field_off) + return false; + } + return std::nullopt; +} + +#endif + //explicit declarations for target evaluation logic shared between ControlSwitches/ControlVariables/ControlStrings template bool Game_Interpreter_Shared::DecodeTargetEvaluationMode(lcf::rpg::EventCommand const&, int&, int&, Game_BaseInterpreterContext const&); template bool Game_Interpreter_Shared::DecodeTargetEvaluationMode(lcf::rpg::EventCommand const&, int&, int&, Game_BaseInterpreterContext const&); diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 6cea473a31..d424c805f0 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -21,9 +21,13 @@ #include #include +#include #include #include #include "compiler.h" +#include + +#define ENABLE_DYNAMIC_INTERPRETER_CONFIG class Game_Character; class Game_BaseInterpreterContext; @@ -103,6 +107,14 @@ namespace Game_Interpreter_Shared { lcf::rpg::MoveCommand DecodeMove(lcf::DBArray::const_iterator& it); bool ManiacCheckContinueLoop(int val, int val2, int type, int op); + +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + typedef bool lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags::* StateRuntimeFlagRef; + + std::optional GetRuntimeFlag(lcf::rpg::SaveEventExecState const& state, StateRuntimeFlagRef const field_on, StateRuntimeFlagRef const field_off); + + std::optional GetRuntimeFlag(lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags const& state_runtime_flags, StateRuntimeFlagRef const field_on, StateRuntimeFlagRef const field_off); +#endif } inline bool Game_Interpreter_Shared::CheckOperator(int val, int val2, int op) { @@ -147,8 +159,16 @@ class Game_BaseInterpreterContext { virtual int GetThisEventId() const = 0; virtual Game_Character* GetCharacter(int event_id, std::string_view origin) const = 0; + + virtual const lcf::rpg::SaveEventExecState& GetState() const = 0; virtual const lcf::rpg::SaveEventExecFrame& GetFrame() const = 0; +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + inline std::optional GetRuntimeFlag(Game_Interpreter_Shared::StateRuntimeFlagRef const field_on, Game_Interpreter_Shared::StateRuntimeFlagRef const field_off) const { + return Game_Interpreter_Shared::GetRuntimeFlag(GetState(), field_on, field_off); + }; +#endif + protected: template inline bool DecodeTargetEvaluationMode(lcf::rpg::EventCommand const& com, int& id_0, int& id_1) const { diff --git a/src/player.cpp b/src/player.cpp index 47825e8411..ad2c2dfb0e 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -136,6 +136,7 @@ namespace Player { int rng_seed = -1; Game_ConfigPlayer player_config; Game_ConfigGame game_config; + lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags* active_interpreter_flags = nullptr; #ifdef EMSCRIPTEN std::string emscripten_game_name; #endif diff --git a/src/player.h b/src/player.h index 13575c7738..8f01ee715c 100644 --- a/src/player.h +++ b/src/player.h @@ -25,9 +25,11 @@ #include "game_clock.h" #include "game_config.h" #include "game_config_game.h" +#include "game_interpreter_shared.h" #include #include #include +#include /** * Player namespace. @@ -297,6 +299,10 @@ namespace Player { */ bool IsPatchDestiny(); + bool IsPatchCommonThisEvent(); + + bool IsPatchUnlockPics(); + /** * @return True when EasyRpg extensions are on */ @@ -435,6 +441,12 @@ namespace Player { /** Name of game emscripten uses */ extern std::string emscripten_game_name; #endif + +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + extern lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags* active_interpreter_flags; + + std::optional GetRuntimeFlag(Game_Interpreter_Shared::StateRuntimeFlagRef field_on, Game_Interpreter_Shared::StateRuntimeFlagRef field_off); +#endif } inline bool Player::IsRPG2k() { @@ -482,31 +494,89 @@ inline bool Player::IsRPG2k3E() { } inline bool Player::IsRPG2k3Commands() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_rpg2k3_cmds_on, &Flags::patch_rpg2k3_cmds_off)) + return *f; +#endif return (IsRPG2k3() || game_config.patch_rpg2k3_commands.Get()); } inline bool Player::IsRPG2k3ECommands() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_rpg2k3_cmds_on, &Flags::patch_rpg2k3_cmds_off)) + return *f; +#endif return (IsRPG2k3E() || game_config.patch_rpg2k3_commands.Get()); } inline bool Player::IsPatchDynRpg() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_dynrpg_on, &Flags::patch_dynrpg_off)) + return *f; +#endif return game_config.patch_dynrpg.Get(); } inline bool Player::IsPatchManiac() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_maniac_on, &Flags::patch_maniac_off)) + return *f; +#endif return game_config.patch_maniac.Get() > 0; } inline bool Player::IsPatchKeyPatch() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_keypatch_on, &Flags::patch_keypatch_off)) + return *f; +#endif return game_config.patch_key_patch.Get(); } inline bool Player::IsPatchDestiny() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_destiny_on, &Flags::patch_destiny_off)) + return *f; +#endif return game_config.patch_destiny.Get(); } +inline bool Player::IsPatchCommonThisEvent() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_common_this_event_on, &Flags::patch_common_this_event_off)) + return *f; +#endif + return game_config.patch_common_this_event.Get(); +} + +inline bool Player::IsPatchUnlockPics() { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + if (auto f = GetRuntimeFlag(&Flags::patch_unlock_pics_on, &Flags::patch_unlock_pics_off)) + return *f; +#endif + return game_config.patch_unlock_pics.Get(); +} + inline bool Player::HasEasyRpgExtensions() { return game_config.patch_easyrpg.Get(); } +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + +inline std::optional Player::GetRuntimeFlag(Game_Interpreter_Shared::StateRuntimeFlagRef field_on, Game_Interpreter_Shared::StateRuntimeFlagRef field_off) { + if (active_interpreter_flags) { + return Game_Interpreter_Shared::GetRuntimeFlag(*active_interpreter_flags, field_on, field_off); + } + return std::nullopt; +} +#endif + #endif diff --git a/src/scene_debug.cpp b/src/scene_debug.cpp index 742401d00f..7a6dd70cde 100644 --- a/src/scene_debug.cpp +++ b/src/scene_debug.cpp @@ -622,7 +622,12 @@ void Scene_Debug::vUpdate() { } break; case eInterpreter: - if (sz == 2) { + if (sz == 3) { + auto action = interpreter_window->GetSelectedAction(); + if (action == Window_Interpreter::UiAction::ShowStackItem) { + /* */ + } + } else if (sz == 2) { PushUiInterpreterView(); } else if (sz == 1) { if (!interpreter_states_cached) { diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index 1c59b8a9c5..6b87cd8093 100644 --- a/src/window_interpreter.cpp +++ b/src/window_interpreter.cpp @@ -15,7 +15,7 @@ * along with EasyRPG Player. If not, see . */ -// Headers + // Headers #include #include "window_interpreter.h" #include "bitmap.h" @@ -26,10 +26,55 @@ #include "input.h" #include "player.h" #include "lcf/reader_util.h" +#include +#include "feature.h" + +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG +namespace { + bool GlobalIsRPG2k3Commands() { return Player::game_config.patch_rpg2k3_commands.Get(); } + bool GlobalIsPatchDynRpg() { return Player::game_config.patch_dynrpg.Get(); } + bool GlobalIsPatchManiac() { return Player::game_config.patch_maniac.Get(); } + bool GlobalIsPatchKeyPatch() { return Player::game_config.patch_key_patch.Get(); } + bool GlobalIsPatchDestiny() { return Player::game_config.patch_destiny.Get(); } + bool GlobalIsPatchCommonThisEvent() { return Player::game_config.patch_common_this_event.Get(); } + bool GlobalIsPatchUnlockPics() { return Player::game_config.patch_unlock_pics.Get(); } + bool GlobalHasRpg2kBattleSystem() { return lcf::Data::system.easyrpg_use_rpg2k_battle_system; } +} + +struct RuntimeFlagInfo { + bool (*config_fn)(); + Game_Interpreter_Shared::StateRuntimeFlagRef field_on, field_off; + char* name; +}; + +using StateFlags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; +constexpr std::array runtime_flags = { { + { &GlobalIsRPG2k3Commands, &StateFlags::patch_rpg2k3_cmds_on, &StateFlags::patch_rpg2k3_cmds_off, "rpg2k3_cmds"}, + { &GlobalIsPatchDynRpg, &StateFlags::patch_dynrpg_on, &StateFlags::patch_dynrpg_off, "dynrpg" }, + { &GlobalIsPatchManiac, &StateFlags::patch_maniac_on, &StateFlags::patch_maniac_off, "maniac" }, + { &GlobalIsPatchKeyPatch, &StateFlags::patch_keypatch_on, &StateFlags::patch_keypatch_off, "keypatch" }, + { &GlobalIsPatchDestiny, &StateFlags::patch_destiny_on, &StateFlags::patch_destiny_off, "destiny" }, + { &GlobalIsPatchCommonThisEvent, &StateFlags::patch_common_this_event_on, &StateFlags::patch_common_this_event_off, "common_this_event" }, + { &GlobalIsPatchUnlockPics, &StateFlags::patch_unlock_pics_on, &StateFlags::patch_unlock_pics_off, "unlock_pics" }, + { &GlobalHasRpg2kBattleSystem, &StateFlags::use_rpg2k_battle_system_on, &StateFlags::use_rpg2k_battle_system_off, "rpg2k_battle_system" } +} }; +#endif Window_Interpreter::Window_Interpreter(int ix, int iy, int iwidth, int iheight) : Window_Selectable(ix, iy, iwidth, iheight) { column_max = 1; + + sub_actions = UiSubActionLine( + { UiAction::ShowRuntimeFlags, UiAction::ShowMovementInfo }, + { "[Flags]", "WAITING for EV movement" }, + { Font::SystemColor::ColorHeal, Font::SystemColor::ColorCritical }, + { [&] { return (this->state.easyrpg_runtime_flags.conf_override_active); }, [&] { return this->state.wait_movement; } } + ); + + sub_window_flags.reset(new Window_Selectable(Player::menu_offset_x + 15, Player::menu_offset_y + 16, 288, 208)); + sub_window_flags->SetVisible(false); + sub_window_flags->SetActive(false); + sub_window_flags->SetIndex(-1); } Window_Interpreter::~Window_Interpreter() { @@ -42,6 +87,14 @@ void Window_Interpreter::SetStackState(bool is_ce, int owner_evt_id, std::string } void Window_Interpreter::Refresh() { + + if (sub_window_flags->GetActive()) { +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + DrawRuntimeFlagsWindow(); +#endif + return; + } + stack_display_items.clear(); int max_cmd_count = 0, max_evt_id = 10, max_page_id = 0; @@ -111,7 +164,7 @@ void Window_Interpreter::Refresh() { item_max = stack_display_items.size() + lines_without_stack; lines_without_stack = lines_without_stack_fixed; - if (state.wait_movement) { + if (sub_actions.IsVisible()) { item_max++; lines_without_stack++; } @@ -143,7 +196,39 @@ bool Window_Interpreter::IsValid() { } void Window_Interpreter::Update() { + if (sub_window_flags->GetActive()) { + if (Input::IsTriggered(Input::InputButton::CANCEL)) { + sub_window_flags->SetActive(false); + sub_window_flags->SetVisible(false); + sub_window_flags->SetIndex(-1); + } else { + sub_window_flags->Update(); + } + return; + } Window_Selectable::Update(); + + if (IsHoveringSubActionLine()) { + sub_actions.Update(*this); + + if (Input::IsTriggered(Input::InputButton::DECISION)) { + if (GetSelectedAction() == UiAction::ShowRuntimeFlags) { + sub_window_flags->SetActive(true); + sub_window_flags->SetVisible(true); + this->Refresh(); + + Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision)); + } else if (GetSelectedAction() == UiAction::ShowMovementInfo) { + Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Buzzer)); + } + } + } else { + sub_actions.ClearIndex(); + } +} + +bool Window_Interpreter::IsHoveringSubActionLine() const { + return GetIndex() == 1 && sub_actions.IsVisible(); } void Window_Interpreter::DrawDescriptionLines() { @@ -153,10 +238,11 @@ void Window_Interpreter::DrawDescriptionLines() { contents->TextDraw(rect.x, rect.y, Font::ColorDefault, display_item.desc); - if (state.wait_movement) { - rect = GetItemRect(i++); - contents->ClearRect(rect); - contents->TextDraw(rect.x, rect.y, Font::ColorCritical, "[WAITING for EV movement!]"); + rect = GetItemRect(i++); + contents->ClearRect(rect); + + if (sub_actions.IsVisible()) { + sub_actions.Draw(contents, rect); } rect = GetItemRect(i++); @@ -194,9 +280,140 @@ void Window_Interpreter::DrawStackLine(int index) { contents->TextDraw(GetWidth() - 16, rect.y, Font::ColorDefault, fmt::format("{:0" + std::to_string(digits_cmdcount) + "d}/{:0" + std::to_string(digits_cmdcount) + "d}", item.cmd_current, item.cmd_count), Text::AlignRight); } -int Window_Interpreter::GetSelectedStackFrameLine() { +Window_Interpreter::UiAction Window_Interpreter::GetSelectedAction() const { + if (GetIndex() >= lines_without_stack) { + return UiAction::ShowStackItem; + } + if (GetIndex() == 1 && sub_actions.IsVisible()) { + return sub_actions.GetSelectedAction(); + } + return UiAction::None; +} + +int Window_Interpreter::GetSelectedStackFrameLine() const { if (GetIndex() < lines_without_stack) { return -1; } return state.stack.size() - (GetIndex() - lines_without_stack) - 1; } + +bool Window_Interpreter::UiSubActionLine::IsVisible() const { + for (auto v : this->visibility_delegates) { + if (v()) + return true; + } + return false; +} + +void Window_Interpreter::UiSubActionLine::Update(Window_Selectable& parent) { + if (actions.size() == 0 || !visibility_delegates[this->index]()) + return; + + int i = this->index; + + if (Input::IsRepeated(Input::RIGHT)) { + i++; + + while (i != this->index) { + if (i >= actions.size()) + i = 0; + if (visibility_delegates[i]()) + break; + i++; + } + } + if (Input::IsRepeated(Input::LEFT)) { + i--; + while (i != this->index) { + if (i < 0) + i = actions.size() - 1; + if (visibility_delegates[i]()) + break; + i--; + } + } + + if (i != this->index) { + this->index = i; + Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor)); + } + int offset_x = 0; + for (i = 0; i < this->index; i++) { + offset_x += (this->texts[i].length() + 1) * 6; + } + auto cursor_rect = parent.GetCursorRect(); + parent.SetCursorRect(Rect(cursor_rect.x + offset_x, cursor_rect.y, (this->texts[this->index].length() + 1) * 6 + 2, cursor_rect.height)); +} + +namespace { + void TextDrawUnderlined(BitmapRef contents, int x, int y, int color, std::string_view text) { + auto sys = Cache::SystemOrBlack(); + auto rect = Rect(x, y + 12, text.length() * 6, 1); + // Draw shadow first + contents->FillRect(Rect(rect.x + 1, rect.y + 1, rect.width, rect.height), sys->GetColorAt(18, 34)); + + //Draw the actual text + contents->TextDraw(x, y, color, text); + + // Draw underline + contents->FillRect(rect, sys->GetColorAt(color % 10 * 16 + 2, color / 10 * 16 + 48 + 15)); + } +} + +void Window_Interpreter::UiSubActionLine::Draw(BitmapRef contents, Rect rect) const { + int offset_x = 0; + + for (int i = 0; i < this->actions.size(); i++) { + if (!visibility_delegates[i]()) + continue; + TextDrawUnderlined(contents, rect.x + offset_x, rect.y, colors[i], texts[i]); + offset_x += (texts[i].length() + 1) * 6; + } +} + +void Window_Interpreter::UiSubActionLine::ClearIndex() { + this->index = 0; +} + +Window_Interpreter::UiAction Window_Interpreter::UiSubActionLine::GetSelectedAction() const { + return this->actions[this->index]; +} + +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG +void Window_Interpreter::DrawRuntimeFlagsWindow() const { + sub_window_flags->CreateContents(); + auto contents = sub_window_flags->GetContents(); + Rect rect = sub_window_flags->GetItemRect(0); + + + contents->TextDraw(rect.x, rect.y, Font::ColorHeal, "Interpreter Flags:"); + contents->TextDraw(rect.x + 19 * 6, rect.y, Font::ColorDefault, display_item.desc); + + int i = 0; + for (auto info : runtime_flags) { + using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; + auto flag = Game_Interpreter_Shared::GetRuntimeFlag(state, info.field_on, info.field_off);; + if (!flag) { + continue; + } + + rect = sub_window_flags->GetItemRect(i + 1); + contents->ClearRect(rect); + + bool is_active = info.config_fn(); + bool is_overridden = *flag; + contents->TextDraw(rect.x, rect.y, Font::ColorDefault, fmt::format("{}:", info.name)); + + int r = 16; + contents->TextDraw(sub_window_flags->GetWidth() - r, rect.y, Font::ColorCritical, is_overridden ? "[ON]" : "[OFF]", Text::AlignRight); + r += 3 * 8; + if (!is_overridden) + r += 8; + contents->TextDraw(sub_window_flags->GetWidth() - r, rect.y, Font::ColorDefault, "->", Text::AlignRight); + r += 16; + contents->TextDraw(sub_window_flags->GetWidth() - r, rect.y, Font::ColorDisabled, is_active ? "[ON]" : "[OFF]", Text::AlignRight); + + ++i; + } +} +#endif diff --git a/src/window_interpreter.h b/src/window_interpreter.h index 69fd1b0877..0d0244e803 100644 --- a/src/window_interpreter.h +++ b/src/window_interpreter.h @@ -18,13 +18,55 @@ #ifndef EP_WINDOW_INTERPRETER_H #define EP_WINDOW_INTERPRETER_H -// Headers + // Headers #include "window_command.h" +#include "game_interpreter_shared.h" #include "lcf/rpg/saveeventexecstate.h" #include "lcf/rpg/saveeventexecframe.h" class Window_Interpreter : public Window_Selectable { public: + enum UiAction { + None = 0, + ShowRuntimeFlags = 1, + ShowMovementInfo, + ShowStackItem + }; + + class UiSubActionLine { + public: + UiSubActionLine() { + + } + + UiSubActionLine(std::initializer_list actions, std::initializer_list texts, + std::initializer_list colors, std::initializer_list> visibility_delegates) { + + assert(actions.size() == texts.size()); + assert(actions.size() == colors.size()); + assert(actions.size() == visibility_delegates.size()); + + this->actions = actions; + this->texts = texts; + this->colors = colors; + this->visibility_delegates = visibility_delegates; + } + + bool IsVisible() const; + void Update(Window_Selectable& parent); + void Draw(BitmapRef contents, Rect rect) const; + void ClearIndex(); + UiAction GetSelectedAction() const; + + private: + int index = 0; + + std::vector actions; + std::vector texts; + std::vector colors; + std::vector> visibility_delegates; + }; + Window_Interpreter(int ix, int iy, int iwidth, int iheight); ~Window_Interpreter() override; @@ -34,14 +76,21 @@ class Window_Interpreter : public Window_Selectable { void Refresh(); bool IsValid(); - int GetSelectedStackFrameLine(); + UiAction GetSelectedAction() const; + int GetSelectedStackFrameLine() const; + protected: + bool IsHoveringSubActionLine() const; void DrawDescriptionLines(); void DrawStackLine(int index); + +#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG + void DrawRuntimeFlagsWindow() const; +#endif private: struct InterpDisplayItem { - bool is_ce; - int owner_evt_id; + bool is_ce = false; + int owner_evt_id = 0; std::string desc; }; @@ -53,7 +102,7 @@ class Window_Interpreter : public Window_Selectable { }; const int lines_without_stack_fixed = 3; - + lcf::rpg::SaveEventExecState state; int lines_without_stack = 0; @@ -61,6 +110,9 @@ class Window_Interpreter : public Window_Selectable { InterpDisplayItem display_item; std::vector stack_display_items; + + UiSubActionLine sub_actions; + std::unique_ptr sub_window_flags; }; #endif From 599de3aa9f13ef4e7c4be9442e07076c2f540e15 Mon Sep 17 00:00:00 2001 From: florianessl Date: Sat, 22 Mar 2025 14:15:59 +0100 Subject: [PATCH 2/6] Fix: Destiny script code would never be executed if Player::HasEasyRpgExtensions() evaluates to true -> Separated script handling for Dyn/Easy & Destiny --- src/game_interpreter.cpp | 21 ++++++++++++++++----- src/game_interpreter.h | 3 +++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 47c137fca5..49541502c6 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -2019,17 +2019,17 @@ bool Game_Interpreter::CommandEndEventProcessing(lcf::rpg::EventCommand const& / return true; } -bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) { +std::optional Game_Interpreter::HandleDynRpgScript(const lcf::rpg::EventCommand& com) { if (Player::IsPatchDynRpg() || Player::HasEasyRpgExtensions()) { if (com.string.empty() || com.string[0] != '@') { // Not a DynRPG command - return true; + return std::nullopt; } if (!Player::IsPatchDynRpg() && Player::HasEasyRpgExtensions()) { // Only accept commands starting with @easyrpg_ if (!StartsWith(com.string, "@easyrpg_")) { - return true; + return std::nullopt; } } @@ -2038,6 +2038,7 @@ bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) { auto& index = frame.current_command; std::string command = ToString(com.string); + // Concat everything that is not another command or a new comment block for (size_t i = index + 1; i < list.size(); ++i) { const auto& cmd = list[i]; @@ -2051,17 +2052,27 @@ bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) { return Main_Data::game_dynrpg->Invoke(command, this); } +} - +std::optional Game_Interpreter::HandleDestinyScript(const lcf::rpg::EventCommand& com) { // DestinyScript if (Player::IsPatchDestiny()) { if (com.string.empty() || com.string[0] != '$') { // Not a DestinyScript - return true; + return std::nullopt; } return Main_Data::game_destiny->Main(GetFrame()); } +} + +bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) { + if (auto handled = HandleDynRpgScript(com); handled.has_value()) { + return handled.value(); + } + if (auto handled = HandleDestinyScript(com); handled.has_value()) { + return handled.value(); + } return true; } diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 720b7b736d..5fd57b44f2 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -310,6 +310,9 @@ class Game_Interpreter : public Game_BaseInterpreterContext void ForegroundTextPush(PendingMessage pm); void EndEventProcessing(); + std::optional HandleDynRpgScript(const lcf::rpg::EventCommand& com); + std::optional HandleDestinyScript(const lcf::rpg::EventCommand& com); + FileRequestBinding request_id; enum class Keys { eDown, From 0745b49bd48210a4b5faf7b54f067de386b889c6 Mon Sep 17 00:00:00 2001 From: florianessl Date: Mon, 24 Mar 2025 16:19:35 +0100 Subject: [PATCH 3/6] RuntimeFlags: Refactored some bits that came up in code review --- src/game_interpreter.cpp | 15 +++++++++++---- src/game_interpreter.h | 11 ----------- src/game_interpreter_shared.h | 2 +- src/window_interpreter.cpp | 2 +- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 49541502c6..672932128b 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -384,6 +384,9 @@ void Game_Interpreter::Update(bool reset_loop_count) { #ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG Player::active_interpreter_flags = &_state.easyrpg_runtime_flags; + auto flags_guard = lcf::makeScopeGuard([]() { + Player::active_interpreter_flags = nullptr; + }); #endif for (; loop_count < loop_limit; ++loop_count) { @@ -530,9 +533,6 @@ void Game_Interpreter::Update(bool reset_loop_count) { if (Game_Map::GetNeedRefresh()) { Game_Map::Refresh(); } -#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG - Player::active_interpreter_flags = nullptr; -#endif } // Setup Starting Event @@ -838,7 +838,9 @@ bool Game_Interpreter::OnFinishStackFrame() { if (is_base_frame) { #ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG - ClearStateRuntimeFlags(); + // Individual runtime flags that may still be set will be cleared by + // CommandEasyRpgSetInterpreterFlag if neccessary + _state.easyrpg_runtime_flags.conf_override_active = false; #endif } @@ -5416,6 +5418,11 @@ bool Game_Interpreter::CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand c } } + // Clear any inactive flags that might be left over from a previous InterpreterFlag command + if (!_state.easyrpg_runtime_flags.conf_override_active) { + _state.easyrpg_runtime_flags.flags.fill(false); + } + switch (flag_id) { case 1: if (flag_value) { diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 5fd57b44f2..f7572b92f0 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -347,10 +347,6 @@ class Game_Interpreter : public Game_BaseInterpreterContext int ManiacBitmask(int value, int mask) const; -#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG - void ClearStateRuntimeFlags(); -#endif - lcf::rpg::SaveEventExecState _state; KeyInputState _keyinput; AsyncOp _async_op = {}; @@ -405,11 +401,4 @@ inline AsyncOp Game_Interpreter::GetAsyncOp() const { return _async_op; } -#ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG -inline void Game_Interpreter::ClearStateRuntimeFlags() { - _state.easyrpg_runtime_flags.conf_override_active = false; - _state.easyrpg_runtime_flags.flags.fill(false); -} -#endif - #endif diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index d424c805f0..15bad41e5d 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -109,7 +109,7 @@ namespace Game_Interpreter_Shared { bool ManiacCheckContinueLoop(int val, int val2, int type, int op); #ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG - typedef bool lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags::* StateRuntimeFlagRef; + using StateRuntimeFlagRef = bool lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags::*; std::optional GetRuntimeFlag(lcf::rpg::SaveEventExecState const& state, StateRuntimeFlagRef const field_on, StateRuntimeFlagRef const field_off); diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index 6b87cd8093..5addbaae43 100644 --- a/src/window_interpreter.cpp +++ b/src/window_interpreter.cpp @@ -44,7 +44,7 @@ namespace { struct RuntimeFlagInfo { bool (*config_fn)(); Game_Interpreter_Shared::StateRuntimeFlagRef field_on, field_off; - char* name; + std::string_view name; }; using StateFlags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; From 5ab3547fdec45aec0c8db198387354ad9a7479e7 Mon Sep 17 00:00:00 2001 From: florianessl Date: Tue, 29 Apr 2025 18:19:09 +0200 Subject: [PATCH 4/6] Fix compiler warning --- src/game_interpreter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 672932128b..9131f3f70c 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -2066,6 +2066,7 @@ std::optional Game_Interpreter::HandleDestinyScript(const lcf::rpg::EventC return Main_Data::game_destiny->Main(GetFrame()); } + return std::nullopt; } bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) { From 118e5085050a85c36420965902f943b233bcda82 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 15 May 2025 15:09:52 +0200 Subject: [PATCH 5/6] GetRuntimeFlag: Put the "conf_override_active" check in the header so it can be inlined to avoid a function call. --- src/game_interpreter.cpp | 2 +- src/player.cpp | 1 - src/player.h | 7 ++++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 9131f3f70c..b0b273acde 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -385,7 +385,7 @@ void Game_Interpreter::Update(bool reset_loop_count) { #ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG Player::active_interpreter_flags = &_state.easyrpg_runtime_flags; auto flags_guard = lcf::makeScopeGuard([]() { - Player::active_interpreter_flags = nullptr; + Player::active_interpreter_flags = &Player::interpreter_default_flags; }); #endif diff --git a/src/player.cpp b/src/player.cpp index ad2c2dfb0e..47825e8411 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -136,7 +136,6 @@ namespace Player { int rng_seed = -1; Game_ConfigPlayer player_config; Game_ConfigGame game_config; - lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags* active_interpreter_flags = nullptr; #ifdef EMSCRIPTEN std::string emscripten_game_name; #endif diff --git a/src/player.h b/src/player.h index 8f01ee715c..ba3eb00077 100644 --- a/src/player.h +++ b/src/player.h @@ -443,7 +443,8 @@ namespace Player { #endif #ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG - extern lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags* active_interpreter_flags; + inline lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags interpreter_default_flags{}; + inline lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags* active_interpreter_flags = &interpreter_default_flags; std::optional GetRuntimeFlag(Game_Interpreter_Shared::StateRuntimeFlagRef field_on, Game_Interpreter_Shared::StateRuntimeFlagRef field_off); #endif @@ -570,9 +571,9 @@ inline bool Player::HasEasyRpgExtensions() { } #ifdef ENABLE_DYNAMIC_INTERPRETER_CONFIG - inline std::optional Player::GetRuntimeFlag(Game_Interpreter_Shared::StateRuntimeFlagRef field_on, Game_Interpreter_Shared::StateRuntimeFlagRef field_off) { - if (active_interpreter_flags) { + assert(active_interpreter_flags); + if (active_interpreter_flags->conf_override_active) { return Game_Interpreter_Shared::GetRuntimeFlag(*active_interpreter_flags, field_on, field_off); } return std::nullopt; From d0ebf24d4b7069b3cb55438c6601422fa02f82b1 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Thu, 15 May 2025 15:10:02 +0200 Subject: [PATCH 6/6] Fix warnings --- src/game_interpreter.cpp | 6 ++++-- src/window_interpreter.cpp | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index b0b273acde..0594e5b596 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -2054,6 +2054,8 @@ std::optional Game_Interpreter::HandleDynRpgScript(const lcf::rpg::EventCo return Main_Data::game_dynrpg->Invoke(command, this); } + + return {}; } std::optional Game_Interpreter::HandleDestinyScript(const lcf::rpg::EventCommand& com) { @@ -2061,12 +2063,12 @@ std::optional Game_Interpreter::HandleDestinyScript(const lcf::rpg::EventC if (Player::IsPatchDestiny()) { if (com.string.empty() || com.string[0] != '$') { // Not a DestinyScript - return std::nullopt; + return {}; } return Main_Data::game_destiny->Main(GetFrame()); } - return std::nullopt; + return {}; } bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) { diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index 5addbaae43..8070a5cf88 100644 --- a/src/window_interpreter.cpp +++ b/src/window_interpreter.cpp @@ -315,7 +315,7 @@ void Window_Interpreter::UiSubActionLine::Update(Window_Selectable& parent) { i++; while (i != this->index) { - if (i >= actions.size()) + if (i >= static_cast(actions.size())) i = 0; if (visibility_delegates[i]()) break; @@ -363,7 +363,7 @@ namespace { void Window_Interpreter::UiSubActionLine::Draw(BitmapRef contents, Rect rect) const { int offset_x = 0; - for (int i = 0; i < this->actions.size(); i++) { + for (int i = 0; i < static_cast(this->actions.size()); i++) { if (!visibility_delegates[i]()) continue; TextDrawUnderlined(contents, rect.x + offset_x, rect.y, colors[i], texts[i]); @@ -391,7 +391,6 @@ void Window_Interpreter::DrawRuntimeFlagsWindow() const { int i = 0; for (auto info : runtime_flags) { - using Flags = lcf::rpg::SaveEventExecState::EasyRpgStateRuntime_Flags; auto flag = Game_Interpreter_Shared::GetRuntimeFlag(state, info.field_on, info.field_off);; if (!flag) { continue;