Initial work on entity introspection

This commit is contained in:
Luke Street 2021-05-25 22:49:24 -04:00
parent bae1d7d59f
commit 0841bd4ab2
11 changed files with 99 additions and 25 deletions

View File

@ -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)

View File

@ -27,7 +27,7 @@ static std::wstring_convert<deletable_facet<std::codecvt<char16_t, char, std::mb
std::string readUtf8String(CStringTable* tbl, int idx) { return conv16.to_bytes(tbl->GetString(idx)); }
static const std::vector<std::pair<std::string, CAssetId>> listWorlds() {
static const std::vector<std::pair<std::string, CAssetId>> ListWorlds() {
std::vector<std::pair<std::string, CAssetId>> worlds;
for (const auto& pak : g_ResFactory->GetResLoader()->GetPaks()) {
if (!pak->IsWorldPak()) {
@ -54,7 +54,7 @@ static const std::vector<std::pair<std::string, CAssetId>> listWorlds() {
return worlds;
}
static const std::vector<std::pair<std::string, TAreaId>> listAreas(CAssetId worldId) {
static const std::vector<std::pair<std::string, TAreaId>> ListAreas(CAssetId worldId) {
std::vector<std::pair<std::string, TAreaId>> areas;
const auto& world = dummyWorlds[worldId];
for (int i = 0; i < world->IGetAreaCount(); ++i) {
@ -74,7 +74,7 @@ static const std::vector<std::pair<std::string, TAreaId>> 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<CActor> 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
} // namespace metaforce

View File

@ -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

View File

@ -0,0 +1 @@
#pragma once

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -419,5 +419,7 @@ public:
static void Initialize();
// endregion
IMGUI_ENTITY_PROTOTYPES
};
} // namespace metaforce

View File

@ -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

View File

@ -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;

View File

@ -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