From b137f2dea8c0d6937ad1002b9a5c4b73f7f449c7 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 25 May 2021 22:49:24 -0400 Subject: [PATCH] Initial work on entity introspection --- Runtime/CMakeLists.txt | 2 +- Runtime/ImGuiConsole.cpp | 57 ++++++++++++++++++++------------- Runtime/ImGuiEntitySupport.cpp | 36 +++++++++++++++++++++ Runtime/ImGuiEntitySupport.hpp | 1 + Runtime/World/CActor.hpp | 2 ++ Runtime/World/CAi.hpp | 2 ++ Runtime/World/CEntity.hpp | 16 +++++++++ Runtime/World/CPatterned.hpp | 2 ++ Runtime/World/CPhysicsActor.hpp | 2 ++ Runtime/World/CPlayer.cpp | 2 +- Runtime/World/CPlayer.hpp | 2 ++ 11 files changed, 99 insertions(+), 25 deletions(-) create mode 100644 Runtime/ImGuiEntitySupport.cpp create mode 100644 Runtime/ImGuiEntitySupport.hpp diff --git a/Runtime/CMakeLists.txt b/Runtime/CMakeLists.txt index 3e7f32c44..13c709792 100644 --- a/Runtime/CMakeLists.txt +++ b/Runtime/CMakeLists.txt @@ -204,7 +204,7 @@ elseif(UNIX) set(PLAT_LIBS rt) endif() -add_executable(metaforce CMain.cpp ${PLAT_SRCS} ImGuiConsole.hpp ImGuiConsole.cpp) +add_executable(metaforce CMain.cpp ${PLAT_SRCS} ImGuiConsole.hpp ImGuiConsole.cpp ImGuiEntitySupport.hpp ImGuiEntitySupport.cpp) target_link_libraries(metaforce PUBLIC RuntimeCommon ${PLAT_LIBS}) if(COMMAND add_sanitizers) diff --git a/Runtime/ImGuiConsole.cpp b/Runtime/ImGuiConsole.cpp index 86e06ef60..4fd909560 100644 --- a/Runtime/ImGuiConsole.cpp +++ b/Runtime/ImGuiConsole.cpp @@ -27,7 +27,7 @@ static std::wstring_convertGetString(idx)); } -static const std::vector> listWorlds() { +static const std::vector> ListWorlds() { std::vector> worlds; for (const auto& pak : g_ResFactory->GetResLoader()->GetPaks()) { if (!pak->IsWorldPak()) { @@ -54,7 +54,7 @@ static const std::vector> listWorlds() { return worlds; } -static const std::vector> listAreas(CAssetId worldId) { +static const std::vector> ListAreas(CAssetId worldId) { std::vector> areas; const auto& world = dummyWorlds[worldId]; for (int i = 0; i < world->IGetAreaCount(); ++i) { @@ -74,7 +74,7 @@ static const std::vector> listAreas(CAssetId wor return areas; } -static void warp(const CAssetId worldId, TAreaId aId) { +static void Warp(const CAssetId worldId, TAreaId aId) { g_GameState->SetCurrentWorldId(worldId); g_GameState->GetWorldTransitionManager()->DisableTransition(); if (aId >= g_GameState->CurrentWorldState().GetLayerState()->GetAreaCount()) { @@ -82,8 +82,12 @@ static void warp(const CAssetId worldId, TAreaId aId) { } g_GameState->CurrentWorldState().SetAreaId(aId); g_Main->SetFlowState(EFlowState::None); - g_StateManager->SetWarping(true); - g_StateManager->SetShouldQuitGame(true); + if (g_StateManager != nullptr) { + g_StateManager->SetWarping(true); + g_StateManager->SetShouldQuitGame(true); + } else { + // TODO warp from menu? + } } static void ShowMenuGame() { @@ -92,12 +96,13 @@ static void ShowMenuGame() { if (ImGui::MenuItem("Paused", nullptr, &paused)) { g_Main->SetPaused(paused); } - if (ImGui::BeginMenu("Warp", g_ResFactory != nullptr && g_ResFactory->GetResLoader() != nullptr)) { - for (const auto& world : listWorlds()) { + if (ImGui::BeginMenu("Warp", g_StateManager != nullptr && g_ResFactory != nullptr && + g_ResFactory->GetResLoader() != nullptr)) { + for (const auto& world : ListWorlds()) { if (ImGui::BeginMenu(world.first.c_str())) { - for (const auto& area : listAreas(world.second)) { + for (const auto& area : ListAreas(world.second)) { if (ImGui::MenuItem(area.first.c_str())) { - warp(world.second, area.second); + Warp(world.second, area.second); } } ImGui::EndMenu(); @@ -112,13 +117,14 @@ static void ShowMenuGame() { static void ShowInspectWindow(bool* isOpen) { if (ImGui::Begin("Inspect", isOpen)) { - if (ImGui::BeginTable("Entities", 3, + if (ImGui::BeginTable("Entities", 4, ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ScrollY)) { ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_PreferSortAscending | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0, 'id'); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 0, 'type'); ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 0, 'name'); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupScrollFreeze(0, 1); @@ -143,6 +149,11 @@ static void ShowInspectWindow(bool* isOpen) { int compare = a->GetName().compare(b->GetName()); return specs.SortDirection == ImGuiSortDirection_Ascending ? compare < 0 : compare > 0; }); + } else if (specs.ColumnUserID == 'type') { + std::sort(items.begin(), items.end(), [&](CEntity* a, CEntity* b) { + int compare = a->ImGuiType().compare(b->ImGuiType()); + return specs.SortDirection == ImGuiSortDirection_Ascending ? compare < 0 : compare > 0; + }); } } } @@ -153,6 +164,9 @@ static void ShowInspectWindow(bool* isOpen) { if (ImGui::TableNextColumn()) { ImGui::Text("%x", uid.Value()); } + if (ImGui::TableNextColumn()) { + ImGui::Text("%s", item->ImGuiType().data()); + } if (ImGui::TableNextColumn()) { ImGui::Text("%s", item->GetName().data()); } @@ -177,12 +191,7 @@ static bool showEntityInfoWindow(TUniqueId uid) { } auto name = fmt::format(FMT_STRING("{}##{:x}"), !ent->GetName().empty() ? ent->GetName() : "Entity", uid.Value()); if (ImGui::Begin(name.c_str(), &open, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("ID: %x", uid.Value()); - ImGui::Text("Name: %s", ent->GetName().data()); - if (const TCastToPtr act = ent) { - const zeus::CVector3f& pos = act->GetTranslation(); - ImGui::Text("Position: %f, %f, %f", pos.x(), pos.y(), pos.z()); - } + ent->ImGuiInspect(); } ImGui::End(); return open; @@ -191,6 +200,7 @@ static bool showEntityInfoWindow(TUniqueId uid) { static void ShowAppMainMenuBar() { static bool showInspectWindow = false; static bool showDemoWindow = false; + bool canInspect = g_StateManager != nullptr && g_StateManager->GetObjectList(); if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("Game")) { ShowMenuGame(); @@ -198,18 +208,17 @@ static void ShowAppMainMenuBar() { } ImGui::Spacing(); if (ImGui::BeginMenu("Tools")) { - ImGui::MenuItem("Inspect", nullptr, &showInspectWindow, - g_StateManager != nullptr && g_StateManager->GetObjectList()); + ImGui::MenuItem("Inspect", nullptr, &showInspectWindow, canInspect); ImGui::Separator(); ImGui::MenuItem("Demo", nullptr, &showDemoWindow); ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } - if (showInspectWindow) { - ShowInspectWindow(&showInspectWindow); - } - { + if (canInspect) { + if (showInspectWindow) { + ShowInspectWindow(&showInspectWindow); + } auto iter = inspectingEntities.begin(); while (iter != inspectingEntities.end()) { if (!showEntityInfoWindow(*iter)) { @@ -218,6 +227,8 @@ static void ShowAppMainMenuBar() { iter++; } } + } else { + inspectingEntities.clear(); } if (showDemoWindow) { ImGui::ShowDemoWindow(&showDemoWindow); @@ -230,4 +241,4 @@ ImGuiConsole::~ImGuiConsole() { dummyWorlds.clear(); stringTables.clear(); } -} // namespace metaforce \ No newline at end of file +} // namespace metaforce diff --git a/Runtime/ImGuiEntitySupport.cpp b/Runtime/ImGuiEntitySupport.cpp new file mode 100644 index 000000000..1dd9ca189 --- /dev/null +++ b/Runtime/ImGuiEntitySupport.cpp @@ -0,0 +1,36 @@ +#include "Runtime/World/CEntity.hpp" +#include "Runtime/World/CActor.hpp" +#include "Runtime/World/CAi.hpp" +#include "Runtime/World/CPatterned.hpp" +#include "Runtime/World/CPlayer.hpp" + +#include "imgui.h" + +#define IMGUI_ENTITY_INSPECT(CLS, PARENT, NAME, BLOCK) \ + std::string_view CLS::ImGuiType() { return #NAME; } \ + void CLS::ImGuiInspect() { \ + PARENT::ImGuiInspect(); \ + if (ImGui::CollapsingHeader(#NAME)) \ + BLOCK \ + } + +namespace metaforce { +std::string_view CEntity::ImGuiType() { return "Entity"; } + +void CEntity::ImGuiInspect() { + if (ImGui::CollapsingHeader("Entity", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Text("ID: %x", x8_uid.Value()); + ImGui::Text("Name: %s", x10_name.c_str()); + } +} + +IMGUI_ENTITY_INSPECT(CActor, CEntity, Actor, { + const zeus::CVector3f& pos = GetTranslation(); + ImGui::Text("Position: %f, %f, %f", pos.x(), pos.y(), pos.z()); +}) +IMGUI_ENTITY_INSPECT(CPhysicsActor, CActor, Physics Actor, {}) +IMGUI_ENTITY_INSPECT(CAi, CPhysicsActor, AI, {}) +IMGUI_ENTITY_INSPECT(CPatterned, CAi, Patterned, {}) + +IMGUI_ENTITY_INSPECT(CPlayer, CPhysicsActor, Player, {}) +} // namespace metaforce diff --git a/Runtime/ImGuiEntitySupport.hpp b/Runtime/ImGuiEntitySupport.hpp new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/Runtime/ImGuiEntitySupport.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/Runtime/World/CActor.hpp b/Runtime/World/CActor.hpp index 8797c428b..a894c8051 100644 --- a/Runtime/World/CActor.hpp +++ b/Runtime/World/CActor.hpp @@ -195,5 +195,7 @@ public: void MoveScannableObjectInfoToActor(CActor*, CStateManager&); const zeus::CAABox& GetRenderBounds() const { return x9c_renderBounds; } void SetNotInSortedLists(bool notIn) { xe4_27_notInSortedLists = notIn; } + + IMGUI_ENTITY_PROTOTYPES }; } // namespace metaforce diff --git a/Runtime/World/CAi.hpp b/Runtime/World/CAi.hpp index 6770076c0..e7baa1f58 100644 --- a/Runtime/World/CAi.hpp +++ b/Runtime/World/CAi.hpp @@ -185,6 +185,8 @@ public: virtual bool FixedRandom(CStateManager&, float) { return false; } virtual bool IsDizzy(CStateManager&, float) { return false; } virtual bool ShouldCallForBackup(CStateManager&, float) { return false; } + + IMGUI_ENTITY_PROTOTYPES }; } // namespace metaforce diff --git a/Runtime/World/CEntity.hpp b/Runtime/World/CEntity.hpp index 26e14543d..971446416 100644 --- a/Runtime/World/CEntity.hpp +++ b/Runtime/World/CEntity.hpp @@ -7,6 +7,18 @@ #include "Runtime/World/CEntityInfo.hpp" #include "Runtime/World/ScriptObjectSupport.hpp" +#ifndef ENABLE_IMGUI +#define ENABLE_IMGUI 1 +#endif + +#if ENABLE_IMGUI +#define IMGUI_ENTITY_PROTOTYPES \ + std::string_view ImGuiType() override; \ + void ImGuiInspect() override; +#else +#define IMGUI_ENTITY_PROTOTYPES +#endif + namespace metaforce { class CStateManager; class IVisitor; @@ -36,6 +48,10 @@ public: virtual void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId objId, CStateManager& stateMgr); virtual void SetActive(bool active) { x30_24_active = active; } + // Debugging utilities + virtual std::string_view ImGuiType(); + virtual void ImGuiInspect(); + bool GetActive() const { return x30_24_active; } void ToggleActive() { x30_24_active ^= 1; } diff --git a/Runtime/World/CPatterned.hpp b/Runtime/World/CPatterned.hpp index ad4f2e09c..5e5d4f84a 100644 --- a/Runtime/World/CPatterned.hpp +++ b/Runtime/World/CPatterned.hpp @@ -419,5 +419,7 @@ public: static void Initialize(); // endregion + + IMGUI_ENTITY_PROTOTYPES }; } // namespace metaforce diff --git a/Runtime/World/CPhysicsActor.hpp b/Runtime/World/CPhysicsActor.hpp index b4daaba05..03d0732da 100644 --- a/Runtime/World/CPhysicsActor.hpp +++ b/Runtime/World/CPhysicsActor.hpp @@ -208,5 +208,7 @@ public: void UseCollisionImpulses(); static constexpr float GravityConstant() { return 9.81f * 2.5f; } /* 9.81 m/s ^ 2 is normal acceleration under earth gravity, Tallon 4 is 2.5 times that */ + + IMGUI_ENTITY_PROTOTYPES }; } // namespace metaforce diff --git a/Runtime/World/CPlayer.cpp b/Runtime/World/CPlayer.cpp index 7fcbfc570..8f42b93c0 100644 --- a/Runtime/World/CPlayer.cpp +++ b/Runtime/World/CPlayer.cpp @@ -1272,7 +1272,7 @@ static CAssetId UpdatePersistentScanPercent(u32 prevLogScans, u32 logScans, u32 if (scanPercentProgStep > prevScanPercentProgStep) { const char* const messageResBase = UnlockMessageResBases[zeus::clamp(0, scanPercentProgStep - 1, 1)]; - const auto message = std::string(messageResBase).append(1, firstTime ? '1' : '2'); + const auto message = std::string(messageResBase).append(1, firstTime ? '1' : '2'); const auto* const id = g_ResFactory->GetResourceIdByName(message); if (id != nullptr) { return id->id; diff --git a/Runtime/World/CPlayer.hpp b/Runtime/World/CPlayer.hpp index ea30fe013..cd1ab84eb 100644 --- a/Runtime/World/CPlayer.hpp +++ b/Runtime/World/CPlayer.hpp @@ -622,5 +622,7 @@ public: bool IsInWaterMovement() const { return x9c4_31_inWaterMovement; } void SetNoDamageLoopSfx(bool val) { x9c7_24_noDamageLoopSfx = val; } void SetAccelerationChangeTimer(float time) { x2d4_accelerationChangeTimer = time; } + + IMGUI_ENTITY_PROTOTYPES }; } // namespace metaforce