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/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_commonevent.cpp b/src/game_commonevent.cpp index 1f74ffe7d1..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); + interpreter->Push(this); } diff --git a/src/game_commonevent.h b/src/game_commonevent.h index f8069a3a0d..11d0518bb0 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 @@ -120,7 +121,7 @@ class Game_CommonEvent { /** Interpreter for parallel common events. */ std::unique_ptr interpreter; - friend class Scene_Debug; + friend class Game_Interpreter_Inspector; }; #endif diff --git a/src/game_event.cpp b/src/game_event.cpp index 62c78757a3..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); + interpreter->Push(this); } interpreter->Update(!resume_async); diff --git a/src/game_event.h b/src/game_event.h index 94139a7a91..9a96a00412 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" @@ -218,7 +219,7 @@ class Game_Event : public Game_EventBase { const lcf::rpg::EventPage* page = nullptr; std::unique_ptr interpreter; - friend class Scene_Debug; + 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..456edf409b 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -100,10 +100,10 @@ bool Game_Interpreter::IsRunning() const { } // Setup. -void Game_Interpreter::Push( +void Game_Interpreter::PushInternal( + 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) << 4); + } + if (_state.stack.empty() && main_flag && !Game_Battle::IsBattleRunning()) { Main_Data::game_system->ClearMessageFace(); Main_Data::game_player->SetMenuCalling(false); @@ -529,16 +542,22 @@ 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::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, bool triggered_by_decision_key) { - Push(page->event_commands, ev->GetId(), triggered_by_decision_key, page->ID); +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) { - Push(ev->GetList(), 0, false); +void Game_Interpreter::PushInternal(Game_CommonEvent* ev, ExecutionType ex_type) { + PushInternal({ ex_type, EventType::CommonEvent }, ev->GetList(), ev->GetId()); } bool Game_Interpreter::CheckGameOver() { @@ -3921,7 +3940,7 @@ bool Game_Interpreter::CommandCallEvent(lcf::rpg::EventCommand const& com) { // return true; } - Push(common_event); + Push(common_event); return true; } @@ -3949,7 +3968,7 @@ bool Game_Interpreter::CommandCallEvent(lcf::rpg::EventCommand const& com) { // return true; } - Push(page->event_commands, event->GetId(), false, page->ID); + Push(page->event_commands, event->GetId(), page->ID); return true; } @@ -4117,8 +4136,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()); @@ -5320,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({ cmd }, GetCurrentEventId(), false); //FIXME: add some new flag, so the interpreter debug view (window_interpreter) can differentiate this frame from normal ones + Push({ cmd }, GetCurrentEventId(), 0); return true; } @@ -5508,3 +5549,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..9736be0251 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 */ @@ -64,14 +67,20 @@ class Game_Interpreter : public Game_BaseInterpreterContext void Update(bool reset_loop_count=true); + template void Push( - std::vector _list, - int _event_id, - bool started_by_decision_key = false, - int event_page_id = 0 + std::vector _list, + int _event_id, + int event_page_id = 0 ); + + template void Push(Game_Event* ev); - void Push(Game_Event* ev, const lcf::rpg::EventPage* page, bool triggered_by_decision_key); + + template + void Push(Game_Event* ev, const lcf::rpg::EventPage* page); + + template void Push(Game_CommonEvent* ev); void InputButton(); @@ -348,9 +357,63 @@ class Game_Interpreter : public Game_BaseInterpreterContext KeyInputState _keyinput; AsyncOp _async_op = {}; - friend class Scene_Debug; + 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; }; +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); +}; + +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 || type_ex == InterpreterExecutionType::ManiacHook, "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 da2ec95d08..a018b16512 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(page.event_commands, 0); 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); 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); // 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(pre_commands, 0); // Necessary to start the sub-event. maniac_interpreter->Update(); diff --git a/src/game_interpreter_debug.cpp b/src/game_interpreter_debug.cpp new file mode 100644 index 0000000000..0be5d2872c --- /dev/null +++ b/src/game_interpreter_debug.cpp @@ -0,0 +1,135 @@ +/* + * 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() { + Game_Interpreter_Inspector inspector; + + 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 (!inspector.IsInActiveExcecution(ev, true)) { + continue; + } + + ev_ids.emplace_back(ev.GetId()); + state_ev.emplace_back(inspector.GetExecState(ev)); + } + for (auto& ce : Game_Map::GetCommonEvents()) { + 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 + } + + return { ev_ids, ce_ids, state_ev, state_ce }; +} + +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--) { + 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); + } + + 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: + 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 ""; +} + +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 new file mode 100644 index 0000000000..24ff1384cc --- /dev/null +++ b/src/game_interpreter_debug.h @@ -0,0 +1,77 @@ +/* + * 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 { + 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 lcf::rpg::SaveEventExecState& state); + + std::string GetEventName(const lcf::rpg::SaveEventExecFrame& frame); + + std::string FormatEventName(Game_Character const& ev); + + std::string FormatEventName(Game_CommonEvent const& ce); +} + +#endif diff --git a/src/game_interpreter_shared.h b/src/game_interpreter_shared.h index 42c27990de..d4eaa6f921 100644 --- a/src/game_interpreter_shared.h +++ b/src/game_interpreter_shared.h @@ -29,6 +29,64 @@ class Game_BaseInterpreterContext; namespace Game_Interpreter_Shared { + enum class EventType { + None = 0, + MapEvent, + CommonEvent, + BattleEvent, + LAST + }; + static constexpr auto kEventType = lcf::makeEnumTags( + "None", + "MapEvent", + "CommonEvent", + "BattleEvent" + ); + static_assert(kEventType.size() == static_cast(EventType::LAST)); + + 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, + ManiacHook, + LAST + }; + static constexpr auto kExecutionType = lcf::makeEnumTags( + "Action", + "Touch", + "Collision", + "AutoStart", + "Parallel", + "Call", + "BattleStart", + "BattleParallel", + "---", + "---", + "DeathHandler", + "Eval", + "DebugCall", + "ManiacHook" + ); + static_assert(kExecutionType.size() == static_cast(ExecutionType::LAST)); + /* * Indicates how the target of an interpreter operation (lvalue) should be evaluated. */ @@ -102,6 +160,12 @@ 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); + + 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) { @@ -140,6 +204,36 @@ 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; +} + +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/game_map.cpp b/src/game_map.cpp index 5180fdd437..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); + interp.Push(run_ce); } 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); + } else { + switch (run_ev->GetTrigger()) { + case lcf::rpg::EventPage::Trigger_touched: + interp.Push(run_ev); + break; + case lcf::rpg::EventPage::Trigger_collision: + interp.Push(run_ev); + break; + case lcf::rpg::EventPage::Trigger_auto_start: + interp.Push(run_ev); + break; + case lcf::rpg::EventPage::Trigger_action: + default: + interp.Push(run_ev); + 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); } auto tt = Game_Battle::GetDeathHandlerTeleport(); 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/scene_debug.cpp b/src/scene_debug.cpp index f172d53580..ab759b2a2f 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())); + 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++; } - 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]; + 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_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: @@ -1123,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); 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); Scene::PopUntil(Scene::Map); Output::Debug("Debug Scene Forced execution of common event {} on the map foreground interpreter.", ce.GetIndex()); } @@ -1152,11 +1153,11 @@ void Scene_Debug::DoCallMapEvent() { } if (Game_Battle::IsBattleRunning()) { - Game_Battle::GetInterpreter().Push(me, page, false); + 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, false); + 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); } @@ -1180,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(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() { @@ -1213,51 +1214,29 @@ 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; + lcf::rpg::SaveEventExecState state_display; 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(); + state_display = 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]; - first_line = fmt::format("EV{:04d}: {}", evt_id, Game_Map::GetEvent(evt_id)->GetName()); + } 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 - 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()) { + const auto& [ce_id, state] = bg_states.GetCommonEventInterpreter(index - bg_states.CountEventInterpreters() - 1); + state_display = state; 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; @@ -1267,36 +1246,39 @@ 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(first_line, state_display); } else { state_interpreter.selected_state = -1; - interpreter_window->SetStackState(0, 0, "", {}); + interpreter_window->SetStackState("", {}); } } -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 <= 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; + auto const& state = inspector.GetExecState(*ev); 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; + 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 711bcc9097..c5a24b01db 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" @@ -190,13 +191,9 @@ class Scene_Debug : public Scene { bool interpreter_states_cached = false; void UpdateInterpreterWindow(int index); - lcf::rpg::SaveEventExecFrame& GetSelectedInterpreterFrameFromUiState() const; - void CacheBackgroundInterpreterStates(); + lcf::rpg::SaveEventExecFrame const& GetSelectedInterpreterFrameFromUiState() const; 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_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; diff --git a/src/window_interpreter.cpp b/src/window_interpreter.cpp index 1c59b8a9c5..b631c60335 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,66 +46,17 @@ 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()); - } - } + stack_display_items = Debug::CreateCallStack(state); - if (static_cast(state.stack[i].commands.size()) > max_cmd_count) - max_cmd_count = state.stack[i].commands.size(); + for (auto it = stack_display_items.begin(); it < stack_display_items.end(); ++it) { + auto& item = *it; - stack_display_items.push_back(item); + 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; @@ -162,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); } @@ -171,16 +126,36 @@ 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) { - 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 69fd1b0877..e484976788 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" @@ -30,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(); @@ -40,17 +41,9 @@ class Window_Interpreter : public Window_Selectable { void DrawStackLine(int index); private: struct InterpDisplayItem { - bool is_ce; - int owner_evt_id; 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 +53,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