diff --git a/soh/assets/custom/textures/buttons/ABtn.png b/soh/assets/custom/textures/buttons/ABtn.png index c1b06571c..031924cb9 100644 Binary files a/soh/assets/custom/textures/buttons/ABtn.png and b/soh/assets/custom/textures/buttons/ABtn.png differ diff --git a/soh/assets/custom/textures/buttons/ABtnOutline.png b/soh/assets/custom/textures/buttons/ABtnOutline.png new file mode 100644 index 000000000..b3c9d3aab Binary files /dev/null and b/soh/assets/custom/textures/buttons/ABtnOutline.png differ diff --git a/soh/assets/custom/textures/buttons/AnalogStick.png b/soh/assets/custom/textures/buttons/AnalogStick.png new file mode 100644 index 000000000..d3fec7d01 Binary files /dev/null and b/soh/assets/custom/textures/buttons/AnalogStick.png differ diff --git a/soh/assets/custom/textures/buttons/AnalogStickOutline.png b/soh/assets/custom/textures/buttons/AnalogStickOutline.png new file mode 100644 index 000000000..e4d8d7190 Binary files /dev/null and b/soh/assets/custom/textures/buttons/AnalogStickOutline.png differ diff --git a/soh/assets/custom/textures/buttons/BBtn.png b/soh/assets/custom/textures/buttons/BBtn.png index 99b1197c9..8e2fbb54b 100644 Binary files a/soh/assets/custom/textures/buttons/BBtn.png and b/soh/assets/custom/textures/buttons/BBtn.png differ diff --git a/soh/assets/custom/textures/buttons/BBtnOutline.png b/soh/assets/custom/textures/buttons/BBtnOutline.png new file mode 100644 index 000000000..fab802f3b Binary files /dev/null and b/soh/assets/custom/textures/buttons/BBtnOutline.png differ diff --git a/soh/assets/custom/textures/buttons/CDown.png b/soh/assets/custom/textures/buttons/CDown.png index 741188eaf..f30cec2b3 100644 Binary files a/soh/assets/custom/textures/buttons/CDown.png and b/soh/assets/custom/textures/buttons/CDown.png differ diff --git a/soh/assets/custom/textures/buttons/CDownOutline.png b/soh/assets/custom/textures/buttons/CDownOutline.png new file mode 100644 index 000000000..9324c282d Binary files /dev/null and b/soh/assets/custom/textures/buttons/CDownOutline.png differ diff --git a/soh/assets/custom/textures/buttons/CLeft.png b/soh/assets/custom/textures/buttons/CLeft.png index 5e26a2067..43b04412f 100644 Binary files a/soh/assets/custom/textures/buttons/CLeft.png and b/soh/assets/custom/textures/buttons/CLeft.png differ diff --git a/soh/assets/custom/textures/buttons/CLeftOutline.png b/soh/assets/custom/textures/buttons/CLeftOutline.png new file mode 100644 index 000000000..c0d48f659 Binary files /dev/null and b/soh/assets/custom/textures/buttons/CLeftOutline.png differ diff --git a/soh/assets/custom/textures/buttons/CRight.png b/soh/assets/custom/textures/buttons/CRight.png index 9e6180639..c2d1afabf 100644 Binary files a/soh/assets/custom/textures/buttons/CRight.png and b/soh/assets/custom/textures/buttons/CRight.png differ diff --git a/soh/assets/custom/textures/buttons/CRightOutline.png b/soh/assets/custom/textures/buttons/CRightOutline.png new file mode 100644 index 000000000..d450084ea Binary files /dev/null and b/soh/assets/custom/textures/buttons/CRightOutline.png differ diff --git a/soh/assets/custom/textures/buttons/CUp.png b/soh/assets/custom/textures/buttons/CUp.png index 6c0e29d2d..aca464728 100644 Binary files a/soh/assets/custom/textures/buttons/CUp.png and b/soh/assets/custom/textures/buttons/CUp.png differ diff --git a/soh/assets/custom/textures/buttons/CUpOutline.png b/soh/assets/custom/textures/buttons/CUpOutline.png new file mode 100644 index 000000000..b21cd3ae9 Binary files /dev/null and b/soh/assets/custom/textures/buttons/CUpOutline.png differ diff --git a/soh/assets/custom/textures/buttons/DPadDown.png b/soh/assets/custom/textures/buttons/DPadDown.png new file mode 100644 index 000000000..cec0af1e5 Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadDown.png differ diff --git a/soh/assets/custom/textures/buttons/DPadDownOutline.png b/soh/assets/custom/textures/buttons/DPadDownOutline.png new file mode 100644 index 000000000..e8dca39b6 Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadDownOutline.png differ diff --git a/soh/assets/custom/textures/buttons/DPadLeft.png b/soh/assets/custom/textures/buttons/DPadLeft.png new file mode 100644 index 000000000..2a4a09b79 Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadLeft.png differ diff --git a/soh/assets/custom/textures/buttons/DPadLeftOutline.png b/soh/assets/custom/textures/buttons/DPadLeftOutline.png new file mode 100644 index 000000000..ba3dbf4e8 Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadLeftOutline.png differ diff --git a/soh/assets/custom/textures/buttons/DPadRight.png b/soh/assets/custom/textures/buttons/DPadRight.png new file mode 100644 index 000000000..e7854a219 Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadRight.png differ diff --git a/soh/assets/custom/textures/buttons/DPadRightOutline.png b/soh/assets/custom/textures/buttons/DPadRightOutline.png new file mode 100644 index 000000000..f6b4764c6 Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadRightOutline.png differ diff --git a/soh/assets/custom/textures/buttons/DPadUp.png b/soh/assets/custom/textures/buttons/DPadUp.png new file mode 100644 index 000000000..8d70d96da Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadUp.png differ diff --git a/soh/assets/custom/textures/buttons/DPadUpOutline.png b/soh/assets/custom/textures/buttons/DPadUpOutline.png new file mode 100644 index 000000000..8ad2d7959 Binary files /dev/null and b/soh/assets/custom/textures/buttons/DPadUpOutline.png differ diff --git a/soh/assets/custom/textures/buttons/InputViewerBackground.png b/soh/assets/custom/textures/buttons/InputViewerBackground.png new file mode 100644 index 000000000..091d686c0 Binary files /dev/null and b/soh/assets/custom/textures/buttons/InputViewerBackground.png differ diff --git a/soh/assets/custom/textures/buttons/LBtn.png b/soh/assets/custom/textures/buttons/LBtn.png index 2e0a8f00c..351ea383a 100644 Binary files a/soh/assets/custom/textures/buttons/LBtn.png and b/soh/assets/custom/textures/buttons/LBtn.png differ diff --git a/soh/assets/custom/textures/buttons/LBtnOutline.png b/soh/assets/custom/textures/buttons/LBtnOutline.png new file mode 100644 index 000000000..10cca9c8f Binary files /dev/null and b/soh/assets/custom/textures/buttons/LBtnOutline.png differ diff --git a/soh/assets/custom/textures/buttons/RBtn.png b/soh/assets/custom/textures/buttons/RBtn.png index c255643c3..ecb96bd6c 100644 Binary files a/soh/assets/custom/textures/buttons/RBtn.png and b/soh/assets/custom/textures/buttons/RBtn.png differ diff --git a/soh/assets/custom/textures/buttons/RBtnOutline.png b/soh/assets/custom/textures/buttons/RBtnOutline.png new file mode 100644 index 000000000..afeba32eb Binary files /dev/null and b/soh/assets/custom/textures/buttons/RBtnOutline.png differ diff --git a/soh/assets/custom/textures/buttons/RightStick.png b/soh/assets/custom/textures/buttons/RightStick.png new file mode 100644 index 000000000..6b8490aaf Binary files /dev/null and b/soh/assets/custom/textures/buttons/RightStick.png differ diff --git a/soh/assets/custom/textures/buttons/RightStickOutline.png b/soh/assets/custom/textures/buttons/RightStickOutline.png new file mode 100644 index 000000000..8fbd54fcc Binary files /dev/null and b/soh/assets/custom/textures/buttons/RightStickOutline.png differ diff --git a/soh/assets/custom/textures/buttons/StartBtn.png b/soh/assets/custom/textures/buttons/StartBtn.png index c3e08dc36..ec85f2619 100644 Binary files a/soh/assets/custom/textures/buttons/StartBtn.png and b/soh/assets/custom/textures/buttons/StartBtn.png differ diff --git a/soh/assets/custom/textures/buttons/StartBtnOutline.png b/soh/assets/custom/textures/buttons/StartBtnOutline.png new file mode 100644 index 000000000..a7902edbd Binary files /dev/null and b/soh/assets/custom/textures/buttons/StartBtnOutline.png differ diff --git a/soh/assets/custom/textures/buttons/ZBtn.png b/soh/assets/custom/textures/buttons/ZBtn.png index def8d9a6d..4fee52d57 100644 Binary files a/soh/assets/custom/textures/buttons/ZBtn.png and b/soh/assets/custom/textures/buttons/ZBtn.png differ diff --git a/soh/assets/custom/textures/buttons/ZBtnOutline.png b/soh/assets/custom/textures/buttons/ZBtnOutline.png new file mode 100644 index 000000000..5832ed339 Binary files /dev/null and b/soh/assets/custom/textures/buttons/ZBtnOutline.png differ diff --git a/soh/soh/Enhancements/controls/InputViewer.cpp b/soh/soh/Enhancements/controls/InputViewer.cpp new file mode 100644 index 000000000..6b25589c6 --- /dev/null +++ b/soh/soh/Enhancements/controls/InputViewer.cpp @@ -0,0 +1,519 @@ +#include "InputViewer.h" + +#include "public/bridge/consolevariablebridge.h" +#include "libultraship/libultra/controller.h" +#include "Context.h" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include +#include +#include + +#include "../../UIWidgets.hpp" + +// Text colors +static ImVec4 textColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); +static ImVec4 range1Color = ImVec4(1.0f, 0.7f, 0, 1.0f); +static ImVec4 range2Color = ImVec4(0, 1.0f, 0, 1.0f); + +static const char* buttonOutlineOptions[4] = { "Always Shown", "Shown Only While Not Pressed", + "Shown Only While Pressed", "Always Hidden" }; + +static const char* stickModeOptions[3] = { "Always", "While In Use", "Never" }; + +static Color_RGBA8 vec2Color(ImVec4 vec) { + Color_RGBA8 color; + color.r = vec.x * 255.0; + color.g = vec.y * 255.0; + color.b = vec.z * 255.0; + color.a = vec.w * 255.0; + return color; +} + +static ImVec4 color2Vec(Color_RGBA8 color) { + return ImVec4(color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0); +} + +InputViewer::~InputViewer() { + SPDLOG_TRACE("destruct input viewer"); +} + +void InputViewer::RenderButton(std::string btnTexture, std::string btnOutlineTexture, int state, ImVec2 size, + int outlineMode) { + const ImVec2 pos = ImGui::GetCursorPos(); + ImGui::SetNextItemAllowOverlap(); + // Render Outline based on settings + if (outlineMode == BUTTON_OUTLINE_ALWAYS_SHOWN || (outlineMode == BUTTON_OUTLINE_NOT_PRESSED && !state) || + (outlineMode == BUTTON_OUTLINE_PRESSED && state)) { + ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(btnOutlineTexture), size, + ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255)); + } + // Render button if pressed + if (state) { + ImGui::SetCursorPos(pos); + ImGui::SetNextItemAllowOverlap(); + ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(btnTexture), size, + ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255)); + } +} + +void InputViewer::DrawElement() { + if (CVarGetInteger("gOpenWindows.InputViewer", 0)) { + static bool sButtonTexturesLoaded = false; + if (!sButtonTexturesLoaded) { + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage( + "Input-Viewer-Background", "textures/buttons/InputViewerBackground.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("A-Btn", "textures/buttons/ABtn.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("B-Btn", "textures/buttons/BBtn.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("L-Btn", "textures/buttons/LBtn.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("R-Btn", "textures/buttons/RBtn.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Z-Btn", "textures/buttons/ZBtn.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Start-Btn", + "textures/buttons/StartBtn.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Left", "textures/buttons/CLeft.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Right", "textures/buttons/CRight.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Up", "textures/buttons/CUp.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Down", "textures/buttons/CDown.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Analog-Stick", + "textures/buttons/AnalogStick.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Left", + "textures/buttons/DPadLeft.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Right", + "textures/buttons/DPadRight.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Up", "textures/buttons/DPadUp.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Down", + "textures/buttons/DPadDown.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Right-Stick", + "textures/buttons/RightStick.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("A-Btn Outline", + "textures/buttons/ABtnOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("B-Btn Outline", + "textures/buttons/BBtnOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("L-Btn Outline", + "textures/buttons/LBtnOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("R-Btn Outline", + "textures/buttons/RBtnOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Z-Btn Outline", + "textures/buttons/ZBtnOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Start-Btn Outline", + "textures/buttons/StartBtnOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Left Outline", + "textures/buttons/CLeftOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Right Outline", + "textures/buttons/CRightOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Up Outline", + "textures/buttons/CUpOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("C-Down Outline", + "textures/buttons/CDownOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Analog-Stick Outline", + "textures/buttons/AnalogStickOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Left Outline", + "textures/buttons/DPadLeftOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Right Outline", + "textures/buttons/DPadRightOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Up Outline", + "textures/buttons/DPadUpOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Dpad-Down Outline", + "textures/buttons/DPadDownOutline.png"); + LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTextureFromRawImage("Right-Stick Outline", + "textures/buttons/RightStickOutline.png"); + sButtonTexturesLoaded = true; + } + + ImVec2 mainPos = ImGui::GetWindowPos(); + ImVec2 size = ImGui::GetContentRegionAvail(); + +#ifdef __WIIU__ + const float scale = CVarGetFloat("gInputViewer.Scale", 1.0f) * 2.0f; +#else + const float scale = CVarGetFloat("gInputViewer.Scale", 1.0f); +#endif + const int showAnalogAngles = CVarGetInteger("gInputViewer.AnalogAngles.Enabled", 0); + const int buttonOutlineMode = CVarGetInteger("gInputViewer.ButtonOutlineMode", BUTTON_OUTLINE_NOT_PRESSED); + + ImVec2 bgSize = LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureSize("Input-Viewer-Background"); + ImVec2 scaledBGSize = ImVec2(bgSize.x * scale, bgSize.y * scale); + + ImGui::SetNextWindowSize(ImVec2( + scaledBGSize.x + 20, + scaledBGSize.y + + (showAnalogAngles ? ImGui::CalcTextSize("X").y : 0) * scale * CVarGetFloat("gInputViewer.AnalogAngles.Scale", 1.0f) + 20)); + ImGui::SetNextWindowContentSize( + ImVec2(scaledBGSize.x, scaledBGSize.y + (showAnalogAngles ? 15 : 0) * scale * + CVarGetFloat("gInputViewer.AnalogAngles.Scale", 1.0f))); + ImGui::SetNextWindowPos( + ImVec2(mainPos.x + size.x - scaledBGSize.x - 30, mainPos.y + size.y - scaledBGSize.y - 30), + ImGuiCond_FirstUseEver); + + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + + OSContPad* pads = LUS::Context::GetInstance()->GetControlDeck()->GetPads(); + + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground | + ImGuiWindowFlags_NoFocusOnAppearing; + + if (!CVarGetInteger("gInputViewer.EnableDragging", 1)) { + windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; + } + + if (pads != nullptr && ImGui::Begin("Input Viewer", nullptr, windowFlags)) { + ImGui::SetCursorPos(ImVec2(10, 10)); + const ImVec2 aPos = ImGui::GetCursorPos(); + + if (CVarGetInteger("gInputViewer.ShowBackground", 1)) { + ImGui::SetNextItemAllowOverlap(); + // Background + ImGui::Image( + LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Input-Viewer-Background"), + scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255)); + } + + // A/B + if (CVarGetInteger("gInputViewer.BBtn", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("B-Btn", "B-Btn Outline", pads[0].button & BTN_B, scaledBGSize, buttonOutlineMode); + } + if (CVarGetInteger("gInputViewer.ABtn", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("A-Btn", "A-Btn Outline", pads[0].button & BTN_A, scaledBGSize, buttonOutlineMode); + } + + // C buttons + if (CVarGetInteger("gInputViewer.CUp", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("C-Up", "C-Up Outline", pads[0].button & BTN_CUP, scaledBGSize, buttonOutlineMode); + } + if (CVarGetInteger("gInputViewer.CLeft", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("C-Left", "C-Left Outline", pads[0].button & BTN_CLEFT, scaledBGSize, buttonOutlineMode); + } + if (CVarGetInteger("gInputViewer.CRight", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("C-Right", "C-Right Outline", pads[0].button & BTN_CRIGHT, scaledBGSize, + buttonOutlineMode); + } + if (CVarGetInteger("gInputViewer.CDown", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("C-Down", "C-Down Outline", pads[0].button & BTN_CDOWN, scaledBGSize, buttonOutlineMode); + } + + // L/R/Z + if (CVarGetInteger("gInputViewer.LBtn", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("L-Btn", "L-Btn Outline", pads[0].button & BTN_L, scaledBGSize, buttonOutlineMode); + } + if (CVarGetInteger("gInputViewer.RBtn", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("R-Btn", "R-Btn Outline", pads[0].button & BTN_R, scaledBGSize, buttonOutlineMode); + } + if (CVarGetInteger("gInputViewer.ZBtn", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("Z-Btn", "Z-Btn Outline", pads[0].button & BTN_Z, scaledBGSize, buttonOutlineMode); + } + + // Start + if (CVarGetInteger("gInputViewer.StartBtn", 1)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("Start-Btn", "Start-Btn Outline", pads[0].button & BTN_START, scaledBGSize, + buttonOutlineMode); + } + + // Dpad + if (CVarGetInteger("gInputViewer.Dpad", 0)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("Dpad-Left", "Dpad-Left Outline", pads[0].button & BTN_DLEFT, scaledBGSize, + buttonOutlineMode); + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("Dpad-Right", "Dpad-Right Outline", pads[0].button & BTN_DRIGHT, scaledBGSize, + buttonOutlineMode); + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("Dpad-Up", "Dpad-Up Outline", pads[0].button & BTN_DUP, scaledBGSize, buttonOutlineMode); + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + RenderButton("Dpad-Down", "Dpad-Down Outline", pads[0].button & BTN_DDOWN, scaledBGSize, + buttonOutlineMode); + } + + const bool analogStickIsInDeadzone = !pads[0].stick_x && !pads[0].stick_y; + const bool rightStickIsInDeadzone = !pads[0].right_stick_x && !pads[0].right_stick_y; + + // Analog Stick + const int analogOutlineMode = + CVarGetInteger("gInputViewer.AnalogStick.OutlineMode", STICK_MODE_ALWAYS_SHOWN); + const float maxStickDistance = CVarGetInteger("gInputViewer.AnalogStick.Movement", 12); + if (analogOutlineMode == STICK_MODE_ALWAYS_SHOWN || + (analogOutlineMode == STICK_MODE_HIDDEN_IN_DEADZONE && !analogStickIsInDeadzone)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + ImGui::Image( + LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Analog-Stick Outline"), + scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255)); + } + const int analogStickMode = + CVarGetInteger("gInputViewer.AnalogStick.VisibilityMode", STICK_MODE_ALWAYS_SHOWN); + if (analogStickMode == STICK_MODE_ALWAYS_SHOWN || + (analogStickMode == STICK_MODE_HIDDEN_IN_DEADZONE && !analogStickIsInDeadzone)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos( + ImVec2(aPos.x + maxStickDistance * ((float)(pads[0].stick_x) / MAX_AXIS_RANGE) * scale, + aPos.y - maxStickDistance * ((float)(pads[0].stick_y) / MAX_AXIS_RANGE) * scale)); + ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Analog-Stick"), + scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255)); + } + + // Right Stick + const float maxRightStickDistance = CVarGetInteger("gInputViewer.RightStick.Movement", 7); + const int rightOutlineMode = + CVarGetInteger("gInputViewer.RightStick.OutlineMode", STICK_MODE_ALWAYS_HIDDEN); + if (rightOutlineMode == STICK_MODE_ALWAYS_SHOWN || + (rightOutlineMode == STICK_MODE_HIDDEN_IN_DEADZONE && !rightStickIsInDeadzone)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos(aPos); + ImGui::Image( + LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Right-Stick Outline"), + scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255)); + } + const int rightStickMode = + CVarGetInteger("gInputViewer.RightStick.VisibilityMode", STICK_MODE_ALWAYS_HIDDEN); + if (rightStickMode == STICK_MODE_ALWAYS_SHOWN || + (rightStickMode == STICK_MODE_HIDDEN_IN_DEADZONE && !rightStickIsInDeadzone)) { + ImGui::SetNextItemAllowOverlap(); + ImGui::SetCursorPos( + ImVec2(aPos.x + maxRightStickDistance * ((float)(pads[0].right_stick_x) / MAX_AXIS_RANGE) * scale, + aPos.y - maxRightStickDistance * ((float)(pads[0].right_stick_y) / MAX_AXIS_RANGE) * scale)); + ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Right-Stick"), + scaledBGSize, ImVec2(0, 0), ImVec2(1.0f, 1.0f), ImVec4(255, 255, 255, 255)); + } + + // Analog stick angle text + if (showAnalogAngles) { + ImGui::SetCursorPos(ImVec2(aPos.x + 10 + CVarGetInteger("gInputViewer.AnalogAngles.Offset", 0) * scale, + scaledBGSize.y + aPos.y + 10)); + // Scale font with input viewer scale + float oldFontScale = ImGui::GetFont()->Scale; + ImGui::GetFont()->Scale *= scale * CVarGetFloat("gInputViewer.AnalogAngles.Scale", 1.0f); + ImGui::PushFont(ImGui::GetFont()); + + // Calculate polar R coordinate from X and Y angles, squared to avoid sqrt + const float rSquared = pads[0].stick_x * pads[0].stick_x + pads[0].stick_y * pads[0].stick_y; + + // ESS range + const int range1Min = CVarGetInteger("gInputViewer.AnalogAngles.Range1.Min", 8); + const int range1Max = CVarGetInteger("gInputViewer.AnalogAngles.Range1.Max", 27); + // Walking speed range + const int range2Min = CVarGetInteger("gInputViewer.AnalogAngles.Range2.Min", 27); + const int range2Max = CVarGetInteger("gInputViewer.AnalogAngles.Range2.Max", 62); + + // Push color based on angle ranges + if (CVarGetInteger("gInputViewer.AnalogAngles.Range1.Enabled", 0) && + (rSquared >= (range1Min * range1Min)) && (rSquared < (range1Max * range1Max))) { + ImGui::PushStyleColor( + ImGuiCol_Text, + color2Vec(CVarGetColor("gInputViewer.AnalogAngles.Range1.Color", vec2Color(range1Color)))); + } else if (CVarGetInteger("gInputViewer.AnalogAngles.Range2.Enabled", 0) && + (rSquared >= (range2Min * range2Min)) && (rSquared < (range2Max * range2Max))) { + ImGui::PushStyleColor( + ImGuiCol_Text, + color2Vec(CVarGetColor("gInputViewer.AnalogAngles.Range2.Color", vec2Color(range2Color)))); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, color2Vec(CVarGetColor("gInputViewer.AnalogAngles.TextColor", + vec2Color(textColor)))); + } + + // Render text + ImGui::Text("X: %-3d Y: %-3d", pads[0].stick_x, pads[0].stick_y); + // Restore original color + ImGui::PopStyleColor(); + // Restore original font scale + ImGui::GetFont()->Scale = oldFontScale; + ImGui::PopFont(); + } + + ImGui::End(); + } + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } +} + +InputViewerSettingsWindow::~InputViewerSettingsWindow() { + SPDLOG_TRACE("destruct input viewer settings window"); +} + +void InputViewerSettingsWindow::DrawElement() { + ImGui::SetNextWindowSize(ImVec2(450, 525), ImGuiCond_FirstUseEver); + + if (ImGui::Begin("Input Viewer Settings", &mIsVisible)) { + + // gInputViewer.Scale + UIWidgets::EnhancementSliderFloat("Input Viewer Scale: %.2f", "##Input", "gInputViewer.Scale", 0.1f, 5.0f, "", + 1.0f, false, true); + UIWidgets::Tooltip("Sets the on screen size of the input viewer"); + + // gInputViewer.EnableDragging + UIWidgets::EnhancementCheckbox("Enable Dragging", "gInputViewer.EnableDragging", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + + UIWidgets::PaddedSeparator(true, true); + + // gInputViewer.ShowBackground + UIWidgets::EnhancementCheckbox("Show Background Layer", "gInputViewer.ShowBackground", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + + UIWidgets::PaddedSeparator(true, true); + + if (ImGui::CollapsingHeader("Buttons")) { + // gInputViewer.ABtn + UIWidgets::EnhancementCheckbox("Show A-Button Layers", "gInputViewer.ABtn", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.BBtn + UIWidgets::EnhancementCheckbox("Show B-Button Layers", "gInputViewer.BBtn", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.CUp + UIWidgets::EnhancementCheckbox("Show C-Up Layers", "gInputViewer.CUp", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.CRight + UIWidgets::EnhancementCheckbox("Show C-Right Layers", "gInputViewer.CRight", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.CDown + UIWidgets::EnhancementCheckbox("Show C-Down Layers", "gInputViewer.CDown", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.CLeft + UIWidgets::EnhancementCheckbox("Show C-Left Layers", "gInputViewer.CLeft", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.LBtn + UIWidgets::EnhancementCheckbox("Show L-Button Layers", "gInputViewer.LBtn", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.RBtn + UIWidgets::EnhancementCheckbox("Show R-Button Layers", "gInputViewer.RBtn", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.ZBtn + UIWidgets::EnhancementCheckbox("Show Z-Button Layers", "gInputViewer.ZBtn", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.StartBtn + UIWidgets::EnhancementCheckbox("Show Start Button Layers", "gInputViewer.StartBtn", false, "", + UIWidgets::CheckboxGraphics::Checkmark, true); + // gInputViewer.Dpad + UIWidgets::EnhancementCheckbox("Show D-Pad Layers", "gInputViewer.Dpad", false, "", + UIWidgets::CheckboxGraphics::Checkmark, false); + + // gInputViewer.ButtonOutlineMode + UIWidgets::PaddedText("Button Outlines/Backgrounds", true, false); + UIWidgets::EnhancementCombobox("gInputViewer.ButtonOutlineMode", buttonOutlineOptions, + BUTTON_OUTLINE_NOT_PRESSED); + UIWidgets::Tooltip( + "Sets the desired visibility behavior for the button outline/background layers. Useful for " + "custom input viewers."); + + UIWidgets::PaddedSeparator(true, true); + } + + if (ImGui::CollapsingHeader("Analog Stick")) { + // gInputViewer.AnalogStick.VisibilityMode + UIWidgets::PaddedText("Analog Stick Visibility", true, false); + UIWidgets::EnhancementCombobox("gInputViewer.AnalogStick.VisibilityMode", stickModeOptions, + STICK_MODE_ALWAYS_SHOWN); + UIWidgets::Tooltip( + "Determines the conditions under which the moving layer of the analog stick texture is visible."); + + // gInputViewer.AnalogStick.OutlineMode + UIWidgets::PaddedText("Analog Stick Outline/Background Visibility", true, false); + UIWidgets::EnhancementCombobox("gInputViewer.AnalogStick.OutlineMode", stickModeOptions, + STICK_MODE_ALWAYS_SHOWN); + UIWidgets::Tooltip( + "Determines the conditions under which the analog stick outline/background texture is visible."); + + // gInputViewer.AnalogStick.Movement + UIWidgets::EnhancementSliderInt("Analog Stick Movement: %dpx", "##AnalogMovement", + "gInputViewer.AnalogStick.Movement", 0, 200, "", 12, true); + UIWidgets::Tooltip( + "Sets the distance to move the analog stick in the input viewer. Useful for custom input viewers."); + UIWidgets::PaddedSeparator(true, true); + } + + if (ImGui::CollapsingHeader("Additional (\"Right\") Stick")) { + // gInputViewer.RightStick.VisibilityMode + UIWidgets::PaddedText("Right Stick Visibility", true, false); + UIWidgets::EnhancementCombobox("gInputViewer.RightStick.VisibilityMode", stickModeOptions, + STICK_MODE_HIDDEN_IN_DEADZONE); + UIWidgets::Tooltip( + "Determines the conditions under which the moving layer of the right stick texture is visible."); + + // gInputViewer.RightStick.OutlineMode + UIWidgets::PaddedText("Right Stick Outline/Background Visibility", true, false); + UIWidgets::EnhancementCombobox("gInputViewer.RightStick.OutlineMode", stickModeOptions, + STICK_MODE_HIDDEN_IN_DEADZONE); + UIWidgets::Tooltip( + "Determines the conditions under which the right stick outline/background texture is visible."); + + // gInputViewer.RightStick.Movement + UIWidgets::EnhancementSliderInt("Right Stick Movement: %dpx", "##RightMovement", + "gInputViewer.RightStick.Movement", 0, 200, "", 7, true); + UIWidgets::Tooltip( + "Sets the distance to move the right stick in the input viewer. Useful for custom input viewers."); + UIWidgets::PaddedSeparator(true, true); + } + + if (ImGui::CollapsingHeader("Analog Angle Values")) { + // gAnalogAngles + UIWidgets::EnhancementCheckbox("Show Analog Stick Angle Values", "gInputViewer.AnalogAngles.Enabled"); + UIWidgets::Tooltip("Displays analog stick angle values in the input viewer"); + if (CVarGetInteger("gInputViewer.AnalogAngles.Enabled", 0)) { + // gInputViewer.AnalogAngles.TextColor + if (ImGui::ColorEdit4("Text Color", (float*)&textColor)) { + CVarSetColor("gInputViewer.AnalogAngles.TextColor", vec2Color(textColor)); + } + // gAnalogAngleScale + UIWidgets::EnhancementSliderFloat("Angle Text Scale: %.2f%%", "##AnalogAngleScale", + "gInputViewer.AnalogAngles.Scale", 0.1f, 5.0f, "", 1.0f, true, true); + // gInputViewer.AnalogAngles.Offset + UIWidgets::EnhancementSliderInt("Angle Text Offset: %dpx", "##AnalogAngleOffset", + "gInputViewer.AnalogAngles.Offset", 0, 400, "", 0, true); + UIWidgets::PaddedSeparator(true, true); + // gInputViewer.AnalogAngles.Range1.Enabled + UIWidgets::EnhancementCheckbox("Highlight ESS Position", "gInputViewer.AnalogAngles.Range1.Enabled"); + UIWidgets::Tooltip( + "Highlights the angle value text when the analog stick is in ESS position (on flat ground)"); + if (CVarGetInteger("gInputViewer.AnalogAngles.Range1.Enabled", 0)) { + // gInputViewer.AnalogAngles.Range1.Color + if (ImGui::ColorEdit4("ESS Color", (float*)&range1Color)) { + CVarSetColor("gInputViewer.AnalogAngles.Range1.Color", vec2Color(range1Color)); + } + } + + UIWidgets::PaddedSeparator(true, true); + // gInputViewer.AnalogAngles.Range2.Enabled + UIWidgets::EnhancementCheckbox("Highlight Walking Speed Angles", + "gInputViewer.AnalogAngles.Range2.Enabled"); + UIWidgets::Tooltip("Highlights the angle value text when the analog stick is at an angle that would " + "produce a walking speed (on flat ground)\n\n" + "Useful for 1.0 Empty Jumpslash Quick Put Away"); + if (CVarGetInteger("gInputViewer.AnalogAngles.Range2.Enabled", 0)) { + // gInputViewer.AnalogAngles.Range2.Color + if (ImGui::ColorEdit4("Walking Speed Color", (float*)&range2Color)) { + CVarSetColor("gInputViewer.AnalogAngles.Range2.Color", vec2Color(range2Color)); + } + } + } + } + + ImGui::End(); + } +} \ No newline at end of file diff --git a/soh/soh/Enhancements/controls/InputViewer.h b/soh/soh/Enhancements/controls/InputViewer.h new file mode 100644 index 000000000..646c15d9d --- /dev/null +++ b/soh/soh/Enhancements/controls/InputViewer.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +typedef enum { + BUTTON_OUTLINE_ALWAYS_SHOWN, + BUTTON_OUTLINE_NOT_PRESSED, + BUTTON_OUTLINE_PRESSED, + BUTTON_OUTLINE_ALWAYS_HIDDEN +} ButtonOutlineMode; + +typedef enum { + STICK_MODE_ALWAYS_SHOWN, + STICK_MODE_HIDDEN_IN_DEADZONE, + STICK_MODE_ALWAYS_HIDDEN, +} StickMode; + +class InputViewer : public LUS::GuiWindow { +public: + using LUS::GuiWindow::GuiWindow; + + void InitElement() override {}; + void DrawElement() override; + void UpdateElement() override {}; + + InputViewer(); + ~InputViewer(); + + void Draw(); + + private: + void RenderButton(std::string btn, std::string btnOutline, int state, ImVec2 size, int outlineMode); +}; + +class InputViewerSettingsWindow : public LUS::GuiWindow { +public: + using LUS::GuiWindow::GuiWindow; + + void InitElement() override {}; + void DrawElement() override; + void UpdateElement() override {}; + + InputViewerSettingsWindow(); + ~InputViewerSettingsWindow(); + + void Draw(); +}; \ No newline at end of file diff --git a/soh/soh/SohGui.cpp b/soh/soh/SohGui.cpp index a295fed66..4cb43d3a8 100644 --- a/soh/soh/SohGui.cpp +++ b/soh/soh/SohGui.cpp @@ -117,6 +117,8 @@ namespace SohGui { std::shared_ptr mInputEditorWindow; std::shared_ptr mAudioEditorWindow; + std::shared_ptr mInputViewer; + std::shared_ptr mInputViewerSettings; std::shared_ptr mCosmeticsEditorWindow; std::shared_ptr mActorViewerWindow; std::shared_ptr mColViewerWindow; @@ -165,6 +167,10 @@ namespace SohGui { mAudioEditorWindow = std::make_shared("gAudioEditor.WindowOpen", "Audio Editor"); gui->AddGuiWindow(mAudioEditorWindow); + mInputViewer = std::make_shared("gOpenWindows.InputViewer", "Input Viewer"); + gui->AddGuiWindow(mInputViewer); + mInputViewerSettings = std::make_shared("gOpenWindows.InputViewerSettings", "Input Viewer Settings"); + gui->AddGuiWindow(mInputViewerSettings); mCosmeticsEditorWindow = std::make_shared("gCosmeticsEditorEnabled", "Cosmetics Editor"); gui->AddGuiWindow(mCosmeticsEditorWindow); mActorViewerWindow = std::make_shared("gActorViewerEnabled", "Actor Viewer"); diff --git a/soh/soh/SohGui.hpp b/soh/soh/SohGui.hpp index 73a3addd4..0e1d970a8 100644 --- a/soh/soh/SohGui.hpp +++ b/soh/soh/SohGui.hpp @@ -11,6 +11,7 @@ #include #include "SohMenuBar.h" #include "Enhancements/audio/AudioEditor.h" +#include "Enhancements/controls/InputViewer.h" #include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/debugger/actorViewer.h" #include "Enhancements/debugger/colViewer.h" diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 8b33a72ef..d74649290 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -21,6 +21,7 @@ #include "Enhancements/audio/AudioEditor.h" +#include "Enhancements/controls/InputViewer.h" #include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/debugger/actorViewer.h" #include "Enhancements/debugger/colViewer.h" @@ -180,6 +181,8 @@ void DrawShipMenu() { } extern std::shared_ptr mInputEditorWindow; +extern std::shared_ptr mInputViewer; +extern std::shared_ptr mInputViewerSettings; extern std::shared_ptr mAdvancedResolutionSettingsWindow; void DrawSettingsMenu() { @@ -245,11 +248,25 @@ void DrawSettingsMenu() { #ifndef __SWITCH__ UIWidgets::EnhancementCheckbox("Menubar Controller Navigation", "gControlNav"); UIWidgets::Tooltip("Allows controller navigation of the SOH menu bar (Settings, Enhancements,...)\nCAUTION: This will disable game inputs while the menubar is visible.\n\nD-pad to move between items, A to select, and X to grab focus on the menu bar"); + UIWidgets::PaddedSeparator(); #endif - UIWidgets::PaddedEnhancementCheckbox("Show Inputs", "gInputEnabled", true, false); - UIWidgets::Tooltip("Shows currently pressed inputs on the bottom right of the screen"); - UIWidgets::PaddedEnhancementSliderFloat("Input Scale: %.2f", "##Input", "gInputScale", 1.0f, 3.0f, "", 1.0f, false, true, true, false); - UIWidgets::Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting"); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2 (12.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f)); + if (mInputViewer) { + if (ImGui::Button(GetWindowButtonText("Input Viewer", CVarGetInteger("gOpenWindows.InputViewer", 0)).c_str(), ImVec2 (-1.0f, 0.0f))) { + mInputViewer->ToggleVisibility(); + } + } + if (mInputViewerSettings) { + if (ImGui::Button(GetWindowButtonText("Input Viewer Settings", CVarGetInteger("gOpenWindows.InputViewerSettings", 0)).c_str(), ImVec2 (-1.0f, 0.0f))) { + mInputViewerSettings->ToggleVisibility(); + } + } + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(3); + UIWidgets::PaddedEnhancementSliderInt("Simulated Input Lag: %d frames", "##SimulatedInputLag", "gSimulatedInputLag", 0, 6, "", 0, true, true, false); UIWidgets::Tooltip("Buffers your inputs to be executed a specified amount of frames later");