feat: Controller hotbars with cross layout, separate storage, and sync with game
- 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
This commit is contained in:
@@ -39,19 +39,19 @@ namespace HSUI.Interface.GeneralElements
|
||||
/// <summary>When we had a game drag and the user released, suppress all slot clicks this frame (release is not a click).</summary>
|
||||
private bool _suppressSlotClicksAfterDragRelease;
|
||||
|
||||
private const string SlotPayloadType = "HSUI_HOTBAR_SLOT";
|
||||
internal const string SlotPayloadType = "HSUI_HOTBAR_SLOT";
|
||||
private const int SlotsPerBar = 12;
|
||||
|
||||
private static int _imGuiDragSourceSlotId = -1;
|
||||
private static bool _anyHotbarAcceptedDrop;
|
||||
private static int _lastOverlayFrame = -1;
|
||||
internal static int _imGuiDragSourceSlotId = -1;
|
||||
internal static bool _anyHotbarAcceptedDrop;
|
||||
internal static int _lastOverlayFrame = -1;
|
||||
/// <summary>Deferred clear when release-outside: set when no overlay accepted, executed next frame.</summary>
|
||||
private static int _pendingReleaseOutsideSlotId = -1;
|
||||
internal static int _pendingReleaseOutsideSlotId = -1;
|
||||
/// <summary>To avoid PICKUP log spam: only log when we first start dragging a new slot.</summary>
|
||||
private static int _lastLoggedPickupSlotId = -1;
|
||||
internal static int _lastLoggedPickupSlotId = -1;
|
||||
|
||||
/// <summary>Encode hotbar (1-10) and slot (0-11) into internal slot id (0-119).</summary>
|
||||
private static int ToSlotId(int hotbarIndex, int slotIndex)
|
||||
/// <summary>Encode hotbar (1-10) and slot (0-11) into internal slot id (0-119). Controller bars use 1-8 and 0-7.</summary>
|
||||
internal static int ToSlotId(int hotbarIndex, int slotIndex)
|
||||
{
|
||||
int bar = Math.Clamp(hotbarIndex, 1, 10) - 1;
|
||||
int slot = Math.Clamp(slotIndex, 0, 11);
|
||||
@@ -59,14 +59,14 @@ namespace HSUI.Interface.GeneralElements
|
||||
}
|
||||
|
||||
/// <summary>Decode internal slot id to hotbar (1-10) and slot (0-11). Returns (-1,-1) if invalid.</summary>
|
||||
private static (int hotbarIndex, int slotIndex) FromSlotId(int slotId)
|
||||
internal static (int hotbarIndex, int slotIndex) FromSlotId(int slotId)
|
||||
{
|
||||
if (slotId < 0 || slotId >= 10 * SlotsPerBar) return (-1, -1);
|
||||
return (slotId / SlotsPerBar + 1, slotId % SlotsPerBar);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct SlotDragPayload
|
||||
internal struct SlotDragPayload
|
||||
{
|
||||
public int SlotId;
|
||||
}
|
||||
@@ -118,6 +118,9 @@ namespace HSUI.Interface.GeneralElements
|
||||
{
|
||||
if (!Config.Enabled || Actor == null)
|
||||
return;
|
||||
// When controller hotbars are effectively enabled (manual or sync with game), normal hotbars are hidden
|
||||
if (ControllerHotbarsConfig.GetEffectiveControllerHotbarsEnabled())
|
||||
return;
|
||||
|
||||
if (ActionBarsManager.Instance == null)
|
||||
return;
|
||||
@@ -496,7 +499,7 @@ namespace HSUI.Interface.GeneralElements
|
||||
return true;
|
||||
}
|
||||
|
||||
private static HotbarsConfig? GetHotbarsConfig() =>
|
||||
internal static HotbarsConfig? GetHotbarsConfig() =>
|
||||
ConfigurationManager.Instance?.GetConfigObject<HotbarsConfig>();
|
||||
|
||||
private static (int Type, int Int1, int Int2, double LastTime) _lastDragNoPayloadLog;
|
||||
@@ -515,7 +518,7 @@ namespace HSUI.Interface.GeneralElements
|
||||
return true;
|
||||
}
|
||||
|
||||
private static unsafe bool IsGameDragging()
|
||||
internal static unsafe bool IsGameDragging()
|
||||
{
|
||||
var stage = AtkStage.Instance();
|
||||
if (stage == null) return false;
|
||||
@@ -523,7 +526,7 @@ namespace HSUI.Interface.GeneralElements
|
||||
return dm->IsDragging;
|
||||
}
|
||||
|
||||
private static unsafe bool TryGetSlotPayload(ImGuiPayloadPtr payload, out SlotDragPayload src)
|
||||
internal static unsafe bool TryGetSlotPayload(ImGuiPayloadPtr payload, out SlotDragPayload src)
|
||||
{
|
||||
if (payload.Data == null || payload.DataSize < sizeof(SlotDragPayload))
|
||||
{
|
||||
@@ -534,7 +537,7 @@ namespace HSUI.Interface.GeneralElements
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void SetSlotPayload(SlotDragPayload pl)
|
||||
internal static void SetSlotPayload(SlotDragPayload pl)
|
||||
{
|
||||
var type = new ImU8String(SlotPayloadType);
|
||||
var data = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref pl, 1));
|
||||
@@ -840,10 +843,10 @@ namespace HSUI.Interface.GeneralElements
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets drag payload from game/Dalamud. When game reports Item drag, prioritize HoveredItem
|
||||
/// (HoveredAction can be stale from previous hotbar hover). Preserves full item id including HQ flag.
|
||||
/// Gets drag payload from game/Dalamud. When game reports Item drag, prioritize HoveredItem.
|
||||
/// Used by ActionBarsHud and CrossBarHud for drag-drop.
|
||||
/// </summary>
|
||||
private static unsafe bool TryGetDragPayload(out RaptureHotbarModule.HotbarSlotType slotType, out uint id)
|
||||
internal static unsafe bool TryGetDragPayload(out RaptureHotbarModule.HotbarSlotType slotType, out uint id)
|
||||
{
|
||||
slotType = RaptureHotbarModule.HotbarSlotType.Empty;
|
||||
id = 0;
|
||||
@@ -1094,7 +1097,7 @@ namespace HSUI.Interface.GeneralElements
|
||||
};
|
||||
}
|
||||
|
||||
private static unsafe uint GetIconIdForPayload(RaptureHotbarModule.HotbarSlotType slotType, uint id)
|
||||
internal static unsafe uint GetIconIdForPayload(RaptureHotbarModule.HotbarSlotType slotType, uint id)
|
||||
{
|
||||
// id 0 is valid for GearSet (first gearset in list) and Macro; don't short-circuit for those
|
||||
if (id == 0 && slotType != RaptureHotbarModule.HotbarSlotType.GearSet && slotType != RaptureHotbarModule.HotbarSlotType.Macro)
|
||||
@@ -1155,6 +1158,19 @@ namespace HSUI.Interface.GeneralElements
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
/// <summary>Public for CrossBarHud and other consumers. Returns (title, body) for a hotbar slot.</summary>
|
||||
public static (string title, string text) GetSlotTooltipPublic(ActionBarsManager.SlotInfo slot)
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetSlotTooltip(slot);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return ("", "");
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe (string title, string text) GetSlotTooltip(ActionBarsManager.SlotInfo slot)
|
||||
{
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user