From d9b7229e793c2d582c0a1cf5cf3d7390313441ec Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sun, 30 May 2021 00:52:20 -0700 Subject: [PATCH] Add input visualization, protect first 7 object slots from deletion The first 7 slots in the game are related to the player, we don't want to allow anyone to delete them because the game *will* crash --- Runtime/ImGuiConsole.cpp | 154 ++++++++++++++++++++++++++++-- Runtime/ImGuiConsole.hpp | 1 + Runtime/Input/CInputGenerator.cpp | 5 +- Runtime/Input/CInputGenerator.hpp | 1 + hecl/include/hecl/CVarCommons.hpp | 1 + hecl/lib/CVarCommons.cpp | 3 + imgui/ImGuiEngine.cpp | 20 ++++ 7 files changed, 178 insertions(+), 7 deletions(-) diff --git a/Runtime/ImGuiConsole.cpp b/Runtime/ImGuiConsole.cpp index f4f59bf66..52845e04c 100644 --- a/Runtime/ImGuiConsole.cpp +++ b/Runtime/ImGuiConsole.cpp @@ -7,6 +7,8 @@ #include "Runtime/World/CPlayer.hpp" #include "ImGuiEngine.hpp" +#define IMGUI_DEFINE_MATH_OPERATORS 1 +#include namespace ImGui { // Internal functions @@ -199,12 +201,16 @@ void ImGuiConsole::BeginEntityRow(const ImGuiEntityEntry& entry) { if (ImGui::MenuItem("Highlight", nullptr, &entry.ent->m_debugSelected)) { entry.ent->SetActive(!isActive); } - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.77f, 0.12f, 0.23f, 1.f}); - if (ImGui::MenuItem("Delete")) { - g_StateManager->FreeScriptObject(entry.uid); + // Only allow deletion if none of the objects are player related + // The player objects will always be in the first 6 slots + if (entry.uid.Value() > 6) { + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.77f, 0.12f, 0.23f, 1.f}); + if (ImGui::MenuItem("Delete")) { + g_StateManager->FreeScriptObject(entry.uid); + } + ImGui::PopStyleColor(); } - ImGui::PopStyleColor(); ImGui::EndPopup(); ImGui::PushStyleColor(ImGuiCol_Text, textColor); } @@ -426,7 +432,7 @@ void ImGuiConsole::ShowAboutWindow() { void ImGuiConsole::ShowDebugOverlay() { if (!m_frameCounter && !m_frameRate && !m_inGameTime && !m_roomTimer && !m_playerInfo && !m_areaInfo && - !m_worldInfo && !m_randomStats && !m_resourceStats) { + !m_worldInfo && !m_randomStats && !m_resourceStats && !m_showInput) { return; } ImGuiIO& io = ImGui::GetIO(); @@ -561,6 +567,139 @@ void ImGuiConsole::ShowDebugOverlay() { } ImGuiStringViewText(fmt::format(FMT_STRING("Resource Objects: {}\n"), g_SimplePool->GetLiveObjects())); } + // Code -stolen- borrowed from Practice Mod + if (m_showInput && g_InputGenerator != nullptr) { + auto input = g_InputGenerator->GetLastInput(); + if (input.x4_controllerIdx == 0) { + ImGui::Separator(); + ImDrawList* dl = ImGui::GetWindowDrawList(); + ImVec2 p = ImGui::GetCursorScreenPos(); + + constexpr float leftStickRadius = 30; + p = p + ImVec2{20, 20}; // Pad p so we don't clip outside our rect + ImVec2 leftStickCenter = p + ImVec2(30, 45); + constexpr float dpadRadius = 15; + constexpr float dpadWidth = 8; + ImVec2 dpadCenter = p + ImVec2(80, 90); + constexpr float rightStickRadius = 20; + ImVec2 rightStickCenter = p + ImVec2(160, 90); + constexpr float startButtonRadius = 8; + ImVec2 startButtonCenter = p + ImVec2(120, 55); + constexpr float aButtonRadius = 16; + ImVec2 aButtonCenter = p + ImVec2(210, 48); + constexpr float bButtonRadius = 8; + ImVec2 bButtonCenter = aButtonCenter + ImVec2(-24, 16); + constexpr float xButtonRadius = 8; + ImVec2 xButtonCenter = aButtonCenter + ImVec2(24, -16); + constexpr float yButtonRadius = 8; + ImVec2 yButtonCenter = aButtonCenter + ImVec2(-12, -24); + constexpr float triggerWidth = leftStickRadius * 2; + constexpr float triggerHeight = 8; + ImVec2 lCenter = leftStickCenter + ImVec2(0, -60); + ImVec2 rCenter = ImVec2(aButtonCenter.x, lCenter.y); + const auto zButtonCenter = rCenter + ImVec2{0, 24}; + const float zButtonHalfWidth = triggerWidth / 2; + const float zButtonHalfHeight = 4; + + constexpr ImU32 stickGray = IM_COL32(150, 150, 150, 255); + constexpr ImU32 darkGray = IM_COL32(60, 60, 60, 255); + constexpr ImU32 red = IM_COL32(255, 0, 0, 255); + constexpr ImU32 green = IM_COL32(0, 255, 0, 255); + + // left stick + { + dl->AddCircleFilled(leftStickCenter, leftStickRadius, stickGray, 8); + float x = input.ALeftX(); + float y = -input.ALeftY(); + dl->AddCircleFilled(leftStickCenter + (ImVec2{x, y} * leftStickRadius), leftStickRadius / 3, red); + dl->AddLine(leftStickCenter, leftStickCenter + ImVec2(x * leftStickRadius, y * leftStickRadius), + IM_COL32(255, 244, 0, 255), 1.5f); + } + + // right stick + { + dl->AddCircleFilled(rightStickCenter, rightStickRadius, stickGray, 8); + float x = input.ARightX(); + float y = -input.ARightY(); + dl->AddCircleFilled(rightStickCenter + (ImVec2{x, y} * rightStickRadius), rightStickRadius / 3, red); + dl->AddLine(rightStickCenter, rightStickCenter + ImVec2(x * rightStickRadius, y * rightStickRadius), + IM_COL32(255, 244, 0, 255), 1.5f); + } + + // dpad + { + constexpr float halfWidth = dpadWidth / 2; + dl->AddRectFilled(dpadCenter + ImVec2(-halfWidth, -dpadRadius), dpadCenter + ImVec2(halfWidth, dpadRadius), + stickGray); + + dl->AddRectFilled(dpadCenter + ImVec2(-dpadRadius, -halfWidth), dpadCenter + ImVec2(dpadRadius, halfWidth), + stickGray); + + if (input.DDPUp()) { + dl->AddRectFilled(dpadCenter + ImVec2(-halfWidth, -dpadRadius), + dpadCenter + ImVec2(halfWidth, -dpadRadius / 2), red); + } + + if (input.DDPDown()) { + dl->AddRectFilled(dpadCenter + ImVec2(-halfWidth, dpadRadius), + dpadCenter + ImVec2(halfWidth, dpadRadius / 2), red); + } + + if (input.DDPLeft()) { + dl->AddRectFilled(dpadCenter + ImVec2(-dpadRadius, -halfWidth), + dpadCenter + ImVec2(-dpadRadius / 2, halfWidth), red); + } + + if (input.DDPRight()) { + dl->AddRectFilled(dpadCenter + ImVec2(dpadRadius, -halfWidth), + dpadCenter + ImVec2(dpadRadius / 2, halfWidth), red); + } + } + + // buttons + { + // start + dl->AddCircleFilled(startButtonCenter, startButtonRadius, input.DStart() ? red : stickGray); + + // a + dl->AddCircleFilled(aButtonCenter, aButtonRadius, input.DA() ? green : stickGray); + + // b + dl->AddCircleFilled(bButtonCenter, bButtonRadius, input.DB() ? red : stickGray); + + // x + dl->AddCircleFilled(xButtonCenter, xButtonRadius, input.DX() ? red : stickGray); + + // y + dl->AddCircleFilled(yButtonCenter, yButtonRadius, input.DY() ? red : stickGray); + + // z + dl->AddRectFilled(zButtonCenter - ImVec2{zButtonHalfWidth, zButtonHalfHeight}, + zButtonCenter + ImVec2{zButtonHalfWidth, zButtonHalfHeight}, + input.DZ() ? IM_COL32(128, 0, 128, 255) : stickGray, 16); + } + + // triggers + { + float halfTriggerWidth = triggerWidth / 2; + ImVec2 lStart = lCenter - ImVec2(halfTriggerWidth, 0); + ImVec2 lEnd = lCenter + ImVec2(halfTriggerWidth, triggerHeight); + float lValue = triggerWidth * input.ALTrigger(); + + dl->AddRectFilled(lStart, lStart + ImVec2(lValue, triggerHeight), input.DL() ? red : stickGray); + dl->AddRectFilled(lStart + ImVec2(lValue, 0), lEnd, darkGray); + + ImVec2 rStart = rCenter - ImVec2(halfTriggerWidth, 0); + ImVec2 rEnd = rCenter + ImVec2(halfTriggerWidth, triggerHeight); + float rValue = triggerWidth * input.ARTrigger(); + + dl->AddRectFilled(rEnd - ImVec2(rValue, triggerHeight), rEnd, input.DR() ? red : stickGray); + dl->AddRectFilled(rStart, rEnd - ImVec2(rValue, 0), darkGray); + } + + ImGui::Dummy(ImVec2(270, 130)); + } + } if (ImGui::BeginPopupContextWindow()) { if (ImGui::MenuItem("Custom", nullptr, m_debugOverlayCorner == -1)) { m_debugOverlayCorner = -1; @@ -623,6 +762,9 @@ void ImGuiConsole::ShowAppMainMenuBar(bool canInspect) { if (ImGui::MenuItem("Resource Stats", nullptr, &m_resourceStats)) { m_cvarCommons.m_debugOverlayShowResourceStats->fromBoolean(m_resourceStats); } + if (ImGui::MenuItem("Show Input", nullptr, &m_showInput)) { + m_cvarCommons.m_debugOverlayShowInput->fromBoolean(m_showInput); + } ImGui::EndMenu(); } ImGui::Spacing(); diff --git a/Runtime/ImGuiConsole.hpp b/Runtime/ImGuiConsole.hpp index 396eb2a59..e8852b6b9 100644 --- a/Runtime/ImGuiConsole.hpp +++ b/Runtime/ImGuiConsole.hpp @@ -71,6 +71,7 @@ private: bool m_areaInfo = m_cvarCommons.m_debugOverlayAreaInfo->toBoolean(); bool m_randomStats = m_cvarCommons.m_debugOverlayShowRandomStats->toBoolean(); bool m_resourceStats = m_cvarCommons.m_debugOverlayShowResourceStats->toBoolean(); + bool m_showInput = m_cvarCommons.m_debugOverlayShowInput->toBoolean(); int m_debugOverlayCorner = 2; // bottom-left const void* m_currentRoom = nullptr; diff --git a/Runtime/Input/CInputGenerator.cpp b/Runtime/Input/CInputGenerator.cpp index 495e2b774..f41df8747 100644 --- a/Runtime/Input/CInputGenerator.cpp +++ b/Runtime/Input/CInputGenerator.cpp @@ -28,13 +28,16 @@ void CInputGenerator::Update(float dt, CArchitectureQueue& queue) { input |= kbInput; kbUsed = true; } + m_lastUpdate = input; queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, input)); } } /* Send straight keyboard input if no first controller present */ - if (!kbUsed) + if (!kbUsed) { + m_lastUpdate = kbInput; queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, kbInput)); + } } } // namespace metaforce diff --git a/Runtime/Input/CInputGenerator.hpp b/Runtime/Input/CInputGenerator.hpp index c8445c5f9..742a0f3ba 100644 --- a/Runtime/Input/CInputGenerator.hpp +++ b/Runtime/Input/CInputGenerator.hpp @@ -180,6 +180,7 @@ public: /* This is where the game thread enters */ void Update(float dt, CArchitectureQueue& queue); + CFinalInput GetLastInput() const { return m_lastUpdate; } }; } // namespace metaforce diff --git a/hecl/include/hecl/CVarCommons.hpp b/hecl/include/hecl/CVarCommons.hpp index 5229543e9..250e91831 100644 --- a/hecl/include/hecl/CVarCommons.hpp +++ b/hecl/include/hecl/CVarCommons.hpp @@ -37,6 +37,7 @@ struct CVarCommons { CVar* m_debugOverlayShowResourceStats = nullptr; CVar* m_debugOverlayShowRandomStats = nullptr; CVar* m_debugOverlayShowRoomTimer = nullptr; + CVar* m_debugOverlayShowInput = nullptr; CVar* m_debugToolDrawAiPath = nullptr; CVar* m_debugToolDrawLighting = nullptr; CVar* m_debugToolDrawCollisionActors = nullptr; diff --git a/hecl/lib/CVarCommons.cpp b/hecl/lib/CVarCommons.cpp index 941cba87d..3bb08441d 100644 --- a/hecl/lib/CVarCommons.cpp +++ b/hecl/lib/CVarCommons.cpp @@ -53,6 +53,9 @@ CVarCommons::CVarCommons(CVarManager& manager) : m_mgr(manager) { m_debugOverlayShowRandomStats = m_mgr.findOrMakeCVar( "debugOverlay.showRandomStats", "Displays the current number of random calls per frame"sv, false, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); + m_debugOverlayShowInput = + m_mgr.findOrMakeCVar("debugOverlay.showInput"sv, "Displays user input"sv, false, + hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); m_debugToolDrawAiPath = m_mgr.findOrMakeCVar("debugTool.drawAiPath", "Draws the selected paths of any AI in the room"sv, false, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); diff --git a/imgui/ImGuiEngine.cpp b/imgui/ImGuiEngine.cpp index 9602979d0..c3beff7cd 100644 --- a/imgui/ImGuiEngine.cpp +++ b/imgui/ImGuiEngine.cpp @@ -10,6 +10,8 @@ #define STBI_ONLY_PNG #include "stb_image.h" +#include "Runtime/GameGlobalObjects.hpp" +#include "Runtime/Input/CInputGenerator.hpp" #include extern "C" const uint8_t NOTO_MONO_FONT[]; @@ -171,6 +173,24 @@ void ImGuiEngine::Begin(float dt, float scale) { io.KeySuper = True(Input.m_modifiers & boo::EModifierKey::Command); memcpy(static_cast(io.KeysDown), Input.m_keys.data(), sizeof(io.KeysDown)); +#if 0 + if (g_InputGenerator != nullptr) { + auto input = g_InputGenerator->GetLastInput(); + if (input.x4_controllerIdx == 0) { + io.NavInputs[ImGuiNavInput_Activate] = input.DA(); + io.NavInputs[ImGuiNavInput_Cancel] = input.DB(); + io.NavInputs[ImGuiNavInput_DpadLeft] = input.DDPLeft() || input.DLALeft(); + io.NavInputs[ImGuiNavInput_DpadRight] = input.DDPRight() || input.DLARight(); + io.NavInputs[ImGuiNavInput_DpadUp] = input.DDPUp() || input.DLAUp(); + io.NavInputs[ImGuiNavInput_DpadDown] = input.DDPDown() || input.DLADown(); + io.NavInputs[ImGuiNavInput_LStickLeft] = input.ALALeft(); + io.NavInputs[ImGuiNavInput_LStickRight] = input.ALARight(); + io.NavInputs[ImGuiNavInput_LStickUp] = input.ALAUp(); + io.NavInputs[ImGuiNavInput_LStickDown] = input.ALADown(); + } + } +#endif + for (const auto c : ImGuiEngine::Input.m_charCodes) { io.AddInputCharacter(c); }