mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-26 03:12:18 -05:00
Improve Controller LED Control (#2864)
* Add brightness control and on/off toggle for tunic LED colors. * Removed toggle, mentioned brightness of 0% for turning off LEDs. * Set up grabbing tunic color values from Cosmetics Editor for cosmetics sync. * Why these stupid differences between Windows and Linux compilers? * Fix Mac build errors? Also try to move the color fetching back into the switch statements to lessen potential computational load. * Real fix? * Move "Customize Game Controls" button under "Controller Configuration" button under Settings -> Controller. Renamed "Controller Configuration" to "Configure Controller" and "Customize Game Controls" to "Customize In-game Controls" * Added LEDColor and LEDColorSource enums for code clarity. Moved controller LED brightness to new LED Colors group in "Customize In-game Controls" menu. Added combobox to choose between vanilla tunics, cosmetics tunics, health, and custom as color sources. Added critical health override checkbox to allow display of red when health is low even when other sources are selected. Port color pickers have not been implemented yet, default color is white. * Moved LED control to OTRControllerCallback and wrapped it in a check to `CanSetLed()`. * Move settings to Port 1 tab in Customize In-game Controls and limited application of colors in `OTRControllerCallback` to port 1. * UI clarity updates. * Removed unnecessary LED color enum. Added custom color picker to port 1 color settings. * Changed Critical Health Override default to true. * Modified logic to not do color fetching and instead default to {0,0,0,0} when brightness is off. * Fix bad cvar string for custom color. * Cleaning up some post-merge artifacts. * Update soh/soh/Enhancements/controls/GameControlEditor.h Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> * Some name changes. * idea for cleaning up controller callback stuff * Rearranged color source checks to make sure criticalOverride is applied regardless of other settings. --------- Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> Co-authored-by: briaguya <briaguya@alice>
This commit is contained in:
parent
cebfcd1d88
commit
0c7e80a190
@ -2232,6 +2232,13 @@ typedef enum {
|
|||||||
/* 0x02 */ PAUSE_ANY_CURSOR_ALWAYS_OFF,
|
/* 0x02 */ PAUSE_ANY_CURSOR_ALWAYS_OFF,
|
||||||
} PauseCursorAnySlotOptions;
|
} PauseCursorAnySlotOptions;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LED_SOURCE_TUNIC_ORIGINAL,
|
||||||
|
LED_SOURCE_TUNIC_COSMETICS,
|
||||||
|
LED_SOURCE_HEALTH,
|
||||||
|
LED_SOURCE_CUSTOM
|
||||||
|
} LEDColorSource;
|
||||||
|
|
||||||
#define ROM_FILE(name) \
|
#define ROM_FILE(name) \
|
||||||
{ 0, 0, #name }
|
{ 0, 0, #name }
|
||||||
|
|
||||||
|
@ -10,10 +10,11 @@
|
|||||||
#include <ImGui/imgui.h>
|
#include <ImGui/imgui.h>
|
||||||
#include <ImGui/imgui_internal.h>
|
#include <ImGui/imgui_internal.h>
|
||||||
#include <libultraship/bridge.h>
|
#include <libultraship/bridge.h>
|
||||||
#include <libultraship/libultra/controller.h>
|
#include <libultraship/libultraship.h>
|
||||||
#include <Utils/StringHelper.h>
|
#include <Utils/StringHelper.h>
|
||||||
#include <ImGuiImpl.h>
|
#include <ImGuiImpl.h>
|
||||||
|
|
||||||
|
#include "Window.h"
|
||||||
#include "../../UIWidgets.hpp"
|
#include "../../UIWidgets.hpp"
|
||||||
|
|
||||||
namespace GameControlEditor {
|
namespace GameControlEditor {
|
||||||
@ -88,7 +89,7 @@ namespace GameControlEditor {
|
|||||||
void DrawUI(bool&);
|
void DrawUI(bool&);
|
||||||
|
|
||||||
void Init() {
|
void Init() {
|
||||||
LUS::AddWindow("Enhancements", "Game Control Editor", DrawUI, CVarGetInteger("gGameControlEditorEnabled", 0));
|
LUS::AddWindow("Enhancements", "Additional Controller Options", DrawUI, CVarGetInteger("gControllerOptionsEnabled", 0));
|
||||||
|
|
||||||
addButtonName(BTN_A, "A");
|
addButtonName(BTN_A, "A");
|
||||||
addButtonName(BTN_B, "B");
|
addButtonName(BTN_B, "B");
|
||||||
@ -325,13 +326,46 @@ namespace GameControlEditor {
|
|||||||
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
|
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
|
||||||
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
|
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
|
||||||
LUS::EndGroupPanel();
|
LUS::EndGroupPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawLEDControlPanel() {
|
||||||
|
LUS::BeginGroupPanel("LED Colors", ImGui::GetContentRegionAvail());
|
||||||
|
static const char* ledSources[4] = { "Original Tunic Colors", "Cosmetics Tunic Colors", "Health Colors", "Custom" };
|
||||||
|
UIWidgets::PaddedText("Source");
|
||||||
|
UIWidgets::EnhancementCombobox("gLedColorSource", ledSources, LED_SOURCE_TUNIC_ORIGINAL);
|
||||||
|
DrawHelpIcon("Health\n- Red when health critical (13-20% depending on max health)\n- Yellow when health < 40%. Green otherwise.\n\n" \
|
||||||
|
"Tunics: colors will mirror currently equipped tunic, whether original or the current values in Cosmetics Editor.\n\n" \
|
||||||
|
"Custom: single, solid color");
|
||||||
|
if (CVarGetInteger("gLedColorSource", 1) == 3) {
|
||||||
|
UIWidgets::Spacer(3);
|
||||||
|
auto port1Color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
|
||||||
|
ImVec4 colorVec = { port1Color.r / 255.0f, port1Color.g / 255.0f, port1Color.b / 255.0f, 1.0f };
|
||||||
|
if (ImGui::ColorEdit3("", (float*)&colorVec, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) {
|
||||||
|
Color_RGB8 color;
|
||||||
|
color.r = colorVec.x * 255.0;
|
||||||
|
color.g = colorVec.y * 255.0;
|
||||||
|
color.b = colorVec.z * 255.0;
|
||||||
|
|
||||||
|
CVarSetColor24("gLedPort1Color", color);
|
||||||
|
LUS::RequestCvarSaveOnNextTick();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("Custom Color");
|
||||||
|
}
|
||||||
|
UIWidgets::PaddedEnhancementSliderFloat("Brightness: %d%%", "##LED_Brightness", "gLedBrightness",
|
||||||
|
0.0f, 1.0f, "", 1.0f, true, true);
|
||||||
|
DrawHelpIcon("Sets the brightness of controller LEDs. 0% brightness = LEDs off.");
|
||||||
|
UIWidgets::PaddedEnhancementCheckbox("Critical Health Override", "gLedCriticalOverride", true, true,
|
||||||
|
CVarGetInteger("gLedColorSource", LED_SOURCE_TUNIC_ORIGINAL) == LED_SOURCE_HEALTH, "Override redundant for health source.",
|
||||||
|
UIWidgets::CheckboxGraphics::Cross, true);
|
||||||
|
DrawHelpIcon("Shows red color when health is critical, otherwise displays according to color source.");
|
||||||
|
LUS::EndGroupPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawUI(bool& open) {
|
void DrawUI(bool& open) {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
if (CVarGetInteger("gGameControlEditorEnabled", 0)) {
|
if (CVarGetInteger("gControllerOptionsEnabled", 0)) {
|
||||||
CVarClear("gGameControlEditorEnabled");
|
CVarClear("gControllerOptionsEnabled");
|
||||||
LUS::RequestCvarSaveOnNextTick();
|
LUS::RequestCvarSaveOnNextTick();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -361,6 +395,9 @@ namespace GameControlEditor {
|
|||||||
DrawMiscControlPanel();
|
DrawMiscControlPanel();
|
||||||
} else {
|
} else {
|
||||||
DrawCustomButtons();
|
DrawCustomButtons();
|
||||||
|
if (CurrentPort == 1 && LUS::Context::GetInstance()->GetControlDeck()->GetDeviceFromPortIndex(0)->CanSetLed()) {
|
||||||
|
DrawLEDControlPanel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
@ -163,7 +163,7 @@ namespace GameMenuBar {
|
|||||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f));
|
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f));
|
||||||
if (ImGui::Button(GetWindowButtonText("Controller Configuration", CVarGetInteger("gControllerConfigurationEnabled", 0)).c_str(), ImVec2 (-1.0f, 0.0f)))
|
if (ImGui::Button(GetWindowButtonText("Controller Mapping", CVarGetInteger("gControllerConfigurationEnabled", 0)).c_str(), ImVec2 (-1.0f, 0.0f)))
|
||||||
{
|
{
|
||||||
if (CVarGetInteger("gControllerConfigurationEnabled", 0)) {
|
if (CVarGetInteger("gControllerConfigurationEnabled", 0)) {
|
||||||
CVarClear("gControllerConfigurationEnabled");
|
CVarClear("gControllerConfigurationEnabled");
|
||||||
@ -173,6 +173,16 @@ namespace GameMenuBar {
|
|||||||
LUS::RequestCvarSaveOnNextTick();
|
LUS::RequestCvarSaveOnNextTick();
|
||||||
LUS::ToggleInputEditorWindow(CVarGetInteger("gControllerConfigurationEnabled", 0));
|
LUS::ToggleInputEditorWindow(CVarGetInteger("gControllerConfigurationEnabled", 0));
|
||||||
}
|
}
|
||||||
|
if (ImGui::Button(GetWindowButtonText("Additional Controller Options", CVarGetInteger("gControllerOptionsEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f)))
|
||||||
|
{
|
||||||
|
if (CVarGetInteger("gControllerOptionsEnabled", 0)) {
|
||||||
|
CVarClear("gControllerOptionsEnabled");
|
||||||
|
} else {
|
||||||
|
CVarSetInteger("gControllerOptionsEnabled", 1);
|
||||||
|
}
|
||||||
|
LUS::RequestCvarSaveOnNextTick();
|
||||||
|
LUS::EnableWindow("Additional Controller Options", CVarGetInteger("gControllerOptionsEnabled", 0));
|
||||||
|
}
|
||||||
UIWidgets::PaddedSeparator();
|
UIWidgets::PaddedSeparator();
|
||||||
ImGui::PopStyleColor(1);
|
ImGui::PopStyleColor(1);
|
||||||
ImGui::PopStyleVar(3);
|
ImGui::PopStyleVar(3);
|
||||||
@ -186,7 +196,6 @@ namespace GameMenuBar {
|
|||||||
UIWidgets::Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting");
|
UIWidgets::Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting");
|
||||||
UIWidgets::PaddedEnhancementSliderInt("Simulated Input Lag: %d frames", "##SimulatedInputLag", "gSimulatedInputLag", 0, 6, "", 0, true, true, false);
|
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");
|
UIWidgets::Tooltip("Buffers your inputs to be executed a specified amount of frames later");
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,16 +999,6 @@ namespace GameMenuBar {
|
|||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f));
|
||||||
|
|
||||||
if (ImGui::Button(GetWindowButtonText("Customize Game Controls", CVarGetInteger("gGameControlEditorEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f)))
|
|
||||||
{
|
|
||||||
if (CVarGetInteger("gGameControlEditorEnabled", 0)) {
|
|
||||||
CVarClear("gGameControlEditorEnabled");
|
|
||||||
} else {
|
|
||||||
CVarSetInteger("gGameControlEditorEnabled", 1);
|
|
||||||
}
|
|
||||||
LUS::RequestCvarSaveOnNextTick();
|
|
||||||
LUS::EnableWindow("Game Control Editor", CVarGetInteger("gGameControlEditorEnabled", 0));
|
|
||||||
}
|
|
||||||
if (ImGui::Button(GetWindowButtonText("Cosmetics Editor", CVarGetInteger("gCosmeticsEditorEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f)))
|
if (ImGui::Button(GetWindowButtonText("Cosmetics Editor", CVarGetInteger("gCosmeticsEditorEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f)))
|
||||||
{
|
{
|
||||||
if (CVarGetInteger("gCosmeticsEditorEnabled", 0)) {
|
if (CVarGetInteger("gCosmeticsEditorEnabled", 0)) {
|
||||||
|
@ -122,6 +122,10 @@ SpeechSynthesizer* SpeechSynthesizer::Instance;
|
|||||||
extern "C" char** cameraStrings;
|
extern "C" char** cameraStrings;
|
||||||
std::vector<std::shared_ptr<std::string>> cameraStdStrings;
|
std::vector<std::shared_ptr<std::string>> cameraStdStrings;
|
||||||
|
|
||||||
|
Color_RGB8 kokiriColor = { 0x1E, 0x69, 0x1B };
|
||||||
|
Color_RGB8 goronColor = { 0x64, 0x14, 0x00 };
|
||||||
|
Color_RGB8 zoraColor = { 0x00, 0xEC, 0x64 };
|
||||||
|
|
||||||
// OTRTODO: A lot of these left in Japanese are used by the mempak manager. LUS does not currently support mempaks. Ignore unused ones.
|
// OTRTODO: A lot of these left in Japanese are used by the mempak manager. LUS does not currently support mempaks. Ignore unused ones.
|
||||||
const char* constCameraStrings[] = {
|
const char* constCameraStrings[] = {
|
||||||
"INSUFFICIENT",
|
"INSUFFICIENT",
|
||||||
@ -1590,28 +1594,63 @@ extern "C" uint32_t OTRGetCurrentHeight() {
|
|||||||
return OTRGlobals::Instance->context->GetWindow()->GetCurrentHeight();
|
return OTRGlobals::Instance->context->GetWindow()->GetCurrentHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void OTRControllerCallback(uint8_t rumble, uint8_t ledColor) {
|
Color_RGB8 GetColorForControllerLED() {
|
||||||
auto controlDeck = LUS::Context::GetInstance()->GetControlDeck();
|
auto brightness = CVarGetFloat("gLedBrightness", 1.0f) / 1.0f;
|
||||||
|
Color_RGB8 color = { 0, 0, 0 };
|
||||||
for (int i = 0; i < controlDeck->GetNumConnectedPorts(); ++i) {
|
if (brightness > 0.0f) {
|
||||||
auto physicalDevice = controlDeck->GetDeviceFromPortIndex(i);
|
LEDColorSource source = static_cast<LEDColorSource>(CVarGetInteger("gLedColorSource", LED_SOURCE_TUNIC_ORIGINAL));
|
||||||
switch (ledColor) {
|
bool criticalOverride = CVarGetInteger("gLedCriticalOverride", 1);
|
||||||
case 0:
|
if (gPlayState && (source == LED_SOURCE_TUNIC_ORIGINAL || source == LED_SOURCE_TUNIC_COSMETICS)) {
|
||||||
physicalDevice->SetLedColor(i, {255, 0, 0});
|
switch (CUR_EQUIP_VALUE(EQUIP_TUNIC) - 1) {
|
||||||
break;
|
case PLAYER_TUNIC_KOKIRI:
|
||||||
case 1:
|
color = source == LED_SOURCE_TUNIC_COSMETICS
|
||||||
physicalDevice->SetLedColor(i, {0x1E, 0x69, 0x1B});
|
? CVarGetColor24("gCosmetics.Link_KokiriTunic.Value", kokiriColor)
|
||||||
break;
|
: kokiriColor;
|
||||||
case 2:
|
break;
|
||||||
physicalDevice->SetLedColor(i, {0x64, 0x14, 0x00});
|
case PLAYER_TUNIC_GORON:
|
||||||
break;
|
color = source == LED_SOURCE_TUNIC_COSMETICS
|
||||||
case 3:
|
? CVarGetColor24("gCosmetics.Link_GoronTunic.Value", goronColor)
|
||||||
physicalDevice->SetLedColor(i, {0x00, 0x3C, 0x64});
|
: goronColor;
|
||||||
break;
|
break;
|
||||||
|
case PLAYER_TUNIC_ZORA:
|
||||||
|
color = source == LED_SOURCE_TUNIC_COSMETICS
|
||||||
|
? CVarGetColor24("gCosmetics.Link_ZoraTunic.Value", zoraColor)
|
||||||
|
: zoraColor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (source == LED_SOURCE_CUSTOM) {
|
||||||
physicalDevice->SetRumble(i, rumble);
|
color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
|
||||||
|
}
|
||||||
|
if (criticalOverride || source == LED_SOURCE_HEALTH) {
|
||||||
|
if (HealthMeter_IsCritical()) {
|
||||||
|
color = { 0xFF, 0, 0 };
|
||||||
|
} else if (source == LED_SOURCE_HEALTH) {
|
||||||
|
if (gSaveContext.health / gSaveContext.healthCapacity <= 0.4f) {
|
||||||
|
color = { 0xFF, 0xFF, 0 };
|
||||||
|
} else {
|
||||||
|
color = { 0, 0xFF, 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color.r = color.r * brightness;
|
||||||
|
color.g = color.g * brightness;
|
||||||
|
color.b = color.b * brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void OTRControllerCallback(uint8_t rumble) {
|
||||||
|
auto physicalDevice = LUS::Context::GetInstance()->GetControlDeck()->GetDeviceFromPortIndex(0);
|
||||||
|
|
||||||
|
if (physicalDevice->CanSetLed()) {
|
||||||
|
// We call this every tick, SDL accounts for this use and prevents driver spam
|
||||||
|
// https://github.com/libsdl-org/SDL/blob/f17058b562c8a1090c0c996b42982721ace90903/src/joystick/SDL_joystick.c#L1114-L1144
|
||||||
|
physicalDevice->SetLedColor(0, GetColorForControllerLED());
|
||||||
|
}
|
||||||
|
|
||||||
|
physicalDevice->SetRumble(0, rumble);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" float OTRGetAspectRatio() {
|
extern "C" float OTRGetAspectRatio() {
|
||||||
|
@ -295,25 +295,7 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t rumble = (padMgr->rumbleEnable[0] > 0);
|
uint8_t rumble = (padMgr->rumbleEnable[0] > 0);
|
||||||
uint8_t ledColor = 1;
|
OTRControllerCallback(rumble);
|
||||||
|
|
||||||
if (HealthMeter_IsCritical()) {
|
|
||||||
ledColor = 0;
|
|
||||||
} else if (gPlayState) {
|
|
||||||
switch (CUR_EQUIP_VALUE(EQUIP_TUNIC) - 1) {
|
|
||||||
case PLAYER_TUNIC_KOKIRI:
|
|
||||||
ledColor = 1;
|
|
||||||
break;
|
|
||||||
case PLAYER_TUNIC_GORON:
|
|
||||||
ledColor = 2;
|
|
||||||
break;
|
|
||||||
case PLAYER_TUNIC_ZORA:
|
|
||||||
ledColor = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OTRControllerCallback(rumble, ledColor);
|
|
||||||
|
|
||||||
if (CVarGetInteger("gPauseBufferBlockInputFrame", 0)) {
|
if (CVarGetInteger("gPauseBufferBlockInputFrame", 0)) {
|
||||||
Controller_BlockGameInput();
|
Controller_BlockGameInput();
|
||||||
|
Loading…
Reference in New Issue
Block a user