From 5971592217e9f74662718a0e19014d69f05da1b8 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 10 Aug 2024 11:52:12 +0200 Subject: [PATCH] Add BonusItem API. --- Glamourer.Api | 2 +- Glamourer/Api/IpcProviders.cs | 3 +- Glamourer/Api/ItemsApi.cs | 69 +++++++++++++++++++ Glamourer/Gui/GlamourerChangelog.cs | 1 + .../Tabs/DebugTab/IpcTester/ItemsIpcTester.cs | 21 +++++- .../Gui/Tabs/UnlocksTab/UnlockOverview.cs | 10 +-- Penumbra.GameData | 2 +- 7 files changed, 100 insertions(+), 8 deletions(-) diff --git a/Glamourer.Api b/Glamourer.Api index 4bad56d..b1b90e6 160000 --- a/Glamourer.Api +++ b/Glamourer.Api @@ -1 +1 @@ -Subproject commit 4bad56d610132b419335b89896e1f387b9ba2039 +Subproject commit b1b90e6ecfeee76a12cb27793753fa87af21083f diff --git a/Glamourer/Api/IpcProviders.cs b/Glamourer/Api/IpcProviders.cs index efbc368..8639a22 100644 --- a/Glamourer/Api/IpcProviders.cs +++ b/Glamourer/Api/IpcProviders.cs @@ -35,7 +35,8 @@ public sealed class IpcProviders : IDisposable, IApiService (a, b, c, d, e, f) => (int)api.Items.SetItem(a, (ApiEquipSlot)b, c, [d], e, (ApplyFlag)f)), new FuncProvider(pi, IpcSubscribers.Legacy.SetItemName.Label, (a, b, c, d, e, f) => (int)api.Items.SetItemName(a, (ApiEquipSlot)b, c, [d], e, (ApplyFlag)f)), - + IpcSubscribers.SetBonusItem.Provider(pi, api.Items), + IpcSubscribers.SetBonusItemName.Provider(pi, api.Items), IpcSubscribers.GetState.Provider(pi, api.State), IpcSubscribers.GetStateName.Provider(pi, api.State), IpcSubscribers.GetStateBase64.Provider(pi, api.State), diff --git a/Glamourer/Api/ItemsApi.cs b/Glamourer/Api/ItemsApi.cs index a2e3533..a516b68 100644 --- a/Glamourer/Api/ItemsApi.cs +++ b/Glamourer/Api/ItemsApi.cs @@ -57,6 +57,64 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager ApiHelpers.Lock(state, key, flags); } + if (!anyFound) + return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args); + + if (!anyHuman) + return ApiHelpers.Return(GlamourerApiEc.ActorNotHuman, args); + + if (!anyUnlocked) + return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args); + + return ApiHelpers.Return(GlamourerApiEc.Success, args); + } + + public GlamourerApiEc SetBonusItem(int objectIndex, ApiBonusSlot slot, ulong bonusItemId, uint key, ApplyFlag flags) + { + var args = ApiHelpers.Args("Index", objectIndex, "Slot", slot, "ID", bonusItemId, "Key", key, "Flags", flags); + if (!ResolveBonusItem(slot, bonusItemId, out var item)) + return ApiHelpers.Return(GlamourerApiEc.ItemInvalid, args); + + if (helpers.FindState(objectIndex) is not { } state) + return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args); + + if (!state.ModelData.IsHuman) + return ApiHelpers.Return(GlamourerApiEc.ActorNotHuman, args); + + if (!state.CanUnlock(key)) + return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args); + + var settings = new ApplySettings(Source: flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcManual : StateSource.IpcFixed, Key: key); + stateManager.ChangeBonusItem(state, item.Slot, item, settings); + ApiHelpers.Lock(state, key, flags); + return GlamourerApiEc.Success; + } + + public GlamourerApiEc SetBonusItemName(string playerName, ApiBonusSlot slot, ulong bonusItemId, uint key, ApplyFlag flags) + { + var args = ApiHelpers.Args("Name", playerName, "Slot", slot, "ID", bonusItemId, "Key", key, "Flags", flags); + if (!ResolveBonusItem(slot, bonusItemId, out var item)) + return ApiHelpers.Return(GlamourerApiEc.ItemInvalid, args); + + var settings = new ApplySettings(Source: flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcManual : StateSource.IpcFixed, Key: key); + var anyHuman = false; + var anyFound = false; + var anyUnlocked = false; + foreach (var state in helpers.FindStates(playerName)) + { + anyFound = true; + if (!state.ModelData.IsHuman) + continue; + + anyHuman = true; + if (!state.CanUnlock(key)) + continue; + + anyUnlocked = true; + stateManager.ChangeBonusItem(state, item.Slot, item, settings); + ApiHelpers.Lock(state, key, flags); + } + if (!anyFound) return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args); @@ -79,4 +137,15 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager item = itemManager.Resolve(slot, id); return item.Valid; } + + private bool ResolveBonusItem(ApiBonusSlot apiSlot, ulong itemId, out BonusItem item) + { + var slot = apiSlot switch + { + ApiBonusSlot.Glasses => BonusItemFlag.Glasses, + _ => BonusItemFlag.Unknown, + }; + + return itemManager.IsBonusItemValid(slot, (BonusItemId)itemId, out item); + } } diff --git a/Glamourer/Gui/GlamourerChangelog.cs b/Glamourer/Gui/GlamourerChangelog.cs index cfa345f..ce0c5e0 100644 --- a/Glamourer/Gui/GlamourerChangelog.cs +++ b/Glamourer/Gui/GlamourerChangelog.cs @@ -91,6 +91,7 @@ public class GlamourerChangelog .RegisterEntry("Glamourer now has a Support Info button akin to Penumbra's.") .RegisterEntry("Glamourer now respects write protection on designs better.") .RegisterEntry("The advanced dye window popup should now get focused when it is opening even in detached state.") + .RegisterEntry("Added API and IPC for bonus items, i.e. the Glasses slot.") .RegisterHighlight("You can now display your characters height in Corgis or Olympic Swimming Pools.") .RegisterEntry("Fixed some issues with advanced customizations and dyes applied via IPC. (1.2.3.2)") .RegisterEntry( diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs index 5f9e748..1499fcb 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs @@ -19,7 +19,8 @@ public class ItemsIpcTester(IDalamudPluginInterface pluginInterface) : IUiServic private ApplyFlag _flags = ApplyFlagEx.DesignDefault; private CustomItemId _customItemId; private StainId _stainId; - private EquipSlot _slot = EquipSlot.Head; + private EquipSlot _slot = EquipSlot.Head; + private BonusItemFlag _bonusSlot = BonusItemFlag.Glasses; private GlamourerApiEc _lastError; public void Draw() @@ -47,6 +48,16 @@ public class ItemsIpcTester(IDalamudPluginInterface pluginInterface) : IUiServic if (ImGui.Button("Set##Name")) _lastError = new SetItemName(pluginInterface).Invoke(_gameObjectName, (ApiEquipSlot)_slot, _customItemId.Id, [_stainId.Id], _key, _flags); + + IpcTesterHelpers.DrawIntro(SetBonusItem.Label); + if (ImGui.Button("Set##BonusIdx")) + _lastError = new SetBonusItem(pluginInterface).Invoke(_gameObjectIndex, ToApi(_bonusSlot), _customItemId.Id, _key, + _flags); + + IpcTesterHelpers.DrawIntro(SetBonusItemName.Label); + if (ImGui.Button("Set##BonusName")) + _lastError = new SetBonusItemName(pluginInterface).Invoke(_gameObjectName, ToApi(_bonusSlot), _customItemId.Id, _key, + _flags); } private void DrawItemInput() @@ -57,6 +68,7 @@ public class ItemsIpcTester(IDalamudPluginInterface pluginInterface) : IUiServic if (ImGuiUtil.InputUlong("Custom Item ID", ref tmp)) _customItemId = tmp; EquipSlotCombo.Draw("Equip Slot", string.Empty, ref _slot, width); + BonusSlotCombo.Draw("Bonus Slot", string.Empty, ref _bonusSlot, width); var value = (int)_stainId.Id; ImGui.SetNextItemWidth(width); if (ImGui.InputInt("Stain ID", ref value, 1, 3)) @@ -65,4 +77,11 @@ public class ItemsIpcTester(IDalamudPluginInterface pluginInterface) : IUiServic _stainId = (StainId)value; } } + + private static ApiBonusSlot ToApi(BonusItemFlag slot) + => slot switch + { + BonusItemFlag.Glasses => ApiBonusSlot.Glasses, + _ => ApiBonusSlot.Unknown, + }; } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index d1624f6..363cbcc 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -7,6 +7,7 @@ using Glamourer.Services; using Glamourer.Unlocks; using ImGuiNET; using OtterGui.Raii; +using OtterGui.Text; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using ImGuiClip = OtterGui.ImGuiClip; @@ -200,11 +201,12 @@ public class UnlockOverview( using var tt = ImRaii.Tooltip(); if (size.X >= iconSize.X && size.Y >= iconSize.Y) ImGui.Image(icon, size); - ImGui.TextUnformatted(item.Name); - ImGui.TextUnformatted($"{item.Slot.ToName()}"); - ImGui.TextUnformatted($"{item.ModelId.Id}-{item.Variant.Id}"); + ImUtf8.Text(item.Name); + ImUtf8.Text($"{item.Slot.ToName()}"); + ImUtf8.Text($"{item.Id.Id}"); + ImUtf8.Text($"{item.ModelId.Id}-{item.Variant.Id}"); // TODO - ImGui.TextUnformatted("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked."); + ImUtf8.Text("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked."); // TODO //tooltip.CreateTooltip(item, string.Empty, false); } diff --git a/Penumbra.GameData b/Penumbra.GameData index bf020eb..3a65ed1 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit bf020ebf5e4980f1814b336aabbaba5e2e00c362 +Subproject commit 3a65ed1c86a2d5fd5794ff5c0559b02fc25d7224