From aae4141550ab206fc2fbff6022f9a55d0d907988 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 23 Dec 2023 13:08:20 +0100 Subject: [PATCH] Add IPC to set single items. --- Glamourer/Api/GlamourerIpc.Set.cs | 92 +++++++++++++++++++ Glamourer/Api/GlamourerIpc.cs | 16 +++- Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs | 48 +++++++++- Penumbra.GameData | 2 +- 4 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 Glamourer/Api/GlamourerIpc.Set.cs diff --git a/Glamourer/Api/GlamourerIpc.Set.cs b/Glamourer/Api/GlamourerIpc.Set.cs new file mode 100644 index 0000000..f6fe92e --- /dev/null +++ b/Glamourer/Api/GlamourerIpc.Set.cs @@ -0,0 +1,92 @@ +using System.Linq; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin; +using Glamourer.Events; +using Glamourer.Services; +using Penumbra.Api.Helpers; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; + +namespace Glamourer.Api; + +public partial class GlamourerIpc +{ + public enum GlamourerErrorCode + { + Success, + ActorNotFound, + ActorNotHuman, + ItemInvalid, + } + + public const string LabelSetItem = "Glamourer.SetItem"; + public const string LabelSetItemByActorName = "Glamourer.SetItemByActorName"; + + + private readonly FuncProvider _setItemProvider; + private readonly FuncProvider _setItemByActorNameProvider; + + public static FuncSubscriber SetItemSubscriber(DalamudPluginInterface pi) + => new(pi, LabelSetItem); + + public static FuncSubscriber SetItemByActorNameSubscriber(DalamudPluginInterface pi) + => new(pi, LabelSetItemByActorName); + + private GlamourerErrorCode SetItem(Character? character, EquipSlot slot, CustomItemId itemId, uint key) + { + if (itemId.Id == 0) + itemId = ItemManager.NothingId(slot); + + var item = _items.Resolve(slot, itemId); + if (!item.Valid) + return GlamourerErrorCode.ItemInvalid; + + var identifier = _actors.FromObject(character, false, false, false); + if (!identifier.IsValid) + return GlamourerErrorCode.ActorNotFound; + + if (!_stateManager.TryGetValue(identifier, out var state)) + { + _objects.Update(); + var data = _objects[identifier]; + if (!data.Valid || !_stateManager.GetOrCreate(identifier, data.Objects[0], out state)) + return GlamourerErrorCode.ActorNotFound; + } + + if (!state.ModelData.IsHuman) + return GlamourerErrorCode.ActorNotHuman; + + _stateManager.ChangeItem(state, slot, item, StateChanged.Source.Ipc, key); + return GlamourerErrorCode.Success; + } + + private GlamourerErrorCode SetItemByActorName(string name, EquipSlot slot, CustomItemId itemId, uint key) + { + if (itemId.Id == 0) + itemId = ItemManager.NothingId(slot); + + var item = _items.Resolve(slot, itemId); + if (!item.Valid) + return GlamourerErrorCode.ItemInvalid; + + var found = false; + _objects.Update(); + foreach (var identifier in FindActorsRevert(name).Distinct()) + { + if (!_stateManager.TryGetValue(identifier, out var state)) + { + var data = _objects[identifier]; + if (!data.Valid || !_stateManager.GetOrCreate(identifier, data.Objects[0], out state)) + continue; + } + + if (!state.ModelData.IsHuman) + return GlamourerErrorCode.ActorNotHuman; + + _stateManager.ChangeItem(state, slot, item, StateChanged.Source.Ipc, key); + found = true; + } + + return found ? GlamourerErrorCode.Success : GlamourerErrorCode.ActorNotFound; + } +} diff --git a/Glamourer/Api/GlamourerIpc.cs b/Glamourer/Api/GlamourerIpc.cs index 53cfb18..a19dd6f 100644 --- a/Glamourer/Api/GlamourerIpc.cs +++ b/Glamourer/Api/GlamourerIpc.cs @@ -7,6 +7,7 @@ using Glamourer.Automation; using Glamourer.Designs; using Glamourer.Events; using Glamourer.Interop; +using Glamourer.Services; using Glamourer.State; using Penumbra.Api.Helpers; using Penumbra.GameData.Actors; @@ -15,7 +16,7 @@ using Penumbra.String; namespace Glamourer.Api; -public partial class GlamourerIpc : IDisposable +public sealed partial class GlamourerIpc : IDisposable { public const int CurrentApiVersionMajor = 0; public const int CurrentApiVersionMinor = 4; @@ -25,15 +26,18 @@ public partial class GlamourerIpc : IDisposable private readonly ActorManager _actors; private readonly DesignConverter _designConverter; private readonly AutoDesignApplier _autoDesignApplier; + private readonly ItemManager _items; public GlamourerIpc(DalamudPluginInterface pi, StateManager stateManager, ObjectManager objects, ActorManager actors, - DesignConverter designConverter, StateChanged stateChangedEvent, GPoseService gPose, AutoDesignApplier autoDesignApplier) + DesignConverter designConverter, StateChanged stateChangedEvent, GPoseService gPose, AutoDesignApplier autoDesignApplier, + ItemManager items) { _stateManager = stateManager; _objects = objects; _actors = actors; _designConverter = designConverter; _autoDesignApplier = autoDesignApplier; + _items = items; _gPose = gPose; _stateChangedEvent = stateChangedEvent; _apiVersionProvider = new FuncProvider(pi, LabelApiVersion, ApiVersion); @@ -76,6 +80,11 @@ public partial class GlamourerIpc : IDisposable _stateChangedProvider = new EventProvider>(pi, LabelStateChanged); _gPoseChangedProvider = new EventProvider(pi, LabelGPoseChanged); + _setItemProvider = new FuncProvider(pi, LabelSetItem, + (idx, slot, item, key) => (int)SetItem(idx, (EquipSlot)slot, item, key)); + _setItemByActorNameProvider = new FuncProvider(pi, LabelSetItemByActorName, + (name, slot, item, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, key)); + _stateChangedEvent.Subscribe(OnStateChanged, StateChanged.Priority.GlamourerIpc); _gPose.Subscribe(OnGPoseChanged, GPoseService.Priority.GlamourerIpc); } @@ -114,6 +123,9 @@ public partial class GlamourerIpc : IDisposable _stateChangedProvider.Dispose(); _gPose.Unsubscribe(OnGPoseChanged); _gPoseChangedProvider.Dispose(); + + _setItemProvider.Dispose(); + _setItemByActorNameProvider.Dispose(); } private IEnumerable FindActors(string actorName) diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs index e42d252..329a2af 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs @@ -1,10 +1,14 @@ using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Interface.Utility; using Dalamud.Plugin; using Glamourer.Api; using Glamourer.Interop; using ImGuiNET; using OtterGui; using OtterGui.Raii; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Gui; +using Penumbra.GameData.Structs; namespace Glamourer.Gui.Tabs.DebugTab; @@ -16,15 +20,20 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag public bool Disabled => false; - private int _gameObjectIndex; - private string _gameObjectName = string.Empty; - private string _base64Apply = string.Empty; + private int _gameObjectIndex; + private CustomItemId _customItemId; + private EquipSlot _slot = EquipSlot.Head; + private string _gameObjectName = string.Empty; + private string _base64Apply = string.Empty; + private GlamourerIpc.GlamourerErrorCode _setItemEc; + private GlamourerIpc.GlamourerErrorCode _setItemByActorNameEc; - public void Draw() + public unsafe void Draw() { ImGui.InputInt("Game Object Index", ref _gameObjectIndex, 0, 0); ImGui.InputTextWithHint("##gameObject", "Character Name...", ref _gameObjectName, 64); ImGui.InputTextWithHint("##base64", "Design Base64...", ref _base64Apply, 2047); + DrawItemIdInput(); using var table = ImRaii.Table("##ipc", 2, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); if (!table) return; @@ -104,5 +113,36 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag if (ImGui.Button("Revert##CustomizeCharacter")) GlamourerIpc.RevertToAutomationCharacterSubscriber(_pluginInterface) .Invoke(_objectManager.Objects[_gameObjectIndex] as Character, 1337); + + ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItem); + ImGui.TableNextColumn(); + if (ImGui.Button("Set##SetItem")) + _setItemEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemSubscriber(_pluginInterface) + .Invoke(_objectManager.Objects[_gameObjectIndex] as Character, (byte)_slot, _customItemId.Id, 1337); + if (_setItemEc != GlamourerIpc.GlamourerErrorCode.Success) + { + ImGui.SameLine(); + ImGui.TextUnformatted(_setItemEc.ToString()); + } + + ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemByActorName); + ImGui.TableNextColumn(); + if (ImGui.Button("Set##SetItemByActorName")) + _setItemByActorNameEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemByActorNameSubscriber(_pluginInterface) + .Invoke(_gameObjectName, (byte)_slot, _customItemId.Id, 1337); + if (_setItemByActorNameEc != GlamourerIpc.GlamourerErrorCode.Success) + { + ImGui.SameLine(); + ImGui.TextUnformatted(_setItemByActorNameEc.ToString()); + } + } + + private unsafe void DrawItemIdInput() + { + var tmp = _customItemId.Id; + if (ImGui.InputScalar("Custom Item ID", ImGuiDataType.U64, (nint)(&tmp), nint.Zero, nint.Zero)) + _customItemId = (CustomItemId)tmp; + ImGui.SameLine(); + EquipSlotCombo.Draw("Equip Slot", string.Empty, 200 * ImGuiHelpers.GlobalScale, ref _slot); } } diff --git a/Penumbra.GameData b/Penumbra.GameData index ed37f83..4af8775 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit ed37f83424c11a5a601e74f4660cd52ebd68a7b3 +Subproject commit 4af8775f0925ff89e6900c8816b03e0ffeb73f6d