f3e10f27d2
- Add controller hotbars: 8 cross bars (L2/R2 style), separate from normal hotbars 1-8 - Controller bar slot data stored in config (not game StandardHotbars) so layouts can differ per mode - Drag-and-drop on controller bars: from game, shift+drag rearrange, release outside to clear - Independent controller bar keybinds with modifier+trigger combinations (e.g. L2+South) - Optional 'Sync bar mode with game client': follow Character Config Mouse/Gamepad toggle (PadMode) - Clone/copy actions: normal hotbars ↔ controller bars - Restore controller bar layout button; deploy to devPlugins on Release build Made-with: Cursor
107 lines
4.0 KiB
C#
107 lines
4.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Dalamud.Game.ClientState.GamePad;
|
|
using Dalamud.Game.ClientState.Keys;
|
|
using Dalamud.Plugin.Services;
|
|
using HSUI.Config;
|
|
using HSUI.Interface.GeneralElements;
|
|
|
|
namespace HSUI.Helpers
|
|
{
|
|
/// <summary>Polls controller bar keybinds and executes slots when keys/buttons are pressed. Supports combinations (e.g. L2+South).</summary>
|
|
public static class ControllerBarKeybindExecutor
|
|
{
|
|
private static readonly Dictionary<string, bool> _triggerWasDown = new Dictionary<string, bool>();
|
|
|
|
public static void Process(IFramework framework)
|
|
{
|
|
try
|
|
{
|
|
if (!ControllerHotbarsConfig.GetEffectiveControllerHotbarsEnabled())
|
|
return;
|
|
|
|
var keybindsConfig = ConfigurationManager.Instance?.GetConfigObject<ControllerBarKeybindsConfig>();
|
|
if (keybindsConfig == null)
|
|
return;
|
|
|
|
if (ActionBarsManager.Instance == null)
|
|
return;
|
|
|
|
for (int bar = 1; bar <= ControllerBarKeybindsConfig.Bars; bar++)
|
|
{
|
|
for (int slot = 0; slot < ControllerBarKeybindsConfig.SlotsPerBar; slot++)
|
|
{
|
|
string binding = keybindsConfig.GetKeybind(bar, slot);
|
|
if (string.IsNullOrEmpty(binding)) continue;
|
|
|
|
bool triggered = IsKeybindTriggered(binding);
|
|
if (triggered)
|
|
ActionBarsManager.Instance.ExecuteControllerSlot(bar, slot);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Plugin.Logger?.Warning($"[HSUI ControllerBarKeybinds] Process: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>True when all modifiers are held and the trigger key/button was just pressed (edge).</summary>
|
|
private static bool IsKeybindTriggered(string binding)
|
|
{
|
|
if (string.IsNullOrEmpty(binding)) return false;
|
|
|
|
string[] parts = binding.Split('+');
|
|
for (int i = 0; i < parts.Length; i++)
|
|
parts[i] = parts[i].Trim();
|
|
|
|
if (parts.Length == 0) return false;
|
|
if (parts.Length == 1)
|
|
{
|
|
bool down = IsSingleKeybindDown(parts[0]);
|
|
if (!_triggerWasDown.TryGetValue(binding, out bool wasDown)) wasDown = false;
|
|
_triggerWasDown[binding] = down;
|
|
return down && !wasDown;
|
|
}
|
|
|
|
string triggerPart = parts[parts.Length - 1];
|
|
for (int i = 0; i < parts.Length - 1; i++)
|
|
{
|
|
if (!IsSingleKeybindDown(parts[i]))
|
|
return false;
|
|
}
|
|
bool triggerDown = IsSingleKeybindDown(triggerPart);
|
|
if (!_triggerWasDown.TryGetValue(binding, out bool triggerWasDown))
|
|
triggerWasDown = false;
|
|
_triggerWasDown[binding] = triggerDown;
|
|
return triggerDown && !triggerWasDown;
|
|
}
|
|
|
|
private static bool IsSingleKeybindDown(string part)
|
|
{
|
|
if (string.IsNullOrEmpty(part)) return false;
|
|
|
|
if (part.StartsWith("Key:", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string vkStr = part.Substring(4).Trim();
|
|
if (Plugin.KeyState == null) return false;
|
|
if (Enum.TryParse<VirtualKey>(vkStr, true, out var vk)
|
|
&& Plugin.KeyState.IsVirtualKeyValid((int)vk))
|
|
return Plugin.KeyState[vk];
|
|
return false;
|
|
}
|
|
|
|
if (part.StartsWith("Pad:", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string padStr = part.Substring(4).Trim();
|
|
if (Plugin.GamepadState == null) return false;
|
|
if (Enum.TryParse<GamepadButtons>(padStr, true, out var btn))
|
|
return Plugin.GamepadState.Raw(btn) > 0.5f;
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|