#include "ImGuiConsole.hpp" #include "CStateManager.hpp" #include "GameGlobalObjects.hpp" #include "MP1/MP1.hpp" #include "imgui.h" #include "TCastTo.hpp" // Generated file, do not modify include path namespace metaforce { std::set ImGuiConsole::inspectingEntities; static std::unordered_map> dummyWorlds; static std::unordered_map> stringTables; // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert template struct deletable_facet : Facet { template deletable_facet(Args&&... args) : Facet(std::forward(args)...) {} ~deletable_facet() {} }; static std::wstring_convert>, char16_t> conv16; std::string readUtf8String(CStringTable* tbl, int idx) { return conv16.to_bytes(tbl->GetString(idx)); } static const std::vector> ListWorlds() { std::vector> worlds; for (const auto& pak : g_ResFactory->GetResLoader()->GetPaks()) { if (!pak->IsWorldPak()) { continue; } CAssetId worldId = pak->GetMLVLId(); if (!dummyWorlds.contains(worldId)) { dummyWorlds[worldId] = std::make_unique(worldId, false); } auto& world = dummyWorlds[worldId]; bool complete = world->ICheckWorldComplete(); if (!complete) { continue; } CAssetId stringId = world->IGetStringTableAssetId(); if (!stringId.IsValid()) { continue; } if (!stringTables.contains(stringId)) { stringTables[stringId] = g_SimplePool->GetObj(SObjectTag{SBIG('STRG'), stringId}); } worlds.emplace_back(readUtf8String(stringTables[stringId].GetObj(), 0), worldId); } return worlds; } static const std::vector> ListAreas(CAssetId worldId) { std::vector> areas; const auto& world = dummyWorlds[worldId]; for (int i = 0; i < world->IGetAreaCount(); ++i) { const auto* area = world->IGetAreaAlways(i); if (area == nullptr) { continue; } CAssetId stringId = area->IGetStringTableAssetId(); if (!stringId.IsValid()) { continue; } if (!stringTables.contains(stringId)) { stringTables[stringId] = g_SimplePool->GetObj(SObjectTag{SBIG('STRG'), stringId}); } areas.emplace_back(readUtf8String(stringTables[stringId].GetObj(), 0), TAreaId{i}); } return areas; } static void Warp(const CAssetId worldId, TAreaId aId) { g_GameState->SetCurrentWorldId(worldId); g_GameState->GetWorldTransitionManager()->DisableTransition(); if (aId >= g_GameState->CurrentWorldState().GetLayerState()->GetAreaCount()) { aId = 0; } g_GameState->CurrentWorldState().SetAreaId(aId); g_Main->SetFlowState(EFlowState::None); if (g_StateManager != nullptr) { g_StateManager->SetWarping(true); g_StateManager->SetShouldQuitGame(true); } else { // TODO warp from menu? } } static bool stepFrame = false; static void ShowMenuGame() { static bool paused; paused = g_Main->IsPaused(); if (ImGui::MenuItem("Paused", nullptr, &paused)) { g_Main->SetPaused(paused); } if (ImGui::MenuItem("Step", nullptr, &stepFrame, paused)) { g_Main->SetPaused(false); } 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)) { if (ImGui::MenuItem(area.first.c_str())) { Warp(world.second, area.second); } } ImGui::EndMenu(); } } ImGui::EndMenu(); } if (ImGui::MenuItem("Quit", "Alt+F4")) { g_Main->Quit(); } } void ImGuiStringViewText(std::string_view text) { ImGui::TextUnformatted(text.begin(), text.end()); } static void LerpActorColor(CActor* act) { act->m_debugAddColorTime += 1.f / 60.f; float lerp = act->m_debugAddColorTime; if (lerp > 2.f) { lerp = 0.f; act->m_debugAddColorTime = 0.f; } else if (lerp > 1.f) { lerp = 2.f - lerp; } act->m_debugAddColor = zeus::CColor::lerp(zeus::skClear, zeus::skBlue, lerp); } static void ShowInspectWindow(bool* isOpen) { if (ImGui::Begin("Inspect", isOpen)) { CObjectList& list = *g_StateManager->GetObjectList(); ImGui::Text("Objects: %d / 1024", list.size()); if (ImGui::Button("Deselect all")) { for (const auto& item : list) { if (TCastToPtr act = item) { act->m_debugSelected = false; } } } 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); ImGui::TableHeadersRow(); std::vector items; items.reserve(list.size()); for (auto* ent : list) { items.push_back(ent); } if (ImGuiTableSortSpecs* sortSpecs = ImGui::TableGetSortSpecs()) { for (int i = 0; i < sortSpecs->SpecsCount; ++i) { const auto& specs = sortSpecs->Specs[i]; if (specs.ColumnUserID == 'id') { std::sort(items.begin(), items.end(), [&](CEntity* a, CEntity* b) { u16 aId = a->GetUniqueId().Value(); u16 bId = b->GetUniqueId().Value(); return specs.SortDirection == ImGuiSortDirection_Ascending ? aId < bId : aId > bId; }); } else if (specs.ColumnUserID == 'name') { std::sort(items.begin(), items.end(), [&](CEntity* a, CEntity* b) { 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; }); } } } for (const auto& item : items) { TUniqueId uid = item->GetUniqueId(); ImGui::PushID(uid.Value()); ImGui::TableNextRow(); if (ImGui::TableNextColumn()) { auto text = fmt::format(FMT_STRING("{:x}"), uid.Value()); if (TCastToPtr act = item) { ImGui::Selectable(text.c_str(), &act->m_debugSelected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap); } else { ImGui::TextUnformatted(text.c_str()); } } if (ImGui::TableNextColumn()) { ImGuiStringViewText(item->ImGuiType()); } if (ImGui::TableNextColumn()) { ImGuiStringViewText(item->GetName()); } if (ImGui::TableNextColumn()) { if (ImGui::SmallButton("View")) { ImGuiConsole::inspectingEntities.insert(uid); } } ImGui::PopID(); } ImGui::EndTable(); } } ImGui::End(); } static bool showEntityInfoWindow(TUniqueId uid) { bool open = true; CEntity* ent = g_StateManager->ObjectById(uid); if (ent == nullptr) { return false; } 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::PushID(ent->GetUniqueId().Value()); ent->ImGuiInspect(); ImGui::PopID(); } ImGui::End(); return open; } static bool showInspectWindow = false; static bool showDemoWindow = false; static void ShowAppMainMenuBar(bool canInspect) { if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("Game")) { ShowMenuGame(); ImGui::EndMenu(); } ImGui::Spacing(); if (ImGui::BeginMenu("Tools")) { ImGui::MenuItem("Inspect", nullptr, &showInspectWindow, canInspect); ImGui::Separator(); ImGui::MenuItem("Demo", nullptr, &showDemoWindow); ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } } void ImGuiConsole::proc() { if (stepFrame) { g_Main->SetPaused(true); stepFrame = false; } bool canInspect = g_StateManager != nullptr && g_StateManager->GetObjectList(); ShowAppMainMenuBar(canInspect); if (canInspect) { if (showInspectWindow) { ShowInspectWindow(&showInspectWindow); } auto iter = inspectingEntities.begin(); while (iter != inspectingEntities.end()) { if (!showEntityInfoWindow(*iter)) { iter = inspectingEntities.erase(iter); } else { iter++; } } for (const auto& item : *g_StateManager->GetObjectList()) { if (TCastToPtr act = item) { if (act->m_debugSelected) { LerpActorColor(act); } } } } else { inspectingEntities.clear(); } if (showDemoWindow) { ImGui::ShowDemoWindow(&showDemoWindow); } } ImGuiConsole::~ImGuiConsole() { dummyWorlds.clear(); stringTables.clear(); } } // namespace metaforce