Allow drag & drop of equipment pieces.

This commit is contained in:
Ottermandias 2025-06-03 18:40:41 +02:00
parent d7b189b714
commit 282935c6d6
6 changed files with 169 additions and 4 deletions

View file

@ -0,0 +1,83 @@
using Glamourer.Services;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment;
[InlineArray(13)]
public struct EquipItemSlotCache
{
private EquipItem _element;
public EquipItem Dragged
{
get => this[^1];
set => this[^1] = value;
}
public void Clear()
=> ((Span<EquipItem>)this).Clear();
public EquipItem this[EquipSlot slot]
{
get => this[(int)slot.ToIndex()];
set => this[(int)slot.ToIndex()] = value;
}
public void Update(ItemManager items, in EquipItem item, EquipSlot startSlot)
{
if (item.Id == Dragged.Id && item.Type == Dragged.Type)
return;
switch (startSlot)
{
case EquipSlot.MainHand:
{
Clear();
this[EquipSlot.MainHand] = item;
if (item.Type is FullEquipType.Sword)
this[EquipSlot.OffHand] = items.FindClosestShield(item.ItemId, out var shield) ? shield : default;
else
this[EquipSlot.OffHand] = items.ItemData.Secondary.GetValueOrDefault(item.ItemId);
break;
}
case EquipSlot.OffHand:
{
Clear();
if (item.Type is FullEquipType.Shield)
this[EquipSlot.MainHand] = items.FindClosestSword(item.ItemId, out var sword) ? sword : default;
else
this[EquipSlot.MainHand] = items.ItemData.Primary.GetValueOrDefault(item.ItemId);
this[EquipSlot.OffHand] = item;
break;
}
default:
{
this[EquipSlot.MainHand] = default;
this[EquipSlot.OffHand] = default;
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
if (startSlot == slot)
{
this[slot] = item;
continue;
}
var slotItem = items.Identify(slot, item.PrimaryId, item.Variant);
if (!slotItem.Valid || slotItem.ItemId.Id is not 0 != item.ItemId.Id is not 0)
{
slotItem = items.Identify(EquipSlot.OffHand, item.PrimaryId, item.SecondaryId, 1, item.Type);
if (slotItem.ItemId.Id is not 0 != item.ItemId.Id is not 0)
slotItem = default;
}
this[slot] = slotItem;
}
break;
}
}
Dragged = item;
}
}

View file

@ -38,6 +38,8 @@ public class EquipmentDrawer
private float _requiredComboWidth; private float _requiredComboWidth;
private Stain? _draggedStain; private Stain? _draggedStain;
private EquipItemSlotCache _draggedItem;
private EquipSlot _dragTarget;
public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, TextureService textures, public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, TextureService textures,
Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes, ItemCopyService itemCopy) Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes, ItemCopyService itemCopy)
@ -80,6 +82,7 @@ public class EquipmentDrawer
_requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale; _requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale;
_advancedMaterialColor = ColorId.AdvancedDyeActive.Value(); _advancedMaterialColor = ColorId.AdvancedDyeActive.Value();
_dragTarget = EquipSlot.Unknown;
} }
private bool VerifyRestrictedGear(EquipDrawData data) private bool VerifyRestrictedGear(EquipDrawData data)
@ -429,7 +432,7 @@ public class EquipmentDrawer
using var dragSource = ImUtf8.DragDropSource(); using var dragSource = ImUtf8.DragDropSource();
if (dragSource.Success) if (dragSource.Success)
{ {
if (DragDropSource.SetPayload("stainDragDrop"u8)) DragDropSource.SetPayload("stainDragDrop"u8);
_draggedStain = stain; _draggedStain = stain;
ImUtf8.Text($"Dragging {stain.Name}..."); ImUtf8.Text($"Dragging {stain.Name}...");
} }
@ -455,6 +458,7 @@ public class EquipmentDrawer
using var disabled = ImRaii.Disabled(data.Locked); using var disabled = ImRaii.Disabled(data.Locked);
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth); _requiredComboWidth);
DrawGearDragDrop(data);
if (change) if (change)
data.SetItem(combo.CurrentSelection); data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0) else if (combo.CustomVariant.Id > 0)
@ -495,6 +499,50 @@ public class EquipmentDrawer
data.SetItem(item); data.SetItem(item);
} }
private void DrawGearDragDrop(in EquipDrawData data)
{
if (data.CurrentItem.Valid)
{
using var dragSource = ImUtf8.DragDropSource();
if (dragSource.Success)
{
DragDropSource.SetPayload("equipDragDrop"u8);
_draggedItem.Update(_items, data.CurrentItem, data.Slot);
}
}
using var dragTarget = ImUtf8.DragDropTarget();
if (!dragTarget)
return;
var item = _draggedItem[data.Slot];
if (!item.Valid)
return;
_dragTarget = data.Slot;
if (!dragTarget.IsDropping("equipDragDrop"u8))
return;
data.SetItem(item);
_draggedItem.Clear();
}
public unsafe void DrawDragDropTooltip()
{
var payload = ImGui.GetDragDropPayload().NativePtr;
if (payload is null)
return;
if (!MemoryMarshal.CreateReadOnlySpanFromNullTerminated(payload->DataType).SequenceEqual("equipDragDrop"u8))
return;
using var tt = ImUtf8.Tooltip();
if (_dragTarget is EquipSlot.Unknown)
ImUtf8.Text($"Dragging {_draggedItem.Dragged.Name}...");
else
ImUtf8.Text($"Converting to {_draggedItem[_dragTarget].Name}...");
}
private static bool ResetOrClear<T>(bool locked, bool clicked, bool allowRevert, bool allowClear, private static bool ResetOrClear<T>(bool locked, bool clicked, bool allowRevert, bool allowClear,
in T currentItem, in T revertItem, in T clearItem, out T? item) where T : IEquatable<T> in T currentItem, in T revertItem, in T clearItem, out T? item) where T : IEquatable<T>
{ {
@ -546,6 +594,7 @@ public class EquipmentDrawer
else if (combo.CustomVariant.Id > 0 && (drawAll || ItemData.ConvertWeaponId(combo.CustomSetId) == mainhand.CurrentItem.Type)) else if (combo.CustomVariant.Id > 0 && (drawAll || ItemData.ConvertWeaponId(combo.CustomSetId) == mainhand.CurrentItem.Type))
changedItem = _items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant); changedItem = _items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant);
_itemCopy.HandleCopyPaste(mainhand); _itemCopy.HandleCopyPaste(mainhand);
DrawGearDragDrop(mainhand);
if (ResetOrClear(mainhand.Locked || unknown, open, mainhand.AllowRevert, false, mainhand.CurrentItem, mainhand.GameItem, if (ResetOrClear(mainhand.Locked || unknown, open, mainhand.AllowRevert, false, mainhand.CurrentItem, mainhand.GameItem,
default, out var c)) default, out var c))
@ -589,6 +638,7 @@ public class EquipmentDrawer
else if (combo.CustomVariant.Id > 0 && ItemData.ConvertWeaponId(combo.CustomSetId) == offhand.CurrentItem.Type) else if (combo.CustomVariant.Id > 0 && ItemData.ConvertWeaponId(combo.CustomSetId) == offhand.CurrentItem.Type)
offhand.SetItem(_items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant)); offhand.SetItem(_items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant));
_itemCopy.HandleCopyPaste(offhand); _itemCopy.HandleCopyPaste(offhand);
DrawGearDragDrop(offhand);
var defaultOffhand = _items.GetDefaultOffhand(mainhand.CurrentItem); var defaultOffhand = _items.GetDefaultOffhand(mainhand.CurrentItem);
if (ResetOrClear(locked, clear, offhand.AllowRevert, true, offhand.CurrentItem, offhand.GameItem, defaultOffhand, out var item)) if (ResetOrClear(locked, clear, offhand.AllowRevert, true, offhand.CurrentItem, offhand.GameItem, defaultOffhand, out var item))

View file

@ -238,6 +238,7 @@ public class ActorPanel
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
DrawEquipmentMetaToggles(); DrawEquipmentMetaToggles();
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
_equipmentDrawer.DrawDragDropTooltip();
} }
private void DrawParameterHeader() private void DrawParameterHeader()

View file

@ -130,6 +130,7 @@ public class DesignPanel
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
DrawEquipmentMetaToggles(); DrawEquipmentMetaToggles();
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
_equipmentDrawer.DrawDragDropTooltip();
} }
private void DrawEquipmentMetaToggles() private void DrawEquipmentMetaToggles()

View file

@ -174,6 +174,36 @@ public class ItemManager
return NothingItem(offhandType); return NothingItem(offhandType);
} }
public bool FindClosestShield(ItemId id, out EquipItem item)
{
var list = ItemData.ByType[FullEquipType.Shield];
try
{
item = list.Where(i => i.ItemId.Id > id.Id && i.ItemId.Id - id.Id < 50).MinBy(i => i.ItemId.Id);
return true;
}
catch
{
item = default;
return false;
}
}
public bool FindClosestSword(ItemId id, out EquipItem item)
{
var list = ItemData.ByType[FullEquipType.Sword];
try
{
item = list.Where(i => i.ItemId.Id < id.Id && id.Id - i.ItemId.Id < 50).MaxBy(i => i.ItemId.Id);
return true;
}
catch
{
item = default;
return false;
}
}
public EquipItem Identify(EquipSlot slot, PrimaryId id, SecondaryId type, Variant variant, public EquipItem Identify(EquipSlot slot, PrimaryId id, SecondaryId type, Variant variant,
FullEquipType mainhandType = FullEquipType.Unknown) FullEquipType mainhandType = FullEquipType.Unknown)
{ {

@ -1 +1 @@
Subproject commit 574ef3a8afd42b949e713e247a0b812886f088bb Subproject commit ff7b3b4014a97455f823380c78b8a7c5107f8e2f