From dbd4a70c9ced1d233c33416488db91e1ab2a96ca Mon Sep 17 00:00:00 2001 From: florianessl Date: Wed, 5 Jun 2024 16:06:00 +0200 Subject: [PATCH 01/10] Cherry Pick commit #ec84539 from experimental branch "florianessl:InterpreterDebug": - Added new file "game_interpreter_debug.cpp" & moved/refactored some of the logic from the interpreter debug scene there --- CMakeLists.txt | 2 + Makefile.am | 2 + src/game_commonevent.h | 2 + src/game_event.h | 2 + src/game_interpreter_debug.cpp | 137 +++++++++++++++++++++++++++++++++ src/game_interpreter_debug.h | 71 +++++++++++++++++ src/scene_debug.cpp | 74 +++++++----------- src/scene_debug.h | 7 +- src/window_interpreter.cpp | 85 ++++++-------------- src/window_interpreter.h | 9 +-- 10 files changed, 272 insertions(+), 119 deletions(-) create mode 100644 src/game_interpreter_debug.cpp create mode 100644 src/game_interpreter_debug.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 87859f18f0..4c21d1a77c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,8 @@ add_library(${PROJECT_NAME} OBJECT src/game_interpreter_battle.h src/game_interpreter_control_variables.cpp src/game_interpreter_control_variables.h + src/game_interpreter_debug.cpp + src/game_interpreter_debug.h src/game_interpreter.cpp src/game_interpreter.h src/game_interpreter_map.cpp diff --git a/Makefile.am b/Makefile.am index 02992d0f91..902bd3d5e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -160,6 +160,8 @@ libeasyrpg_player_a_SOURCES = \ src/game_interpreter_battle.h \ src/game_interpreter_control_variables.cpp \ src/game_interpreter_control_variables.h \ + src/game_interpreter_debug.cpp \ + src/game_interpreter_debug.h \ src/game_interpreter_map.cpp \ src/game_interpreter_map.h \ src/game_interpreter_shared.cpp \ diff --git a/src/game_commonevent.h b/src/game_commonevent.h index f8069a3a0d..bf60830e7c 100644 --- a/src/game_commonevent.h +++ b/src/game_commonevent.h @@ -21,6 +21,7 @@ // Headers #include #include +#include "game_interpreter_debug.h" #include "game_interpreter_map.h" #include #include @@ -121,6 +122,7 @@ class Game_CommonEvent { std::unique_ptr interpreter; friend class Scene_Debug; + friend class Debug::ParallelInterpreterStates; }; #endif diff --git a/src/game_event.h b/src/game_event.h index 94139a7a91..838c5b879e 100644 --- a/src/game_event.h +++ b/src/game_event.h @@ -24,6 +24,7 @@ #include "game_character.h" #include #include +#include "game_interpreter_debug.h" #include "game_interpreter_map.h" #include "async_op.h" @@ -219,6 +220,7 @@ class Game_Event : public Game_EventBase { std::unique_ptr interpreter; friend class Scene_Debug; + friend class Debug::ParallelInterpreterStates; }; inline int Game_Event::GetNumPages() const { diff --git a/src/game_interpreter_debug.cpp b/src/game_interpreter_debug.cpp new file mode 100644 index 0000000000..39dd528cfe --- /dev/null +++ b/src/game_interpreter_debug.cpp @@ -0,0 +1,137 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + +#include "game_interpreter_debug.h" +#include "game_interpreter.h" +#include "game_battle.h" +#include "game_map.h" +#include "main_data.h" +#include "game_variables.h" +#include "output.h" +#include + +Debug::ParallelInterpreterStates Debug::ParallelInterpreterStates::GetCachedStates() { + std::vector ev_ids; + std::vector ce_ids; + + std::vector state_ev; + std::vector state_ce; + + if (Game_Map::GetMapId() > 0) { + for (auto& ev : Game_Map::GetEvents()) { + if (ev.GetTrigger() != lcf::rpg::EventPage::Trigger_parallel || !ev.interpreter) + continue; + ev_ids.emplace_back(ev.GetId()); + state_ev.emplace_back(ev.interpreter->GetState()); + } + for (auto& ce : Game_Map::GetCommonEvents()) { + if (ce.IsWaitingBackgroundExecution(false)) { + ce_ids.emplace_back(ce.common_event_id); + state_ce.emplace_back(ce.interpreter->GetState()); + } + } + } else if (Game_Battle::IsBattleRunning() && Player::IsPatchManiac()) { + //FIXME: Not implemented: battle common events + } + + return { ev_ids, ce_ids, state_ev, state_ce }; +} + +std::vector Debug::CreateCallStack(const int owner_evt_id, const lcf::rpg::SaveEventExecState& state) { + std::vector items(state.stack.size()); + + for (int i = state.stack.size() - 1; i >= 0; i--) { + int evt_id = state.stack[i].event_id; + int page_id = 0; + if (state.stack[i].maniac_event_id > 0) { + evt_id = state.stack[i].maniac_event_id; + page_id = state.stack[i].maniac_event_page_id; + } + if (evt_id == 0 && i == 0) + evt_id = owner_evt_id; + + bool is_calling_ev_ce = false; + + //FIXME: There are some currently unimplemented SaveEventExecFrame fields introduced via the ManiacPatch which should be used to properly get event state information + if (evt_id == 0 && i > 0) { + auto& prev_frame = state.stack[i - 1]; + auto& com = prev_frame.commands[prev_frame.current_command - 1]; + if (com.code == 12330) { // CallEvent + if (com.parameters[0] == 0) { + is_calling_ev_ce = true; + evt_id = com.parameters[1]; + } else if (com.parameters[0] == 3 && Player::IsPatchManiac()) { + is_calling_ev_ce = true; + evt_id = Main_Data::game_variables->Get(com.parameters[1]); + } else if (com.parameters[0] == 4 && Player::IsPatchManiac()) { + is_calling_ev_ce = true; + evt_id = Main_Data::game_variables->GetIndirect(com.parameters[1]); + } + } + } + + auto item = Debug::CallStackItem(); + item.stack_item_no = i + 1; + item.is_ce = is_calling_ev_ce; + item.evt_id = evt_id; + item.page_id = page_id; + item.name = ""; + item.cmd_current = state.stack[i].current_command; + item.cmd_count = state.stack[i].commands.size(); + + if (item.is_ce) { + auto* ce = lcf::ReaderUtil::GetElement(lcf::Data::commonevents, item.evt_id); + if (ce) { + item.name = ToString(ce->name); + } + } else { + auto* ev = Game_Map::GetEvent(evt_id); + if (ev) { + //FIXME: map could have changed, but map_id isn't available + item.name = ToString(ev->GetName()); + } + } + + items.push_back(item); + } + + return items; +} + +std::string Debug::FormatEventName(Game_Character const& ch) { + switch (ch.GetType()) { + case Game_Character::Player: + return "Player"; + case Game_Character::Vehicle: + { + int type = static_cast(ch).GetVehicleType(); + assert(type > Game_Vehicle::None && type <= Game_Vehicle::Airship); + return Game_Vehicle::TypeNames[type]; + } + case Game_Character::Event: + { + auto& ev = static_cast(ch); + if (ev.GetName().empty()) { + return fmt::format("EV{:04d}", ev.GetId()); + } + return fmt::format("EV{:04d} '{}'", ev.GetId(), ev.GetName()); + } + default: + assert(false); + } + return ""; +} diff --git a/src/game_interpreter_debug.h b/src/game_interpreter_debug.h new file mode 100644 index 0000000000..b62b2ff803 --- /dev/null +++ b/src/game_interpreter_debug.h @@ -0,0 +1,71 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + + +#ifndef EP_GAME_INTERPRETER_DEBUG +#define EP_GAME_INTERPRETER_DEBUG + +#include "game_interpreter_shared.h" +#include "game_character.h" +#include +#include "player.h" + +class Game_CommonEvent; + +namespace Debug { + class ParallelInterpreterStates { + private: + std::vector ev_ids; + std::vector ce_ids; + + std::vector state_ev; + std::vector state_ce; + + ParallelInterpreterStates(std::vector ev_ids, std::vector ce_ids, + std::vector state_ev, std::vector state_ce) + : ev_ids(ev_ids), ce_ids(ce_ids), state_ev(state_ev), state_ce(state_ce) { } + public: + ParallelInterpreterStates() = default; + + inline int CountEventInterpreters() const { return ev_ids.size(); } + inline int CountCommonEventInterpreters() const { return ce_ids.size(); } + + inline int Count() { return ev_ids.size() + ce_ids.size(); } + + inline std::tuple GetEventInterpreter(int i) const { + return std::tie(ev_ids[i], state_ev[i]); + } + inline std::tuple GetCommonEventInterpreter(int i) const { + return std::tie(ce_ids[i], state_ce[i]); + } + + static ParallelInterpreterStates GetCachedStates(); + }; + + struct CallStackItem { + bool is_ce; + int evt_id, page_id; + std::string name; + int stack_item_no, cmd_current, cmd_count; + }; + + std::vector CreateCallStack(const int owner_evt_id, const lcf::rpg::SaveEventExecState& state); + + std::string FormatEventName(Game_Character const& ev); +} + +#endif diff --git a/src/scene_debug.cpp b/src/scene_debug.cpp index f172d53580..c41c0d835d 100644 --- a/src/scene_debug.cpp +++ b/src/scene_debug.cpp @@ -638,7 +638,7 @@ void Scene_Debug::vUpdate() { PushUiInterpreterView(); } else if (sz == 1) { if (!interpreter_states_cached) { - CacheBackgroundInterpreterStates(); + state_interpreter.background_states = Debug::ParallelInterpreterStates::GetCachedStates(); interpreter_states_cached = true; } PushUiRangeList(); @@ -819,6 +819,7 @@ void Scene_Debug::UpdateRangeListWindow() { // addItem(fmt::format("{}[{:03d}-{:03d}]", state_interpreter.show_frame_switches ? "FSw" : "FVr", st, st + 9)); // } //} else { + auto& bg_states = state_interpreter.background_states; int skip_items = range_page * 10; int count_items = 0; if (range_page == 0) { @@ -826,23 +827,23 @@ void Scene_Debug::UpdateRangeListWindow() { skip_items = 1; count_items = 1; } - for (int i = 0; i < static_cast(state_interpreter.ev.size()) && count_items < 10; i++) { + for (int i = 0; i < bg_states.CountEventInterpreters() && count_items < 10; i++) { if (skip_items > 0) { skip_items--; continue; } - int evt_id = state_interpreter.ev[i]; - addItem(fmt::format("{}EV{:04d}: {}", state_interpreter.state_ev[i].wait_movement ? "(W) " : "", evt_id, Game_Map::GetEvent(evt_id)->GetName())); + auto& [evt_id, state] = bg_states.GetEventInterpreter(i); + addItem(fmt::format("{}EV{:04d}: {}", state.wait_movement ? "(W) " : "", evt_id, Game_Map::GetEvent(evt_id)->GetName())); count_items++; } - for (int i = 0; i < static_cast(state_interpreter.ce.size()) && count_items < 10; i++) { + for (int i = 0; i < bg_states.CountCommonEventInterpreters() && count_items < 10; i++) { if (skip_items > 0) { skip_items--; continue; } - int ce_id = state_interpreter.ce[i]; + auto& [ce_id, state] = bg_states.GetCommonEventInterpreter(i); auto* ce = lcf::ReaderUtil::GetElement(lcf::Data::commonevents, ce_id); - addItem(fmt::format("{}CE{:04d}: {}", state_interpreter.state_ce[i].wait_movement ? "(W) " : "", ce_id, ce->name)); + addItem(fmt::format("{}CE{:04d}: {}", state.wait_movement ? "(W) " : "", ce_id, ce->name)); count_items++; } //} @@ -863,7 +864,7 @@ void Scene_Debug::UpdateDetailWindow() { // auto & interpreter_frame = GetSelectedInterpreterFrameFromUiState(); // var_window->SetInterpreterFrame(&interpreter_frame); // var_window->UpdateList(GetSelectedIndexFromRange()); - // + // // interpreter_window->SetVisible(false); // interpreter_window->SetActive(false); //} else { @@ -976,12 +977,12 @@ int Scene_Debug::GetLastPage() { case eInterpreter: //if (state_interpreter.show_frame_switches) { // auto & interpreter_frame = GetSelectedInterpreterFrameFromUiState(); - // return interpreter_frame.easyrpg_frame_switches.size(); + // return interpreter_frame.easyrpg_frame_switches.size(); //} else if (state_interpreter.show_frame_vars) { // auto & interpreter_frame = GetSelectedInterpreterFrameFromUiState(); - // return interpreter_frame.easyrpg_frame_variables.size(); + // return interpreter_frame.easyrpg_frame_variables.size(); //} else { - num_elements = 1 + state_interpreter.ev.size() + state_interpreter.ce.size(); + num_elements = 1 + state_interpreter.background_states.Count(); return (static_cast(num_elements) - 1) / 10; //} default: @@ -1213,48 +1214,28 @@ void Scene_Debug::UpdateArrows() { range_window->SetRightArrow(show_right_arrow && arrow_visible); } -void Scene_Debug::CacheBackgroundInterpreterStates() { - state_interpreter.ev.clear(); - state_interpreter.ce.clear(); - state_interpreter.state_ev.clear(); - state_interpreter.state_ce.clear(); - - if (Game_Map::GetMapId() > 0) { - for (auto& ev : Game_Map::GetEvents()) { - if (ev.GetTrigger() != lcf::rpg::EventPage::Trigger_parallel || !ev.interpreter) - continue; - state_interpreter.ev.emplace_back(ev.GetId()); - state_interpreter.state_ev.emplace_back(ev.interpreter->GetState()); - } - for (auto& ce : Game_Map::GetCommonEvents()) { - if (ce.IsWaitingBackgroundExecution(false)) { - state_interpreter.ce.emplace_back(ce.common_event_id); - state_interpreter.state_ce.emplace_back(ce.interpreter->GetState()); - } - } - } else if (Game_Battle::IsBattleRunning() && Player::IsPatchManiac()) { - //Not implemented: battle common events - } -} - void Scene_Debug::UpdateInterpreterWindow(int index) { lcf::rpg::SaveEventExecState state; std::string first_line = ""; bool valid = false; int evt_id = 0; + auto& bg_states = state_interpreter.background_states; + if (index == 1) { state = Game_Interpreter::GetForegroundInterpreter().GetState(); first_line = Game_Battle::IsBattleRunning() ? "Foreground (Battle)" : "Foreground (Map)"; valid = true; - } else if (index <= static_cast(state_interpreter.ev.size())) { - evt_id = state_interpreter.ev[index - 1]; - state = state_interpreter.state_ev[index - 1]; + } else if (index <= bg_states.CountEventInterpreters()) { + auto tuple = bg_states.GetEventInterpreter(index - 1); + evt_id = std::get<0>(tuple); + state = std::get<1>(tuple); first_line = fmt::format("EV{:04d}: {}", evt_id, Game_Map::GetEvent(evt_id)->GetName()); valid = true; - } else if ((index - state_interpreter.ev.size()) <= state_interpreter.ce.size()) { - int ce_id = state_interpreter.ce[index - state_interpreter.ev.size() - 1]; - state = state_interpreter.state_ce[index - state_interpreter.ev.size() - 1]; + } else if ((index - bg_states.CountEventInterpreters()) <= bg_states.CountCommonEventInterpreters()) { + auto tuple = bg_states.GetCommonEventInterpreter(index - bg_states.CountEventInterpreters() - 1); + int ce_id = std::get<0>(tuple); + state = std::get<1>(tuple); for (auto& ce : Game_Map::GetCommonEvents()) { if (ce.common_event_id == ce_id) { first_line = fmt::format("CE{:04d}: {}", ce_id, ce.GetName()); @@ -1267,7 +1248,7 @@ void Scene_Debug::UpdateInterpreterWindow(int index) { if (valid) { state_interpreter.selected_state = index; - interpreter_window->SetStackState(index > static_cast(state_interpreter.ev.size()), evt_id, first_line, state); + interpreter_window->SetStackState(index > bg_states.CountEventInterpreters(), evt_id, first_line, state); } else { state_interpreter.selected_state = -1; interpreter_window->SetStackState(0, 0, "", {}); @@ -1282,18 +1263,19 @@ lcf::rpg::SaveEventExecFrame& Scene_Debug::GetSelectedInterpreterFrameFromUiStat } int index = state_interpreter.selected_state; + const auto& bg_states = state_interpreter.background_states; if (index == 1) { auto& state = Game_Interpreter::GetForegroundInterpreter()._state; return state.stack[state_interpreter.selected_frame]; - } else if (index <= static_cast(state_interpreter.ev.size())) { - int evt_id = state_interpreter.ev[index - 1]; + } else if (index <= bg_states.CountEventInterpreters()) { + int evt_id = std::get<0>(bg_states.GetEventInterpreter(index - 1)); auto ev = Game_Map::GetEvent(evt_id); auto& state = ev->interpreter->_state; return state.stack[state_interpreter.selected_frame]; - } else if ((index - state_interpreter.ev.size()) <= state_interpreter.ce.size()) { - int ce_id = state_interpreter.ce[index - state_interpreter.ev.size() - 1]; + } else if ((index - bg_states.CountEventInterpreters()) <= bg_states.CountCommonEventInterpreters()) { + int ce_id = std::get<0>(bg_states.GetEventInterpreter(index - bg_states.CountEventInterpreters() - 1)); for (auto& ce : Game_Map::GetCommonEvents()) { if (ce.common_event_id == ce_id) { auto& state = ce.interpreter->_state; diff --git a/src/scene_debug.h b/src/scene_debug.h index 711bcc9097..f0e74ca0e2 100644 --- a/src/scene_debug.h +++ b/src/scene_debug.h @@ -20,6 +20,7 @@ // Headers #include +#include "game_interpreter_debug.h" #include "scene.h" #include "window_command.h" #include "window_numberinput.h" @@ -191,12 +192,8 @@ class Scene_Debug : public Scene { void UpdateInterpreterWindow(int index); lcf::rpg::SaveEventExecFrame& GetSelectedInterpreterFrameFromUiState() const; - void CacheBackgroundInterpreterStates(); struct { - std::vector ev; - std::vector ce; - std::vector state_ev; - std::vector state_ce; + Debug::ParallelInterpreterStates background_states; // Frame-scoped data types introduced in 'ScopedVars' branch // bool show_frame_switches = false; diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index 1c59b8a9c5..b716914641 100644 --- a/src/window_interpreter.cpp +++ b/src/window_interpreter.cpp @@ -27,6 +27,15 @@ #include "player.h" #include "lcf/reader_util.h" +namespace { + std::vector CreateEmptyLines(int c) { + std::vector vars; + for (int i = 0; i < c; i++) + vars.push_back(""); + return vars; + } +} + Window_Interpreter::Window_Interpreter(int ix, int iy, int iwidth, int iheight) : Window_Selectable(ix, iy, iwidth, iheight) { column_max = 1; @@ -46,66 +55,20 @@ void Window_Interpreter::Refresh() { int max_cmd_count = 0, max_evt_id = 10, max_page_id = 0; - for (int i = state.stack.size() - 1; i >= 0; i--) { - int evt_id = state.stack[i].event_id; - int page_id = 0; - if (state.stack[i].maniac_event_id > 0) { - evt_id = state.stack[i].maniac_event_id; - page_id = state.stack[i].maniac_event_page_id; - } - if (evt_id == 0 && i == 0) - evt_id = display_item.owner_evt_id; - - bool is_calling_ev_ce = false; - - //FIXME: There are some currently unimplemented SaveEventExecFrame fields introduced via the ManiacPatch which should be used to properly get event state information - if (evt_id == 0 && i > 0) { - auto& prev_frame = state.stack[i - 1]; - auto& com = prev_frame.commands[prev_frame.current_command - 1]; - if (com.code == 12330) { // CallEvent - if (com.parameters[0] == 0) { - is_calling_ev_ce = true; - evt_id = com.parameters[1]; - } else if (com.parameters[0] == 3 && Player::IsPatchManiac()) { - is_calling_ev_ce = true; - evt_id = Main_Data::game_variables->Get(com.parameters[1]); - } else if (com.parameters[0] == 4 && Player::IsPatchManiac()) { - is_calling_ev_ce = true; - evt_id = Main_Data::game_variables->GetIndirect(com.parameters[1]); - } - } - } - if (evt_id > max_evt_id) - max_evt_id = evt_id; - - if (page_id > max_page_id) - max_page_id = page_id; - - StackItem item = StackItem(); - item.is_ce = is_calling_ev_ce || (i == 0 && this->display_item.is_ce); - item.evt_id = evt_id; - item.page_id = page_id; - item.name = ""; - item.cmd_current = state.stack[i].current_command; - item.cmd_count = state.stack[i].commands.size(); - - if (item.is_ce) { - auto* ce = lcf::ReaderUtil::GetElement(lcf::Data::commonevents, item.evt_id); - if (ce) { - item.name = ToString(ce->name); - } - } else { - auto* ev = Game_Map::GetEvent(evt_id); - if (ev) { - //FIXME: map could have changed, but map_id isn't available - item.name = ToString(ev->GetName()); - } - } - - if (static_cast(state.stack[i].commands.size()) > max_cmd_count) - max_cmd_count = state.stack[i].commands.size(); - - stack_display_items.push_back(item); + stack_display_items = Debug::CreateCallStack(display_item.owner_evt_id, state); + if (stack_display_items.size() > 0 && this->display_item.is_ce) { + stack_display_items[0].is_ce = true; + } + + for (auto it = stack_display_items.begin(); it < stack_display_items.end(); ++it) { + auto& item = *it; + + if (item.evt_id > max_evt_id) + max_evt_id = item.evt_id; + if (item.page_id > max_page_id) + max_page_id = item.page_id; + if (item.cmd_count > max_cmd_count) + max_cmd_count = item.cmd_count; } item_max = stack_display_items.size() + lines_without_stack; @@ -171,7 +134,7 @@ void Window_Interpreter::DrawStackLine(int index) { Rect rect = GetItemRect(index + lines_without_stack); contents->ClearRect(rect); - StackItem& item = stack_display_items[index]; + Debug::CallStackItem& item = stack_display_items[index]; contents->TextDraw(rect.x, rect.y, Font::ColorDisabled, fmt::format("[{:0" + std::to_string(digits_stackitemno) + "d}]", state.stack.size() - index)); if (item.is_ce) { diff --git a/src/window_interpreter.h b/src/window_interpreter.h index 69fd1b0877..438aa557ff 100644 --- a/src/window_interpreter.h +++ b/src/window_interpreter.h @@ -19,6 +19,7 @@ #define EP_WINDOW_INTERPRETER_H // Headers +#include "game_interpreter_debug.h" #include "window_command.h" #include "lcf/rpg/saveeventexecstate.h" #include "lcf/rpg/saveeventexecframe.h" @@ -45,12 +46,6 @@ class Window_Interpreter : public Window_Selectable { std::string desc; }; - struct StackItem { - bool is_ce; - int evt_id, page_id; - std::string name; - int cmd_current, cmd_count; - }; const int lines_without_stack_fixed = 3; @@ -60,7 +55,7 @@ class Window_Interpreter : public Window_Selectable { int digits_stackitemno = 0, digits_evt_id = 0, digits_page_id = 0, digits_evt_combined_id = 0, digits_cmdcount = 0; InterpDisplayItem display_item; - std::vector stack_display_items; + std::vector stack_display_items; }; #endif From 8a560f1586b53cad11ab67db4a998761fa910204 Mon Sep 17 00:00:00 2001 From: florianessl Date: Mon, 20 Jan 2025 21:12:47 +0100 Subject: [PATCH 02/10] Cleanup/Fix warning: Remove unused function "CreateEmptyLines" from window_interpreter --- src/window_interpreter.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index b716914641..7d312777b2 100644 --- a/src/window_interpreter.cpp +++ b/src/window_interpreter.cpp @@ -27,15 +27,6 @@ #include "player.h" #include "lcf/reader_util.h" -namespace { - std::vector CreateEmptyLines(int c) { - std::vector vars; - for (int i = 0; i < c; i++) - vars.push_back(""); - return vars; - } -} - Window_Interpreter::Window_Interpreter(int ix, int iy, int iwidth, int iheight) : Window_Selectable(ix, iy, iwidth, iheight) { column_max = 1; From 00b8607de4e010fabeaeace91538d5e6bc0e63ab Mon Sep 17 00:00:00 2001 From: florianessl Date: Wed, 5 Feb 2025 07:25:11 +0100 Subject: [PATCH 03/10] Refactor: Allow outside unsafe access to internal interpreter state only when using a wrapper type of 'Game_Interpreter_Inspector' --- src/game_commonevent.h | 3 +- src/game_event.h | 3 +- src/game_interpreter.cpp | 60 ++++++++++++++++++++++++++++++++++ src/game_interpreter.h | 18 +++++++++- src/game_interpreter_debug.cpp | 22 ++++++++++--- src/game_interpreter_debug.h | 2 ++ src/scene_debug.cpp | 30 ++++++++--------- src/scene_debug.h | 2 +- 8 files changed, 113 insertions(+), 27 deletions(-) diff --git a/src/game_commonevent.h b/src/game_commonevent.h index bf60830e7c..11d0518bb0 100644 --- a/src/game_commonevent.h +++ b/src/game_commonevent.h @@ -121,8 +121,7 @@ class Game_CommonEvent { /** Interpreter for parallel common events. */ std::unique_ptr interpreter; - friend class Scene_Debug; - friend class Debug::ParallelInterpreterStates; + friend class Game_Interpreter_Inspector; }; #endif diff --git a/src/game_event.h b/src/game_event.h index 838c5b879e..9a96a00412 100644 --- a/src/game_event.h +++ b/src/game_event.h @@ -219,8 +219,7 @@ class Game_Event : public Game_EventBase { const lcf::rpg::EventPage* page = nullptr; std::unique_ptr interpreter; - friend class Scene_Debug; - friend class Debug::ParallelInterpreterStates; + friend class Game_Interpreter_Inspector; }; inline int Game_Event::GetNumPages() const { diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 5a5b6dbed7..3d9b670d31 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -5508,3 +5508,63 @@ int Game_Interpreter::ManiacBitmask(int value, int mask) const { return value; } + +namespace { + lcf::rpg::SaveEventExecState const& empty_state = {}; +} + + +lcf::rpg::SaveEventExecState const& Game_Interpreter_Inspector::GetForegroundExecState() { + return Game_Interpreter::GetForegroundInterpreter()._state; +} + +lcf::rpg::SaveEventExecState& Game_Interpreter_Inspector::GetForegroundExecStateUnsafe() { + return Game_Interpreter::GetForegroundInterpreter()._state; +} + +lcf::rpg::SaveEventExecState const& Game_Interpreter_Inspector::GetExecState(Game_Event const& ev) { + if (!ev.interpreter) { + return empty_state; + } + return ev.interpreter->GetState(); +} + +lcf::rpg::SaveEventExecState const& Game_Interpreter_Inspector::GetExecState(Game_CommonEvent const& ce) { + if (!ce.interpreter) { + return empty_state; + } + return ce.interpreter->GetState(); +} + +lcf::rpg::SaveEventExecState& Game_Interpreter_Inspector::GetExecStateUnsafe(Game_Event& ev) { + assert(ev.interpreter); + return ev.interpreter->_state; +} + +lcf::rpg::SaveEventExecState& Game_Interpreter_Inspector::GetExecStateUnsafe(Game_CommonEvent& ce) { + assert(ce.interpreter); + return ce.interpreter->_state; +} + +bool Game_Interpreter_Inspector::IsInActiveExcecution(Game_Event const& ev, bool background_only) { + if (!background_only) { + //TODO + } + if (!ev.IsActive() || ev.GetTrigger() != lcf::rpg::EventPage::Trigger_parallel) { + return false; + } + auto pg = ev.GetActivePage(); + if (pg == nullptr || pg->event_commands.empty()) + return false; + return ev.interpreter && ev.interpreter->IsRunning(); +} + +bool Game_Interpreter_Inspector::IsInActiveExcecution(Game_CommonEvent const& ce, bool background_only) { + if (!background_only) { + //TODO + } + if (!ce.IsWaitingBackgroundExecution(false)) { + return false; + } + return ce.interpreter && ce.interpreter->IsRunning(); +} diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 04bf3afda3..258dfe5572 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -348,7 +348,23 @@ class Game_Interpreter : public Game_BaseInterpreterContext KeyInputState _keyinput; AsyncOp _async_op = {}; - friend class Scene_Debug; + friend class Game_Interpreter_Inspector; +}; + +class Game_Interpreter_Inspector { +public: + bool IsInActiveExcecution(Game_Event const& ev, bool background_only); + + bool IsInActiveExcecution(Game_CommonEvent const& ce, bool background_only); + + lcf::rpg::SaveEventExecState const& GetForegroundExecState(); + lcf::rpg::SaveEventExecState& GetForegroundExecStateUnsafe(); + + lcf::rpg::SaveEventExecState const& GetExecState(Game_Event const& ev); + lcf::rpg::SaveEventExecState const& GetExecState(Game_CommonEvent const& ce); + + lcf::rpg::SaveEventExecState& GetExecStateUnsafe(Game_Event& ev); + lcf::rpg::SaveEventExecState& GetExecStateUnsafe(Game_CommonEvent& ce); }; inline const lcf::rpg::SaveEventExecFrame* Game_Interpreter::GetFramePtr() const { diff --git a/src/game_interpreter_debug.cpp b/src/game_interpreter_debug.cpp index 39dd528cfe..7659af118d 100644 --- a/src/game_interpreter_debug.cpp +++ b/src/game_interpreter_debug.cpp @@ -25,6 +25,8 @@ #include Debug::ParallelInterpreterStates Debug::ParallelInterpreterStates::GetCachedStates() { + Game_Interpreter_Inspector inspector; + std::vector ev_ids; std::vector ce_ids; @@ -33,16 +35,19 @@ Debug::ParallelInterpreterStates Debug::ParallelInterpreterStates::GetCachedStat if (Game_Map::GetMapId() > 0) { for (auto& ev : Game_Map::GetEvents()) { - if (ev.GetTrigger() != lcf::rpg::EventPage::Trigger_parallel || !ev.interpreter) + if (!inspector.IsInActiveExcecution(ev, true)) { continue; + } + ev_ids.emplace_back(ev.GetId()); - state_ev.emplace_back(ev.interpreter->GetState()); + state_ev.emplace_back(inspector.GetExecState(ev)); } for (auto& ce : Game_Map::GetCommonEvents()) { - if (ce.IsWaitingBackgroundExecution(false)) { - ce_ids.emplace_back(ce.common_event_id); - state_ce.emplace_back(ce.interpreter->GetState()); + if (!inspector.IsInActiveExcecution(ce, true)) { + continue; } + ce_ids.emplace_back(ce.GetId()); + state_ce.emplace_back(inspector.GetExecState(ce)); } } else if (Game_Battle::IsBattleRunning() && Player::IsPatchManiac()) { //FIXME: Not implemented: battle common events @@ -135,3 +140,10 @@ std::string Debug::FormatEventName(Game_Character const& ch) { } return ""; } + +std::string Debug::FormatEventName(Game_CommonEvent const& ce) { + if (ce.GetName().empty()) { + return fmt::format("CE{:04d}", ce.GetIndex()); + } + return fmt::format("CE{:04d}: '{}'", ce.GetIndex(), ce.GetName()); +} diff --git a/src/game_interpreter_debug.h b/src/game_interpreter_debug.h index b62b2ff803..5b73d11bdc 100644 --- a/src/game_interpreter_debug.h +++ b/src/game_interpreter_debug.h @@ -66,6 +66,8 @@ namespace Debug { std::vector CreateCallStack(const int owner_evt_id, const lcf::rpg::SaveEventExecState& state); std::string FormatEventName(Game_Character const& ev); + + std::string FormatEventName(Game_CommonEvent const& ce); } #endif diff --git a/src/scene_debug.cpp b/src/scene_debug.cpp index c41c0d835d..5c1c432251 100644 --- a/src/scene_debug.cpp +++ b/src/scene_debug.cpp @@ -832,7 +832,7 @@ void Scene_Debug::UpdateRangeListWindow() { skip_items--; continue; } - auto& [evt_id, state] = bg_states.GetEventInterpreter(i); + const auto& [evt_id, state] = bg_states.GetEventInterpreter(i); addItem(fmt::format("{}EV{:04d}: {}", state.wait_movement ? "(W) " : "", evt_id, Game_Map::GetEvent(evt_id)->GetName())); count_items++; } @@ -841,7 +841,7 @@ void Scene_Debug::UpdateRangeListWindow() { skip_items--; continue; } - auto& [ce_id, state] = bg_states.GetCommonEventInterpreter(i); + const auto& [ce_id, state] = bg_states.GetCommonEventInterpreter(i); auto* ce = lcf::ReaderUtil::GetElement(lcf::Data::commonevents, ce_id); addItem(fmt::format("{}CE{:04d}: {}", state.wait_movement ? "(W) " : "", ce_id, ce->name)); count_items++; @@ -1227,18 +1227,14 @@ void Scene_Debug::UpdateInterpreterWindow(int index) { first_line = Game_Battle::IsBattleRunning() ? "Foreground (Battle)" : "Foreground (Map)"; valid = true; } else if (index <= bg_states.CountEventInterpreters()) { - auto tuple = bg_states.GetEventInterpreter(index - 1); - evt_id = std::get<0>(tuple); - state = std::get<1>(tuple); - first_line = fmt::format("EV{:04d}: {}", evt_id, Game_Map::GetEvent(evt_id)->GetName()); + const auto& [evt_id, state] = bg_states.GetEventInterpreter(index - 1); + first_line = Debug::FormatEventName(*Game_Map::GetEvent(evt_id)); valid = true; } else if ((index - bg_states.CountEventInterpreters()) <= bg_states.CountCommonEventInterpreters()) { - auto tuple = bg_states.GetCommonEventInterpreter(index - bg_states.CountEventInterpreters() - 1); - int ce_id = std::get<0>(tuple); - state = std::get<1>(tuple); + const auto& [ce_id, state] = bg_states.GetCommonEventInterpreter(index - bg_states.CountEventInterpreters() - 1); for (auto& ce : Game_Map::GetCommonEvents()) { - if (ce.common_event_id == ce_id) { - first_line = fmt::format("CE{:04d}: {}", ce_id, ce.GetName()); + if (ce.GetId() == ce_id) { + first_line = Debug::FormatEventName(ce); evt_id = ce_id; valid = true; break; @@ -1255,30 +1251,32 @@ void Scene_Debug::UpdateInterpreterWindow(int index) { } } -lcf::rpg::SaveEventExecFrame& Scene_Debug::GetSelectedInterpreterFrameFromUiState() const { +lcf::rpg::SaveEventExecFrame const& Scene_Debug::GetSelectedInterpreterFrameFromUiState() const { static lcf::rpg::SaveEventExecFrame empty; if (state_interpreter.selected_state <= 0 || state_interpreter.selected_frame < 0) { return empty; } + Game_Interpreter_Inspector inspector; + int index = state_interpreter.selected_state; const auto& bg_states = state_interpreter.background_states; if (index == 1) { - auto& state = Game_Interpreter::GetForegroundInterpreter()._state; + auto const& state = inspector.GetForegroundExecState(); return state.stack[state_interpreter.selected_frame]; } else if (index <= bg_states.CountEventInterpreters()) { int evt_id = std::get<0>(bg_states.GetEventInterpreter(index - 1)); auto ev = Game_Map::GetEvent(evt_id); - auto& state = ev->interpreter->_state; + auto const& state = inspector.GetExecState(*ev); return state.stack[state_interpreter.selected_frame]; } else if ((index - bg_states.CountEventInterpreters()) <= bg_states.CountCommonEventInterpreters()) { int ce_id = std::get<0>(bg_states.GetEventInterpreter(index - bg_states.CountEventInterpreters() - 1)); for (auto& ce : Game_Map::GetCommonEvents()) { - if (ce.common_event_id == ce_id) { - auto& state = ce.interpreter->_state; + if (ce.GetId() == ce_id) { + auto const& state = inspector.GetExecState(ce); return state.stack[state_interpreter.selected_frame]; } } diff --git a/src/scene_debug.h b/src/scene_debug.h index f0e74ca0e2..c5a24b01db 100644 --- a/src/scene_debug.h +++ b/src/scene_debug.h @@ -191,7 +191,7 @@ class Scene_Debug : public Scene { bool interpreter_states_cached = false; void UpdateInterpreterWindow(int index); - lcf::rpg::SaveEventExecFrame& GetSelectedInterpreterFrameFromUiState() const; + lcf::rpg::SaveEventExecFrame const& GetSelectedInterpreterFrameFromUiState() const; struct { Debug::ParallelInterpreterStates background_states; From 98d3c1c5ae911fc5de9d55d42814906ea0295f3b Mon Sep 17 00:00:00 2001 From: florianessl Date: Wed, 5 Feb 2025 20:46:22 +0100 Subject: [PATCH 04/10] =?UTF-8?q?Refactored=20Game=5FIntepreter::Push=20&?= =?UTF-8?q?=20implemented=20Maniac=C2=B4s=20special=20field=20"maniac=5Fev?= =?UTF-8?q?ent=5Finfo"=20which=20holds=20info=20about=20the=20current=20in?= =?UTF-8?q?terpreter=20frame.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/game_commonevent.cpp | 2 +- src/game_event.cpp | 2 +- src/game_interpreter.cpp | 52 +++++++++++++++++++++++++-------- src/game_interpreter.h | 18 +++++++----- src/game_interpreter_battle.cpp | 12 ++++---- src/game_interpreter_shared.h | 34 +++++++++++++++++++++ src/game_map.cpp | 24 +++++++++++++-- src/scene_debug.cpp | 12 ++++---- 8 files changed, 121 insertions(+), 35 deletions(-) diff --git a/src/game_commonevent.cpp b/src/game_commonevent.cpp index 1f74ffe7d1..e226928209 100644 --- a/src/game_commonevent.cpp +++ b/src/game_commonevent.cpp @@ -32,7 +32,7 @@ Game_CommonEvent::Game_CommonEvent(int common_event_id) : if (ce->trigger == lcf::rpg::EventPage::Trigger_parallel && !ce->event_commands.empty()) { interpreter.reset(new Game_Interpreter_Map()); - interpreter->Push(this); + interpreter->Push(this, InterpreterExecutionType::Parallel); } diff --git a/src/game_event.cpp b/src/game_event.cpp index 62c78757a3..e962eb140b 100644 --- a/src/game_event.cpp +++ b/src/game_event.cpp @@ -587,7 +587,7 @@ AsyncOp Game_Event::Update(bool resume_async) { // the wait will tick by 1 each time the interpreter is invoked. if ((resume_async || GetTrigger() == lcf::rpg::EventPage::Trigger_parallel) && interpreter) { if (!interpreter->IsRunning() && page && !page->event_commands.empty()) { - interpreter->Push(this); + interpreter->Push(this, InterpreterExecutionType::Parallel); } interpreter->Update(!resume_async); diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 3d9b670d31..1dd4cf3af8 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -101,9 +101,9 @@ bool Game_Interpreter::IsRunning() const { // Setup. void Game_Interpreter::Push( + InterpreterPush push_info, std::vector _list, int event_id, - bool started_by_decision_key, int event_page_id ) { if (_list.empty()) { @@ -114,15 +114,28 @@ void Game_Interpreter::Push( Output::Error("Call Event limit ({}) has been exceeded", call_stack_limit); } + auto type_ex = std::get(push_info); + auto type_src = std::get(push_info); + lcf::rpg::SaveEventExecFrame frame; frame.ID = _state.stack.size() + 1; frame.commands = std::move(_list); frame.current_command = 0; - frame.triggered_by_decision_key = started_by_decision_key; - frame.event_id = event_id; + frame.triggered_by_decision_key = type_ex == ExecutionType::Action; + if (type_src == EventType::MapEvent) { + frame.event_id = event_id; + } frame.maniac_event_id = event_id; frame.maniac_event_page_id = event_page_id; + if (type_ex <= ExecutionType::BattleParallel) { + frame.maniac_event_info = static_cast(type_ex); + } + + if (type_src <= EventType::BattleEvent) { + frame.maniac_event_info |= static_cast(type_src); + } + if (_state.stack.empty() && main_flag && !Game_Battle::IsBattleRunning()) { Main_Data::game_system->ClearMessageFace(); Main_Data::game_player->SetMenuCalling(false); @@ -529,16 +542,31 @@ void Game_Interpreter::Update(bool reset_loop_count) { } // Setup Starting Event -void Game_Interpreter::Push(Game_Event* ev) { - Push(ev->GetList(), ev->GetId(), ev->WasStartedByDecisionKey(), ev->GetActivePage() ? ev->GetActivePage()->ID : 0); +void Game_Interpreter::Push(Game_Event* ev, ExecutionType ex_type) { + assert(ex_type <= ExecutionType::Call || ex_type == ExecutionType::DebugCall); + + Push( + { ex_type, EventType::MapEvent }, + ev->GetList(), ev->GetId(), ev->GetActivePage() ? ev->GetActivePage()->ID : 0 + ); } -void Game_Interpreter::Push(Game_Event* ev, const lcf::rpg::EventPage* page, bool triggered_by_decision_key) { - Push(page->event_commands, ev->GetId(), triggered_by_decision_key, page->ID); +void Game_Interpreter::Push(Game_Event* ev, const lcf::rpg::EventPage* page, ExecutionType ex_type) { + assert(ex_type <= ExecutionType::Call || ex_type == ExecutionType::DebugCall); + + Push( + { ex_type, EventType::MapEvent }, + page->event_commands, ev->GetId(), page->ID + ); } -void Game_Interpreter::Push(Game_CommonEvent* ev) { - Push(ev->GetList(), 0, false); +void Game_Interpreter::Push(Game_CommonEvent* ev, ExecutionType ex_type) { + assert(ex_type == ExecutionType::AutoStart || ex_type == ExecutionType::Parallel + || ex_type == ExecutionType::Call || ex_type == ExecutionType::DeathHandler + || ex_type == ExecutionType::DebugCall + ); + + Push({ ex_type, EventType::CommonEvent }, ev->GetList(), ev->GetId()); } bool Game_Interpreter::CheckGameOver() { @@ -3921,7 +3949,7 @@ bool Game_Interpreter::CommandCallEvent(lcf::rpg::EventCommand const& com) { // return true; } - Push(common_event); + Push(common_event, ExecutionType::Call); return true; } @@ -3949,7 +3977,7 @@ bool Game_Interpreter::CommandCallEvent(lcf::rpg::EventCommand const& com) { // return true; } - Push(page->event_commands, event->GetId(), false, page->ID); + Push({ ExecutionType::Call, EventType::MapEvent }, page->event_commands, event->GetId(), page->ID); return true; } @@ -5320,7 +5348,7 @@ bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& co // Our implementation pushes a new frame containing the command instead of invoking it directly. // This is incompatible to Maniacs but has a better compatibility with our code. - Push({ cmd }, GetCurrentEventId(), false); //FIXME: add some new flag, so the interpreter debug view (window_interpreter) can differentiate this frame from normal ones + Push({ ExecutionType::Eval, EventType::None }, { cmd }, GetCurrentEventId(), 0); return true; } diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 258dfe5572..ecb508b972 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -38,6 +38,9 @@ class Game_Event; class Game_CommonEvent; class PendingMessage; + +using InterpreterPush = std::tuple; + /** * Game_Interpreter class */ @@ -65,14 +68,15 @@ class Game_Interpreter : public Game_BaseInterpreterContext void Update(bool reset_loop_count=true); void Push( - std::vector _list, - int _event_id, - bool started_by_decision_key = false, - int event_page_id = 0 + InterpreterPush push_info, + std::vector _list, + int _event_id, + int event_page_id = 0 ); - void Push(Game_Event* ev); - void Push(Game_Event* ev, const lcf::rpg::EventPage* page, bool triggered_by_decision_key); - void Push(Game_CommonEvent* ev); + + void Push(Game_Event* ev, InterpreterExecutionType ex_type); + void Push(Game_Event* ev, const lcf::rpg::EventPage* page, InterpreterExecutionType ex_type); + void Push(Game_CommonEvent* ev, InterpreterExecutionType ex_type); void InputButton(); void SetupChoices(const std::vector& choices, int indent, PendingMessage& pm); diff --git a/src/game_interpreter_battle.cpp b/src/game_interpreter_battle.cpp index da2ec95d08..5e7d21ab4c 100644 --- a/src/game_interpreter_battle.cpp +++ b/src/game_interpreter_battle.cpp @@ -33,6 +33,8 @@ #include #include "scene_battle.h" +using namespace Game_Interpreter_Shared; + enum BranchBattleSubcommand { eOptionBranchBattleElse = 1 }; @@ -211,7 +213,7 @@ int Game_Interpreter_Battle::ScheduleNextPage(lcf::rpg::TroopPageCondition::Flag continue; } Clear(); - Push(page.event_commands, 0); + Push({ ExecutionType::Eval, EventType::None }, page.event_commands, 0); // FIXME: clarify type_src & type_ex for battle events executed[i] = true; return i + 1; } @@ -275,7 +277,7 @@ bool Game_Interpreter_Battle::CommandCallCommonEvent(lcf::rpg::EventCommand cons return true; } - Push(common_event); + Push(common_event, ExecutionType::Call); return true; } @@ -643,9 +645,9 @@ bool Game_Interpreter_Battle::ManiacBattleHook(ManiacBattleHookType hook_type, i Output::Warning("CommandManiacControlBattle: Can't call invalid common event {}", common_event_id); return false; } - + // pushes the common event to be run into the queue of events. - maniac_interpreter->Push(common_event); + maniac_interpreter->Push(common_event, ExecutionType::Call); // FIXME: clarify type_src & type_ex for battle events // pushes the change variable events into the interpreters // event queue, so we don't run into a race condition. @@ -683,7 +685,7 @@ bool Game_Interpreter_Battle::ManiacBattleHook(ManiacBattleHookType hook_type, i } // Push is actually "push_back", so this gets added before other events. - maniac_interpreter->Push(pre_commands, 0); + maniac_interpreter->Push({ ExecutionType::Eval, EventType::None }, pre_commands, 0); // FIXME: clarify type_src & type_ex for battle events // Necessary to start the sub-event. maniac_interpreter->Update(); diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 42c27990de..b1833ef4a5 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -29,6 +29,37 @@ class Game_BaseInterpreterContext; namespace Game_Interpreter_Shared { + enum class EventType { + None = 0, + MapEvent, + CommonEvent, + BattleEvent + }; + + enum class ExecutionType { + /* + * MapEvent Triggered by decision key + * (or via custom command EasyRpg_TriggerEventAt) + */ + Action = 0, + Touch, + Collision, + AutoStart, + Parallel, + /* Frame was pushed via "CallCommand" */ + Call, + /* Maniacīs special CE type "Battle start" */ + BattleStart, + /* Maniacīs special CE type "Battle Parallel" */ + BattleParallel, + + /* 2k3 Death Handler */ + DeathHandler = 10, + /* Event code was dynamically evaluated. (ManiacCallCommand) */ + Eval, + DebugCall + }; + /* * Indicates how the target of an interpreter operation (lvalue) should be evaluated. */ @@ -169,4 +200,7 @@ class Game_BaseInterpreterContext { } }; +using InterpreterExecutionType = Game_Interpreter_Shared::ExecutionType; +using InterpreterEventType = Game_Interpreter_Shared::EventType; + #endif diff --git a/src/game_map.cpp b/src/game_map.cpp index 5180fdd437..1622b35e42 100644 --- a/src/game_map.cpp +++ b/src/game_map.cpp @@ -1384,7 +1384,7 @@ bool Game_Map::UpdateForegroundEvents(MapUpdateAsyncContext& actx) { } } if (run_ce) { - interp.Push(run_ce); + interp.Push(run_ce, InterpreterExecutionType::AutoStart); } Game_Event* run_ev = nullptr; @@ -1399,7 +1399,25 @@ bool Game_Map::UpdateForegroundEvents(MapUpdateAsyncContext& actx) { } } if (run_ev) { - interp.Push(run_ev); + if (run_ev->WasStartedByDecisionKey()) { + interp.Push(run_ev, InterpreterExecutionType::Action); + } else { + switch (run_ev->GetTrigger()) { + case lcf::rpg::EventPage::Trigger_touched: + interp.Push(run_ev, InterpreterExecutionType::Touch); + break; + case lcf::rpg::EventPage::Trigger_collision: + interp.Push(run_ev, InterpreterExecutionType::Collision); + break; + case lcf::rpg::EventPage::Trigger_auto_start: + interp.Push(run_ev, InterpreterExecutionType::AutoStart); + break; + case lcf::rpg::EventPage::Trigger_action: + default: + interp.Push(run_ev, InterpreterExecutionType::Action); + break; + } + } run_ev->ClearWaitingForegroundExecution(); } @@ -1549,7 +1567,7 @@ static void OnEncounterEnd(BattleResult result) { auto* ce = lcf::ReaderUtil::GetElement(common_events, Game_Battle::GetDeathHandlerCommonEvent()); if (ce) { auto& interp = Game_Map::GetInterpreter(); - interp.Push(ce); + interp.Push(ce, InterpreterExecutionType::DeathHandler); } auto tt = Game_Battle::GetDeathHandlerTeleport(); diff --git a/src/scene_debug.cpp b/src/scene_debug.cpp index 5c1c432251..79b0d16639 100644 --- a/src/scene_debug.cpp +++ b/src/scene_debug.cpp @@ -1124,11 +1124,11 @@ void Scene_Debug::DoCallCommonEvent() { auto& ce = Game_Map::GetCommonEvents()[ceid - 1]; if (Game_Battle::IsBattleRunning()) { - Game_Battle::GetInterpreter().Push(&ce); + Game_Battle::GetInterpreter().Push(&ce, InterpreterExecutionType::DebugCall); Scene::PopUntil(Scene::Battle); Output::Debug("Debug Scene Forced execution of common event {} on the battle foreground interpreter.", ce.GetIndex()); } else { - Game_Map::GetInterpreter().Push(&ce); + Game_Map::GetInterpreter().Push(&ce, InterpreterExecutionType::DebugCall); Scene::PopUntil(Scene::Map); Output::Debug("Debug Scene Forced execution of common event {} on the map foreground interpreter.", ce.GetIndex()); } @@ -1153,11 +1153,11 @@ void Scene_Debug::DoCallMapEvent() { } if (Game_Battle::IsBattleRunning()) { - Game_Battle::GetInterpreter().Push(me, page, false); + Game_Battle::GetInterpreter().Push(me, page, InterpreterExecutionType::DebugCall); Scene::PopUntil(Scene::Battle); Output::Debug("Debug Scene Forced execution of map event {} page {} on the battle foreground interpreter.", me->GetId(), page->ID); } else { - Game_Map::GetInterpreter().Push(me, page, false); + Game_Map::GetInterpreter().Push(me, page, InterpreterExecutionType::DebugCall); Scene::PopUntil(Scene::Map); Output::Debug("Debug Scene Forced execution of map event {} page {} on the map foreground interpreter.", me->GetId(), page->ID); } @@ -1181,9 +1181,9 @@ void Scene_Debug::DoCallBattleEvent() { auto& page = troop->pages[page_idx]; - Game_Battle::GetInterpreter().Push(page.event_commands, 0, false); + Game_Battle::GetInterpreter().Push({ InterpreterExecutionType::DebugCall, InterpreterEventType::BattleEvent }, page.event_commands, 0, false); Scene::PopUntil(Scene::Battle); - Output::Debug("Debug Scene Forced execution of battle troop {} event page {} on the map foreground interpreter.", troop->ID, page.ID); + Output::Debug("Debug Scene Forced execution of battle troop {} event page {} on the battle foreground interpreter.", troop->ID, page.ID); } void Scene_Debug::DoOpenMenu() { From 7e3ecd3cc5828eb2abd66d5f8b38d22d13ea07d5 Mon Sep 17 00:00:00 2001 From: florianessl Date: Thu, 6 Feb 2025 17:14:24 +0100 Subject: [PATCH 05/10] Implemented missing Maniac subcommand "GetGameInfo" -> "Get Command Interpreter State" --- src/game_interpreter.cpp | 28 +++++++++++++++++++++++++--- src/game_interpreter_shared.h | 27 ++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 1dd4cf3af8..206b0537d1 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -133,7 +133,7 @@ void Game_Interpreter::Push( } if (type_src <= EventType::BattleEvent) { - frame.maniac_event_info |= static_cast(type_src); + frame.maniac_event_info |= (static_cast(type_src) << 4); } if (_state.stack.empty() && main_flag && !Game_Battle::IsBattleRunning()) { @@ -4145,8 +4145,30 @@ bool Game_Interpreter::CommandManiacGetGameInfo(lcf::rpg::EventCommand const& co Output::Warning("GetGameInfo: Option 'Pixel Info' not implemented."); break; case 4: // Get command interpreter state - // FIXME: figure out how 'command interpreter state' works - Output::Warning("GetGameInfo: Option 'Command Interpreter State' not implemented."); + { + // Parameter "Nest" in the English version of Maniacs + // This value specifies how far you'd want to go back the stack + int peek = ValueOrVariableBitfield(com.parameters[0], 2, com.parameters[4]); + + //First set everything to '0' + Main_Data::game_variables->SetRange(var, var + 4, 0); + + int stack_no = _state.stack.size() - peek; + if (stack_no > 0) { + auto frame = &_state.stack[stack_no - 1]; + + // Note: It looks like for Battles, Maniacs doesn't give out any detailed interpreter + // information via this command (only the current command line: frame->current_command) + // The others are implemented here nonetheless for consistency. + // (This is true for both the normal "Troop" events & the new "Battle Start"/"Battle Parallel" execution types) + + Main_Data::game_variables->Set(var, static_cast(ManiacEventType(*frame))); + Main_Data::game_variables->Set(var + 1, frame->maniac_event_id); + Main_Data::game_variables->Set(var + 2, frame->maniac_event_page_id); + Main_Data::game_variables->Set(var + 3, static_cast(ManiacExecutionType(*frame))); + Main_Data::game_variables->Set(var + 4, frame->current_command + 1); + } + } break; case 5: // Get tileset ID Main_Data::game_variables->Set(var, Game_Map::GetChipset()); diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index b1833ef4a5..061b200a4c 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -133,6 +133,9 @@ namespace Game_Interpreter_Shared { lcf::rpg::MoveCommand DecodeMove(lcf::DBArray::const_iterator& it); bool ManiacCheckContinueLoop(int val, int val2, int type, int op); + + ExecutionType ManiacExecutionType(lcf::rpg::SaveEventExecFrame const& frame); + EventType ManiacEventType(lcf::rpg::SaveEventExecFrame const& frame); } inline bool Game_Interpreter_Shared::CheckOperator(int val, int val2, int op) { @@ -171,6 +174,27 @@ inline bool Game_Interpreter_Shared::ManiacCheckContinueLoop(int val, int val2, } } +using InterpreterExecutionType = Game_Interpreter_Shared::ExecutionType; +using InterpreterEventType = Game_Interpreter_Shared::EventType; + +inline InterpreterExecutionType Game_Interpreter_Shared::ManiacExecutionType(lcf::rpg::SaveEventExecFrame const& frame) { + if (int type_ex = (frame.maniac_event_info & 0xF); type_ex <= static_cast(ExecutionType::BattleParallel)) { + return static_cast(type_ex); + } + return InterpreterExecutionType::Action; +} + +inline InterpreterEventType Game_Interpreter_Shared::ManiacEventType(lcf::rpg::SaveEventExecFrame const& frame) { + if ((frame.maniac_event_info & 0x10) > 0) { + return InterpreterEventType::MapEvent; + } else if ((frame.maniac_event_info & 0x20) > 0) { + return InterpreterEventType::CommonEvent; + } else if ((frame.maniac_event_info & 0x40) > 0) { + return InterpreterEventType::BattleEvent; + } + return InterpreterEventType::None; +} + class Game_BaseInterpreterContext { public: virtual ~Game_BaseInterpreterContext() {} @@ -200,7 +224,4 @@ class Game_BaseInterpreterContext { } }; -using InterpreterExecutionType = Game_Interpreter_Shared::ExecutionType; -using InterpreterEventType = Game_Interpreter_Shared::EventType; - #endif From 4f083636888f4d4b972013f00370e9a6f6697598 Mon Sep 17 00:00:00 2001 From: florianessl Date: Thu, 6 Feb 2025 17:37:36 +0100 Subject: [PATCH 06/10] Refactored "Interpreter.Push" some more, so that we could potentially catch some undefined or incorrect types of interpreter pushes at compile time. --- src/game_commonevent.cpp | 2 +- src/game_event.cpp | 2 +- src/game_interpreter.cpp | 29 +++++++------------ src/game_interpreter.h | 51 ++++++++++++++++++++++++++++++--- src/game_interpreter_battle.cpp | 8 +++--- src/game_map.cpp | 14 ++++----- src/scene_debug.cpp | 10 +++---- 7 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/game_commonevent.cpp b/src/game_commonevent.cpp index e226928209..e46bd887e8 100644 --- a/src/game_commonevent.cpp +++ b/src/game_commonevent.cpp @@ -32,7 +32,7 @@ Game_CommonEvent::Game_CommonEvent(int common_event_id) : if (ce->trigger == lcf::rpg::EventPage::Trigger_parallel && !ce->event_commands.empty()) { interpreter.reset(new Game_Interpreter_Map()); - interpreter->Push(this, InterpreterExecutionType::Parallel); + interpreter->Push(this); } diff --git a/src/game_event.cpp b/src/game_event.cpp index e962eb140b..951320a38a 100644 --- a/src/game_event.cpp +++ b/src/game_event.cpp @@ -587,7 +587,7 @@ AsyncOp Game_Event::Update(bool resume_async) { // the wait will tick by 1 each time the interpreter is invoked. if ((resume_async || GetTrigger() == lcf::rpg::EventPage::Trigger_parallel) && interpreter) { if (!interpreter->IsRunning() && page && !page->event_commands.empty()) { - interpreter->Push(this, InterpreterExecutionType::Parallel); + interpreter->Push(this); } interpreter->Update(!resume_async); diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 206b0537d1..456edf409b 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -100,7 +100,7 @@ bool Game_Interpreter::IsRunning() const { } // Setup. -void Game_Interpreter::Push( +void Game_Interpreter::PushInternal( InterpreterPush push_info, std::vector _list, int event_id, @@ -542,31 +542,22 @@ void Game_Interpreter::Update(bool reset_loop_count) { } // Setup Starting Event -void Game_Interpreter::Push(Game_Event* ev, ExecutionType ex_type) { - assert(ex_type <= ExecutionType::Call || ex_type == ExecutionType::DebugCall); - - Push( +void Game_Interpreter::PushInternal(Game_Event* ev, ExecutionType ex_type) { + PushInternal( { ex_type, EventType::MapEvent }, ev->GetList(), ev->GetId(), ev->GetActivePage() ? ev->GetActivePage()->ID : 0 ); } -void Game_Interpreter::Push(Game_Event* ev, const lcf::rpg::EventPage* page, ExecutionType ex_type) { - assert(ex_type <= ExecutionType::Call || ex_type == ExecutionType::DebugCall); - - Push( +void Game_Interpreter::PushInternal(Game_Event* ev, const lcf::rpg::EventPage* page, ExecutionType ex_type) { + PushInternal( { ex_type, EventType::MapEvent }, page->event_commands, ev->GetId(), page->ID ); } -void Game_Interpreter::Push(Game_CommonEvent* ev, ExecutionType ex_type) { - assert(ex_type == ExecutionType::AutoStart || ex_type == ExecutionType::Parallel - || ex_type == ExecutionType::Call || ex_type == ExecutionType::DeathHandler - || ex_type == ExecutionType::DebugCall - ); - - Push({ ex_type, EventType::CommonEvent }, ev->GetList(), ev->GetId()); +void Game_Interpreter::PushInternal(Game_CommonEvent* ev, ExecutionType ex_type) { + PushInternal({ ex_type, EventType::CommonEvent }, ev->GetList(), ev->GetId()); } bool Game_Interpreter::CheckGameOver() { @@ -3949,7 +3940,7 @@ bool Game_Interpreter::CommandCallEvent(lcf::rpg::EventCommand const& com) { // return true; } - Push(common_event, ExecutionType::Call); + Push(common_event); return true; } @@ -3977,7 +3968,7 @@ bool Game_Interpreter::CommandCallEvent(lcf::rpg::EventCommand const& com) { // return true; } - Push({ ExecutionType::Call, EventType::MapEvent }, page->event_commands, event->GetId(), page->ID); + Push(page->event_commands, event->GetId(), page->ID); return true; } @@ -5370,7 +5361,7 @@ bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& co // Our implementation pushes a new frame containing the command instead of invoking it directly. // This is incompatible to Maniacs but has a better compatibility with our code. - Push({ ExecutionType::Eval, EventType::None }, { cmd }, GetCurrentEventId(), 0); + Push({ cmd }, GetCurrentEventId(), 0); return true; } diff --git a/src/game_interpreter.h b/src/game_interpreter.h index ecb508b972..4c12dc04fb 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -67,16 +67,21 @@ class Game_Interpreter : public Game_BaseInterpreterContext void Update(bool reset_loop_count=true); + template void Push( - InterpreterPush push_info, std::vector _list, int _event_id, int event_page_id = 0 ); - void Push(Game_Event* ev, InterpreterExecutionType ex_type); - void Push(Game_Event* ev, const lcf::rpg::EventPage* page, InterpreterExecutionType ex_type); - void Push(Game_CommonEvent* ev, InterpreterExecutionType ex_type); + template + void Push(Game_Event* ev); + + template + void Push(Game_Event* ev, const lcf::rpg::EventPage* page); + + template + void Push(Game_CommonEvent* ev); void InputButton(); void SetupChoices(const std::vector& choices, int indent, PendingMessage& pm); @@ -352,6 +357,18 @@ class Game_Interpreter : public Game_BaseInterpreterContext KeyInputState _keyinput; AsyncOp _async_op = {}; + private: + void PushInternal( + InterpreterPush push_info, + std::vector _list, + int _event_id, + int event_page_id = 0 + ); + + void PushInternal(Game_Event* ev, InterpreterExecutionType ex_type); + void PushInternal(Game_Event* ev, const lcf::rpg::EventPage* page, InterpreterExecutionType ex_type); + void PushInternal(Game_CommonEvent* ev, InterpreterExecutionType ex_type); + friend class Game_Interpreter_Inspector; }; @@ -371,6 +388,32 @@ class Game_Interpreter_Inspector { lcf::rpg::SaveEventExecState& GetExecStateUnsafe(Game_CommonEvent& ce); }; +template +inline void Game_Interpreter::Push(std::vector _list, int _event_id, int event_page_id) { + PushInternal({ type_ex, type_ev }, _list, _event_id, event_page_id); +} + +template +inline void Game_Interpreter::Push(Game_Event* ev) { + static_assert(type_ex <= InterpreterExecutionType::Call || type_ex == InterpreterExecutionType::DebugCall, "Unexpected ExecutionType for MapEvent"); + PushInternal(ev, type_ex); +} + +template +inline void Game_Interpreter::Push(Game_Event* ev, const lcf::rpg::EventPage* page) { + static_assert(type_ex <= InterpreterExecutionType::Call || type_ex == InterpreterExecutionType::DebugCall, "Unexpected ExecutionType for MapEvent"); + PushInternal(ev, page, type_ex); +} + +template +inline void Game_Interpreter::Push(Game_CommonEvent* ev) { + static_assert(type_ex == InterpreterExecutionType::AutoStart || type_ex == InterpreterExecutionType::Parallel + || type_ex == InterpreterExecutionType::Call || type_ex == InterpreterExecutionType::DeathHandler + || type_ex == InterpreterExecutionType::DebugCall, "Unexpected ExecutionType for CommonEvent" + ); + PushInternal(ev, type_ex); +} + inline const lcf::rpg::SaveEventExecFrame* Game_Interpreter::GetFramePtr() const { return !_state.stack.empty() ? &_state.stack.back() : nullptr; } diff --git a/src/game_interpreter_battle.cpp b/src/game_interpreter_battle.cpp index 5e7d21ab4c..a94ecff0f4 100644 --- a/src/game_interpreter_battle.cpp +++ b/src/game_interpreter_battle.cpp @@ -213,7 +213,7 @@ int Game_Interpreter_Battle::ScheduleNextPage(lcf::rpg::TroopPageCondition::Flag continue; } Clear(); - Push({ ExecutionType::Eval, EventType::None }, page.event_commands, 0); // FIXME: clarify type_src & type_ex for battle events + Push(page.event_commands, 0); // FIXME: clarify type_src & type_ex for battle events executed[i] = true; return i + 1; } @@ -277,7 +277,7 @@ bool Game_Interpreter_Battle::CommandCallCommonEvent(lcf::rpg::EventCommand cons return true; } - Push(common_event, ExecutionType::Call); + Push(common_event); return true; } @@ -647,7 +647,7 @@ bool Game_Interpreter_Battle::ManiacBattleHook(ManiacBattleHookType hook_type, i } // pushes the common event to be run into the queue of events. - maniac_interpreter->Push(common_event, ExecutionType::Call); // FIXME: clarify type_src & type_ex for battle events + maniac_interpreter->Push(common_event); // FIXME: clarify type_src & type_ex for battle events // pushes the change variable events into the interpreters // event queue, so we don't run into a race condition. @@ -685,7 +685,7 @@ bool Game_Interpreter_Battle::ManiacBattleHook(ManiacBattleHookType hook_type, i } // Push is actually "push_back", so this gets added before other events. - maniac_interpreter->Push({ ExecutionType::Eval, EventType::None }, pre_commands, 0); // FIXME: clarify type_src & type_ex for battle events + maniac_interpreter->Push(pre_commands, 0); // FIXME: clarify type_src & type_ex for battle events // Necessary to start the sub-event. maniac_interpreter->Update(); diff --git a/src/game_map.cpp b/src/game_map.cpp index 1622b35e42..b68163cae4 100644 --- a/src/game_map.cpp +++ b/src/game_map.cpp @@ -1384,7 +1384,7 @@ bool Game_Map::UpdateForegroundEvents(MapUpdateAsyncContext& actx) { } } if (run_ce) { - interp.Push(run_ce, InterpreterExecutionType::AutoStart); + interp.Push(run_ce); } Game_Event* run_ev = nullptr; @@ -1400,21 +1400,21 @@ bool Game_Map::UpdateForegroundEvents(MapUpdateAsyncContext& actx) { } if (run_ev) { if (run_ev->WasStartedByDecisionKey()) { - interp.Push(run_ev, InterpreterExecutionType::Action); + interp.Push(run_ev); } else { switch (run_ev->GetTrigger()) { case lcf::rpg::EventPage::Trigger_touched: - interp.Push(run_ev, InterpreterExecutionType::Touch); + interp.Push(run_ev); break; case lcf::rpg::EventPage::Trigger_collision: - interp.Push(run_ev, InterpreterExecutionType::Collision); + interp.Push(run_ev); break; case lcf::rpg::EventPage::Trigger_auto_start: - interp.Push(run_ev, InterpreterExecutionType::AutoStart); + interp.Push(run_ev); break; case lcf::rpg::EventPage::Trigger_action: default: - interp.Push(run_ev, InterpreterExecutionType::Action); + interp.Push(run_ev); break; } } @@ -1567,7 +1567,7 @@ static void OnEncounterEnd(BattleResult result) { auto* ce = lcf::ReaderUtil::GetElement(common_events, Game_Battle::GetDeathHandlerCommonEvent()); if (ce) { auto& interp = Game_Map::GetInterpreter(); - interp.Push(ce, InterpreterExecutionType::DeathHandler); + interp.Push(ce); } auto tt = Game_Battle::GetDeathHandlerTeleport(); diff --git a/src/scene_debug.cpp b/src/scene_debug.cpp index 79b0d16639..eb31fdc273 100644 --- a/src/scene_debug.cpp +++ b/src/scene_debug.cpp @@ -1124,11 +1124,11 @@ void Scene_Debug::DoCallCommonEvent() { auto& ce = Game_Map::GetCommonEvents()[ceid - 1]; if (Game_Battle::IsBattleRunning()) { - Game_Battle::GetInterpreter().Push(&ce, InterpreterExecutionType::DebugCall); + Game_Battle::GetInterpreter().Push(&ce); Scene::PopUntil(Scene::Battle); Output::Debug("Debug Scene Forced execution of common event {} on the battle foreground interpreter.", ce.GetIndex()); } else { - Game_Map::GetInterpreter().Push(&ce, InterpreterExecutionType::DebugCall); + Game_Map::GetInterpreter().Push(&ce); Scene::PopUntil(Scene::Map); Output::Debug("Debug Scene Forced execution of common event {} on the map foreground interpreter.", ce.GetIndex()); } @@ -1153,11 +1153,11 @@ void Scene_Debug::DoCallMapEvent() { } if (Game_Battle::IsBattleRunning()) { - Game_Battle::GetInterpreter().Push(me, page, InterpreterExecutionType::DebugCall); + Game_Battle::GetInterpreter().Push(me, page); Scene::PopUntil(Scene::Battle); Output::Debug("Debug Scene Forced execution of map event {} page {} on the battle foreground interpreter.", me->GetId(), page->ID); } else { - Game_Map::GetInterpreter().Push(me, page, InterpreterExecutionType::DebugCall); + Game_Map::GetInterpreter().Push(me, page); Scene::PopUntil(Scene::Map); Output::Debug("Debug Scene Forced execution of map event {} page {} on the map foreground interpreter.", me->GetId(), page->ID); } @@ -1181,7 +1181,7 @@ void Scene_Debug::DoCallBattleEvent() { auto& page = troop->pages[page_idx]; - Game_Battle::GetInterpreter().Push({ InterpreterExecutionType::DebugCall, InterpreterEventType::BattleEvent }, page.event_commands, 0, false); + Game_Battle::GetInterpreter().Push(page.event_commands, 0, false); Scene::PopUntil(Scene::Battle); Output::Debug("Debug Scene Forced execution of battle troop {} event page {} on the battle foreground interpreter.", troop->ID, page.ID); } From eb37dbc1cee5558ad4485af78ea9648dd7617637 Mon Sep 17 00:00:00 2001 From: florianessl Date: Thu, 6 Feb 2025 18:25:17 +0100 Subject: [PATCH 07/10] Fix: Interpreter debug window was broken after refactor, Rewrite & simplify the whole thing by using the recently implemented event info fields --- src/game_interpreter_debug.cpp | 90 ++++++++++++++-------------------- src/game_interpreter_debug.h | 8 ++- src/game_interpreter_shared.h | 12 +++++ src/scene_debug.cpp | 10 ++-- src/window_interpreter.cpp | 41 +++++++++++----- src/window_interpreter.h | 4 +- 6 files changed, 92 insertions(+), 73 deletions(-) diff --git a/src/game_interpreter_debug.cpp b/src/game_interpreter_debug.cpp index 7659af118d..0be5d2872c 100644 --- a/src/game_interpreter_debug.cpp +++ b/src/game_interpreter_debug.cpp @@ -56,60 +56,26 @@ Debug::ParallelInterpreterStates Debug::ParallelInterpreterStates::GetCachedStat return { ev_ids, ce_ids, state_ev, state_ce }; } -std::vector Debug::CreateCallStack(const int owner_evt_id, const lcf::rpg::SaveEventExecState& state) { - std::vector items(state.stack.size()); +std::vector Debug::CreateCallStack(const lcf::rpg::SaveEventExecState& state) { + std::vector items; + items.reserve(state.stack.size()); for (int i = state.stack.size() - 1; i >= 0; i--) { - int evt_id = state.stack[i].event_id; - int page_id = 0; - if (state.stack[i].maniac_event_id > 0) { - evt_id = state.stack[i].maniac_event_id; - page_id = state.stack[i].maniac_event_page_id; - } - if (evt_id == 0 && i == 0) - evt_id = owner_evt_id; - - bool is_calling_ev_ce = false; - - //FIXME: There are some currently unimplemented SaveEventExecFrame fields introduced via the ManiacPatch which should be used to properly get event state information - if (evt_id == 0 && i > 0) { - auto& prev_frame = state.stack[i - 1]; - auto& com = prev_frame.commands[prev_frame.current_command - 1]; - if (com.code == 12330) { // CallEvent - if (com.parameters[0] == 0) { - is_calling_ev_ce = true; - evt_id = com.parameters[1]; - } else if (com.parameters[0] == 3 && Player::IsPatchManiac()) { - is_calling_ev_ce = true; - evt_id = Main_Data::game_variables->Get(com.parameters[1]); - } else if (com.parameters[0] == 4 && Player::IsPatchManiac()) { - is_calling_ev_ce = true; - evt_id = Main_Data::game_variables->GetIndirect(com.parameters[1]); - } - } - } - - auto item = Debug::CallStackItem(); - item.stack_item_no = i + 1; - item.is_ce = is_calling_ev_ce; - item.evt_id = evt_id; - item.page_id = page_id; - item.name = ""; - item.cmd_current = state.stack[i].current_command; - item.cmd_count = state.stack[i].commands.size(); - - if (item.is_ce) { - auto* ce = lcf::ReaderUtil::GetElement(lcf::Data::commonevents, item.evt_id); - if (ce) { - item.name = ToString(ce->name); - } - } else { - auto* ev = Game_Map::GetEvent(evt_id); - if (ev) { - //FIXME: map could have changed, but map_id isn't available - item.name = ToString(ev->GetName()); - } - } + auto& frame = state.stack[i]; + + bool map_has_changed = (frame.event_id == 0 && frame.maniac_event_id > 0); + + Debug::CallStackItem item = { + Game_Interpreter_Shared::EasyRpgExecutionType(frame), + Game_Interpreter_Shared::EasyRpgEventType(frame), + frame.maniac_event_id, + frame.maniac_event_page_id, + GetEventName(frame), + i + 1, //stack_item_no + frame.current_command, // cmd_current + frame.commands.size(), // cmd_count + map_has_changed + }; items.push_back(item); } @@ -117,6 +83,26 @@ std::vector Debug::CreateCallStack(const int owner_evt_id, return items; } +std::string Debug::GetEventName(const lcf::rpg::SaveEventExecFrame& frame) { + switch (Game_Interpreter_Shared::EasyRpgEventType(frame)) { + case InterpreterEventType::MapEvent: + if (auto* ev = Game_Map::GetEvent(frame.event_id)) { + return ToString(ev->GetName()); + } else if (frame.maniac_event_id > 0) { + return fmt::format("[(EV{:04d}) from another map..]", frame.maniac_event_id); + } + break; + case InterpreterEventType::CommonEvent: + if (auto* ce = lcf::ReaderUtil::GetElement(lcf::Data::commonevents, frame.maniac_event_id)) { + return ToString(ce->name); + } + break; + default: + break; + } + return ""; +} + std::string Debug::FormatEventName(Game_Character const& ch) { switch (ch.GetType()) { case Game_Character::Player: diff --git a/src/game_interpreter_debug.h b/src/game_interpreter_debug.h index 5b73d11bdc..24ff1384cc 100644 --- a/src/game_interpreter_debug.h +++ b/src/game_interpreter_debug.h @@ -57,13 +57,17 @@ namespace Debug { }; struct CallStackItem { - bool is_ce; + InterpreterExecutionType type_ex; + InterpreterEventType type_ev; int evt_id, page_id; std::string name; int stack_item_no, cmd_current, cmd_count; + bool map_has_changed; }; - std::vector CreateCallStack(const int owner_evt_id, const lcf::rpg::SaveEventExecState& state); + std::vector CreateCallStack(const lcf::rpg::SaveEventExecState& state); + + std::string GetEventName(const lcf::rpg::SaveEventExecFrame& frame); std::string FormatEventName(Game_Character const& ev); diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 061b200a4c..0c6404da54 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -136,6 +136,9 @@ namespace Game_Interpreter_Shared { ExecutionType ManiacExecutionType(lcf::rpg::SaveEventExecFrame const& frame); EventType ManiacEventType(lcf::rpg::SaveEventExecFrame const& frame); + + ExecutionType EasyRpgExecutionType(lcf::rpg::SaveEventExecFrame const& frame); + EventType EasyRpgEventType(lcf::rpg::SaveEventExecFrame const& frame); } inline bool Game_Interpreter_Shared::CheckOperator(int val, int val2, int op) { @@ -195,6 +198,15 @@ inline InterpreterEventType Game_Interpreter_Shared::ManiacEventType(lcf::rpg::S return InterpreterEventType::None; } +inline InterpreterExecutionType Game_Interpreter_Shared::EasyRpgExecutionType(lcf::rpg::SaveEventExecFrame const& frame) { + return static_cast(frame.maniac_event_info & 0xF); +} + +inline InterpreterEventType Game_Interpreter_Shared::EasyRpgEventType(lcf::rpg::SaveEventExecFrame const& frame) { + // Same as ManiacEventType, because no special new event types exist at the moment + return ManiacEventType(frame); +} + class Game_BaseInterpreterContext { public: virtual ~Game_BaseInterpreterContext() {} diff --git a/src/scene_debug.cpp b/src/scene_debug.cpp index eb31fdc273..ab759b2a2f 100644 --- a/src/scene_debug.cpp +++ b/src/scene_debug.cpp @@ -1215,7 +1215,7 @@ void Scene_Debug::UpdateArrows() { } void Scene_Debug::UpdateInterpreterWindow(int index) { - lcf::rpg::SaveEventExecState state; + lcf::rpg::SaveEventExecState state_display; std::string first_line = ""; bool valid = false; int evt_id = 0; @@ -1223,15 +1223,17 @@ void Scene_Debug::UpdateInterpreterWindow(int index) { auto& bg_states = state_interpreter.background_states; if (index == 1) { - state = Game_Interpreter::GetForegroundInterpreter().GetState(); + state_display = Game_Interpreter::GetForegroundInterpreter().GetState(); first_line = Game_Battle::IsBattleRunning() ? "Foreground (Battle)" : "Foreground (Map)"; valid = true; } else if (index <= bg_states.CountEventInterpreters()) { const auto& [evt_id, state] = bg_states.GetEventInterpreter(index - 1); first_line = Debug::FormatEventName(*Game_Map::GetEvent(evt_id)); + state_display = state; valid = true; } else if ((index - bg_states.CountEventInterpreters()) <= bg_states.CountCommonEventInterpreters()) { const auto& [ce_id, state] = bg_states.GetCommonEventInterpreter(index - bg_states.CountEventInterpreters() - 1); + state_display = state; for (auto& ce : Game_Map::GetCommonEvents()) { if (ce.GetId() == ce_id) { first_line = Debug::FormatEventName(ce); @@ -1244,10 +1246,10 @@ void Scene_Debug::UpdateInterpreterWindow(int index) { if (valid) { state_interpreter.selected_state = index; - interpreter_window->SetStackState(index > bg_states.CountEventInterpreters(), evt_id, first_line, state); + interpreter_window->SetStackState(first_line, state_display); } else { state_interpreter.selected_state = -1; - interpreter_window->SetStackState(0, 0, "", {}); + interpreter_window->SetStackState("", {}); } } diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index 7d312777b2..59af50b2ab 100644 --- a/src/window_interpreter.cpp +++ b/src/window_interpreter.cpp @@ -36,8 +36,8 @@ Window_Interpreter::~Window_Interpreter() { } -void Window_Interpreter::SetStackState(bool is_ce, int owner_evt_id, std::string interpreter_desc, lcf::rpg::SaveEventExecState state) { - this->display_item = { is_ce, owner_evt_id, interpreter_desc }; +void Window_Interpreter::SetStackState(std::string interpreter_desc, lcf::rpg::SaveEventExecState state) { + this->display_item = { interpreter_desc }; this->state = state; } @@ -46,10 +46,7 @@ void Window_Interpreter::Refresh() { int max_cmd_count = 0, max_evt_id = 10, max_page_id = 0; - stack_display_items = Debug::CreateCallStack(display_item.owner_evt_id, state); - if (stack_display_items.size() > 0 && this->display_item.is_ce) { - stack_display_items[0].is_ce = true; - } + stack_display_items = Debug::CreateCallStack(state); for (auto it = stack_display_items.begin(); it < stack_display_items.end(); ++it) { auto& item = *it; @@ -128,13 +125,33 @@ void Window_Interpreter::DrawStackLine(int index) { Debug::CallStackItem& item = stack_display_items[index]; contents->TextDraw(rect.x, rect.y, Font::ColorDisabled, fmt::format("[{:0" + std::to_string(digits_stackitemno) + "d}]", state.stack.size() - index)); - if (item.is_ce) { - contents->TextDraw(rect.x + (digits_stackitemno * 6) + 16, rect.y, Font::ColorDefault, fmt::format("CE{:0" + std::to_string(digits_evt_id) + "d}", item.evt_id)); - } else if (item.page_id > 0) { - contents->TextDraw(rect.x + (digits_stackitemno * 6) + 16, rect.y, Font::ColorDefault, fmt::format("EV{:0" + std::to_string(digits_evt_id) + "d}[{:0" + std::to_string(digits_page_id) + "d}]", item.evt_id, item.page_id)); - } else { - contents->TextDraw(rect.x + (digits_stackitemno * 6) + 16, rect.y, Font::ColorDefault, fmt::format("EV{:0" + std::to_string(digits_evt_id) + "d}", item.evt_id)); + + std::string formatted_id; + Font::SystemColor color = Font::ColorDefault; + + switch (item.type_ev) { + case InterpreterEventType::MapEvent: + if (item.page_id > 0) { + formatted_id = fmt::format("EV{:0" + std::to_string(digits_evt_id) + "d}[{:0" + std::to_string(digits_page_id) + "d}]", item.evt_id, item.page_id); + } else { + formatted_id = fmt::format("EV{:0" + std::to_string(digits_evt_id) + "d}", item.evt_id); + } + if (item.map_has_changed) { + color = Font::ColorKnockout; + } + break; + case InterpreterEventType::CommonEvent: + formatted_id = fmt::format("CE{:0" + std::to_string(digits_evt_id) + "d}", item.evt_id); + break; + case InterpreterEventType::BattleEvent: + formatted_id = fmt::format("BE{:0" + std::to_string(digits_evt_id) + "d}", item.evt_id); + break; + default: + formatted_id = fmt::format("{:0" + std::to_string(digits_evt_id + 2) + "d}", 0); + color = Font::ColorKnockout; + break; } + contents->TextDraw(rect.x + (digits_stackitemno * 6) + 16, rect.y, color, formatted_id); std::string name = item.name; int max_length = 28; diff --git a/src/window_interpreter.h b/src/window_interpreter.h index 438aa557ff..e484976788 100644 --- a/src/window_interpreter.h +++ b/src/window_interpreter.h @@ -31,7 +31,7 @@ class Window_Interpreter : public Window_Selectable { void Update() override; - void SetStackState(bool is_ce, int owner_evt_id, std::string interpreter_desc, lcf::rpg::SaveEventExecState state); + void SetStackState(std::string interpreter_desc, lcf::rpg::SaveEventExecState state); void Refresh(); bool IsValid(); @@ -41,8 +41,6 @@ class Window_Interpreter : public Window_Selectable { void DrawStackLine(int index); private: struct InterpDisplayItem { - bool is_ce; - int owner_evt_id; std::string desc; }; From 513dd4a47cd23401f0d59f431bc933c434f5a842 Mon Sep 17 00:00:00 2001 From: florianessl Date: Thu, 6 Feb 2025 21:36:35 +0100 Subject: [PATCH 08/10] Interpreter debug window: Extended view to display the initial event execution type of an interpreter stack (Action, Touch, Parallel, etc...) --- src/game_interpreter_shared.h | 21 +++++++++++++++++++++ src/window_interpreter.cpp | 6 +++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 0c6404da54..0b1c2cad9c 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -35,6 +35,12 @@ namespace Game_Interpreter_Shared { CommonEvent, BattleEvent }; + static constexpr auto kEventType = lcf::makeEnumTags( + "None", + "MapEvent", + "CommonEvent", + "BattleEvent" + ); enum class ExecutionType { /* @@ -59,6 +65,21 @@ namespace Game_Interpreter_Shared { Eval, DebugCall }; + static constexpr auto kExecutionType = lcf::makeEnumTags( + "Action", + "Touch", + "Collision", + "AutoStart", + "Parallel", + "Call", + "BattleStart", + "BattleParallel", + "---", + "---", + "DeathHandler", + "Eval", + "DebugCall" + ); /* * Indicates how the target of an interpreter operation (lvalue) should be evaluated. diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index 59af50b2ab..b631c60335 100644 --- a/src/window_interpreter.cpp +++ b/src/window_interpreter.cpp @@ -113,7 +113,11 @@ void Window_Interpreter::DrawDescriptionLines() { rect = GetItemRect(i++); contents->ClearRect(rect); - contents->TextDraw(rect.x, rect.y, Font::ColorDefault, "Stack Size: "); + auto str_ex_type = std::string(Game_Interpreter_Shared::kExecutionType.tag(static_cast(stack_display_items[0].type_ex))); + contents->TextDraw(rect.x, rect.y, Font::ColorDefault, "("); + contents->TextDraw(rect.x + 6, rect.y, Font::ColorHeal, str_ex_type); + contents->TextDraw(rect.x + 6 * (str_ex_type.length() + 1), rect.y, Font::ColorDefault, ")"); + contents->TextDraw(rect.x + rect.width / 2, rect.y, Font::ColorDefault, "Stack Size: "); contents->TextDraw(GetWidth() - 16, rect.y, Font::ColorCritical, std::to_string(state.stack.size()), Text::AlignRight); } From 0d3970925a9dfdd855a9ab1cdc26a28a0599791b Mon Sep 17 00:00:00 2001 From: florianessl Date: Fri, 7 Feb 2025 17:03:38 +0100 Subject: [PATCH 09/10] Minor: Defined "ExecutionType::ManiacHook" & clarified Push instances inside battle interpreter --- src/game_interpreter.h | 2 +- src/game_interpreter_battle.cpp | 6 +++--- src/game_interpreter_shared.h | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 4c12dc04fb..9736be0251 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -409,7 +409,7 @@ template inline void Game_Interpreter::Push(Game_CommonEvent* ev) { static_assert(type_ex == InterpreterExecutionType::AutoStart || type_ex == InterpreterExecutionType::Parallel || type_ex == InterpreterExecutionType::Call || type_ex == InterpreterExecutionType::DeathHandler - || type_ex == InterpreterExecutionType::DebugCall, "Unexpected ExecutionType for CommonEvent" + || type_ex == InterpreterExecutionType::DebugCall || type_ex == InterpreterExecutionType::ManiacHook, "Unexpected ExecutionType for CommonEvent" ); PushInternal(ev, type_ex); } diff --git a/src/game_interpreter_battle.cpp b/src/game_interpreter_battle.cpp index a94ecff0f4..a018b16512 100644 --- a/src/game_interpreter_battle.cpp +++ b/src/game_interpreter_battle.cpp @@ -213,7 +213,7 @@ int Game_Interpreter_Battle::ScheduleNextPage(lcf::rpg::TroopPageCondition::Flag continue; } Clear(); - Push(page.event_commands, 0); // FIXME: clarify type_src & type_ex for battle events + Push(page.event_commands, 0); executed[i] = true; return i + 1; } @@ -647,7 +647,7 @@ bool Game_Interpreter_Battle::ManiacBattleHook(ManiacBattleHookType hook_type, i } // pushes the common event to be run into the queue of events. - maniac_interpreter->Push(common_event); // FIXME: clarify type_src & type_ex for battle events + maniac_interpreter->Push(common_event); // pushes the change variable events into the interpreters // event queue, so we don't run into a race condition. @@ -685,7 +685,7 @@ bool Game_Interpreter_Battle::ManiacBattleHook(ManiacBattleHookType hook_type, i } // Push is actually "push_back", so this gets added before other events. - maniac_interpreter->Push(pre_commands, 0); // FIXME: clarify type_src & type_ex for battle events + maniac_interpreter->Push(pre_commands, 0); // Necessary to start the sub-event. maniac_interpreter->Update(); diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 0b1c2cad9c..943050cb55 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -63,7 +63,8 @@ namespace Game_Interpreter_Shared { DeathHandler = 10, /* Event code was dynamically evaluated. (ManiacCallCommand) */ Eval, - DebugCall + DebugCall, + ManiacHook }; static constexpr auto kExecutionType = lcf::makeEnumTags( "Action", From b987a848252cd77e5c62fee396b2b91b85727a3f Mon Sep 17 00:00:00 2001 From: florianessl Date: Tue, 11 Feb 2025 15:31:12 +0100 Subject: [PATCH 10/10] Amended missing tag "ManiacHook" to Debug::kExecutionType & added some static_asserts so it don't happen again --- src/filefinder.h | 6 ++++-- src/game_interpreter_shared.h | 11 ++++++++--- src/input_buttons.h | 12 ++++++------ src/rtp.h | 4 +++- src/window_gamelist.cpp | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/filefinder.h b/src/filefinder.h index ec24d0f6a6..214f3065ef 100644 --- a/src/filefinder.h +++ b/src/filefinder.h @@ -50,7 +50,7 @@ namespace FileFinder { * Type of the project. Used to differentiate between supported games (2kX or EasyRPG) * and known but unsupported (i.e. newer RPG Makers). */ - enum ProjectType { + enum class ProjectType { Unknown, // 2kX or EasyRPG Supported, @@ -62,7 +62,8 @@ namespace FileFinder { WolfRpgEditor, Encrypted2k3Maniacs, RpgMaker95, - SimRpgMaker95 + SimRpgMaker95, + LAST }; constexpr auto kProjectType = lcf::makeEnumTags( @@ -77,6 +78,7 @@ namespace FileFinder { "RPG Maker 95", "Sim RPG Maker 95" ); + static_assert(kProjectType.size() == static_cast(ProjectType::LAST)); /** * Helper struct combining the project's directory and its type (used by Game Browser) diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 943050cb55..d4eaa6f921 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -33,7 +33,8 @@ namespace Game_Interpreter_Shared { None = 0, MapEvent, CommonEvent, - BattleEvent + BattleEvent, + LAST }; static constexpr auto kEventType = lcf::makeEnumTags( "None", @@ -41,6 +42,7 @@ namespace Game_Interpreter_Shared { "CommonEvent", "BattleEvent" ); + static_assert(kEventType.size() == static_cast(EventType::LAST)); enum class ExecutionType { /* @@ -64,7 +66,8 @@ namespace Game_Interpreter_Shared { /* Event code was dynamically evaluated. (ManiacCallCommand) */ Eval, DebugCall, - ManiacHook + ManiacHook, + LAST }; static constexpr auto kExecutionType = lcf::makeEnumTags( "Action", @@ -79,8 +82,10 @@ namespace Game_Interpreter_Shared { "---", "DeathHandler", "Eval", - "DebugCall" + "DebugCall", + "ManiacHook" ); + static_assert(kExecutionType.size() == static_cast(ExecutionType::LAST)); /* * Indicates how the target of an interpreter operation (lvalue) should be evaluated. diff --git a/src/input_buttons.h b/src/input_buttons.h index e9dce4cb2d..6524062927 100644 --- a/src/input_buttons.h +++ b/src/input_buttons.h @@ -130,8 +130,8 @@ namespace Input { "FAST_FORWARD_A", "FAST_FORWARD_B", "TOGGLE_FULLSCREEN", - "TOGGLE_ZOOM", - "BUTTON_COUNT"); + "TOGGLE_ZOOM"); + static_assert(kInputButtonNames.size() == static_cast(BUTTON_COUNT)); constexpr auto kInputButtonHelp = lcf::makeEnumTags( "Up Direction", @@ -175,8 +175,8 @@ namespace Input { "Run the game at x{} speed", "Run the game at x{} speed", "Toggle Fullscreen mode", - "Toggle Window Zoom level", - "Total Button Count"); + "Toggle Window Zoom level"); + static_assert(kInputButtonHelp.size() == static_cast(BUTTON_COUNT)); /** * Return true if the given button is a system button. @@ -241,8 +241,8 @@ namespace Input { "RIGHT", "UPLEFT", "UP", - "UPRIGHT", - "NUM_DIRECTIONS"); + "UPRIGHT"); + static_assert(kInputDirectionNames.size() == static_cast(NUM_DIRECTIONS)); }; using ButtonMappingArray = FlatUniqueMultiMap; diff --git a/src/rtp.h b/src/rtp.h index b062025c48..d9122b27d8 100644 --- a/src/rtp.h +++ b/src/rtp.h @@ -50,7 +50,8 @@ namespace RTP { RPG2003_VladRussian, RPG2003_RpgUniverseSpanishPortuguese, RPG2003_Korean, - RPG2003_OfficialTraditionalChinese + RPG2003_OfficialTraditionalChinese, + LAST }; constexpr auto kTypes = lcf::makeEnumTags( @@ -66,6 +67,7 @@ namespace RTP { "Korean Translation", "Official Traditional Chinese" ); + static_assert(kTypes.size() == static_cast(Type::LAST)); struct RtpHitInfo { RTP::Type type; diff --git a/src/window_gamelist.cpp b/src/window_gamelist.cpp index 6fd0bb17d5..a7a19a8919 100644 --- a/src/window_gamelist.cpp +++ b/src/window_gamelist.cpp @@ -118,7 +118,7 @@ void Window_GameList::DrawItem(int index) { #ifndef USE_CUSTOM_FILEBUF auto color = Font::ColorDefault; - if (ge.type == FileFinder::Unknown) { + if (ge.type == FileFinder::ProjectType::Unknown) { color = Font::ColorHeal; } else if (ge.type > FileFinder::ProjectType::Supported) { color = Font::ColorKnockout;