From cb0da11529bc744d805d39c0bc4d3a127cc416ed Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 2 Oct 2023 16:56:24 +0200 Subject: [PATCH] Update API 9 and add data to items. --- Glamourer.GameData/Customization/CmpFile.cs | 4 +- .../Customization/CustomizationManager.cs | 7 +- .../Customization/CustomizationOptions.cs | 11 +- .../Customization/ICustomizationManager.cs | 5 +- Glamourer.GameData/GameData.cs | 25 ++- Glamourer.GameData/Structs/Job.cs | 11 +- Glamourer.GameData/Structs/JobGroup.cs | 27 ++- Glamourer/Designs/DesignData.cs | 24 +-- Glamourer/Events/GPoseService.cs | 17 +- Glamourer/Glamourer.csproj | 2 +- Glamourer/Glamourer.json | 4 +- .../CustomizationDrawer.Color.cs | 4 +- .../Gui/Customization/CustomizationDrawer.cs | 17 +- Glamourer/Gui/Equipment/EquipmentDrawer.cs | 12 +- Glamourer/Gui/Equipment/ItemCombo.cs | 5 +- Glamourer/Gui/Equipment/WeaponCombo.cs | 5 +- Glamourer/Gui/GenericPopupWindow.cs | 6 +- Glamourer/Gui/PenumbraChangedItemTooltip.cs | 3 + Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs | 47 ++--- Glamourer/Gui/Tabs/ActorTab/ActorTab.cs | 2 +- .../Gui/Tabs/AutomationTab/AutomationTab.cs | 2 +- .../Gui/Tabs/AutomationTab/DesignCombo.cs | 11 +- .../Gui/Tabs/AutomationTab/HumanNpcCombo.cs | 5 +- .../Tabs/AutomationTab/IdentifierDrawer.cs | 4 +- Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs | 8 +- .../Gui/Tabs/AutomationTab/SetSelector.cs | 2 + Glamourer/Gui/Tabs/DebugTab.cs | 23 ++- .../DesignTab/DesignFileSystemSelector.cs | 13 +- Glamourer/Gui/Tabs/DesignTab/DesignTab.cs | 2 +- .../Gui/Tabs/DesignTab/ModAssociationsTab.cs | 3 +- Glamourer/Gui/Tabs/DesignTab/ModCombo.cs | 8 +- Glamourer/Gui/Tabs/HeaderDrawer.cs | 1 + Glamourer/Gui/Tabs/SettingsTab.cs | 1 + .../Gui/Tabs/UnlocksTab/UnlockOverview.cs | 37 +++- Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs | 166 +++++++++++++++++- Glamourer/Gui/UiHelpers.cs | 2 +- Glamourer/Interop/ChangeCustomizeService.cs | 11 +- Glamourer/Interop/ContextMenuService.cs | 15 +- Glamourer/Interop/InventoryService.cs | 11 +- Glamourer/Interop/JobService.cs | 8 +- Glamourer/Interop/MetaService.cs | 19 +- Glamourer/Interop/ObjectManager.cs | 11 +- Glamourer/Interop/Penumbra/PenumbraService.cs | 4 +- Glamourer/Interop/ScalingService.cs | 27 +-- Glamourer/Interop/UpdateSlotService.cs | 5 +- Glamourer/Interop/VisorService.cs | 21 ++- Glamourer/Interop/WeaponService.cs | 7 +- Glamourer/Services/CommandService.cs | 5 +- Glamourer/Services/CustomizationService.cs | 4 +- Glamourer/Services/DalamudServices.cs | 16 +- Glamourer/Services/ItemManager.cs | 22 +-- Glamourer/Services/ServiceWrapper.cs | 15 +- Glamourer/Services/TextureService.cs | 9 +- Glamourer/State/ActorState.cs | 3 +- Glamourer/State/StateEditor.cs | 6 +- Glamourer/State/StateListener.cs | 5 +- Glamourer/State/StateManager.cs | 6 +- Glamourer/Unlocks/CustomizeUnlockManager.cs | 11 +- Glamourer/Unlocks/ItemUnlockManager.cs | 19 +- OtterGui | 2 +- Penumbra.GameData | 2 +- repo.json | 2 +- 62 files changed, 524 insertions(+), 268 deletions(-) diff --git a/Glamourer.GameData/Customization/CmpFile.cs b/Glamourer.GameData/Customization/CmpFile.cs index 6ed5ecb..d62e5da 100644 --- a/Glamourer.GameData/Customization/CmpFile.cs +++ b/Glamourer.GameData/Customization/CmpFile.cs @@ -20,7 +20,7 @@ internal class CmpFile public bool Valid => _file != null; - public CmpFile(IDataManager gameData) + public CmpFile(IDataManager gameData, IPluginLog log) { try { @@ -36,7 +36,7 @@ internal class CmpFile } catch (Exception e) { - PluginLog.Error("READ THIS\n======== Could not obtain the human.cmp file which is necessary for color sets.\n" + log.Error("READ THIS\n======== Could not obtain the human.cmp file which is necessary for color sets.\n" + "======== This usually indicates an error with your index files caused by TexTools modifications.\n" + "======== If you have used TexTools before, you will probably need to start over in it to use Glamourer.", e); _file = null; diff --git a/Glamourer.GameData/Customization/CustomizationManager.cs b/Glamourer.GameData/Customization/CustomizationManager.cs index dc73279..9221838 100644 --- a/Glamourer.GameData/Customization/CustomizationManager.cs +++ b/Glamourer.GameData/Customization/CustomizationManager.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Dalamud.Interface.Internal; using Dalamud.Plugin.Services; using Penumbra.GameData.Enums; @@ -11,9 +12,9 @@ public class CustomizationManager : ICustomizationManager private CustomizationManager() { } - public static ICustomizationManager Create(ITextureProvider textures, IDataManager gameData) + public static ICustomizationManager Create(ITextureProvider textures, IDataManager gameData, IPluginLog log) { - _options ??= new CustomizationOptions(textures, gameData); + _options ??= new CustomizationOptions(textures, gameData, log); return new CustomizationManager(); } @@ -29,7 +30,7 @@ public class CustomizationManager : ICustomizationManager public CustomizationSet GetList(SubRace clan, Gender gender) => _options!.GetList(clan, gender); - public ImGuiScene.TextureWrap GetIcon(uint iconId) + public IDalamudTextureWrap GetIcon(uint iconId) => _options!.GetIcon(iconId); public string GetName(CustomName name) diff --git a/Glamourer.GameData/Customization/CustomizationOptions.cs b/Glamourer.GameData/Customization/CustomizationOptions.cs index c9deaac..a3e04f5 100644 --- a/Glamourer.GameData/Customization/CustomizationOptions.cs +++ b/Glamourer.GameData/Customization/CustomizationOptions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Dalamud; +using Dalamud.Interface.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; using Lumina.Excel; @@ -34,7 +35,7 @@ public partial class CustomizationOptions => _customizationSets[ToIndex(race, gender)]; // Get specific icons. - internal ImGuiScene.TextureWrap GetIcon(uint id) + internal IDalamudTextureWrap GetIcon(uint id) => _icons.LoadIcon(id)!; private readonly IconStorage _icons; @@ -61,9 +62,9 @@ public partial class CustomizationOptions public string GetName(CustomName name) => _names[(int)name]; - internal CustomizationOptions(ITextureProvider textures, IDataManager gameData) + internal CustomizationOptions(ITextureProvider textures, IDataManager gameData, IPluginLog log) { - var tmp = new TemporaryData(gameData, this); + var tmp = new TemporaryData(gameData, this, log); _icons = new IconStorage(textures, gameData); SetNames(gameData, tmp); foreach (var race in Clans) @@ -179,10 +180,10 @@ public partial class CustomizationOptions } - public TemporaryData(IDataManager gameData, CustomizationOptions options) + public TemporaryData(IDataManager gameData, CustomizationOptions options, IPluginLog log) { _options = options; - _cmpFile = new CmpFile(gameData); + _cmpFile = new CmpFile(gameData, log); _customizeSheet = gameData.GetExcelSheet()!; _bnpcCustomize = gameData.GetExcelSheet()!; _enpcBase = gameData.GetExcelSheet()!; diff --git a/Glamourer.GameData/Customization/ICustomizationManager.cs b/Glamourer.GameData/Customization/ICustomizationManager.cs index 6e1cfe3..e946e3d 100644 --- a/Glamourer.GameData/Customization/ICustomizationManager.cs +++ b/Glamourer.GameData/Customization/ICustomizationManager.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Dalamud.Interface.Internal; using Penumbra.GameData.Enums; namespace Glamourer.Customization; @@ -11,6 +12,6 @@ public interface ICustomizationManager public CustomizationSet GetList(SubRace race, Gender gender); - public ImGuiScene.TextureWrap GetIcon(uint iconId); - public string GetName(CustomName name); + public IDalamudTextureWrap GetIcon(uint iconId); + public string GetName(CustomName name); } diff --git a/Glamourer.GameData/GameData.cs b/Glamourer.GameData/GameData.cs index 28986d8..4a40e05 100644 --- a/Glamourer.GameData/GameData.cs +++ b/Glamourer.GameData/GameData.cs @@ -9,8 +9,9 @@ namespace Glamourer; public static class GameData { - private static Dictionary? _jobs; - private static Dictionary? _jobGroups; + private static Dictionary? _jobs; + private static Dictionary? _jobGroups; + private static JobGroup[]? _allJobGroups; public static IReadOnlyDictionary Jobs(IDataManager dataManager) { @@ -22,14 +23,22 @@ public static class GameData return _jobs; } + public static IReadOnlyList AllJobGroups(IDataManager dataManager) + { + if (_allJobGroups != null) + return _allJobGroups; + + var sheet = dataManager.GetExcelSheet()!; + var jobs = dataManager.GetExcelSheet(ClientLanguage.English)!; + _allJobGroups = sheet.Select(j => new JobGroup(j, jobs)).ToArray(); + return _allJobGroups; + } + public static IReadOnlyDictionary JobGroups(IDataManager dataManager) { if (_jobGroups != null) return _jobGroups; - var sheet = dataManager.GetExcelSheet()!; - var jobs = dataManager.GetExcelSheet(ClientLanguage.English)!; - static bool ValidIndex(uint idx) { if (idx is > 0 and < 36) @@ -68,12 +77,12 @@ public static class GameData 69 => true, 68 => true, 93 => true, - _ => false, + _ => false, }; } - _jobGroups = sheet.Where(j => ValidIndex(j.RowId)) - .ToDictionary(j => (ushort)j.RowId, j => new JobGroup(j, jobs)); + _jobGroups = AllJobGroups(dataManager).Where(j => ValidIndex(j.Id)) + .ToDictionary(j => (ushort) j.Id, j => j); return _jobGroups; } } diff --git a/Glamourer.GameData/Structs/Job.cs b/Glamourer.GameData/Structs/Job.cs index d946c12..700d3d3 100644 --- a/Glamourer.GameData/Structs/Job.cs +++ b/Glamourer.GameData/Structs/Job.cs @@ -7,17 +7,20 @@ namespace Glamourer.Structs; // Also contains the jobs Name and Abbreviation as strings. public readonly struct Job { - public readonly string Name; - public readonly string Abbreviation; + public readonly string Name; + public readonly string Abbreviation; public readonly ClassJob Base; public uint Id => Base.RowId; + public JobFlag Flag + => (JobFlag)(1u << (int)Base.RowId); + public Job(ClassJob job) { - Base = job; - Name = job.Name.ToDalamudString().ToString(); + Base = job; + Name = job.Name.ToDalamudString().ToString(); Abbreviation = job.Abbreviation.ToDalamudString().ToString(); } diff --git a/Glamourer.GameData/Structs/JobGroup.cs b/Glamourer.GameData/Structs/JobGroup.cs index 55c77a6..5471e7f 100644 --- a/Glamourer.GameData/Structs/JobGroup.cs +++ b/Glamourer.GameData/Structs/JobGroup.cs @@ -1,16 +1,21 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using Lumina.Excel; using Lumina.Excel.GeneratedSheets; namespace Glamourer.Structs; +[Flags] +public enum JobFlag : ulong +{ } + // The game specifies different job groups that can contain specific jobs or not. public readonly struct JobGroup { - public readonly string Name; - public readonly int Count; - public readonly uint Id; - private readonly ulong _flags; + public readonly string Name; + public readonly int Count; + public readonly uint Id; + private readonly JobFlag _flags; // Create a job group from a given category and the ClassJob sheet. // It looks up the different jobs contained in the category and sets the flags appropriately. @@ -35,18 +40,22 @@ public readonly struct JobGroup continue; ++Count; - _flags |= 1ul << (int)job.RowId; + _flags |= (JobFlag)(1ul << (int)job.RowId); } } // Check if a job is contained inside this group. public bool Fits(Job job) - => Fits(job.Id); + => _flags.HasFlag(job.Flag); + + // Check if any of the jobs in the given flags fit this group. + public bool Fits(JobFlag flag) + => (_flags & flag) != 0; // Check if a job is contained inside this group. public bool Fits(uint jobId) { - var flag = 1ul << (int)jobId; - return (flag & _flags) != 0; + var flag = (JobFlag)(1ul << (int)jobId); + return _flags.HasFlag(flag); } } diff --git a/Glamourer/Designs/DesignData.cs b/Glamourer/Designs/DesignData.cs index b7bee4a..ab58a4b 100644 --- a/Glamourer/Designs/DesignData.cs +++ b/Glamourer/Designs/DesignData.cs @@ -55,18 +55,18 @@ public unsafe struct DesignData => slot.ToIndex() switch { // @formatter:off - 0 => EquipItem.FromIds(_itemIds[ 0], _iconIds[ 0], (SetId)(_equipmentBytes[ 0] | (_equipmentBytes[ 1] << 8)), (WeaponType)0, _equipmentBytes[ 2], FullEquipType.Head, _nameHead ), - 1 => EquipItem.FromIds(_itemIds[ 1], _iconIds[ 1], (SetId)(_equipmentBytes[ 4] | (_equipmentBytes[ 5] << 8)), (WeaponType)0, _equipmentBytes[ 6], FullEquipType.Body, _nameBody ), - 2 => EquipItem.FromIds(_itemIds[ 2], _iconIds[ 2], (SetId)(_equipmentBytes[ 8] | (_equipmentBytes[ 9] << 8)), (WeaponType)0, _equipmentBytes[10], FullEquipType.Hands, _nameHands ), - 3 => EquipItem.FromIds(_itemIds[ 3], _iconIds[ 3], (SetId)(_equipmentBytes[12] | (_equipmentBytes[13] << 8)), (WeaponType)0, _equipmentBytes[14], FullEquipType.Legs, _nameLegs ), - 4 => EquipItem.FromIds(_itemIds[ 4], _iconIds[ 4], (SetId)(_equipmentBytes[16] | (_equipmentBytes[17] << 8)), (WeaponType)0, _equipmentBytes[18], FullEquipType.Feet, _nameFeet ), - 5 => EquipItem.FromIds(_itemIds[ 5], _iconIds[ 5], (SetId)(_equipmentBytes[20] | (_equipmentBytes[21] << 8)), (WeaponType)0, _equipmentBytes[22], FullEquipType.Ears, _nameEars ), - 6 => EquipItem.FromIds(_itemIds[ 6], _iconIds[ 6], (SetId)(_equipmentBytes[24] | (_equipmentBytes[25] << 8)), (WeaponType)0, _equipmentBytes[26], FullEquipType.Neck, _nameNeck ), - 7 => EquipItem.FromIds(_itemIds[ 7], _iconIds[ 7], (SetId)(_equipmentBytes[28] | (_equipmentBytes[29] << 8)), (WeaponType)0, _equipmentBytes[30], FullEquipType.Wrists, _nameWrists ), - 8 => EquipItem.FromIds(_itemIds[ 8], _iconIds[ 8], (SetId)(_equipmentBytes[32] | (_equipmentBytes[33] << 8)), (WeaponType)0, _equipmentBytes[34], FullEquipType.Finger, _nameRFinger ), - 9 => EquipItem.FromIds(_itemIds[ 9], _iconIds[ 9], (SetId)(_equipmentBytes[36] | (_equipmentBytes[37] << 8)), (WeaponType)0, _equipmentBytes[38], FullEquipType.Finger, _nameLFinger ), - 10 => EquipItem.FromIds(_itemIds[10], _iconIds[10], (SetId)(_equipmentBytes[40] | (_equipmentBytes[41] << 8)), _secondaryMainhand, _equipmentBytes[42], _typeMainhand, _nameMainhand), - 11 => EquipItem.FromIds(_itemIds[11], _iconIds[11], (SetId)(_equipmentBytes[44] | (_equipmentBytes[45] << 8)), _secondaryOffhand, _equipmentBytes[46], _typeOffhand, _nameOffhand ), + 0 => EquipItem.FromIds(_itemIds[ 0], _iconIds[ 0], (SetId)(_equipmentBytes[ 0] | (_equipmentBytes[ 1] << 8)), (WeaponType)0, _equipmentBytes[ 2], FullEquipType.Head, name: _nameHead ), + 1 => EquipItem.FromIds(_itemIds[ 1], _iconIds[ 1], (SetId)(_equipmentBytes[ 4] | (_equipmentBytes[ 5] << 8)), (WeaponType)0, _equipmentBytes[ 6], FullEquipType.Body, name: _nameBody ), + 2 => EquipItem.FromIds(_itemIds[ 2], _iconIds[ 2], (SetId)(_equipmentBytes[ 8] | (_equipmentBytes[ 9] << 8)), (WeaponType)0, _equipmentBytes[10], FullEquipType.Hands, name: _nameHands ), + 3 => EquipItem.FromIds(_itemIds[ 3], _iconIds[ 3], (SetId)(_equipmentBytes[12] | (_equipmentBytes[13] << 8)), (WeaponType)0, _equipmentBytes[14], FullEquipType.Legs, name: _nameLegs ), + 4 => EquipItem.FromIds(_itemIds[ 4], _iconIds[ 4], (SetId)(_equipmentBytes[16] | (_equipmentBytes[17] << 8)), (WeaponType)0, _equipmentBytes[18], FullEquipType.Feet, name: _nameFeet ), + 5 => EquipItem.FromIds(_itemIds[ 5], _iconIds[ 5], (SetId)(_equipmentBytes[20] | (_equipmentBytes[21] << 8)), (WeaponType)0, _equipmentBytes[22], FullEquipType.Ears, name: _nameEars ), + 6 => EquipItem.FromIds(_itemIds[ 6], _iconIds[ 6], (SetId)(_equipmentBytes[24] | (_equipmentBytes[25] << 8)), (WeaponType)0, _equipmentBytes[26], FullEquipType.Neck, name: _nameNeck ), + 7 => EquipItem.FromIds(_itemIds[ 7], _iconIds[ 7], (SetId)(_equipmentBytes[28] | (_equipmentBytes[29] << 8)), (WeaponType)0, _equipmentBytes[30], FullEquipType.Wrists, name: _nameWrists ), + 8 => EquipItem.FromIds(_itemIds[ 8], _iconIds[ 8], (SetId)(_equipmentBytes[32] | (_equipmentBytes[33] << 8)), (WeaponType)0, _equipmentBytes[34], FullEquipType.Finger, name: _nameRFinger ), + 9 => EquipItem.FromIds(_itemIds[ 9], _iconIds[ 9], (SetId)(_equipmentBytes[36] | (_equipmentBytes[37] << 8)), (WeaponType)0, _equipmentBytes[38], FullEquipType.Finger, name: _nameLFinger ), + 10 => EquipItem.FromIds(_itemIds[10], _iconIds[10], (SetId)(_equipmentBytes[40] | (_equipmentBytes[41] << 8)), _secondaryMainhand, _equipmentBytes[42], _typeMainhand, name: _nameMainhand), + 11 => EquipItem.FromIds(_itemIds[11], _iconIds[11], (SetId)(_equipmentBytes[44] | (_equipmentBytes[45] << 8)), _secondaryOffhand, _equipmentBytes[46], _typeOffhand, name: _nameOffhand ), _ => new EquipItem(), // @formatter:on }; diff --git a/Glamourer/Events/GPoseService.cs b/Glamourer/Events/GPoseService.cs index a427682..e869cf4 100644 --- a/Glamourer/Events/GPoseService.cs +++ b/Glamourer/Events/GPoseService.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Concurrent; -using Dalamud.Game; +using Dalamud.Plugin.Services; using OtterGui.Classes; -using FFXIVClientStructs.FFXIV.Client.Game; namespace Glamourer.Events; public class GPoseService : EventWrapper, GPoseService.Priority> { - private readonly Framework _framework; + private readonly IFramework _framework; + private readonly IClientState _state; private readonly ConcurrentQueue _onLeave = new(); private readonly ConcurrentQueue _onEnter = new(); @@ -19,13 +19,14 @@ public class GPoseService : EventWrapper, GPoseService.Priority> GlamourerIpc = int.MinValue, } - public bool InGPose { get; private set; } = false; + public bool InGPose { get; private set; } - public GPoseService(Framework framework) + public GPoseService(IFramework framework, IClientState state) : base(nameof(GPoseService)) { _framework = framework; - InGPose = GameMain.IsInGPose(); + _state = state; + InGPose = state.IsGPosing; _framework.Update += OnFramework; } @@ -51,9 +52,9 @@ public class GPoseService : EventWrapper, GPoseService.Priority> _onEnter.Enqueue(onEnter); } - private void OnFramework(Framework _) + private void OnFramework(IFramework _) { - var inGPose = GameMain.IsInGPose(); + var inGPose = _state.IsGPosing; if (InGPose == inGPose) return; diff --git a/Glamourer/Glamourer.csproj b/Glamourer/Glamourer.csproj index 8f9f7f6..7ecb3b6 100644 --- a/Glamourer/Glamourer.csproj +++ b/Glamourer/Glamourer.csproj @@ -89,7 +89,7 @@ - + diff --git a/Glamourer/Glamourer.json b/Glamourer/Glamourer.json index 48f2033..148951b 100644 --- a/Glamourer/Glamourer.json +++ b/Glamourer/Glamourer.json @@ -5,10 +5,10 @@ "Description": "Adds functionality to change and store appearance of players, customization and equip. Requires Penumbra to be installed and activated to work. Can also add preview options to the Changed Items tab for Penumbra.", "Tags": [ "Appearance", "Glamour", "Race", "Outfit", "Armor", "Clothes", "Skins", "Customization", "Design", "Character" ], "InternalName": "Glamourer", - "AssemblyVersion": "1.0.0.2", + "AssemblyVersion": "1.0.1.0", "RepoUrl": "https://github.com/Ottermandias/Glamourer", "ApplicableVersion": "any", - "DalamudApiLevel": 8, + "DalamudApiLevel": 9, "ImageUrls": null, "IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/master/images/icon.png" } \ No newline at end of file diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs index c1f497d..bd7599b 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs @@ -1,6 +1,6 @@ -using System; -using System.Numerics; +using System.Numerics; using Dalamud.Interface; +using Dalamud.Interface.Utility; using Glamourer.Customization; using ImGuiNET; using OtterGui.Raii; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs index 5289cf3..364b469 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs @@ -1,7 +1,8 @@ using System; using System.Numerics; using System.Reflection; -using Dalamud.Interface; +using Dalamud.Interface.Internal; +using Dalamud.Interface.Utility; using Dalamud.Plugin; using Glamourer.Customization; using Glamourer.Services; @@ -18,10 +19,10 @@ public partial class CustomizationDrawer : IDisposable private readonly CodeService _codes; private readonly Configuration _config; - private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f); - private readonly ImGuiScene.TextureWrap? _legacyTattoo; + private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f); + private readonly IDalamudTextureWrap? _legacyTattoo; - private Exception? _terminate = null; + private Exception? _terminate; private Customize _customize; private CustomizationSet _set = null!; @@ -34,8 +35,8 @@ public partial class CustomizationDrawer : IDisposable public CustomizeFlag ChangeApply { get; private set; } private CustomizeFlag _initialApply; - private bool _locked = false; - private bool _lockedRedraw = false; + private bool _locked; + private bool _lockedRedraw; private Vector2 _spacing; private Vector2 _iconSize; private Vector2 _framedIconSize; @@ -132,7 +133,7 @@ public partial class CustomizationDrawer : IDisposable private bool DrawInternal() { - using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _spacing); + using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _spacing); try { @@ -207,7 +208,7 @@ public partial class CustomizationDrawer : IDisposable _raceSelectorWidth = _inputIntSize + _comboSelectorSize - _framedIconSize.X; } - private static ImGuiScene.TextureWrap? GetLegacyTattooIcon(DalamudPluginInterface pi) + private static IDalamudTextureWrap? GetLegacyTattooIcon(DalamudPluginInterface pi) { using var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Glamourer.LegacyTattoo.raw"); if (resource == null) diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.cs index b265e26..d7032e6 100644 --- a/Glamourer/Gui/Equipment/EquipmentDrawer.cs +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Numerics; -using Dalamud.Interface; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Dalamud.Plugin.Services; using Glamourer.Designs; using Glamourer.Events; @@ -47,18 +47,18 @@ public class EquipmentDrawer _gPose = gPose; _stainData = items.Stains; _stainCombo = new FilterComboColors(DefaultWidth - 20, - _stainData.Data.Prepend(new KeyValuePair(0, ("None", 0, false)))); - _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e)).ToArray(); + _stainData.Data.Prepend(new KeyValuePair(0, ("None", 0, false))), Glamourer.Log); + _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log)).ToArray(); _weaponCombo = new Dictionary(FullEquipTypeExtensions.WeaponTypes.Count * 2); foreach (var type in Enum.GetValues()) { if (type.ToSlot() is EquipSlot.MainHand) - _weaponCombo.TryAdd(type, new WeaponCombo(items, type)); + _weaponCombo.TryAdd(type, new WeaponCombo(items, type, Glamourer.Log)); else if (type.ToSlot() is EquipSlot.OffHand) - _weaponCombo.TryAdd(type, new WeaponCombo(items, type)); + _weaponCombo.TryAdd(type, new WeaponCombo(items, type, Glamourer.Log)); } - _weaponCombo.Add(FullEquipType.Unknown, new WeaponCombo(items, FullEquipType.Unknown)); + _weaponCombo.Add(FullEquipType.Unknown, new WeaponCombo(items, FullEquipType.Unknown, Glamourer.Log)); } private Vector2 _iconSize; diff --git a/Glamourer/Gui/Equipment/ItemCombo.cs b/Glamourer/Gui/Equipment/ItemCombo.cs index 1a2f1c2..0430e94 100644 --- a/Glamourer/Gui/Equipment/ItemCombo.cs +++ b/Glamourer/Gui/Equipment/ItemCombo.cs @@ -6,6 +6,7 @@ using ImGuiNET; using Lumina.Excel.GeneratedSheets; using OtterGui; using OtterGui.Classes; +using OtterGui.Log; using OtterGui.Raii; using OtterGui.Widgets; using Penumbra.GameData.Enums; @@ -19,8 +20,8 @@ public sealed class ItemCombo : FilterComboCache private ItemId _currentItem; private float _innerWidth; - public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot) - : base(() => GetItems(items, slot)) + public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log) + : base(() => GetItems(items, slot), log) { Label = GetLabel(gameData, slot); _currentItem = ItemManager.NothingId(slot); diff --git a/Glamourer/Gui/Equipment/WeaponCombo.cs b/Glamourer/Gui/Equipment/WeaponCombo.cs index 667fad2..5a1792e 100644 --- a/Glamourer/Gui/Equipment/WeaponCombo.cs +++ b/Glamourer/Gui/Equipment/WeaponCombo.cs @@ -5,6 +5,7 @@ using Glamourer.Services; using ImGuiNET; using OtterGui; using OtterGui.Classes; +using OtterGui.Log; using OtterGui.Raii; using OtterGui.Widgets; using Penumbra.GameData.Enums; @@ -18,8 +19,8 @@ public sealed class WeaponCombo : FilterComboCache private ItemId _currentItemId; private float _innerWidth; - public WeaponCombo(ItemManager items, FullEquipType type) - : base(() => GetWeapons(items, type)) + public WeaponCombo(ItemManager items, FullEquipType type, Logger log) + : base(() => GetWeapons(items, type), log) { Label = GetLabel(type); SearchByParts = true; diff --git a/Glamourer/Gui/GenericPopupWindow.cs b/Glamourer/Gui/GenericPopupWindow.cs index 7cbe5c5..7a201e8 100644 --- a/Glamourer/Gui/GenericPopupWindow.cs +++ b/Glamourer/Gui/GenericPopupWindow.cs @@ -1,5 +1,5 @@ using System.Numerics; -using Dalamud.Interface; +using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using ImGuiNET; using OtterGui; @@ -9,8 +9,8 @@ namespace Glamourer.Gui; public class GenericPopupWindow : Window { - private Configuration _config; - public bool OpenFestivalPopup { get; internal set; } = false; + private readonly Configuration _config; + public bool OpenFestivalPopup { get; internal set; } = false; public GenericPopupWindow(Configuration config) : base("Glamourer Popups", diff --git a/Glamourer/Gui/PenumbraChangedItemTooltip.cs b/Glamourer/Gui/PenumbraChangedItemTooltip.cs index a0ea92d..ea57515 100644 --- a/Glamourer/Gui/PenumbraChangedItemTooltip.cs +++ b/Glamourer/Gui/PenumbraChangedItemTooltip.cs @@ -65,6 +65,9 @@ public class PenumbraChangedItemTooltip : IDisposable public void CreateTooltip(EquipItem item, string prefix, bool openTooltip) { + if (!Player()) + return; + var slot = item.Type.ToSlot(); var last = _lastItems[slot.ToIndex()]; switch (slot) diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs index 53c1dd1..ac9269b 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs @@ -1,9 +1,9 @@ using System; -using System.Linq; using System.Numerics; using Dalamud.Game.ClientState.Conditions; using Dalamud.Interface; using Dalamud.Interface.Internal.Notifications; +using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using Glamourer.Automation; using Glamourer.Customization; @@ -36,7 +36,7 @@ public class ActorPanel private readonly ObjectManager _objects; private readonly DesignManager _designManager; private readonly DatFileService _datFileService; - private readonly Condition _conditions; + private readonly ICondition _conditions; private ActorIdentifier _identifier; private string _actorName = string.Empty; @@ -48,7 +48,7 @@ public class ActorPanel public ActorPanel(ActorSelector selector, StateManager stateManager, CustomizationDrawer customizationDrawer, EquipmentDrawer equipmentDrawer, IdentifierService identification, AutoDesignApplier autoDesignApplier, Configuration config, DesignConverter converter, ObjectManager objects, DesignManager designManager, DatFileService datFileService, - Condition conditions) + ICondition conditions) { _selector = selector; _stateManager = stateManager; @@ -280,29 +280,32 @@ public class ActorPanel private HeaderDrawer.Button SetFromClipboardButton() => new() { - Description = "Try to apply a design from your clipboard.\nHold Control to only apply gear.\nHold Shift to only apply customizations.", - Icon = FontAwesomeIcon.Clipboard, - OnClick = SetFromClipboard, - Visible = _state != null, - Disabled = _state?.IsLocked ?? true, + Description = + "Try to apply a design from your clipboard.\nHold Control to only apply gear.\nHold Shift to only apply customizations.", + Icon = FontAwesomeIcon.Clipboard, + OnClick = SetFromClipboard, + Visible = _state != null, + Disabled = _state?.IsLocked ?? true, }; private HeaderDrawer.Button ExportToClipboardButton() => new() { - Description = "Copy the current design to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.", - Icon = FontAwesomeIcon.Copy, - OnClick = ExportToClipboard, - Visible = _state?.ModelData.ModelId == 0, + Description = + "Copy the current design to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.", + Icon = FontAwesomeIcon.Copy, + OnClick = ExportToClipboard, + Visible = _state?.ModelData.ModelId == 0, }; private HeaderDrawer.Button SaveAsDesignButton() => new() { - Description = "Save the current state as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.", - Icon = FontAwesomeIcon.Save, - OnClick = SaveDesignOpen, - Visible = _state?.ModelData.ModelId == 0, + Description = + "Save the current state as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.", + Icon = FontAwesomeIcon.Save, + OnClick = SaveDesignOpen, + Visible = _state?.ModelData.ModelId == 0, }; private HeaderDrawer.Button LockedButton() @@ -317,8 +320,8 @@ public class ActorPanel BorderColor = ColorId.ActorUnavailable.Value(), }; - private string _newName = string.Empty; - private DesignBase? _newDesign = null; + private string _newName = string.Empty; + private DesignBase? _newDesign = null; private void SaveDesignOpen() { @@ -344,8 +347,9 @@ public class ActorPanel try { var (applyGear, applyCustomize) = UiHelpers.ConvertKeysToBool(); - var text = ImGui.GetClipboardText(); - var design = _converter.FromBase64(text, applyCustomize, applyGear, out _) ?? throw new Exception("The clipboard did not contain valid data."); + var text = ImGui.GetClipboardText(); + var design = _converter.FromBase64(text, applyCustomize, applyGear, out _) + ?? throw new Exception("The clipboard did not contain valid data."); _stateManager.ApplyDesign(design, _state!, StateChanged.Source.Manual); } catch (Exception ex) @@ -394,7 +398,8 @@ public class ActorPanel private void DrawApplyToSelf() { var (id, data) = _objects.PlayerData; - if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero, "Apply the current state to your own character.\nHold Control to only apply gear.\nHold Shift to only apply customizations.", + if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero, + "Apply the current state to your own character.\nHold Control to only apply gear.\nHold Shift to only apply customizations.", !data.Valid || id == _identifier || _state!.ModelData.ModelId != 0)) return; diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs b/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs index 4590f45..76b0a55 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs @@ -1,5 +1,5 @@ using System; -using Dalamud.Interface; +using Dalamud.Interface.Utility; using ImGuiNET; using OtterGui.Widgets; diff --git a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs index f1c2d15..e5d4b21 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs @@ -1,5 +1,5 @@ using System; -using Dalamud.Interface; +using Dalamud.Interface.Utility; using ImGuiNET; using OtterGui.Widgets; diff --git a/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs b/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs index 70e94d6..998270b 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/DesignCombo.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Dalamud.Interface; +using Dalamud.Interface.Utility; using Glamourer.Automation; using Glamourer.Customization; using Glamourer.Designs; @@ -9,6 +9,7 @@ using Glamourer.Services; using ImGuiNET; using OtterGui; using OtterGui.Classes; +using OtterGui.Log; using OtterGui.Widgets; namespace Glamourer.Gui.Tabs.AutomationTab; @@ -23,14 +24,14 @@ public sealed class DesignCombo : FilterComboCache<(Design, string)> private float _innerWidth; public DesignCombo(AutoDesignManager manager, DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected, - ItemManager items, CustomizationService customize) - : this(manager, designs, fileSystem, tabSelected, CreateRevertDesign(customize, items)) + ItemManager items, CustomizationService customize, Logger log) + : this(manager, designs, fileSystem, tabSelected, CreateRevertDesign(customize, items), log) { } private DesignCombo(AutoDesignManager manager, DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected, - Design revertDesign) + Design revertDesign, Logger log) : base(() => designs.Designs.Select(d => (d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)).OrderBy(d => d.Item2) - .Prepend((revertDesign, string.Empty)).ToList()) + .Prepend((revertDesign, string.Empty)).ToList(), log) { _manager = manager; _tabSelected = tabSelected; diff --git a/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs b/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs index b1e3247..da4fda1 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs @@ -6,6 +6,7 @@ using Dalamud.Utility; using Glamourer.Services; using ImGuiNET; using OtterGui.Custom; +using OtterGui.Log; using OtterGui.Widgets; using Penumbra.GameData.Data; @@ -15,8 +16,8 @@ public sealed class HumanNpcCombo : FilterComboCache<(string Name, ObjectKind Ki { private readonly string _label; - public HumanNpcCombo(string label, IdentifierService service, HumanModelList humans) - : base(() => CreateList(service, humans)) + public HumanNpcCombo(string label, IdentifierService service, HumanModelList humans, Logger log) + : base(() => CreateList(service, humans), log) => _label = label; protected override string ToString((string Name, ObjectKind Kind, uint[] Ids) obj) diff --git a/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs b/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs index 7970389..e88c55b 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs @@ -24,8 +24,8 @@ public class IdentifierDrawer public IdentifierDrawer(ActorService actors, IdentifierService identifier, HumanModelList humans) { _actors = actors; - _worldCombo = new WorldCombo(actors.AwaitedService.Data.Worlds); - _humanNpcCombo = new HumanNpcCombo("##npcs", identifier, humans); + _worldCombo = new WorldCombo(actors.AwaitedService.Data.Worlds, Glamourer.Log); + _humanNpcCombo = new HumanNpcCombo("##npcs", identifier, humans, Glamourer.Log); } public void DrawName(float width) diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs index 496999e..a10f1ff 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Numerics; using System.Text; using Dalamud.Interface; +using Dalamud.Interface.Utility; using Glamourer.Automation; using Glamourer.Customization; using Glamourer.Interop; @@ -11,6 +12,7 @@ using Glamourer.Structs; using Glamourer.Unlocks; using ImGuiNET; using OtterGui; +using OtterGui.Log; using OtterGui.Raii; using OtterGui.Widgets; using Penumbra.GameData.Enums; @@ -48,7 +50,7 @@ public class SetPanel _identifierDrawer = identifierDrawer; _config = config; _designCombo = designCombo; - _jobGroupCombo = new JobGroupCombo(manager, jobs); + _jobGroupCombo = new JobGroupCombo(manager, jobs, Glamourer.Log); } private AutoDesignSet Selection @@ -427,8 +429,8 @@ public class SetPanel private readonly AutoDesignManager _manager; private readonly JobService _jobs; - public JobGroupCombo(AutoDesignManager manager, JobService jobs) - : base(() => jobs.JobGroups.Values.ToList()) + public JobGroupCombo(AutoDesignManager manager, JobService jobs, Logger log) + : base(() => jobs.JobGroups.Values.ToList(), log) { _manager = manager; _jobs = jobs; diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs index f608c58..10b6383 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using Dalamud.Interface; +using Dalamud.Interface.Utility; using Glamourer.Automation; using Glamourer.Events; using Glamourer.Interop; @@ -12,6 +13,7 @@ using OtterGui; using OtterGui.Classes; using OtterGui.Raii; using Penumbra.String; +using ImGuiClip = OtterGui.ImGuiClip; namespace Glamourer.Gui.Tabs.AutomationTab; diff --git a/Glamourer/Gui/Tabs/DebugTab.cs b/Glamourer/Gui/Tabs/DebugTab.cs index 27943fd..05e313e 100644 --- a/Glamourer/Gui/Tabs/DebugTab.cs +++ b/Glamourer/Gui/Tabs/DebugTab.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Text; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface; +using Dalamud.Interface.Utility; using Dalamud.Plugin; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; @@ -31,6 +32,7 @@ using Penumbra.Api.Enums; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; +using ImGuiClip = OtterGui.ImGuiClip; namespace Glamourer.Gui.Tabs; @@ -579,14 +581,29 @@ public unsafe class DebugTab : ITab if (table) foreach (var (id, job) in _jobs.Jobs) { - ImGuiUtil.DrawTableColumn(id.ToString("D2")); + ImGuiUtil.DrawTableColumn(id.ToString("D3")); ImGuiUtil.DrawTableColumn(job.Name); ImGuiUtil.DrawTableColumn(job.Abbreviation); } } } - using (var t = ImRaii.TreeNode("Job Groups")) + using (var t = ImRaii.TreeNode("All Job Groups")) + { + if (t) + { + using var table = ImRaii.Table("##groups", 3, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); + if (table) + foreach (var (group, idx) in _jobs.AllJobGroups.WithIndex()) + { + ImGuiUtil.DrawTableColumn(idx.ToString("D3")); + ImGuiUtil.DrawTableColumn(group.Name); + ImGuiUtil.DrawTableColumn(group.Count.ToString()); + } + } + } + + using (var t = ImRaii.TreeNode("Valid Job Groups")) { if (t) { @@ -594,7 +611,7 @@ public unsafe class DebugTab : ITab if (table) foreach (var (id, group) in _jobs.JobGroups) { - ImGuiUtil.DrawTableColumn(id.ToString("D2")); + ImGuiUtil.DrawTableColumn(id.ToString("D3")); ImGuiUtil.DrawTableColumn(group.Name); ImGuiUtil.DrawTableColumn(group.Count.ToString()); } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs b/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs index f38a09e..8a4188c 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs @@ -1,9 +1,9 @@ using System; using System.Linq; using System.Numerics; -using Dalamud.Game.ClientState.Keys; using Dalamud.Interface; using Dalamud.Interface.Internal.Notifications; +using Dalamud.Plugin.Services; using Glamourer.Customization; using Glamourer.Designs; using Glamourer.Events; @@ -12,6 +12,7 @@ using OtterGui; using OtterGui.Classes; using OtterGui.Filesystem; using OtterGui.FileSystem.Selector; +using OtterGui.Log; using OtterGui.Raii; namespace Glamourer.Gui.Tabs.DesignTab; @@ -25,8 +26,8 @@ public sealed class DesignFileSystemSelector : FileSystemSelector { - public ModCombo(PenumbraService penumbra) - : base(penumbra.GetMods) + public ModCombo(PenumbraService penumbra, Logger log) + : base(penumbra.GetMods, log) { SearchByParts = false; } diff --git a/Glamourer/Gui/Tabs/HeaderDrawer.cs b/Glamourer/Gui/Tabs/HeaderDrawer.cs index 2963c0c..6f62e08 100644 --- a/Glamourer/Gui/Tabs/HeaderDrawer.cs +++ b/Glamourer/Gui/Tabs/HeaderDrawer.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Numerics; using Dalamud.Interface; +using Dalamud.Interface.Utility; using ImGuiNET; using OtterGui; using OtterGui.Raii; diff --git a/Glamourer/Gui/Tabs/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab.cs index 1d852e9..ff55b14 100644 --- a/Glamourer/Gui/Tabs/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using Dalamud.Interface; using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Interop; using Glamourer.Interop.Penumbra; diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index c2c94bf..8c596ae 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -1,15 +1,16 @@ using System; using System.Linq; using System.Numerics; -using Dalamud.Interface; +using Dalamud.Interface.Utility; using Glamourer.Customization; +using Glamourer.Interop; using Glamourer.Services; using Glamourer.Unlocks; using ImGuiNET; -using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; +using ImGuiClip = OtterGui.ImGuiClip; namespace Glamourer.Gui.Tabs.UnlocksTab; @@ -22,6 +23,7 @@ public class UnlockOverview private readonly PenumbraChangedItemTooltip _tooltip; private readonly TextureService _textures; private readonly CodeService _codes; + private readonly JobService _jobs; private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f); @@ -67,7 +69,8 @@ public class UnlockOverview } public UnlockOverview(ItemManager items, CustomizationService customizations, ItemUnlockManager itemUnlocks, - CustomizeUnlockManager customizeUnlocks, PenumbraChangedItemTooltip tooltip, TextureService textures, CodeService codes) + CustomizeUnlockManager customizeUnlocks, PenumbraChangedItemTooltip tooltip, TextureService textures, CodeService codes, + JobService jobs) { _items = items; _customizations = customizations; @@ -76,6 +79,7 @@ public class UnlockOverview _tooltip = tooltip; _textures = textures; _codes = codes; + _jobs = jobs; } public void Draw() @@ -113,10 +117,11 @@ public class UnlockOverview if (!_customizeUnlocks.Unlockable.TryGetValue(customize, out var unlockData)) continue; - var unlocked = _customizeUnlocks.IsUnlocked(customize, out var time) ; + var unlocked = _customizeUnlocks.IsUnlocked(customize, out var time); var icon = _customizations.AwaitedService.GetIcon(customize.IconId); - ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One, unlocked || _codes.EnabledShirts ? Vector4.One : UnavailableTint); + ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One, + unlocked || _codes.EnabledShirts ? Vector4.One : UnavailableTint); if (ImGui.IsItemHovered()) { using var tt = ImRaii.Tooltip(); @@ -184,6 +189,28 @@ public class UnlockOverview ImGui.TextUnformatted(slot is EquipSlot.MainHand ? $"{item.Weapon()}" : $"{item.Armor()}"); ImGui.TextUnformatted( unlocked ? time == DateTimeOffset.MinValue ? "Always Unlocked" : $"Unlocked on {time:g}" : "Not Unlocked."); + + if (item.Level.Value <= 1) + { + if (item.JobRestrictions.Id <= 1 || item.JobRestrictions.Id >= _jobs.AllJobGroups.Count) + ImGui.TextUnformatted("For Everyone"); + else + ImGui.TextUnformatted($"For all {_jobs.AllJobGroups[item.JobRestrictions.Id].Name}"); + } + else + { + if (item.JobRestrictions.Id <= 1 || item.JobRestrictions.Id >= _jobs.AllJobGroups.Count) + ImGui.TextUnformatted($"For Everyone of at least Level {item.Level}"); + else + ImGui.TextUnformatted($"For all {_jobs.AllJobGroups[item.JobRestrictions.Id].Name} of at least Level {item.Level}"); + } + + if (item.Flags.HasFlag(ItemFlags.IsDyable)) + ImGui.TextUnformatted("Dyable"); + if (item.Flags.HasFlag(ItemFlags.IsTradable)) + ImGui.TextUnformatted("Tradable"); + if (item.Flags.HasFlag(ItemFlags.IsCrestWorthy)) + ImGui.TextUnformatted("Can apply Crest"); _tooltip.CreateTooltip(item, string.Empty, false); } } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs index e7eaf15..7ac9989 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs @@ -4,7 +4,9 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using Dalamud.Interface; +using Dalamud.Interface.Utility; using Glamourer.Events; +using Glamourer.Interop; using Glamourer.Services; using Glamourer.Structs; using Glamourer.Unlocks; @@ -22,14 +24,20 @@ public class UnlockTable : Table, IDisposable private readonly ObjectUnlocked _event; public UnlockTable(ItemManager items, TextureService textures, ItemUnlockManager itemUnlocks, - PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event) + PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event, JobService jobs) : base("ItemUnlockTable", new ItemList(items), new NameColumn(textures, tooltip) { Label = "Item Name..." }, new SlotColumn() { Label = "Equip Slot" }, new TypeColumn() { Label = "Item Type..." }, new UnlockDateColumn(itemUnlocks) { Label = "Unlocked" }, new ItemIdColumn() { Label = "Item Id..." }, - new ModelDataColumn(items) { Label = "Model Data..." }) + new ModelDataColumn(items) { Label = "Model Data..." }, + new JobColumn(jobs) { Label = "Jobs" }, + new LevelColumn() { Label = "Level..." }, + new DyableColumn() { Label = "Dye" }, + new CrestColumn() { Label = "Crest" }, + new TradableColumn() { Label = "Trade" } + ) { _event = @event; Sortable = true; @@ -267,6 +275,160 @@ public class UnlockTable : Table, IDisposable } } + private sealed class LevelColumn : ColumnString + { + public override float Width + => 70 * ImGuiHelpers.GlobalScale; + + public override string ToName(EquipItem item) + => item.Level.ToString(); + + public override void DrawColumn(EquipItem item, int _) + => ImGuiUtil.RightAlign(item.Level.Value.ToString()); + + public override int Compare(EquipItem lhs, EquipItem rhs) + => lhs.Level.Value.CompareTo(rhs.Level.Value); + } + + + private sealed class JobColumn : ColumnFlags + { + public override float Width + => 200 * ImGuiHelpers.GlobalScale; + + private readonly JobService _jobs; + + private readonly JobFlag[] _values; + private readonly string[] _names; + private JobFlag _filterValue; + + public override JobFlag FilterValue + => _filterValue; + + public JobColumn(JobService jobs) + { + _jobs = jobs; + _values = _jobs.Jobs.Values.Skip(1).Select(j => j.Flag).ToArray(); + _names = _jobs.Jobs.Values.Skip(1).Select(j => j.Abbreviation).ToArray(); + AllFlags = _values.Aggregate((l, r) => l | r); + _filterValue = AllFlags; + } + + protected override void SetValue(JobFlag value, bool enable) + => _filterValue = enable ? _filterValue | value : _filterValue & ~value; + + protected override IReadOnlyList Values + => _values; + + protected override string[] Names + => _names; + + public override int Compare(EquipItem lhs, EquipItem rhs) + => lhs.JobRestrictions.Id.CompareTo(rhs.JobRestrictions.Id); + + public override bool FilterFunc(EquipItem item) + { + if (item.JobRestrictions.Id < 2) + return true; + + if (item.JobRestrictions.Id >= _jobs.AllJobGroups.Count) + return false; + + var group = _jobs.AllJobGroups[item.JobRestrictions.Id]; + return group.Fits(FilterValue); + } + + public override void DrawColumn(EquipItem item, int idx) + { + var text = $"Unknown {item.JobRestrictions.Id}"; + if (item.JobRestrictions.Id < _jobs.AllJobGroups.Count) + { + var group = _jobs.AllJobGroups[Math.Max((int)item.JobRestrictions.Id, 1)]; + if (group.Name.Length > 0) + text = group.Name; + } + + ImGui.TextUnformatted(text); + } + } + + [Flags] + private enum YesNoFlag + { + Yes = 0x01, + No = 0x02, + }; + + private class YesNoColumn : ColumnFlags + { + public string Tooltip = string.Empty; + + private YesNoFlag _filterValue; + + public override YesNoFlag FilterValue + => _filterValue; + + public YesNoColumn() + { + AllFlags = YesNoFlag.Yes | YesNoFlag.No; + _filterValue = AllFlags; + } + + protected override void SetValue(YesNoFlag value, bool enable) + => _filterValue = enable ? _filterValue | value : _filterValue & ~value; + + protected virtual bool GetValue(EquipItem item) + => false; + + public override float Width + => ImGui.GetFrameHeight() * 2; + + public override bool FilterFunc(EquipItem item) + => GetValue(item) + ? FilterValue.HasFlag(YesNoFlag.Yes) + : FilterValue.HasFlag(YesNoFlag.No); + + public override int Compare(EquipItem lhs, EquipItem rhs) + => GetValue(lhs).CompareTo(GetValue(rhs)); + + public override void DrawColumn(EquipItem item, int idx) + { + using (var font = ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGuiUtil.Center(GetValue(item) ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString()); + } + + ImGuiUtil.HoverTooltip(Tooltip); + } + } + + private sealed class DyableColumn : YesNoColumn + { + public DyableColumn() + => Tooltip = "Whether the item is dyable."; + + protected override bool GetValue(EquipItem item) + => item.Flags.HasFlag(ItemFlags.IsDyable); + } + + private sealed class TradableColumn : YesNoColumn + { + public TradableColumn() + => Tooltip = "Whether the item is tradable."; + + protected override bool GetValue(EquipItem item) + => item.Flags.HasFlag(ItemFlags.IsTradable); + } + + private sealed class CrestColumn : YesNoColumn + { + public CrestColumn() + => Tooltip = "Whether a crest can be applied to the item.."; + + protected override bool GetValue(EquipItem item) + => item.Flags.HasFlag(ItemFlags.IsCrestWorthy); + } + private sealed class ItemList : IReadOnlyCollection { private readonly ItemManager _items; diff --git a/Glamourer/Gui/UiHelpers.cs b/Glamourer/Gui/UiHelpers.cs index 223d71d..93311b6 100644 --- a/Glamourer/Gui/UiHelpers.cs +++ b/Glamourer/Gui/UiHelpers.cs @@ -1,6 +1,6 @@ using System; using System.Numerics; -using Dalamud.Interface; +using Dalamud.Interface.Utility; using Glamourer.Customization; using Glamourer.Services; using Glamourer.Structs; diff --git a/Glamourer/Interop/ChangeCustomizeService.cs b/Glamourer/Interop/ChangeCustomizeService.cs index f3ca2b5..fe7c907 100644 --- a/Glamourer/Interop/ChangeCustomizeService.cs +++ b/Glamourer/Interop/ChangeCustomizeService.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using Dalamud.Hooking; +using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Glamourer.Customization; @@ -18,7 +19,8 @@ namespace Glamourer.Interop; /// public unsafe class ChangeCustomizeService : EventWrapper>, ChangeCustomizeService.Priority> { - private readonly PenumbraReloaded _penumbraReloaded; + private readonly PenumbraReloaded _penumbraReloaded; + private readonly IGameInteropProvider _interop; /// Check whether we in a manual customize update, in which case we need to not toggle certain flags. public static readonly ThreadLocal InUpdate = new(() => false); @@ -29,10 +31,11 @@ public unsafe class ChangeCustomizeService : EventWrapper Create() { - var ret = Hook.FromAddress((nint)Human.MemberFunctionPointers.UpdateDrawData, ChangeCustomizeDetour); + var ret = _interop.HookFromAddress((nint)Human.MemberFunctionPointers.UpdateDrawData, ChangeCustomizeDetour); ret.Enable(); return ret; } @@ -66,7 +69,7 @@ public unsafe class ChangeCustomizeService : EventWrapper _itemList = new(12); - public InventoryService(MovedEquipment @event) + public InventoryService(MovedEquipment @event, IGameInteropProvider interop) { _event = @event; - _moveItemHook = Hook.FromAddress((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour); + _moveItemHook = interop.HookFromAddress((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour); _equipGearsetHook = - Hook.FromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearset, EquipGearSetDetour); + interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearset, EquipGearSetDetour); _moveItemHook.Enable(); _equipGearsetHook.Enable(); } @@ -76,7 +77,7 @@ public unsafe class InventoryService : IDisposable Add(EquipSlot.Neck, plate.ItemIds[8], plate.StainIds[8], ref entry->Neck); Add(EquipSlot.Wrists, plate.ItemIds[9], plate.StainIds[9], ref entry->Wrists); Add(EquipSlot.RFinger, plate.ItemIds[10], plate.StainIds[10], ref entry->RingRight); - Add(EquipSlot.LFinger, plate.ItemIds[11], plate.StainIds[11], ref entry->RightLeft); + Add(EquipSlot.LFinger, plate.ItemIds[11], plate.StainIds[11], ref entry->RingLeft); } else { @@ -101,7 +102,7 @@ public unsafe class InventoryService : IDisposable Add(EquipSlot.Neck, ref entry->Neck); Add(EquipSlot.Wrists, ref entry->Wrists); Add(EquipSlot.RFinger, ref entry->RingRight); - Add(EquipSlot.LFinger, ref entry->RightLeft); + Add(EquipSlot.LFinger, ref entry->RingLeft); } _event.Invoke(_itemList.ToArray()); diff --git a/Glamourer/Interop/JobService.cs b/Glamourer/Interop/JobService.cs index fd00dd7..2fe322f 100644 --- a/Glamourer/Interop/JobService.cs +++ b/Glamourer/Interop/JobService.cs @@ -16,14 +16,16 @@ public class JobService : IDisposable public readonly IReadOnlyDictionary Jobs; public readonly IReadOnlyDictionary JobGroups; + public readonly IReadOnlyList AllJobGroups; public event Action? JobChanged; - public JobService(IDataManager gameData) + public JobService(IDataManager gameData, IGameInteropProvider interop) { - SignatureHelper.Initialise(this); + interop.InitializeFromAttributes(this); _characterDataOffset = Marshal.OffsetOf(nameof(Character.CharacterData)); Jobs = GameData.Jobs(gameData); + AllJobGroups = GameData.AllJobGroups(gameData); JobGroups = GameData.JobGroups(gameData); _changeJobHook.Enable(); } @@ -47,7 +49,7 @@ public class JobService : IDisposable var actor = (Actor)(data - _characterDataOffset); var newJob = Jobs.TryGetValue(newJobIndex, out var j) ? j : Jobs[0]; var oldJob = Jobs.TryGetValue(oldJobIndex, out var o) ? o : Jobs[0]; - + Glamourer.Log.Excessive($"{actor} changed job from {oldJob} to {newJob}."); JobChanged?.Invoke(actor, oldJob, newJob); } diff --git a/Glamourer/Interop/MetaService.cs b/Glamourer/Interop/MetaService.cs index 142fe75..95077bd 100644 --- a/Glamourer/Interop/MetaService.cs +++ b/Glamourer/Interop/MetaService.cs @@ -1,5 +1,6 @@ using System; using Dalamud.Hooking; +using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Glamourer.Events; using Glamourer.Interop.Structs; @@ -19,14 +20,18 @@ public unsafe class MetaService : IDisposable private readonly Hook _hideWeaponsHook; private readonly Hook _toggleVisorHook; - public MetaService(WeaponVisibilityChanged weaponEvent, HeadGearVisibilityChanged headGearEvent, VisorStateChanged visorEvent) + public MetaService(WeaponVisibilityChanged weaponEvent, HeadGearVisibilityChanged headGearEvent, VisorStateChanged visorEvent, + IGameInteropProvider interop) { - _weaponEvent = weaponEvent; - _headGearEvent = headGearEvent; - _visorEvent = visorEvent; - _hideHatGearHook = Hook.FromAddress((nint)DrawDataContainer.MemberFunctionPointers.HideHeadgear, HideHatDetour); - _hideWeaponsHook = Hook.FromAddress((nint)DrawDataContainer.MemberFunctionPointers.HideWeapons, HideWeaponsDetour); - _toggleVisorHook = Hook.FromAddress((nint)DrawDataContainer.MemberFunctionPointers.SetVisor, ToggleVisorDetour); + _weaponEvent = weaponEvent; + _headGearEvent = headGearEvent; + _visorEvent = visorEvent; + _hideHatGearHook = + interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.HideHeadgear, HideHatDetour); + _hideWeaponsHook = + interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.HideWeapons, HideWeaponsDetour); + _toggleVisorHook = + interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.SetVisor, ToggleVisorDetour); _hideHatGearHook.Enable(); _hideWeaponsHook.Enable(); _toggleVisorHook.Enable(); diff --git a/Glamourer/Interop/ObjectManager.cs b/Glamourer/Interop/ObjectManager.cs index 92012d6..15caeee 100644 --- a/Glamourer/Interop/ObjectManager.cs +++ b/Glamourer/Interop/ObjectManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using Dalamud.Game; using Dalamud.Game.ClientState.Objects; using Dalamud.Plugin.Services; using Glamourer.Interop.Structs; @@ -12,16 +11,16 @@ namespace Glamourer.Interop; public class ObjectManager : IReadOnlyDictionary { - private readonly Framework _framework; - private readonly IClientState _clientState; - private readonly IObjectTable _objects; - private readonly ActorService _actors; + private readonly IFramework _framework; + private readonly IClientState _clientState; + private readonly IObjectTable _objects; + private readonly ActorService _actors; private readonly ITargetManager _targets; public IObjectTable Objects => _objects; - public ObjectManager(Framework framework, IClientState clientState, IObjectTable objects, ActorService actors, ITargetManager targets) + public ObjectManager(IFramework framework, IClientState clientState, IObjectTable objects, ActorService actors, ITargetManager targets) { _framework = framework; _clientState = clientState; diff --git a/Glamourer/Interop/Penumbra/PenumbraService.cs b/Glamourer/Interop/Penumbra/PenumbraService.cs index 2bde8d1..16c1138 100644 --- a/Glamourer/Interop/Penumbra/PenumbraService.cs +++ b/Glamourer/Interop/Penumbra/PenumbraService.cs @@ -236,7 +236,7 @@ public unsafe class PenumbraService : IDisposable } catch (Exception e) { - PluginLog.Debug($"Failure redrawing object:\n{e}"); + Glamourer.Log.Debug($"Failure redrawing object:\n{e}"); } } @@ -249,7 +249,7 @@ public unsafe class PenumbraService : IDisposable } catch (Exception e) { - PluginLog.Debug($"Failure redrawing object:\n{e}"); + Glamourer.Log.Debug($"Failure redrawing object:\n{e}"); } } diff --git a/Glamourer/Interop/ScalingService.cs b/Glamourer/Interop/ScalingService.cs index 5747bcc..b2fee58 100644 --- a/Glamourer/Interop/ScalingService.cs +++ b/Glamourer/Interop/ScalingService.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Hooking; +using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Glamourer.Interop.Structs; @@ -9,12 +10,16 @@ using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; namespace Glamourer.Interop; -// TODO: Use client structs sigs. public unsafe class ScalingService : IDisposable { - public ScalingService() + public ScalingService(IGameInteropProvider interop) { - SignatureHelper.Initialise(this); + interop.InitializeFromAttributes(this); + _setupMountHook = + interop.HookFromAddress((nint)Character.MountContainer.MemberFunctionPointers.SetupMount, SetupMountDetour); + _setupOrnamentHook = interop.HookFromAddress((nint)Ornament.MemberFunctionPointers.SetupOrnament, SetupOrnamentDetour); + _calculateHeightHook = + interop.HookFromAddress((nint)Character.MemberFunctionPointers.CalculateHeight, CalculateHeightDetour); _setupMountHook.Enable(); _setupOrnamentHook.Enable(); _placeMinionHook.Enable(); @@ -34,20 +39,16 @@ public unsafe class ScalingService : IDisposable private delegate void PlaceMinion(Companion* character); private delegate float CalculateHeight(Character* character); - [Signature("E8 ?? ?? ?? ?? 48 8B 43 ?? 80 B8 ?? ?? ?? ?? ?? 74 ?? 0F B6 90", DetourName = nameof(SetupMountDetour))] - private readonly Hook _setupMountHook = null!; + private readonly Hook _setupMountHook; - [Signature("48 89 5C 24 ?? 41 54 41 56 41 57 48 83 EC ?? 4D 8B F8", DetourName = nameof(SetupOrnamentDetour))] - private readonly Hook _setupOrnamentHook = null!; + private readonly Hook _setupOrnamentHook; + private readonly Hook _calculateHeightHook; + + // TODO: Use client structs sig. [Signature("48 89 5C 24 ?? 55 57 41 57 48 8D 6C 24", DetourName = nameof(PlaceMinionDetour))] private readonly Hook _placeMinionHook = null!; - [Signature(global::Penumbra.GameData.Sigs.CalculateHeight, DetourName = nameof(CalculateHeightDetour))] - private readonly Hook _calculateHeightHook = null!; - - [Signature("E8 ?? ?? ?? ?? 48 85 C0 48 0F 45 F8")] - private readonly delegate* unmanaged _getParentGameObject = null!; private void SetupMountDetour(Character.MountContainer* container, short mountId, uint unk1, uint unk2, uint unk3, byte unk4) { @@ -59,7 +60,7 @@ public unsafe class ScalingService : IDisposable private void SetupOrnamentDetour(Ornament* ornament, uint* unk1, float* unk2) { - var character = _getParentGameObject(ornament); + var character = ornament->Character.GetParentCharacter(); if (character == null) { _setupOrnamentHook.Original(ornament, unk1, unk2); diff --git a/Glamourer/Interop/UpdateSlotService.cs b/Glamourer/Interop/UpdateSlotService.cs index 8d0c0eb..f336f5a 100644 --- a/Glamourer/Interop/UpdateSlotService.cs +++ b/Glamourer/Interop/UpdateSlotService.cs @@ -1,5 +1,6 @@ using System; using Dalamud.Hooking; +using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using Glamourer.Events; using Glamourer.Interop.Structs; @@ -12,10 +13,10 @@ public unsafe class UpdateSlotService : IDisposable { public readonly SlotUpdating SlotUpdatingEvent; - public UpdateSlotService(SlotUpdating slotUpdating) + public UpdateSlotService(SlotUpdating slotUpdating, IGameInteropProvider interop) { SlotUpdatingEvent = slotUpdating; - SignatureHelper.Initialise(this); + interop.InitializeFromAttributes(this); _flagSlotForUpdateHook.Enable(); } diff --git a/Glamourer/Interop/VisorService.cs b/Glamourer/Interop/VisorService.cs index f6da12d..7a3aff8 100644 --- a/Glamourer/Interop/VisorService.cs +++ b/Glamourer/Interop/VisorService.cs @@ -1,7 +1,9 @@ using System; using System.Runtime.CompilerServices; using Dalamud.Hooking; +using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Glamourer.Events; using Glamourer.Interop.Structs; using Penumbra.GameData.Enums; @@ -12,10 +14,11 @@ public class VisorService : IDisposable { public readonly VisorStateChanged Event; - public VisorService(VisorStateChanged visorStateChanged) + public unsafe VisorService(VisorStateChanged visorStateChanged, IGameInteropProvider interop) { - Event = visorStateChanged; - SignatureHelper.Initialise(this); + Event = visorStateChanged; + _setupVisorHook = interop.HookFromAddress((nint)Human.MemberFunctionPointers.SetupVisor, SetupVisorDetour); + interop.InitializeFromAttributes(this); _setupVisorHook.Enable(); } @@ -30,7 +33,7 @@ public class VisorService : IDisposable /// The draw object. /// The desired state (true: toggled). /// Whether the state was changed. - public unsafe bool SetVisorState(Model human, bool on) + public bool SetVisorState(Model human, bool on) { if (!human.IsHuman) return false; @@ -46,12 +49,11 @@ public class VisorService : IDisposable private delegate void UpdateVisorDelegateInternal(nint humanPtr, ushort modelId, bool on); - [Signature(global::Penumbra.GameData.Sigs.SetupVisor, DetourName = nameof(SetupVisorDetour))] - private readonly Hook _setupVisorHook = null!; + private readonly Hook _setupVisorHook; private void SetupVisorDetour(nint human, ushort modelId, bool on) { - var originalOn = on; + var originalOn = on; // Invoke an event that can change the requested value // and also control whether the function should be called at all. Event.Invoke(human, ref on); @@ -70,10 +72,7 @@ public class VisorService : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe void SetupVisorHook(Model human, ushort modelId, bool on) { - // TODO: use client structs. - human.AsCharacterBase->UnkFlags_01 = (byte)(on - ? human.AsCharacterBase->UnkFlags_01 | Offsets.DrawObjectVisorStateFlag - : human.AsCharacterBase->UnkFlags_01 & ~Offsets.DrawObjectVisorStateFlag); + human.AsCharacterBase->VisorToggled = on; _setupVisorHook.Original(human.Address, modelId, on); } } diff --git a/Glamourer/Interop/WeaponService.cs b/Glamourer/Interop/WeaponService.cs index 8873114..708377b 100644 --- a/Glamourer/Interop/WeaponService.cs +++ b/Glamourer/Interop/WeaponService.cs @@ -1,5 +1,6 @@ using System; using Dalamud.Hooking; +using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Glamourer.Events; @@ -13,11 +14,11 @@ public unsafe class WeaponService : IDisposable { private readonly WeaponLoading _event; - public WeaponService(WeaponLoading @event) + public WeaponService(WeaponLoading @event, IGameInteropProvider interop) { _event = @event; - SignatureHelper.Initialise(this); - _loadWeaponHook = Hook.FromAddress((nint)DrawDataContainer.MemberFunctionPointers.LoadWeapon, LoadWeaponDetour); + _loadWeaponHook = + interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.LoadWeapon, LoadWeaponDetour); _loadWeaponHook.Enable(); } diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index b873dde..924d091 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Dalamud.Game.Command; -using Dalamud.Game.Gui; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.Services; using Glamourer.Automation; @@ -27,7 +26,7 @@ public class CommandService : IDisposable private readonly ICommandManager _commands; private readonly MainWindow _mainWindow; - private readonly ChatGui _chat; + private readonly IChatGui _chat; private readonly ActorService _actors; private readonly ObjectManager _objects; private readonly StateManager _stateManager; @@ -37,7 +36,7 @@ public class CommandService : IDisposable private readonly DesignConverter _converter; private readonly DesignFileSystem _designFileSystem; - public CommandService(ICommandManager commands, MainWindow mainWindow, ChatGui chat, ActorService actors, ObjectManager objects, + public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorService actors, ObjectManager objects, AutoDesignApplier autoDesignApplier, StateManager stateManager, DesignManager designManager, DesignConverter converter, DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager) { diff --git a/Glamourer/Services/CustomizationService.cs b/Glamourer/Services/CustomizationService.cs index 630479d..1b3f6e2 100644 --- a/Glamourer/Services/CustomizationService.cs +++ b/Glamourer/Services/CustomizationService.cs @@ -11,8 +11,8 @@ public sealed class CustomizationService : AsyncServiceWrapper CustomizationManager.Create(textures, gameData)) + public CustomizationService(ITextureProvider textures, IDataManager gameData, HumanModelList humanModels, IPluginLog log) + : base(nameof(CustomizationService), () => CustomizationManager.Create(textures, gameData, log)) => HumanModels = humanModels; public (Customize NewValue, CustomizeFlag Applied, CustomizeFlag Changed) Combine(Customize oldValues, Customize newValues, diff --git a/Glamourer/Services/DalamudServices.cs b/Glamourer/Services/DalamudServices.cs index 7872db1..30cf9c8 100644 --- a/Glamourer/Services/DalamudServices.cs +++ b/Glamourer/Services/DalamudServices.cs @@ -1,8 +1,4 @@ -using Dalamud.Game; -using Dalamud.Game.ClientState.Conditions; -using Dalamud.Game.ClientState.Keys; using Dalamud.Game.ClientState.Objects; -using Dalamud.Game.Gui; using Dalamud.Interface.DragDrop; using Dalamud.IoC; using Dalamud.Plugin; @@ -35,6 +31,8 @@ public class DalamudServices services.AddSingleton(PluginInterface.UiBuilder); services.AddSingleton(DragDropManager); services.AddSingleton(TextureProvider); + services.AddSingleton(Log); + services.AddSingleton(Interop); } // @formatter:off @@ -42,14 +40,16 @@ public class DalamudServices [PluginService][RequiredVersion("1.0")] public ICommandManager Commands { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IDataManager GameData { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IClientState ClientState { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public Condition Condition { get; private set; } = null!; + [PluginService][RequiredVersion("1.0")] public ICondition Condition { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IGameGui GameGui { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public ChatGui Chat { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public Framework Framework { get; private set; } = null!; + [PluginService][RequiredVersion("1.0")] public IChatGui Chat { get; private set; } = null!; + [PluginService][RequiredVersion("1.0")] public IFramework Framework { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public ITargetManager Targets { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IObjectTable Objects { get; private set; } = null!; - [PluginService][RequiredVersion("1.0")] public KeyState KeyState { get; private set; } = null!; + [PluginService][RequiredVersion("1.0")] public IKeyState KeyState { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public IDragDropManager DragDropManager { get; private set; } = null!; [PluginService][RequiredVersion("1.0")] public ITextureProvider TextureProvider { get; private set; } = null!; + [PluginService][RequiredVersion("1.0")] public IPluginLog Log { get; private set; } = null!; + [PluginService][RequiredVersion("1.0")] public IGameInteropProvider Interop { get; private set; } = null!; // @formatter:on } diff --git a/Glamourer/Services/ItemManager.cs b/Glamourer/Services/ItemManager.cs index 7489c55..7df33bd 100644 --- a/Glamourer/Services/ItemManager.cs +++ b/Glamourer/Services/ItemManager.cs @@ -28,14 +28,14 @@ public class ItemManager : IDisposable public readonly EquipItem DefaultSword; public ItemManager(Configuration config, DalamudPluginInterface pi, IDataManager gameData, IdentifierService identifierService, - ItemService itemService) + ItemService itemService, IPluginLog log) { _config = config; ItemSheet = gameData.GetExcelSheet()!; IdentifierService = identifierService; - Stains = new StainData(pi, gameData, gameData.Language); + Stains = new StainData(pi, gameData, gameData.Language, log); ItemService = itemService; - RestrictedGear = new RestrictedGear(pi, gameData.Language, gameData); + RestrictedGear = new RestrictedGear(pi, gameData.Language, gameData, log); DefaultSword = EquipItem.FromMainhand(ItemSheet.GetRow(1601)!); // Weathered Shortsword } @@ -58,13 +58,13 @@ public class ItemManager : IDisposable => uint.MaxValue - 384 - (uint)type; public static EquipItem NothingItem(EquipSlot slot) - => new(Nothing, NothingId(slot), 0, 0, 0, 0, slot.ToEquipType()); + => new(Nothing, NothingId(slot), 0, 0, 0, 0, slot.ToEquipType(), 0, 0, 0); public static EquipItem NothingItem(FullEquipType type) - => new(Nothing, NothingId(type), 0, 0, 0, 0, type); + => new(Nothing, NothingId(type), 0, 0, 0, 0, type, 0, 0, 0); public static EquipItem SmallClothesItem(EquipSlot slot) - => new(SmallClothesNpc, SmallclothesId(slot), 0, SmallClothesNpcModel, 0, 1, slot.ToEquipType()); + => new(SmallClothesNpc, SmallclothesId(slot), 0, SmallClothesNpcModel, 0, 1, slot.ToEquipType(), 0, 0, 0); public EquipItem Resolve(EquipSlot slot, ItemId itemId) { @@ -78,7 +78,7 @@ public class ItemManager : IDisposable return EquipItem.FromId(itemId); if (item.Type.ToSlot() != slot) - return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.ModelId, item.WeaponType, item.Variant, 0); + return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.ModelId, item.WeaponType, item.Variant, 0, 0, 0, 0); return item; } @@ -92,7 +92,7 @@ public class ItemManager : IDisposable return EquipItem.FromId(itemId); if (item.Type != type) - return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.ModelId, item.WeaponType, item.Variant, 0); + return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.ModelId, item.WeaponType, item.Variant, 0, 0, 0, 0); return item; } @@ -101,7 +101,7 @@ public class ItemManager : IDisposable { slot = slot.ToSlot(); if (slot.ToIndex() == uint.MaxValue) - return new EquipItem($"Invalid ({id.Id}-{variant})", 0, 0, id, 0, variant, 0); + return new EquipItem($"Invalid ({id.Id}-{variant})", 0, 0, id, 0, variant, 0, 0, 0, 0); switch (id.Id) { @@ -135,12 +135,12 @@ public class ItemManager : IDisposable } if (slot is not EquipSlot.MainHand and not EquipSlot.OffHand) - return new EquipItem($"Invalid ({id.Id}-{type.Id}-{variant})", 0, 0, id, type, variant, 0); + return new EquipItem($"Invalid ({id.Id}-{type.Id}-{variant})", 0, 0, id, type, variant, 0, 0, 0, 0); var item = IdentifierService.AwaitedService.Identify(id, type, variant, slot).FirstOrDefault(i => i.Type.ToSlot() == slot); return item.Valid ? item - : EquipItem.FromIds(0, 0, id, type, variant, slot.ToEquipType(), null); + : EquipItem.FromIds(0, 0, id, type, variant, slot.ToEquipType()); } /// Returns whether an item id represents a valid item for a slot and gives the item. diff --git a/Glamourer/Services/ServiceWrapper.cs b/Glamourer/Services/ServiceWrapper.cs index 9e6bc9c..ded8ef0 100644 --- a/Glamourer/Services/ServiceWrapper.cs +++ b/Glamourer/Services/ServiceWrapper.cs @@ -2,7 +2,6 @@ using Dalamud.Plugin; using Penumbra.GameData.Actors; using System; using System.Threading.Tasks; -using Dalamud.Game; using Dalamud.Plugin.Services; using Glamourer.Interop.Penumbra; using Penumbra.GameData.Data; @@ -72,23 +71,23 @@ public abstract class AsyncServiceWrapper : IDisposable public sealed class IdentifierService : AsyncServiceWrapper { - public IdentifierService(DalamudPluginInterface pi, IDataManager data, ItemService itemService) - : base(nameof(IdentifierService), () => Penumbra.GameData.GameData.GetIdentifier(pi, data, itemService.AwaitedService)) + public IdentifierService(DalamudPluginInterface pi, IDataManager data, ItemService itemService, IPluginLog log) + : base(nameof(IdentifierService), () => Penumbra.GameData.GameData.GetIdentifier(pi, data, itemService.AwaitedService, log)) { } } public sealed class ItemService : AsyncServiceWrapper { - public ItemService(DalamudPluginInterface pi, IDataManager gameData) - : base(nameof(ItemService), () => new ItemData(pi, gameData, gameData.Language)) + public ItemService(DalamudPluginInterface pi, IDataManager gameData, IPluginLog log) + : base(nameof(ItemService), () => new ItemData(pi, gameData, gameData.Language, log)) { } } public sealed class ActorService : AsyncServiceWrapper { - public ActorService(DalamudPluginInterface pi, IObjectTable objects, IClientState clientState, Framework framework, IDataManager gameData, - IGameGui gui, PenumbraService penumbra) + public ActorService(DalamudPluginInterface pi, IObjectTable objects, IClientState clientState, IFramework framework, IGameInteropProvider interop, IDataManager gameData, + IGameGui gui, PenumbraService penumbra, IPluginLog log) : base(nameof(ActorService), - () => new ActorManager(pi, objects, clientState, framework, gameData, gui, idx => (short)penumbra.CutsceneParent(idx))) + () => new ActorManager(pi, objects, clientState, framework, interop, gameData, gui, idx => (short)penumbra.CutsceneParent(idx), log)) { } } diff --git a/Glamourer/Services/TextureService.cs b/Glamourer/Services/TextureService.cs index c37cf38..8f65bfa 100644 --- a/Glamourer/Services/TextureService.cs +++ b/Glamourer/Services/TextureService.cs @@ -1,9 +1,8 @@ using System; using System.Numerics; -using Dalamud.Game; using Dalamud.Interface; +using Dalamud.Interface.Internal; using Dalamud.Plugin.Services; -using ImGuiScene; using OtterGui.Classes; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -16,7 +15,7 @@ public sealed class TextureService : TextureCache, IDisposable : base(dataManager, textureProvider) => _slotIcons = CreateSlotIcons(uiBuilder); - private readonly TextureWrap?[] _slotIcons; + private readonly IDalamudTextureWrap?[] _slotIcons; public (nint, Vector2, bool) GetIcon(EquipItem item, EquipSlot slot) { @@ -38,9 +37,9 @@ public sealed class TextureService : TextureCache, IDisposable } } - private static TextureWrap[] CreateSlotIcons(UiBuilder uiBuilder) + private static IDalamudTextureWrap[] CreateSlotIcons(UiBuilder uiBuilder) { - var ret = new TextureWrap[12]; + var ret = new IDalamudTextureWrap[12]; using var uldWrapper = uiBuilder.LoadUld("ui/uld/ArmouryBoard.uld"); diff --git a/Glamourer/State/ActorState.cs b/Glamourer/State/ActorState.cs index 037d465..2cb3f2a 100644 --- a/Glamourer/State/ActorState.cs +++ b/Glamourer/State/ActorState.cs @@ -6,6 +6,7 @@ using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using System.Linq; using Dalamud.Game.ClientState.Conditions; +using Dalamud.Plugin.Services; using CustomizeIndex = Glamourer.Customization.CustomizeIndex; namespace Glamourer.State; @@ -23,7 +24,7 @@ public class ActorState public readonly ActorIdentifier Identifier; - public bool AllowsRedraw(Condition condition) + public bool AllowsRedraw(ICondition condition) => Identifier.Type is not IdentifierType.Special && !condition[ConditionFlag.OccupiedInCutSceneEvent]; /// This should always represent the unmodified state of the draw object. diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index f9bede5..4bd39b4 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Dalamud.Game.ClientState.Conditions; +using Dalamud.Plugin.Services; using Glamourer.Customization; using Glamourer.Events; using Glamourer.Services; @@ -16,9 +16,9 @@ public class StateEditor private readonly CustomizationService _customizations; private readonly HumanModelList _humans; private readonly GPoseService _gPose; - private readonly Condition _condition; + private readonly ICondition _condition; - public StateEditor(CustomizationService customizations, HumanModelList humans, ItemManager items, GPoseService gPose, Condition condition) + public StateEditor(CustomizationService customizations, HumanModelList humans, ItemManager items, GPoseService gPose, ICondition condition) { _customizations = customizations; _humans = humans; diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 304f3b8..8742c76 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -12,6 +12,7 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using System; using Dalamud.Game.ClientState.Conditions; +using Dalamud.Plugin.Services; namespace Glamourer.State; @@ -41,7 +42,7 @@ public class StateListener : IDisposable private readonly MovedEquipment _movedEquipment; private readonly GPoseService _gPose; private readonly ChangeCustomizeService _changeCustomizeService; - private readonly Condition _condition; + private readonly ICondition _condition; private ActorIdentifier _creatingIdentifier = ActorIdentifier.Invalid; private ActorState? _creatingState; @@ -57,7 +58,7 @@ public class StateListener : IDisposable SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose, - ChangeCustomizeService changeCustomizeService, CustomizationService customizations, Condition condition) + ChangeCustomizeService changeCustomizeService, CustomizationService customizations, ICondition condition) { _manager = manager; _items = items; diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index 47af9e7..dc4971d 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -3,8 +3,6 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using Dalamud.Game.ClientState; -using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; using Glamourer.Customization; using Glamourer.Designs; @@ -27,13 +25,13 @@ public class StateManager : IReadOnlyDictionary private readonly StateChanged _event; private readonly StateApplier _applier; private readonly StateEditor _editor; - private readonly Condition _condition; + private readonly ICondition _condition; private readonly IClientState _clientState; private readonly Dictionary _states = new(); public StateManager(ActorService actors, ItemManager items, StateChanged @event, StateApplier applier, StateEditor editor, - HumanModelList humans, Condition condition, IClientState clientState) + HumanModelList humans, ICondition condition, IClientState clientState) { _actors = actors; _items = items; diff --git a/Glamourer/Unlocks/CustomizeUnlockManager.cs b/Glamourer/Unlocks/CustomizeUnlockManager.cs index 9973f06..19a3f6c 100644 --- a/Glamourer/Unlocks/CustomizeUnlockManager.cs +++ b/Glamourer/Unlocks/CustomizeUnlockManager.cs @@ -29,23 +29,23 @@ public class CustomizeUnlockManager : IDisposable, ISavable => _unlocked; public CustomizeUnlockManager(SaveService saveService, CustomizationService customizations, IDataManager gameData, - IClientState clientState, ObjectUnlocked @event) + IClientState clientState, ObjectUnlocked @event, IGameInteropProvider interop) { - SignatureHelper.Initialise(this); + interop.InitializeFromAttributes(this); _saveService = saveService; _clientState = clientState; _event = @event; Unlockable = CreateUnlockableCustomizations(customizations, gameData); Load(); _setUnlockLinkValueHook.Enable(); - _clientState.Login += OnLogin; + _clientState.Login += Scan; Scan(); } public void Dispose() { _setUnlockLinkValueHook.Dispose(); - _clientState.Login -= OnLogin; + _clientState.Login -= Scan; } /// Check if a customization is unlocked for Glamourer. @@ -159,9 +159,6 @@ public class CustomizeUnlockManager : IDisposable, ISavable } } - private void OnLogin(object? _, EventArgs _2) - => Scan(); - public string ToFilename(FilenameService fileNames) => fileNames.UnlockFileCustomize; diff --git a/Glamourer/Unlocks/ItemUnlockManager.cs b/Glamourer/Unlocks/ItemUnlockManager.cs index 2ffa205..972a393 100644 --- a/Glamourer/Unlocks/ItemUnlockManager.cs +++ b/Glamourer/Unlocks/ItemUnlockManager.cs @@ -3,9 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using Dalamud.Game; using Dalamud.Plugin.Services; -using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.UI; using Glamourer.Events; @@ -22,7 +20,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary Unlockable; - public ItemUnlockManager(SaveService saveService, ItemManager items, IClientState clientState, IDataManager gameData, Framework framework, - ObjectUnlocked @event, IdentifierService identifier) + public ItemUnlockManager(SaveService saveService, ItemManager items, IClientState clientState, IDataManager gameData, IFramework framework, + ObjectUnlocked @event, IdentifierService identifier, IGameInteropProvider interop) { - SignatureHelper.Initialise(this); + interop.InitializeFromAttributes(this); _saveService = saveService; _items = items; _clientState = clientState; @@ -58,7 +56,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary Scan(); - private static Dictionary CreateUnlockData(IDataManager gameData, ItemManager items) { var ret = new Dictionary(); diff --git a/OtterGui b/OtterGui index 99047a4..b4092c4 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 99047a4780991a38e42a5ea8480c947601d4b57b +Subproject commit b4092c45d9b0b885bcc08f0f81b5c4fc9ea383a7 diff --git a/Penumbra.GameData b/Penumbra.GameData index 64fe8c3..acf8cf6 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 64fe8c348dda8aed2e2e3c0dd1864501bfb4be85 +Subproject commit acf8cf68f06e37bbe64f1100a68937da9efb762c diff --git a/repo.json b/repo.json index 5fd3ba2..bb3cad0 100644 --- a/repo.json +++ b/repo.json @@ -21,7 +21,7 @@ "TestingAssemblyVersion": "1.0.0.6", "RepoUrl": "https://github.com/Ottermandias/Glamourer", "ApplicableVersion": "any", - "DalamudApiLevel": 8, + "DalamudApiLevel": 9, "IsHide": "False", "IsTestingExclusive": "False", "DownloadCount": 1,