diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index feecf19..bac600a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,20 +9,18 @@ jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v2 with: submodules: recursive - name: Setup .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v1 with: - dotnet-version: | - 10.x.x - 9.x.x + dotnet-version: '8.x.x' - name: Restore dependencies run: dotnet restore - name: Download Dalamud run: | - Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -OutFile latest.zip + Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/latest.zip -OutFile latest.zip Expand-Archive -Force latest.zip "$env:AppData\XIVLauncher\addon\Hooks\dev" - name: Build run: | diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index 5639c7b..b9d3672 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -9,15 +9,13 @@ jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v2 with: submodules: recursive - name: Setup .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v1 with: - dotnet-version: | - 10.x.x - 9.x.x + dotnet-version: '8.x.x' - name: Restore dependencies run: dotnet restore - name: Download Dalamud diff --git a/Glamourer.Api b/Glamourer.Api index 5b6730d..b1b90e6 160000 --- a/Glamourer.Api +++ b/Glamourer.Api @@ -1 +1 @@ -Subproject commit 5b6730d46f17bdd02a441e23e2141576cf7acf53 +Subproject commit b1b90e6ecfeee76a12cb27793753fa87af21083f diff --git a/Glamourer.sln b/Glamourer.sln index e2915d5..4ac3356 100644 --- a/Glamourer.sln +++ b/Glamourer.sln @@ -7,7 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .github\workflows\release.yml = .github\workflows\release.yml - Glamourer\Glamourer.json = Glamourer\Glamourer.json repo.json = repo.json .github\workflows\test_release.yml = .github\workflows\test_release.yml EndProjectSection @@ -30,30 +29,30 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.ActiveCfg = Debug|x64 - {01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.Build.0 = Debug|x64 - {01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.ActiveCfg = Release|x64 - {01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.Build.0 = Release|x64 - {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.ActiveCfg = Debug|x64 - {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.Build.0 = Debug|x64 - {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.ActiveCfg = Release|x64 - {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.Build.0 = Release|x64 - {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.ActiveCfg = Debug|x64 - {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.Build.0 = Debug|x64 - {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.ActiveCfg = Release|x64 - {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.Build.0 = Release|x64 - {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.ActiveCfg = Debug|x64 - {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.Build.0 = Debug|x64 - {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.ActiveCfg = Release|x64 - {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.Build.0 = Release|x64 - {EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.ActiveCfg = Debug|x64 - {EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.Build.0 = Debug|x64 - {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.ActiveCfg = Release|x64 - {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.Build.0 = Release|x64 - {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.ActiveCfg = Debug|x64 - {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.Build.0 = Debug|x64 - {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.ActiveCfg = Release|x64 - {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.Build.0 = Release|x64 + {01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01EB903D-871F-4285-A8CF-6486561D5B5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01EB903D-871F-4285-A8CF-6486561D5B5B}.Release|Any CPU.Build.0 = Release|Any CPU + {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29C589ED-7AF1-4DE9-82EF-33EBEF19AAFA}.Release|Any CPU.Build.0 = Release|Any CPU + {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0A2FAF8-C3AE-4B7B-ADDB-4AAC1A855428}.Release|Any CPU.Build.0 = Release|Any CPU + {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.Build.0 = Release|Any CPU + {EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|Any CPU.Build.0 = Release|Any CPU + {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Glamourer/Api/ApiHelpers.cs b/Glamourer/Api/ApiHelpers.cs index 14cff3b..ed58500 100644 --- a/Glamourer/Api/ApiHelpers.cs +++ b/Glamourer/Api/ApiHelpers.cs @@ -1,43 +1,33 @@ using Glamourer.Api.Enums; using Glamourer.Designs; using Glamourer.State; -using OtterGui.Extensions; +using OtterGui; using OtterGui.Log; using OtterGui.Services; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; -using Penumbra.GameData.Structs; using Penumbra.String; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.Api; -public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService +public class ApiHelpers(ObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService { [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - internal IEnumerable FindExistingStates(string actorName, ushort worldId = ushort.MaxValue) + internal IEnumerable FindExistingStates(string actorName) { if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString)) yield break; - if (worldId == WorldId.AnyWorld.Id) - { - foreach (var state in stateManager.Values.Where(state - => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)) - yield return state; - } - else - { - var identifier = actors.CreatePlayer(byteString, worldId); - if (stateManager.TryGetValue(identifier, out var state)) - yield return state; - } + foreach (var state in stateManager.Values.Where(state + => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)) + yield return state; } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal GlamourerApiEc FindExistingState(int objectIndex, out ActorState? state) { - var actor = objects.Objects[objectIndex]; + var actor = objects[objectIndex]; var identifier = actor.GetIdentifier(actors); if (!identifier.IsValid) { @@ -52,7 +42,7 @@ public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, A [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal ActorState? FindState(int objectIndex) { - var actor = objects.Objects[objectIndex]; + var actor = objects[objectIndex]; var identifier = actor.GetIdentifier(actors); if (identifier.IsValid && stateManager.GetOrCreate(identifier, actor, out var state)) return state; @@ -83,8 +73,10 @@ public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, A if (objectName.Length == 0 || !ByteString.FromString(objectName, out var byteString)) return []; + objects.Update(); + return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString) - .Concat(objects + .Concat(objects.Identifiers .Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString) .SelectWhere(kvp => { diff --git a/Glamourer/Api/DesignsApi.cs b/Glamourer/Api/DesignsApi.cs index 9b48ade..ee49bd5 100644 --- a/Glamourer/Api/DesignsApi.cs +++ b/Glamourer/Api/DesignsApi.cs @@ -2,32 +2,15 @@ using Glamourer.Api.Enums; using Glamourer.Designs; using Glamourer.State; -using Newtonsoft.Json.Linq; using OtterGui.Services; namespace Glamourer.Api; -public class DesignsApi( - ApiHelpers helpers, - DesignManager designs, - StateManager stateManager, - DesignFileSystem fileSystem, - DesignColors color, - DesignConverter converter) - : IGlamourerApiDesigns, IApiService +public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager stateManager) : IGlamourerApiDesigns, IApiService { public Dictionary GetDesignList() => designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text); - public Dictionary GetDesignListExtended() - => fileSystem.ToDictionary(kvp => kvp.Key.Identifier, - kvp => (kvp.Key.Name.Text, kvp.Value.FullName(), color.GetColor(kvp.Key), kvp.Key.QuickDesign)); - - public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId) - => designs.Designs.ByIdentifier(designId) is { } d - ? (d.Name.Text, fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : d.Name.Text, color.GetColor(d), d.QuickDesign) - : (string.Empty, string.Empty, 0, false); - public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags) { var args = ApiHelpers.Args("Design", designId, "Index", objectIndex, "Key", key, "Flags", flags); @@ -50,7 +33,7 @@ public class DesignsApi( { var once = (flags & ApplyFlag.Once) != 0; var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true, - ResetMaterials: !once && key != 0, IsFinal: true); + ResetMaterials: !once && key != 0); using var restrict = ApiHelpers.Restrict(design, flags); stateManager.ApplyDesign(state, design, settings); @@ -83,56 +66,4 @@ public class DesignsApi( return ApiHelpers.Return(GlamourerApiEc.Success, args); } - - public (GlamourerApiEc, Guid) AddDesign(string designInput, string name) - { - var args = ApiHelpers.Args("DesignData", designInput, "Name", name); - - if (converter.FromBase64(designInput, true, true, out _) is not { } designBase) - try - { - var jObj = JObject.Parse(designInput); - designBase = converter.FromJObject(jObj, true, true); - if (designBase is null) - return (ApiHelpers.Return(GlamourerApiEc.CouldNotParse, args), Guid.Empty); - } - catch (Exception ex) - { - Glamourer.Log.Error($"Failure parsing data for AddDesign due to\n{ex}"); - return (ApiHelpers.Return(GlamourerApiEc.CouldNotParse, args), Guid.Empty); - } - - try - { - var design = designBase is Design d - ? designs.CreateClone(d, name, true) - : designs.CreateClone(designBase, name, true); - return (ApiHelpers.Return(GlamourerApiEc.Success, args), design.Identifier); - } - catch (Exception ex) - { - Glamourer.Log.Error($"Unknown error creating design via IPC:\n{ex}"); - return (ApiHelpers.Return(GlamourerApiEc.UnknownError, args), Guid.Empty); - } - } - - public GlamourerApiEc DeleteDesign(Guid designId) - { - var args = ApiHelpers.Args("DesignId", designId); - if (designs.Designs.ByIdentifier(designId) is not { } design) - return ApiHelpers.Return(GlamourerApiEc.NothingDone, args); - - designs.Delete(design); - return ApiHelpers.Return(GlamourerApiEc.Success, args); - } - - public string? GetDesignBase64(Guid designId) - => designs.Designs.ByIdentifier(designId) is { } design - ? converter.ShareBase64(design) - : null; - - public JObject? GetDesignJObject(Guid designId) - => designs.Designs.ByIdentifier(designId) is { } design - ? converter.ShareJObject(design) - : null; } diff --git a/Glamourer/Api/GlamourerApi.cs b/Glamourer/Api/GlamourerApi.cs index 85f873a..24ed840 100644 --- a/Glamourer/Api/GlamourerApi.cs +++ b/Glamourer/Api/GlamourerApi.cs @@ -3,17 +3,14 @@ using OtterGui.Services; namespace Glamourer.Api; -public class GlamourerApi(Configuration config, DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService +public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService { public const int CurrentApiVersionMajor = 1; - public const int CurrentApiVersionMinor = 7; + public const int CurrentApiVersionMinor = 3; public (int Major, int Minor) ApiVersion => (CurrentApiVersionMajor, CurrentApiVersionMinor); - public bool AutoReloadGearEnabled - => config.AutoRedrawEquipOnChanges; - public IGlamourerApiDesigns Designs => designs; diff --git a/Glamourer/Api/IpcProviders.cs b/Glamourer/Api/IpcProviders.cs index f120db3..8639a22 100644 --- a/Glamourer/Api/IpcProviders.cs +++ b/Glamourer/Api/IpcProviders.cs @@ -2,6 +2,7 @@ using Dalamud.Plugin; using Glamourer.Api.Api; using Glamourer.Api.Helpers; using OtterGui.Services; +using System.Reflection.Emit; using Glamourer.Api.Enums; namespace Glamourer.Api; @@ -22,17 +23,10 @@ public sealed class IpcProviders : IDisposable, IApiService new FuncProvider<(int Major, int Minor)>(pi, "Glamourer.ApiVersions", () => api.ApiVersion), // backward compatibility new FuncProvider(pi, "Glamourer.ApiVersion", () => api.ApiVersion.Major), // backward compatibility IpcSubscribers.ApiVersion.Provider(pi, api), - IpcSubscribers.AutoReloadGearEnabled.Provider(pi, api), IpcSubscribers.GetDesignList.Provider(pi, api.Designs), - IpcSubscribers.GetDesignListExtended.Provider(pi, api.Designs), - IpcSubscribers.GetExtendedDesignData.Provider(pi, api.Designs), IpcSubscribers.ApplyDesign.Provider(pi, api.Designs), IpcSubscribers.ApplyDesignName.Provider(pi, api.Designs), - IpcSubscribers.AddDesign.Provider(pi, api.Designs), - IpcSubscribers.DeleteDesign.Provider(pi, api.Designs), - IpcSubscribers.GetDesignBase64.Provider(pi, api.Designs), - IpcSubscribers.GetDesignJObject.Provider(pi, api.Designs), IpcSubscribers.SetItem.Provider(pi, api.Items), IpcSubscribers.SetItemName.Provider(pi, api.Items), @@ -43,29 +37,21 @@ public sealed class IpcProviders : IDisposable, IApiService (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.SetMetaState.Provider(pi, api.Items), - IpcSubscribers.SetMetaStateName.Provider(pi, api.Items), IpcSubscribers.GetState.Provider(pi, api.State), IpcSubscribers.GetStateName.Provider(pi, api.State), IpcSubscribers.GetStateBase64.Provider(pi, api.State), IpcSubscribers.GetStateBase64Name.Provider(pi, api.State), IpcSubscribers.ApplyState.Provider(pi, api.State), IpcSubscribers.ApplyStateName.Provider(pi, api.State), - IpcSubscribers.ReapplyState.Provider(pi, api.State), - IpcSubscribers.ReapplyStateName.Provider(pi, api.State), IpcSubscribers.RevertState.Provider(pi, api.State), IpcSubscribers.RevertStateName.Provider(pi, api.State), IpcSubscribers.UnlockState.Provider(pi, api.State), - IpcSubscribers.CanUnlock.Provider(pi, api.State), IpcSubscribers.UnlockStateName.Provider(pi, api.State), - IpcSubscribers.DeletePlayerState.Provider(pi, api.State), IpcSubscribers.UnlockAll.Provider(pi, api.State), IpcSubscribers.RevertToAutomation.Provider(pi, api.State), IpcSubscribers.RevertToAutomationName.Provider(pi, api.State), - IpcSubscribers.AutoReloadGearChanged.Provider(pi, api.State), IpcSubscribers.StateChanged.Provider(pi, api.State), IpcSubscribers.StateChangedWithType.Provider(pi, api.State), - IpcSubscribers.StateFinalized.Provider(pi, api.State), IpcSubscribers.GPoseChanged.Provider(pi, api.State), ]; _initializedProvider.Invoke(); @@ -81,5 +67,3 @@ public sealed class IpcProviders : IDisposable, IApiService _disposedProvider.Dispose(); } } - - diff --git a/Glamourer/Api/ItemsApi.cs b/Glamourer/Api/ItemsApi.cs index ac971c9..fd174ca 100644 --- a/Glamourer/Api/ItemsApi.cs +++ b/Glamourer/Api/ItemsApi.cs @@ -96,9 +96,9 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager 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 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)) { @@ -115,72 +115,6 @@ 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 SetMetaState(int objectIndex, MetaFlag types, bool newValue, uint key, ApplyFlag flags) - { - var args = ApiHelpers.Args("Index", objectIndex, "MetaTypes", types, "NewValue", newValue, "Key", key, "ApplyFlags", flags); - if (types == 0) - return ApiHelpers.Return(GlamourerApiEc.InvalidState, 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); - - // Grab MetaIndices from attached flags, and update the states. - var indices = types.ToIndices(); - foreach (var index in indices) - { - stateManager.ChangeMetaState(state, index, newValue, ApplySettings.Manual); - ApiHelpers.Lock(state, key, flags); - } - - return GlamourerApiEc.Success; - } - - public GlamourerApiEc SetMetaStateName(string playerName, MetaFlag types, bool newValue, uint key, ApplyFlag flags) - { - var args = ApiHelpers.Args("Name", playerName, "MetaTypes", types, "NewValue", newValue, "Key", key, "ApplyFlags", flags); - if (types == 0) - return ApiHelpers.Return(GlamourerApiEc.ItemInvalid, args); - - 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; - // update all MetaStates for this ActorState - foreach (var index in types.ToIndices()) - { - stateManager.ChangeMetaState(state, index, newValue, ApplySettings.Manual); - ApiHelpers.Lock(state, key, flags); - } - } - if (!anyFound) return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args); diff --git a/Glamourer/Api/StateApi.cs b/Glamourer/Api/StateApi.cs index 4ce9c01..331942b 100644 --- a/Glamourer/Api/StateApi.cs +++ b/Glamourer/Api/StateApi.cs @@ -4,57 +4,51 @@ using Glamourer.Automation; using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Events; +using Glamourer.Interop.Structs; using Glamourer.State; using Newtonsoft.Json.Linq; using OtterGui.Services; using Penumbra.GameData.Interop; -using Penumbra.GameData.Structs; +using ObjectManager = Glamourer.Interop.ObjectManager; using StateChanged = Glamourer.Events.StateChanged; namespace Glamourer.Api; public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable { - private readonly ApiHelpers _helpers; - private readonly StateManager _stateManager; - private readonly DesignConverter _converter; - private readonly AutoDesignApplier _autoDesigns; - private readonly ActorObjectManager _objects; - private readonly AutoRedrawChanged _autoRedraw; - private readonly StateChanged _stateChanged; - private readonly StateFinalized _stateFinalized; - private readonly GPoseService _gPose; + private readonly ApiHelpers _helpers; + private readonly StateManager _stateManager; + private readonly DesignConverter _converter; + private readonly Configuration _config; + private readonly AutoDesignApplier _autoDesigns; + private readonly ObjectManager _objects; + private readonly StateChanged _stateChanged; + private readonly GPoseService _gPose; public StateApi(ApiHelpers helpers, StateManager stateManager, DesignConverter converter, + Configuration config, AutoDesignApplier autoDesigns, - ActorObjectManager objects, - AutoRedrawChanged autoRedraw, + ObjectManager objects, StateChanged stateChanged, - StateFinalized stateFinalized, GPoseService gPose) { - _helpers = helpers; - _stateManager = stateManager; - _converter = converter; - _autoDesigns = autoDesigns; - _objects = objects; - _autoRedraw = autoRedraw; - _stateChanged = stateChanged; - _stateFinalized = stateFinalized; - _gPose = gPose; - _autoRedraw.Subscribe(OnAutoRedrawChange, AutoRedrawChanged.Priority.StateApi); + _helpers = helpers; + _stateManager = stateManager; + _converter = converter; + _config = config; + _autoDesigns = autoDesigns; + _objects = objects; + _stateChanged = stateChanged; + _gPose = gPose; _stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc); - _stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi); - _gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi); + _gPose.Subscribe(OnGPoseChange, GPoseService.Priority.GlamourerIpc); } public void Dispose() { - _autoRedraw.Unsubscribe(OnAutoRedrawChange); _stateChanged.Unsubscribe(OnStateChanged); - _stateFinalized.Unsubscribe(OnStateFinalized); _gPose.Unsubscribe(OnGPoseChange); } @@ -126,48 +120,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable return ApiHelpers.Return(GlamourerApiEc.Success, args); } - public GlamourerApiEc ReapplyState(int objectIndex, uint key, ApplyFlag flags) - { - var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags); - if (_helpers.FindExistingState(objectIndex, out var state) is not GlamourerApiEc.Success) - return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args); - - if (state is null) - return ApiHelpers.Return(GlamourerApiEc.NothingDone, args); - - if (!state.CanUnlock(key)) - return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args); - - Reapply(_objects.Objects[objectIndex], state, key, flags); - return ApiHelpers.Return(GlamourerApiEc.Success, args); - } - - public GlamourerApiEc ReapplyStateName(string playerName, uint key, ApplyFlag flags) - { - var args = ApiHelpers.Args("Name", playerName, "Key", key, "Flags", flags); - var states = _helpers.FindExistingStates(playerName); - - var any = false; - var anyReapplied = false; - foreach (var state in states) - { - any = true; - if (!state.CanUnlock(key)) - continue; - - anyReapplied = true; - anyReapplied |= Reapply(state, key, flags) is GlamourerApiEc.Success; - } - - if (any) - ApiHelpers.Return(GlamourerApiEc.NothingDone, args); - - if (!anyReapplied) - return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args); - - return ApiHelpers.Return(GlamourerApiEc.Success, args); - } - public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags) { var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags); @@ -225,20 +177,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable return ApiHelpers.Return(GlamourerApiEc.Success, args); } - public GlamourerApiEc CanUnlock(int objectIndex, uint key, out bool isLocked, out bool canUnlock) - { - var args = ApiHelpers.Args("Index", objectIndex, "Key", key); - isLocked = false; - canUnlock = true; - if (_helpers.FindExistingState(objectIndex, out var state) is not GlamourerApiEc.Success) - return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args); - if (state is null) - return ApiHelpers.Return(GlamourerApiEc.Success, args); - isLocked = state.IsLocked; - canUnlock = state.CanUnlock(key); - return ApiHelpers.Return(GlamourerApiEc.Success, args); - } - public GlamourerApiEc UnlockStateName(string playerName, uint key) { var args = ApiHelpers.Args("Name", playerName, "Key", key); @@ -261,27 +199,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable return ApiHelpers.Return(GlamourerApiEc.Success, args); } - public GlamourerApiEc DeletePlayerState(string playerName, ushort worldId, uint key) - { - var args = ApiHelpers.Args("Name", playerName, "World", worldId, "Key", key); - var states = _helpers.FindExistingStates(playerName).ToList(); - if (states.Count is 0) - return ApiHelpers.Return(GlamourerApiEc.NothingDone, args); - - var anyLocked = false; - foreach (var state in states) - { - if (state.CanUnlock(key)) - _stateManager.DeleteState(state.Identifier); - else - anyLocked = true; - } - - return ApiHelpers.Return(anyLocked - ? GlamourerApiEc.InvalidKey - : GlamourerApiEc.Success, args); - } - public int UnlockAll(uint key) => _stateManager.Values.Count(state => state.Unlock(key)); @@ -297,7 +214,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable if (!state.CanUnlock(key)) return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args); - RevertToAutomation(_objects.Objects[objectIndex], state, key, flags); + RevertToAutomation(_objects[objectIndex], state, key, flags); return ApiHelpers.Return(GlamourerApiEc.Success, args); } @@ -331,47 +248,33 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable return ApiHelpers.Return(GlamourerApiEc.Success, args); } - public event Action? AutoReloadGearChanged; - public event Action? StateChanged; - public event Action? StateChangedWithType; - public event Action? StateFinalized; - public event Action? GPoseChanged; + public event Action? StateChanged; + public event Action? StateChangedWithType; + public event Action? GPoseChanged; private void ApplyDesign(ActorState state, DesignBase design, uint key, ApplyFlag flags) { var once = (flags & ApplyFlag.Once) != 0; var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true, - ResetMaterials: !once && key != 0, IsFinal: true); + ResetMaterials: !once && key != 0); _stateManager.ApplyDesign(state, design, settings); ApiHelpers.Lock(state, key, flags); } - private GlamourerApiEc Reapply(ActorState state, uint key, ApplyFlag flags) - { - if (!_objects.TryGetValue(state.Identifier, out var actors) || !actors.Valid) - return GlamourerApiEc.ActorNotFound; - - foreach (var actor in actors.Objects) - Reapply(actor, state, key, flags); - - return GlamourerApiEc.Success; - } - - private void Reapply(Actor actor, ActorState state, uint key, ApplyFlag flags) - { - var source = flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcFixed : StateSource.IpcManual; - _stateManager.ReapplyState(actor, state, false, source, true); - ApiHelpers.Lock(state, key, flags); - } - private void Revert(ActorState state, uint key, ApplyFlag flags) { - var source = flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcFixed : StateSource.IpcManual; + var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed; switch (flags & (ApplyFlag.Equipment | ApplyFlag.Customization)) { - case ApplyFlag.Equipment: _stateManager.ResetEquip(state, source, key); break; - case ApplyFlag.Customization: _stateManager.ResetCustomize(state, source, key); break; - case ApplyFlag.Equipment | ApplyFlag.Customization: _stateManager.ResetState(state, source, key, true); break; + case ApplyFlag.Equipment: + _stateManager.ResetEquip(state, source, key); + break; + case ApplyFlag.Customization: + _stateManager.ResetCustomize(state, source, key); + break; + case ApplyFlag.Equipment | ApplyFlag.Customization: + _stateManager.ResetState(state, source, key); + break; } ApiHelpers.Lock(state, key, flags); @@ -379,6 +282,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable private GlamourerApiEc RevertToAutomation(ActorState state, uint key, ApplyFlag flags) { + _objects.Update(); if (!_objects.TryGetValue(state.Identifier, out var actors) || !actors.Valid) return GlamourerApiEc.ActorNotFound; @@ -391,8 +295,8 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable private void RevertToAutomation(Actor actor, ActorState state, uint key, ApplyFlag flags) { var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed; - _autoDesigns.ReapplyAutomation(actor, state.Identifier, state, true, false, out var forcedRedraw); - _stateManager.ReapplyAutomationState(actor, state, forcedRedraw, true, source); + _autoDesigns.ReapplyAutomation(actor, state.Identifier, state, true, out var forcedRedraw); + _stateManager.ReapplyState(actor, state, forcedRedraw, source); ApiHelpers.Lock(state, key, flags); } @@ -404,7 +308,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable if (!state.CanUnlock(key)) return (GlamourerApiEc.InvalidKey, null); - return (GlamourerApiEc.Success, _converter.ShareJObject(state, ApplicationRules.All)); + return (GlamourerApiEc.Success, _converter.ShareJObject(state, ApplicationRules.AllWithConfig(_config))); } private (GlamourerApiEc, string?) ConvertBase64(ActorState? state, uint key) @@ -424,12 +328,11 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable }; } - private void OnAutoRedrawChange(bool autoReload) - => AutoReloadGearChanged?.Invoke(autoReload); + private void OnGPoseChange(bool gPose) + => GPoseChanged?.Invoke(gPose); private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5) { - Glamourer.Log.Excessive($"[OnStateChanged] State Changed with Type {type} [Affecting {actors.ToLazyString("nothing")}.]"); if (StateChanged != null) foreach (var actor in actors.Objects) StateChanged.Invoke(actor.Address); @@ -438,15 +341,4 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable foreach (var actor in actors.Objects) StateChangedWithType.Invoke(actor.Address, type); } - - private void OnStateFinalized(StateFinalizationType type, ActorData actors) - { - Glamourer.Log.Verbose($"[OnStateUpdated] State Updated with Type {type}. [Affecting {actors.ToLazyString("nothing")}.]"); - if (StateFinalized != null) - foreach (var actor in actors.Objects) - StateFinalized.Invoke(actor.Address, type); - } - - private void OnGPoseChange(bool gPose) - => GPoseChanged?.Invoke(gPose); } diff --git a/Glamourer/Automation/ApplicationType.cs b/Glamourer/Automation/ApplicationType.cs index f72c93f..3d409cb 100644 --- a/Glamourer/Automation/ApplicationType.cs +++ b/Glamourer/Automation/ApplicationType.cs @@ -1,5 +1,4 @@ -using Glamourer.Api.Enums; -using Glamourer.Designs; +using Glamourer.Designs; using Glamourer.GameData; using Penumbra.GameData.Enums; @@ -38,7 +37,7 @@ public static class ApplicationTypeExtensions var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0; var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0; var crestFlags = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 0; - var metaFlags = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.EarState : 0) + var metaFlags = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState : 0) | (type.HasFlag(ApplicationType.Weapons) ? MetaFlag.WeaponState : 0) | (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0); var bonusFlags = type.HasFlag(ApplicationType.Armor) ? BonusExtensions.All : 0; @@ -47,13 +46,7 @@ public static class ApplicationTypeExtensions } public static ApplicationCollection ApplyWhat(this ApplicationType type, IDesignStandIn designStandIn) - { - if(designStandIn is not DesignBase design) - return type.Collection(); - var ret = type.Collection().Restrict(design.Application); - ret.CustomizeRaw = ret.CustomizeRaw.FixApplication(design.CustomizeSet); - return ret; - } + => designStandIn is not DesignBase design ? type.Collection() : type.Collection().Restrict(design.Application); public const EquipFlag WeaponFlags = EquipFlag.Mainhand | EquipFlag.Offhand; public const EquipFlag ArmorFlags = EquipFlag.Head | EquipFlag.Body | EquipFlag.Hands | EquipFlag.Legs | EquipFlag.Feet; diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index a61a004..660acf4 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -11,28 +11,29 @@ using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.Automation; public sealed class AutoDesignApplier : IDisposable { - private readonly Configuration _config; - private readonly AutoDesignManager _manager; - private readonly StateManager _state; - private readonly JobService _jobs; - private readonly EquippedGearset _equippedGearset; - private readonly ActorManager _actors; - private readonly AutomationChanged _event; - private readonly ActorObjectManager _objects; - private readonly WeaponLoading _weapons; - private readonly HumanModelList _humans; - private readonly DesignMerger _designMerger; - private readonly IClientState _clientState; + private readonly Configuration _config; + private readonly AutoDesignManager _manager; + private readonly StateManager _state; + private readonly JobService _jobs; + private readonly EquippedGearset _equippedGearset; + private readonly ActorManager _actors; + private readonly AutomationChanged _event; + private readonly ObjectManager _objects; + private readonly WeaponLoading _weapons; + private readonly HumanModelList _humans; + private readonly DesignMerger _designMerger; + private readonly IClientState _clientState; private readonly JobChangeState _jobChangeState; public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs, ActorManager actors, - AutomationChanged @event, ActorObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState, + AutomationChanged @event, ObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState, EquippedGearset equippedGearset, DesignMerger designMerger, JobChangeState jobChangeState) { _config = config; @@ -153,6 +154,7 @@ public sealed class AutoDesignApplier : IDisposable if (newSet is not { Enabled: true }) return; + _objects.Update(); foreach (var id in newSet.Identifiers) { if (_objects.TryGetValue(id, out var data)) @@ -161,7 +163,7 @@ public sealed class AutoDesignApplier : IDisposable { Reduce(data.Objects[0], state, newSet, _config.RespectManualOnAutomationUpdate, false, true, out var forcedRedraw); foreach (var actor in data.Objects) - _state.ReapplyAutomationState(actor, forcedRedraw, false, StateSource.Fixed); + _state.ReapplyState(actor, forcedRedraw, StateSource.Fixed); } } else if (_objects.TryGetValueAllWorld(id, out data) || _objects.TryGetValueNonOwned(id, out data)) @@ -172,7 +174,7 @@ public sealed class AutoDesignApplier : IDisposable if (_state.GetOrCreate(specificId, actor, out var state)) { Reduce(actor, state, newSet, _config.RespectManualOnAutomationUpdate, false, true, out var forcedRedraw); - _state.ReapplyAutomationState(actor, forcedRedraw, false, StateSource.Fixed); + _state.ReapplyState(actor, forcedRedraw, StateSource.Fixed); } } } @@ -223,7 +225,7 @@ public sealed class AutoDesignApplier : IDisposable _state.ReapplyState(actor, forcedRedraw, StateSource.Fixed); } - public void ReapplyAutomation(Actor actor, ActorIdentifier identifier, ActorState state, bool reset, bool forcedNew, out bool forcedRedraw) + public void ReapplyAutomation(Actor actor, ActorIdentifier identifier, ActorState state, bool reset, out bool forcedRedraw) { forcedRedraw = false; if (!_config.EnableAutoDesigns) @@ -233,7 +235,7 @@ public sealed class AutoDesignApplier : IDisposable _state.ResetState(state, StateSource.Game); if (GetPlayerSet(identifier, out var set)) - Reduce(actor, state, set, false, false, forcedNew, out forcedRedraw); + Reduce(actor, state, set, false, false, false, out forcedRedraw); } public bool Reduce(Actor actor, ActorIdentifier identifier, [NotNullWhen(true)] out ActorState? state) @@ -291,16 +293,8 @@ public sealed class AutoDesignApplier : IDisposable set.Designs.Where(d => d.IsActive(actor)) .SelectMany(d => d.Design.AllLinks(newApplication).Select(l => (l.Design, l.Flags & d.Type, d.Jobs.Flags))), state.ModelData.Customize, state.BaseData, true, _config.AlwaysApplyAssociatedMods); - - if (_objects.IsInGPose && actor.IsGPoseOrCutscene) - { - mergedDesign.ResetTemporarySettings = false; - mergedDesign.AssociatedMods.Clear(); - } - else if (set.ResetTemporarySettings) - { + if (set.ResetTemporarySettings) mergedDesign.ResetTemporarySettings = true; - } _state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false, false, false)); forcedRedraw = mergedDesign.ForcedRedraw; diff --git a/Glamourer/Automation/AutoDesignManager.cs b/Glamourer/Automation/AutoDesignManager.cs index 7a4511b..5d30de0 100644 --- a/Glamourer/Automation/AutoDesignManager.cs +++ b/Glamourer/Automation/AutoDesignManager.cs @@ -10,7 +10,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Filesystem; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; @@ -235,22 +234,6 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos _event.Invoke(AutomationChanged.Type.ChangedBase, set, (old, newBase)); } - public void ChangeResetSettings(int whichSet, bool newValue) - { - if (whichSet >= _data.Count || whichSet < 0) - return; - - var set = _data[whichSet]; - if (newValue == set.ResetTemporarySettings) - return; - - var old = set.ResetTemporarySettings; - set.ResetTemporarySettings = newValue; - Save(); - Glamourer.Log.Debug($"Changed resetting of temporary settings of set {whichSet + 1} from {old} to {newValue}."); - _event.Invoke(AutomationChanged.Type.ChangedTemporarySettingsReset, set, newValue); - } - public void AddDesign(AutoDesignSet set, IDesignStandIn design) { var newDesign = new AutoDesign() diff --git a/Glamourer/Configuration.cs b/Glamourer/Configuration.cs index d266a55..4b59191 100644 --- a/Glamourer/Configuration.cs +++ b/Glamourer/Configuration.cs @@ -8,7 +8,6 @@ using Glamourer.Services; using Newtonsoft.Json; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Filesystem; using OtterGui.Widgets; using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; @@ -32,7 +31,6 @@ public class DefaultDesignSettings public bool ResetAdvancedDyes = false; public bool ShowQuickDesignBar = true; public bool ResetTemporarySettings = false; - public bool Locked = false; } public class Configuration : IPluginConfiguration, ISavable @@ -40,40 +38,35 @@ public class Configuration : IPluginConfiguration, ISavable [JsonIgnore] public readonly EphemeralConfig Ephemeral; - public bool AttachToPcp { get; set; } = true; - public bool UseRestrictedGearProtection { get; set; } = false; - public bool OpenFoldersByDefault { get; set; } = false; - public bool AutoRedrawEquipOnChanges { get; set; } = false; - public bool EnableAutoDesigns { get; set; } = true; - public bool HideApplyCheckmarks { get; set; } = false; - public bool SmallEquip { get; set; } = false; - public bool UnlockedItemMode { get; set; } = false; - public byte DisableFestivals { get; set; } = 1; - public bool EnableGameContextMenu { get; set; } = true; - public bool HideWindowInCutscene { get; set; } = false; - public bool ShowAutomationSetEditing { get; set; } = true; - public bool ShowAllAutomatedApplicationRules { get; set; } = true; - public bool ShowUnlockedItemWarnings { get; set; } = true; - public bool RevertManualChangesOnZoneChange { get; set; } = false; - public bool ShowQuickBarInTabs { get; set; } = true; - public bool OpenWindowAtStart { get; set; } = false; - public bool ShowWindowWhenUiHidden { get; set; } = false; - public bool KeepAdvancedDyesAttached { get; set; } = true; - public bool ShowPalettePlusImport { get; set; } = true; - public bool UseFloatForColors { get; set; } = true; - public bool UseRgbForColors { get; set; } = true; - public bool ShowColorConfig { get; set; } = true; - public bool ChangeEntireItem { get; set; } = false; - public bool AlwaysApplyAssociatedMods { get; set; } = true; - public bool UseTemporarySettings { get; set; } = true; - public bool AllowDoubleClickToApply { get; set; } = false; - public bool RespectManualOnAutomationUpdate { get; set; } = false; - public bool PreventRandomRepeats { get; set; } = false; - public string PcpFolder { get; set; } = "PCP"; - public string PcpColor { get; set; } = ""; - - public DesignPanelFlag HideDesignPanel { get; set; } = 0; - public DesignPanelFlag AutoExpandDesignPanel { get; set; } = 0; + public bool UseRestrictedGearProtection { get; set; } = false; + public bool OpenFoldersByDefault { get; set; } = false; + public bool AutoRedrawEquipOnChanges { get; set; } = false; + public bool EnableAutoDesigns { get; set; } = true; + public bool HideApplyCheckmarks { get; set; } = false; + public bool SmallEquip { get; set; } = false; + public bool UnlockedItemMode { get; set; } = false; + public byte DisableFestivals { get; set; } = 1; + public bool EnableGameContextMenu { get; set; } = true; + public bool HideWindowInCutscene { get; set; } = false; + public bool ShowAutomationSetEditing { get; set; } = true; + public bool ShowAllAutomatedApplicationRules { get; set; } = true; + public bool ShowUnlockedItemWarnings { get; set; } = true; + public bool RevertManualChangesOnZoneChange { get; set; } = false; + public bool ShowQuickBarInTabs { get; set; } = true; + public bool OpenWindowAtStart { get; set; } = false; + public bool ShowWindowWhenUiHidden { get; set; } = false; + public bool UseAdvancedParameters { get; set; } = true; + public bool UseAdvancedDyes { get; set; } = true; + public bool KeepAdvancedDyesAttached { get; set; } = true; + public bool ShowPalettePlusImport { get; set; } = true; + public bool UseFloatForColors { get; set; } = true; + public bool UseRgbForColors { get; set; } = true; + public bool ShowColorConfig { get; set; } = true; + public bool ChangeEntireItem { get; set; } = false; + public bool AlwaysApplyAssociatedMods { get; set; } = false; + public bool UseTemporarySettings { get; set; } = true; + public bool AllowDoubleClickToApply { get; set; } = false; + public bool RespectManualOnAutomationUpdate { get; set; } = false; public DefaultDesignSettings DefaultDesignSettings { get; set; } = new(); @@ -81,11 +74,10 @@ public class Configuration : IPluginConfiguration, ISavable public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY); public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); - public DoubleModifier IncognitoModifier { get; set; } = new(ModifierHotkey.Control); public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; public QdbButtons QdbButtons { get; set; } = - QdbButtons.ApplyDesign | QdbButtons.RevertAll | QdbButtons.RevertAutomation | QdbButtons.RevertAdvancedDyes; + QdbButtons.ApplyDesign | QdbButtons.RevertAll | QdbButtons.RevertAutomation | QdbButtons.RevertAdvanced; [JsonConverter(typeof(SortModeConverter))] [JsonProperty(Order = int.MaxValue)] @@ -162,7 +154,7 @@ public class Configuration : IPluginConfiguration, ISavable public static class Constants { - public const int CurrentVersion = 8; + public const int CurrentVersion = 7; public static readonly ISortMode[] ValidSortModes = [ diff --git a/Glamourer/DesignPanelFlag.cs b/Glamourer/DesignPanelFlag.cs deleted file mode 100644 index f9465d9..0000000 --- a/Glamourer/DesignPanelFlag.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Glamourer.Designs; -using Dalamud.Bindings.ImGui; -using OtterGui.Text; -using OtterGui.Text.EndObjects; - -namespace Glamourer; - -[Flags] -public enum DesignPanelFlag : uint -{ - Customization = 0x0001, - Equipment = 0x0002, - AdvancedCustomizations = 0x0004, - AdvancedDyes = 0x0008, - AppearanceDetails = 0x0010, - DesignDetails = 0x0020, - ModAssociations = 0x0040, - DesignLinks = 0x0080, - ApplicationRules = 0x0100, - DebugData = 0x0200, -} - -public static class DesignPanelFlagExtensions -{ - public static ReadOnlySpan ToName(this DesignPanelFlag flag) - => flag switch - { - DesignPanelFlag.Customization => "Customization"u8, - DesignPanelFlag.Equipment => "Equipment"u8, - DesignPanelFlag.AdvancedCustomizations => "Advanced Customization"u8, - DesignPanelFlag.AdvancedDyes => "Advanced Dyes"u8, - DesignPanelFlag.DesignDetails => "Design Details"u8, - DesignPanelFlag.ApplicationRules => "Application Rules"u8, - DesignPanelFlag.ModAssociations => "Mod Associations"u8, - DesignPanelFlag.DesignLinks => "Design Links"u8, - DesignPanelFlag.DebugData => "Debug Data"u8, - DesignPanelFlag.AppearanceDetails => "Appearance Details"u8, - _ => ""u8, - }; - - public static CollapsingHeader Header(this DesignPanelFlag flag, Configuration config) - { - if (config.HideDesignPanel.HasFlag(flag)) - return new CollapsingHeader() - { - Disposed = true, - }; - - var expand = config.AutoExpandDesignPanel.HasFlag(flag); - return ImUtf8.CollapsingHeaderId(flag.ToName(), expand ? ImGuiTreeNodeFlags.DefaultOpen : ImGuiTreeNodeFlags.None); - } - - public static void DrawTable(ReadOnlySpan label, DesignPanelFlag hidden, DesignPanelFlag expanded, Action setterHide, - Action setterExpand) - { - var checkBoxWidth = Math.Max(ImGui.GetFrameHeight(), ImUtf8.CalcTextSize("Expand"u8).X); - var textWidth = ImUtf8.CalcTextSize(DesignPanelFlag.AdvancedCustomizations.ToName()).X; - var tableSize = 2 * (textWidth + 2 * checkBoxWidth) + 10 * ImGui.GetStyle().CellPadding.X + 2 * ImGui.GetStyle().WindowPadding.X + 2 * ImGui.GetStyle().FrameBorderSize; - using var table = ImUtf8.Table(label, 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Borders, new Vector2(tableSize, 6 * ImGui.GetFrameHeight())); - if (!table) - return; - - var headerColor = ImGui.GetColorU32(ImGuiCol.TableHeaderBg); - var checkBoxOffset = (checkBoxWidth - ImGui.GetFrameHeight()) / 2; - ImUtf8.TableSetupColumn("Panel##1"u8, ImGuiTableColumnFlags.WidthFixed, textWidth); - ImUtf8.TableSetupColumn("Show##1"u8, ImGuiTableColumnFlags.WidthFixed, checkBoxWidth); - ImUtf8.TableSetupColumn("Expand##1"u8, ImGuiTableColumnFlags.WidthFixed, checkBoxWidth); - ImUtf8.TableSetupColumn("Panel##2"u8, ImGuiTableColumnFlags.WidthFixed, textWidth); - ImUtf8.TableSetupColumn("Show##2"u8, ImGuiTableColumnFlags.WidthFixed, checkBoxWidth); - ImUtf8.TableSetupColumn("Expand##2"u8, ImGuiTableColumnFlags.WidthFixed, checkBoxWidth); - - ImGui.TableHeadersRow(); - foreach (var panel in Enum.GetValues()) - { - using var id = ImUtf8.PushId((int)panel); - ImGui.TableNextColumn(); - ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, headerColor); - ImUtf8.TextFrameAligned(panel.ToName()); - var isShown = !hidden.HasFlag(panel); - var isExpanded = expanded.HasFlag(panel); - - ImGui.TableNextColumn(); - ImGui.SetCursorPosX(ImGui.GetCursorPosX() + checkBoxOffset); - if (ImUtf8.Checkbox("##show"u8, ref isShown)) - setterHide.Invoke(isShown ? hidden & ~panel : hidden | panel); - ImUtf8.HoverTooltip( - "Show this panel and associated functionality in all relevant tabs.\n\nToggling this off does NOT disable any functionality, just the display of it, so hide panels at your own risk."u8); - - ImGui.TableNextColumn(); - ImGui.SetCursorPosX(ImGui.GetCursorPosX() + checkBoxOffset); - if (ImUtf8.Checkbox("##expand"u8, ref isExpanded)) - setterExpand.Invoke(isExpanded ? expanded | panel : expanded & ~panel); - ImUtf8.HoverTooltip("Expand this panel by default in all relevant tabs."u8); - } - } -} diff --git a/Glamourer/Designs/ApplicationCollection.cs b/Glamourer/Designs/ApplicationCollection.cs index c03d4b4..0fd18f0 100644 --- a/Glamourer/Designs/ApplicationCollection.cs +++ b/Glamourer/Designs/ApplicationCollection.cs @@ -1,6 +1,5 @@ -using Glamourer.Api.Enums; using Glamourer.GameData; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Penumbra.GameData.Enums; namespace Glamourer.Designs; @@ -19,13 +18,13 @@ public record struct ApplicationCollection( public static readonly ApplicationCollection None = new(0, 0, CustomizeFlag.BodyType, 0, 0, 0); public static readonly ApplicationCollection Equipment = new(EquipFlagExtensions.All, BonusExtensions.All, - CustomizeFlag.BodyType, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState | MetaFlag.EarState); + CustomizeFlag.BodyType, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState); public static readonly ApplicationCollection Customizations = new(0, 0, CustomizeFlagExtensions.AllRelevant, 0, CustomizeParameterExtensions.All, MetaFlag.Wetness); public static readonly ApplicationCollection Default = new(EquipFlagExtensions.All, BonusExtensions.All, - CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState); + CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState); public static ApplicationCollection FromKeys() => (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch @@ -47,7 +46,7 @@ public record struct ApplicationCollection( Equip = 0; BonusItem = 0; Crest = 0; - Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState); + Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState); } public void RemoveCustomize() diff --git a/Glamourer/Designs/ApplicationRules.cs b/Glamourer/Designs/ApplicationRules.cs index 281a940..3c5fed2 100644 --- a/Glamourer/Designs/ApplicationRules.cs +++ b/Glamourer/Designs/ApplicationRules.cs @@ -1,7 +1,6 @@ -using Glamourer.Api.Enums; -using Glamourer.GameData; +using Glamourer.GameData; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Penumbra.GameData.Enums; namespace Glamourer.Designs; @@ -19,6 +18,9 @@ public readonly struct ApplicationRules(ApplicationCollection application, bool public static ApplicationRules AllButParameters(ActorState state) => new(ApplicationCollection.All with { Parameters = ComputeParameters(state.ModelData, state.BaseData, All.Parameters) }, true); + public static ApplicationRules AllWithConfig(Configuration config) + => new(ApplicationCollection.All with { Parameters = config.UseAdvancedParameters ? All.Parameters : 0 }, config.UseAdvancedDyes); + public static ApplicationRules NpcFromModifiers(bool ctrl, bool shift) { var equip = ctrl || !shift ? EquipFlagExtensions.All : 0; diff --git a/Glamourer/Designs/Design.cs b/Glamourer/Designs/Design.cs index 848e7d6..35ee3aa 100644 --- a/Glamourer/Designs/Design.cs +++ b/Glamourer/Designs/Design.cs @@ -100,7 +100,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn public new JObject JsonSerialize() { - var ret = new JObject + var ret = new JObject() { ["FileVersion"] = FileVersion, ["Identifier"] = Identifier, @@ -131,17 +131,12 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn var ret = new JArray(); foreach (var (mod, settings) in AssociatedMods) { - var obj = new JObject + var obj = new JObject() { ["Name"] = mod.Name, ["Directory"] = mod.DirectoryName, + ["Enabled"] = settings.Enabled, }; - if (settings.Remove) - obj["Remove"] = true; - else if (settings.ForceInherit) - obj["Inherit"] = true; - else - obj["Enabled"] = settings.Enabled; if (settings.Enabled) { obj["Priority"] = settings.Priority; diff --git a/Glamourer/Designs/DesignBase.cs b/Glamourer/Designs/DesignBase.cs index f87d75a..30d4ddd 100644 --- a/Glamourer/Designs/DesignBase.cs +++ b/Glamourer/Designs/DesignBase.cs @@ -40,8 +40,7 @@ public class DesignBase } /// Used when importing .cma or .chara files. - internal DesignBase(CustomizeService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags, - BonusItemFlag bonusFlags) + internal DesignBase(CustomizeService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags, BonusItemFlag bonusFlags) { _designData = designData; ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant; @@ -196,9 +195,6 @@ public class DesignBase return true; } - public IEnumerable FilteredItemNames - => _designData.FilteredItemNames(Application.Equip, Application.BonusItem); - internal FlagRestrictionResetter TemporarilyRestrictApplication(ApplicationCollection restrictions) => new(this, restrictions); @@ -255,10 +251,9 @@ public class DesignBase ret[slot.ToString()] = Serialize(item.Id, stains, crest, DoApplyEquip(slot), DoApplyStain(slot), DoApplyCrest(crestSlot)); } - ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply"); - ret["VieraEars"] = new QuadBool(_designData.AreEarsVisible(), DoApplyMeta(MetaIndex.EarState)).ToJObject("Show", "Apply"); - ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply"); - ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).ToJObject("Show", "Apply"); + ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply"); + ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply"); + ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).ToJObject("Show", "Apply"); } else { @@ -605,10 +600,6 @@ public class DesignBase metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse); design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled); design._designData.SetVisor(metaValue.ForcedValue); - - metaValue = QuadBool.FromJObject(equip["VieraEars"], "Show", "Apply", QuadBool.NullTrue); - design.SetApplyMeta(MetaIndex.EarState, metaValue.Enabled); - design._designData.SetEarsVisible(metaValue.ForcedValue); return; void PrintWarning(string msg) diff --git a/Glamourer/Designs/DesignBase64Migration.cs b/Glamourer/Designs/DesignBase64Migration.cs index 8cd137f..a60c527 100644 --- a/Glamourer/Designs/DesignBase64Migration.cs +++ b/Glamourer/Designs/DesignBase64Migration.cs @@ -1,7 +1,5 @@ -using Glamourer.Api.Enums; -using Glamourer.Services; +using Glamourer.Services; using OtterGui; -using OtterGui.Extensions; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; diff --git a/Glamourer/Designs/DesignColors.cs b/Glamourer/Designs/DesignColors.cs index a8f3178..96592bf 100644 --- a/Glamourer/Designs/DesignColors.cs +++ b/Glamourer/Designs/DesignColors.cs @@ -3,12 +3,11 @@ using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.Utility.Raii; using Glamourer.Gui; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; namespace Glamourer.Designs; diff --git a/Glamourer/Designs/DesignData.cs b/Glamourer/Designs/DesignData.cs index c7ca8e5..4205996 100644 --- a/Glamourer/Designs/DesignData.cs +++ b/Glamourer/Designs/DesignData.cs @@ -46,9 +46,20 @@ public unsafe struct DesignData public DesignData() { } - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] public readonly bool ContainsName(LowerString name) - => ItemNames.Any(name.IsContained); + => name.IsContained(_nameHead) + || name.IsContained(_nameBody) + || name.IsContained(_nameHands) + || name.IsContained(_nameLegs) + || name.IsContained(_nameFeet) + || name.IsContained(_nameEars) + || name.IsContained(_nameNeck) + || name.IsContained(_nameWrists) + || name.IsContained(_nameRFinger) + || name.IsContained(_nameLFinger) + || name.IsContained(_nameMainhand) + || name.IsContained(_nameOffhand) + || name.IsContained(_nameGlasses); public readonly StainIds Stain(EquipSlot slot) { @@ -65,57 +76,6 @@ public unsafe struct DesignData public readonly bool Crest(CrestFlag slot) => CrestVisibility.HasFlag(slot); - public readonly IEnumerable ItemNames - { - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - get - { - yield return _nameHead; - yield return _nameBody; - yield return _nameHands; - yield return _nameLegs; - yield return _nameFeet; - yield return _nameEars; - yield return _nameNeck; - yield return _nameWrists; - yield return _nameRFinger; - yield return _nameLFinger; - yield return _nameMainhand; - yield return _nameOffhand; - yield return _nameGlasses; - } - } - - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - public readonly IEnumerable FilteredItemNames(EquipFlag item, BonusItemFlag bonusItem) - { - if (item.HasFlag(EquipFlag.Head)) - yield return _nameHead; - if (item.HasFlag(EquipFlag.Body)) - yield return _nameBody; - if (item.HasFlag(EquipFlag.Hands)) - yield return _nameHands; - if (item.HasFlag(EquipFlag.Legs)) - yield return _nameLegs; - if (item.HasFlag(EquipFlag.Feet)) - yield return _nameFeet; - if (item.HasFlag(EquipFlag.Ears)) - yield return _nameEars; - if (item.HasFlag(EquipFlag.Neck)) - yield return _nameNeck; - if (item.HasFlag(EquipFlag.Wrist)) - yield return _nameWrists; - if (item.HasFlag(EquipFlag.RFinger)) - yield return _nameRFinger; - if (item.HasFlag(EquipFlag.LFinger)) - yield return _nameLFinger; - if (item.HasFlag(EquipFlag.Mainhand)) - yield return _nameMainhand; - if (item.HasFlag(EquipFlag.Offhand)) - yield return _nameOffhand; - if (bonusItem.HasFlag(BonusItemFlag.Glasses)) - yield return _nameGlasses; - } public readonly FullEquipType MainhandType => _typeMainhand; @@ -287,7 +247,6 @@ public unsafe struct DesignData MetaIndex.HatState => IsHatVisible(), MetaIndex.VisorState => IsVisorToggled(), MetaIndex.WeaponState => IsWeaponVisible(), - MetaIndex.EarState => AreEarsVisible(), _ => false, }; @@ -298,7 +257,6 @@ public unsafe struct DesignData MetaIndex.HatState => SetHatVisible(value), MetaIndex.VisorState => SetVisor(value), MetaIndex.WeaponState => SetWeaponVisible(value), - MetaIndex.EarState => SetEarsVisible(value), _ => false, }; @@ -342,9 +300,6 @@ public unsafe struct DesignData public readonly bool IsWeaponVisible() => (_states & 0x08) == 0x08; - public readonly bool AreEarsVisible() - => (_states & 0x10) == 0x00; - public bool SetWeaponVisible(bool value) { if (value == IsWeaponVisible()) @@ -354,15 +309,6 @@ public unsafe struct DesignData return true; } - public bool SetEarsVisible(bool value) - { - if (value == AreEarsVisible()) - return false; - - _states = (byte)(value ? _states & ~0x10 : _states | 0x10); - return true; - } - public void SetDefaultEquipment(ItemManager items) { foreach (var slot in EquipSlotExtensions.EqdpSlots) @@ -400,7 +346,6 @@ public unsafe struct DesignData SetHatVisible(true); SetWeaponVisible(true); - SetEarsVisible(true); SetVisor(false); fixed (uint* ptr = _itemIds) { diff --git a/Glamourer/Designs/DesignFileSystem.cs b/Glamourer/Designs/DesignFileSystem.cs index fd47793..e985e32 100644 --- a/Glamourer/Designs/DesignFileSystem.cs +++ b/Glamourer/Designs/DesignFileSystem.cs @@ -41,11 +41,11 @@ public sealed class DesignFileSystem : FileSystem, IDisposable, ISavable public struct CreationDate : ISortMode { - public ReadOnlySpan Name - => "Creation Date (Older First)"u8; + public string Name + => "Creation Date (Older First)"; - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8; + public string Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."; public IEnumerable GetChildren(Folder f) => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderBy(l => l.Value.CreationDate)); @@ -53,11 +53,11 @@ public sealed class DesignFileSystem : FileSystem, IDisposable, ISavable public struct UpdateDate : ISortMode { - public ReadOnlySpan Name - => "Update Date (Older First)"u8; + public string Name + => "Update Date (Older First)"; - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."u8; + public string Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their last update date."; public IEnumerable GetChildren(Folder f) => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderBy(l => l.Value.LastEdit)); @@ -65,11 +65,11 @@ public sealed class DesignFileSystem : FileSystem, IDisposable, ISavable public struct InverseCreationDate : ISortMode { - public ReadOnlySpan Name - => "Creation Date (Newer First)"u8; + public string Name + => "Creation Date (Newer First)"; - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."u8; + public string Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date."; public IEnumerable GetChildren(Folder f) => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderByDescending(l => l.Value.CreationDate)); @@ -77,11 +77,11 @@ public sealed class DesignFileSystem : FileSystem, IDisposable, ISavable public struct InverseUpdateDate : ISortMode { - public ReadOnlySpan Name - => "Update Date (Newer First)"u8; + public string Name + => "Update Date (Newer First)"; - public ReadOnlySpan Description - => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."u8; + public string Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse last update date."; public IEnumerable GetChildren(Folder f) => f.GetSubFolders().Cast().Concat(f.GetLeaves().OrderByDescending(l => l.Value.LastEdit)); @@ -114,14 +114,14 @@ public sealed class DesignFileSystem : FileSystem, IDisposable, ISavable return; case DesignChanged.Type.Deleted: - if (TryGetValue(design, out var leaf1)) + if (FindLeaf(design, out var leaf1)) Delete(leaf1); return; case DesignChanged.Type.ReloadedAll: Reload(); return; case DesignChanged.Type.Renamed when (data as RenameTransaction?)?.Old is { } oldName: - if (!TryGetValue(design, out var leaf2)) + if (!FindLeaf(design, out var leaf2)) return; var old = oldName.FixName(); @@ -150,6 +150,15 @@ public sealed class DesignFileSystem : FileSystem, IDisposable, ISavable ? (string.Empty, false) : (DesignToIdentifier(design), true); + // Search the entire filesystem for the leaf corresponding to a design. + public bool FindLeaf(Design design, [NotNullWhen(true)] out Leaf? leaf) + { + leaf = Root.GetAllDescendants(ISortMode.Lexicographical) + .OfType() + .FirstOrDefault(l => l.Value == design); + return leaf != null; + } + internal static void MigrateOldPaths(SaveService saveService, Dictionary oldPaths) { if (oldPaths.Count == 0) diff --git a/Glamourer/Designs/DesignManager.cs b/Glamourer/Designs/DesignManager.cs index 92f8398..f931489 100644 --- a/Glamourer/Designs/DesignManager.cs +++ b/Glamourer/Designs/DesignManager.cs @@ -3,16 +3,14 @@ using Glamourer.Designs.History; using Glamourer.Designs.Links; using Glamourer.Events; using Glamourer.GameData; -using Glamourer.Interop.Material; using Glamourer.Interop.Penumbra; using Glamourer.Services; -using OtterGui.Extensions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OtterGui; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; - namespace Glamourer.Designs; public sealed class DesignManager : DesignEditor @@ -111,7 +109,6 @@ public sealed class DesignManager : DesignEditor QuickDesign = Config.DefaultDesignSettings.ShowQuickDesignBar, ResetTemporarySettings = Config.DefaultDesignSettings.ResetTemporarySettings, }; - design.SetWriteProtected(Config.DefaultDesignSettings.Locked); Designs.Add(design); Glamourer.Log.Debug($"Added new design {design.Identifier}."); SaveService.ImmediateSave(design); @@ -136,7 +133,6 @@ public sealed class DesignManager : DesignEditor ResetTemporarySettings = Config.DefaultDesignSettings.ResetTemporarySettings, }; - design.SetWriteProtected(Config.DefaultDesignSettings.Locked); Designs.Add(design); Glamourer.Log.Debug($"Added new design {design.Identifier} by cloning Temporary Design."); SaveService.ImmediateSave(design); @@ -156,7 +152,6 @@ public sealed class DesignManager : DesignEditor Name = actualName, Index = Designs.Count, }; - design.SetWriteProtected(Config.DefaultDesignSettings.Locked); Designs.Add(design); Glamourer.Log.Debug( $"Added new design {design.Identifier} by cloning {clone.Identifier.ToString()}."); @@ -229,7 +224,7 @@ public sealed class DesignManager : DesignEditor design.Tags = design.Tags.Append(tag).OrderBy(t => t).ToArray(); design.LastEdit = DateTimeOffset.UtcNow; - var idx = design.Tags.AsEnumerable().IndexOf(tag); + var idx = design.Tags.IndexOf(tag); SaveService.QueueSave(design); Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}."); DesignChanged.Invoke(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx)); @@ -262,7 +257,7 @@ public sealed class DesignManager : DesignEditor SaveService.QueueSave(design); Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags."); DesignChanged.Invoke(DesignChanged.Type.ChangedTag, design, - new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag))); + new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.IndexOf(newTag))); } /// Add an associated mod to a design. @@ -453,39 +448,6 @@ public sealed class DesignManager : DesignEditor DesignChanged.Invoke(DesignChanged.Type.ApplyParameter, design, new ApplicationTransaction(flag, !value, value)); } - /// Change multiple application values at once. - public void ChangeApplyMulti(Design design, bool? equipment, bool? customization, bool? bonus, bool? parameters, bool? meta, bool? stains, - bool? materials, bool? crest) - { - if (equipment is { } e) - foreach (var f in EquipSlotExtensions.FullSlots) - ChangeApplyItem(design, f, e); - if (stains is { } s) - foreach (var f in EquipSlotExtensions.FullSlots) - ChangeApplyStains(design, f, s); - if (customization is { } c) - foreach (var f in CustomizationExtensions.All.Where(design.CustomizeSet.IsAvailable).Prepend(CustomizeIndex.Clan) - .Prepend(CustomizeIndex.Gender)) - ChangeApplyCustomize(design, f, c); - if (bonus is { } b) - foreach (var f in BonusExtensions.AllFlags) - ChangeApplyBonusItem(design, f, b); - if (meta is { } m) - foreach (var f in MetaExtensions.AllRelevant) - ChangeApplyMeta(design, f, m); - if (crest is { } cr) - foreach (var f in CrestExtensions.AllRelevantSet) - ChangeApplyCrest(design, f, cr); - - if (parameters is { } p) - foreach (var f in CustomizeParameterExtensions.AllFlags) - ChangeApplyParameter(design, f, p); - - if (materials is { } ma) - foreach (var (key, _) in design.GetMaterialData().ToArray()) - ChangeApplyMaterialValue(design, MaterialValueIndex.FromKey(key), ma); - } - #endregion public void UndoDesignChange(Design design) @@ -557,7 +519,7 @@ public sealed class DesignManager : DesignEditor try { File.Move(SaveService.FileNames.MigrationDesignFile, - Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak"), true); + Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak")); Glamourer.Log.Information($"Moved migrated design file {SaveService.FileNames.MigrationDesignFile} to backup file."); } catch (Exception ex) diff --git a/Glamourer/Designs/History/EditorHistory.cs b/Glamourer/Designs/History/EditorHistory.cs index caec151..6382b94 100644 --- a/Glamourer/Designs/History/EditorHistory.cs +++ b/Glamourer/Designs/History/EditorHistory.cs @@ -1,8 +1,8 @@ using Glamourer.Api.Enums; using Glamourer.Events; +using Glamourer.Interop.Structs; using Glamourer.State; using OtterGui.Services; -using Penumbra.GameData.Interop; namespace Glamourer.Designs.History; @@ -152,7 +152,7 @@ public class EditorHistory : IDisposable, IService { if (!_stateEntries.TryGetValue(state, out var list)) { - list = []; + list = new Queue(); _stateEntries.Add(state, list); } @@ -163,7 +163,7 @@ public class EditorHistory : IDisposable, IService { if (!_designEntries.TryGetValue(design, out var list)) { - list = []; + list = new Queue(); _designEntries.Add(design, list); } diff --git a/Glamourer/Designs/IDesignEditor.cs b/Glamourer/Designs/IDesignEditor.cs index c18c98b..935263b 100644 --- a/Glamourer/Designs/IDesignEditor.cs +++ b/Glamourer/Designs/IDesignEditor.cs @@ -13,8 +13,7 @@ public readonly record struct ApplySettings( bool FromJobChange = false, bool UseSingleSource = false, bool MergeLinks = false, - bool ResetMaterials = false, - bool IsFinal = false) + bool ResetMaterials = false) { public static readonly ApplySettings Manual = new() { @@ -25,7 +24,6 @@ public readonly record struct ApplySettings( UseSingleSource = false, MergeLinks = false, ResetMaterials = false, - IsFinal = false, }; public static readonly ApplySettings ManualWithLinks = new() @@ -37,7 +35,6 @@ public readonly record struct ApplySettings( UseSingleSource = false, MergeLinks = true, ResetMaterials = false, - IsFinal = false, }; public static readonly ApplySettings Game = new() @@ -49,7 +46,6 @@ public readonly record struct ApplySettings( UseSingleSource = false, MergeLinks = false, ResetMaterials = true, - IsFinal = false, }; } diff --git a/Glamourer/Designs/Links/DesignLinkLoader.cs b/Glamourer/Designs/Links/DesignLinkLoader.cs index 24138a8..fc7a26d 100644 --- a/Glamourer/Designs/Links/DesignLinkLoader.cs +++ b/Glamourer/Designs/Links/DesignLinkLoader.cs @@ -1,6 +1,6 @@ using Dalamud.Interface.ImGuiNotification; +using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Services; using Notification = OtterGui.Classes.Notification; diff --git a/Glamourer/Designs/Links/DesignMerger.cs b/Glamourer/Designs/Links/DesignMerger.cs index 847d5f1..0284322 100644 --- a/Glamourer/Designs/Links/DesignMerger.cs +++ b/Glamourer/Designs/Links/DesignMerger.cs @@ -1,5 +1,4 @@ -using Glamourer.Api.Enums; -using Glamourer.Automation; +using Glamourer.Automation; using Glamourer.Designs.Special; using Glamourer.GameData; using Glamourer.Interop.Material; diff --git a/Glamourer/Designs/MetaIndex.cs b/Glamourer/Designs/MetaIndex.cs index 1842ae3..edbf7b6 100644 --- a/Glamourer/Designs/MetaIndex.cs +++ b/Glamourer/Designs/MetaIndex.cs @@ -1,5 +1,4 @@ -using Glamourer.Api.Enums; -using Glamourer.State; +using Glamourer.State; namespace Glamourer.Designs; @@ -10,15 +9,23 @@ public enum MetaIndex VisorState = StateIndex.MetaVisorState, WeaponState = StateIndex.MetaWeaponState, ModelId = StateIndex.MetaModelId, - EarState = StateIndex.MetaEarState, +} + +[Flags] +public enum MetaFlag : byte +{ + Wetness = 0x01, + HatState = 0x02, + VisorState = 0x04, + WeaponState = 0x08, } public static class MetaExtensions { public static readonly IReadOnlyList AllRelevant = - [MetaIndex.Wetness, MetaIndex.HatState, MetaIndex.VisorState, MetaIndex.WeaponState, MetaIndex.EarState]; + [MetaIndex.Wetness, MetaIndex.HatState, MetaIndex.VisorState, MetaIndex.WeaponState]; - public const MetaFlag All = MetaFlag.Wetness | MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState | MetaFlag.EarState; + public const MetaFlag All = MetaFlag.Wetness | MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState; public static MetaFlag ToFlag(this MetaIndex index) => index switch @@ -27,7 +34,6 @@ public static class MetaExtensions MetaIndex.HatState => MetaFlag.HatState, MetaIndex.VisorState => MetaFlag.VisorState, MetaIndex.WeaponState => MetaFlag.WeaponState, - MetaIndex.EarState => MetaFlag.EarState, _ => (MetaFlag)byte.MaxValue, }; @@ -38,24 +44,9 @@ public static class MetaExtensions MetaFlag.HatState => MetaIndex.HatState, MetaFlag.VisorState => MetaIndex.VisorState, MetaFlag.WeaponState => MetaIndex.WeaponState, - MetaFlag.EarState => MetaIndex.EarState, _ => (MetaIndex)byte.MaxValue, }; - public static IEnumerable ToIndices(this MetaFlag index) - { - if (index.HasFlag(MetaFlag.Wetness)) - yield return MetaIndex.Wetness; - if (index.HasFlag(MetaFlag.HatState)) - yield return MetaIndex.HatState; - if (index.HasFlag(MetaFlag.VisorState)) - yield return MetaIndex.VisorState; - if (index.HasFlag(MetaFlag.WeaponState)) - yield return MetaIndex.WeaponState; - if (index.HasFlag(MetaFlag.EarState)) - yield return MetaIndex.EarState; - } - public static string ToName(this MetaIndex index) => index switch { @@ -63,7 +54,6 @@ public static class MetaExtensions MetaIndex.VisorState => "Visor Toggled", MetaIndex.WeaponState => "Weapon Visible", MetaIndex.Wetness => "Force Wetness", - MetaIndex.EarState => "Ears Visible", _ => "Unknown Meta", }; @@ -74,7 +64,6 @@ public static class MetaExtensions MetaIndex.VisorState => "Toggle the visor state of the characters head gear.", MetaIndex.WeaponState => "Hide or show the characters weapons when not drawn.", MetaIndex.Wetness => "Force the character to be wet or not.", - MetaIndex.EarState => "Hide or show the characters ears through the head gear. (Viera only)", _ => string.Empty, }; } diff --git a/Glamourer/Designs/Special/RandomDesignGenerator.cs b/Glamourer/Designs/Special/RandomDesignGenerator.cs index b1e1e7c..7ed4452 100644 --- a/Glamourer/Designs/Special/RandomDesignGenerator.cs +++ b/Glamourer/Designs/Special/RandomDesignGenerator.cs @@ -1,33 +1,19 @@ -using OtterGui; -using OtterGui.Services; +using OtterGui.Services; namespace Glamourer.Designs.Special; -public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem, Configuration config) : IService +public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem) : IService { - private readonly Random _rng = new(); - private readonly WeakReference _lastDesign = new(null!, false); + private readonly Random _rng = new(); public Design? Design(IReadOnlyList localDesigns) { - if (localDesigns.Count is 0) + if (localDesigns.Count == 0) return null; var idx = _rng.Next(0, localDesigns.Count); - if (localDesigns.Count is 1) - { - _lastDesign.SetTarget(localDesigns[idx]); - return localDesigns[idx]; - } - - if (config.PreventRandomRepeats && _lastDesign.TryGetTarget(out var lastDesign)) - while (lastDesign == localDesigns[idx]) - idx = _rng.Next(0, localDesigns.Count); - - var design = localDesigns[idx]; - Glamourer.Log.Verbose($"[Random Design] Chose design {idx + 1} out of {localDesigns.Count}: {design.Incognito}."); - _lastDesign.SetTarget(design); - return design; + Glamourer.Log.Verbose($"[Random Design] Chose design {idx + 1} out of {localDesigns.Count}: {localDesigns[idx].Incognito}."); + return localDesigns[idx]; } public Design? Design() @@ -38,12 +24,12 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS public Design? Design(IReadOnlyList predicates) { - return predicates.Count switch - { - 0 => Design(), - 1 => Design(predicates[0]), - _ => Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()), - }; + if (predicates.Count == 0) + return Design(); + if (predicates.Count == 1) + return Design(predicates[0]); + + return Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()); } public Design? Design(string restrictions) diff --git a/Glamourer/Designs/Special/RandomPredicate.cs b/Glamourer/Designs/Special/RandomPredicate.cs index ae05f8f..efb3233 100644 --- a/Glamourer/Designs/Special/RandomPredicate.cs +++ b/Glamourer/Designs/Special/RandomPredicate.cs @@ -22,7 +22,7 @@ public interface IDesignPredicate : designs; private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d, DesignFileSystem fs) - => (d, d.Name.Lower, d.Identifier.ToString(), fs.TryGetValue(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty); + => (d, d.Name.Lower, d.Identifier.ToString(), fs.FindLeaf(d, out var l) ? l.FullName().ToLowerInvariant() : string.Empty); } public static class RandomPredicate diff --git a/Glamourer/EphemeralConfig.cs b/Glamourer/EphemeralConfig.cs index 98dabec..3e13dc4 100644 --- a/Glamourer/EphemeralConfig.cs +++ b/Glamourer/EphemeralConfig.cs @@ -20,10 +20,6 @@ public class EphemeralConfig : ISavable public Guid SelectedQuickDesign { get; set; } = Guid.Empty; public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion; - public float CurrentDesignSelectorWidth { get; set; } = 200f; - public float DesignSelectorMinimumScale { get; set; } = 0.1f; - public float DesignSelectorMaximumScale { get; set; } = 0.5f; - [JsonIgnore] private readonly SaveService _saveService; diff --git a/Glamourer/Events/AutoRedrawChanged.cs b/Glamourer/Events/AutoRedrawChanged.cs deleted file mode 100644 index a8dd03a..0000000 --- a/Glamourer/Events/AutoRedrawChanged.cs +++ /dev/null @@ -1,16 +0,0 @@ -using OtterGui.Classes; - -namespace Glamourer.Events; - -/// -/// Triggered when the auto-reload gear setting is changed in glamourer configuration. -/// -public sealed class AutoRedrawChanged() - : EventWrapper(nameof(AutoRedrawChanged)) -{ - public enum Priority - { - /// - StateApi = int.MinValue, - } -} \ No newline at end of file diff --git a/Glamourer/Events/AutomationChanged.cs b/Glamourer/Events/AutomationChanged.cs index c368899..26f799a 100644 --- a/Glamourer/Events/AutomationChanged.cs +++ b/Glamourer/Events/AutomationChanged.cs @@ -37,9 +37,6 @@ public sealed class AutomationChanged() /// Change the used base state of a given set. Additional data is prior and new base. [(AutoDesignSet.Base, AutoDesignSet.Base)]. ChangedBase, - /// Change the resetting of temporary settings for a given set. Additional data is the new value. - ChangedTemporarySettingsReset, - /// Add a new associated design to a given set. Additional data is the index it got added at [int]. AddedDesign, diff --git a/Glamourer/Events/GPoseService.cs b/Glamourer/Events/GPoseService.cs index 44421a0..a84f1d6 100644 --- a/Glamourer/Events/GPoseService.cs +++ b/Glamourer/Events/GPoseService.cs @@ -13,8 +13,8 @@ public sealed class GPoseService : EventWrapper public enum Priority { - /// - StateApi = int.MinValue, + /// + GlamourerIpc = int.MinValue, } public bool InGPose { get; private set; } diff --git a/Glamourer/Events/GearsetDataLoaded.cs b/Glamourer/Events/GearsetDataLoaded.cs deleted file mode 100644 index 620bdab..0000000 --- a/Glamourer/Events/GearsetDataLoaded.cs +++ /dev/null @@ -1,21 +0,0 @@ -using OtterGui.Classes; -using Penumbra.GameData.Interop; - -namespace Glamourer.Events; - -/// -/// Triggers when the equipped gearset finished all LoadEquipment, LoadWeapon, and LoadCrest calls. (All Non-MetaData) -/// This defines an endpoint for when the gameState is updated. -/// -/// The model draw object associated with the finished load (Also fired by other players on render) -/// -/// -public sealed class GearsetDataLoaded() - : EventWrapper(nameof(GearsetDataLoaded)) -{ - public enum Priority - { - /// - StateListener = 0, - } -} diff --git a/Glamourer/Events/PenumbraReloaded.cs b/Glamourer/Events/PenumbraReloaded.cs index 0975670..20b58f9 100644 --- a/Glamourer/Events/PenumbraReloaded.cs +++ b/Glamourer/Events/PenumbraReloaded.cs @@ -15,8 +15,5 @@ public sealed class PenumbraReloaded() /// VisorService = 0, - - /// - VieraEarService = 0, } } diff --git a/Glamourer/Events/StateChanged.cs b/Glamourer/Events/StateChanged.cs index 2bcc6fe..c704195 100644 --- a/Glamourer/Events/StateChanged.cs +++ b/Glamourer/Events/StateChanged.cs @@ -3,7 +3,6 @@ using Glamourer.Designs.History; using Glamourer.Interop.Structs; using Glamourer.State; using OtterGui.Classes; -using Penumbra.GameData.Interop; namespace Glamourer.Events; diff --git a/Glamourer/Events/StateFinalized.cs b/Glamourer/Events/StateFinalized.cs deleted file mode 100644 index 0ccaa8b..0000000 --- a/Glamourer/Events/StateFinalized.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Glamourer.Api; -using Glamourer.Api.Enums; -using Glamourer.Interop.Structs; -using OtterGui.Classes; -using Penumbra.GameData.Interop; - -namespace Glamourer.Events; - -/// -/// Triggered when a set of grouped changes finishes being applied to a Glamourer state. -/// -/// Parameter is the operation that finished updating the saved state. -/// Parameter is the existing actors using this saved state. -/// -/// -public sealed class StateFinalized() - : EventWrapper(nameof(StateFinalized)) -{ - public enum Priority - { - /// - StateApi = int.MinValue, - } -} diff --git a/Glamourer/Events/VieraEarStateChanged.cs b/Glamourer/Events/VieraEarStateChanged.cs deleted file mode 100644 index 65730b8..0000000 --- a/Glamourer/Events/VieraEarStateChanged.cs +++ /dev/null @@ -1,22 +0,0 @@ -using OtterGui.Classes; -using Penumbra.GameData.Interop; - -namespace Glamourer.Events; - -/// -/// Triggered when the state of viera ear visibility for any draw object is changed. -/// -/// Parameter is the model with a changed viera ear visibility state. -/// Parameter is the new state. -/// Parameter is whether to call the original function. -/// -/// -public sealed class VieraEarStateChanged() - : EventWrapperRef2(nameof(VieraEarStateChanged)) -{ - public enum Priority - { - /// - StateListener = 0, - } -} diff --git a/Glamourer/Events/VisorStateChanged.cs b/Glamourer/Events/VisorStateChanged.cs index 03b7336..d2d3a6c 100644 --- a/Glamourer/Events/VisorStateChanged.cs +++ b/Glamourer/Events/VisorStateChanged.cs @@ -19,4 +19,4 @@ public sealed class VisorStateChanged() /// StateListener = 0, } -} \ No newline at end of file +} diff --git a/Glamourer/GameData/CustomizeSet.cs b/Glamourer/GameData/CustomizeSet.cs index 8795c19..178ef07 100644 --- a/Glamourer/GameData/CustomizeSet.cs +++ b/Glamourer/GameData/CustomizeSet.cs @@ -1,5 +1,4 @@ using OtterGui; -using OtterGui.Extensions; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Race = Penumbra.GameData.Enums.Race; @@ -12,7 +11,7 @@ namespace Glamourer.GameData; /// public class CustomizeSet { - private readonly NpcCustomizeSet _npcCustomizations; + private NpcCustomizeSet _npcCustomizations; internal CustomizeSet(NpcCustomizeSet npcCustomizations, SubRace clan, Gender gender) { @@ -89,7 +88,7 @@ public class CustomizeSet { if (IsAvailable(index)) return DataByValue(index, value, out custom, face) >= 0 - || _npcCustomizations.CheckValue(index, value) + || _npcCustomizations.CheckColor(index, value) || NpcOptions.Any(t => t.Type == index && t.Value == value); custom = null; diff --git a/Glamourer/GameData/CustomizeSetFactory.cs b/Glamourer/GameData/CustomizeSetFactory.cs index 77a6973..13a9865 100644 --- a/Glamourer/GameData/CustomizeSetFactory.cs +++ b/Glamourer/GameData/CustomizeSetFactory.cs @@ -76,6 +76,7 @@ internal class CustomizeSetFactory( CustomizeIndex.Hairstyle, CustomizeIndex.LipColor, CustomizeIndex.SkinColor, + CustomizeIndex.FacePaint, CustomizeIndex.TailShape, }; diff --git a/Glamourer/GameData/NpcCustomizeSet.cs b/Glamourer/GameData/NpcCustomizeSet.cs index 725f80f..4dbfd83 100644 --- a/Glamourer/GameData/NpcCustomizeSet.cs +++ b/Glamourer/GameData/NpcCustomizeSet.cs @@ -3,7 +3,6 @@ using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Lumina.Excel.Sheets; using OtterGui.Services; -using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -41,19 +40,17 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList private readonly BitArray _eyeColors = new(256); private readonly BitArray _facepaintColors = new(256); private readonly BitArray _tattooColors = new(256); - private readonly BitArray _facepaints = new(128); - public bool CheckValue(CustomizeIndex type, CustomizeValue value) + public bool CheckColor(CustomizeIndex type, CustomizeValue value) => type switch { - CustomizeIndex.HairColor => _hairColors[value.Value], - CustomizeIndex.HighlightsColor => _hairColors[value.Value], - CustomizeIndex.EyeColorLeft => _eyeColors[value.Value], - CustomizeIndex.EyeColorRight => _eyeColors[value.Value], - CustomizeIndex.FacePaintColor => _facepaintColors[value.Value], - CustomizeIndex.TattooColor => _tattooColors[value.Value], - CustomizeIndex.FacePaint when value.Value < 128 => _facepaints[value.Value], - _ => false, + CustomizeIndex.HairColor => _hairColors[value.Value], + CustomizeIndex.HighlightsColor => _hairColors[value.Value], + CustomizeIndex.EyeColorLeft => _eyeColors[value.Value], + CustomizeIndex.EyeColorRight => _eyeColors[value.Value], + CustomizeIndex.FacePaintColor => _facepaintColors[value.Value], + CustomizeIndex.TattooColor => _tattooColors[value.Value], + _ => false, }; /// Create the data when ready. @@ -61,14 +58,13 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList { var waitTask = Task.WhenAll(eNpcs.Awaiter, bNpcs.Awaiter, bNpcNames.Awaiter); Awaiter = waitTask.ContinueWith(_ => - { - var watch = Stopwatch.StartNew(); - var eNpcTask = Task.Run(() => CreateEnpcData(data, eNpcs)); - var bNpcTask = Task.Run(() => CreateBnpcData(data, bNpcs, bNpcNames)); - FilterAndOrderNpcData(eNpcTask.Result, bNpcTask.Result); - Time = watch.ElapsedMilliseconds; - }) - .ContinueWith(_ => CheckFacepaintFiles(data, _facepaints)); + { + var watch = Stopwatch.StartNew(); + var eNpcTask = Task.Run(() => CreateEnpcData(data, eNpcs)); + var bNpcTask = Task.Run(() => CreateBnpcData(data, bNpcs, bNpcNames)); + FilterAndOrderNpcData(eNpcTask.Result, bNpcTask.Result); + Time = watch.ElapsedMilliseconds; + }); } /// Create data from event NPCs. @@ -327,17 +323,6 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList return (true, customize); } - /// Check decal files for existence. - private static void CheckFacepaintFiles(IDataManager data, BitArray facepaints) - { - for (byte i = 0; i < 128; ++i) - { - var path = GamePaths.Tex.FaceDecal(i); - if (data.FileExists(path)) - facepaints[i] = true; - } - } - /// public IEnumerator GetEnumerator() => _data.GetEnumerator(); diff --git a/Glamourer/Glamourer.cs b/Glamourer/Glamourer.cs index 33c67d5..9c4583f 100644 --- a/Glamourer/Glamourer.cs +++ b/Glamourer/Glamourer.cs @@ -6,10 +6,12 @@ using Glamourer.Gui; using Glamourer.Interop; using Glamourer.Services; using Glamourer.State; +using OtterGui; using OtterGui.Classes; using OtterGui.Log; using OtterGui.Services; -using Penumbra.GameData.Interop; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Files; namespace Glamourer; @@ -26,7 +28,6 @@ public class Glamourer : IDalamudPlugin public static readonly Logger Log = new(); public static MessageService Messager { get; private set; } = null!; - public static DynamisIpc Dynamis { get; private set; } = null!; private readonly ServiceManager _services; @@ -36,7 +37,6 @@ public class Glamourer : IDalamudPlugin { _services = StaticServiceManager.CreateProvider(pluginInterface, Log, this); Messager = _services.GetService(); - Dynamis = _services.GetService(); _services.EnsureRequiredServices(); _services.GetService(); @@ -47,6 +47,15 @@ public class Glamourer : IDalamudPlugin _services.GetService(); // initialize commands. _services.GetService(); // initialize IPC. Log.Information($"Glamourer v{Version} loaded successfully."); + + //var text = File.ReadAllBytes(@"C:\FFXIVMods\PBDTest\files\human.pbd"); + //var pbd = new PbdFile(text); + //var roundtrip = pbd.Write(); + //File.WriteAllBytes(@"C:\FFXIVMods\PBDTest\files\Vanilla resaved save.pbd", roundtrip); + //var deformer = pbd.Deformers.FirstOrDefault(d => d.GenderRace is GenderRace.RoegadynFemale)!.RacialDeformer; + //deformer.DeformMatrices["ya_fukubu_phys"] = deformer.DeformMatrices["j_kosi"]; + //var aleks = pbd.Write(); + //File.WriteAllBytes(@"C:\FFXIVMods\PBDTest\files\rue.pbd", aleks); } catch { @@ -69,10 +78,10 @@ public class Glamourer : IDalamudPlugin sb.Append($"> **`Auto-Reload Gear: `** {config.AutoRedrawEquipOnChanges}\n"); sb.Append($"> **`Revert on Zone Change:`** {config.RevertManualChangesOnZoneChange}\n"); sb.Append($"> **`Festival Easter-Eggs: `** {config.DisableFestivals}\n"); + sb.Append($"> **`Advanced Customize: `** {config.UseAdvancedParameters}\n"); + sb.Append($"> **`Advanced Dye: `** {config.UseAdvancedDyes}\n"); sb.Append($"> **`Apply Entire Weapon: `** {config.ChangeEntireItem}\n"); sb.Append($"> **`Apply Associated Mods:`** {config.AlwaysApplyAssociatedMods}\n"); - sb.Append($"> **`Attach to PCP: `** {config.AttachToPcp}\n"); - sb.Append($"> **`Hidden Panels: `** {config.HideDesignPanel}\n"); sb.Append($"> **`Show QDB: `** {config.Ephemeral.ShowDesignQuickBar}\n"); sb.Append($"> **`QDB Hotkey: `** {config.ToggleQuickDesignBar}\n"); sb.Append($"> **`Smaller Equip Display:`** {config.SmallEquip}\n"); @@ -83,7 +92,7 @@ public class Glamourer : IDalamudPlugin var designManager = _services.GetService(); var autoManager = _services.GetService(); var stateManager = _services.GetService(); - var objectManager = _services.GetService(); + var objectManager = _services.GetService(); var currentPlayer = objectManager.PlayerData.Identifier; var states = stateManager.Where(kvp => objectManager.ContainsKey(kvp.Key)).ToList(); diff --git a/Glamourer/Glamourer.csproj b/Glamourer/Glamourer.csproj index 560621d..9a1b95b 100644 --- a/Glamourer/Glamourer.csproj +++ b/Glamourer/Glamourer.csproj @@ -1,32 +1,92 @@ - + + net8.0-windows + preview + x64 Glamourer Glamourer 9.0.0.1 9.0.0.1 + SoftOtter Glamourer - Copyright © 2025 + Copyright © 2023 + true + Library 4 + true + enable bin\$(Configuration)\ + $(MSBuildWarningsAsMessages);MSB3277 + true + false + false + + + + true + full + false + DEBUG;TRACE + + + + pdbonly + true + TRACE + + + + OnOutputUpdated - - - PreserveNewest - + + $(AppData)\XIVLauncher\addon\Hooks\dev\ + + + + + $(DalamudLibPath)Dalamud.dll + False + + + $(DalamudLibPath)FFXIVClientStructs.dll + False + + + $(DalamudLibPath)ImGui.NET.dll + False + + + $(DalamudLibPath)ImGuiScene.dll + False + + + $(DalamudLibPath)Lumina.dll + False + + + $(DalamudLibPath)Lumina.Excel.dll + False + + + $(DalamudLibPath)Newtonsoft.Json.dll + False + + + - + @@ -56,4 +116,14 @@ $(GitCommitHash) + + + + PreserveNewest + + + + + + \ No newline at end of file diff --git a/Glamourer/Glamourer.json b/Glamourer/Glamourer.json index e2dbf8b..1e9edf7 100644 --- a/Glamourer/Glamourer.json +++ b/Glamourer/Glamourer.json @@ -8,7 +8,7 @@ "AssemblyVersion": "9.0.0.1", "RepoUrl": "https://github.com/Ottermandias/Glamourer", "ApplicableVersion": "any", - "DalamudApiLevel": 14, + "DalamudApiLevel": 11, "ImageUrls": null, "IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/master/images/icon.png" } \ No newline at end of file diff --git a/Glamourer/Gui/Colors.cs b/Glamourer/Gui/Colors.cs index b2713eb..b7f9737 100644 --- a/Glamourer/Gui/Colors.cs +++ b/Glamourer/Gui/Colors.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using ImGuiNET; namespace Glamourer.Gui; @@ -29,10 +29,6 @@ public enum ColorId TriStateNeutral, BattleNpc, EventNpc, - ModdedItemMarker, - ContainsItemsEnabled, - ContainsItemsDisabled, - AdvancedDyeActive, } public static class Colors @@ -43,36 +39,32 @@ public static class Colors => color switch { // @formatter:off - ColorId.NormalDesign => (0xFFFFFFFF, "Normal Design", "A design with no specific traits." ), - ColorId.CustomizationDesign => (0xFFC000C0, "Customization Design", "A design that only changes customizations on a character." ), - ColorId.StateDesign => (0xFF00C0C0, "State Design", "A design that does not change equipment or customizations on a character." ), - ColorId.EquipmentDesign => (0xFF00C000, "Equipment Design", "A design that only changes equipment on a character." ), - ColorId.ActorAvailable => (0xFF18C018, "Actor Available", "The header in the Actor tab panel if the currently selected actor exists in the game world at least once." ), - ColorId.ActorUnavailable => (0xFF1818C0, "Actor Unavailable", "The Header in the Actor tab panel if the currently selected actor does not exist in the game world." ), - ColorId.FolderExpanded => (0xFFFFF0C0, "Expanded Design Folder", "A design folder that is currently expanded." ), - ColorId.FolderCollapsed => (0xFFFFF0C0, "Collapsed Design Folder", "A design folder that is currently collapsed." ), - ColorId.FolderLine => (0xFFFFF0C0, "Expanded Design Folder Line", "The line signifying which descendants belong to an expanded design folder." ), - ColorId.EnabledAutoSet => (0xFFA0F0A0, "Enabled Automation Set", "An automation set that is currently enabled. Only one set can be enabled for each identifier at once." ), - ColorId.DisabledAutoSet => (0xFF808080, "Disabled Automation Set", "An automation set that is currently disabled." ), - ColorId.AutomationActorAvailable => (0xFFFFFFFF, "Automation Actor Available", "A character associated with the given automated design set is currently visible." ), - ColorId.AutomationActorUnavailable => (0xFF808080, "Automation Actor Unavailable", "No character associated with the given automated design set is currently visible." ), - ColorId.HeaderButtons => (0xFFFFF0C0, "Header Buttons", "The text and border color of buttons in the header, like the Incognito toggle." ), - ColorId.FavoriteStarOn => (0xFF40D0D0, "Favored Item", "The color of the star for favored items and of the border in the unlock overview tab." ), - ColorId.FavoriteStarHovered => (0xFFD040D0, "Favorite Star Hovered", "The color of the star for favored items when it is hovered." ), - ColorId.FavoriteStarOff => (0x20808080, "Favorite Star Outline", "The color of the star for items that are not favored when it is not hovered." ), - ColorId.QuickDesignButton => (0x900A0A0A, "Quick Design Bar Button Background", "The color of button frames in the quick design bar." ), - ColorId.QuickDesignFrame => (0x90383838, "Quick Design Bar Combo Background", "The color of the combo background in the quick design bar." ), - ColorId.QuickDesignBg => (0x00F0F0F0, "Quick Design Bar Window Background", "The color of the window background in the quick design bar." ), - ColorId.TriStateCheck => (0xFF00D000, "Checkmark in Tri-State Checkboxes", "The color of the checkmark indicating positive change in tri-state checkboxes." ), - ColorId.TriStateCross => (0xFF0000D0, "Cross in Tri-State Checkboxes", "The color of the cross indicating negative change in tri-state checkboxes." ), - ColorId.TriStateNeutral => (0xFFD0D0D0, "Dot in Tri-State Checkboxes", "The color of the dot indicating no change in tri-state checkboxes." ), - ColorId.BattleNpc => (0xFFFFFFFF, "Battle NPC in NPC Tab", "The color of the names of battle NPCs in the NPC tab that do not have a more specific color assigned." ), - ColorId.EventNpc => (0xFFFFFFFF, "Event NPC in NPC Tab", "The color of the names of event NPCs in the NPC tab that do not have a more specific color assigned." ), - ColorId.ModdedItemMarker => (0xFFFF20FF, "Modded Item Marker", "The color of dot in the unlocks overview tab signaling that the item is modded in the currently selected Penumbra collection." ), - ColorId.ContainsItemsEnabled => (0xFFA0F0A0, "Enabled Mod Contains Design Items", "The color of enabled mods in the associated mod dropdown menu when they contain items used in this design." ), - ColorId.ContainsItemsDisabled => (0x80A0F0A0, "Disabled Mod Contains Design Items", "The color of disabled mods in the associated mod dropdown menu when they contain items used in this design." ), - ColorId.AdvancedDyeActive => (0xFF58DDFF, "Advanced Dyes Active", "The highlight color for the advanced dye button and marker if any advanced dyes are active for this slot." ), - _ => (0x00000000, string.Empty, string.Empty ), + ColorId.NormalDesign => (0xFFFFFFFF, "Normal Design", "A design with no specific traits." ), + ColorId.CustomizationDesign => (0xFFC000C0, "Customization Design", "A design that only changes customizations on a character." ), + ColorId.StateDesign => (0xFF00C0C0, "State Design", "A design that does not change equipment or customizations on a character." ), + ColorId.EquipmentDesign => (0xFF00C000, "Equipment Design", "A design that only changes equipment on a character." ), + ColorId.ActorAvailable => (0xFF18C018, "Actor Available", "The header in the Actor tab panel if the currently selected actor exists in the game world at least once." ), + ColorId.ActorUnavailable => (0xFF1818C0, "Actor Unavailable", "The Header in the Actor tab panel if the currently selected actor does not exist in the game world." ), + ColorId.FolderExpanded => (0xFFFFF0C0, "Expanded Design Folder", "A design folder that is currently expanded." ), + ColorId.FolderCollapsed => (0xFFFFF0C0, "Collapsed Design Folder", "A design folder that is currently collapsed." ), + ColorId.FolderLine => (0xFFFFF0C0, "Expanded Design Folder Line", "The line signifying which descendants belong to an expanded design folder." ), + ColorId.EnabledAutoSet => (0xFFA0F0A0, "Enabled Automation Set", "An automation set that is currently enabled. Only one set can be enabled for each identifier at once." ), + ColorId.DisabledAutoSet => (0xFF808080, "Disabled Automation Set", "An automation set that is currently disabled." ), + ColorId.AutomationActorAvailable => (0xFFFFFFFF, "Automation Actor Available", "A character associated with the given automated design set is currently visible." ), + ColorId.AutomationActorUnavailable => (0xFF808080, "Automation Actor Unavailable", "No character associated with the given automated design set is currently visible." ), + ColorId.HeaderButtons => (0xFFFFF0C0, "Header Buttons", "The text and border color of buttons in the header, like the Incognito toggle." ), + ColorId.FavoriteStarOn => (0xFF40D0D0, "Favored Item", "The color of the star for favored items and of the border in the unlock overview tab." ), + ColorId.FavoriteStarHovered => (0xFFD040D0, "Favorite Star Hovered", "The color of the star for favored items when it is hovered." ), + ColorId.FavoriteStarOff => (0x20808080, "Favorite Star Outline", "The color of the star for items that are not favored when it is not hovered." ), + ColorId.QuickDesignButton => (0x900A0A0A, "Quick Design Bar Button Background", "The color of button frames in the quick design bar." ), + ColorId.QuickDesignFrame => (0x90383838, "Quick Design Bar Combo Background", "The color of the combo background in the quick design bar." ), + ColorId.QuickDesignBg => (0x00F0F0F0, "Quick Design Bar Window Background", "The color of the window background in the quick design bar." ), + ColorId.TriStateCheck => (0xFF00D000, "Checkmark in Tri-State Checkboxes", "The color of the checkmark indicating positive change in tri-state checkboxes." ), + ColorId.TriStateCross => (0xFF0000D0, "Cross in Tri-State Checkboxes", "The color of the cross indicating negative change in tri-state checkboxes." ), + ColorId.TriStateNeutral => (0xFFD0D0D0, "Dot in Tri-State Checkboxes", "The color of the dot indicating no change in tri-state checkboxes." ), + ColorId.BattleNpc => (0xFFFFFFFF, "Battle NPC in NPC Tab", "The color of the names of battle NPCs in the NPC tab that do not have a more specific color assigned." ), + ColorId.EventNpc => (0xFFFFFFFF, "Event NPC in NPC Tab", "The color of the names of event NPCs in the NPC tab that do not have a more specific color assigned." ), + _ => (0x00000000, string.Empty, string.Empty ), // @formatter:on }; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs index 4f463d6..4d34a05 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Color.cs @@ -1,83 +1,16 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; using Glamourer.GameData; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; -using OtterGui.Text; -using OtterGui.Text.EndObjects; using Penumbra.GameData.Enums; -using Penumbra.GameData.Structs; -using System; namespace Glamourer.Gui.Customization; public partial class CustomizationDrawer { - private const string ColorPickerPopupName = "ColorPicker"; - private CustomizeValue _draggedColorValue; - private CustomizeIndex _draggedColorType; - - - private void DrawDragDropSource(CustomizeIndex index, CustomizeData custom) - { - using var dragDropSource = ImUtf8.DragDropSource(); - if (!dragDropSource) - return; - - if (!DragDropSource.SetPayload("##colorDragDrop"u8)) - _draggedColorValue = _customize[index]; - ImUtf8.Text( - $"Dragging {(custom.Color == 0 ? $"{_currentOption} (NPC)" : _currentOption)} #{_draggedColorValue.Value}..."); - _draggedColorType = index; - } - - private void DrawDragDropTarget(CustomizeIndex index) - { - using var dragDropTarget = ImUtf8.DragDropTarget(); - if (!dragDropTarget.Success || !dragDropTarget.IsDropping("##colorDragDrop"u8)) - return; - - var idx = _set.DataByValue(_draggedColorType, _draggedColorValue, out var draggedData, _customize.Face); - var bestMatch = _draggedColorValue; - if (draggedData.HasValue) - { - var draggedColor = draggedData.Value.Color; - var targetData = _set.Data(index, idx); - if (targetData.Color != draggedColor) - { - var bestDiff = Diff(targetData.Color, draggedColor); - var count = _set.Count(index); - for (var i = 0; i < count; ++i) - { - targetData = _set.Data(index, i); - if (targetData.Color == draggedColor) - { - UpdateValue(_draggedColorValue); - return; - } - - var diff = Diff(targetData.Color, draggedColor); - if (diff >= bestDiff) - continue; - - bestDiff = diff; - bestMatch = (CustomizeValue)i; - } - } - } - - UpdateValue(bestMatch); - return; - - static uint Diff(uint color1, uint color2) - { - var r = (color1 & 0xFF) - (color2 & 0xFF); - var g = ((color1 >> 8) & 0xFF) - ((color2 >> 8) & 0xFF); - var b = ((color1 >> 16) & 0xFF) - ((color2 >> 16) & 0xFF); - return 30 * r * r + 59 * g * g + 11 * b * b; - } - } + private const string ColorPickerPopupName = "ColorPicker"; private void DrawColorPicker(CustomizeIndex index) { @@ -88,7 +21,7 @@ public partial class CustomizationDrawer using (_ = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, current < 0)) { - if (ImGui.ColorButton($"{_customize[index].Value}##color", color, ImGuiColorEditFlags.NoDragDrop, _framedIconSize)) + if (ImGui.ColorButton($"{_customize[index].Value}##color", color, ImGuiColorEditFlags.None, _framedIconSize)) { ImGui.OpenPopup(ColorPickerPopupName); } @@ -97,9 +30,6 @@ public partial class CustomizationDrawer var data = _set.Data(_currentIndex, current, _customize.Face); UpdateValue(data.Value); } - - DrawDragDropSource(index, custom); - DrawDragDropTarget(index); } var npc = false; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.GenderRace.cs b/Glamourer/Gui/Customization/CustomizationDrawer.GenderRace.cs index 26e9002..2f67012 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.GenderRace.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.GenderRace.cs @@ -1,5 +1,5 @@ using Dalamud.Interface; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Enums; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs index 8599f8c..486fdb4 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs @@ -1,9 +1,8 @@ using Dalamud.Interface.Textures.TextureWraps; using Glamourer.GameData; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; using OtterGui.Raii; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -35,7 +34,7 @@ public partial class CustomizationDrawer var hasIcon = icon.TryGetWrap(out var wrap, out _); using (_ = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw)) { - if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize)) + if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize)) { ImGui.OpenPopup(IconSelectorPopup); } @@ -89,7 +88,7 @@ public partial class CustomizationDrawer : ImRaii.PushColor(ImGuiCol.Button, ColorId.FavoriteStarOn.Value(), isFavorite); var hasIcon = icon.TryGetWrap(out var wrap, out var _); - if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize)) + if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize)) { UpdateValue(custom.Value); ImGui.CloseCurrentPopup(); @@ -215,7 +214,7 @@ public partial class CustomizationDrawer hasIcon = icon.TryGetWrap(out wrap, out _); } - if (ImGui.ImageButton(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, _iconSize, Vector2.Zero, Vector2.One, + if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize, Vector2.Zero, Vector2.One, (int)ImGui.GetStyle().FramePadding.X, Vector4.Zero, enabled ? Vector4.One : _redTint)) { _customize.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max); diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs index ec5523f..2c797b8 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGuiInternal; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs index 349891c..4ec6146 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs @@ -4,7 +4,7 @@ using Dalamud.Plugin.Services; using Glamourer.GameData; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Enums; diff --git a/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs b/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs index 4db6b14..9bfb2f8 100644 --- a/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizeParameterDrawer.cs @@ -3,7 +3,7 @@ using Glamourer.Designs; using Glamourer.GameData; using Glamourer.Interop.PalettePlus; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Services; @@ -287,13 +287,13 @@ public class CustomizeParameterDrawer(Configuration config, PaletteImport import } private ImGuiColorEditFlags GetFlags() - => Format | Display | ImGuiColorEditFlags.Hdr | ImGuiColorEditFlags.NoOptions; + => Format | Display | ImGuiColorEditFlags.HDR | ImGuiColorEditFlags.NoOptions; private ImGuiColorEditFlags Format => config.UseFloatForColors ? ImGuiColorEditFlags.Float : ImGuiColorEditFlags.Uint8; private ImGuiColorEditFlags Display - => config.UseRgbForColors ? ImGuiColorEditFlags.DisplayRgb : ImGuiColorEditFlags.DisplayHsv; + => config.UseRgbForColors ? ImGuiColorEditFlags.DisplayRGB : ImGuiColorEditFlags.DisplayHSV; private ImRaii.IEndObject EnsureSize() { diff --git a/Glamourer/Gui/DesignCombo.cs b/Glamourer/Gui/DesignCombo.cs index 2d8880e..dccfa44 100644 --- a/Glamourer/Gui/DesignCombo.cs +++ b/Glamourer/Gui/DesignCombo.cs @@ -5,10 +5,9 @@ using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Designs.Special; using Glamourer.Events; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Log; using OtterGui.Widgets; @@ -22,7 +21,6 @@ public abstract class DesignComboBase : FilterComboCache>> generator, Logger log, DesignChanged designChanged, TabSelected tabSelected, EphemeralConfig config, DesignColors designColors) @@ -85,11 +83,17 @@ public abstract class DesignComboBase : FilterComboCache _currentDesign == p.Item1); + UpdateSelection(CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : null); + return CurrentSelectionIdx; + } + protected bool Draw(IDesignStandIn? currentDesign, string? label, float width) { _currentDesign = currentDesign; - UpdateCurrentSelection(); - InnerWidth = 400 * ImGuiHelpers.GlobalScale; + InnerWidth = 400 * ImGuiHelpers.GlobalScale; var name = label ?? "Select Design Here..."; bool ret; using (_ = currentDesign != null ? ImRaii.PushColor(ImGuiCol.Text, DesignColors.GetColor(currentDesign as Design)) : null) @@ -123,60 +127,37 @@ public abstract class DesignComboBase : FilterComboCache ReferenceEquals(s.Item1, CurrentSelection?.Item1)); - if (CurrentSelectionIdx >= 0) + switch (type) { - UpdateSelection(Items[CurrentSelectionIdx]); + case DesignChanged.Type.Created: + case DesignChanged.Type.Renamed: + case DesignChanged.Type.ChangedColor: + case DesignChanged.Type.Deleted: + case DesignChanged.Type.QuickDesignBar: + var priorState = IsInitialized; + if (priorState) + Cleanup(); + CurrentSelectionIdx = Items.IndexOf(s => ReferenceEquals(s.Item1, CurrentSelection?.Item1)); + if (CurrentSelectionIdx >= 0) + { + UpdateSelection(Items[CurrentSelectionIdx]); + } + else if (Items.Count > 0) + { + CurrentSelectionIdx = 0; + UpdateSelection(Items[0]); + } + else + { + UpdateSelection(null); + } + + if (!priorState) + Cleanup(); + break; } - else if (Items.Count > 0) - { - CurrentSelectionIdx = 0; - UpdateSelection(Items[0]); - } - else - { - UpdateSelection(null); - } - - if (!priorState) - Cleanup(); - _isCurrentSelectionDirty = false; - } - - protected override int UpdateCurrentSelected(int currentSelected) - { - CurrentSelectionIdx = Items.IndexOf(p => _currentDesign == p.Item1); - UpdateSelection(CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : null); - return CurrentSelectionIdx; - } - - private void OnDesignChanged(DesignChanged.Type type, Design? _1, ITransaction? _2 = null) - { - _isCurrentSelectionDirty = type switch - { - DesignChanged.Type.Created => true, - DesignChanged.Type.Renamed => true, - DesignChanged.Type.ChangedColor => true, - DesignChanged.Type.Deleted => true, - DesignChanged.Type.QuickDesignBar => true, - _ => _isCurrentSelectionDirty, - }; } private void QuickSelectedDesignTooltip(IDesignStandIn? design) @@ -194,7 +175,7 @@ public abstract class DesignComboBase : FilterComboCache [ - .. fileSystem - .Where(kvp => kvp.Key.QuickDesign) - .Select(kvp => new Tuple(kvp.Key, kvp.Value.FullName())) + .. designs.Designs + .Where(d => d.QuickDesign) + .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .OrderBy(d => d.Item2), ]) { @@ -295,6 +277,7 @@ public sealed class QuickDesignCombo : DesignCombo } public sealed class LinkDesignCombo( + DesignManager designs, DesignFileSystem fileSystem, Logger log, DesignChanged designChanged, @@ -303,8 +286,8 @@ public sealed class LinkDesignCombo( DesignColors designColors) : DesignCombo(log, designChanged, tabSelected, config, designColors, () => [ - .. fileSystem - .Select(kvp => new Tuple(kvp.Key, kvp.Value.FullName())) + .. designs.Designs + .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .OrderBy(d => d.Item2), ]); @@ -318,8 +301,8 @@ public sealed class RandomDesignCombo( DesignColors designColors) : DesignCombo(log, designChanged, tabSelected, config, designColors, () => [ - .. fileSystem - .Select(kvp => new Tuple(kvp.Key, kvp.Value.FullName())) + .. designs.Designs + .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .OrderBy(d => d.Item2), ]) { @@ -345,6 +328,7 @@ public sealed class RandomDesignCombo( } public sealed class SpecialDesignCombo( + DesignManager designs, DesignFileSystem fileSystem, TabSelected tabSelected, DesignColors designColors, @@ -354,8 +338,8 @@ public sealed class SpecialDesignCombo( EphemeralConfig config, RandomDesignGenerator rng, QuickSelectedDesign quickSelectedDesign) - : DesignComboBase(() => fileSystem - .Select(kvp => new Tuple(kvp.Key, kvp.Value.FullName())) + : DesignComboBase(() => designs.Designs + .Select(d => new Tuple(d, fileSystem.FindLeaf(d, out var l) ? l.FullName() : string.Empty)) .OrderBy(d => d.Item2) .Prepend(new Tuple(new RandomDesign(rng), string.Empty)) .Prepend(new Tuple(quickSelectedDesign, string.Empty)) diff --git a/Glamourer/Gui/DesignQuickBar.cs b/Glamourer/Gui/DesignQuickBar.cs index e8c0ce3..31ca45e 100644 --- a/Glamourer/Gui/DesignQuickBar.cs +++ b/Glamourer/Gui/DesignQuickBar.cs @@ -6,28 +6,26 @@ using Dalamud.Interface.Windowing; using Dalamud.Plugin.Services; using Glamourer.Automation; using Glamourer.Designs; -using Glamourer.Interop.Penumbra; +using Glamourer.Interop; +using Glamourer.Interop.Structs; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; +using OtterGui; using OtterGui.Classes; -using OtterGui.Text; using Penumbra.GameData.Actors; -using Penumbra.GameData.Interop; namespace Glamourer.Gui; [Flags] public enum QdbButtons { - ApplyDesign = 0x01, - RevertAll = 0x02, - RevertAutomation = 0x04, - RevertAdvancedDyes = 0x08, - RevertEquip = 0x10, - RevertCustomize = 0x20, - ReapplyAutomation = 0x40, - ResetSettings = 0x80, - RevertAdvancedCustomization = 0x100, + ApplyDesign = 0x01, + RevertAll = 0x02, + RevertAutomation = 0x04, + RevertAdvanced = 0x08, + RevertEquip = 0x10, + RevertCustomize = 0x20, + ReapplyAutomation = 0x40, } public sealed class DesignQuickBar : Window, IDisposable @@ -37,21 +35,19 @@ public sealed class DesignQuickBar : Window, IDisposable ? ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoMove : ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing; - private readonly Configuration _config; - private readonly QuickDesignCombo _designCombo; - private readonly StateManager _stateManager; - private readonly AutoDesignApplier _autoDesignApplier; - private readonly ActorObjectManager _objects; - private readonly PenumbraService _penumbra; - private readonly IKeyState _keyState; - private readonly ImRaii.Style _windowPadding = new(); - private readonly ImRaii.Color _windowColor = new(); - private DateTime _keyboardToggle = DateTime.UnixEpoch; - private int _numButtons; - private readonly StringBuilder _tooltipBuilder = new(512); + private readonly Configuration _config; + private readonly QuickDesignCombo _designCombo; + private readonly StateManager _stateManager; + private readonly AutoDesignApplier _autoDesignApplier; + private readonly ObjectManager _objects; + private readonly IKeyState _keyState; + private readonly ImRaii.Style _windowPadding = new(); + private readonly ImRaii.Color _windowColor = new(); + private DateTime _keyboardToggle = DateTime.UnixEpoch; + private int _numButtons; public DesignQuickBar(Configuration config, QuickDesignCombo designCombo, StateManager stateManager, IKeyState keyState, - ActorObjectManager objects, AutoDesignApplier autoDesignApplier, PenumbraService penumbra) + ObjectManager objects, AutoDesignApplier autoDesignApplier) : base("Glamourer Quick Bar", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoDocking) { _config = config; @@ -60,11 +56,9 @@ public sealed class DesignQuickBar : Window, IDisposable _keyState = keyState; _objects = objects; _autoDesignApplier = autoDesignApplier; - _penumbra = penumbra; IsOpen = _config.Ephemeral.ShowDesignQuickBar; DisableWindowSounds = true; Size = Vector2.Zero; - RespectCloseHotkey = false; } public void Dispose() @@ -109,7 +103,7 @@ public sealed class DesignQuickBar : Window, IDisposable private void Draw(float width) { - using var group = ImUtf8.Group(); + using var group = ImRaii.Group(); var spacing = ImGui.GetStyle().ItemInnerSpacing; using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing); var buttonSize = new Vector2(ImGui.GetFrameHeight()); @@ -126,10 +120,8 @@ public sealed class DesignQuickBar : Window, IDisposable DrawRevertEquipButton(buttonSize); DrawRevertCustomizeButton(buttonSize); DrawRevertAdvancedCustomization(buttonSize); - DrawRevertAdvancedDyes(buttonSize); DrawRevertAutomationButton(buttonSize); DrawReapplyAutomationButton(buttonSize); - DrawResetSettingsButton(buttonSize); } private ActorIdentifier _playerIdentifier; @@ -152,38 +144,33 @@ public sealed class DesignQuickBar : Window, IDisposable { var design = _designCombo.Design as Design; var available = 0; - _tooltipBuilder.Clear(); - + var tooltip = string.Empty; if (design == null) { - _tooltipBuilder.Append("No design selected."); + tooltip = "No design selected."; } else { if (_playerIdentifier.IsValid && _playerData.Valid) { available |= 1; - _tooltipBuilder.Append("Left-Click: Apply ") - .Append(design.ResolveName(_config.Ephemeral.IncognitoMode)) - .Append(" to yourself."); + tooltip = $"Left-Click: Apply {design.ResolveName(_config.Ephemeral.IncognitoMode)} to yourself."; } if (_targetIdentifier.IsValid && _targetData.Valid) { if (available != 0) - _tooltipBuilder.Append('\n'); + tooltip += '\n'; available |= 2; - _tooltipBuilder.Append("Right-Click: Apply ") - .Append(design.ResolveName(_config.Ephemeral.IncognitoMode)) - .Append(" to ").Append(_config.Ephemeral.IncognitoMode ? _targetIdentifier.Incognito(null) : _targetIdentifier.ToName()); + tooltip += $"Right-Click: Apply {design.ResolveName(_config.Ephemeral.IncognitoMode)} to {_targetIdentifier}."; } if (available == 0) - _tooltipBuilder.Append("Neither player character nor target available."); + tooltip = "Neither player character nor target available."; } - var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.PlayCircle, size, available); + var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.PlayCircle, size, tooltip, available); ImGui.SameLine(); if (!clicked) return; @@ -196,7 +183,7 @@ public sealed class DesignQuickBar : Window, IDisposable } using var _ = design!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); - _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true }); + _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks); } private void DrawRevertButton(Vector2 buttonSize) @@ -205,32 +192,28 @@ public sealed class DesignQuickBar : Window, IDisposable return; var available = 0; - _tooltipBuilder.Clear(); - + var tooltip = string.Empty; if (_playerIdentifier.IsValid && _playerState is { IsLocked: false }) { available |= 1; - _tooltipBuilder.Append("Left-Click: Revert the player character to their game state."); + tooltip = "Left-Click: Revert the player character to their game state."; } if (_targetIdentifier.IsValid && _targetState is { IsLocked: false }) { if (available != 0) - _tooltipBuilder.Append('\n'); + tooltip += '\n'; available |= 2; - _tooltipBuilder.Append("Right-Click: Revert ") - .Append(_targetIdentifier) - .Append(" to their game state."); + tooltip += $"Right-Click: Revert {_targetIdentifier} to their game state."; } if (available == 0) - _tooltipBuilder.Append( - "Neither player character nor target are available, have state modified by Glamourer, or their state is locked."); + tooltip = "Neither player character nor target are available, have state modified by Glamourer, or their state is locked."; - var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, available); + var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, tooltip, available); ImGui.SameLine(); if (clicked) - _stateManager.ResetState(state!, StateSource.Manual, isFinal: true); + _stateManager.ResetState(state!, StateSource.Manual); } private void DrawRevertAutomationButton(Vector2 buttonSize) @@ -242,37 +225,34 @@ public sealed class DesignQuickBar : Window, IDisposable return; var available = 0; - _tooltipBuilder.Clear(); + var tooltip = string.Empty; if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid) { available |= 1; - _tooltipBuilder.Append("Left-Click: Revert the player character to their automation state."); + tooltip = "Left-Click: Revert the player character to their automation state."; } if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid) { if (available != 0) - _tooltipBuilder.Append('\n'); + tooltip += '\n'; available |= 2; - _tooltipBuilder.Append("Right-Click: Revert ") - .Append(_targetIdentifier) - .Append(" to their automation state."); + tooltip += $"Right-Click: Revert {_targetIdentifier} to their automation state."; } if (available == 0) - _tooltipBuilder.Append( - "Neither player character nor target are available, have state modified by Glamourer, or their state is locked."); + tooltip = "Neither player character nor target are available, have state modified by Glamourer, or their state is locked."; - var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.SyncAlt, buttonSize, available); + var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.SyncAlt, buttonSize, tooltip, available); ImGui.SameLine(); if (!clicked) return; foreach (var actor in data.Objects) { - _autoDesignApplier.ReapplyAutomation(actor, id, state!, true, false, out var forcedRedraw); - _stateManager.ReapplyAutomationState(actor, forcedRedraw, true, StateSource.Manual); + _autoDesignApplier.ReapplyAutomation(actor, id, state!, true, out var forcedRedraw); + _stateManager.ReapplyState(actor, forcedRedraw, StateSource.Manual); } } @@ -285,104 +265,69 @@ public sealed class DesignQuickBar : Window, IDisposable return; var available = 0; - _tooltipBuilder.Clear(); + var tooltip = string.Empty; if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid) { available |= 1; - _tooltipBuilder.Append("Left-Click: Reapply the player character's current automation on top of their current state."); + tooltip = "Left-Click: Reapply the player character's current automation on top of their current state."; } if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid) { if (available != 0) - _tooltipBuilder.Append('\n'); + tooltip += '\n'; available |= 2; - _tooltipBuilder.Append("Right-Click: Reapply ") - .Append(_targetIdentifier) - .Append("'s current automation on top of their current state."); + tooltip += $"Right-Click: Reapply {_targetIdentifier}'s current automation on top of their current state."; } if (available == 0) - _tooltipBuilder.Append( - "Neither player character nor target are available, have state modified by Glamourer, or their state is locked."); + tooltip = "Neither player character nor target are available, have state modified by Glamourer, or their state is locked."; - var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.Repeat, buttonSize, available); + var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.Repeat, buttonSize, tooltip, available); ImGui.SameLine(); if (!clicked) return; foreach (var actor in data.Objects) { - _autoDesignApplier.ReapplyAutomation(actor, id, state!, false, false, out var forcedRedraw); - _stateManager.ReapplyAutomationState(actor, forcedRedraw, false, StateSource.Manual); + _autoDesignApplier.ReapplyAutomation(actor, id, state!, false, out var forcedRedraw); + _stateManager.ReapplyState(actor, forcedRedraw, StateSource.Manual); } } private void DrawRevertAdvancedCustomization(Vector2 buttonSize) { - if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedCustomization)) + if (!_config.UseAdvancedParameters) + return; + + if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvanced)) return; var available = 0; - _tooltipBuilder.Clear(); + var tooltip = string.Empty; if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid) { available |= 1; - _tooltipBuilder.Append("Left-Click: Revert the advanced customizations of the player character to their game state."); + tooltip = "Left-Click: Revert the advanced customizations and dyes of the player character to their game state."; } if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid) { if (available != 0) - _tooltipBuilder.Append('\n'); + tooltip += '\n'; available |= 2; - _tooltipBuilder.Append("Right-Click: Revert the advanced customizations of ") - .Append(_targetIdentifier) - .Append(" to their game state."); + tooltip += $"Right-Click: Revert the advanced customizations and dyes of {_targetIdentifier} to their game state."; } if (available == 0) - _tooltipBuilder.Append("Neither player character nor target are available or their state is locked."); + tooltip = "Neither player character nor target are available or their state is locked."; - var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.PaintBrush, buttonSize, available); + var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Palette, buttonSize, tooltip, available); ImGui.SameLine(); if (clicked) - _stateManager.ResetAdvancedCustomizations(state!, StateSource.Manual); - } - - private void DrawRevertAdvancedDyes(Vector2 buttonSize) - { - if (!_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedDyes)) - return; - - var available = 0; - _tooltipBuilder.Clear(); - - if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid) - { - available |= 1; - _tooltipBuilder.Append("Left-Click: Revert the advanced dyes of the player character to their game state."); - } - - if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid) - { - if (available != 0) - _tooltipBuilder.Append('\n'); - available |= 2; - _tooltipBuilder.Append("Right-Click: Revert the advanced dyes of ") - .Append(_targetIdentifier) - .Append(" to their game state."); - } - - if (available == 0) - _tooltipBuilder.Append("Neither player character nor target are available or their state is locked."); - - var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Palette, buttonSize, available); - ImGui.SameLine(); - if (clicked) - _stateManager.ResetAdvancedDyes(state!, StateSource.Manual); + _stateManager.ResetAdvancedState(state!, StateSource.Manual); } private void DrawRevertCustomizeButton(Vector2 buttonSize) @@ -391,28 +336,26 @@ public sealed class DesignQuickBar : Window, IDisposable return; var available = 0; - _tooltipBuilder.Clear(); + var tooltip = string.Empty; if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid) { available |= 1; - _tooltipBuilder.Append("Left-Click: Revert the customizations of the player character to their game state."); + tooltip = "Left-Click: Revert the customizations of the player character to their game state."; } if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid) { if (available != 0) - _tooltipBuilder.Append('\n'); + tooltip += '\n'; available |= 2; - _tooltipBuilder.Append("Right-Click: Revert the customizations of ") - .Append(_targetIdentifier) - .Append(" to their game state."); + tooltip += $"Right-Click: Revert the customizations of {_targetIdentifier} to their game state."; } if (available == 0) - _tooltipBuilder.Append("Neither player character nor target are available or their state is locked."); + tooltip = "Neither player character nor target are available or their state is locked."; - var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.User, buttonSize, available); + var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.User, buttonSize, tooltip, available); ImGui.SameLine(); if (clicked) _stateManager.ResetCustomize(state!, StateSource.Manual); @@ -424,80 +367,35 @@ public sealed class DesignQuickBar : Window, IDisposable return; var available = 0; - _tooltipBuilder.Clear(); + var tooltip = string.Empty; if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid) { available |= 1; - _tooltipBuilder.Append("Left-Click: Revert the equipment of the player character to its game state."); + tooltip = "Left-Click: Revert the equipment of the player character to its game state."; } if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid) { if (available != 0) - _tooltipBuilder.Append('\n'); + tooltip += '\n'; available |= 2; - _tooltipBuilder.Append("Right-Click: Revert the equipment of ") - .Append(_targetIdentifier) - .Append(" to its game state."); + tooltip += $"Right-Click: Revert the equipment of {_targetIdentifier} to its game state."; } if (available == 0) - _tooltipBuilder.Append("Neither player character nor target are available or their state is locked."); + tooltip = "Neither player character nor target are available or their state is locked."; - var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Vest, buttonSize, available); + var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Vest, buttonSize, tooltip, available); ImGui.SameLine(); if (clicked) _stateManager.ResetEquip(state!, StateSource.Manual); } - private void DrawResetSettingsButton(Vector2 buttonSize) + private (bool, ActorIdentifier, ActorData, ActorState?) ResolveTarget(FontAwesomeIcon icon, Vector2 buttonSize, string tooltip, + int available) { - if (!_config.QdbButtons.HasFlag(QdbButtons.ResetSettings)) - return; - - var available = 0; - _tooltipBuilder.Clear(); - - if (_playerIdentifier.IsValid && _playerData.Valid) - { - available |= 1; - _tooltipBuilder - .Append( - "Left-Click: Reset all temporary settings applied by Glamourer (manually or through automation) to the collection affecting ") - .Append(_playerIdentifier) - .Append('.'); - } - - if (_targetIdentifier.IsValid && _targetData.Valid) - { - if (available != 0) - _tooltipBuilder.Append('\n'); - available |= 2; - _tooltipBuilder - .Append( - "Right-Click: Reset all temporary settings applied by Glamourer (manually or through automation) to the collection affecting ") - .Append(_targetIdentifier) - .Append('.'); - } - - if (available == 0) - _tooltipBuilder.Append("Neither player character nor target are available to identify their collections."); - - var (clicked, _, data, _) = ResolveTarget(FontAwesomeIcon.Cog, buttonSize, available); - ImGui.SameLine(); - if (clicked) - { - _penumbra.RemoveAllTemporarySettings(data.Objects[0].Index, StateSource.Manual); - _penumbra.RemoveAllTemporarySettings(data.Objects[0].Index, StateSource.Fixed); - } - } - - private (bool, ActorIdentifier, ActorData, ActorState?) ResolveTarget(FontAwesomeIcon icon, Vector2 buttonSize, int available) - { - var enumerator = _tooltipBuilder.GetChunks(); - var span = enumerator.MoveNext() ? enumerator.Current.Span : []; - ImUtf8.IconButton(icon, span, buttonSize, available == 0); + ImGuiUtil.DrawDisabledButton(icon.ToIconString(), buttonSize, tooltip, available == 0, true); if ((available & 1) == 1 && ImGui.IsItemClicked(ImGuiMouseButton.Left)) return (true, _playerIdentifier, _playerData, _playerState); if ((available & 2) == 2 && ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -537,16 +435,12 @@ public sealed class DesignQuickBar : Window, IDisposable ++_numButtons; } - if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedCustomization)) - ++_numButtons; - if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedDyes)) + if ((_config.UseAdvancedParameters || _config.UseAdvancedDyes) && _config.QdbButtons.HasFlag(QdbButtons.RevertAdvanced)) ++_numButtons; if (_config.QdbButtons.HasFlag(QdbButtons.RevertCustomize)) ++_numButtons; if (_config.QdbButtons.HasFlag(QdbButtons.RevertEquip)) ++_numButtons; - if (_config.UseTemporarySettings && _config.QdbButtons.HasFlag(QdbButtons.ResetSettings)) - ++_numButtons; if (_config.QdbButtons.HasFlag(QdbButtons.ApplyDesign)) { ++_numButtons; diff --git a/Glamourer/Gui/Equipment/BonusDrawData.cs b/Glamourer/Gui/Equipment/BonusDrawData.cs index 067c0c6..e19287a 100644 --- a/Glamourer/Gui/Equipment/BonusDrawData.cs +++ b/Glamourer/Gui/Equipment/BonusDrawData.cs @@ -1,5 +1,4 @@ using Glamourer.Designs; -using Glamourer.Interop.Material; using Glamourer.State; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -10,11 +9,10 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData) { private IDesignEditor _editor = null!; private object _object = null!; - public readonly BonusItemFlag Slot = slot; + public readonly BonusItemFlag Slot = slot; public bool Locked; public bool DisplayApplication; public bool AllowRevert; - public bool HasAdvancedDyes; public readonly bool IsDesign => _object is Design; @@ -44,7 +42,6 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData) CurrentApply = design.DoApplyBonusItem(slot), Locked = design.WriteProtected(), DisplayApplication = true, - HasAdvancedDyes = design.GetMaterialDataRef().CheckExistenceSlot(MaterialValueIndex.FromSlot(slot)), }; public static BonusDrawData FromState(StateManager manager, ActorState state, BonusItemFlag slot) @@ -56,6 +53,5 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData) DisplayApplication = false, GameItem = state.BaseData.BonusItem(slot), AllowRevert = true, - HasAdvancedDyes = state.Materials.CheckExistenceSlot(MaterialValueIndex.FromSlot(slot)), }; } diff --git a/Glamourer/Gui/Equipment/BonusItemCombo.cs b/Glamourer/Gui/Equipment/BonusItemCombo.cs index aa43da7..c333a87 100644 --- a/Glamourer/Gui/Equipment/BonusItemCombo.cs +++ b/Glamourer/Gui/Equipment/BonusItemCombo.cs @@ -1,11 +1,10 @@ using Dalamud.Plugin.Services; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Lumina.Excel.Sheets; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Log; using OtterGui.Raii; using OtterGui.Widgets; diff --git a/Glamourer/Gui/Equipment/EquipDrawData.cs b/Glamourer/Gui/Equipment/EquipDrawData.cs index f32e22b..b72e234 100644 --- a/Glamourer/Gui/Equipment/EquipDrawData.cs +++ b/Glamourer/Gui/Equipment/EquipDrawData.cs @@ -1,5 +1,4 @@ using Glamourer.Designs; -using Glamourer.Interop.Material; using Glamourer.State; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -10,7 +9,7 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData) { private IDesignEditor _editor = null!; private object _object = null!; - public readonly EquipSlot Slot = slot; + public readonly EquipSlot Slot = slot; public bool Locked; public bool DisplayApplication; public bool AllowRevert; @@ -27,19 +26,18 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData) public readonly void SetStains(StainIds stains) => _editor.ChangeStains(_object, Slot, stains, ApplySettings.Manual); - public readonly void SetStain(int which, StainId stain) - => _editor.ChangeStains(_object, Slot, CurrentStains.With(which, stain), ApplySettings.Manual); - public readonly void SetApplyItem(bool value) { var manager = (DesignManager)_editor; - manager.ChangeApplyItem((Design)_object, Slot, value); + var design = (Design)_object; + manager.ChangeApplyItem(design, Slot, value); } public readonly void SetApplyStain(bool value) { var manager = (DesignManager)_editor; - manager.ChangeApplyStains((Design)_object, Slot, value); + var design = (Design)_object; + manager.ChangeApplyStains(design, Slot, value); } public EquipItem CurrentItem = designData.Item(slot); @@ -48,7 +46,6 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData) public StainIds GameStains = default; public bool CurrentApply; public bool CurrentApplyStain; - public bool HasAdvancedDyes; public readonly Gender CurrentGender = designData.Customize.Gender; public readonly Race CurrentRace = designData.Customize.Race; @@ -61,7 +58,6 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData) CurrentApply = design.DoApplyEquip(slot), CurrentApplyStain = design.DoApplyStain(slot), Locked = design.WriteProtected(), - HasAdvancedDyes = design.GetMaterialDataRef().CheckExistenceSlot(MaterialValueIndex.FromSlot(slot)), DisplayApplication = true, }; @@ -74,7 +70,6 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData) DisplayApplication = false, GameItem = state.BaseData.Item(slot), GameStains = state.BaseData.Stain(slot), - HasAdvancedDyes = state.Materials.CheckExistenceSlot(MaterialValueIndex.FromSlot(slot)), AllowRevert = true, }; -} +} \ No newline at end of file diff --git a/Glamourer/Gui/Equipment/EquipItemSlotCache.cs b/Glamourer/Gui/Equipment/EquipItemSlotCache.cs deleted file mode 100644 index 20aaf11..0000000 --- a/Glamourer/Gui/Equipment/EquipItemSlotCache.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Glamourer.Services; -using Penumbra.GameData.Enums; -using Penumbra.GameData.Structs; - -namespace Glamourer.Gui.Equipment; - -[InlineArray(13)] -public struct EquipItemSlotCache -{ - private EquipItem _element; - - public EquipItem Dragged - { - get => this[^1]; - set => this[^1] = value; - } - - public void Clear() - => ((Span)this).Clear(); - - public EquipItem this[EquipSlot slot] - { - get => this[(int)slot.ToIndex()]; - set => this[(int)slot.ToIndex()] = value; - } - - public void Update(ItemManager items, in EquipItem item, EquipSlot startSlot) - { - if (item.Id == Dragged.Id && item.Type == Dragged.Type) - return; - - switch (startSlot) - { - case EquipSlot.MainHand: - { - Clear(); - this[EquipSlot.MainHand] = item; - if (item.Type is FullEquipType.Sword) - this[EquipSlot.OffHand] = items.FindClosestShield(item.ItemId, out var shield) ? shield : default; - else - this[EquipSlot.OffHand] = items.ItemData.Secondary.GetValueOrDefault(item.ItemId); - break; - } - case EquipSlot.OffHand: - { - Clear(); - if (item.Type is FullEquipType.Shield) - this[EquipSlot.MainHand] = items.FindClosestSword(item.ItemId, out var sword) ? sword : default; - else - this[EquipSlot.MainHand] = items.ItemData.Primary.GetValueOrDefault(item.ItemId); - this[EquipSlot.OffHand] = item; - break; - } - default: - { - this[EquipSlot.MainHand] = default; - this[EquipSlot.OffHand] = default; - foreach (var slot in EquipSlotExtensions.EqdpSlots) - { - if (startSlot == slot) - { - this[slot] = item; - continue; - } - - var slotItem = items.Identify(slot, item.PrimaryId, item.Variant); - if (!slotItem.Valid || slotItem.ItemId.Id is not 0 != item.ItemId.Id is not 0) - { - slotItem = items.Identify(EquipSlot.OffHand, item.PrimaryId, item.SecondaryId, 1, item.Type); - if (slotItem.ItemId.Id is not 0 != item.ItemId.Id is not 0) - slotItem = default; - } - - this[slot] = slotItem; - } - - break; - } - } - - Dragged = item; - } -} diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.cs index 01ec938..c8a4b11 100644 --- a/Glamourer/Gui/Equipment/EquipmentDrawer.cs +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.cs @@ -5,13 +5,12 @@ using Glamourer.Events; using Glamourer.Gui.Materials; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; -using OtterGui.Extensions; +using ImGuiNET; +using OtterGui; using OtterGui.Raii; using OtterGui.Text; using OtterGui.Text.EndObjects; using OtterGui.Widgets; -using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -32,24 +31,20 @@ public class EquipmentDrawer private readonly Configuration _config; private readonly GPoseService _gPose; private readonly AdvancedDyePopup _advancedDyes; - private readonly ItemCopyService _itemCopy; private float _requiredComboWidthUnscaled; private float _requiredComboWidth; - private Stain? _draggedStain; - private EquipItemSlotCache _draggedItem; - private EquipSlot _dragTarget; + private Stain? _draggedStain; public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, TextureService textures, - Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes, ItemCopyService itemCopy) + Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes) { _items = items; _textures = textures; _config = config; _gPose = gPose; _advancedDyes = advancedDyes; - _itemCopy = itemCopy; _stainData = items.Stains; _stainCombo = new GlamourerColorCombo(DefaultWidth - 20, _stainData, favorites); _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray(); @@ -68,7 +63,6 @@ public class EquipmentDrawer private Vector2 _iconSize; private float _comboLength; - private uint _advancedMaterialColor; public void Prepare() { @@ -80,9 +74,7 @@ public class EquipmentDrawer .Max(i => ImGui.CalcTextSize($"{i.Item2.Name} ({i.Item2.ModelString})").X) / ImGuiHelpers.GlobalScale; - _requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale; - _advancedMaterialColor = ColorId.AdvancedDyeActive.Value(); - _dragTarget = EquipSlot.Unknown; + _requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale; } private bool VerifyRestrictedGear(EquipDrawData data) @@ -99,7 +91,7 @@ public class EquipmentDrawer if (_config.HideApplyCheckmarks) equipDrawData.DisplayApplication = false; - using var id = ImUtf8.PushId((int)equipDrawData.Slot); + using var id = ImRaii.PushId((int)equipDrawData.Slot); var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y }; using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing); @@ -114,7 +106,7 @@ public class EquipmentDrawer if (_config.HideApplyCheckmarks) bonusDrawData.DisplayApplication = false; - using var id = ImUtf8.PushId(100 + (int)bonusDrawData.Slot); + using var id = ImRaii.PushId(100 + (int)bonusDrawData.Slot); var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y }; using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing); @@ -135,7 +127,7 @@ public class EquipmentDrawer offhand.DisplayApplication = false; } - using var id = ImUtf8.PushId("Weapons"u8); + using var id = ImRaii.PushId("Weapons"); var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y }; using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing); @@ -182,13 +174,12 @@ public class EquipmentDrawer change = true; } - ImUtf8.HoverTooltip($"{_config.DeleteDesignModifier.ToString()} and Right-click to clear."); + ImGuiUtil.HoverTooltip($"{_config.DeleteDesignModifier.ToString()} and Right-click to clear."); } return change; } - #region Small private void DrawEquipSmall(in EquipDrawData equipDrawData) @@ -205,13 +196,14 @@ public class EquipmentDrawer } else if (equipDrawData.IsState) { - _advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(equipDrawData.Slot); } if (VerifyRestrictedGear(equipDrawData)) label += " (Restricted)"; - DrawEquipLabel(equipDrawData is { IsDesign: true, HasAdvancedDyes: true }, label); + ImGui.SameLine(); + ImGui.TextUnformatted(label); } private void DrawBonusItemSmall(in BonusDrawData bonusDrawData) @@ -226,10 +218,11 @@ public class EquipmentDrawer } else if (bonusDrawData.IsState) { - _advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(bonusDrawData.Slot); } - DrawEquipLabel(bonusDrawData is { IsDesign: true, HasAdvancedDyes: true }, label); + ImGui.SameLine(); + ImGui.TextUnformatted(label); } private void DrawWeaponsSmall(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons) @@ -246,12 +239,12 @@ public class EquipmentDrawer } else if (mainhand.IsState) { - _advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(EquipSlot.MainHand); } if (allWeapons) mainhandLabel += $" ({mainhand.CurrentItem.Type.ToName()})"; - WeaponHelpMarker(mainhand is { IsDesign: true, HasAdvancedDyes: true }, mainhandLabel); + WeaponHelpMarker(mainhandLabel); if (offhand.CurrentItem.Type is FullEquipType.Unknown) return; @@ -268,10 +261,10 @@ public class EquipmentDrawer } else if (offhand.IsState) { - _advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(EquipSlot.OffHand); } - WeaponHelpMarker(offhand is { IsDesign: true, HasAdvancedDyes: true }, offhandLabel); + WeaponHelpMarker(offhandLabel); } #endregion @@ -292,8 +285,8 @@ public class EquipmentDrawer DrawApply(equipDrawData); } - DrawEquipLabel(equipDrawData is { IsDesign: true, HasAdvancedDyes: true }, label); - + ImGui.SameLine(); + ImGui.TextUnformatted(label); DrawStain(equipDrawData, false); if (equipDrawData.DisplayApplication) { @@ -302,13 +295,13 @@ public class EquipmentDrawer } else if (equipDrawData.IsState) { - _advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(equipDrawData.Slot); } if (VerifyRestrictedGear(equipDrawData)) { ImGui.SameLine(); - ImUtf8.Text("(Restricted)"u8); + ImGui.TextUnformatted("(Restricted)"); } } @@ -326,10 +319,11 @@ public class EquipmentDrawer } else if (bonusDrawData.IsState) { - _advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(bonusDrawData.Slot); } - DrawEquipLabel(bonusDrawData is { IsDesign: true, HasAdvancedDyes: true }, label); + ImGui.SameLine(); + ImGui.TextUnformatted(label); } private void DrawWeaponsNormal(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons) @@ -340,7 +334,7 @@ public class EquipmentDrawer mainhand.CurrentItem.DrawIcon(_textures, _iconSize, EquipSlot.MainHand); var left = ImGui.IsItemClicked(ImGuiMouseButton.Left); ImGui.SameLine(); - using (ImUtf8.Group()) + using (ImRaii.Group()) { DrawMainhand(ref mainhand, ref offhand, out var mainhandLabel, allWeapons, false, left); if (mainhand.DisplayApplication) @@ -349,8 +343,7 @@ public class EquipmentDrawer DrawApply(mainhand); } - WeaponHelpMarker(mainhand is { IsDesign: true, HasAdvancedDyes: true }, mainhandLabel, - allWeapons ? mainhand.CurrentItem.Type.ToName() : null); + WeaponHelpMarker(mainhandLabel, allWeapons ? mainhand.CurrentItem.Type.ToName() : null); DrawStain(mainhand, false); if (mainhand.DisplayApplication) @@ -360,7 +353,7 @@ public class EquipmentDrawer } else if (mainhand.IsState) { - _advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(EquipSlot.MainHand); } } @@ -371,7 +364,7 @@ public class EquipmentDrawer var right = ImGui.IsItemClicked(ImGuiMouseButton.Right); left = ImGui.IsItemClicked(ImGuiMouseButton.Left); ImGui.SameLine(); - using (ImUtf8.Group()) + using (ImRaii.Group()) { DrawOffhand(mainhand, offhand, out var offhandLabel, false, right, left); if (offhand.DisplayApplication) @@ -380,7 +373,7 @@ public class EquipmentDrawer DrawApply(offhand); } - WeaponHelpMarker(offhand is { IsDesign: true, HasAdvancedDyes: true }, offhandLabel); + WeaponHelpMarker(offhandLabel); DrawStain(offhand, false); if (offhand.DisplayApplication) @@ -388,9 +381,9 @@ public class EquipmentDrawer ImGui.SameLine(); DrawApplyStain(offhand); } - else if (offhand.IsState) + else if (mainhand.IsState) { - _advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : 0u); + _advancedDyes.DrawButton(EquipSlot.OffHand); } } } @@ -407,7 +400,6 @@ public class EquipmentDrawer ? _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss) : _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss, width); - _itemCopy.HandleCopyPaste(data, index); if (!change) DrawStainDragDrop(data, index, stain, found); @@ -432,8 +424,8 @@ public class EquipmentDrawer using var dragSource = ImUtf8.DragDropSource(); if (dragSource.Success) { - DragDropSource.SetPayload("stainDragDrop"u8); - _draggedStain = stain; + if (DragDropSource.SetPayload("stainDragDrop"u8)) + _draggedStain = stain; ImUtf8.Text($"Dragging {stain.Name}..."); } } @@ -458,12 +450,10 @@ public class EquipmentDrawer using var disabled = ImRaii.Disabled(data.Locked); var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, _requiredComboWidth); - DrawGearDragDrop(data); if (change) data.SetItem(combo.CurrentSelection); else if (combo.CustomVariant.Id > 0) data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant)); - _itemCopy.HandleCopyPaste(data); if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, ItemManager.NothingItem(data.Slot), out var item)) @@ -478,71 +468,17 @@ public class EquipmentDrawer UiHelpers.OpenCombo($"##{combo.Label}"); using var disabled = ImRaii.Disabled(data.Locked); - var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem, - small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, + var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, _requiredComboWidth); - if (ImGui.IsItemHovered() && ImGui.GetIO().KeyCtrl) - { - if (ImGui.IsKeyPressed(ImGuiKey.C)) - _itemCopy.Copy(combo.CurrentSelection); - else if (ImGui.IsKeyPressed(ImGuiKey.V)) - _itemCopy.Paste(data.Slot.ToEquipType(), data.SetItem); - } - if (change) data.SetItem(combo.CurrentSelection); else if (combo.CustomVariant.Id > 0) data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant)); - if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, EquipItem.BonusItemNothing(data.Slot), - out var item)) + if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, EquipItem.BonusItemNothing(data.Slot), out var item)) data.SetItem(item); } - private void DrawGearDragDrop(in EquipDrawData data) - { - if (data.CurrentItem.Valid) - { - using var dragSource = ImUtf8.DragDropSource(); - if (dragSource.Success) - { - DragDropSource.SetPayload("equipDragDrop"u8); - _draggedItem.Update(_items, data.CurrentItem, data.Slot); - } - } - - using var dragTarget = ImUtf8.DragDropTarget(); - if (!dragTarget) - return; - - var item = _draggedItem[data.Slot]; - if (!item.Valid) - return; - - _dragTarget = data.Slot; - if (!dragTarget.IsDropping("equipDragDrop"u8)) - return; - - data.SetItem(item); - _draggedItem.Clear(); - } - - public unsafe void DrawDragDropTooltip() - { - var payload = ImGui.GetDragDropPayload().Handle; - if (payload is null) - return; - - if (!MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)Unsafe.AsPointer(ref payload->DataType_0)).SequenceEqual("equipDragDrop"u8)) - return; - - using var tt = ImUtf8.Tooltip(); - if (_dragTarget is EquipSlot.Unknown) - ImUtf8.Text($"Dragging {_draggedItem.Dragged.Name}..."); - else - ImUtf8.Text($"Converting to {_draggedItem[_dragTarget].Name}..."); - } - private static bool ResetOrClear(bool locked, bool clicked, bool allowRevert, bool allowClear, in T currentItem, in T revertItem, in T clearItem, out T? item) where T : IEquatable { @@ -566,7 +502,7 @@ public class EquipmentDrawer (false, true, _) => ("Right-click to clear.\nControl and mouse wheel to scroll.", clearItem, true), (false, false, _) => ("Control and mouse wheel to scroll.", default, false), }; - ImUtf8.HoverTooltip(tt); + ImGuiUtil.HoverTooltip(tt); return clicked && valid; } @@ -591,13 +527,8 @@ public class EquipmentDrawer if (combo.Draw(mainhand.CurrentItem.Name, mainhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, _requiredComboWidth)) changedItem = combo.CurrentSelection; - else if (combo.CustomVariant.Id > 0 && (drawAll || ItemData.ConvertWeaponId(combo.CustomSetId) == mainhand.CurrentItem.Type)) - changedItem = _items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant); - _itemCopy.HandleCopyPaste(mainhand); - DrawGearDragDrop(mainhand); - - if (ResetOrClear(mainhand.Locked || unknown, open, mainhand.AllowRevert, false, mainhand.CurrentItem, mainhand.GameItem, - default, out var c)) + else if (ResetOrClear(mainhand.Locked || unknown, open, mainhand.AllowRevert, false, mainhand.CurrentItem, mainhand.GameItem, + default, out var c)) changedItem = c; if (changedItem != null) @@ -613,9 +544,8 @@ public class EquipmentDrawer } } - if (unknown) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, - "The weapon type could not be identified, thus changing it to other weapons of that type is not possible."u8); + if (unknown && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) + ImGui.SetTooltip("The weapon type could not be identified, thus changing it to other weapons of that type is not possible."); } private void DrawOffhand(in EquipDrawData mainhand, in EquipDrawData offhand, out string label, bool small, bool clear, bool open) @@ -635,10 +565,6 @@ public class EquipmentDrawer if (combo.Draw(offhand.CurrentItem.Name, offhand.CurrentItem.ItemId, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, _requiredComboWidth)) offhand.SetItem(combo.CurrentSelection); - else if (combo.CustomVariant.Id > 0 && ItemData.ConvertWeaponId(combo.CustomSetId) == offhand.CurrentItem.Type) - offhand.SetItem(_items.Identify(mainhand.Slot, combo.CustomSetId, combo.CustomWeaponId, combo.CustomVariant)); - _itemCopy.HandleCopyPaste(offhand); - DrawGearDragDrop(offhand); var defaultOffhand = _items.GetDefaultOffhand(mainhand.CurrentItem); if (ResetOrClear(locked, clear, offhand.AllowRevert, true, offhand.CurrentItem, offhand.GameItem, defaultOffhand, out var item)) @@ -669,14 +595,14 @@ public class EquipmentDrawer #endregion - private void WeaponHelpMarker(bool hasAdvancedDyes, string label, string? type = null) + private static void WeaponHelpMarker(string label, string? type = null) { ImGui.SameLine(); ImGuiComponents.HelpMarker( "Changing weapons to weapons of different types can cause crashes, freezes, soft- and hard locks and cheating, " + "thus it is only allowed to change weapons to other weapons of the same type."); - DrawEquipLabel(hasAdvancedDyes, label); - + ImGui.SameLine(); + ImGui.TextUnformatted(label); if (type == null) return; @@ -684,17 +610,4 @@ public class EquipmentDrawer pos.Y += ImGui.GetFrameHeightWithSpacing(); ImGui.GetWindowDrawList().AddText(pos, ImGui.GetColorU32(ImGuiCol.Text), $"({type})"); } - - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - private void DrawEquipLabel(bool hasAdvancedDyes, string label) - { - ImGui.SameLine(); - using (ImRaii.PushColor(ImGuiCol.Text, _advancedMaterialColor, hasAdvancedDyes)) - { - ImUtf8.Text(label); - } - - if (hasAdvancedDyes) - ImUtf8.HoverTooltip("This design has advanced dyes setup for this slot."u8); - } } diff --git a/Glamourer/Gui/Equipment/GlamourerColorCombo.cs b/Glamourer/Gui/Equipment/GlamourerColorCombo.cs index 3149e67..527dbb5 100644 --- a/Glamourer/Gui/Equipment/GlamourerColorCombo.cs +++ b/Glamourer/Gui/Equipment/GlamourerColorCombo.cs @@ -2,7 +2,7 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Widgets; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Structs; diff --git a/Glamourer/Gui/Equipment/ItemCombo.cs b/Glamourer/Gui/Equipment/ItemCombo.cs index 7c0c3bc..f7c75e1 100644 --- a/Glamourer/Gui/Equipment/ItemCombo.cs +++ b/Glamourer/Gui/Equipment/ItemCombo.cs @@ -1,10 +1,10 @@ using Dalamud.Plugin.Services; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Lumina.Excel.Sheets; +using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Log; using OtterGui.Raii; using OtterGui.Text; diff --git a/Glamourer/Gui/Equipment/ItemCopyService.cs b/Glamourer/Gui/Equipment/ItemCopyService.cs deleted file mode 100644 index 6912f1f..0000000 --- a/Glamourer/Gui/Equipment/ItemCopyService.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Glamourer.Services; -using Dalamud.Bindings.ImGui; -using OtterGui.Services; -using Penumbra.GameData.DataContainers; -using Penumbra.GameData.Enums; -using Penumbra.GameData.Structs; - -namespace Glamourer.Gui.Equipment; - -public class ItemCopyService(ItemManager items, DictStain stainData) : IUiService -{ - public EquipItem? Item { get; private set; } - public Stain? Stain { get; private set; } - - public void Copy(in EquipItem item) - => Item = item; - - public void Copy(in Stain stain) - => Stain = stain; - - public void Paste(int which, Action setter) - { - if (Stain is { } stain) - setter(which, stain.RowIndex); - } - - public void Paste(FullEquipType type, Action setter) - { - if (Item is not { } item) - return; - - if (type != item.Type) - { - if (type.IsBonus()) - item = items.Identify(type.ToBonus(), item.PrimaryId, item.Variant); - else if (type.IsEquipment() || type.IsAccessory()) - item = items.Identify(type.ToSlot(), item.PrimaryId, item.Variant); - else - item = items.Identify(type.ToSlot(), item.PrimaryId, item.SecondaryId, item.Variant); - } - - if (item.Valid && item.Type == type) - setter(item); - } - - public void HandleCopyPaste(in EquipDrawData data) - { - if (ImGui.GetIO().KeyCtrl) - { - if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Middle)) - Paste(data.CurrentItem.Type, data.SetItem); - } - else if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled) && ImGui.IsMouseClicked(ImGuiMouseButton.Middle)) - { - Copy(data.CurrentItem); - } - } - - public void HandleCopyPaste(in EquipDrawData data, int which) - { - if (ImGui.GetIO().KeyCtrl) - { - if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Middle)) - Paste(which, data.SetStain); - } - else if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled) - && ImGui.IsMouseClicked(ImGuiMouseButton.Middle) - && stainData.TryGetValue(data.CurrentStains[which].Id, out var stain)) - { - Copy(stain); - } - } -} diff --git a/Glamourer/Gui/Equipment/WeaponCombo.cs b/Glamourer/Gui/Equipment/WeaponCombo.cs index 3029db7..37d9d3c 100644 --- a/Glamourer/Gui/Equipment/WeaponCombo.cs +++ b/Glamourer/Gui/Equipment/WeaponCombo.cs @@ -1,8 +1,8 @@ using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; +using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Log; using OtterGui.Raii; using OtterGui.Text; @@ -19,10 +19,6 @@ public sealed class WeaponCombo : FilterComboCache private ItemId _currentItem; private float _innerWidth; - public PrimaryId CustomSetId { get; private set; } - public SecondaryId CustomWeaponId { get; private set; } - public Variant CustomVariant { get; private set; } - public WeaponCombo(ItemManager items, FullEquipType type, Logger log, FavoriteManager favorites) : base(() => GetWeapons(favorites, items, type), MouseWheelType.Control, log) { @@ -50,9 +46,8 @@ public sealed class WeaponCombo : FilterComboCache public bool Draw(string previewName, ItemId previewIdx, float width, float innerWidth) { - _innerWidth = innerWidth; - _currentItem = previewIdx; - CustomVariant = 0; + _innerWidth = innerWidth; + _currentItem = previewIdx; return Draw($"##{Label}", previewName, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()); } @@ -79,24 +74,6 @@ public sealed class WeaponCombo : FilterComboCache return ret; } - protected override void OnClosePopup() - { - // If holding control while the popup closes, try to parse the input as a full tuple of set id, weapon id and variant, and set a custom item for that. - if (!ImGui.GetIO().KeyCtrl) - return; - - var split = Filter.Text.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - if (split.Length != 3 - || !ushort.TryParse(split[0], out var setId) - || !ushort.TryParse(split[1], out var weaponId) - || !byte.TryParse(split[2], out var variant)) - return; - - CustomSetId = setId; - CustomWeaponId = weaponId; - CustomVariant = variant; - } - protected override bool IsVisible(int globalIndex, LowerString filter) => base.IsVisible(globalIndex, filter) || Items[globalIndex].ModelString.StartsWith(filter.Lower); diff --git a/Glamourer/Gui/GenericPopupWindow.cs b/Glamourer/Gui/GenericPopupWindow.cs index 5061862..502af14 100644 --- a/Glamourer/Gui/GenericPopupWindow.cs +++ b/Glamourer/Gui/GenericPopupWindow.cs @@ -3,7 +3,7 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Plugin.Services; using Glamourer.Gui.Materials; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; diff --git a/Glamourer/Gui/GlamourerChangelog.cs b/Glamourer/Gui/GlamourerChangelog.cs index 686d4a1..0c9d99b 100644 --- a/Glamourer/Gui/GlamourerChangelog.cs +++ b/Glamourer/Gui/GlamourerChangelog.cs @@ -39,12 +39,6 @@ public class GlamourerChangelog Add1_3_3_0(Changelog); Add1_3_4_0(Changelog); Add1_3_5_0(Changelog); - Add1_3_6_0(Changelog); - Add1_3_7_0(Changelog); - Add1_3_8_0(Changelog); - Add1_4_0_0(Changelog); - Add1_5_0_0(Changelog); - Add1_5_1_0(Changelog); } private (int, ChangeLogDisplayType) ConfigData() @@ -65,127 +59,16 @@ public class GlamourerChangelog } } - private static void Add1_5_1_0(Changelog log) - => log.NextVersion("Version 1.5.1.0") - .RegisterHighlight("Added support for Penumbras PCP functionality to add the current state of the character as a design.") - .RegisterEntry("On import, a design for the PCP is created and, if possible, applied to the character.", 1) - .RegisterEntry("No automation is assigned.", 1) - .RegisterEntry("Finer control about this can be found in the settings.", 1) - .RegisterEntry("Fixed an issue with static visors not toggling through Glamourer (1.5.0.7).") - .RegisterEntry("The advanced dye slot combo now contains glasses (1.5.0.7).") - .RegisterEntry("Several fixes for patch-related issues (1.5.0.1 - 1.5.0.6"); - - private static void Add1_5_0_0(Changelog log) - => log.NextVersion("Version 1.5.0.0") - .RegisterImportant("Updated for game version 7.30 and Dalamud API13, which uses a new GUI backend. Some things may not work as expected. Please let me know any issues you encounter.") - .RegisterHighlight("Added the new Viera Ears state to designs. Old designs will not apply the state.") - .RegisterHighlight("Added the option to make newly created designs write-protected by default to the design defaults.") - .RegisterEntry("Fixed issues with reverting state and IPC.") - .RegisterEntry("Fixed an issue when using the mousewheel to scroll through designs (1.4.0.3).") - .RegisterEntry("Fixed an issue with invalid bonus items (1.4.0.3).") - .RegisterHighlight("Added drag & drop of equipment pieces which will try to match the corresponding model IDs in other slots if possible (1.4.0.2).") - .RegisterEntry("Heavily optimized some issues when having many designs and creating new ones or updating them (1.4.0.2)") - .RegisterEntry("Fixed an issue with staining templates (1.4.0.1).") - .RegisterEntry("Fixed an issue with the QDB buttons not counting correctly (1.4.0.1)."); - - private static void Add1_4_0_0(Changelog log) - => log.NextVersion("Version 1.4.0.0") - .RegisterHighlight("The design selector width is now draggable within certain restrictions that depend on the total window width.") - .RegisterEntry("The current behavior may not be final, let me know if you have any comments.", 1) - .RegisterEntry("Regular customization colors can now be dragged & dropped onto other customizations.") - .RegisterEntry( - "If no identical color is available in the target slot, the most similar color available (for certain values of similar) will be chosen instead.", - 1) - .RegisterEntry("Resetting advanced dyes and customizations has been split into two buttons for the quick design bar.") - .RegisterEntry("Weapons now also support custom ID input in the combo search box.") - .RegisterEntry("Added new IPC methods GetExtendedDesignData, AddDesign, DeleteDesign, GetDesignBase64, GetDesignJObject.") - .RegisterEntry("Added the option to prevent immediate repeats for random design selection (Thanks Diorik!).") - .RegisterEntry("Optimized some multi-design changes when selecting many designs and changing them at once.") - .RegisterEntry("Fixed item combos not starting from the currently selected item when scrolling them via mouse wheel.") - .RegisterEntry("Fixed some issue with Glamourer not searching mods by name for mod associations in some cases.") - .RegisterEntry("Fixed the IPC methods SetMetaState and SetMetaStateName not working (Thanks Caraxi!).") - .RegisterEntry("Added new IPC method GetDesignListExtended. (1.3.8.6)") - .RegisterEntry( - "Improved the naming of NPCs for identifiers by using Haselnussbombers new naming functionality (Thanks Hasel!). (1.3.8.6)") - .RegisterEntry( - "Added a modifier key separate from the delete modifier key that is used for less important key-checks, specifically toggling incognito mode. (1.3.8.5)") - .RegisterEntry("Used better Penumbra IPC for some things. (1.3.8.5)") - .RegisterEntry("Fixed an issue with advanced dyes for weapons. (1.3.8.5)") - .RegisterEntry("Fixed an issue with NPC automation due to missing job detection. (1.3.8.1)"); - - private static void Add1_3_8_0(Changelog log) - => log.NextVersion("Version 1.3.8.0") - .RegisterImportant("Updated Glamourer for update 7.20 and Dalamud API 12.") - .RegisterEntry( - "This is not thoroughly tested, but I decided to push to stable instead of testing because otherwise a lot of people would just go to testing just for early access again despite having no business doing so.", - 1) - .RegisterEntry( - "I also do not use most of the functionality of Glamourer myself, so I am unable to even encounter most issues myself.", 1) - .RegisterEntry("If you encounter any issues, please report them quickly on the discord.", 1) - .RegisterEntry("Added a chat command to clear temporary settings applied by Glamourer to Penumbra.") - .RegisterEntry("Fixed small issues with customizations not applicable to your race still applying."); - - private static void Add1_3_7_0(Changelog log) - => log.NextVersion("Version 1.3.7.0") - .RegisterImportant( - "The option to disable advanced customizations or advanced dyes has been removed. The functionality can no longer be disabled entirely, you can just decide not to use it, and to hide it.") - .RegisterHighlight( - "You can now configure which panels (like Customization, Equipment, Advanced Customization etc.) are displayed at all, and which are expanded by default. This does not disable any functionality.") - .RegisterHighlight( - "The Unlocks tab now shows whether items are modded in the currently selected collection in Penumbra in Overview mode and shows and can filter and sort for it in Detailed mode.") - .RegisterEntry("Added an optional button to the Quick Design Bar to reset all temporary settings applied by Glamourer.") - .RegisterHighlight( - "Any existing advanced dyes will now be highlighted on the corresponding Advanced Dye buttons in the actors panel and on the corresponding equip slot name in the design panel.") - .RegisterEntry("This also affects currently inactive advanced dyes, which can now be manually removed on the inactive materials.", - 1) - .RegisterHighlight( - "In the design list of an automation set, the design indices are now highlighted if a design contains advanced dyes, mod associations, or links to other designs.") - .RegisterHighlight("Some quality of life improvements:") - .RegisterEntry("Added some buttons for some application rule presets to the Application Rules panel.", 1) - .RegisterEntry("Added some buttons to enable, disable or delete all advanced dyes in a design.", 1) - .RegisterEntry("Some of those buttons are also available in multi-design selection to apply to all selected designs at once.", 1) - .RegisterEntry( - "A copied material color set from Penumbra should now be able to be imported into a advanced dye color set, as well as the other way around.") - .RegisterEntry( - "Automatically applied character updates when applying a design with mod associations and temporary settings are now skipped to prevent some issues with GPose. This should not affect anything else.") - .RegisterEntry("Glamourer now differentiates between temporary settings applied through manual or automatic application."); - - - private static void Add1_3_6_0(Changelog log) - => log.NextVersion("Version 1.3.6.0") - .RegisterHighlight("Added some new multi design selection functionality to change design settings of many designs at once.") - .RegisterEntry("Also added the number of selected designs and folders to the multi design selection display.", 1) - .RegisterEntry("Glamourer will now use temporary settings when saving mod associations, if they exist in Penumbra.") - .RegisterEntry( - "Actually added the checkbox to reset all temporary settings to Automation Sets (functionality was there, just not exposed to the UI...).") - .RegisterEntry( - "Adapted the behavior for identified copies of characters that have a different state than the character itself to deal with the associated Penumbra changes.") - .RegisterEntry( - "Added '/glamour resetdesign' as a command, that re-applies automation but resets randomly chosen designs (Thanks Diorik).") - .RegisterEntry("All existing facepaints should now be accepted in designs, including NPC facepaints.") - .RegisterEntry( - "Overwriting a design with your characters current state will now discard any prior advanced dyes and only add those from the current state.") - .RegisterEntry("Fixed an issue with racial mount and accessory scaling when changing zones on a changed race.") - .RegisterEntry("Fixed issues with the detection of gear set changes in certain circumstances (Thanks Cordelia).") - .RegisterEntry("Fixed an issue with the Force to Inherit checkbox in mod associations.") - .RegisterEntry( - "Added a new IPC event that fires only when Glamourer finalizes its current changes to a character (for/from Cordelia).") - .RegisterEntry("Added new IPC to set a meta flag on actors. (for/from Cordelia)."); - private static void Add1_3_5_0(Changelog log) => log.NextVersion("Version 1.3.5.0") - .RegisterHighlight( - "Added the usage of the new Temporary Mod Setting functionality from Penumbra to apply mod associations. This is on by default but can be turned back to permanent changes in the settings.") + .RegisterHighlight("Added the usage of the new Temporary Mod Setting functionality from Penumbra to apply mod associations. This is on by default but can be turned back to permanent changes in the settings.") .RegisterEntry("Designs now have a setting to always reset all prior temporary settings made by Glamourer on application.", 1) - .RegisterEntry("Automation Sets also have a setting to do this, independently of the designs contained in them.", 1) + .RegisterEntry("Automation Sets also have a setting to do this, independently of the designs contained in them.", 1) .RegisterHighlight("More NPC customization options should now be accepted as valid for designs, regardless of clan/gender.") .RegisterHighlight("The 'Apply' chat command had the currently selected design and the current quick bar design added as choices.") - .RegisterEntry( - "The application buttons for designs, NPCs or actors should now stick at the top of their respective panels even when scrolling down.") + .RegisterEntry("The application buttons for designs, NPCs or actors should now stick at the top of their respective panels even when scrolling down.") .RegisterHighlight("Randomly chosen designs should now stay across loading screens or redrawing. (1.3.4.3)") - .RegisterEntry( - "In automation, Random designs now have an option to always choose another design, including during loading screens or redrawing.", - 1) + .RegisterEntry("In automation, Random designs now have an option to always choose another design, including during loading screens or redrawing.", 1) .RegisterEntry("Fixed an issue where disabling auto designs did not work as expected.") .RegisterEntry("Fixed the inversion of application flags in IPC calls.") .RegisterEntry("Fixed an issue with the scaling of the Advanced Dye popup with increased font sizes.") @@ -210,18 +93,15 @@ public class GlamourerChangelog => log.NextVersion("Version 1.3.2.0") .RegisterEntry("Fixed an issue with weapon hiding when leaving GPose or changing zones.") .RegisterEntry("Added support for unnamed items to be previewed from Penumbra.") - .RegisterEntry( - "Item combos filters now check if the model string starts with the current filter, instead of checking if the primary ID contains the current filter.") + .RegisterEntry("Item combos filters now check if the model string starts with the current filter, instead of checking if the primary ID contains the current filter.") .RegisterEntry("Improved the handling of bonus items (glasses) in designs.") .RegisterEntry("Imported .chara files now import bonus items.") - .RegisterEntry( - "Added a Debug Data rider in the Actors tab that is visible if Debug Mode is enabled and (currently) contains some IDs.") + .RegisterEntry("Added a Debug Data rider in the Actors tab that is visible if Debug Mode is enabled and (currently) contains some IDs.") .RegisterEntry("Fixed bonus items not reverting correctly in some cases.") .RegisterEntry("Fixed an issue with the RNG in cheat codes and events skipping some possible entries.") .RegisterEntry("Fixed the chat log context menu for glamourer Try-On.") .RegisterEntry("Fixed some issues with cheat code sets.") - .RegisterEntry( - "Made the popped out Advanced Dye Window and Unlocks Window non-docking as that caused issues when docked to the main Glamourer window.") + .RegisterEntry("Made the popped out Advanced Dye Window and Unlocks Window non-docking as that caused issues when docked to the main Glamourer window.") .RegisterEntry("Refreshed NPC name associations.") .RegisterEntry("Removed a now useless cheat code.") .RegisterEntry("Added API for Bonus Items. (1.3.1.1)"); diff --git a/Glamourer/Gui/MainWindow.cs b/Glamourer/Gui/MainWindow.cs index abde603..f21a2b7 100644 --- a/Glamourer/Gui/MainWindow.cs +++ b/Glamourer/Gui/MainWindow.cs @@ -12,7 +12,7 @@ using Glamourer.Gui.Tabs.NpcTab; using Glamourer.Gui.Tabs.SettingsTab; using Glamourer.Gui.Tabs.UnlocksTab; using Glamourer.Interop.Penumbra; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Custom; @@ -102,8 +102,6 @@ public class MainWindow : Window, IDisposable SelectTab = _config.Ephemeral.SelectedTab; _event.Subscribe(OnTabSelected, TabSelected.Priority.MainWindow); IsOpen = _config.OpenWindowAtStart; - - _penumbra.DrawSettingsSection += Settings.DrawPenumbraIntegrationSettings; } public void OpenSettings() @@ -122,10 +120,7 @@ public class MainWindow : Window, IDisposable } public void Dispose() - { - _event.Unsubscribe(OnTabSelected); - _penumbra.DrawSettingsSection -= Settings.DrawPenumbraIntegrationSettings; - } + => _event.Unsubscribe(OnTabSelected); public override void Draw() { diff --git a/Glamourer/Gui/Materials/AdvancedDyePopup.cs b/Glamourer/Gui/Materials/AdvancedDyePopup.cs index 4499107..b842d7f 100644 --- a/Glamourer/Gui/Materials/AdvancedDyePopup.cs +++ b/Glamourer/Gui/Materials/AdvancedDyePopup.cs @@ -1,5 +1,4 @@ using Dalamud.Interface; -using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Render; @@ -8,7 +7,8 @@ using FFXIVClientStructs.Interop; using Glamourer.Designs; using Glamourer.Interop.Material; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; +using OtterGui; using OtterGui.Raii; using OtterGui.Services; using OtterGui.Text; @@ -17,7 +17,6 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Files.MaterialStructs; using Penumbra.GameData.Interop; using Penumbra.String; -using Notification = OtterGui.Classes.Notification; namespace Glamourer.Gui.Materials; @@ -40,6 +39,9 @@ public sealed unsafe class AdvancedDyePopup( private bool ShouldBeDrawn() { + if (!config.UseAdvancedDyes) + return false; + if (_drawIndex is not { Valid: true }) return false; @@ -49,29 +51,28 @@ public sealed unsafe class AdvancedDyePopup( return true; } - public void DrawButton(EquipSlot slot, uint color) - => DrawButton(MaterialValueIndex.FromSlot(slot), color); + public void DrawButton(EquipSlot slot) + => DrawButton(MaterialValueIndex.FromSlot(slot)); - public void DrawButton(BonusItemFlag slot, uint color) - => DrawButton(MaterialValueIndex.FromSlot(slot), color); + public void DrawButton(BonusItemFlag slot) + => DrawButton(MaterialValueIndex.FromSlot(slot)); - private void DrawButton(MaterialValueIndex index, uint color) + private void DrawButton(MaterialValueIndex index) { - if (config.HideDesignPanel.HasFlag(DesignPanelFlag.AdvancedDyes)) + if (!config.UseAdvancedDyes) return; ImGui.SameLine(); - using var id = ImUtf8.PushId(index.SlotIndex | ((int)index.DrawObject << 8)); + using var id = ImRaii.PushId(index.SlotIndex | ((int)index.DrawObject << 8)); var isOpen = index == _drawIndex; - var (textColor, buttonColor) = isOpen - ? (ColorId.HeaderButtons.Value(), ImGui.GetColorU32(ImGuiCol.ButtonActive)) - : (color, 0u); - - using (ImRaii.PushColor(ImGuiCol.Border, textColor, isOpen)) + using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), isOpen) + .Push(ImGuiCol.Text, ColorId.HeaderButtons.Value(), isOpen) + .Push(ImGuiCol.Border, ColorId.HeaderButtons.Value(), isOpen)) { using var frame = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, isOpen); - if (ImUtf8.IconButton(FontAwesomeIcon.Palette, ""u8, default, false, textColor, buttonColor)) + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Palette.ToIconString(), new Vector2(ImGui.GetFrameHeight()), + string.Empty, false, true)) { _forceFocus = true; _selectedMaterial = byte.MaxValue; @@ -79,7 +80,7 @@ public sealed unsafe class AdvancedDyePopup( } } - ImUtf8.HoverTooltip("Open advanced dyes for this slot."u8); + ImGuiUtil.HoverTooltip("Open advanced dyes for this slot."); } private (string Path, string GamePath) ResourceName(MaterialValueIndex index) @@ -91,21 +92,20 @@ public sealed unsafe class AdvancedDyePopup( var modelHandle = model == null ? null : model->ModelResourceHandle; var path = materialHandle == null ? string.Empty - : ByteString.FromSpanUnsafe(materialHandle->FileName.AsSpan(), true).ToString(); + : ByteString.FromSpanUnsafe(materialHandle->ResourceHandle.FileName.AsSpan(), true).ToString(); var gamePath = modelHandle == null ? string.Empty - : modelHandle->GetMaterialFileNameBySlot(index.MaterialIndex).ToString(); + : modelHandle->GetMaterialFileNameBySlotAsString(index.MaterialIndex); return (path, gamePath); } private void DrawTabBar(ReadOnlySpan> textures, ReadOnlySpan> materials, ref bool firstAvailable) { - using var bar = ImUtf8.TabBar("tabs"u8); + using var bar = ImRaii.TabBar("tabs"); if (!bar) return; - var table = new ColorTable.Table(); - var highLightColor = ColorId.AdvancedDyeActive.Value(); + var table = new ColorTable.Table(); for (byte i = 0; i < MaterialService.MaterialsPerModel; ++i) { var index = _drawIndex!.Value with { MaterialIndex = i }; @@ -124,30 +124,17 @@ public sealed unsafe class AdvancedDyePopup( if (available) firstAvailable = false; - var hasAdvancedDyes = _state.Materials.CheckExistenceMaterial(index); - using var c = ImRaii.PushColor(ImGuiCol.Text, highLightColor, hasAdvancedDyes); - using var tab = _label.TabItem(i, select); - c.Pop(); + using var tab = _label.TabItem(i, select); if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { using var enabled = ImRaii.Enabled(); var (path, gamePath) = ResourceName(index); - using var tt = ImUtf8.Tooltip(); - if (gamePath.Length == 0 || path.Length == 0) - ImUtf8.Text("This material does not exist."u8); + ImGui.SetTooltip("This material does not exist."); else if (!available) - ImUtf8.Text($"This material does not have an associated color set.\n\n{gamePath}\n{path}"); + ImGui.SetTooltip($"This material does not have an associated color set.\n\n{gamePath}\n{path}"); else - ImUtf8.Text($"{gamePath}\n{path}"); - - if (hasAdvancedDyes && !available) - { - ImUtf8.Text("\nRight-Click to remove ineffective advanced dyes."u8); - if (ImGui.IsMouseClicked(ImGuiMouseButton.Right)) - for (byte row = 0; row < ColorTable.NumRows; ++row) - stateManager.ResetMaterialValue(_state, index with { RowIndex = row }, ApplySettings.Game); - } + ImGui.SetTooltip($"{gamePath}\n{path}"); } if ((tab.Success || select is ImGuiTabItemFlags.SetSelected) && available) @@ -186,7 +173,7 @@ public sealed unsafe class AdvancedDyePopup( DrawTabBar(textures, materials, ref firstAvailable); if (firstAvailable) - ImUtf8.Text("No Editable Materials available."u8); + ImGui.TextUnformatted("No Editable Materials available."); } private void DrawWindow(ReadOnlySpan> textures, ReadOnlySpan> materials) @@ -210,7 +197,7 @@ public sealed unsafe class AdvancedDyePopup( var width = 7 * ImGui.GetFrameHeight() // Buttons + 3 * ImGui.GetStyle().ItemSpacing.X // around text + 7 * ImGui.GetStyle().ItemInnerSpacing.X - + 200 * ImGuiHelpers.GlobalScale // Drags + + 200 * ImGuiHelpers.GlobalScale // Drags + 7 * UiBuilder.MonoFont.GetCharAdvance(' ') * ImGuiHelpers.GlobalScale // Row + 2 * ImGui.GetStyle().WindowPadding.X; var height = 19 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + 3 * ImGui.GetStyle().ItemSpacing.Y; @@ -264,89 +251,32 @@ public sealed unsafe class AdvancedDyePopup( DrawAllRow(materialIndex, table); } - private static void CopyToClipboard(in ColorTable.Table table) - { - try - { - fixed (ColorTable.Table* ptr = &table) - { - var data = new ReadOnlySpan(ptr, sizeof(ColorTable.Table)); - var base64 = Convert.ToBase64String(data); - ImGui.SetClipboardText(base64); - } - } - catch (Exception ex) - { - Glamourer.Log.Error($"Could not copy color table to clipboard:\n{ex}"); - } - } - - private static bool ImportFromClipboard(out ColorTable.Table table) - { - try - { - var base64 = ImGui.GetClipboardText(); - if (base64.Length > 0) - { - var data = Convert.FromBase64String(base64); - if (sizeof(ColorTable.Table) <= data.Length) - { - table = new ColorTable.Table(); - fixed (ColorTable.Table* tPtr = &table) - { - fixed (byte* ptr = data) - { - new ReadOnlySpan(ptr, sizeof(ColorTable.Table)).CopyTo(new Span(tPtr, sizeof(ColorTable.Table))); - return true; - } - } - } - } - - if (ColorRowClipboard.IsTableSet) - { - table = ColorRowClipboard.Table; - return true; - } - } - catch (Exception ex) - { - Glamourer.Messager.AddMessage(new Notification(ex, "Could not paste color table from clipboard.", - "Could not paste color table from clipboard.", NotificationType.Error)); - } - - table = default; - return false; - } - private void DrawAllRow(MaterialValueIndex materialIndex, in ColorTable.Table table) { using var id = ImRaii.PushId(100); var buttonSize = new Vector2(ImGui.GetFrameHeight()); - ImUtf8.IconButton(FontAwesomeIcon.Crosshairs, "Highlight all affected colors on the character."u8, buttonSize); + ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Crosshairs.ToIconString(), buttonSize, "Highlight all affected colors on the character.", + false, true); if (ImGui.IsItemHovered()) preview.OnHover(materialIndex with { RowIndex = byte.MaxValue }, _actor.Index, table); ImGui.SameLine(); ImGui.AlignTextToFramePadding(); using (ImRaii.PushFont(UiBuilder.MonoFont)) { - ImUtf8.Text("All Color Row Pairs (1-16)"u8); + ImGui.TextUnformatted("All Color Row Pairs (1-16)"); } var spacing = ImGui.GetStyle().ItemInnerSpacing.X; ImGui.SameLine(ImGui.GetWindowSize().X - 3 * buttonSize.X - 2 * spacing - ImGui.GetStyle().WindowPadding.X); - if (ImUtf8.IconButton(FontAwesomeIcon.Clipboard, "Export this table to your clipboard."u8, buttonSize)) - { + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), buttonSize, "Export this table to your clipboard.", false, + true)) ColorRowClipboard.Table = table; - CopyToClipboard(table); - } - ImGui.SameLine(0, spacing); - if (ImUtf8.IconButton(FontAwesomeIcon.Paste, "Import an exported table from your clipboard onto this table."u8, buttonSize) - && ImportFromClipboard(out var newTable)) + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Paste.ToIconString(), buttonSize, + "Import an exported table from your clipboard onto this table.", !ColorRowClipboard.IsTableSet, true)) for (var idx = 0; idx < ColorTable.NumRows; ++idx) { - var row = newTable[idx]; + var row = ColorRowClipboard.Table[idx]; var internalRow = new ColorRow(row); var slot = materialIndex.ToEquipSlot(); var weapon = slot is EquipSlot.MainHand or EquipSlot.OffHand @@ -357,14 +287,15 @@ public sealed unsafe class AdvancedDyePopup( } ImGui.SameLine(0, spacing); - if (ImUtf8.IconButton(FontAwesomeIcon.UndoAlt, "Reset this table to game state."u8, buttonSize, !_anyChanged)) + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this table to game state.", !_anyChanged, + true)) for (byte i = 0; i < ColorTable.NumRows; ++i) stateManager.ResetMaterialValue(_state, materialIndex with { RowIndex = i }, ApplySettings.Game); } private void DrawRow(ref ColorTableRow row, MaterialValueIndex index, in ColorTable.Table table) { - using var id = ImUtf8.PushId(index.RowIndex); + using var id = ImRaii.PushId(index.RowIndex); var changed = _state.Materials.TryGetValue(index, out var value); if (!changed) { @@ -374,9 +305,8 @@ public sealed unsafe class AdvancedDyePopup( { EquipSlot.MainHand => _state.ModelData.Weapon(EquipSlot.MainHand), EquipSlot.OffHand => _state.ModelData.Weapon(EquipSlot.OffHand), - EquipSlot.Unknown => - _state.ModelData.BonusItem((index.SlotIndex - 16u).ToBonusSlot()).Armor().ToWeapon(0), // TODO: Handle better - _ => _state.ModelData.Armor(slot).ToWeapon(0), + EquipSlot.Unknown => _state.ModelData.BonusItem((index.SlotIndex - 16u).ToBonusSlot()).Armor().ToWeapon(0), // TODO: Handle better + _ => _state.ModelData.Armor(slot).ToWeapon(0), }; value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual); } @@ -387,7 +317,8 @@ public sealed unsafe class AdvancedDyePopup( } var buttonSize = new Vector2(ImGui.GetFrameHeight()); - ImUtf8.IconButton(FontAwesomeIcon.Crosshairs, "Highlight the affected colors on the character."u8, buttonSize); + ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Crosshairs.ToIconString(), buttonSize, "Highlight the affected colors on the character.", + false, true); if (ImGui.IsItemHovered()) preview.OnHover(index, _actor.Index, table); @@ -397,28 +328,28 @@ public sealed unsafe class AdvancedDyePopup( { var rowIndex = index.RowIndex / 2 + 1; var rowSuffix = (index.RowIndex & 1) == 0 ? 'A' : 'B'; - ImUtf8.Text($"Row {rowIndex,2}{rowSuffix}"); + ImGui.TextUnformatted($"Row {rowIndex,2}{rowSuffix}"); } ImGui.SameLine(0, ImGui.GetStyle().ItemSpacing.X * 2); - var applied = ImUtf8.ColorPicker("##diffuse"u8, "Change the diffuse value for this row."u8, value.Model.Diffuse, - v => value.Model.Diffuse = v, "D"u8); + var applied = ImGuiUtil.ColorPicker("##diffuse", "Change the diffuse value for this row.", value.Model.Diffuse, + v => value.Model.Diffuse = v, "D"); var spacing = ImGui.GetStyle().ItemInnerSpacing; ImGui.SameLine(0, spacing.X); - applied |= ImUtf8.ColorPicker("##specular"u8, "Change the specular value for this row."u8, value.Model.Specular, - v => value.Model.Specular = v, "S"u8); + applied |= ImGuiUtil.ColorPicker("##specular", "Change the specular value for this row.", value.Model.Specular, + v => value.Model.Specular = v, "S"); ImGui.SameLine(0, spacing.X); - applied |= ImUtf8.ColorPicker("##emissive"u8, "Change the emissive value for this row."u8, value.Model.Emissive, - v => value.Model.Emissive = v, "E"u8); + applied |= ImGuiUtil.ColorPicker("##emissive", "Change the emissive value for this row.", value.Model.Emissive, + v => value.Model.Emissive = v, "E"); ImGui.SameLine(0, spacing.X); if (_mode is not ColorRow.Mode.Dawntrail) { ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale); applied |= DragGloss(ref value.Model.GlossStrength); - ImUtf8.HoverTooltip("Change the gloss strength for this row."u8); + ImGuiUtil.HoverTooltip("Change the gloss strength for this row."); } else { @@ -430,7 +361,7 @@ public sealed unsafe class AdvancedDyePopup( { ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale); applied |= DragSpecularStrength(ref value.Model.SpecularStrength); - ImUtf8.HoverTooltip("Change the specular strength for this row."u8); + ImGuiUtil.HoverTooltip("Change the specular strength for this row."); } else { @@ -438,18 +369,19 @@ public sealed unsafe class AdvancedDyePopup( } ImGui.SameLine(0, spacing.X); - if (ImUtf8.IconButton(FontAwesomeIcon.Clipboard, "Export this row to your clipboard."u8, buttonSize)) + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), buttonSize, "Export this row to your clipboard.", false, + true)) ColorRowClipboard.Row = value.Model; ImGui.SameLine(0, spacing.X); - if (ImUtf8.IconButton(FontAwesomeIcon.Paste, "Import an exported row from your clipboard onto this row."u8, buttonSize, - !ColorRowClipboard.IsSet)) + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Paste.ToIconString(), buttonSize, + "Import an exported row from your clipboard onto this row.", !ColorRowClipboard.IsSet, true)) { value.Model = ColorRowClipboard.Row; applied = true; } ImGui.SameLine(0, spacing.X); - if (ImUtf8.IconButton(FontAwesomeIcon.UndoAlt, "Reset this row to game state."u8, buttonSize, !changed)) + if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this row to game state.", !changed, true)) stateManager.ResetMaterialValue(_state, index, ApplySettings.Game); if (applied) @@ -460,8 +392,7 @@ public sealed unsafe class AdvancedDyePopup( { var tmp = value; var minValue = ImGui.GetIO().KeyCtrl ? 0f : (float)Half.Epsilon; - if (!ImUtf8.DragScalar("##Gloss"u8, ref tmp, "%.1f G"u8, 0.001f, minValue, Math.Max(0.01f, 0.005f * value), - ImGuiSliderFlags.AlwaysClamp)) + if (!ImUtf8.DragScalar("##Gloss"u8, ref tmp, "%.1f G"u8, 0.001f, minValue, Math.Max(0.01f, 0.005f * value), ImGuiSliderFlags.AlwaysClamp)) return false; var tmp2 = Math.Clamp(tmp, minValue, (float)Half.MaxValue); diff --git a/Glamourer/Gui/Materials/MaterialDrawer.cs b/Glamourer/Gui/Materials/MaterialDrawer.cs index 7c16372..1b5e65a 100644 --- a/Glamourer/Gui/Materials/MaterialDrawer.cs +++ b/Glamourer/Gui/Materials/MaterialDrawer.cs @@ -3,7 +3,7 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Glamourer.Designs; using Glamourer.Interop.Material; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Services; using OtterGui.Text; @@ -18,6 +18,7 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config) public const float GlossWidth = 100; public const float SpecularStrengthWidth = 125; + private EquipSlot _newSlot = EquipSlot.Head; private int _newMaterialIdx; private int _newRowIdx; private MaterialValueIndex _newKey = MaterialValueIndex.FromSlot(EquipSlot.Head); @@ -34,10 +35,6 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config) + (GlossWidth + SpecularStrengthWidth) * ImGuiHelpers.GlobalScale + 6 * _spacing + ImUtf8.CalcTextSize("Revert"u8).X; - DrawMultiButtons(design); - ImUtf8.Dummy(0); - ImGui.Separator(); - ImUtf8.Dummy(0); if (available > 1.95 * colorWidth) DrawSingleRow(design); else @@ -45,42 +42,9 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config) DrawNew(design); } - private void DrawMultiButtons(Design design) - { - var any = design.Materials.Count > 0; - var disabled = !_config.DeleteDesignModifier.IsActive(); - var size = new Vector2(200 * ImUtf8.GlobalScale, 0); - if (ImUtf8.ButtonEx("Enable All Advanced Dyes"u8, - any - ? "Enable the application of all contained advanced dyes without deleting them."u8 - : "This design does not contain any advanced dyes."u8, size, - !any || disabled)) - _designManager.ChangeApplyMulti(design, null, null, null, null, null, null, true, null); - ; - if (disabled && any) - ImUtf8.HoverTooltip($"Hold {_config.DeleteDesignModifier} while clicking to enable."); - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Disable All Advanced Dyes"u8, - any - ? "Disable the application of all contained advanced dyes without deleting them."u8 - : "This design does not contain any advanced dyes."u8, size, - !any || disabled)) - _designManager.ChangeApplyMulti(design, null, null, null, null, null, null, false, null); - if (disabled && any) - ImUtf8.HoverTooltip($"Hold {_config.DeleteDesignModifier} while clicking to disable."); - - if (ImUtf8.ButtonEx("Delete All Advanced Dyes"u8, any ? ""u8 : "This design does not contain any advanced dyes."u8, size, - !any || disabled)) - while (design.Materials.Count > 0) - _designManager.ChangeMaterialValue(design, MaterialValueIndex.FromKey(design.Materials[0].Item1), null); - - if (disabled && any) - ImUtf8.HoverTooltip($"Hold {_config.DeleteDesignModifier} while clicking to delete."); - } - private void DrawName(MaterialValueIndex index) { - using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0.05f, 0.5f)); + using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f)); ImUtf8.TextFramed(index.ToString(), 0, new Vector2((GlossWidth + SpecularStrengthWidth) * ImGuiHelpers.GlobalScale + _spacing, 0), borderColor: ImGui.GetColorU32(ImGuiCol.Text)); } @@ -175,44 +139,16 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config) "If this is checked, Glamourer will try to revert the advanced dye row to its game state instead of applying a specific row."u8); } - public sealed class MaterialSlotCombo; - - private void DrawSlotCombo() - { - var width = ImUtf8.CalcTextSize(EquipSlot.OffHand.ToName()).X + ImGui.GetFrameHeightWithSpacing(); - ImGui.SetNextItemWidth(width); - using var combo = ImUtf8.Combo("##slot"u8, _newKey.SlotName()); - if (combo) - { - var currentSlot = _newKey.ToEquipSlot(); - foreach (var tmpSlot in EquipSlotExtensions.FullSlots) - { - if (ImUtf8.Selectable(tmpSlot.ToName(), tmpSlot == currentSlot) && currentSlot != tmpSlot) - _newKey = MaterialValueIndex.FromSlot(tmpSlot) with - { - MaterialIndex = (byte)_newMaterialIdx, - RowIndex = (byte)_newRowIdx, - }; - } - - var currentBonus = _newKey.ToBonusSlot(); - foreach (var bonusSlot in BonusExtensions.AllFlags) - { - if (ImUtf8.Selectable(bonusSlot.ToName(), bonusSlot == currentBonus) && bonusSlot != currentBonus) - _newKey = MaterialValueIndex.FromSlot(bonusSlot) with - { - MaterialIndex = (byte)_newMaterialIdx, - RowIndex = (byte)_newRowIdx, - }; - } - } - - ImUtf8.HoverTooltip("Choose a slot for an advanced dye row."u8); - } + public sealed class MaterialSlotCombo; public void DrawNew(Design design) { - DrawSlotCombo(); + if (EquipSlotCombo.Draw("##slot", "Choose a slot for an advanced dye row.", ref _newSlot)) + _newKey = MaterialValueIndex.FromSlot(_newSlot) with + { + MaterialIndex = (byte)_newMaterialIdx, + RowIndex = (byte)_newRowIdx, + }; ImUtf8.SameLineInner(); DrawMaterialIdxDrag(); ImUtf8.SameLineInner(); @@ -229,27 +165,22 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config) { ImGui.SetNextItemWidth(ImUtf8.CalcTextSize("Material AA"u8).X); var format = $"Material {(char)('A' + _newMaterialIdx)}"; - if (ImUtf8.DragScalar("##Material"u8, ref _newMaterialIdx, format, 0, MaterialService.MaterialsPerModel - 1, 0.01f, - ImGuiSliderFlags.NoInput)) + if (ImUtf8.DragScalar("##Material"u8, ref _newMaterialIdx, format, 0, MaterialService.MaterialsPerModel - 1, 0.01f)) { _newMaterialIdx = Math.Clamp(_newMaterialIdx, 0, MaterialService.MaterialsPerModel - 1); _newKey = _newKey with { MaterialIndex = (byte)_newMaterialIdx }; } - - ImUtf8.HoverTooltip("Drag this to the left or right to change its value."u8); } private void DrawRowIdxDrag() { ImGui.SetNextItemWidth(ImUtf8.CalcTextSize("Row 0000"u8).X); var format = $"Row {_newRowIdx / 2 + 1}{(char)(_newRowIdx % 2 + 'A')}"; - if (ImUtf8.DragScalar("##Row"u8, ref _newRowIdx, format, 0, ColorTable.NumRows - 1, 0.01f, ImGuiSliderFlags.NoInput)) + if (ImUtf8.DragScalar("##Row"u8, ref _newRowIdx, format, 0, ColorTable.NumRows - 1, 0.01f)) { _newRowIdx = Math.Clamp(_newRowIdx, 0, ColorTable.NumRows - 1); _newKey = _newKey with { RowIndex = (byte)_newRowIdx }; } - - ImUtf8.HoverTooltip("Drag this to the left or right to change its value."u8); } private void DrawRow(Design design, MaterialValueIndex index, in ColorRow row, bool disabled) diff --git a/Glamourer/Gui/PenumbraChangedItemTooltip.cs b/Glamourer/Gui/PenumbraChangedItemTooltip.cs index dff9a6e..68ba18e 100644 --- a/Glamourer/Gui/PenumbraChangedItemTooltip.cs +++ b/Glamourer/Gui/PenumbraChangedItemTooltip.cs @@ -1,40 +1,39 @@ using Glamourer.Designs; using Glamourer.Events; +using Glamourer.Interop; using Glamourer.Interop.Penumbra; using Glamourer.Services; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Raii; using Penumbra.Api.Enums; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Gui; public sealed class PenumbraChangedItemTooltip : IDisposable { - private readonly PenumbraService _penumbra; - private readonly StateManager _stateManager; - private readonly ItemManager _items; - private readonly ActorObjectManager _objects; - private readonly CustomizeService _customize; - private readonly GPoseService _gpose; + private readonly PenumbraService _penumbra; + private readonly StateManager _stateManager; + private readonly ItemManager _items; + private readonly ObjectManager _objects; + private readonly CustomizeService _customize; + private readonly GPoseService _gpose; - private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2 + BonusExtensions.AllFlags.Count]; + private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2]; - public IEnumerable> LastItems - => EquipSlotExtensions.EqdpSlots.Cast().Append(EquipSlot.MainHand).Append(EquipSlot.OffHand) - .Concat(BonusExtensions.AllFlags.Cast()).Zip(_lastItems) - .Select(p => new KeyValuePair(p.First, p.Second)); + public IEnumerable> LastItems + => EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand).Zip(_lastItems) + .Select(p => new KeyValuePair(p.First, p.Second)); public ChangedItemType LastType { get; private set; } = ChangedItemType.None; - public uint LastId { get; private set; } + public uint LastId { get; private set; } = 0; public DateTime LastTooltip { get; private set; } = DateTime.MinValue; public DateTime LastClick { get; private set; } = DateTime.MinValue; - public PenumbraChangedItemTooltip(PenumbraService penumbra, StateManager stateManager, ItemManager items, ActorObjectManager objects, + public PenumbraChangedItemTooltip(PenumbraService penumbra, StateManager stateManager, ItemManager items, ObjectManager objects, CustomizeService customize, GPoseService gpose) { _penumbra = penumbra; @@ -73,21 +72,6 @@ public sealed class PenumbraChangedItemTooltip : IDisposable if (!Player()) return; - var bonusSlot = item.Type.ToBonus(); - if (bonusSlot is not BonusItemFlag.Unknown) - { - // + 2 due to weapons. - var glasses = _lastItems[bonusSlot.ToSlot() + 2]; - using (_ = !openTooltip ? null : ImRaii.Tooltip()) - { - ImGui.TextUnformatted($"{prefix}Right-Click to apply to current actor."); - if (glasses.Valid) - ImGui.TextUnformatted($"{prefix}Control + Right-Click to re-apply {glasses.Name} to current actor."); - } - - return; - } - var slot = item.Type.ToSlot(); var last = _lastItems[slot.ToIndex()]; switch (slot) @@ -125,27 +109,6 @@ public sealed class PenumbraChangedItemTooltip : IDisposable public void ApplyItem(ActorState state, EquipItem item) { - var bonusSlot = item.Type.ToBonus(); - if (bonusSlot is not BonusItemFlag.Unknown) - { - // + 2 due to weapons. - var glasses = _lastItems[bonusSlot.ToSlot() + 2]; - if (ImGui.GetIO().KeyCtrl && glasses.Valid) - { - Glamourer.Log.Debug($"Re-Applying {glasses.Name} to {bonusSlot.ToName()}."); - SetLastItem(bonusSlot, default, state); - _stateManager.ChangeBonusItem(state, bonusSlot, glasses, ApplySettings.Manual); - } - else - { - Glamourer.Log.Debug($"Applying {item.Name} to {bonusSlot.ToName()}."); - SetLastItem(bonusSlot, item, state); - _stateManager.ChangeBonusItem(state, bonusSlot, item, ApplySettings.Manual); - } - - return; - } - var slot = item.Type.ToSlot(); var last = _lastItems[slot.ToIndex()]; switch (slot) @@ -302,22 +265,7 @@ public sealed class PenumbraChangedItemTooltip : IDisposable { var oldItem = state.ModelData.Item(slot); if (oldItem.Id != item.Id) - last = oldItem; - } - } - - private void SetLastItem(BonusItemFlag slot, EquipItem item, ActorState state) - { - ref var last = ref _lastItems[slot.ToSlot() + 2]; - if (!item.Valid) - { - last = default; - } - else - { - var oldItem = state.ModelData.BonusItem(slot); - if (oldItem.Id != item.Id) - last = oldItem; + _lastItems[slot.ToIndex()] = oldItem; } } diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs index 224154b..265e1d9 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorPanel.cs @@ -10,8 +10,9 @@ using Glamourer.Gui.Customization; using Glamourer.Gui.Equipment; using Glamourer.Gui.Materials; using Glamourer.Interop; +using Glamourer.Interop.Structs; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Raii; @@ -21,6 +22,7 @@ using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.Gui.Tabs.ActorTab; @@ -33,7 +35,7 @@ public class ActorPanel private readonly AutoDesignApplier _autoDesignApplier; private readonly Configuration _config; private readonly DesignConverter _converter; - private readonly ActorObjectManager _objects; + private readonly ObjectManager _objects; private readonly DesignManager _designManager; private readonly ImportService _importService; private readonly ICondition _conditions; @@ -51,13 +53,13 @@ public class ActorPanel AutoDesignApplier autoDesignApplier, Configuration config, DesignConverter converter, - ActorObjectManager objects, + ObjectManager objects, DesignManager designManager, ImportService importService, ICondition conditions, DictModelChara modelChara, CustomizeParameterDrawer parameterDrawer, - AdvancedDyePopup advancedDyes, + AdvancedDyePopup advancedDyes, EditorHistory editorHistory) { _selector = selector; @@ -85,7 +87,7 @@ public class ActorPanel _rightButtons = [ new LockedButton(this), - new HeaderDrawer.IncognitoButton(_config), + new HeaderDrawer.IncognitoButton(_config.Ephemeral), ]; } @@ -104,7 +106,7 @@ public class ActorPanel { using var group = ImRaii.Group(); (_identifier, _data) = _selector.Selection; - _lockedRedraw = _identifier.Type is IdentifierType.Special || _objects.IsInLobby + _lockedRedraw = _identifier.Type is IdentifierType.Special || _conditions[ConditionFlag.OccupiedInCutSceneEvent]; (_actorName, _actor) = GetHeaderName(); DrawHeader(); @@ -155,7 +157,6 @@ public class ActorPanel using var table = ImUtf8.Table("##Panel", 1, ImGuiTableFlags.BordersOuter | ImGuiTableFlags.ScrollY, ImGui.GetContentRegionAvail()); if (!table || !_selector.HasSelection || !_stateManager.GetOrCreate(_identifier, _actor, out _state)) return; - ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableNextColumn(); ImGui.Dummy(Vector2.Zero); @@ -190,14 +191,10 @@ public class ActorPanel private void DrawCustomizationsHeader() { - if (_config.HideDesignPanel.HasFlag(DesignPanelFlag.Customization)) - return; - var header = _state!.ModelData.ModelId == 0 ? "Customization" : $"Customization (Model Id #{_state.ModelData.ModelId})###Customization"; - var expand = _config.AutoExpandDesignPanel.HasFlag(DesignPanelFlag.Customization); - using var h = ImUtf8.CollapsingHeaderId(header, expand ? ImGuiTreeNodeFlags.DefaultOpen : ImGuiTreeNodeFlags.None); + using var h = ImUtf8.CollapsingHeaderId(header); if (!h) return; @@ -210,7 +207,7 @@ public class ActorPanel private void DrawEquipmentHeader() { - using var h = DesignPanelFlag.Equipment.Header(_config); + using var h = ImUtf8.CollapsingHeaderId("Equipment"u8); if (!h) return; @@ -238,12 +235,14 @@ public class ActorPanel ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); DrawEquipmentMetaToggles(); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); - _equipmentDrawer.DrawDragDropTooltip(); } private void DrawParameterHeader() { - using var h = DesignPanelFlag.AdvancedCustomizations.Header(_config); + if (!_config.UseAdvancedParameters) + return; + + using var h = ImUtf8.CollapsingHeaderId("Advanced Customizations"u8); if (!h) return; @@ -255,7 +254,7 @@ public class ActorPanel if (!_config.DebugMode) return; - using var h = DesignPanelFlag.DebugData.Header(_config); + using var h = ImUtf8.CollapsingHeaderId("Debug Data"u8); if (!h) return; @@ -305,12 +304,6 @@ public class ActorPanel EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!)); EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _stateManager, _state!)); } - - ImGui.SameLine(); - using (_ = ImRaii.Group()) - { - EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.EarState, _stateManager, _state!)); - } } private void DrawMonsterPanel() @@ -392,7 +385,7 @@ public class ActorPanel { if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.", _state!.IsLocked)) - _stateManager.ResetState(_state!, StateSource.Manual, isFinal: true); + _stateManager.ResetState(_state!, StateSource.Manual); ImGui.SameLine(); @@ -400,8 +393,8 @@ public class ActorPanel "Reapply the current automation state for the character on top of its current state..", !_config.EnableAutoDesigns || _state!.IsLocked)) { - _autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, false, false, out var forcedRedraw); - _stateManager.ReapplyAutomationState(_actor, forcedRedraw, false, StateSource.Manual); + _autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, false, out var forcedRedraw); + _stateManager.ReapplyState(_actor, forcedRedraw, StateSource.Manual); } ImGui.SameLine(); @@ -409,15 +402,15 @@ public class ActorPanel "Try to revert the character to the state it would have using automated designs.", !_config.EnableAutoDesigns || _state!.IsLocked)) { - _autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, true, false, out var forcedRedraw); - _stateManager.ReapplyAutomationState(_actor, forcedRedraw, true, StateSource.Manual); + _autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!, true, out var forcedRedraw); + _stateManager.ReapplyState(_actor, forcedRedraw, StateSource.Manual); } ImGui.SameLine(); if (ImGuiUtil.DrawDisabledButton("Reapply", Vector2.Zero, "Try to reapply the configured state if something went wrong. Should generally not be necessary.", _state!.IsLocked)) - _stateManager.ReapplyState(_actor, false, StateSource.Manual, true); + _stateManager.ReapplyState(_actor, false, StateSource.Manual); } private void DrawApplyToSelf() @@ -430,7 +423,7 @@ public class ActorPanel if (_stateManager.GetOrCreate(id, data.Objects[0], out var state)) _stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)), - ApplySettings.Manual with { IsFinal = true }); + ApplySettings.Manual); } private void DrawApplyToTarget() @@ -447,7 +440,7 @@ public class ActorPanel if (_stateManager.GetOrCreate(id, data.Objects[0], out var state)) _stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)), - ApplySettings.Manual with { IsFinal = true }); + ApplySettings.Manual); } @@ -474,7 +467,7 @@ public class ActorPanel var text = ImGui.GetClipboardText(); var design = panel._converter.FromBase64(text, applyCustomize, applyGear, out _) ?? throw new Exception("The clipboard did not contain valid data."); - panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks with { IsFinal = true }); + panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks); } catch (Exception ex) { diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs b/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs index 7d132a1..76f0ba4 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorSelector.cs @@ -1,17 +1,19 @@ -using Dalamud.Interface; -using Dalamud.Bindings.ImGui; +using System.Security.AccessControl; +using Dalamud.Interface; +using Glamourer.Interop; +using Glamourer.Interop.Structs; +using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Raii; using OtterGui.Text; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Gui.Tabs.ActorTab; -public class ActorSelector(ActorObjectManager objects, ActorManager actors, EphemeralConfig config) +public class ActorSelector(ObjectManager objects, ActorManager actors, EphemeralConfig config) { private ActorIdentifier _identifier = ActorIdentifier.Invalid; @@ -87,11 +89,11 @@ public class ActorSelector(ActorObjectManager objects, ActorManager actors, Ephe if (!child) return; + objects.Update(); _world = new WorldId(objects.Player.Valid ? objects.Player.HomeWorld : (ushort)0); - using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing); - var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeight()); - var remainder = ImGuiClip.FilteredClippedDraw(objects.Where(p => p.Value.Objects.Any(a => a.Model)), skips, CheckFilter, - DrawSelectable); + using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing); + var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeight()); + var remainder = ImGuiClip.FilteredClippedDraw(objects.Identifiers, skips, CheckFilter, DrawSelectable); ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight()); } diff --git a/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs b/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs index 9751a71..4e5e15c 100644 --- a/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs +++ b/Glamourer/Gui/Tabs/ActorTab/ActorTab.cs @@ -1,5 +1,5 @@ using Dalamud.Interface.Utility; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Widgets; namespace Glamourer.Gui.Tabs.ActorTab; diff --git a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs index da3b636..831ee7c 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs @@ -1,5 +1,5 @@ using Dalamud.Interface.Utility; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Widgets; namespace Glamourer.Gui.Tabs.AutomationTab; diff --git a/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs b/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs index 1d3e711..530e04a 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs @@ -1,12 +1,11 @@ using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Utility; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; +using OtterGui.Custom; using OtterGui.Log; using OtterGui.Widgets; using Penumbra.GameData.DataContainers; -using OtterGui.Custom; namespace Glamourer.Gui.Tabs.AutomationTab; diff --git a/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs b/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs index ba2e424..b197a1a 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs @@ -1,5 +1,5 @@ using Dalamud.Game.ClientState.Objects.Enums; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Gui; diff --git a/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs b/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs index 8eba59b..e7efc09 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs @@ -4,7 +4,7 @@ using Glamourer.Automation; using Glamourer.Designs; using Glamourer.Designs.Special; using Glamourer.Events; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Services; @@ -278,7 +278,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable private void LookupTooltip(IEnumerable designs) { using var _ = ImRaii.Tooltip(); - var tt = string.Join('\n', designs.Select(d => _designFileSystem.TryGetValue(d, out var l) ? l.FullName() : d.Name.Text).OrderBy(t => t)); + var tt = string.Join('\n', designs.Select(d => _designFileSystem.FindLeaf(d, out var l) ? l.FullName() : d.Name.Text).OrderBy(t => t)); ImGui.TextUnformatted(tt.Length == 0 ? "Matches no currently existing designs." : "Matches the following designs:"); diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs index 8a85a45..924f822 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs @@ -6,9 +6,8 @@ using Glamourer.Designs.Special; using Glamourer.Interop; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; using OtterGui.Log; using OtterGui.Raii; using OtterGui.Text; @@ -31,10 +30,10 @@ public class SetPanel( Configuration _config, RandomRestrictionDrawer _randomDrawer) { - private readonly JobGroupCombo _jobGroupCombo = new(_manager, _jobs, Glamourer.Log); - private readonly HeaderDrawer.Button[] _rightButtons = [new HeaderDrawer.IncognitoButton(_config)]; - private string? _tempName; - private int _dragIndex = -1; + private readonly JobGroupCombo _jobGroupCombo = new(_manager, _jobs, Glamourer.Log); + private readonly HeaderDrawer.Button[] _rightButtons = [new HeaderDrawer.IncognitoButton(_config.Ephemeral)]; + private string? _tempName; + private int _dragIndex = -1; private Action? _endAction; @@ -53,59 +52,44 @@ public class SetPanel( private void DrawPanel() { - using var child = ImUtf8.Child("##Panel"u8, -Vector2.One, true); + using var child = ImRaii.Child("##Panel", -Vector2.One, true); if (!child || !_selector.HasSelection) return; var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y }; - using (ImUtf8.Group()) + using (_ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) { - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) - { - var enabled = Selection.Enabled; - if (ImUtf8.Checkbox("##Enabled"u8, ref enabled)) - _manager.SetState(_selector.SelectionIndex, enabled); - ImUtf8.LabeledHelpMarker("Enabled"u8, - "Whether the designs in this set should be applied at all. Only one set can be enabled for a character at the same time."u8); - } - - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) - { - var useGame = _selector.Selection!.BaseState is AutoDesignSet.Base.Game; - if (ImUtf8.Checkbox("##gameState"u8, ref useGame)) - _manager.ChangeBaseState(_selector.SelectionIndex, useGame ? AutoDesignSet.Base.Game : AutoDesignSet.Base.Current); - ImUtf8.LabeledHelpMarker("Use Game State as Base"u8, - "When this is enabled, the designs matching conditions will be applied successively on top of what your character is supposed to look like for the game. "u8 - + "Otherwise, they will be applied on top of the characters actual current look using Glamourer."u8); - } + var enabled = Selection.Enabled; + if (ImGui.Checkbox("##Enabled", ref enabled)) + _manager.SetState(_selector.SelectionIndex, enabled); + ImGuiUtil.LabeledHelpMarker("Enabled", + "Whether the designs in this set should be applied at all. Only one set can be enabled for a character at the same time."); } ImGui.SameLine(); - using (ImUtf8.Group()) + using (_ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) { - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) - { - var editing = _config.ShowAutomationSetEditing; - if (ImUtf8.Checkbox("##Show Editing"u8, ref editing)) - { - _config.ShowAutomationSetEditing = editing; - _config.Save(); - } + var useGame = _selector.Selection!.BaseState is AutoDesignSet.Base.Game; + if (ImGui.Checkbox("##gameState", ref useGame)) + _manager.ChangeBaseState(_selector.SelectionIndex, useGame ? AutoDesignSet.Base.Game : AutoDesignSet.Base.Current); + ImGuiUtil.LabeledHelpMarker("Use Game State as Base", + "When this is enabled, the designs matching conditions will be applied successively on top of what your character is supposed to look like for the game. " + + "Otherwise, they will be applied on top of the characters actual current look using Glamourer."); + } - ImUtf8.LabeledHelpMarker("Show Editing"u8, - "Show options to change the name or the associated character or NPC of this design set."u8); + ImGui.SameLine(); + using (_ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) + { + var editing = _config.ShowAutomationSetEditing; + if (ImGui.Checkbox("##Show Editing", ref editing)) + { + _config.ShowAutomationSetEditing = editing; + _config.Save(); } - using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) - { - var resetSettings = _selector.Selection!.ResetTemporarySettings; - if (ImGui.Checkbox("##resetSettings", ref resetSettings)) - _manager.ChangeResetSettings(_selector.SelectionIndex, resetSettings); - - ImUtf8.LabeledHelpMarker("Reset Temporary Settings"u8, - "Always reset all temporary settings applied by Glamourer when this automation set is applied, regardless of active designs."u8); - } + ImGuiUtil.LabeledHelpMarker("Show Editing", + "Show options to change the name or the associated character or NPC of this design set."); } if (_config.ShowAutomationSetEditing) @@ -161,43 +145,42 @@ public class SetPanel( (false, false) => 4, }; - using var table = ImUtf8.Table("SetTable"u8, numRows, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollX | ImGuiTableFlags.ScrollY); + using var table = ImRaii.Table("SetTable", numRows, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollX | ImGuiTableFlags.ScrollY); if (!table) return; - ImUtf8.TableSetupColumn("##del"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight()); - ImUtf8.TableSetupColumn("##Index"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("##del", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight()); + ImGui.TableSetupColumn("##Index", ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale); if (singleRow) { - ImUtf8.TableSetupColumn("Design"u8, ImGuiTableColumnFlags.WidthFixed, 220 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthFixed, 220 * ImGuiHelpers.GlobalScale); if (_config.ShowAllAutomatedApplicationRules) - ImUtf8.TableSetupColumn("Application"u8, ImGuiTableColumnFlags.WidthFixed, + ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed, 6 * ImGui.GetFrameHeight() + 10 * ImGuiHelpers.GlobalScale); else - ImUtf8.TableSetupColumn("Use"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X); + ImGui.TableSetupColumn("Use", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X); } else { - ImUtf8.TableSetupColumn("Design / Job Restrictions"u8, ImGuiTableColumnFlags.WidthFixed, - 250 * ImGuiHelpers.GlobalScale - (ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ScrollbarSize : 0)); + ImGui.TableSetupColumn("Design / Job Restrictions", ImGuiTableColumnFlags.WidthFixed, 250 * ImGuiHelpers.GlobalScale); if (_config.ShowAllAutomatedApplicationRules) - ImUtf8.TableSetupColumn("Application"u8, ImGuiTableColumnFlags.WidthFixed, + ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed, 3 * ImGui.GetFrameHeight() + 4 * ImGuiHelpers.GlobalScale); else - ImUtf8.TableSetupColumn("Use"u8, ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X); + ImGui.TableSetupColumn("Use", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X); } if (singleRow) - ImUtf8.TableSetupColumn("Job Restrictions"u8, ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Job Restrictions", ImGuiTableColumnFlags.WidthStretch); if (_config.ShowUnlockedItemWarnings) - ImUtf8.TableSetupColumn(""u8, ImGuiTableColumnFlags.WidthFixed, 2 * ImGui.GetFrameHeight() + 4 * ImGuiHelpers.GlobalScale); + ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthFixed, 2 * ImGui.GetFrameHeight() + 4 * ImGuiHelpers.GlobalScale); ImGui.TableHeadersRow(); foreach (var (design, idx) in Selection.Designs.WithIndex()) { - using var id = ImUtf8.PushId(idx); + using var id = ImRaii.PushId(idx); ImGui.TableNextColumn(); var keyValid = _config.DeleteDesignModifier.IsActive(); var tt = keyValid @@ -207,8 +190,8 @@ public class SetPanel( if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), new Vector2(ImGui.GetFrameHeight()), tt, !keyValid, true)) _endAction = () => _manager.DeleteDesign(Selection, idx); ImGui.TableNextColumn(); - DrawSelectable(idx, design.Design); - + ImGui.Selectable($"#{idx + 1:D2}"); + DrawDragDrop(Selection, idx); ImGui.TableNextColumn(); DrawRandomEditing(Selection, design, idx); _designCombo.Draw(Selection, design, idx); @@ -236,7 +219,8 @@ public class SetPanel( ImGui.TableNextColumn(); ImGui.TableNextColumn(); - ImUtf8.TextFrameAligned("New"u8); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted("New"); ImGui.TableNextColumn(); _designCombo.Draw(Selection, null, -1); ImGui.TableNextRow(); @@ -245,64 +229,26 @@ public class SetPanel( _endAction = null; } - private void DrawSelectable(int idx, IDesignStandIn design) - { - var highlight = 0u; - var sb = new StringBuilder(); - if (design is Design d) - { - var count = design.AllLinks(true).Count(); - if (count > 1) - { - sb.AppendLine($"This design contains {count - 1} links to other designs."); - highlight = ColorId.HeaderButtons.Value(); - } - - count = d.AssociatedMods.Count; - if (count > 0) - { - sb.AppendLine($"This design contains {count} mod associations."); - highlight = ColorId.ModdedItemMarker.Value(); - } - - count = design.GetMaterialData().Count(p => p.Item2.Enabled); - if (count > 0) - { - sb.AppendLine($"This design contains {count} enabled advanced dyes."); - highlight = ColorId.AdvancedDyeActive.Value(); - } - } - - using (ImRaii.PushColor(ImGuiCol.Text, highlight, highlight != 0)) - { - ImUtf8.Selectable($"#{idx + 1:D2}"); - } - - ImUtf8.HoverTooltip($"{sb}"); - - DrawDragDrop(Selection, idx); - } - private int _tmpGearset = int.MaxValue; private int _whichIndex = -1; private void DrawConditions(AutoDesign design, int idx) { var usingGearset = design.GearsetIndex >= 0; - if (ImUtf8.Button($"{(usingGearset ? "Gearset:" : "Jobs:")}##usingGearset")) + if (ImGui.Button($"{(usingGearset ? "Gearset:" : "Jobs:")}##usingGearset")) { usingGearset = !usingGearset; _manager.ChangeGearsetCondition(Selection, idx, (short)(usingGearset ? 0 : -1)); } - ImUtf8.HoverTooltip("Click to switch between Job and Gearset restrictions."u8); + ImGuiUtil.HoverTooltip("Click to switch between Job and Gearset restrictions."); ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); if (usingGearset) { var set = 1 + (_tmpGearset == int.MaxValue || _whichIndex != idx ? design.GearsetIndex : _tmpGearset); ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImUtf8.InputScalar("##whichGearset"u8, ref set)) + if (ImGui.InputInt("##whichGearset", ref set, 0, 0)) { _whichIndex = idx; _tmpGearset = Math.Clamp(set, 1, 100); @@ -400,12 +346,12 @@ public class SetPanel( ImGuiUtil.DrawTextButton(FontAwesomeIcon.ExclamationCircle.ToIconString(), size, color); } - ImUtf8.HoverTooltip($"{sb}"); + ImGuiUtil.HoverTooltip(sb.ToString()); } else { ImGuiUtil.DrawTextButton(string.Empty, size, 0); - ImUtf8.HoverTooltip(good); + ImGuiUtil.HoverTooltip(good); } } } @@ -413,7 +359,7 @@ public class SetPanel( private void DrawDragDrop(AutoDesignSet set, int index) { const string dragDropLabel = "DesignDragDrop"; - using (var target = ImUtf8.DragDropTarget()) + using (var target = ImRaii.DragDropTarget()) { if (target.Success && ImGuiUtil.IsDropping(dragDropLabel)) { @@ -427,15 +373,15 @@ public class SetPanel( } } - using (var source = ImUtf8.DragDropSource()) + using (var source = ImRaii.DragDropSource()) { if (source) { - ImUtf8.Text($"Moving design #{index + 1:D2}..."); - if (ImGui.SetDragDropPayload(dragDropLabel, null, 0)) + ImGui.TextUnformatted($"Moving design #{index + 1:D2}..."); + if (ImGui.SetDragDropPayload(dragDropLabel, nint.Zero, 0)) { _dragIndex = index; - _selector.DragDesignIndex = index; + _selector._dragDesignIndex = index; } } } @@ -454,16 +400,16 @@ public class SetPanel( } style.Pop(); - ImUtf8.HoverTooltip("Toggle all application modes at once."u8); + ImGuiUtil.HoverTooltip("Toggle all application modes at once."); if (_config.ShowAllAutomatedApplicationRules) { void Box(int idx) { var (type, description) = ApplicationTypeExtensions.Types[idx]; var value = design.Type.HasFlag(type); - if (ImUtf8.Checkbox($"##{(byte)type}", ref value)) + if (ImGui.Checkbox($"##{(byte)type}", ref value)) newType = value ? newType | type : newType & ~type; - ImUtf8.HoverTooltip(description); + ImGuiUtil.HoverTooltip(description); } ImGui.SameLine(); diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs index 8a235ae..950b735 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs @@ -2,12 +2,12 @@ using Dalamud.Interface.Utility; using Glamourer.Automation; using Glamourer.Events; -using Dalamud.Bindings.ImGui; +using Glamourer.Interop; +using ImGuiNET; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Raii; -using Penumbra.GameData.Interop; +using Penumbra.GameData.Actors; using Penumbra.String; using ImGuiClip = OtterGui.ImGuiClip; @@ -18,7 +18,8 @@ public class SetSelector : IDisposable private readonly Configuration _config; private readonly AutoDesignManager _manager; private readonly AutomationChanged _event; - private readonly ActorObjectManager _objects; + private readonly ActorManager _actors; + private readonly ObjectManager _objects; private readonly List<(AutoDesignSet, int)> _list = []; public AutoDesignSet? Selection { get; private set; } @@ -37,13 +38,14 @@ public class SetSelector : IDisposable private int _dragIndex = -1; private Action? _endAction; - internal int DragDesignIndex = -1; + internal int _dragDesignIndex = -1; - public SetSelector(AutoDesignManager manager, AutomationChanged @event, Configuration config, ActorObjectManager objects) + public SetSelector(AutoDesignManager manager, AutomationChanged @event, Configuration config, ActorManager actors, ObjectManager objects) { _manager = manager; _event = @event; _config = config; + _actors = actors; _objects = objects; _event.Subscribe(OnAutomationChange, AutomationChanged.Priority.SetSelector); } @@ -92,7 +94,7 @@ public class SetSelector : IDisposable } private LowerString _filter = LowerString.Empty; - private uint _enabledFilter; + private uint _enabledFilter = 0; private float _width; private Vector2 _defaultItemSpacing; private Vector2 _selectableSize; @@ -144,7 +146,7 @@ public class SetSelector : IDisposable ImGui.SameLine(); var f = _enabledFilter; - if (ImGui.CheckboxFlags("##enabledFilter", ref f, 3u)) + if (ImGui.CheckboxFlags("##enabledFilter", ref f, 3)) { _enabledFilter = _enabledFilter switch { @@ -175,6 +177,7 @@ public class SetSelector : IDisposable UpdateList(); using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing); _selectableSize = new Vector2(0, 2 * ImGui.GetTextLineHeight() + ImGui.GetStyle().ItemSpacing.Y); + _objects.Update(); ImGuiClip.ClippedDraw(_list, DrawSetSelectable, _selectableSize.Y + 2 * ImGui.GetStyle().ItemSpacing.Y); _endAction?.Invoke(); _endAction = null; @@ -183,7 +186,7 @@ public class SetSelector : IDisposable private void DrawSetSelectable((AutoDesignSet Set, int Index) pair) { using var id = ImRaii.PushId(pair.Index); - using (ImRaii.PushColor(ImGuiCol.Text, pair.Set.Enabled ? ColorId.EnabledAutoSet.Value() : ColorId.DisabledAutoSet.Value())) + using (var color = ImRaii.PushColor(ImGuiCol.Text, pair.Set.Enabled ? ColorId.EnabledAutoSet.Value() : ColorId.DisabledAutoSet.Value())) { if (ImGui.Selectable(GetSetName(pair.Set, pair.Index), pair.Set == Selection, ImGuiSelectableFlags.None, _selectableSize)) { @@ -282,9 +285,9 @@ public class SetSelector : IDisposable private void NewSetButton(Vector2 size) { - var id = _objects.Actors.GetCurrentPlayer(); + var id = _actors.GetCurrentPlayer(); if (!id.IsValid) - id = _objects.Actors.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue); + id = _actors.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue); if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size, $"Create a new Automatic Design Set for {id}. The associated player can be changed later.", !id.IsValid, true)) _manager.AddDesignSet("New Automation Set", id); @@ -329,15 +332,15 @@ public class SetSelector : IDisposable } else if (ImGuiUtil.IsDropping("DesignDragDrop")) { - if (DragDesignIndex >= 0) + if (_dragDesignIndex >= 0) { - var idx = DragDesignIndex; + var idx = _dragDesignIndex; var setTo = set; var setFrom = Selection!; _endAction = () => _manager.MoveDesignToSet(setFrom, idx, setTo); } - DragDesignIndex = -1; + _dragDesignIndex = -1; } } } @@ -347,7 +350,7 @@ public class SetSelector : IDisposable if (source) { ImGui.TextUnformatted($"Moving design set {GetSetName(set, index)} from position {index + 1}..."); - if (ImGui.SetDragDropPayload(dragDropLabel, null, 0)) + if (ImGui.SetDragDropPayload(dragDropLabel, nint.Zero, 0)) _dragIndex = index; } } diff --git a/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs b/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs index 35642a7..f5fe088 100644 --- a/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs @@ -1,17 +1,18 @@ using Dalamud.Interface; using Glamourer.GameData; using Glamourer.Designs; +using Glamourer.Interop; +using Glamourer.Interop.Structs; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Enums; using Penumbra.GameData.Gui.Debug; -using Penumbra.GameData.Interop; namespace Glamourer.Gui.Tabs.DebugTab; -public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _objectManager) : IGameDataDrawer +public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectManager) : IGameDataDrawer { public string Label => $"Active Actors ({_stateManager.Count})###Active Actors"; @@ -21,7 +22,8 @@ public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _ob public void Draw() { - foreach (var (identifier, actors) in _objectManager) + _objectManager.Update(); + foreach (var (identifier, actors) in _objectManager.Identifiers) { if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.Trash.ToIconString()}##{actors.Label}", new Vector2(ImGui.GetFrameHeight()), string.Empty, !_stateManager.ContainsKey(identifier), true)) @@ -64,15 +66,13 @@ public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _ob static string ItemString(in DesignData data, EquipSlot slot) { var item = data.Item(slot); - return - $"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})"; + return $"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})"; } static string BonusItemString(in DesignData data, BonusItemFlag slot) { var item = data.BonusItem(slot); - return - $"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})"; + return $"{item.Name} ({item.Id.ToDiscriminatingString()} {item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})"; } PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state.Sources[MetaIndex.ModelId]); @@ -87,9 +87,6 @@ public class ActiveStatePanel(StateManager _stateManager, ActorObjectManager _ob PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(), state.Sources[MetaIndex.VisorState]); ImGui.TableNextRow(); - PrintRow("Viera Ears Visible", state.BaseData.AreEarsVisible(), state.ModelData.AreEarsVisible(), - state.Sources[MetaIndex.EarState]); - ImGui.TableNextRow(); PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(), state.Sources[MetaIndex.WeaponState]); ImGui.TableNextRow(); diff --git a/Glamourer/Gui/Tabs/DebugTab/AdvancedCustomizationDrawer.cs b/Glamourer/Gui/Tabs/DebugTab/AdvancedCustomizationDrawer.cs index 2202ceb..6f6d27a 100644 --- a/Glamourer/Gui/Tabs/DebugTab/AdvancedCustomizationDrawer.cs +++ b/Glamourer/Gui/Tabs/DebugTab/AdvancedCustomizationDrawer.cs @@ -1,13 +1,13 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; -using Dalamud.Bindings.ImGui; +using Glamourer.Interop; +using ImGuiNET; using OtterGui.Raii; using OtterGui.Text; using Penumbra.GameData.Gui.Debug; -using Penumbra.GameData.Interop; namespace Glamourer.Gui.Tabs.DebugTab; -public unsafe class AdvancedCustomizationDrawer(ActorObjectManager objects) : IGameDataDrawer +public unsafe class AdvancedCustomizationDrawer(ObjectManager objects) : IGameDataDrawer { public string Label => "Advanced Customizations"; @@ -31,8 +31,8 @@ public unsafe class AdvancedCustomizationDrawer(ActorObjectManager objects) : IG return; } - DrawCBuffer("Customize"u8, model.AsHuman->CustomizeParameterCBuffer, 0); - DrawCBuffer("Decal"u8, model.AsHuman->DecalColorCBuffer, 1); + DrawCBuffer("Customize"u8, model.AsHuman->CustomizeParameterCBuffer, 0); + DrawCBuffer("Decal"u8, model.AsHuman->DecalColorCBuffer, 1); DrawCBuffer("Unk1"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA0), 2); DrawCBuffer("Unk2"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA8), 3); } diff --git a/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs b/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs index aee59b6..98b7d9e 100644 --- a/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/AutoDesignPanel.cs @@ -1,7 +1,6 @@ using Glamourer.Automation; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; using OtterGui.Raii; using Penumbra.GameData.Gui.Debug; diff --git a/Glamourer/Gui/Tabs/DebugTab/CustomizationServicePanel.cs b/Glamourer/Gui/Tabs/DebugTab/CustomizationServicePanel.cs index 6c0995c..afc7d56 100644 --- a/Glamourer/Gui/Tabs/DebugTab/CustomizationServicePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/CustomizationServicePanel.cs @@ -1,7 +1,7 @@ using Dalamud.Interface; using Glamourer.GameData; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Text; @@ -28,35 +28,9 @@ public class CustomizationServicePanel(CustomizeService customize) : IGameDataDr DrawNpcCustomizationInfo(set); } - DrawFacepaintInfo(); DrawColorInfo(); } - private void DrawFacepaintInfo() - { - using var tree = ImUtf8.TreeNode("NPC Facepaints"u8); - if (!tree) - return; - - using var table = ImUtf8.Table("data"u8, 2, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); - if (!table) - return; - - ImGui.TableNextColumn(); - ImUtf8.TableHeader("Id"u8); - ImGui.TableNextColumn(); - ImUtf8.TableHeader("Facepaint"u8); - - for (var i = 0; i < 128; ++i) - { - var index = new CustomizeValue((byte)i); - ImUtf8.DrawTableColumn($"{i:D3}"); - using var font = ImRaii.PushFont(UiBuilder.IconFont); - ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckValue(CustomizeIndex.FacePaint, index) - ? FontAwesomeIcon.Check.ToIconString() - : FontAwesomeIcon.Times.ToIconString()); - } - } private void DrawColorInfo() { using var tree = ImUtf8.TreeNode("NPC Colors"u8); @@ -83,16 +57,16 @@ public class CustomizationServicePanel(CustomizeService customize) : IGameDataDr var index = new CustomizeValue((byte)i); ImUtf8.DrawTableColumn($"{i:D3}"); using var font = ImRaii.PushFont(UiBuilder.IconFont); - ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckValue(CustomizeIndex.HairColor, index) + ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckColor(CustomizeIndex.HairColor, index) ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString()); - ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckValue(CustomizeIndex.EyeColorLeft, index) + ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckColor(CustomizeIndex.EyeColorLeft, index) ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString()); - ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckValue(CustomizeIndex.FacePaintColor, index) + ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckColor(CustomizeIndex.FacePaintColor, index) ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString()); - ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckValue(CustomizeIndex.TattooColor, index) + ImUtf8.DrawTableColumn(customize.NpcCustomizeSet.CheckColor(CustomizeIndex.TattooColor, index) ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString()); } diff --git a/Glamourer/Gui/Tabs/DebugTab/CustomizationUnlockPanel.cs b/Glamourer/Gui/Tabs/DebugTab/CustomizationUnlockPanel.cs index 4bf7d7b..a53a677 100644 --- a/Glamourer/Gui/Tabs/DebugTab/CustomizationUnlockPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/CustomizationUnlockPanel.cs @@ -1,5 +1,5 @@ using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Enums; diff --git a/Glamourer/Gui/Tabs/DebugTab/DatFilePanel.cs b/Glamourer/Gui/Tabs/DebugTab/DatFilePanel.cs index 7c61392..11f27fd 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DatFilePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DatFilePanel.cs @@ -1,5 +1,5 @@ using Glamourer.Interop; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using Penumbra.GameData.Files; using Penumbra.GameData.Gui.Debug; diff --git a/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs b/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs index b760221..cd89aec 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DebugTab.cs @@ -1,4 +1,4 @@ -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Raii; using OtterGui.Services; using OtterGui.Widgets; diff --git a/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs b/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs index 3df425f..90282e8 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs @@ -1,4 +1,5 @@ using Glamourer.Gui.Tabs.DebugTab.IpcTester; +using ImGuiNET; using Microsoft.Extensions.DependencyInjection; using OtterGui.Raii; using Penumbra.GameData.Gui.Debug; @@ -35,7 +36,6 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), - provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), diff --git a/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs b/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs index 287d373..2345abc 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DesignConverterPanel.cs @@ -1,7 +1,7 @@ using Dalamud.Interface; using Glamourer.Designs; using Glamourer.Utility; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Newtonsoft.Json.Linq; using OtterGui; using OtterGui.Raii; diff --git a/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs b/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs index 7c60dda..b562ecf 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs @@ -1,11 +1,8 @@ using Dalamud.Interface; using Glamourer.Designs; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; -using OtterGui.Filesystem; using OtterGui.Raii; -using OtterGui.Text; using Penumbra.GameData.Enums; using Penumbra.GameData.Gui.Debug; @@ -21,7 +18,6 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _ public void Draw() { - DrawButtons(); foreach (var (design, idx) in _designManager.Designs.WithIndex()) { using var t = ImRaii.TreeNode($"{design.Name}##{idx}"); @@ -29,8 +25,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _ continue; DrawDesign(design, _designFileSystem); - var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize, - design.Application.Meta, + var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize, design.Application.Meta, design.WriteProtected()); using var font = ImRaii.PushFont(UiBuilder.MonoFont); ImGuiUtil.TextWrapped(base64); @@ -39,26 +34,6 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _ } } - private void DrawButtons() - { - if (ImUtf8.Button("Generate 500 Test Designs"u8)) - for (var i = 0; i < 500; ++i) - { - var design = _designManager.CreateEmpty($"Test Designs/Test Design {i}", true); - _designManager.AddTag(design, "_DebugTest"); - } - - ImUtf8.SameLineInner(); - if (ImUtf8.Button("Remove All Test Designs"u8)) - { - var designs = _designManager.Designs.Where(d => d.Tags.Contains("_DebugTest")).ToArray(); - foreach (var design in designs) - _designManager.Delete(design); - if (_designFileSystem.Find("Test Designs", out var path) && path is DesignFileSystem.Folder { TotalChildren: 0 }) - _designFileSystem.Delete(path); - } - } - public static void DrawDesign(DesignBase design, DesignFileSystem? fileSystem) { using var table = ImRaii.Table("##equip", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit); @@ -77,7 +52,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _ ImGui.TableNextRow(); ImGuiUtil.DrawTableColumn("Design File System Path"); if (fileSystem != null) - ImGuiUtil.DrawTableColumn(fileSystem.TryGetValue(d, out var leaf) ? leaf.FullName() : "No Path Known"); + ImGuiUtil.DrawTableColumn(fileSystem.FindLeaf(d, out var leaf) ? leaf.FullName() : "No Path Known"); ImGui.TableNextRow(); ImGuiUtil.DrawTableColumn("Creation"); @@ -114,7 +89,6 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _ ImGuiUtil.DrawTableColumn(index.ToName()); ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString()); ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep"); - ImGui.TableNextRow(); } ImGuiUtil.DrawTableColumn("Model ID"); diff --git a/Glamourer/Gui/Tabs/DebugTab/DesignTesterPanel.cs b/Glamourer/Gui/Tabs/DebugTab/DesignTesterPanel.cs index cf45077..c893f4c 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DesignTesterPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DesignTesterPanel.cs @@ -1,9 +1,8 @@ using Dalamud.Interface; using Glamourer.Designs; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; using OtterGui.Raii; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; diff --git a/Glamourer/Gui/Tabs/DebugTab/DynamisPanel.cs b/Glamourer/Gui/Tabs/DebugTab/DynamisPanel.cs deleted file mode 100644 index 92cd777..0000000 --- a/Glamourer/Gui/Tabs/DebugTab/DynamisPanel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using OtterGui.Services; -using Penumbra.GameData.Gui.Debug; - -namespace Glamourer.Gui.Tabs.DebugTab; - -public class DynamisPanel(DynamisIpc dynamis) : IGameDataDrawer -{ - public string Label - => "Dynamis Interop"; - - public void Draw() - => dynamis.DrawDebugInfo(); - - public bool Disabled - => false; -} diff --git a/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs b/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs index 370c4e5..c517070 100644 --- a/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/FunPanel.cs @@ -1,5 +1,5 @@ using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Penumbra.GameData.Gui.Debug; namespace Glamourer.Gui.Tabs.DebugTab; diff --git a/Glamourer/Gui/Tabs/DebugTab/GlamourPlatePanel.cs b/Glamourer/Gui/Tabs/DebugTab/GlamourPlatePanel.cs index f480f6d..394bd7f 100644 --- a/Glamourer/Gui/Tabs/DebugTab/GlamourPlatePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/GlamourPlatePanel.cs @@ -3,26 +3,24 @@ using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game; using Glamourer.Designs; +using Glamourer.Interop; using Glamourer.Services; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; -using OtterGui.Text; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.GameData.Gui.Debug; -using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Gui.Tabs.DebugTab; public unsafe class GlamourPlatePanel : IGameDataDrawer { - private readonly DesignManager _design; - private readonly ItemManager _items; - private readonly StateManager _state; - private readonly ActorObjectManager _objects; + private readonly DesignManager _design; + private readonly ItemManager _items; + private readonly StateManager _state; + private readonly ObjectManager _objects; public string Label => "Glamour Plates"; @@ -30,8 +28,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer public bool Disabled => false; - public GlamourPlatePanel(IGameInteropProvider interop, ItemManager items, DesignManager design, StateManager state, - ActorObjectManager objects) + public GlamourPlatePanel(IGameInteropProvider interop, ItemManager items, DesignManager design, StateManager state, ObjectManager objects) { _items = items; _design = design; @@ -45,24 +42,24 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer var manager = MirageManager.Instance(); using (ImRaii.Group()) { - ImUtf8.Text("Address:"u8); - ImUtf8.Text("Number of Glamour Plates:"u8); - ImUtf8.Text("Glamour Plates Requested:"u8); - ImUtf8.Text("Glamour Plates Loaded:"u8); - ImUtf8.Text("Is Applying Glamour Plates:"u8); + ImGui.TextUnformatted("Address:"); + ImGui.TextUnformatted("Number of Glamour Plates:"); + ImGui.TextUnformatted("Glamour Plates Requested:"); + ImGui.TextUnformatted("Glamour Plates Loaded:"); + ImGui.TextUnformatted("Is Applying Glamour Plates:"); } ImGui.SameLine(); using (ImRaii.Group()) { - ImUtf8.CopyOnClickSelectable($"0x{(ulong)manager:X}"); - ImUtf8.Text(manager == null ? "-" : manager->GlamourPlates.Length.ToString()); - ImUtf8.Text(manager == null ? "-" : manager->GlamourPlatesRequested.ToString()); + ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)manager:X}"); + ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlates.Length.ToString()); + ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlatesRequested.ToString()); ImGui.SameLine(); - if (ImUtf8.SmallButton("Request Update"u8)) + if (ImGui.SmallButton("Request Update")) RequestGlamour(); - ImUtf8.Text(manager == null ? "-" : manager->GlamourPlatesLoaded.ToString()); - ImUtf8.Text(manager == null ? "-" : manager->IsApplyingGlamourPlate.ToString()); + ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlatesLoaded.ToString()); + ImGui.TextUnformatted(manager == null ? "-" : manager->IsApplyingGlamourPlate.ToString()); } if (manager == null) @@ -74,28 +71,28 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer for (var i = 0; i < manager->GlamourPlates.Length; ++i) { - using var tree = ImUtf8.TreeNode($"Plate #{i + 1:D2}"); + using var tree = ImRaii.TreeNode($"Plate #{i + 1:D2}"); if (!tree) continue; ref var plate = ref manager->GlamourPlates[i]; - if (ImUtf8.ButtonEx("Apply to Player"u8, ""u8, Vector2.Zero, !enabled)) + if (ImGuiUtil.DrawDisabledButton("Apply to Player", Vector2.Zero, string.Empty, !enabled)) { var design = CreateDesign(plate); - _state.ApplyDesign(state!, design, ApplySettings.Manual with { IsFinal = true }); + _state.ApplyDesign(state!, design, ApplySettings.Manual); } using (ImRaii.Group()) { foreach (var slot in EquipSlotExtensions.FullSlots) - ImUtf8.Text(slot.ToName()); + ImGui.TextUnformatted(slot.ToName()); } ImGui.SameLine(); using (ImRaii.Group()) { foreach (var (_, index) in EquipSlotExtensions.FullSlots.WithIndex()) - ImUtf8.Text($"{plate.ItemIds[index]:D6}, {StainIds.FromGlamourPlate(plate, index)}"); + ImGui.TextUnformatted($"{plate.ItemIds[index]:D6}, {StainIds.FromGlamourPlate(plate, index)}"); } } } diff --git a/Glamourer/Gui/Tabs/DebugTab/InventoryPanel.cs b/Glamourer/Gui/Tabs/DebugTab/InventoryPanel.cs index ca9ff7b..b021656 100644 --- a/Glamourer/Gui/Tabs/DebugTab/InventoryPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/InventoryPanel.cs @@ -1,5 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Game; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Gui.Debug; @@ -23,7 +23,7 @@ public unsafe class InventoryPanel : IGameDataDrawer ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)inventory:X}"); var equip = inventory->GetInventoryContainer(InventoryType.EquippedItems); - if (equip == null || equip->IsLoaded) + if (equip == null || equip->Loaded == 0) return; ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)equip:X}"); diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTester/DesignIpcTester.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTester/DesignIpcTester.cs index 8cbf57a..918c7ad 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTester/DesignIpcTester.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTester/DesignIpcTester.cs @@ -3,11 +3,10 @@ using Dalamud.Interface.Utility; using Dalamud.Plugin; using Glamourer.Api.Enums; using Glamourer.Api.IpcSubscribers; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Services; -using OtterGui.Text; namespace Glamourer.Gui.Tabs.DebugTab.IpcTester; @@ -16,7 +15,6 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi private Dictionary _designs = []; private int _gameObjectIndex; private string _gameObjectName = string.Empty; - private string _designName = string.Empty; private uint _key; private ApplyFlag _flags = ApplyFlagEx.DesignDefault; private Guid? _design; @@ -32,7 +30,6 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi IpcTesterHelpers.IndexInput(ref _gameObjectIndex); IpcTesterHelpers.KeyInput(ref _key); IpcTesterHelpers.NameInput(ref _gameObjectName); - ImUtf8.InputText("##designName"u8, ref _designName, "Design Name..."u8); ImGuiUtil.GuidInput("##identifier", "Design Identifier...", string.Empty, ref _design, ref _designText, ImGui.GetContentRegionAvail().X); IpcTesterHelpers.DrawFlagInput(ref _flags); @@ -57,48 +54,6 @@ public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiServi IpcTesterHelpers.DrawIntro(ApplyDesignName.Label); if (ImGuiUtil.DrawDisabledButton("Apply##Name", Vector2.Zero, string.Empty, !_design.HasValue)) _lastError = new ApplyDesignName(pluginInterface).Invoke(_design!.Value, _gameObjectName, _key, _flags); - - IpcTesterHelpers.DrawIntro(GetExtendedDesignData.Label); - if (_design.HasValue) - { - var (display, path, color, draw) = new GetExtendedDesignData(pluginInterface).Invoke(_design.Value); - if (path.Length > 0) - ImUtf8.Text($"{display} ({path}){(draw ? " in QDB"u8 : ""u8)}", color); - else - ImUtf8.Text("No Data"u8); - } - else - { - ImUtf8.Text("No Data"u8); - } - - IpcTesterHelpers.DrawIntro(GetDesignBase64.Label); - if (ImUtf8.Button("To Clipboard##Base64"u8) && _design.HasValue) - { - var data = new GetDesignBase64(pluginInterface).Invoke(_design.Value); - ImUtf8.SetClipboardText(data); - } - - IpcTesterHelpers.DrawIntro(AddDesign.Label); - if (ImUtf8.Button("Add from Clipboard"u8)) - try - { - var data = ImUtf8.GetClipboardText(); - _lastError = new AddDesign(pluginInterface).Invoke(data, _designName, out var newDesign); - if (_lastError is GlamourerApiEc.Success) - { - _design = newDesign; - _designText = newDesign.ToString(); - } - } - catch - { - _lastError = GlamourerApiEc.UnknownError; - } - - IpcTesterHelpers.DrawIntro(DeleteDesign.Label); - if (ImUtf8.Button("Delete##Design"u8) && _design.HasValue) - _lastError = new DeleteDesign(pluginInterface).Invoke(_design.Value); } private void DrawDesignsPopup() diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterHelpers.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterHelpers.cs index 61dad53..500fddd 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterHelpers.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterHelpers.cs @@ -1,5 +1,8 @@ using Glamourer.Api.Enums; -using Dalamud.Bindings.ImGui; +using Glamourer.Designs; +using ImGuiNET; +using OtterGui; +using static Penumbra.GameData.Files.ShpkFile; namespace Glamourer.Gui.Tabs.DebugTab.IpcTester; diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterPanel.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterPanel.cs index 22c7597..8f561af 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTester/IpcTesterPanel.cs @@ -1,7 +1,7 @@ using Dalamud.Plugin; using Dalamud.Plugin.Services; using Glamourer.Api.IpcSubscribers; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Penumbra.GameData.Gui.Debug; namespace Glamourer.Gui.Tabs.DebugTab.IpcTester; @@ -33,11 +33,6 @@ public class IpcTesterPanel( ImGui.SameLine(); ImGui.TextUnformatted($"({major}.{minor:D4})"); - ImGui.TextUnformatted(AutoReloadGearEnabled.Label); - var autoRedraw = new AutoReloadGearEnabled(pluginInterface).Invoke(); - ImGui.SameLine(); - ImGui.TextUnformatted(autoRedraw ? "Enabled" : "Disabled"); - designs.Draw(); items.Draw(); state.Draw(); @@ -54,10 +49,8 @@ public class IpcTesterPanel( return; Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester."); - state.AutoRedrawChanged.Enable(); state.GPoseChanged.Enable(); state.StateChanged.Enable(); - state.StateFinalized.Enable(); framework.Update += CheckUnsubscribe; _subscribed = true; } @@ -78,9 +71,7 @@ public class IpcTesterPanel( Glamourer.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester."); _subscribed = false; - state.AutoRedrawChanged.Disable(); state.GPoseChanged.Disable(); state.StateChanged.Disable(); - state.StateFinalized.Disable(); } } diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs index ea95a9d..1499fcb 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTester/ItemsIpcTester.cs @@ -1,7 +1,7 @@ using Dalamud.Plugin; using Glamourer.Api.Enums; using Glamourer.Api.IpcSubscribers; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Services; diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTester/StateIpcTester.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTester/StateIpcTester.cs index 6fb9d68..f378625 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTester/StateIpcTester.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTester/StateIpcTester.cs @@ -1,17 +1,16 @@ -using Dalamud.Bindings.ImGui; -using Dalamud.Interface; +using Dalamud.Interface; using Dalamud.Interface.Utility; using Dalamud.Plugin; using Glamourer.Api.Enums; using Glamourer.Api.Helpers; using Glamourer.Api.IpcSubscribers; using Glamourer.Designs; +using ImGuiNET; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; using OtterGui.Raii; using OtterGui.Services; -using OtterGui.Text; using Penumbra.GameData.Interop; using Penumbra.String; @@ -31,21 +30,10 @@ public class StateIpcTester : IUiService, IDisposable private string _base64State = string.Empty; private string? _getStateString; - public readonly EventSubscriber AutoRedrawChanged; - private bool _lastAutoRedrawChangeValue; - private DateTime _lastAutoRedrawChangeTime; - public readonly EventSubscriber StateChanged; - private nint _lastStateChangeActor; - private ByteString _lastStateChangeName = ByteString.Empty; - private DateTime _lastStateChangeTime; - private StateChangeType _lastStateChangeType; - - public readonly EventSubscriber StateFinalized; - private nint _lastStateFinalizeActor; - private ByteString _lastStateFinalizeName = ByteString.Empty; - private DateTime _lastStateFinalizeTime; - private StateFinalizationType _lastStateFinalizeType; + private nint _lastStateChangeActor; + private ByteString _lastStateChangeName = ByteString.Empty; + private DateTime _lastStateChangeTime; public readonly EventSubscriber GPoseChanged; private bool _lastGPoseChangeValue; @@ -55,22 +43,16 @@ public class StateIpcTester : IUiService, IDisposable public StateIpcTester(IDalamudPluginInterface pluginInterface) { - _pluginInterface = pluginInterface; - AutoRedrawChanged = AutoReloadGearChanged.Subscriber(_pluginInterface, OnAutoRedrawChanged); - StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged); - StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized); - GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange); - AutoRedrawChanged.Disable(); + _pluginInterface = pluginInterface; + StateChanged = Api.IpcSubscribers.StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged); + GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange); StateChanged.Disable(); - StateFinalized.Disable(); GPoseChanged.Disable(); } public void Dispose() { - AutoRedrawChanged.Dispose(); StateChanged.Dispose(); - StateFinalized.Dispose(); GPoseChanged.Dispose(); } @@ -90,99 +72,87 @@ public class StateIpcTester : IUiService, IDisposable IpcTesterHelpers.DrawIntro("Last Error"); ImGui.TextUnformatted(_lastError.ToString()); - IpcTesterHelpers.DrawIntro("Last Auto Redraw Change"); - ImGui.TextUnformatted($"{_lastAutoRedrawChangeValue} at {_lastAutoRedrawChangeTime.ToLocalTime().TimeOfDay}"); IpcTesterHelpers.DrawIntro("Last State Change"); - PrintChangeName(); - IpcTesterHelpers.DrawIntro("Last State Finalization"); - PrintFinalizeName(); + PrintName(); IpcTesterHelpers.DrawIntro("Last GPose Change"); ImGui.TextUnformatted($"{_lastGPoseChangeValue} at {_lastGPoseChangeTime.ToLocalTime().TimeOfDay}"); IpcTesterHelpers.DrawIntro(GetState.Label); DrawStatePopup(); - if (ImUtf8.Button("Get##Idx"u8)) + if (ImGui.Button("Get##Idx")) { (_lastError, _state) = new GetState(_pluginInterface).Invoke(_gameObjectIndex, _key); _stateString = _state?.ToString(Formatting.Indented) ?? "No State Available"; - ImUtf8.OpenPopup("State"u8); + ImGui.OpenPopup("State"); } IpcTesterHelpers.DrawIntro(GetStateName.Label); - if (ImUtf8.Button("Get##Name"u8)) + if (ImGui.Button("Get##Name")) { (_lastError, _state) = new GetStateName(_pluginInterface).Invoke(_gameObjectName, _key); _stateString = _state?.ToString(Formatting.Indented) ?? "No State Available"; - ImUtf8.OpenPopup("State"u8); + ImGui.OpenPopup("State"); } IpcTesterHelpers.DrawIntro(GetStateBase64.Label); - if (ImUtf8.Button("Get##Base64Idx"u8)) + if (ImGui.Button("Get##Base64Idx")) { (_lastError, _getStateString) = new GetStateBase64(_pluginInterface).Invoke(_gameObjectIndex, _key); _stateString = _getStateString ?? "No State Available"; - ImUtf8.OpenPopup("State"u8); + ImGui.OpenPopup("State"); } IpcTesterHelpers.DrawIntro(GetStateBase64Name.Label); - if (ImUtf8.Button("Get##Base64Idx"u8)) + if (ImGui.Button("Get##Base64Idx")) { (_lastError, _getStateString) = new GetStateBase64Name(_pluginInterface).Invoke(_gameObjectName, _key); _stateString = _getStateString ?? "No State Available"; - ImUtf8.OpenPopup("State"u8); + ImGui.OpenPopup("State"); } IpcTesterHelpers.DrawIntro(ApplyState.Label); if (ImGuiUtil.DrawDisabledButton("Apply Last##Idx", Vector2.Zero, string.Empty, _state == null)) _lastError = new ApplyState(_pluginInterface).Invoke(_state!, _gameObjectIndex, _key, _flags); ImGui.SameLine(); - if (ImUtf8.Button("Apply Base64##Idx"u8)) + if (ImGui.Button("Apply Base64##Idx")) _lastError = new ApplyState(_pluginInterface).Invoke(_base64State, _gameObjectIndex, _key, _flags); IpcTesterHelpers.DrawIntro(ApplyStateName.Label); if (ImGuiUtil.DrawDisabledButton("Apply Last##Name", Vector2.Zero, string.Empty, _state == null)) _lastError = new ApplyStateName(_pluginInterface).Invoke(_state!, _gameObjectName, _key, _flags); ImGui.SameLine(); - if (ImUtf8.Button("Apply Base64##Name"u8)) + if (ImGui.Button("Apply Base64##Name")) _lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags); - IpcTesterHelpers.DrawIntro(ReapplyState.Label); - if (ImUtf8.Button("Reapply##Idx"u8)) - _lastError = new ReapplyState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags); - - IpcTesterHelpers.DrawIntro(ReapplyStateName.Label); - if (ImUtf8.Button("Reapply##Name"u8)) - _lastError = new ReapplyStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags); - IpcTesterHelpers.DrawIntro(RevertState.Label); - if (ImUtf8.Button("Revert##Idx"u8)) + if (ImGui.Button("Revert##Idx")) _lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags); IpcTesterHelpers.DrawIntro(RevertStateName.Label); - if (ImUtf8.Button("Revert##Name"u8)) + if (ImGui.Button("Revert##Name")) _lastError = new RevertStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags); IpcTesterHelpers.DrawIntro(UnlockState.Label); - if (ImUtf8.Button("Unlock##Idx"u8)) + if (ImGui.Button("Unlock##Idx")) _lastError = new UnlockState(_pluginInterface).Invoke(_gameObjectIndex, _key); IpcTesterHelpers.DrawIntro(UnlockStateName.Label); - if (ImUtf8.Button("Unlock##Name"u8)) + if (ImGui.Button("Unlock##Name")) _lastError = new UnlockStateName(_pluginInterface).Invoke(_gameObjectName, _key); IpcTesterHelpers.DrawIntro(UnlockAll.Label); - if (ImUtf8.Button("Unlock##All"u8)) + if (ImGui.Button("Unlock##All")) _numUnlocked = new UnlockAll(_pluginInterface).Invoke(_key); ImGui.SameLine(); ImGui.TextUnformatted($"Unlocked {_numUnlocked}"); IpcTesterHelpers.DrawIntro(RevertToAutomation.Label); - if (ImUtf8.Button("Revert##AutomationIdx"u8)) + if (ImGui.Button("Revert##AutomationIdx")) _lastError = new RevertToAutomation(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags); IpcTesterHelpers.DrawIntro(RevertToAutomationName.Label); - if (ImUtf8.Button("Revert##AutomationName"u8)) + if (ImGui.Button("Revert##AutomationName")) _lastError = new RevertToAutomationName(_pluginInterface).Invoke(_gameObjectName, _key, _flags); } @@ -192,76 +162,44 @@ public class StateIpcTester : IUiService, IDisposable if (_stateString == null) return; - using var p = ImUtf8.Popup("State"u8); + using var p = ImRaii.Popup("State"); if (!p) return; - if (ImUtf8.Button("Copy to Clipboard"u8)) - ImUtf8.SetClipboardText(_stateString); + if (ImGui.Button("Copy to Clipboard")) + ImGui.SetClipboardText(_stateString); if (_stateString[0] is '{') { ImGui.SameLine(); - if (ImUtf8.Button("Copy as Base64"u8) && _state != null) - ImUtf8.SetClipboardText(DesignConverter.ToBase64(_state)); + if (ImGui.Button("Copy as Base64") && _state != null) + ImGui.SetClipboardText(DesignConverter.ToBase64(_state)); } using var font = ImRaii.PushFont(UiBuilder.MonoFont); - ImUtf8.TextWrapped(_stateString ?? string.Empty); + ImGuiUtil.TextWrapped(_stateString ?? string.Empty); - if (ImUtf8.Button("Close"u8, -Vector2.UnitX) || !ImGui.IsWindowFocused()) + if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) ImGui.CloseCurrentPopup(); } - private unsafe void PrintChangeName() + private unsafe void PrintName() { - ImUtf8.Text(_lastStateChangeName.Span); - ImGui.SameLine(0, 0); - ImUtf8.Text($" ({_lastStateChangeType})"); + ImGuiNative.igTextUnformatted(_lastStateChangeName.Path, _lastStateChangeName.Path + _lastStateChangeName.Length); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.MonoFont)) { - ImUtf8.CopyOnClickSelectable($"0x{_lastStateChangeActor:X}"); + ImGuiUtil.CopyOnClickSelectable($"0x{_lastStateChangeActor:X}"); } ImGui.SameLine(); - ImUtf8.Text($"at {_lastStateChangeTime.ToLocalTime().TimeOfDay}"); + ImGui.TextUnformatted($"at {_lastStateChangeTime.ToLocalTime().TimeOfDay}"); } - private unsafe void PrintFinalizeName() - { - ImUtf8.Text(_lastStateFinalizeName.Span); - ImGui.SameLine(0, 0); - ImUtf8.Text($" ({_lastStateFinalizeType})"); - ImGui.SameLine(); - using (ImRaii.PushFont(UiBuilder.MonoFont)) - { - ImUtf8.CopyOnClickSelectable($"0x{_lastStateFinalizeActor:X}"); - } - - ImGui.SameLine(); - ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}"); - } - - private void OnAutoRedrawChanged(bool value) - { - _lastAutoRedrawChangeValue = value; - _lastAutoRedrawChangeTime = DateTime.UtcNow; - } - - private void OnStateChanged(nint actor, StateChangeType type) + private void OnStateChanged(nint actor, StateChangeType _) { _lastStateChangeActor = actor; _lastStateChangeTime = DateTime.UtcNow; _lastStateChangeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty; - _lastStateChangeType = type; - } - - private void OnStateFinalized(nint actor, StateFinalizationType type) - { - _lastStateFinalizeActor = actor; - _lastStateFinalizeTime = DateTime.UtcNow; - _lastStateFinalizeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty; - _lastStateFinalizeType = type; } private void OnGPoseChange(bool value) diff --git a/Glamourer/Gui/Tabs/DebugTab/ItemUnlockPanel.cs b/Glamourer/Gui/Tabs/DebugTab/ItemUnlockPanel.cs index f82bfb3..afbb86b 100644 --- a/Glamourer/Gui/Tabs/DebugTab/ItemUnlockPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/ItemUnlockPanel.cs @@ -1,7 +1,7 @@ using Dalamud.Interface.Utility; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Enums; diff --git a/Glamourer/Gui/Tabs/DebugTab/ModelEvaluationPanel.cs b/Glamourer/Gui/Tabs/DebugTab/ModelEvaluationPanel.cs index 185e19b..c1b5847 100644 --- a/Glamourer/Gui/Tabs/DebugTab/ModelEvaluationPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/ModelEvaluationPanel.cs @@ -2,7 +2,7 @@ using Glamourer.GameData; using Glamourer.Interop; using Glamourer.Interop.Structs; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Text; @@ -11,13 +11,13 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.Gui.Tabs.DebugTab; public unsafe class ModelEvaluationPanel( - ActorObjectManager _objectManager, + ObjectManager _objectManager, VisorService _visorService, - VieraEarService _vieraEarService, UpdateSlotService _updateSlotService, ChangeCustomizeService _changeCustomizeService, CrestService _crestService, @@ -34,7 +34,7 @@ public unsafe class ModelEvaluationPanel( public void Draw() { ImGui.InputInt("Game Object Index", ref _gameObjectIndex, 0, 0); - var actor = _objectManager.Objects[_gameObjectIndex]; + var actor = _objectManager[_gameObjectIndex]; var model = actor.Model; using var table = ImRaii.Table("##evaluationTable", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg); ImGui.TableNextColumn(); @@ -46,10 +46,9 @@ public unsafe class ModelEvaluationPanel( ImGuiUtil.DrawTableColumn("Address"); ImGui.TableNextColumn(); - - Glamourer.Dynamis.DrawPointer(actor); + ImGuiUtil.CopyOnClickSelectable(actor.ToString()); ImGui.TableNextColumn(); - Glamourer.Dynamis.DrawPointer(model); + ImGuiUtil.CopyOnClickSelectable(model.ToString()); ImGui.TableNextColumn(); if (actor.IsCharacter) { @@ -85,7 +84,6 @@ public unsafe class ModelEvaluationPanel( ImGuiUtil.CopyOnClickSelectable(offhand.ToString()); DrawVisor(actor, model); - DrawVieraEars(actor, model); DrawHatState(actor, model); DrawWeaponState(actor, model); DrawWetness(actor, model); @@ -137,26 +135,6 @@ public unsafe class ModelEvaluationPanel( _visorService.SetVisorState(model, !VisorService.GetVisorState(model)); } - private void DrawVieraEars(Actor actor, Model model) - { - using var id = ImRaii.PushId("Viera Ears"); - ImGuiUtil.DrawTableColumn("Viera Ears"); - ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.ShowVieraEars.ToString() : "No Character"); - ImGuiUtil.DrawTableColumn(model.IsHuman ? model.VieraEarsVisible.ToString() : "No Human"); - ImGui.TableNextColumn(); - if (!model.IsHuman) - return; - - if (ImGui.SmallButton("Set True")) - _vieraEarService.SetVieraEarState(model, true); - ImGui.SameLine(); - if (ImGui.SmallButton("Set False")) - _vieraEarService.SetVieraEarState(model, false); - ImGui.SameLine(); - if (ImGui.SmallButton("Toggle")) - _vieraEarService.SetVieraEarState(model, !model.VieraEarsVisible); - } - private void DrawHatState(Actor actor, Model model) { using var id = ImRaii.PushId("HatState"); diff --git a/Glamourer/Gui/Tabs/DebugTab/NpcAppearancePanel.cs b/Glamourer/Gui/Tabs/DebugTab/NpcAppearancePanel.cs index 0d93bb8..04537b5 100644 --- a/Glamourer/Gui/Tabs/DebugTab/NpcAppearancePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/NpcAppearancePanel.cs @@ -3,18 +3,18 @@ using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Glamourer.Designs; using Glamourer.GameData; +using Glamourer.Interop; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; +using OtterGui; using OtterGui.Raii; -using OtterGui.Text; using Penumbra.GameData.Enums; using Penumbra.GameData.Gui.Debug; -using Penumbra.GameData.Interop; using ImGuiClip = OtterGui.ImGuiClip; namespace Glamourer.Gui.Tabs.DebugTab; -public class NpcAppearancePanel(NpcCombo npcCombo, StateManager stateManager, ActorObjectManager objectManager, DesignConverter designConverter) +public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectManager _objectManager, DesignConverter _designConverter) : IGameDataDrawer { public string Label @@ -28,9 +28,9 @@ public class NpcAppearancePanel(NpcCombo npcCombo, StateManager stateManager, Ac public void Draw() { - ImUtf8.Checkbox("Compare Customize (or Gear)"u8, ref _customizeOrGear); + ImGui.Checkbox("Compare Customize (or Gear)", ref _customizeOrGear); ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - var resetScroll = ImUtf8.InputText("##npcFilter"u8, ref _npcFilter, "Filter..."u8); + var resetScroll = ImGui.InputTextWithHint("##npcFilter", "Filter...", ref _npcFilter, 64); using var table = ImRaii.Table("npcs", 7, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit, new Vector2(-1, 400 * ImGuiHelpers.GlobalScale)); @@ -40,19 +40,19 @@ public class NpcAppearancePanel(NpcCombo npcCombo, StateManager stateManager, Ac if (resetScroll) ImGui.SetScrollY(0); - ImUtf8.TableSetupColumn("Button"u8, ImGuiTableColumnFlags.WidthFixed); - ImUtf8.TableSetupColumn("Name"u8, ImGuiTableColumnFlags.WidthFixed, ImGuiHelpers.GlobalScale * 300); - ImUtf8.TableSetupColumn("Kind"u8, ImGuiTableColumnFlags.WidthFixed); - ImUtf8.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed); - ImUtf8.TableSetupColumn("Model"u8, ImGuiTableColumnFlags.WidthFixed); - ImUtf8.TableSetupColumn("Visor"u8, ImGuiTableColumnFlags.WidthFixed); - ImUtf8.TableSetupColumn("Compare"u8, ImGuiTableColumnFlags.WidthStretch); + ImGui.TableSetupColumn("Button", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed, ImGuiHelpers.GlobalScale * 300); + ImGui.TableSetupColumn("Kind", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Id", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Model", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Visor", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Compare", ImGuiTableColumnFlags.WidthStretch); ImGui.TableNextColumn(); var skips = ImGuiClip.GetNecessarySkips(ImGui.GetFrameHeightWithSpacing()); ImGui.TableNextRow(); var idx = 0; - var remainder = ImGuiClip.FilteredClippedDraw(npcCombo.Items, skips, + var remainder = ImGuiClip.FilteredClippedDraw(_npcCombo.Items, skips, d => d.Name.Contains(_npcFilter, StringComparison.OrdinalIgnoreCase), DrawData); ImGui.TableNextColumn(); ImGuiClip.DrawEndDummy(remainder, ImGui.GetFrameHeightWithSpacing()); @@ -61,31 +61,43 @@ public class NpcAppearancePanel(NpcCombo npcCombo, StateManager stateManager, Ac void DrawData(NpcData data) { using var id = ImRaii.PushId(idx++); - var disabled = !stateManager.GetOrCreate(objectManager.Player, out var state); + var disabled = !_state.GetOrCreate(_objectManager.Player, out var state); ImGui.TableNextColumn(); - if (ImUtf8.ButtonEx("Apply"u8, ""u8, Vector2.Zero, disabled)) + if (ImGuiUtil.DrawDisabledButton("Apply", Vector2.Zero, string.Empty, disabled)) { - foreach (var (slot, item, stain) in designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true)) - stateManager.ChangeEquip(state!, slot, item, stain, ApplySettings.Manual); - stateManager.ChangeMetaState(state!, MetaIndex.VisorState, data.VisorToggled, ApplySettings.Manual); - stateManager.ChangeEntireCustomize(state!, data.Customize, CustomizeFlagExtensions.All, ApplySettings.Manual); + foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true)) + _state.ChangeEquip(state!, slot, item, stain, ApplySettings.Manual); + _state.ChangeMetaState(state!, MetaIndex.VisorState, data.VisorToggled, ApplySettings.Manual); + _state.ChangeEntireCustomize(state!, data.Customize, CustomizeFlagExtensions.All, ApplySettings.Manual); } - ImUtf8.DrawFrameColumn(data.Name); + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(data.Name); - ImUtf8.DrawFrameColumn(data.Kind is ObjectKind.BattleNpc ? "B" : "E"); + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(data.Kind is ObjectKind.BattleNpc ? "B" : "E"); - ImUtf8.DrawFrameColumn(data.Id.Id.ToString()); + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(data.Id.Id.ToString()); - ImUtf8.DrawFrameColumn(data.ModelId.ToString()); + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(data.ModelId.ToString()); using (_ = ImRaii.PushFont(UiBuilder.IconFont)) { - ImUtf8.DrawFrameColumn(data.VisorToggled ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString()); + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(data.VisorToggled ? FontAwesomeIcon.Check.ToIconString() : FontAwesomeIcon.Times.ToIconString()); } using var mono = ImRaii.PushFont(UiBuilder.MonoFont); - ImUtf8.DrawFrameColumn(_customizeOrGear ? data.Customize.ToString() : data.WriteGear()); + ImGui.TableNextColumn(); + ImGui.AlignTextToFramePadding(); + ImGui.TextUnformatted(_customizeOrGear ? data.Customize.ToString() : data.WriteGear()); } } } diff --git a/Glamourer/Gui/Tabs/DebugTab/ObjectManagerPanel.cs b/Glamourer/Gui/Tabs/DebugTab/ObjectManagerPanel.cs index 97847ae..e519ea5 100644 --- a/Glamourer/Gui/Tabs/DebugTab/ObjectManagerPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/ObjectManagerPanel.cs @@ -1,13 +1,13 @@ -using Dalamud.Bindings.ImGui; +using Glamourer.Interop; +using ImGuiNET; using OtterGui; -using OtterGui.Text; +using OtterGui.Raii; using Penumbra.GameData.Actors; using Penumbra.GameData.Gui.Debug; -using Penumbra.GameData.Interop; namespace Glamourer.Gui.Tabs.DebugTab; -public class ObjectManagerPanel(ActorObjectManager _objectManager, ActorManager _actors) : IGameDataDrawer +public class ObjectManagerPanel(ObjectManager _objectManager, ActorManager _actors) : IGameDataDrawer { public string Label => "Object Manager"; @@ -19,45 +19,44 @@ public class ObjectManagerPanel(ActorObjectManager _objectManager, ActorManager public void Draw() { - _objectManager.Objects.DrawDebug(); - - using (var table = ImUtf8.Table("##data"u8, 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit)) + _objectManager.Update(); + using (var table = ImRaii.Table("##data", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit)) { if (!table) return; - ImUtf8.DrawTableColumn("World"u8); - ImUtf8.DrawTableColumn(_actors.Finished ? _actors.Data.ToWorldName(_objectManager.World) : "Service Missing"); - ImUtf8.DrawTableColumn(_objectManager.World.ToString()); - - ImUtf8.DrawTableColumn("Player Character"u8); - ImUtf8.DrawTableColumn($"{_objectManager.Player.Utf8Name} ({_objectManager.Player.Index})"); - ImGui.TableNextColumn(); - ImUtf8.CopyOnClickSelectable(_objectManager.Player.ToString()); - - ImUtf8.DrawTableColumn("In GPose"u8); - ImUtf8.DrawTableColumn(_objectManager.IsInGPose.ToString()); + ImGuiUtil.DrawTableColumn("Last Update"); + ImGuiUtil.DrawTableColumn(_objectManager.LastUpdate.ToString(CultureInfo.InvariantCulture)); ImGui.TableNextColumn(); - ImUtf8.DrawTableColumn("In Lobby"u8); - ImUtf8.DrawTableColumn(_objectManager.IsInLobby.ToString()); + ImGuiUtil.DrawTableColumn("World"); + ImGuiUtil.DrawTableColumn(_actors.Finished ? _actors.Data.ToWorldName(_objectManager.World) : "Service Missing"); + ImGuiUtil.DrawTableColumn(_objectManager.World.ToString()); + + ImGuiUtil.DrawTableColumn("Player Character"); + ImGuiUtil.DrawTableColumn($"{_objectManager.Player.Utf8Name} ({_objectManager.Player.Index})"); + ImGui.TableNextColumn(); + ImGuiUtil.CopyOnClickSelectable(_objectManager.Player.ToString()); + + ImGuiUtil.DrawTableColumn("In GPose"); + ImGuiUtil.DrawTableColumn(_objectManager.IsInGPose.ToString()); ImGui.TableNextColumn(); if (_objectManager.IsInGPose) { - ImUtf8.DrawTableColumn("GPose Player"u8); - ImUtf8.DrawTableColumn($"{_objectManager.GPosePlayer.Utf8Name} ({_objectManager.GPosePlayer.Index})"); + ImGuiUtil.DrawTableColumn("GPose Player"); + ImGuiUtil.DrawTableColumn($"{_objectManager.GPosePlayer.Utf8Name} ({_objectManager.GPosePlayer.Index})"); ImGui.TableNextColumn(); - ImUtf8.CopyOnClickSelectable(_objectManager.GPosePlayer.ToString()); + ImGuiUtil.CopyOnClickSelectable(_objectManager.GPosePlayer.ToString()); } - ImUtf8.DrawTableColumn("Number of Players"u8); - ImUtf8.DrawTableColumn(_objectManager.Count.ToString()); + ImGuiUtil.DrawTableColumn("Number of Players"); + ImGuiUtil.DrawTableColumn(_objectManager.Count.ToString()); ImGui.TableNextColumn(); } - var filterChanged = ImUtf8.InputText("##Filter"u8, ref _objectFilter, "Filter..."u8); - using var table2 = ImUtf8.Table("##data2"u8, 3, + var filterChanged = ImGui.InputTextWithHint("##Filter", "Filter...", ref _objectFilter, 64); + using var table2 = ImRaii.Table("##data2", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.ScrollY, new Vector2(-1, 20 * ImGui.GetTextLineHeightWithSpacing())); if (!table2) @@ -70,13 +69,13 @@ public class ObjectManagerPanel(ActorObjectManager _objectManager, ActorManager var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing()); ImGui.TableNextRow(); - var remainder = ImGuiClip.FilteredClippedDraw(_objectManager, skips, + var remainder = ImGuiClip.FilteredClippedDraw(_objectManager.Identifiers, skips, p => p.Value.Label.Contains(_objectFilter, StringComparison.OrdinalIgnoreCase), p => { - ImUtf8.DrawTableColumn(p.Key.ToString()); - ImUtf8.DrawTableColumn(p.Value.Label); - ImUtf8.DrawTableColumn(string.Join(", ", p.Value.Objects.OrderBy(a => a.Index).Select(a => a.Index.ToString()))); + ImGuiUtil.DrawTableColumn(p.Key.ToString()); + ImGuiUtil.DrawTableColumn(p.Value.Label); + ImGuiUtil.DrawTableColumn(string.Join(", ", p.Value.Objects.OrderBy(a => a.Index).Select(a => a.Index.ToString()))); }); ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeightWithSpacing()); } diff --git a/Glamourer/Gui/Tabs/DebugTab/PenumbraPanel.cs b/Glamourer/Gui/Tabs/DebugTab/PenumbraPanel.cs index 833ebe4..3714c82 100644 --- a/Glamourer/Gui/Tabs/DebugTab/PenumbraPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/PenumbraPanel.cs @@ -1,6 +1,6 @@ using Dalamud.Interface.Utility; using Glamourer.Interop.Penumbra; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.Api.Enums; @@ -49,7 +49,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem ImGui.TableNextColumn(); var address = _drawObject.Address; ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); - if (ImGui.InputScalar("##drawObjectPtr", ImGuiDataType.U64, ref address, nint.Zero, nint.Zero, "%llx", + if (ImGui.InputScalar("##drawObjectPtr", ImGuiDataType.U64, (nint)(&address), nint.Zero, nint.Zero, "%llx", ImGuiInputTextFlags.CharsHexadecimal)) _drawObject = address; ImGuiUtil.DrawTableColumn(_penumbra.Available @@ -61,7 +61,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale); ImGui.InputInt("##CutsceneIndex", ref _gameObjectIndex, 0, 0); ImGuiUtil.DrawTableColumn(_penumbra.Available - ? _penumbra.CutsceneParent((ushort)_gameObjectIndex).ToString() + ? _penumbra.CutsceneParent((ushort) _gameObjectIndex).ToString() : "Penumbra Unavailable"); ImGuiUtil.DrawTableColumn("Redraw Object"); @@ -76,9 +76,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem } ImGuiUtil.DrawTableColumn("Last Tooltip Date"); - ImGuiUtil.DrawTableColumn(_penumbraTooltip.LastTooltip > DateTime.MinValue - ? $"{_penumbraTooltip.LastTooltip.ToLongTimeString()} ({_penumbraTooltip.LastType} {_penumbraTooltip.LastId})" - : "Never"); + ImGuiUtil.DrawTableColumn(_penumbraTooltip.LastTooltip > DateTime.MinValue ? $"{_penumbraTooltip.LastTooltip.ToLongTimeString()} ({_penumbraTooltip.LastType} {_penumbraTooltip.LastId})" : "Never"); ImGui.TableNextColumn(); ImGuiUtil.DrawTableColumn("Last Click Date"); @@ -89,13 +87,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem ImGui.Separator(); foreach (var (slot, item) in _penumbraTooltip.LastItems) { - switch (slot) - { - case EquipSlot e: ImGuiUtil.DrawTableColumn($"{e.ToName()} Revert-Item"); break; - case BonusItemFlag f: ImGuiUtil.DrawTableColumn($"{f.ToName()} Revert-Item"); break; - default: ImGuiUtil.DrawTableColumn("Unk Revert-Item"); break; - } - + ImGuiUtil.DrawTableColumn($"{slot.ToName()} Revert-Item"); ImGuiUtil.DrawTableColumn(item.Valid ? item.Name : "None"); ImGui.TableNextColumn(); } diff --git a/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs b/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs index 21f0c50..2abc1db 100644 --- a/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs @@ -3,11 +3,10 @@ using Glamourer.Interop.Structs; using Glamourer.State; using OtterGui.Raii; using Penumbra.GameData.Gui.Debug; -using Penumbra.GameData.Interop; namespace Glamourer.Gui.Tabs.DebugTab; -public class RetainedStatePanel(StateManager _stateManager, ActorObjectManager _objectManager) : IGameDataDrawer +public class RetainedStatePanel(StateManager _stateManager, ObjectManager _objectManager) : IGameDataDrawer { public string Label => "Retained States (Inactive Actors)"; diff --git a/Glamourer/Gui/Tabs/DebugTab/UnlockableItemsPanel.cs b/Glamourer/Gui/Tabs/DebugTab/UnlockableItemsPanel.cs index b22008d..99c79ed 100644 --- a/Glamourer/Gui/Tabs/DebugTab/UnlockableItemsPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/UnlockableItemsPanel.cs @@ -1,7 +1,7 @@ using Dalamud.Interface.Utility; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using Penumbra.GameData.Enums; diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignColorCombo.cs b/Glamourer/Gui/Tabs/DesignTab/DesignColorCombo.cs index e9fe775..e59be09 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignColorCombo.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignColorCombo.cs @@ -1,5 +1,5 @@ using Glamourer.Designs; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Widgets; @@ -12,6 +12,13 @@ public sealed class DesignColorCombo(DesignColors _designColors, bool _skipAutom : _designColors.Keys.OrderBy(k => k).Prepend(DesignColors.AutomaticName), MouseWheelType.Control, Glamourer.Log) { + protected override void OnMouseWheel(string preview, ref int current, int steps) + { + if (CurrentSelectionIdx < 0) + CurrentSelectionIdx = Items.IndexOf(preview); + base.OnMouseWheel(preview, ref current, steps); + } + protected override bool DrawSelectable(int globalIdx, bool selected) { var isAutomatic = !_skipAutomatic && globalIdx == 0; diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs index 8a3dd06..1469deb 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs @@ -2,7 +2,7 @@ using Dalamud.Interface.ImGuiNotification; using Glamourer.Designs; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Raii; @@ -14,7 +14,6 @@ namespace Glamourer.Gui.Tabs.DesignTab; public class DesignDetailTab { private readonly SaveService _saveService; - private readonly Configuration _config; private readonly DesignFileSystemSelector _selector; private readonly DesignFileSystem _fileSystem; private readonly DesignManager _manager; @@ -31,20 +30,19 @@ public class DesignDetailTab private DesignFileSystem.Leaf? _changeLeaf; public DesignDetailTab(SaveService saveService, DesignFileSystemSelector selector, DesignManager manager, DesignFileSystem fileSystem, - DesignColors colors, Configuration config) + DesignColors colors) { _saveService = saveService; _selector = selector; _manager = manager; _fileSystem = fileSystem; _colors = colors; - _config = config; _colorCombo = new DesignColorCombo(_colors, false); } public void Draw() { - using var h = DesignPanelFlag.DesignDetails.Header(_config); + using var h = ImUtf8.CollapsingHeaderId("Design Details"u8); if (!h) return; @@ -161,8 +159,7 @@ public class DesignDetailTab ImGui.TableNextColumn(); if (ImUtf8.Checkbox("##ResetTemporarySettings"u8, ref resetTemporarySettings)) _manager.ChangeResetTemporarySettings(_selector.Selected!, resetTemporarySettings); - ImUtf8.HoverTooltip( - "Set this design to reset any temporary settings previously applied to the associated collection when it is applied through any means."u8); + ImUtf8.HoverTooltip("Set this design to reset any temporary settings previously applied to the associated collection when it is applied through any means."u8); ImUtf8.DrawFrameColumn("Color"u8); var colorName = _selector.Selected!.Color.Length == 0 ? DesignColors.AutomaticName : _selector.Selected!.Color; @@ -189,7 +186,10 @@ public class DesignDetailTab else if (_selector.Selected!.Color.Length != 0) { ImGui.SameLine(); - ImUtf8.Icon(FontAwesomeIcon.ExclamationCircle, "The color associated with this design does not exist."u8, _colors.MissingColor); + var size = new Vector2(ImGui.GetFrameHeight()); + using var font = ImRaii.PushFont(UiBuilder.IconFont); + ImGuiUtil.DrawTextButton(FontAwesomeIcon.ExclamationCircle.ToIconString(), size, 0, _colors.MissingColor); + ImUtf8.HoverTooltip("The color associated with this design does not exist."u8); } ImUtf8.DrawFrameColumn("Creation Date"u8); diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs b/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs index e0e4543..ea117c5 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignFileSystemSelector.cs @@ -5,14 +5,13 @@ using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Events; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Filesystem; using OtterGui.FileSystem.Selector; using OtterGui.Log; using OtterGui.Raii; -using OtterGui.Text; namespace Glamourer.Gui.Tabs.DesignTab; @@ -46,29 +45,6 @@ public sealed class DesignFileSystemSelector : FileSystemSelector _config.Ephemeral.CurrentDesignSelectorWidth * ImUtf8.GlobalScale; - - protected override float MinimumAbsoluteRemainder - => 470 * ImUtf8.GlobalScale; - - protected override float MinimumScaling - => _config.Ephemeral.DesignSelectorMinimumScale; - - protected override float MaximumScaling - => _config.Ephemeral.DesignSelectorMaximumScale; - - protected override void SetSize(Vector2 size) - { - base.SetSize(size); - var adaptedSize = MathF.Round(size.X / ImUtf8.GlobalScale); - if (adaptedSize == _config.Ephemeral.CurrentDesignSelectorWidth) - return; - - _config.Ephemeral.CurrentDesignSelectorWidth = adaptedSize; - _config.Ephemeral.Save(); - } - public DesignFileSystemSelector(DesignManager designManager, DesignFileSystem fileSystem, IKeyState keyState, DesignChanged @event, Configuration config, DesignConverter converter, TabSelected selectionEvent, Logger log, DesignColors designColors, DesignApplier designApplier) @@ -175,7 +151,7 @@ public sealed class DesignFileSystemSelector : FileSystemSelector slots) { var flags = (uint)(allFlags & _selector.Selected!.Application.Equip); - using var id = ImUtf8.PushId(label); + using var id = ImRaii.PushId(label); var bigChange = ImGui.CheckboxFlags($"Apply All {label}", ref flags, (uint)allFlags); if (stain) foreach (var slot in slots) { var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToStainFlag()) : _selector.Selected!.DoApplyStain(slot); - if (ImUtf8.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange) + if (ImGui.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange) _manager.ChangeApplyStains(_selector.Selected!, slot, apply); } else foreach (var slot in slots) { var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToFlag()) : _selector.Selected!.DoApplyEquip(slot); - if (ImUtf8.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange) + if (ImGui.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange) _manager.ChangeApplyItem(_selector.Selected!, slot, apply); } } @@ -314,115 +311,30 @@ public class DesignPanel EquipSlotExtensions.FullSlots); ImUtf8.IconDummy(); - DrawParameterApplication(); - - ImUtf8.IconDummy(); - DrawBonusSlotApplication(); + if (_config.UseAdvancedParameters) + { + DrawParameterApplication(); + } + else + { + DrawMetaApplication(); + ImUtf8.IconDummy(); + DrawBonusSlotApplication(); + } } } - private void DrawAllButtons() - { - var enabled = _config.DeleteDesignModifier.IsActive(); - bool? equip = null; - bool? customize = null; - var size = new Vector2(210 * ImUtf8.GlobalScale, 0); - if (ImUtf8.ButtonEx("Disable Everything"u8, - "Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, size, - !enabled)) - { - equip = false; - customize = false; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Enable Everything"u8, - "Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness."u8, size, - !enabled)) - { - equip = true; - customize = true; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - - if (ImUtf8.ButtonEx("Equipment Only"u8, - "Enable application of anything related to gear, disable anything that is not related to gear."u8, size, - !enabled)) - { - equip = true; - customize = false; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Customization Only"u8, - "Enable application of anything related to customization, disable anything that is not related to customization."u8, size, - !enabled)) - { - equip = false; - customize = true; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - - if (ImUtf8.ButtonEx("Default Application"u8, - "Set the application rules to the default values as if the design was newly created, without any advanced features or wetness."u8, - size, - !enabled)) - { - _manager.ChangeApplyMulti(_selector.Selected!, true, true, true, false, true, true, false, true); - _manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.Wetness, false); - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Disable Advanced"u8, "Disable all advanced dyes and customizations but keep everything else as is."u8, - size, - !enabled)) - _manager.ChangeApplyMulti(_selector.Selected!, null, null, null, false, null, null, false, null); - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteDesignModifier} while clicking."); - - if (equip is null && customize is null) - return; - - _manager.ChangeApplyMulti(_selector.Selected!, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null, - equip, equip, equip); - if (equip.HasValue) - { - _manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, equip.Value); - _manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.VisorState, equip.Value); - _manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.WeaponState, equip.Value); - _manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.EarState, equip.Value); - } - - if (customize.HasValue) - _manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.Wetness, customize.Value); - } - private static readonly IReadOnlyList MetaLabels = [ "Apply Wetness", "Apply Hat Visibility", "Apply Visor State", "Apply Weapon Visibility", - "Apply Viera Ear Visibility", ]; private void DrawMetaApplication() { - using var id = ImUtf8.PushId("Meta"); + using var id = ImRaii.PushId("Meta"); const uint all = (uint)MetaExtensions.All; var flags = (uint)_selector.Selected!.Application.Meta; var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all); @@ -430,7 +342,7 @@ public class DesignPanel foreach (var (index, label) in MetaExtensions.AllRelevant.Zip(MetaLabels)) { var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : _selector.Selected!.DoApplyMeta(index); - if (ImUtf8.Checkbox(label, ref apply) || bigChange) + if (ImGui.Checkbox(label, ref apply) || bigChange) _manager.ChangeApplyMeta(_selector.Selected!, index, apply); } } @@ -456,20 +368,20 @@ public class DesignPanel private void DrawParameterApplication() { - using var id = ImUtf8.PushId("Parameter"); + using var id = ImRaii.PushId("Parameter"); var flags = (uint)_selector.Selected!.Application.Parameters; var bigChange = ImGui.CheckboxFlags("Apply All Customize Parameters", ref flags, (uint)CustomizeParameterExtensions.All); foreach (var flag in CustomizeParameterExtensions.AllFlags) { var apply = bigChange ? ((CustomizeParameterFlag)flags).HasFlag(flag) : _selector.Selected!.DoApplyParameter(flag); - if (ImUtf8.Checkbox($"Apply {flag.ToName()}", ref apply) || bigChange) + if (ImGui.Checkbox($"Apply {flag.ToName()}", ref apply) || bigChange) _manager.ChangeApplyParameter(_selector.Selected!, flag, apply); } } public void Draw() { - using var group = ImUtf8.Group(); + using var group = ImRaii.Group(); if (_selector.SelectedPaths.Count > 1) { _multiDesignPanel.Draw(); @@ -507,12 +419,10 @@ public class DesignPanel using var table = ImUtf8.Table("##Panel", 1, ImGuiTableFlags.BordersOuter | ImGuiTableFlags.ScrollY, ImGui.GetContentRegionAvail()); if (!table || _selector.Selected == null) return; - ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableNextColumn(); if (_selector.Selected == null) return; - ImGui.Dummy(Vector2.Zero); DrawButtonRow(); ImGui.TableNextColumn(); @@ -550,7 +460,7 @@ public class DesignPanel if (_state.GetOrCreate(id, data.Objects[0], out var state)) { using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); - _state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true }); + _state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks); } } @@ -568,7 +478,7 @@ public class DesignPanel if (_state.GetOrCreate(id, data.Objects[0], out var state)) { using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys()); - _state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true }); + _state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks); } } @@ -725,7 +635,6 @@ public class DesignPanel var design = panel._converter.Convert(state, ApplicationRules.FromModifiers(state)) ?? throw new Exception("The clipboard did not contain valid data."); - panel._selector.Selected!.GetMaterialDataRef().Clear(); panel._manager.ApplyDesign(panel._selector.Selected!, design); } catch (Exception ex) diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs b/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs index 1b92291..9832451 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignTab.cs @@ -2,7 +2,7 @@ using Dalamud.Interface.Utility; using Glamourer.Designs; using Glamourer.Interop; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Classes; using OtterGui.Widgets; @@ -16,7 +16,7 @@ public class DesignTab(DesignFileSystemSelector _selector, DesignPanel _panel, I public void DrawContent() { - _selector.Draw(); + _selector.Draw(GetDesignSelectorSize()); if (_importService.CreateCharaTarget(out var designBase, out var name)) { var newDesign = _manager.CreateClone(designBase, name, true); @@ -27,4 +27,7 @@ public class DesignTab(DesignFileSystemSelector _selector, DesignPanel _panel, I _panel.Draw(); _importService.CreateCharaSource(); } + + public float GetDesignSelectorSize() + => 200f * ImGuiHelpers.GlobalScale; } diff --git a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs index 587fe65..5eda7cc 100644 --- a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs @@ -4,11 +4,9 @@ using Dalamud.Interface.Utility; using Dalamud.Utility; using Glamourer.Designs; using Glamourer.Interop.Penumbra; -using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Raii; using OtterGui.Text; using OtterGui.Text.Widget; @@ -17,15 +15,12 @@ namespace Glamourer.Gui.Tabs.DesignTab; public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelector selector, DesignManager manager, Configuration config) { - private readonly ModCombo _modCombo = new(penumbra, Glamourer.Log, selector); + private readonly ModCombo _modCombo = new(penumbra, Glamourer.Log); private (Mod, ModSettings)[]? _copy; public void Draw() { - using var h = DesignPanelFlag.ModAssociations.Header(config); - if (h.Disposed) - return; - + using var h = ImRaii.CollapsingHeader("Mod Associations"); ImGuiUtil.HoverTooltip( "This tab can store information about specific mods associated with this design.\n\n" + "It does NOT change any mod settings automatically, though there is functionality to apply desired mod settings manually.\n" @@ -71,8 +66,6 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect private void DrawApplyAllButton() { var (id, name) = penumbra.CurrentCollection; - if (config.Ephemeral.IncognitoMode) - name = id.ShortGuid(); if (ImGuiUtil.DrawDisabledButton($"Try Applying All Associated Mods to {name}##applyAll", new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, id == Guid.Empty)) ApplyAll(); @@ -90,7 +83,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect public void ApplyAll() { foreach (var (mod, settings) in selector.Selected!.AssociatedMods) - penumbra.SetMod(mod, settings, StateSource.Manual, false); + penumbra.SetMod(mod, settings); } private void DrawTable() @@ -104,7 +97,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect ImUtf8.TableSetupColumn("Mod Name"u8, ImGuiTableColumnFlags.WidthStretch); if (config.UseTemporarySettings) ImUtf8.TableSetupColumn("Remove"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Remove"u8).X); - ImUtf8.TableSetupColumn("Inherit"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Inherit"u8).X); + ImUtf8.TableSetupColumn("Inherit"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Inherit"u8).X); ImUtf8.TableSetupColumn("State"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("State"u8).X); ImUtf8.TableSetupColumn("Priority"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Priority"u8).X); ImUtf8.TableSetupColumn("##Options"u8, ImGuiTableColumnFlags.WidthFixed, ImUtf8.CalcTextSize("Applym"u8).X); @@ -208,7 +201,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect ImGui.TableNextColumn(); var inherit = settings.ForceInherit; - if (TwoStateCheckbox.Instance.Draw("##ForceInherit"u8, ref inherit)) + if (TwoStateCheckbox.Instance.Draw("##Enabled"u8, ref inherit)) updatedMod = (mod, settings with { ForceInherit = inherit }); ImUtf8.HoverTooltip("Force the mod to inherit its settings from inherited collections."u8); ImGui.TableNextColumn(); @@ -225,7 +218,7 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect if (ImGuiUtil.DrawDisabledButton("Apply", new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, !penumbra.Available)) { - var text = penumbra.SetMod(mod, settings, StateSource.Manual, false); + var text = penumbra.SetMod(mod, settings); if (text.Length > 0) Glamourer.Messager.NotificationMessage(text, NotificationType.Warning, false); } diff --git a/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs b/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs index 76579c2..53501b0 100644 --- a/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs +++ b/Glamourer/Gui/Tabs/DesignTab/ModCombo.cs @@ -1,21 +1,22 @@ using Dalamud.Interface.Utility; using Glamourer.Interop.Penumbra; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Classes; using OtterGui.Log; using OtterGui.Raii; -using OtterGui.Text; using OtterGui.Widgets; namespace Glamourer.Gui.Tabs.DesignTab; -public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings, int Count)> +public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings)> { - public ModCombo(PenumbraService penumbra, Logger log, DesignFileSystemSelector selector) - : base(() => penumbra.GetMods(selector.Selected?.FilteredItemNames.ToArray() ?? []), MouseWheelType.None, log) - => SearchByParts = false; + public ModCombo(PenumbraService penumbra, Logger log) + : base(penumbra.GetMods, MouseWheelType.None, log) + { + SearchByParts = false; + } - protected override string ToString((Mod Mod, ModSettings Settings, int Count) obj) + protected override string ToString((Mod Mod, ModSettings Settings) obj) => obj.Mod.Name; protected override bool IsVisible(int globalIndex, LowerString filter) @@ -23,45 +24,36 @@ public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings, protected override bool DrawSelectable(int globalIdx, bool selected) { - using var id = ImUtf8.PushId(globalIdx); - var (mod, settings, count) = Items[globalIdx]; + using var id = ImRaii.PushId(globalIdx); + var (mod, settings) = Items[globalIdx]; bool ret; - var color = settings.Enabled - ? count > 0 - ? ColorId.ContainsItemsEnabled.Value() - : ImGui.GetColorU32(ImGuiCol.Text) - : count > 0 - ? ColorId.ContainsItemsDisabled.Value() - : ImGui.GetColorU32(ImGuiCol.TextDisabled); - using (ImRaii.PushColor(ImGuiCol.Text, color)) + using (var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !settings.Enabled)) { - ret = ImUtf8.Selectable(mod.Name, selected); + ret = ImGui.Selectable(mod.Name, selected); } if (ImGui.IsItemHovered()) { using var style = ImRaii.PushStyle(ImGuiStyleVar.PopupBorderSize, 2 * ImGuiHelpers.GlobalScale); - using var tt = ImUtf8.Tooltip(); + using var tt = ImRaii.Tooltip(); var namesDifferent = mod.Name != mod.DirectoryName; ImGui.Dummy(new Vector2(300 * ImGuiHelpers.GlobalScale, 0)); - using (ImUtf8.Group()) + using (var group = ImRaii.Group()) { if (namesDifferent) - ImUtf8.Text("Directory Name"u8); - ImUtf8.Text("Enabled"u8); - ImUtf8.Text("Priority"u8); - ImUtf8.Text("Affected Design Items"u8); + ImGui.TextUnformatted("Directory Name"); + ImGui.TextUnformatted("Enabled"); + ImGui.TextUnformatted("Priority"); DrawSettingsLeft(settings); } ImGui.SameLine(Math.Max(ImGui.GetItemRectSize().X + 3 * ImGui.GetStyle().ItemSpacing.X, 150 * ImGuiHelpers.GlobalScale)); - using (ImUtf8.Group()) + using (var group = ImRaii.Group()) { if (namesDifferent) - ImUtf8.Text(mod.DirectoryName); - ImUtf8.Text($"{settings.Enabled}"); - ImUtf8.Text($"{settings.Priority}"); - ImUtf8.Text($"{count}"); + ImGui.TextUnformatted(mod.DirectoryName); + ImGui.TextUnformatted(settings.Enabled.ToString()); + ImGui.TextUnformatted(settings.Priority.ToString()); DrawSettingsRight(settings); } } @@ -73,7 +65,7 @@ public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings, { foreach (var setting in settings.Settings) { - ImUtf8.Text(setting.Key); + ImGui.TextUnformatted(setting.Key); for (var i = 1; i < setting.Value.Count; ++i) ImGui.NewLine(); } @@ -84,10 +76,10 @@ public sealed class ModCombo : FilterComboCache<(Mod Mod, ModSettings Settings, foreach (var setting in settings.Settings) { if (setting.Value.Count == 0) - ImUtf8.Text(""u8); + ImGui.TextUnformatted(""); else foreach (var option in setting.Value) - ImUtf8.Text(option); + ImGui.TextUnformatted(option); } } } diff --git a/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs b/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs index a68c191..a1f17d4 100644 --- a/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs +++ b/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs @@ -1,24 +1,15 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; using Glamourer.Designs; -using Glamourer.Interop.Material; -using Dalamud.Bindings.ImGui; -using OtterGui.Extensions; +using ImGuiNET; +using OtterGui; using OtterGui.Raii; using OtterGui.Text; -using static Glamourer.Gui.Tabs.HeaderDrawer; namespace Glamourer.Gui.Tabs.DesignTab; -public class MultiDesignPanel( - DesignFileSystemSelector selector, - DesignManager editor, - DesignColors colors, - Configuration config) +public class MultiDesignPanel(DesignFileSystemSelector selector, DesignManager editor, DesignColors colors) { - private readonly Button[] _leftButtons = []; - private readonly Button[] _rightButtons = [new IncognitoButton(config)]; - private readonly DesignColorCombo _colorCombo = new(colors, true); public void Draw() @@ -26,24 +17,14 @@ public class MultiDesignPanel( if (selector.SelectedPaths.Count == 0) return; - HeaderDrawer.Draw(string.Empty, 0, ImGui.GetColorU32(ImGuiCol.FrameBg), _leftButtons, _rightButtons); - using var child = ImUtf8.Child("##MultiPanel"u8, default, true); - if (!child) - return; - - var width = ImGuiHelpers.ScaledVector2(145, 0); + var width = ImGuiHelpers.ScaledVector2(145, 0); + ImGui.NewLine(); var treeNodePos = ImGui.GetCursorPos(); _numDesigns = DrawDesignList(); DrawCounts(treeNodePos); var offset = DrawMultiTagger(width); DrawMultiColor(width, offset); DrawMultiQuickDesignBar(offset); - DrawMultiLock(offset); - DrawMultiResetSettings(offset); - DrawMultiResetDyes(offset); - DrawMultiForceRedraw(offset); - DrawAdvancedButtons(offset); - DrawApplicationButtons(offset); } private void DrawCounts(Vector2 treeNodePos) @@ -62,54 +43,19 @@ public class MultiDesignPanel( ImGui.SetCursorPos(startPos); } - private void ResetCounts() - { - _numQuickDesignEnabled = 0; - _numDesignsLocked = 0; - _numDesignsForcedRedraw = 0; - _numDesignsResetSettings = 0; - _numDesignsResetDyes = 0; - _numDesignsWithAdvancedDyes = 0; - _numAdvancedDyes = 0; - } - - private bool CountLeaves(DesignFileSystem.IPath path) - { - if (path is not DesignFileSystem.Leaf l) - return false; - - if (l.Value.QuickDesign) - ++_numQuickDesignEnabled; - if (l.Value.WriteProtected()) - ++_numDesignsLocked; - if (l.Value.ResetTemporarySettings) - ++_numDesignsResetSettings; - if (l.Value.ForcedRedraw) - ++_numDesignsForcedRedraw; - if (l.Value.ResetAdvancedDyes) - ++_numDesignsResetDyes; - if (l.Value.Materials.Count > 0) - { - ++_numDesignsWithAdvancedDyes; - _numAdvancedDyes += l.Value.Materials.Count; - } - - return true; - } - private int DrawDesignList() { - ResetCounts(); using var tree = ImUtf8.TreeNode("Currently Selected Objects"u8, ImGuiTreeNodeFlags.DefaultOpen | ImGuiTreeNodeFlags.NoTreePushOnOpen); ImGui.Separator(); if (!tree) - return selector.SelectedPaths.Count(CountLeaves); + return selector.SelectedPaths.Count(l => l is DesignFileSystem.Leaf); var sizeType = new Vector2(ImGui.GetFrameHeight()); var availableSizePercent = (ImGui.GetContentRegionAvail().X - sizeType.X - 4 * ImGui.GetStyle().CellPadding.X) / 100; var sizeMods = availableSizePercent * 35; var sizeFolders = availableSizePercent * 65; + _numQuickDesignEnabled = 0; var numDesigns = 0; using (var table = ImUtf8.Table("mods"u8, 3, ImGuiTableFlags.RowBg)) { @@ -135,8 +81,12 @@ public class MultiDesignPanel( ImUtf8.DrawFrameColumn(text); ImUtf8.DrawFrameColumn(fullName); - if (CountLeaves(path)) - ++numDesigns; + if (path is not DesignFileSystem.Leaf l2) + continue; + + ++numDesigns; + if (l2.Value.QuickDesign) + ++_numQuickDesignEnabled; } } @@ -146,12 +96,6 @@ public class MultiDesignPanel( private string _tag = string.Empty; private int _numQuickDesignEnabled; - private int _numDesignsLocked; - private int _numDesignsForcedRedraw; - private int _numDesignsResetSettings; - private int _numDesignsResetDyes; - private int _numAdvancedDyes; - private int _numDesignsWithAdvancedDyes; private int _numDesigns; private readonly List _addDesigns = []; private readonly List<(Design, int)> _removeDesigns = []; @@ -160,7 +104,7 @@ public class MultiDesignPanel( { ImUtf8.TextFrameAligned("Multi Tagger:"u8); ImGui.SameLine(); - var offset = ImGui.GetItemRectSize().X + ImGui.GetStyle().WindowPadding.X; + var offset = ImGui.GetItemRectSize().X; ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 2 * (width.X + ImGui.GetStyle().ItemSpacing.X)); ImUtf8.InputText("##tag"u8, ref _tag, "Tag Name..."u8); @@ -204,119 +148,22 @@ public class MultiDesignPanel( ? $"All {_numDesigns} selected designs are already displayed in the quick design bar." : $"Display all {_numDesigns} selected designs in the quick design bar. Changes {diff} designs."; if (ImUtf8.ButtonEx("Display Selected Designs in QDB"u8, tt, buttonWidth, diff == 0)) - { foreach (var design in selector.SelectedPaths.OfType()) editor.SetQuickDesign(design.Value, true); - } ImGui.SameLine(); tt = _numQuickDesignEnabled == 0 ? $"All {_numDesigns} selected designs are already hidden in the quick design bar." : $"Hide all {_numDesigns} selected designs in the quick design bar. Changes {_numQuickDesignEnabled} designs."; if (ImUtf8.ButtonEx("Hide Selected Designs in QDB"u8, tt, buttonWidth, _numQuickDesignEnabled == 0)) - { foreach (var design in selector.SelectedPaths.OfType()) editor.SetQuickDesign(design.Value, false); - } - - ImGui.Separator(); - } - - private void DrawMultiLock(float offset) - { - ImUtf8.TextFrameAligned("Multi Lock:"u8); - ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X); - var buttonWidth = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsLocked; - var tt = diff == 0 - ? $"All {_numDesigns} selected designs are already write protected." - : $"Write-protect all {_numDesigns} designs. Changes {diff} designs."; - if (ImUtf8.ButtonEx("Turn Write-Protected"u8, tt, buttonWidth, diff == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.SetWriteProtection(design.Value, true); - - ImGui.SameLine(); - tt = _numDesignsLocked == 0 - ? $"None of the {_numDesigns} selected designs are write-protected." - : $"Remove the write protection of the {_numDesigns} selected designs. Changes {_numDesignsLocked} designs."; - if (ImUtf8.ButtonEx("Remove Write-Protection"u8, tt, buttonWidth, _numDesignsLocked == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.SetWriteProtection(design.Value, false); - ImGui.Separator(); - } - - private void DrawMultiResetSettings(float offset) - { - ImUtf8.TextFrameAligned("Settings:"u8); - ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X); - var buttonWidth = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsResetSettings; - var tt = diff == 0 - ? $"All {_numDesigns} selected designs already reset temporary settings." - : $"Make all {_numDesigns} selected designs reset temporary settings. Changes {diff} designs."; - if (ImUtf8.ButtonEx("Set Reset Temp. Settings"u8, tt, buttonWidth, diff == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetTemporarySettings(design.Value, true); - - ImGui.SameLine(); - tt = _numDesignsResetSettings == 0 - ? $"None of the {_numDesigns} selected designs reset temporary settings." - : $"Stop all {_numDesigns} selected designs from resetting temporary settings. Changes {_numDesignsResetSettings} designs."; - if (ImUtf8.ButtonEx("Remove Reset Temp. Settings"u8, tt, buttonWidth, _numDesignsResetSettings == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetTemporarySettings(design.Value, false); - ImGui.Separator(); - } - - private void DrawMultiResetDyes(float offset) - { - ImUtf8.TextFrameAligned("Adv. Dyes:"u8); - ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X); - var buttonWidth = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsResetDyes; - var tt = diff == 0 - ? $"All {_numDesigns} selected designs already reset advanced dyes." - : $"Make all {_numDesigns} selected designs reset advanced dyes. Changes {diff} designs."; - if (ImUtf8.ButtonEx("Set Reset Dyes"u8, tt, buttonWidth, diff == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetAdvancedDyes(design.Value, true); - - ImGui.SameLine(); - tt = _numDesignsLocked == 0 - ? $"None of the {_numDesigns} selected designs reset advanced dyes." - : $"Stop all {_numDesigns} selected designs from resetting advanced dyes. Changes {_numDesignsResetDyes} designs."; - if (ImUtf8.ButtonEx("Remove Reset Dyes"u8, tt, buttonWidth, _numDesignsResetDyes == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeResetAdvancedDyes(design.Value, false); - ImGui.Separator(); - } - - private void DrawMultiForceRedraw(float offset) - { - ImUtf8.TextFrameAligned("Redrawing:"u8); - ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X); - var buttonWidth = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X) / 2, 0); - var diff = _numDesigns - _numDesignsForcedRedraw; - var tt = diff == 0 - ? $"All {_numDesigns} selected designs already force redraws." - : $"Make all {_numDesigns} designs force redraws. Changes {diff} designs."; - if (ImUtf8.ButtonEx("Force Redraws"u8, tt, buttonWidth, diff == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeForcedRedraw(design.Value, true); - - ImGui.SameLine(); - tt = _numDesignsLocked == 0 - ? $"None of the {_numDesigns} selected designs force redraws." - : $"Stop all {_numDesigns} selected designs from forcing redraws. Changes {_numDesignsForcedRedraw} designs."; - if (ImUtf8.ButtonEx("Remove Forced Redraws"u8, tt, buttonWidth, _numDesignsForcedRedraw == 0)) - foreach (var design in selector.SelectedPaths.OfType()) - editor.ChangeForcedRedraw(design.Value, false); ImGui.Separator(); } private void DrawMultiColor(Vector2 width, float offset) { - ImUtf8.TextFrameAligned("Multi Colors:"u8); + ImUtf8.TextFrameAligned("Multi Colors:"); ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X); _colorCombo.Draw("##color", _colorCombo.CurrentSelection ?? string.Empty, "Select a design color.", ImGui.GetContentRegionAvail().X - 2 * (width.X + ImGui.GetStyle().ItemSpacing.X), ImGui.GetTextLineHeight()); @@ -335,10 +182,8 @@ public class MultiDesignPanel( : $"Set the color of {_addDesigns.Count} designs to \"{_colorCombo.CurrentSelection}\"\n\n\t{string.Join("\n\t", _addDesigns.Select(m => m.Name.Text))}"; ImGui.SameLine(); if (ImUtf8.ButtonEx(label, tooltip, width, _addDesigns.Count == 0)) - { foreach (var design in _addDesigns) editor.ChangeColor(design, _colorCombo.CurrentSelection!); - } label = _removeDesigns.Count > 0 ? $"Unset {_removeDesigns.Count} Designs" @@ -348,139 +193,12 @@ public class MultiDesignPanel( : $"Set {_removeDesigns.Count} designs to use automatic color again:\n\n\t{string.Join("\n\t", _removeDesigns.Select(m => m.Item1.Name.Text))}"; ImGui.SameLine(); if (ImUtf8.ButtonEx(label, tooltip, width, _removeDesigns.Count == 0)) - { foreach (var (design, _) in _removeDesigns) editor.ChangeColor(design, string.Empty); - } ImGui.Separator(); } - private void DrawAdvancedButtons(float offset) - { - ImUtf8.TextFrameAligned("Delete Adv."u8); - ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X); - var enabled = config.DeleteDesignModifier.IsActive(); - var tt = _numDesignsWithAdvancedDyes is 0 - ? "No selected designs contain any advanced dyes." - : $"Delete {_numAdvancedDyes} advanced dyes from {_numDesignsWithAdvancedDyes} of the selected designs."; - if (ImUtf8.ButtonEx("Delete All Advanced Dyes"u8, tt, new Vector2(ImGui.GetContentRegionAvail().X, 0), - !enabled || _numDesignsWithAdvancedDyes is 0)) - - foreach (var design in selector.SelectedPaths.OfType()) - { - while (design.Value.Materials.Count > 0) - editor.ChangeMaterialValue(design.Value, MaterialValueIndex.FromKey(design.Value.Materials[0].Item1), null); - } - - if (!enabled && _numDesignsWithAdvancedDyes is not 0) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking to delete."); - ImGui.Separator(); - } - - private void DrawApplicationButtons(float offset) - { - ImUtf8.TextFrameAligned("Application"u8); - ImGui.SameLine(offset, ImGui.GetStyle().ItemSpacing.X); - var width = new Vector2((ImGui.GetContentRegionAvail().X - ImGui.GetStyle().ItemSpacing.X) / 2, 0); - var enabled = config.DeleteDesignModifier.IsActive(); - bool? equip = null; - bool? customize = null; - var group = ImUtf8.Group(); - if (ImUtf8.ButtonEx("Disable Everything"u8, - _numDesigns > 0 - ? $"Disable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness for all {_numDesigns} designs." - : "No designs selected.", width, !enabled)) - { - equip = false; - customize = false; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); - - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Enable Everything"u8, - _numDesigns > 0 - ? $"Enable application of everything, including any existing advanced dyes, advanced customizations, crests and wetness for all {_numDesigns} designs." - : "No designs selected.", width, !enabled)) - { - equip = true; - customize = true; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); - - if (ImUtf8.ButtonEx("Equipment Only"u8, - _numDesigns > 0 - ? $"Enable application of anything related to gear, disable anything that is not related to gear for all {_numDesigns} designs." - : "No designs selected.", width, !enabled)) - { - equip = true; - customize = false; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); - - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Customization Only"u8, - _numDesigns > 0 - ? $"Enable application of anything related to customization, disable anything that is not related to customization for all {_numDesigns} designs." - : "No designs selected.", width, !enabled)) - { - equip = false; - customize = true; - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); - - if (ImUtf8.ButtonEx("Default Application"u8, - _numDesigns > 0 - ? $"Set the application rules to the default values as if the {_numDesigns} were newly created,without any advanced features or wetness." - : "No designs selected.", width, !enabled)) - foreach (var design in selector.SelectedPaths.OfType().Select(l => l.Value)) - { - editor.ChangeApplyMulti(design, true, true, true, false, true, true, false, true); - editor.ChangeApplyMeta(design, MetaIndex.Wetness, false); - } - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); - - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Disable Advanced"u8, _numDesigns > 0 - ? $"Disable all advanced dyes and customizations but keep everything else as is for all {_numDesigns} designs." - : "No designs selected.", width, !enabled)) - foreach (var design in selector.SelectedPaths.OfType().Select(l => l.Value)) - editor.ChangeApplyMulti(design, null, null, null, false, null, null, false, null); - - if (!enabled) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {config.DeleteDesignModifier} while clicking."); - - group.Dispose(); - ImGui.Separator(); - if (equip is null && customize is null) - return; - - foreach (var design in selector.SelectedPaths.OfType().Select(l => l.Value)) - { - editor.ChangeApplyMulti(design, equip, customize, equip, customize.HasValue && !customize.Value ? false : null, null, equip, equip, - equip); - if (equip.HasValue) - { - editor.ChangeApplyMeta(design, MetaIndex.HatState, equip.Value); - editor.ChangeApplyMeta(design, MetaIndex.VisorState, equip.Value); - editor.ChangeApplyMeta(design, MetaIndex.WeaponState, equip.Value); - } - - if (customize.HasValue) - editor.ChangeApplyMeta(design, MetaIndex.Wetness, customize.Value); - } - } - private void UpdateTagCache() { _addDesigns.Clear(); @@ -490,7 +208,7 @@ public class MultiDesignPanel( foreach (var leaf in selector.SelectedPaths.OfType()) { - var index = leaf.Value.Tags.AsEnumerable().IndexOf(_tag); + var index = leaf.Value.Tags.IndexOf(_tag); if (index >= 0) _removeDesigns.Add((leaf.Value, index)); else diff --git a/Glamourer/Gui/Tabs/HeaderDrawer.cs b/Glamourer/Gui/Tabs/HeaderDrawer.cs index cb169ba..0e9237d 100644 --- a/Glamourer/Gui/Tabs/HeaderDrawer.cs +++ b/Glamourer/Gui/Tabs/HeaderDrawer.cs @@ -1,6 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; @@ -44,36 +44,22 @@ public static class HeaderDrawer } } - public sealed class IncognitoButton(Configuration config) : Button + public sealed class IncognitoButton(EphemeralConfig config) : Button { protected override string Description - { - get - { - var hold = config.IncognitoModifier.IsActive(); - return (config.Ephemeral.IncognitoMode, hold) - switch - { - (true, true) => "Toggle incognito mode off.", - (false, true) => "Toggle incognito mode on.", - (true, false) => $"Toggle incognito mode off.\n\nHold {config.IncognitoModifier} while clicking to toggle.", - (false, false) => $"Toggle incognito mode on.\n\nHold {config.IncognitoModifier} while clicking to toggle.", - }; - } - } + => config.IncognitoMode + ? "Toggle incognito mode off." + : "Toggle incognito mode on."; protected override FontAwesomeIcon Icon - => config.Ephemeral.IncognitoMode + => config.IncognitoMode ? FontAwesomeIcon.EyeSlash : FontAwesomeIcon.Eye; protected override void OnClick() { - if (!config.IncognitoModifier.IsActive()) - return; - - config.Ephemeral.IncognitoMode = !config.Ephemeral.IncognitoMode; - config.Ephemeral.Save(); + config.IncognitoMode = !config.IncognitoMode; + config.Save(); } } diff --git a/Glamourer/Gui/Tabs/NpcTab/LocalNpcAppearanceData.cs b/Glamourer/Gui/Tabs/NpcTab/LocalNpcAppearanceData.cs index 1b70e27..7941c13 100644 --- a/Glamourer/Gui/Tabs/NpcTab/LocalNpcAppearanceData.cs +++ b/Glamourer/Gui/Tabs/NpcTab/LocalNpcAppearanceData.cs @@ -2,7 +2,7 @@ using Glamourer.Designs; using Glamourer.GameData; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Newtonsoft.Json; using Newtonsoft.Json.Linq; diff --git a/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs b/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs index 29fe7ef..312bceb 100644 --- a/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs +++ b/Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs @@ -5,21 +5,20 @@ using Glamourer.Designs; using Glamourer.Gui.Customization; using Glamourer.Gui.Equipment; using Glamourer.Gui.Tabs.DesignTab; +using Glamourer.Interop; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; using OtterGui.Raii; using OtterGui.Text; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; using static Glamourer.Gui.Tabs.HeaderDrawer; namespace Glamourer.Gui.Tabs.NpcTab; public class NpcPanel { - private readonly Configuration _config; private readonly DesignColorCombo _colorCombo; private string _newName = string.Empty; private DesignBase? _newDesign; @@ -30,7 +29,7 @@ public class NpcPanel private readonly DesignConverter _converter; private readonly DesignManager _designManager; private readonly StateManager _state; - private readonly ActorObjectManager _objects; + private readonly ObjectManager _objects; private readonly DesignColors _colors; private readonly Button[] _leftButtons; private readonly Button[] _rightButtons; @@ -42,9 +41,8 @@ public class NpcPanel DesignConverter converter, DesignManager designManager, StateManager state, - ActorObjectManager objects, - DesignColors colors, - Configuration config) + ObjectManager objects, + DesignColors colors) { _selector = selector; _favorites = favorites; @@ -55,7 +53,6 @@ public class NpcPanel _state = state; _objects = objects; _colors = colors; - _config = config; _colorCombo = new DesignColorCombo(colors, true); _leftButtons = [ @@ -142,14 +139,9 @@ public class NpcPanel private void DrawCustomization() { - if (_config.HideDesignPanel.HasFlag(DesignPanelFlag.Customization)) - return; - - var header = _selector.Selection.ModelId == 0 - ? "Customization" - : $"Customization (Model Id #{_selector.Selection.ModelId})###Customization"; - var expand = _config.AutoExpandDesignPanel.HasFlag(DesignPanelFlag.Customization); - using var h = ImUtf8.CollapsingHeaderId(header, expand ? ImGuiTreeNodeFlags.DefaultOpen : ImGuiTreeNodeFlags.None); + using var h = _selector.Selection.ModelId == 0 + ? ImUtf8.CollapsingHeaderId("Customization"u8) + : ImUtf8.CollapsingHeaderId($"Customization (Model Id #{_selector.Selection.ModelId})###Customization"); if (!h) return; @@ -159,7 +151,7 @@ public class NpcPanel private void DrawEquipment() { - using var h = DesignPanelFlag.Equipment.Header(_config); + using var h = ImUtf8.CollapsingHeaderId("Equipment"u8); if (!h) return; @@ -198,15 +190,13 @@ public class NpcPanel private void DrawApplyToSelf() { var (id, data) = _objects.PlayerData; - if (!ImUtf8.ButtonEx("Apply to Yourself"u8, - "Apply the current NPC appearance to your character.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8, - Vector2.Zero, !data.Valid)) + if (!ImUtf8.ButtonEx("Apply to Yourself"u8, "Apply the current NPC appearance to your character.\nHold Control to only apply gear.\nHold Shift to only apply customizations."u8, Vector2.Zero, !data.Valid)) return; if (_state.GetOrCreate(id, data.Objects[0], out var state)) { var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers()); - _state.ApplyDesign(state, design, ApplySettings.Manual with { IsFinal = true }); + _state.ApplyDesign(state, design, ApplySettings.Manual); } } @@ -224,14 +214,14 @@ public class NpcPanel if (_state.GetOrCreate(id, data.Objects[0], out var state)) { var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers()); - _state.ApplyDesign(state, design, ApplySettings.Manual with { IsFinal = true }); + _state.ApplyDesign(state, design, ApplySettings.Manual); } } private void DrawAppearanceInfo() { - using var h = DesignPanelFlag.AppearanceDetails.Header(_config); + using var h = ImUtf8.CollapsingHeaderId("Appearance Details"u8); if (!h) return; diff --git a/Glamourer/Gui/Tabs/NpcTab/NpcSelector.cs b/Glamourer/Gui/Tabs/NpcTab/NpcSelector.cs index 8497ab4..b3f0cef 100644 --- a/Glamourer/Gui/Tabs/NpcTab/NpcSelector.cs +++ b/Glamourer/Gui/Tabs/NpcTab/NpcSelector.cs @@ -1,7 +1,6 @@ using Glamourer.GameData; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; -using OtterGui.Extensions; using OtterGui.Raii; using ImGuiClip = OtterGui.ImGuiClip; diff --git a/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs b/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs index 318e017..4efa4c3 100644 --- a/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs +++ b/Glamourer/Gui/Tabs/NpcTab/NpcTab.cs @@ -1,5 +1,5 @@ using Dalamud.Interface.Utility; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Widgets; namespace Glamourer.Gui.Tabs.NpcTab; diff --git a/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs b/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs index 1dc9331..b4d3740 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/CodeDrawer.cs @@ -1,7 +1,7 @@ using Dalamud.Interface; using Glamourer.Services; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Filesystem; using OtterGui.Raii; using OtterGui.Services; diff --git a/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs b/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs index 7080b4d..98ba870 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/CollectionCombo.cs @@ -1,6 +1,6 @@ using Dalamud.Interface; using Glamourer.Interop.Penumbra; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Log; using OtterGui.Raii; diff --git a/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs b/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs index 5c4fec3..d976d28 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/CollectionOverrideDrawer.cs @@ -1,19 +1,19 @@ using Dalamud.Interface; using Glamourer.Interop.Penumbra; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Services; using Penumbra.GameData.Actors; -using Penumbra.GameData.Interop; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.Gui.Tabs.SettingsTab; public class CollectionOverrideDrawer( CollectionOverrideService collectionOverrides, Configuration config, - ActorObjectManager objects, + ObjectManager objects, ActorManager actors, PenumbraService penumbra, CollectionCombo combo) : IService @@ -61,8 +61,7 @@ public class CollectionOverrideDrawer( DrawActorIdentifier(idx, actor); ImGui.TableNextColumn(); - if (combo.Draw("##collection", name, $"Select the overriding collection. Current GUID:", ImGui.GetContentRegionAvail().X, - ImGui.GetTextLineHeight())) + if (combo.Draw("##collection", name, $"Select the overriding collection. Current GUID:", ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight())) { var (guid, _, newName) = combo.CurrentSelection; collectionOverrides.ChangeOverride(idx, guid, newName); @@ -70,7 +69,7 @@ public class CollectionOverrideDrawer( if (ImGui.IsItemHovered()) { - using var tt = ImRaii.Tooltip(); + using var tt = ImRaii.Tooltip(); using var font = ImRaii.PushFont(UiBuilder.MonoFont); ImGui.TextUnformatted($" {collection}"); } @@ -103,7 +102,7 @@ public class CollectionOverrideDrawer( return; using var tt2 = ImRaii.Tooltip(); - using var f = ImRaii.PushFont(UiBuilder.MonoFont); + using var f = ImRaii.PushFont(UiBuilder.MonoFont); ImGui.TextUnformatted(collection.ToString()); } @@ -123,7 +122,7 @@ public class CollectionOverrideDrawer( { if (source) { - ImGui.SetDragDropPayload("DraggingOverride", null, 0); + ImGui.SetDragDropPayload("DraggingOverride", nint.Zero, 0); ImGui.TextUnformatted($"Reordering Override #{idx + 1}..."); _dragDropIndex = idx; } @@ -147,7 +146,7 @@ public class CollectionOverrideDrawer( } catch (ActorIdentifierFactory.IdentifierParseError e) { - _exception = e; + _exception = e; _identifiers = []; } diff --git a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs index 6432811..ab40a48 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs @@ -1,17 +1,14 @@ -using Dalamud.Bindings.ImGui; -using Dalamud.Game.ClientState.Keys; +using Dalamud.Game.ClientState.Keys; using Dalamud.Interface; using Dalamud.Interface.Components; using Dalamud.Interface.Utility; using Dalamud.Plugin.Services; using Glamourer.Automation; using Glamourer.Designs; -using Glamourer.Events; using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Interop; using Glamourer.Interop.PalettePlus; -using Glamourer.Interop.Penumbra; -using Glamourer.Services; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Text; @@ -28,12 +25,11 @@ public class SettingsTab( IKeyState keys, DesignColorUi designColorUi, PaletteImport paletteImport, + PalettePlusChecker paletteChecker, CollectionOverrideDrawer overrides, CodeDrawer codeDrawer, Glamourer glamourer, - AutoDesignApplier autoDesignApplier, - AutoRedrawChanged autoRedraw, - PcpService pcpService) + AutoDesignApplier autoDesignApplier) : ITab { private readonly VirtualKey[] _validKeys = keys.GetValidVirtualKeys().Prepend(VirtualKey.NO_KEY).ToArray(); @@ -43,12 +39,12 @@ public class SettingsTab( public void DrawContent() { - using var child = ImUtf8.Child("MainWindowChild"u8, default); + using var child = ImRaii.Child("MainWindowChild"); if (!child) return; - Checkbox("Enable Auto Designs"u8, - "Enable the application of designs associated to characters in the Automation tab to be applied automatically."u8, + Checkbox("Enable Auto Designs", + "Enable the application of designs associated to characters in the Automation tab to be applied automatically.", config.EnableAutoDesigns, v => { config.EnableAutoDesigns = v; @@ -59,7 +55,7 @@ public class SettingsTab( ImGui.NewLine(); ImGui.NewLine(); - using (ImUtf8.Child("SettingsChild"u8, default)) + using (ImRaii.Child("SettingsChild")) { DrawBehaviorSettings(); DrawDesignDefaultSettings(); @@ -72,75 +68,48 @@ public class SettingsTab( MainWindow.DrawSupportButtons(glamourer, changelog.Changelog); } - public void DrawPenumbraIntegrationSettings() - { - DrawPenumbraIntegrationSettings1(); - DrawPenumbraIntegrationSettings2(); - } - private void DrawBehaviorSettings() { - if (!ImUtf8.CollapsingHeader("Glamourer Behavior"u8)) + if (!ImGui.CollapsingHeader("Glamourer Behavior")) return; - Checkbox("Always Apply Entire Weapon for Mainhand"u8, - "When manually applying a mainhand item, will also apply a corresponding offhand and potentially gauntlets for certain fist weapons."u8, + Checkbox("Always Apply Entire Weapon for Mainhand", + "When manually applying a mainhand item, will also apply a corresponding offhand and potentially gauntlets for certain fist weapons.", config.ChangeEntireItem, v => config.ChangeEntireItem = v); - Checkbox("Use Replacement Gear for Gear Unavailable to Your Race or Gender"u8, - "Use different gender- and race-appropriate models as a substitute when detecting certain items not available for a characters current gender and race."u8, + Checkbox("Use Replacement Gear for Gear Unavailable to Your Race or Gender", + "Use different gender- and race-appropriate models as a substitute when detecting certain items not available for a characters current gender and race.", config.UseRestrictedGearProtection, v => config.UseRestrictedGearProtection = v); - Checkbox("Do Not Apply Unobtained Items in Automation"u8, - "Enable this if you want automatically applied designs to only consider items and customizations you have actually unlocked once, and skip those you have not."u8, + Checkbox("Do Not Apply Unobtained Items in Automation", + "Enable this if you want automatically applied designs to only consider items and customizations you have actually unlocked once, and skip those you have not.", config.UnlockedItemMode, v => config.UnlockedItemMode = v); - Checkbox("Respect Manual Changes When Editing Automation"u8, - "Whether changing any currently active automation group will respect manual changes to the character before re-applying the changed automation or not."u8, + Checkbox("Respect Manual Changes When Editing Automation", + "Whether changing any currently active automation group will respect manual changes to the character before re-applying the changed automation or not.", config.RespectManualOnAutomationUpdate, v => config.RespectManualOnAutomationUpdate = v); - Checkbox("Enable Festival Easter-Eggs"u8, - "Glamourer may do some fun things on specific dates. Disable this if you do not want your experience disrupted by this."u8, + Checkbox("Enable Festival Easter-Eggs", + "Glamourer may do some fun things on specific dates. Disable this if you do not want your experience disrupted by this.", config.DisableFestivals == 0, v => config.DisableFestivals = v ? (byte)0 : (byte)2); - DrawPenumbraIntegrationSettings1(); - Checkbox("Revert Manual Changes on Zone Change"u8, - "Restores the old behaviour of reverting your character to its game or automation base whenever you change the zone."u8, + Checkbox("Auto-Reload Gear", + "Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection.", + config.AutoRedrawEquipOnChanges, v => config.AutoRedrawEquipOnChanges = v); + Checkbox("Revert Manual Changes on Zone Change", + "Restores the old behaviour of reverting your character to its game or automation base whenever you change the zone.", config.RevertManualChangesOnZoneChange, v => config.RevertManualChangesOnZoneChange = v); + Checkbox("Enable Advanced Customization Options", + "Enable the display and editing of advanced customization options like arbitrary colors.", + config.UseAdvancedParameters, paletteChecker.SetAdvancedParameters); PaletteImportButton(); - DrawPenumbraIntegrationSettings2(); - Checkbox("Prevent Random Design Repeats"u8, - "When using random designs, prevent the same design from being chosen twice in a row."u8, - config.PreventRandomRepeats, v => config.PreventRandomRepeats = v); - ImGui.NewLine(); - } - - private void DrawPenumbraIntegrationSettings1() - { - Checkbox("Auto-Reload Gear"u8, - "Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection."u8, - config.AutoRedrawEquipOnChanges, v => - { - config.AutoRedrawEquipOnChanges = v; - autoRedraw.Invoke(v); - }); - Checkbox("Attach to PCP-Handling"u8, - "Add the actor's glamourer state when a PCP is created by Penumbra, and create a design and apply it if possible when a PCP is installed by Penumbra."u8, - config.AttachToPcp, pcpService.Set); - var active = config.DeleteDesignModifier.IsActive(); - ImGui.SameLine(); - if (ImUtf8.ButtonEx("Delete all PCP Designs"u8, "Deletes all designs tagged with 'PCP' from the design list."u8, disabled: !active)) - pcpService.CleanPcpDesigns(); - if (!active) - ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"\nHold {config.DeleteDesignModifier} while clicking."); - } - - private void DrawPenumbraIntegrationSettings2() - { - Checkbox("Always Apply Associated Mods"u8, - "Whenever a design is applied to a character (including via automation), Glamourer will try to apply its associated mod settings to the collection currently associated with that character, if it is available.\n\n"u8 - + "Glamourer will NOT revert these applied settings automatically. This may mess up your collection and configuration.\n\n"u8 - + "If you enable this setting, you are aware that any resulting misconfiguration is your own fault."u8, + Checkbox("Enable Advanced Dye Options", + "Enable the display and editing of advanced dyes (color sets) for all equipment", + config.UseAdvancedDyes, v => config.UseAdvancedDyes = v); + Checkbox("Always Apply Associated Mods", + "Whenever a design is applied to a character (including via automation), Glamourer will try to apply its associated mod settings to the collection currently associated with that character, if it is available.\n\n" + + "Glamourer will NOT revert these applied settings automatically. This may mess up your collection and configuration.\n\n" + + "If you enable this setting, you are aware that any resulting misconfiguration is your own fault.", config.AlwaysApplyAssociatedMods, v => config.AlwaysApplyAssociatedMods = v); - Checkbox("Use Temporary Mod Settings"u8, - "Apply all settings as temporary settings so they will be reset when Glamourer or the game shut down."u8, - config.UseTemporarySettings, + Checkbox("Use Temporary Mod Settings", + "Apply all settings as temporary settings so they will be reset when Glamourer or the game shut down.", config.UseTemporarySettings, v => config.UseTemporarySettings = v); + ImGui.NewLine(); } private void DrawDesignDefaultSettings() @@ -148,59 +117,33 @@ public class SettingsTab( if (!ImUtf8.CollapsingHeader("Design Defaults")) return; - Checkbox("Locked Designs"u8, "Newly created designs will be locked to prevent unintended changes."u8, - config.DefaultDesignSettings.Locked, v => config.DefaultDesignSettings.Locked = v); - Checkbox("Show in Quick Design Bar"u8, "Newly created designs will be shown in the quick design bar by default."u8, + Checkbox("Show in Quick Design Bar", "Newly created designs will be shown in the quick design bar by default.", config.DefaultDesignSettings.ShowQuickDesignBar, v => config.DefaultDesignSettings.ShowQuickDesignBar = v); - Checkbox("Reset Advanced Dyes"u8, "Newly created designs will be configured to reset advanced dyes on application by default."u8, + Checkbox("Reset Advanced Dyes", "Newly created designs will be configured to reset advanced dyes on application by default.", config.DefaultDesignSettings.ResetAdvancedDyes, v => config.DefaultDesignSettings.ResetAdvancedDyes = v); - Checkbox("Always Force Redraw"u8, "Newly created designs will be configured to force character redraws on application by default."u8, + Checkbox("Always Force Redraw", "Newly created designs will be configured to force character redraws on application by default.", config.DefaultDesignSettings.AlwaysForceRedrawing, v => config.DefaultDesignSettings.AlwaysForceRedrawing = v); - Checkbox("Reset Temporary Settings"u8, - "Newly created designs will be configured to clear all advanced settings applied by Glamourer to the collection by default."u8, + Checkbox("Reset Temporary Settings", "Newly created designs will be configured to clear all advanced settings applied by Glamourer to the collection by default.", config.DefaultDesignSettings.ResetTemporarySettings, v => config.DefaultDesignSettings.ResetTemporarySettings = v); - - var tmp = config.PcpFolder; - ImGui.SetNextItemWidth(0.4f * ImGui.GetContentRegionAvail().X); - if (ImUtf8.InputText("##pcpFolder"u8, ref tmp)) - config.PcpFolder = tmp; - - if (ImGui.IsItemDeactivatedAfterEdit()) - config.Save(); - - ImGuiUtil.LabeledHelpMarker("Default PCP Organizational Folder", - "The folder any designs created due to penumbra character packs are moved to on creation.\nLeave blank to import into Root."); - - tmp = config.PcpColor; - ImGui.SetNextItemWidth(0.4f * ImGui.GetContentRegionAvail().X); - if (ImUtf8.InputText("##pcpColor"u8, ref tmp)) - config.PcpColor = tmp; - - if (ImGui.IsItemDeactivatedAfterEdit()) - config.Save(); - - ImGuiUtil.LabeledHelpMarker("Default PCP Design Color", - "The name of the color group any designs created due to penumbra character packs are assigned.\nLeave blank for no specific color assignment."); } private void DrawInterfaceSettings() { - if (!ImUtf8.CollapsingHeader("Interface"u8)) + if (!ImGui.CollapsingHeader("Interface")) return; - EphemeralCheckbox("Show Quick Design Bar"u8, - "Show a bar separate from the main window that allows you to quickly apply designs or revert your character and target."u8, + EphemeralCheckbox("Show Quick Design Bar", + "Show a bar separate from the main window that allows you to quickly apply designs or revert your character and target.", config.Ephemeral.ShowDesignQuickBar, v => config.Ephemeral.ShowDesignQuickBar = v); - EphemeralCheckbox("Lock Quick Design Bar"u8, "Prevent the quick design bar from being moved and lock it in place."u8, + EphemeralCheckbox("Lock Quick Design Bar", "Prevent the quick design bar from being moved and lock it in place.", config.Ephemeral.LockDesignQuickBar, v => config.Ephemeral.LockDesignQuickBar = v); if (Widget.ModifiableKeySelector("Hotkey to Toggle Quick Design Bar", "Set a hotkey that opens or closes the quick design bar.", 100 * ImGuiHelpers.GlobalScale, config.ToggleQuickDesignBar, v => config.ToggleQuickDesignBar = v, _validKeys)) config.Save(); - - Checkbox("Show Quick Design Bar in Main Window"u8, - "Show the quick design bar in the tab selection part of the main window, too."u8, + Checkbox("Show Quick Design Bar in Main Window", + "Show the quick design bar in the tab selection part of the main window, too.", config.ShowQuickBarInTabs, v => config.ShowQuickBarInTabs = v); DrawQuickDesignBoxes(); @@ -208,8 +151,8 @@ public class SettingsTab( ImGui.Separator(); ImGui.Dummy(Vector2.Zero); - Checkbox("Enable Game Context Menus"u8, "Whether to show a Try On via Glamourer button on context menus for equippable items."u8, - config.EnableGameContextMenu, v => + Checkbox("Enable Game Context Menus", "Whether to show a Try On via Glamourer button on context menus for equippable items.", + config.EnableGameContextMenu, v => { config.EnableGameContextMenu = v; if (v) @@ -217,142 +160,116 @@ public class SettingsTab( else contextMenuService.Disable(); }); - Checkbox("Show Window when UI is Hidden"u8, "Whether to show Glamourer windows even when the games UI is hidden."u8, - config.ShowWindowWhenUiHidden, v => + Checkbox("Show Window when UI is Hidden", "Whether to show Glamourer windows even when the games UI is hidden.", + config.ShowWindowWhenUiHidden, v => { config.ShowWindowWhenUiHidden = v; uiBuilder.DisableUserUiHide = v; }); - Checkbox("Hide Window in Cutscenes"u8, - "Whether the main Glamourer window should automatically be hidden when entering cutscenes or not."u8, + Checkbox("Hide Window in Cutscenes", "Whether the main Glamourer window should automatically be hidden when entering cutscenes or not.", config.HideWindowInCutscene, v => { config.HideWindowInCutscene = v; uiBuilder.DisableCutsceneUiHide = !v; }); - EphemeralCheckbox("Lock Main Window"u8, "Prevent the main window from being moved and lock it in place."u8, + EphemeralCheckbox("Lock Main Window", "Prevent the main window from being moved and lock it in place.", config.Ephemeral.LockMainWindow, v => config.Ephemeral.LockMainWindow = v); - Checkbox("Open Main Window at Game Start"u8, "Whether the main Glamourer window should be open or closed after launching the game."u8, - config.OpenWindowAtStart, v => config.OpenWindowAtStart = v); + Checkbox("Open Main Window at Game Start", "Whether the main Glamourer window should be open or closed after launching the game.", + config.OpenWindowAtStart, v => config.OpenWindowAtStart = v); ImGui.Dummy(Vector2.Zero); ImGui.Separator(); ImGui.Dummy(Vector2.Zero); - Checkbox("Smaller Equip Display"u8, "Use single-line display without icons and small dye buttons instead of double-line display."u8, - config.SmallEquip, v => config.SmallEquip = v); + Checkbox("Smaller Equip Display", "Use single-line display without icons and small dye buttons instead of double-line display.", + config.SmallEquip, v => config.SmallEquip = v); DrawHeightUnitSettings(); - Checkbox("Show Application Checkboxes"u8, - "Show the application checkboxes in the Customization and Equipment panels of the design tab, instead of only showing them under Application Rules."u8, + Checkbox("Show Application Checkboxes", + "Show the application checkboxes in the Customization and Equipment panels of the design tab, instead of only showing them under Application Rules.", !config.HideApplyCheckmarks, v => config.HideApplyCheckmarks = !v); if (Widget.DoubleModifierSelector("Design Deletion Modifier", "A modifier you need to hold while clicking the Delete Design button for it to take effect.", 100 * ImGuiHelpers.GlobalScale, config.DeleteDesignModifier, v => config.DeleteDesignModifier = v)) config.Save(); - if (Widget.DoubleModifierSelector("Incognito Modifier", - "A modifier you need to hold while clicking the Incognito button for it to take effect.", 100 * ImGuiHelpers.GlobalScale, - config.IncognitoModifier, v => config.IncognitoModifier = v)) - config.Save(); DrawRenameSettings(); - Checkbox("Auto-Open Design Folders"u8, - "Have design folders open or closed as their default state after launching."u8, config.OpenFoldersByDefault, + Checkbox("Auto-Open Design Folders", + "Have design folders open or closed as their default state after launching.", config.OpenFoldersByDefault, v => config.OpenFoldersByDefault = v); DrawFolderSortType(); - ImGui.NewLine(); - ImUtf8.Text("Show the following panels in their respective tabs:"u8); - ImGui.Dummy(Vector2.Zero); - DesignPanelFlagExtensions.DrawTable("##panelTable"u8, config.HideDesignPanel, config.AutoExpandDesignPanel, v => - { - config.HideDesignPanel = v; - config.Save(); - }, v => - { - config.AutoExpandDesignPanel = v; - config.Save(); - }); - - ImGui.Dummy(Vector2.Zero); ImGui.Separator(); ImGui.Dummy(Vector2.Zero); - Checkbox("Allow Double-Clicking Designs to Apply"u8, - "Tries to apply a design to the current player character When double-clicking it in the design selector."u8, + Checkbox("Allow Double-Clicking Designs to Apply", + "Tries to apply a design to the current player character When double-clicking it in the design selector.", config.AllowDoubleClickToApply, v => config.AllowDoubleClickToApply = v); - Checkbox("Show all Application Rule Checkboxes for Automation"u8, - "Show multiple separate application rule checkboxes for automated designs, instead of a single box for enabling or disabling."u8, + Checkbox("Show all Application Rule Checkboxes for Automation", + "Show multiple separate application rule checkboxes for automated designs, instead of a single box for enabling or disabling.", config.ShowAllAutomatedApplicationRules, v => config.ShowAllAutomatedApplicationRules = v); - Checkbox("Show Unobtained Item Warnings"u8, - "Show information whether you have unlocked all items and customizations in your automated design or not."u8, + Checkbox("Show Unobtained Item Warnings", + "Show information whether you have unlocked all items and customizations in your automated design or not.", config.ShowUnlockedItemWarnings, v => config.ShowUnlockedItemWarnings = v); - Checkbox("Show Color Display Config"u8, "Show the Color Display configuration options in the Advanced Customization panels."u8, - config.ShowColorConfig, v => config.ShowColorConfig = v); - Checkbox("Show Palette+ Import Button"u8, - "Show the import button that allows you to import Palette+ palettes onto a design in the Advanced Customization options section for designs."u8, - config.ShowPalettePlusImport, v => config.ShowPalettePlusImport = v); - using (ImRaii.PushId(1)) + if (config.UseAdvancedParameters) { + Checkbox("Show Color Display Config", "Show the Color Display configuration options in the Advanced Customization panels.", + config.ShowColorConfig, v => config.ShowColorConfig = v); + Checkbox("Show Palette+ Import Button", + "Show the import button that allows you to import Palette+ palettes onto a design in the Advanced Customization options section for designs.", + config.ShowPalettePlusImport, v => config.ShowPalettePlusImport = v); + using var id = ImRaii.PushId(1); PaletteImportButton(); } - Checkbox("Keep Advanced Dye Window Attached"u8, - "Keeps the advanced dye window expansion attached to the main window, or makes it freely movable."u8, - config.KeepAdvancedDyesAttached, v => config.KeepAdvancedDyesAttached = v); + if (config.UseAdvancedDyes) + Checkbox("Keep Advanced Dye Window Attached", + "Keeps the advanced dye window expansion attached to the main window, or makes it freely movable.", + config.KeepAdvancedDyesAttached, v => config.KeepAdvancedDyesAttached = v); - Checkbox("Debug Mode"u8, "Show the debug tab. Only useful for debugging or advanced use. Not recommended in general."u8, - config.DebugMode, + Checkbox("Debug Mode", "Show the debug tab. Only useful for debugging or advanced use. Not recommended in general.", config.DebugMode, v => config.DebugMode = v); ImGui.NewLine(); } private void DrawQuickDesignBoxes() { - var showAuto = config.EnableAutoDesigns; - var numColumns = 9 - (showAuto ? 0 : 2) - (config.UseTemporarySettings ? 0 : 1); + var showAuto = config.EnableAutoDesigns; + var showAdvanced = config.UseAdvancedParameters || config.UseAdvancedDyes; + var numColumns = 7 - (showAuto ? 0 : 2) - (showAdvanced ? 0 : 1); ImGui.NewLine(); - ImUtf8.Text("Show the Following Buttons in the Quick Design Bar:"u8); + ImGui.TextUnformatted("Show the Following Buttons in the Quick Design Bar:"); ImGui.Dummy(Vector2.Zero); - using var table = ImUtf8.Table("##tableQdb"u8, numColumns, + using var table = ImRaii.Table("##tableQdb", numColumns, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Borders | ImGuiTableFlags.NoHostExtendX); if (!table) return; - ReadOnlySpan<(string, bool, QdbButtons)> columns = - [ - ("Apply Design", true, QdbButtons.ApplyDesign), - ("Revert All", true, QdbButtons.RevertAll), - ("Revert to Auto", showAuto, QdbButtons.RevertAutomation), - ("Reapply Auto", showAuto, QdbButtons.ReapplyAutomation), - ("Revert Equip", true, QdbButtons.RevertEquip), - ("Revert Customize", true, QdbButtons.RevertCustomize), - ("Revert Advanced Customization", true, QdbButtons.RevertAdvancedCustomization), - ("Revert Advanced Dyes", true, QdbButtons.RevertAdvancedDyes), - ("Reset Settings", config.UseTemporarySettings, QdbButtons.ResetSettings), - ]; - - for (var i = 0; i < columns.Length; ++i) + var columns = new[] { - if (!columns[i].Item2) - continue; + (" Apply Design ", true, QdbButtons.ApplyDesign), + (" Revert All ", true, QdbButtons.RevertAll), + (" Revert to Auto ", showAuto, QdbButtons.RevertAutomation), + (" Reapply Auto ", showAuto, QdbButtons.ReapplyAutomation), + (" Revert Equip ", true, QdbButtons.RevertEquip), + (" Revert Customize ", true, QdbButtons.RevertCustomize), + (" Revert Advanced ", showAdvanced, QdbButtons.RevertAdvanced), + }; + foreach (var (label, _, _) in columns.Where(t => t.Item2)) + { ImGui.TableNextColumn(); - ImUtf8.TableHeader(columns[i].Item1); + ImGui.TableHeader(label); } - for (var i = 0; i < columns.Length; ++i) + foreach (var (_, _, flag) in columns.Where(t => t.Item2)) { - if (!columns[i].Item2) - continue; - - var flag = columns[i].Item3; - using var id = ImUtf8.PushId((int)flag); + using var id = ImRaii.PushId((int)flag); ImGui.TableNextColumn(); var offset = (ImGui.GetContentRegionAvail().X - ImGui.GetFrameHeight()) / 2; ImGui.SetCursorPosX(ImGui.GetCursorPosX() + offset); var value = config.QdbButtons.HasFlag(flag); - if (!ImUtf8.Checkbox(""u8, ref value)) + if (!ImGui.Checkbox(string.Empty, ref value)) continue; var buttons = value ? config.QdbButtons | flag : config.QdbButtons & ~flag; @@ -366,35 +283,35 @@ public class SettingsTab( private void PaletteImportButton() { - if (!config.ShowPalettePlusImport) + if (!config.UseAdvancedParameters || !config.ShowPalettePlusImport) return; ImGui.SameLine(); - if (ImUtf8.Button("Import Palette+ to Designs"u8)) + if (ImGui.Button("Import Palette+ to Designs")) paletteImport.ImportDesigns(); - ImUtf8.HoverTooltip( + ImGuiUtil.HoverTooltip( $"Import all existing Palettes from your Palette+ Config into Designs at PalettePlus/[Name] if these do not exist. Existing Palettes are:\n\n\t - {string.Join("\n\t - ", paletteImport.Data.Keys)}"); } /// Draw the entire Color subsection. private void DrawColorSettings() { - if (!ImUtf8.CollapsingHeader("Colors"u8)) + if (!ImGui.CollapsingHeader("Colors")) return; - using (var tree = ImUtf8.TreeNode("Custom Design Colors"u8)) + using (var tree = ImRaii.TreeNode("Custom Design Colors")) { if (tree) designColorUi.Draw(); } - using (var tree = ImUtf8.TreeNode("Color Settings"u8)) + using (var tree = ImRaii.TreeNode("Color Settings")) { if (tree) foreach (var color in Enum.GetValues()) { var (defaultColor, name, description) = color.Data(); - var currentColor = config.Colors.GetValueOrDefault(color, defaultColor); + var currentColor = config.Colors.TryGetValue(color, out var current) ? current : defaultColor; if (Widget.ColorPicker(name, description, currentColor, c => config.Colors[color] = c, defaultColor)) config.Save(); } @@ -404,33 +321,33 @@ public class SettingsTab( } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private void Checkbox(ReadOnlySpan label, ReadOnlySpan tooltip, bool current, Action setter) + private void Checkbox(string label, string tooltip, bool current, Action setter) { - using var id = ImUtf8.PushId(label); + using var id = ImRaii.PushId(label); var tmp = current; - if (ImUtf8.Checkbox(""u8, ref tmp) && tmp != current) + if (ImGui.Checkbox(string.Empty, ref tmp) && tmp != current) { setter(tmp); config.Save(); } ImGui.SameLine(); - ImUtf8.LabeledHelpMarker(label, tooltip); + ImGuiUtil.LabeledHelpMarker(label, tooltip); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private void EphemeralCheckbox(ReadOnlySpan label, ReadOnlySpan tooltip, bool current, Action setter) + private void EphemeralCheckbox(string label, string tooltip, bool current, Action setter) { - using var id = ImUtf8.PushId(label); + using var id = ImRaii.PushId(label); var tmp = current; - if (ImUtf8.Checkbox(""u8, ref tmp) && tmp != current) + if (ImGui.Checkbox(string.Empty, ref tmp) && tmp != current) { setter(tmp); config.Ephemeral.Save(); } ImGui.SameLine(); - ImUtf8.LabeledHelpMarker(label, tooltip); + ImGuiUtil.LabeledHelpMarker(label, tooltip); } /// Different supported sort modes as a combo. @@ -438,29 +355,29 @@ public class SettingsTab( { var sortMode = config.SortMode; ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale); - using (var combo = ImUtf8.Combo("##sortMode"u8, sortMode.Name)) + using (var combo = ImRaii.Combo("##sortMode", sortMode.Name)) { if (combo) foreach (var val in Configuration.Constants.ValidSortModes) { - if (ImUtf8.Selectable(val.Name, val.GetType() == sortMode.GetType()) && val.GetType() != sortMode.GetType()) + if (ImGui.Selectable(val.Name, val.GetType() == sortMode.GetType()) && val.GetType() != sortMode.GetType()) { config.SortMode = val; selector.SetFilterDirty(); config.Save(); } - ImUtf8.HoverTooltip(val.Description); + ImGuiUtil.HoverTooltip(val.Description); } } - ImUtf8.LabeledHelpMarker("Sort Mode"u8, "Choose the sort mode for the mod selector in the designs tab."u8); + ImGuiUtil.LabeledHelpMarker("Sort Mode", "Choose the sort mode for the mod selector in the designs tab."); } private void DrawRenameSettings() { ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale); - using (var combo = ImUtf8.Combo("##renameSettings"u8, config.ShowRename.GetData().Name)) + using (var combo = ImRaii.Combo("##renameSettings", config.ShowRename.GetData().Name)) { if (combo) foreach (var value in Enum.GetValues()) @@ -473,7 +390,7 @@ public class SettingsTab( config.Save(); } - ImUtf8.HoverTooltip(desc); + ImGuiUtil.HoverTooltip(desc); } } @@ -482,19 +399,19 @@ public class SettingsTab( "Select which of the two renaming input fields are visible when opening the right-click context menu of a design in the design selector."; ImGuiComponents.HelpMarker(tt); ImGui.SameLine(); - ImUtf8.Text("Rename Fields in Design Context Menu"u8); - ImUtf8.HoverTooltip(tt); + ImGui.TextUnformatted("Rename Fields in Design Context Menu"); + ImGuiUtil.HoverTooltip(tt); } private void DrawHeightUnitSettings() { ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale); - using (var combo = ImUtf8.Combo("##heightUnit"u8, HeightDisplayTypeName(config.HeightDisplayType))) + using (var combo = ImRaii.Combo("##heightUnit", HeightDisplayTypeName(config.HeightDisplayType))) { if (combo) foreach (var type in Enum.GetValues()) { - if (ImUtf8.Selectable(HeightDisplayTypeName(type), type == config.HeightDisplayType) && type != config.HeightDisplayType) + if (ImGui.Selectable(HeightDisplayTypeName(type), type == config.HeightDisplayType) && type != config.HeightDisplayType) { config.HeightDisplayType = type; config.Save(); @@ -506,20 +423,20 @@ public class SettingsTab( const string tt = "Select how to display the height of characters in real-world units, if at all."; ImGuiComponents.HelpMarker(tt); ImGui.SameLine(); - ImUtf8.Text("Character Height Display Type"u8); - ImUtf8.HoverTooltip(tt); + ImGui.TextUnformatted("Character Height Display Type"); + ImGuiUtil.HoverTooltip(tt); } - private static ReadOnlySpan HeightDisplayTypeName(HeightDisplayType type) + private static string HeightDisplayTypeName(HeightDisplayType type) => type switch { - HeightDisplayType.None => "Do Not Display"u8, - HeightDisplayType.Centimetre => "Centimetres (000.0 cm)"u8, - HeightDisplayType.Metre => "Metres (0.00 m)"u8, - HeightDisplayType.Wrong => "Inches (00.0 in)"u8, - HeightDisplayType.WrongFoot => "Feet (0'00'')"u8, - HeightDisplayType.Corgi => "Corgis (0.0 Corgis)"u8, - HeightDisplayType.OlympicPool => "Olympic-size swimming Pools (0.000 Pools)"u8, - _ => ""u8, + HeightDisplayType.None => "Do Not Display", + HeightDisplayType.Centimetre => "Centimetres (000.0 cm)", + HeightDisplayType.Metre => "Metres (0.00 m)", + HeightDisplayType.Wrong => "Inches (00.0 in)", + HeightDisplayType.WrongFoot => "Feet (0'00'')", + HeightDisplayType.Corgi => "Corgis (0.0 Corgis)", + HeightDisplayType.OlympicPool => "Olympic-size swimming Pools (0.000 Pools)", + _ => string.Empty, }; } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index 8644aeb..afc4f7b 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -1,11 +1,11 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Interface.Utility; +using Glamourer.Designs; using Glamourer.GameData; using Glamourer.Interop; -using Glamourer.Interop.Penumbra; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Raii; using OtterGui.Text; using Penumbra.GameData.Enums; @@ -23,8 +23,7 @@ public class UnlockOverview( TextureService textures, CodeService codes, JobService jobs, - FavoriteManager favorites, - PenumbraService penumbra) + FavoriteManager favorites) { private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f); @@ -33,9 +32,6 @@ public class UnlockOverview( private Gender _selected3 = Gender.Unknown; private BonusItemFlag _selected4 = BonusItemFlag.Unknown; - private uint _favoriteColor; - private uint _moddedColor; - private void DrawSelector() { using var child = ImRaii.Child("Selector", new Vector2(200 * ImGuiHelpers.GlobalScale, -1), true); @@ -94,9 +90,6 @@ public class UnlockOverview( if (!child) return; - _moddedColor = ColorId.ModdedItemMarker.Value(); - _favoriteColor = ColorId.FavoriteStarOn.Value(); - if (_selected1 is not FullEquipType.Unknown) DrawItems(); else if (_selected2 is not SubRace.Unknown && _selected3 is not Gender.Unknown) @@ -123,11 +116,11 @@ public class UnlockOverview( var unlocked = customizeUnlocks.IsUnlocked(customize, out var time); var icon = customizations.Manager.GetIcon(customize.IconId); var hasIcon = icon.TryGetWrap(out var wrap, out _); - ImGui.Image(wrap?.Handle ?? icon.GetWrapOrEmpty().Handle, iconSize, Vector2.Zero, Vector2.One, + ImGui.Image(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, iconSize, Vector2.Zero, Vector2.One, unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); if (favorites.Contains(_selected3, _selected2, customize.Index, customize.Value)) - ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), _favoriteColor, + ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), 12 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 6 * ImGuiHelpers.GlobalScale); if (hasIcon && ImGui.IsItemHovered()) @@ -135,7 +128,7 @@ public class UnlockOverview( using var tt = ImRaii.Tooltip(); var size = new Vector2(wrap!.Width, wrap.Height); if (size.X >= iconSize.X && size.Y >= iconSize.Y) - ImGui.Image(wrap.Handle, size); + ImGui.Image(wrap.ImGuiHandle, size); ImGui.TextUnformatted(unlockData.Name); ImGui.TextUnformatted($"{customize.Index.ToDefaultName()} {customize.Value.Value}"); ImGui.TextUnformatted(unlocked ? $"Unlocked on {time:g}" : "Not unlocked."); @@ -194,16 +187,14 @@ public class UnlockOverview( if (!textures.TryLoadIcon(item.IconId.Id, out var iconHandle)) return; - var (icon, size) = (iconHandle.Handle, new Vector2(iconHandle.Width, iconHandle.Height)); + var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height)); ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One, unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); if (favorites.Contains(item)) - ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), _favoriteColor, + ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), 2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale); - var mods = DrawModdedMarker(item, iconSize); - // TODO handle clicking if (ImGui.IsItemHovered()) { @@ -215,10 +206,9 @@ public class UnlockOverview( ImUtf8.Text($"{item.Id.Id}"); ImUtf8.Text($"{item.PrimaryId.Id}-{item.Variant.Id}"); // TODO - ImUtf8.Text("Always Unlocked"u8); // : $"Unlocked on {time:g}" : "Not Unlocked."); + ImUtf8.Text("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked."); // TODO //tooltip.CreateTooltip(item, string.Empty, false); - DrawModTooltip(mods); } } } @@ -265,7 +255,7 @@ public class UnlockOverview( if (!textures.TryLoadIcon(item.IconId.Id, out var iconHandle)) return; - var (icon, size) = (iconHandle.Handle, new Vector2(iconHandle.Width, iconHandle.Height)); + var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height)); ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One, unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); @@ -273,8 +263,6 @@ public class UnlockOverview( ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), 2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale); - var mods = DrawModdedMarker(item, iconSize); - if (ImGui.IsItemClicked()) Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString); @@ -318,7 +306,6 @@ public class UnlockOverview( ImGui.TextUnformatted("Tradable"); if (item.Flags.HasFlag(ItemFlags.IsCrestWorthy)) ImGui.TextUnformatted("Can apply Crest"); - DrawModTooltip(mods); tooltip.CreateTooltip(item, string.Empty, false); } } @@ -329,36 +316,4 @@ public class UnlockOverview( private static int IconsPerRow(float iconWidth, float iconSpacing) => (int)(ImGui.GetContentRegionAvail().X / (iconWidth + iconSpacing)); - - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - private (string ModDirectory, string ModName)[] DrawModdedMarker(in EquipItem item, Vector2 iconSize) - { - var mods = penumbra.CheckCurrentChangedItem(item.Name); - if (mods.Length == 0) - return mods; - - var center = ImGui.GetItemRectMin() + new Vector2(iconSize.X * 0.85f, iconSize.Y * 0.15f); - ImGui.GetWindowDrawList().AddCircleFilled(center, iconSize.X * 0.1f, _moddedColor); - ImGui.GetWindowDrawList().AddCircle(center, iconSize.X * 0.1f, 0xFF000000); - return mods; - } - - [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - private void DrawModTooltip((string ModDirectory, string ModName)[] mods) - { - switch (mods.Length) - { - case 0: return; - case 1: - ImUtf8.Text("Modded by: "u8, _moddedColor); - ImGui.SameLine(0, 0); - ImUtf8.Text(mods[0].ModName); - return; - default: - ImUtf8.Text("Modded by:"u8, _moddedColor); - foreach (var (_, mod) in mods) - ImUtf8.BulletText(mod); - return; - } - } } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs index d75f2dc..9651e85 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs @@ -3,10 +3,9 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; using Glamourer.Events; using Glamourer.Interop; -using Glamourer.Interop.Penumbra; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Raii; using OtterGui.Table; @@ -18,16 +17,12 @@ namespace Glamourer.Gui.Tabs.UnlocksTab; public class UnlockTable : Table, IDisposable { - private readonly ObjectUnlocked _event; - private readonly PenumbraService _penumbra; - - private Guid _lastCurrentCollection = Guid.Empty; + private readonly ObjectUnlocked _event; public UnlockTable(ItemManager items, TextureService textures, ItemUnlockManager itemUnlocks, - PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event, JobService jobs, FavoriteManager favorites, PenumbraService penumbra) + PenumbraChangedItemTooltip tooltip, ObjectUnlocked @event, JobService jobs, FavoriteManager favorites) : base("ItemUnlockTable", new ItemList(items), new FavoriteColumn(favorites, @event) { Label = "F" }, - new ModdedColumn(penumbra) { Label = "M" }, new NameColumn(textures, tooltip) { Label = "Item Name..." }, new SlotColumn { Label = "Equip Slot" }, new TypeColumn { Label = "Item Type..." }, @@ -41,40 +36,14 @@ public class UnlockTable : Table, IDisposable new TradableColumn { Label = "Trade" } ) { - _event = @event; - _penumbra = penumbra; - Sortable = true; - Flags |= ImGuiTableFlags.Hideable | ImGuiTableFlags.Reorderable | ImGuiTableFlags.Resizable; + _event = @event; + Sortable = true; + Flags |= ImGuiTableFlags.Hideable | ImGuiTableFlags.Reorderable | ImGuiTableFlags.Resizable; _event.Subscribe(OnObjectUnlock, ObjectUnlocked.Priority.UnlockTable); - _penumbra.ModSettingChanged += OnModSettingsChanged; - - } - - private void OnModSettingsChanged(Penumbra.Api.Enums.ModSettingChange type, Guid collection, string mod, bool inherited) - { - if (collection != _lastCurrentCollection) - return; - - FilterDirty = true; - SortDirty = true; - } - - protected override void PreDraw() - { - var lastCurrentCollection = _penumbra.CurrentCollection.Id; - if (_lastCurrentCollection != lastCurrentCollection) - { - _lastCurrentCollection = lastCurrentCollection; - FilterDirty = true; - SortDirty = true; - } } public void Dispose() - { - _event.Unsubscribe(OnObjectUnlock); - _penumbra.ModSettingChanged -= OnModSettingsChanged; - } + => _event.Unsubscribe(OnObjectUnlock); private sealed class FavoriteColumn : YesNoColumn { @@ -108,66 +77,6 @@ public class UnlockTable : Table, IDisposable => _favorites.Contains(rhs).CompareTo(_favorites.Contains(lhs)); } - private sealed class ModdedColumn : YesNoColumn - { - public override float Width - => ImGui.GetFrameHeightWithSpacing(); - - private readonly PenumbraService _penumbra; - private readonly Dictionary _compareCache = []; - - public ModdedColumn(PenumbraService penumbra) - { - _penumbra = penumbra; - Flags |= ImGuiTableColumnFlags.NoResize; - } - - public override void PostSort() - { - _compareCache.Clear(); - } - - public override void DrawColumn(EquipItem item, int idx) - { - var value = _penumbra.CheckCurrentChangedItem(item.Name); - if (value.Length == 0) - return; - - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.ModdedItemMarker.Value()); - ImGuiUtil.Center(FontAwesomeIcon.Circle.ToIconString()); - } - - if (ImGui.IsItemHovered()) - { - using var tt = ImUtf8.Tooltip(); - foreach (var (_, mod) in value) - ImUtf8.BulletText(mod); - } - } - - public override bool FilterFunc(EquipItem item) - => FilterValue.HasFlag(_penumbra.CheckCurrentChangedItem(item.Name).Length > 0 ? YesNoFlag.Yes : YesNoFlag.No); - - public override int Compare(EquipItem lhs, EquipItem rhs) - { - if (!_compareCache.TryGetValue(lhs.Id, out var lhsCount)) - { - lhsCount = _penumbra.CheckCurrentChangedItem(lhs.Name).Length; - _compareCache[lhs.Id] = lhsCount; - } - - if (!_compareCache.TryGetValue(rhs.Id, out var rhsCount)) - { - rhsCount = _penumbra.CheckCurrentChangedItem(rhs.Name).Length; - _compareCache[rhs.Id] = rhsCount; - } - - return lhsCount.CompareTo(rhsCount); - } - } - private sealed class NameColumn : ColumnString { private readonly TextureService _textures; @@ -408,6 +317,7 @@ public class UnlockTable : Table, IDisposable { } } + private sealed class JobColumn : ColumnFlags { public override float Width @@ -505,6 +415,7 @@ public class UnlockTable : Table, IDisposable } } + private sealed class DyableColumn : ColumnFlags { [Flags] diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs index 661b2a4..c2e06e5 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs @@ -1,6 +1,6 @@ using Dalamud.Interface; using Dalamud.Interface.Windowing; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Raii; using OtterGui; using OtterGui.Widgets; diff --git a/Glamourer/Gui/ToggleDrawData.cs b/Glamourer/Gui/ToggleDrawData.cs index 28afc2c..75edc72 100644 --- a/Glamourer/Gui/ToggleDrawData.cs +++ b/Glamourer/Gui/ToggleDrawData.cs @@ -1,5 +1,4 @@ -using Glamourer.Api.Enums; -using Glamourer.Designs; +using Glamourer.Designs; using Glamourer.State; using Penumbra.GameData.Enums; diff --git a/Glamourer/Gui/UiHelpers.cs b/Glamourer/Gui/UiHelpers.cs index 94ddb06..1ec79d7 100644 --- a/Glamourer/Gui/UiHelpers.cs +++ b/Glamourer/Gui/UiHelpers.cs @@ -2,7 +2,7 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; using Glamourer.Services; using Glamourer.Unlocks; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using Lumina.Misc; using OtterGui; using OtterGui.Raii; diff --git a/Glamourer/Interop/ContextMenuService.cs b/Glamourer/Interop/ContextMenuService.cs index 1f85612..71a9280 100644 --- a/Glamourer/Interop/ContextMenuService.cs +++ b/Glamourer/Interop/ContextMenuService.cs @@ -5,7 +5,6 @@ using Glamourer.Designs; using Glamourer.Services; using Glamourer.State; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Interop; @@ -14,16 +13,16 @@ public class ContextMenuService : IDisposable { public const int ChatLogContextItemId = 0x958; - private readonly ItemManager _items; - private readonly IContextMenu _contextMenu; - private readonly StateManager _state; - private readonly ActorObjectManager _objects; - private EquipItem _lastItem; - private readonly StainId[] _lastStains = new StainId[StainId.NumStains]; + private readonly ItemManager _items; + private readonly IContextMenu _contextMenu; + private readonly StateManager _state; + private readonly ObjectManager _objects; + private EquipItem _lastItem; + private readonly StainId[] _lastStains = new StainId[StainId.NumStains]; private readonly MenuItem _inventoryItem; - public ContextMenuService(ItemManager items, StateManager state, ActorObjectManager objects, Configuration config, + public ContextMenuService(ItemManager items, StateManager state, ObjectManager objects, Configuration config, IContextMenu context) { _contextMenu = context; diff --git a/Glamourer/Interop/CrestService.cs b/Glamourer/Interop/CrestService.cs index 2b55f94..95b3587 100644 --- a/Glamourer/Interop/CrestService.cs +++ b/Glamourer/Interop/CrestService.cs @@ -67,7 +67,7 @@ public sealed unsafe class CrestService : EventWrapperRef3 _crestChangeHook = null!; private void CrestChangeDetour(DrawDataContainer* container, byte crestFlags) @@ -122,7 +122,7 @@ public sealed unsafe class CrestService : EventWrapperRef3IsFreeCompanyCrestVisibleOnSlot(index); + return model.AsHuman->IsFreeCompanyCrestVisibleOnSlot(index) != 0; } case CrestType.Offhand: { @@ -130,7 +130,7 @@ public sealed unsafe class CrestService : EventWrapperRef3IsFreeCompanyCrestVisibleOnSlot(index); + return model.AsWeapon->IsFreeCompanyCrestVisibleOnSlot(index) != 0; } } diff --git a/Glamourer/Interop/ImportService.cs b/Glamourer/Interop/ImportService.cs index c6e90fd..b9dfbe1 100644 --- a/Glamourer/Interop/ImportService.cs +++ b/Glamourer/Interop/ImportService.cs @@ -3,7 +3,7 @@ using Dalamud.Interface.ImGuiNotification; using Glamourer.Designs; using Glamourer.Interop.CharaFile; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Classes; using Penumbra.GameData.Enums; using Penumbra.GameData.Files; diff --git a/Glamourer/Interop/InventoryService.cs b/Glamourer/Interop/InventoryService.cs index c30ae06..f0ed6b5 100644 --- a/Glamourer/Interop/InventoryService.cs +++ b/Glamourer/Interop/InventoryService.cs @@ -23,26 +23,34 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService _gearsetEvent = gearsetEvent; _moveItemHook = interop.HookFromAddress((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour); - _equipGearsetHook = interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetDetour); + // This can be uncommented after ClientStructs Updates with EquipGearsetInternal after merged PR. (See comment below) + //_equipGearsetInternalHook = interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetInternalDetour); + + // Can be removed after ClientStructs Update since this is only needed for current EquipGearsetInternal [Signature] + interop.InitializeFromAttributes(this); _moveItemHook.Enable(); - _equipGearsetHook.Enable(); + _equipGearsetInternalHook.Enable(); } public void Dispose() { _moveItemHook.Dispose(); - _equipGearsetHook.Dispose(); + _equipGearsetInternalHook.Dispose(); } + // This is the internal function processed by all sources of Equipping a gearset, such as hotbar gearset application and command gearset application. + // Currently is pending to ClientStructs for integration. See: https://github.com/aers/FFXIVClientStructs/pull/1277 + public const string EquipGearsetInternal = "40 55 53 56 57 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 4C 63 FA"; private delegate nint EquipGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId); - private readonly Hook _equipGearsetHook = null!; + [Signature(EquipGearsetInternal, DetourName = nameof(EquipGearSetInternalDetour))] + private readonly Hook _equipGearsetInternalHook = null!; - private nint EquipGearSetDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId) + private nint EquipGearSetInternalDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId) { var prior = module->CurrentGearsetIndex; - var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId); + var ret = _equipGearsetInternalHook.Original(module, gearsetId, glamourPlateId); var set = module->GetGearset((int)gearsetId); _gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob); Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})"); @@ -182,7 +190,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService // Invoked after calling Original, so the item is already moved. var inventory = manager->GetInventoryContainer(targetContainer); - if (inventory == null || inventory->IsLoaded || inventory->Size <= targetSlot) + if (inventory == null || inventory->Loaded == 0 || inventory->Size <= targetSlot) return false; var item = inventory->GetInventorySlot((int)targetSlot); diff --git a/Glamourer/Interop/Material/DirectXService.cs b/Glamourer/Interop/Material/DirectXService.cs index 8006a2f..a809a34 100644 --- a/Glamourer/Interop/Material/DirectXService.cs +++ b/Glamourer/Interop/Material/DirectXService.cs @@ -1,9 +1,12 @@ using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; +using Lumina.Data.Files; using OtterGui.Services; using Penumbra.GameData.Files.MaterialStructs; using Penumbra.String.Functions; using SharpGen.Runtime; using Vortice.Direct3D11; +using Vortice.DXGI; using MapFlags = Vortice.Direct3D11.MapFlags; using Texture = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture; @@ -11,7 +14,7 @@ namespace Glamourer.Interop.Material; public unsafe class DirectXService(IFramework framework) : IService { - private readonly object _lock = new(); + private readonly object _lock = new(); private readonly ConcurrentDictionary _textures = []; /// Generate a color table the way the game does inside the original texture, and release the original. @@ -29,7 +32,9 @@ public unsafe class DirectXService(IFramework framework) : IService lock (_lock) { - using var texture = new SafeTextureHandle(MaterialService.CreateColorTableTexture(), false); + using var texture = new SafeTextureHandle(Device.Instance()->CreateTexture2D(textureSize, 1, + (uint)TexFile.TextureFormat.R16G16B16A16F, + (uint)(TexFile.Attribute.TextureType2D | TexFile.Attribute.Managed | TexFile.Attribute.Immutable), 7), false); if (texture.IsInvalid) return false; @@ -114,7 +119,7 @@ public unsafe class DirectXService(IFramework framework) : IService { var desc = resource.Description1; - if (desc.Format is not Vortice.DXGI.Format.R16G16B16A16_Float + if (desc.Format is not Format.R16G16B16A16_Float || desc.Width != MaterialService.TextureWidth || desc.Height != MaterialService.TextureHeight || map.DepthPitch != map.RowPitch * desc.Height) diff --git a/Glamourer/Interop/Material/LiveColorTablePreviewer.cs b/Glamourer/Interop/Material/LiveColorTablePreviewer.cs index 3b9edb7..94cb9ca 100644 --- a/Glamourer/Interop/Material/LiveColorTablePreviewer.cs +++ b/Glamourer/Interop/Material/LiveColorTablePreviewer.cs @@ -1,5 +1,5 @@ using Dalamud.Plugin.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Services; using Penumbra.GameData.Files.MaterialStructs; using Penumbra.GameData.Structs; @@ -114,9 +114,8 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable var frame = DateTimeOffset.UtcNow.UtcTicks; var hueByte = frame % (steps * frameLength) / frameLength; var hue = (float)hueByte / steps; - Vector3 ret; - ImGui.ColorConvertHSVtoRGB(hue, 1, 1, &ret.X, &ret.Y, &ret.Z); - return ret; + ImGui.ColorConvertHSVtoRGB(hue, 1, 1, out var r, out var g, out var b); + return new Vector3(r, g, b); } public void Dispose() diff --git a/Glamourer/Interop/Material/MaterialManager.cs b/Glamourer/Interop/Material/MaterialManager.cs index 43e500b..f3c1875 100644 --- a/Glamourer/Interop/Material/MaterialManager.cs +++ b/Glamourer/Interop/Material/MaterialManager.cs @@ -41,6 +41,9 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHandle* material, ref StainIds stain, ref nint ret) { + if (!_config.UseAdvancedDyes) + return; + var actor = _penumbra.GameObjectFromDrawObject(characterBase); var validType = FindType(characterBase, actor, out var type); var (slotId, materialId) = FindMaterial(characterBase, material); @@ -62,7 +65,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable var drawData = type switch { - MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, (HumanSlot)slotId), + MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, slotId), _ => GetTempSlot((Weapon*)characterBase), }; var mode = PrepareColorSet.GetMode(material); @@ -157,23 +160,15 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable /// Find the type of the given draw object by checking the actors pointers. private static bool FindType(CharacterBase* characterBase, Actor actor, out MaterialValueIndex.DrawObjectType type) { + type = MaterialValueIndex.DrawObjectType.Human; if (!actor.Valid) - { - type = MaterialValueIndex.DrawObjectType.Invalid; return false; - } - if (actor.Model.AsCharacterBase == characterBase && ((Model)characterBase).IsHuman) - { - type = MaterialValueIndex.DrawObjectType.Human; + if (actor.Model.AsCharacterBase == characterBase) return true; - } if (!actor.AsObject->IsCharacter()) - { - type = MaterialValueIndex.DrawObjectType.Invalid; return false; - } if (actor.AsCharacter->DrawData.WeaponData[0].DrawObject == characterBase) { @@ -187,29 +182,16 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable return true; } - type = MaterialValueIndex.DrawObjectType.Invalid; return false; } /// We need to get the temporary set, variant and stain that is currently being set if it is available. - private static CharacterWeapon GetTempSlot(Human* human, HumanSlot slotId) + private static CharacterWeapon GetTempSlot(Human* human, byte slotId) { - if (human->ChangedEquipData is null) - return slotId.ToSpecificEnum() switch - { - EquipSlot slot => ((Model)human).GetArmor(slot).ToWeapon(0), - BonusItemFlag bonus => ((Model)human).GetBonus(bonus).ToWeapon(0), - _ => default, - }; + if (human->ChangedEquipData == null) + return ((Model)human).GetArmor(((uint)slotId).ToEquipSlot()).ToWeapon(0); - if (!slotId.ToSlotIndex(out var index)) - return default; - - var item = (ChangedEquipData*)human->ChangedEquipData + index; - if (index < 10) - return ((CharacterArmor*)item)->ToWeapon(0); - - return new CharacterWeapon(item->BonusModel, 0, item->BonusVariant, StainIds.None); + return ((CharacterArmor*)human->ChangedEquipData + slotId * 3)->ToWeapon(0); } /// @@ -218,11 +200,12 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable /// private static CharacterWeapon GetTempSlot(Weapon* weapon) { - var changedData = weapon->ChangedData; + // TODO: Use ClientStructs + var changedData = *(void**)((byte*)weapon + 0xA40); if (changedData == null) return new CharacterWeapon(weapon->ModelSetId, weapon->SecondaryId, (Variant)weapon->Variant, StainIds.FromWeapon(*weapon)); - return new CharacterWeapon(weapon->ModelSetId, changedData->SecondaryId, changedData->Variant, - new StainIds(changedData->Stain0, changedData->Stain1)); + return new CharacterWeapon(weapon->ModelSetId, *(SecondaryId*)changedData, ((Variant*)changedData)[2], + new StainIds(((StainId*)changedData)[3], ((StainId*)changedData)[4])); } } diff --git a/Glamourer/Interop/Material/MaterialService.cs b/Glamourer/Interop/Material/MaterialService.cs index 4893e14..f7ffe0f 100644 --- a/Glamourer/Interop/Material/MaterialService.cs +++ b/Glamourer/Interop/Material/MaterialService.cs @@ -1,5 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; +using Lumina.Data.Files; using Penumbra.GameData.Files.MaterialStructs; using Penumbra.GameData.Interop; using Texture = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture; @@ -8,24 +9,18 @@ namespace Glamourer.Interop.Material; public static unsafe class MaterialService { - private const TextureFormat Format = TextureFormat.R16G16B16A16_FLOAT; - private const TextureFlags Flags = TextureFlags.TextureType2D | TextureFlags.Managed | TextureFlags.Immutable; - public const int TextureWidth = 8; public const int TextureHeight = ColorTable.NumRows; public const int MaterialsPerModel = 10; - public static Texture* CreateColorTableTexture() + public static bool GenerateNewColorTable(in ColorTable.Table colorTable, out Texture* texture) { var textureSize = stackalloc int[2]; textureSize[0] = TextureWidth; textureSize[1] = TextureHeight; - return Device.Instance()->CreateTexture2D(textureSize, 1, Format, Flags, 7); - } - public static bool GenerateNewColorTable(in ColorTable.Table colorTable, out Texture* texture) - { - texture = CreateColorTableTexture(); + texture = Device.Instance()->CreateTexture2D(textureSize, 1, (uint)TexFile.TextureFormat.R16G16B16A16F, + (uint)(TexFile.Attribute.TextureType2D | TexFile.Attribute.Managed | TexFile.Attribute.Immutable), 7); if (texture == null) return false; @@ -68,9 +63,9 @@ public static unsafe class MaterialService return null; var material = (MaterialResourceHandle*) model.AsCharacterBase->MaterialsSpan[index].Value; - if (material == null || material->DataSet == null || material->DataSetSize < sizeof(ColorTable.Table) || !material->HasColorTable) + if (material == null || material->ColorTable == null) return null; - return (ColorTable.Table*)material->DataSet; + return (ColorTable.Table*)material->ColorTable; } } diff --git a/Glamourer/Interop/Material/MaterialValueIndex.cs b/Glamourer/Interop/Material/MaterialValueIndex.cs index eb3f71f..712b0d5 100644 --- a/Glamourer/Interop/Material/MaterialValueIndex.cs +++ b/Glamourer/Interop/Material/MaterialValueIndex.cs @@ -50,18 +50,6 @@ public readonly record struct MaterialValueIndex( return idx > 2 ? Invalid : new MaterialValueIndex(DrawObjectType.Human, (byte)(idx + 16), 0, 0); } - public string SlotName() - { - var slot = ToEquipSlot(); - if (slot is not EquipSlot.Unknown) - return slot.ToName(); - - if (DrawObject is DrawObjectType.Human && SlotIndex is 16) - return BonusItemFlag.Glasses.ToString(); - - return EquipSlot.Unknown.ToName(); - } - public EquipSlot ToEquipSlot() => DrawObject switch { @@ -71,13 +59,6 @@ public readonly record struct MaterialValueIndex( _ => EquipSlot.Unknown, }; - public BonusItemFlag ToBonusSlot() - => DrawObject switch - { - DrawObjectType.Human when SlotIndex > 15 => ((uint)SlotIndex - 16).ToBonusSlot(), - _ => BonusItemFlag.Unknown, - }; - public unsafe bool TryGetModel(Actor actor, out Model model) { if (!actor.Valid) diff --git a/Glamourer/Interop/Material/MaterialValueManager.cs b/Glamourer/Interop/Material/MaterialValueManager.cs index 01cb479..f1ec440 100644 --- a/Glamourer/Interop/Material/MaterialValueManager.cs +++ b/Glamourer/Interop/Material/MaterialValueManager.cs @@ -280,36 +280,6 @@ public readonly struct MaterialValueManager return true; } - public bool CheckExistenceSlot(MaterialValueIndex index) - { - var key = CheckExistence(index); - return key.Valid && key.DrawObject == index.DrawObject && key.SlotIndex == index.SlotIndex; - } - - public bool CheckExistenceMaterial(MaterialValueIndex index) - { - var key = CheckExistence(index); - return key.Valid && key.DrawObject == index.DrawObject && key.SlotIndex == index.SlotIndex && key.MaterialIndex == index.MaterialIndex; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private MaterialValueIndex CheckExistence(MaterialValueIndex index) - { - if (_values.Count == 0) - return MaterialValueIndex.Invalid; - - var key = index.Key; - var idx = Search(key); - if (idx >= 0) - return index; - - idx = ~idx; - if (idx >= _values.Count) - return MaterialValueIndex.Invalid; - - return MaterialValueIndex.FromKey(_values[idx].Key); - } - public bool RemoveValue(MaterialValueIndex index) => RemoveValue(index.Key); diff --git a/Glamourer/Interop/Material/PrepareColorSet.cs b/Glamourer/Interop/Material/PrepareColorSet.cs index 821a152..b44246b 100644 --- a/Glamourer/Interop/Material/PrepareColorSet.cs +++ b/Glamourer/Interop/Material/PrepareColorSet.cs @@ -27,8 +27,7 @@ public sealed unsafe class PrepareColorSet : base("Prepare Color Set ") { _updateColorSets = updateColorSets; - hooks.Provider.InitializeFromAttributes(this); - _task = hooks.CreateHook(Name, Sigs.PrepareColorSet, Detour, true); + _task = hooks.CreateHook(Name, Sigs.PrepareColorSet, Detour, true); } private readonly Task> _task; @@ -69,20 +68,22 @@ public sealed unsafe class PrepareColorSet public static bool TryGetColorTable(MaterialResourceHandle* material, StainIds stainIds, out ColorTable.Table table) { - if (material->DataSet == null || material->DataSetSize < sizeof(ColorTable.Table) || !material->HasColorTable) + if (material->ColorTable == null) { table = default; return false; } - var newTable = *(ColorTable.Table*)material->DataSet; + var newTable = *(ColorTable.Table*)material->ColorTable; if (GetDyeTable(material, out var dyeTable)) { if (stainIds.Stain1.Id != 0) - material->ReadStainingTemplate(dyeTable, stainIds.Stain1.Id, (Half*)&newTable, 0); + ((delegate* unmanaged)MaterialResourceHandle.MemberFunctionPointers + .ReadStainingTemplate)(material, dyeTable, stainIds.Stain1.Id, (Half*)(&newTable), 0); if (stainIds.Stain2.Id != 0) - material->ReadStainingTemplate(dyeTable, stainIds.Stain2.Id, (Half*)&newTable, 1); + ((delegate* unmanaged)MaterialResourceHandle.MemberFunctionPointers + .ReadStainingTemplate)(material, dyeTable, stainIds.Stain2.Id, (Half*)(&newTable), 1); } table = newTable; @@ -118,7 +119,7 @@ public sealed unsafe class PrepareColorSet case MaterialValueIndex.DrawObjectType.Human: return index.SlotIndex < 10 ? actor.Model.GetArmor(((uint)index.SlotIndex).ToEquipSlot()).Stains : StainIds.None; case MaterialValueIndex.DrawObjectType.Mainhand: - var mainhand = (Model)actor.AsCharacter->DrawData.WeaponData[0].DrawObject; + var mainhand = (Model)actor.AsCharacter->DrawData.WeaponData[1].DrawObject; return mainhand.IsWeapon ? StainIds.FromWeapon(*mainhand.AsWeapon) : StainIds.None; case MaterialValueIndex.DrawObjectType.Offhand: var offhand = (Model)actor.AsCharacter->DrawData.WeaponData[1].DrawObject; @@ -132,7 +133,7 @@ public sealed unsafe class PrepareColorSet public static ColorRow.Mode GetMode(MaterialResourceHandle* handle) => handle == null ? ColorRow.Mode.Dawntrail - : handle->ShpkName.AsSpan().SequenceEqual("characterlegacy.shpk"u8) + : handle->ShpkNameSpan.SequenceEqual("characterlegacy.shpk"u8) ? ColorRow.Mode.Legacy : ColorRow.Mode.Dawntrail; diff --git a/Glamourer/Interop/Material/UpdateColorSets.cs b/Glamourer/Interop/Material/UpdateColorSets.cs index e503bc6..a96c60f 100644 --- a/Glamourer/Interop/Material/UpdateColorSets.cs +++ b/Glamourer/Interop/Material/UpdateColorSets.cs @@ -8,7 +8,7 @@ public sealed class UpdateColorSets : FastHook { public delegate void Delegate(Model model, uint unk); - private readonly ThreadLocal _updatingModel = new(() => Model.Null); + private readonly ThreadLocal _updatingModel = new(); public UpdateColorSets(HookManager hooks) => Task = hooks.CreateHook("Update Color Sets", Sigs.UpdateColorSets, Detour, true); @@ -17,7 +17,7 @@ public sealed class UpdateColorSets : FastHook { _updatingModel.Value = model; Task.Result.Original(model, unk); - _updatingModel.Value = Model.Null; + _updatingModel.Value = nint.Zero; } public Model Get() diff --git a/Glamourer/Interop/ObjectManager.cs b/Glamourer/Interop/ObjectManager.cs new file mode 100644 index 0000000..b185f4a --- /dev/null +++ b/Glamourer/Interop/ObjectManager.cs @@ -0,0 +1,207 @@ +using Dalamud.Game.ClientState.Objects; +using Dalamud.Plugin; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game.Control; +using Glamourer.Interop.Structs; +using OtterGui.Log; +using Penumbra.GameData.Actors; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Interop; + +namespace Glamourer.Interop; + +public class ObjectManager( + IFramework framework, + IClientState clientState, + IObjectTable objects, + IDalamudPluginInterface pi, + Logger log, + ActorManager actors, + ITargetManager targets) + : global::Penumbra.GameData.Interop.ObjectManager(pi, log, framework, objects) +{ + public DateTime LastUpdate + => LastFrame; + + private DateTime _identifierUpdate; + public bool IsInGPose { get; private set; } + public ushort World { get; private set; } + + private readonly Dictionary _identifiers = new(200); + private readonly Dictionary _allWorldIdentifiers = new(200); + private readonly Dictionary _nonOwnedIdentifiers = new(200); + + public IReadOnlyDictionary Identifiers + => _identifiers; + + public override bool Update() + { + if (!base.Update() && _identifierUpdate >= LastUpdate) + return false; + + _identifierUpdate = LastUpdate; + World = (ushort)(Player.Valid ? Player.HomeWorld : 0); + _identifiers.Clear(); + _allWorldIdentifiers.Clear(); + _nonOwnedIdentifiers.Clear(); + + foreach (var actor in BattleNpcs.Concat(CutsceneCharacters)) + { + if (actor.Identifier(actors, out var identifier)) + HandleIdentifier(identifier, actor); + } + + void AddSpecial(ScreenActor idx, string label) + { + var actor = this[(int)idx]; + if (actor.Identifier(actors, out var ident)) + { + var data = new ActorData(actor, label); + _identifiers.Add(ident, data); + } + } + + AddSpecial(ScreenActor.CharacterScreen, "Character Screen Actor"); + AddSpecial(ScreenActor.ExamineScreen, "Examine Screen Actor"); + AddSpecial(ScreenActor.FittingRoom, "Fitting Room Actor"); + AddSpecial(ScreenActor.DyePreview, "Dye Preview Actor"); + AddSpecial(ScreenActor.Portrait, "Portrait Actor"); + AddSpecial(ScreenActor.Card6, "Card Actor 6"); + AddSpecial(ScreenActor.Card7, "Card Actor 7"); + AddSpecial(ScreenActor.Card8, "Card Actor 8"); + + foreach (var actor in EventNpcs) + { + if (actor.Identifier(actors, out var identifier)) + HandleIdentifier(identifier, actor); + } + + var gPose = GPosePlayer; + IsInGPose = gPose.Utf8Name.Length > 0; + return true; + } + + private void HandleIdentifier(ActorIdentifier identifier, Actor character) + { + if (!character.Model || !identifier.IsValid) + return; + + if (!_identifiers.TryGetValue(identifier, out var data)) + { + data = new ActorData(character, identifier.ToString()); + _identifiers[identifier] = data; + } + else + { + data.Objects.Add(character); + } + + if (identifier.Type is IdentifierType.Player or IdentifierType.Owned) + { + var allWorld = actors.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue, + identifier.Kind, + identifier.DataId); + + if (!_allWorldIdentifiers.TryGetValue(allWorld, out var allWorldData)) + { + allWorldData = new ActorData(character, allWorld.ToString()); + _allWorldIdentifiers[allWorld] = allWorldData; + } + else + { + allWorldData.Objects.Add(character); + } + } + + if (identifier.Type is IdentifierType.Owned) + { + var nonOwned = actors.CreateNpc(identifier.Kind, identifier.DataId); + if (!_nonOwnedIdentifiers.TryGetValue(nonOwned, out var nonOwnedData)) + { + nonOwnedData = new ActorData(character, nonOwned.ToString()); + _nonOwnedIdentifiers[nonOwned] = nonOwnedData; + } + else + { + nonOwnedData.Objects.Add(character); + } + } + } + + public Actor GPosePlayer + => this[(int)ScreenActor.GPosePlayer]; + + public Actor Player + => this[0]; + + public unsafe Actor Target + => clientState.IsGPosing ? TargetSystem.Instance()->GPoseTarget : TargetSystem.Instance()->Target; + + public Actor Focus + => targets.FocusTarget?.Address ?? nint.Zero; + + public Actor MouseOver + => targets.MouseOverTarget?.Address ?? nint.Zero; + + public (ActorIdentifier Identifier, ActorData Data) PlayerData + { + get + { + Update(); + return Player.Identifier(actors, out var ident) && _identifiers.TryGetValue(ident, out var data) + ? (ident, data) + : (ident, ActorData.Invalid); + } + } + + public (ActorIdentifier Identifier, ActorData Data) TargetData + { + get + { + Update(); + return Target.Identifier(actors, out var ident) && _identifiers.TryGetValue(ident, out var data) + ? (ident, data) + : (ident, ActorData.Invalid); + } + } + + /// Also handles All Worlds players and non-owned NPCs. + public bool ContainsKey(ActorIdentifier key) + => Identifiers.ContainsKey(key) || _allWorldIdentifiers.ContainsKey(key) || _nonOwnedIdentifiers.ContainsKey(key); + + public bool TryGetValue(ActorIdentifier key, out ActorData value) + => Identifiers.TryGetValue(key, out value); + + public bool TryGetValueAllWorld(ActorIdentifier key, out ActorData value) + => _allWorldIdentifiers.TryGetValue(key, out value); + + public bool TryGetValueNonOwned(ActorIdentifier key, out ActorData value) + => _nonOwnedIdentifiers.TryGetValue(key, out value); + + public ActorData this[ActorIdentifier key] + => Identifiers[key]; + + public IEnumerable Keys + => Identifiers.Keys; + + public IEnumerable Values + => Identifiers.Values; + + public bool GetName(string lowerName, out Actor actor) + { + (actor, var ret) = lowerName switch + { + "" => (Actor.Null, true), + "" => (Player, true), + "self" => (Player, true), + "" => (Target, true), + "target" => (Target, true), + "" => (Focus, true), + "focus" => (Focus, true), + "" => (MouseOver, true), + "mouseover" => (MouseOver, true), + _ => (Actor.Null, false), + }; + return ret; + } +} diff --git a/Glamourer/Interop/PalettePlus/PalettePlusChecker.cs b/Glamourer/Interop/PalettePlus/PalettePlusChecker.cs new file mode 100644 index 0000000..a5a5ed9 --- /dev/null +++ b/Glamourer/Interop/PalettePlus/PalettePlusChecker.cs @@ -0,0 +1,49 @@ +using Dalamud.Interface.ImGuiNotification; +using Dalamud.Plugin; +using OtterGui.Services; +using Notification = OtterGui.Classes.Notification; + +namespace Glamourer.Interop.PalettePlus; + +public sealed class PalettePlusChecker : IRequiredService, IDisposable +{ + private readonly Timer _paletteTimer; + private readonly Configuration _config; + private readonly IDalamudPluginInterface _pluginInterface; + + public PalettePlusChecker(Configuration config, IDalamudPluginInterface pluginInterface) + { + _config = config; + _pluginInterface = pluginInterface; + _paletteTimer = new Timer(_ => PalettePlusCheck(), null, TimeSpan.FromSeconds(30), Timeout.InfiniteTimeSpan); + } + + public void Dispose() + => _paletteTimer.Dispose(); + + public void SetAdvancedParameters(bool value) + { + _config.UseAdvancedParameters = value; + PalettePlusCheck(); + } + + private void PalettePlusCheck() + { + if (!_config.UseAdvancedParameters) + return; + + try + { + var subscriber = _pluginInterface.GetIpcSubscriber("PalettePlus.ApiVersion"); + subscriber.InvokeFunc(); + Glamourer.Messager.AddMessage(new Notification( + "You currently have Palette+ installed. This conflicts with Glamourers advanced options and will cause invalid state.\n\n" + + "Please uninstall Palette+ and restart your game. Palette+ is deprecated and no longer supported by Mare Synchronos.", + NotificationType.Warning, 10000)); + } + catch + { + // ignored + } + } +} diff --git a/Glamourer/Interop/Penumbra/ModSettingApplier.cs b/Glamourer/Interop/Penumbra/ModSettingApplier.cs index b94be09..5b27e6e 100644 --- a/Glamourer/Interop/Penumbra/ModSettingApplier.cs +++ b/Glamourer/Interop/Penumbra/ModSettingApplier.cs @@ -7,16 +7,17 @@ using Penumbra.GameData.Structs; namespace Glamourer.Interop.Penumbra; -public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip autoRedrawSkip, Configuration config, ActorObjectManager objects, CollectionOverrideService overrides) +public class ModSettingApplier(PenumbraService penumbra, Configuration config, ObjectManager objects, CollectionOverrideService overrides) : IService { private readonly HashSet _collectionTracker = []; - public void HandleStateApplication(ActorState state, MergedDesign design, StateSource source, bool skipAutoRedraw, bool respectManual) + public void HandleStateApplication(ActorState state, MergedDesign design) { if (!config.AlwaysApplyAssociatedMods || (design.AssociatedMods.Count == 0 && !design.ResetTemporarySettings)) return; + objects.Update(); if (!objects.TryGetValue(state.Identifier, out var data)) { Glamourer.Log.Verbose( @@ -25,7 +26,6 @@ public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip } _collectionTracker.Clear(); - using var skip = autoRedrawSkip.SkipAutoUpdates(skipAutoRedraw); foreach (var actor in data.Objects) { var (collection, _, overridden) = overrides.GetCollection(actor, state.Identifier); @@ -35,10 +35,10 @@ public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip if (!_collectionTracker.Add(collection)) continue; - var index = ResetOldSettings(collection, actor, source, design.ResetTemporarySettings, respectManual); + var index = ResetOldSettings(collection, actor, design.ResetTemporarySettings); foreach (var (mod, setting) in design.AssociatedMods) { - var message = penumbra.SetMod(mod, setting, source, respectManual, collection, index); + var message = penumbra.SetMod(mod, setting, collection, index); if (message.Length > 0) Glamourer.Log.Verbose($"[Mod Applier] Error applying mod settings: {message}"); else @@ -49,7 +49,7 @@ public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip } public (List Messages, int Applied, Guid Collection, string Name, bool Overridden) ApplyModSettings( - IReadOnlyDictionary settings, Actor actor, StateSource source, bool resetOther) + IReadOnlyDictionary settings, Actor actor, bool resetOther) { var (collection, name, overridden) = overrides.GetCollection(actor); if (collection == Guid.Empty) @@ -58,10 +58,10 @@ public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip var messages = new List(); var appliedMods = 0; - var index = ResetOldSettings(collection, actor, source, resetOther, true); + var index = ResetOldSettings(collection, actor, resetOther); foreach (var (mod, setting) in settings) { - var message = penumbra.SetMod(mod, setting, source, false, collection, index); + var message = penumbra.SetMod(mod, setting, collection, index); if (message.Length > 0) messages.Add($"Error applying mod settings: {message}"); else @@ -72,24 +72,16 @@ public class ModSettingApplier(PenumbraService penumbra, PenumbraAutoRedrawSkip } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ObjectIndex? ResetOldSettings(Guid collection, Actor actor, StateSource source, bool resetOther, bool respectManual) + private ObjectIndex? ResetOldSettings(Guid collection, Actor actor, bool resetOther) { ObjectIndex? index = actor.Valid ? actor.Index : null; if (!resetOther) return index; if (index == null) - { - penumbra.RemoveAllTemporarySettings(collection, source); - if (!respectManual && source.IsFixed()) - penumbra.RemoveAllTemporarySettings(collection, StateSource.Manual); - } + penumbra.RemoveAllTemporarySettings(collection); else - { - penumbra.RemoveAllTemporarySettings(index.Value, source); - if (!respectManual && source.IsFixed()) - penumbra.RemoveAllTemporarySettings(index.Value, StateSource.Manual); - } + penumbra.RemoveAllTemporarySettings(index.Value); return index; } } diff --git a/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs b/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs index 4e3c8e3..fbe0d9d 100644 --- a/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs +++ b/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs @@ -2,29 +2,26 @@ using Glamourer.Api.Enums; using Glamourer.Designs.History; using Glamourer.Events; +using Glamourer.Interop.Structs; using Glamourer.State; using OtterGui.Classes; using OtterGui.Services; using Penumbra.Api.Enums; -using Penumbra.GameData.Interop; namespace Glamourer.Interop.Penumbra; public class PenumbraAutoRedraw : IDisposable, IRequiredService { - private const int WaitFrames = 5; - private readonly Configuration _config; - private readonly PenumbraService _penumbra; - private readonly StateManager _state; - private readonly ActorObjectManager _objects; - private readonly IFramework _framework; - private readonly StateChanged _stateChanged; - private readonly PenumbraAutoRedrawSkip _skip; + private const int WaitFrames = 5; + private readonly Configuration _config; + private readonly PenumbraService _penumbra; + private readonly StateManager _state; + private readonly ObjectManager _objects; + private readonly IFramework _framework; + private readonly StateChanged _stateChanged; - - public PenumbraAutoRedraw(PenumbraService penumbra, Configuration config, StateManager state, ActorObjectManager objects, - IFramework framework, - StateChanged stateChanged, PenumbraAutoRedrawSkip skip) + public PenumbraAutoRedraw(PenumbraService penumbra, Configuration config, StateManager state, ObjectManager objects, IFramework framework, + StateChanged stateChanged) { _penumbra = penumbra; _config = config; @@ -32,7 +29,6 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService _objects = objects; _framework = framework; _stateChanged = stateChanged; - _skip = skip; _penumbra.ModSettingChanged += OnModSettingChange; _framework.Update += OnFramework; _stateChanged.Subscribe(OnStateChanged, StateChanged.Priority.PenumbraAutoRedraw); @@ -79,6 +75,7 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService { _framework.RunOnFrameworkThread(() => { + _objects.Update(); foreach (var (id, state) in _state) { if (!_objects.TryGetValue(id, out var actors) || !actors.Valid) @@ -91,13 +88,13 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService _actions.Enqueue((state, () => { foreach (var actor in actors.Objects) - _state.ReapplyState(actor, state, false, StateSource.IpcManual, true); + _state.ReapplyState(actor, state, false, StateSource.IpcManual); Glamourer.Log.Debug($"Automatically applied mod settings of type {type} to {id.Incognito(null)}."); }, WaitFrames)); } }); } - else if (_config.AutoRedrawEquipOnChanges && !_skip.Skip) + else if (_config.AutoRedrawEquipOnChanges) { // Only update once per frame. var playerName = _penumbra.GetCurrentPlayerCollection(); @@ -111,7 +108,7 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService _frame = currentFrame; _framework.RunOnFrameworkThread(() => { - _state.ReapplyState(_objects.Player, false, StateSource.IpcManual, true); + _state.ReapplyState(_objects.Player, false, StateSource.IpcManual); Glamourer.Log.Debug( $"Automatically applied mod settings of type {type} to {_objects.PlayerData.Identifier.Incognito(null)} (Local Player)."); }); diff --git a/Glamourer/Interop/Penumbra/PenumbraAutoRedrawSkip.cs b/Glamourer/Interop/Penumbra/PenumbraAutoRedrawSkip.cs deleted file mode 100644 index 8ef522c..0000000 --- a/Glamourer/Interop/Penumbra/PenumbraAutoRedrawSkip.cs +++ /dev/null @@ -1,15 +0,0 @@ -using OtterGui.Classes; -using OtterGui.Services; - -namespace Glamourer.Interop.Penumbra; - -public class PenumbraAutoRedrawSkip : IService -{ - private bool _skipAutoUpdates; - - public BoolSetter SkipAutoUpdates(bool skip) - => new(ref _skipAutoUpdates, skip); - - public bool Skip - => _skipAutoUpdates; -} diff --git a/Glamourer/Interop/Penumbra/PenumbraService.cs b/Glamourer/Interop/Penumbra/PenumbraService.cs index b2813cd..13be628 100644 --- a/Glamourer/Interop/Penumbra/PenumbraService.cs +++ b/Glamourer/Interop/Penumbra/PenumbraService.cs @@ -1,9 +1,6 @@ using Dalamud.Interface.ImGuiNotification; using Dalamud.Plugin; -using Dalamud.Plugin.Ipc.Exceptions; using Glamourer.Events; -using Glamourer.State; -using Newtonsoft.Json.Linq; using OtterGui.Classes; using Penumbra.Api.Enums; using Penumbra.Api.Helpers; @@ -36,13 +33,12 @@ public readonly record struct ModSettings(Dictionary> Setti public class PenumbraService : IDisposable { - public const int RequiredPenumbraBreakingVersion = 5; - public const int RequiredPenumbraFeatureVersion = 13; + public const int RequiredPenumbraBreakingVersion = 5; + public const int RequiredPenumbraFeatureVersion = 3; + public const int RequiredPenumbraFeatureVersionTemp = 4; + public const int RequiredPenumbraFeatureVersionTemp2 = 5; - private const int KeyFixed = -1610; - private const string NameFixed = "Glamourer (Automation)"; - private const int KeyManual = -6160; - private const string NameManual = "Glamourer (Manually)"; + private const int Key = -1610; private readonly IDalamudPluginInterface _pluginInterface; private readonly Configuration _config; @@ -51,39 +47,29 @@ public class PenumbraService : IDisposable private readonly EventSubscriber _creatingCharacterBase; private readonly EventSubscriber _createdCharacterBase; private readonly EventSubscriber _modSettingChanged; - private readonly EventSubscriber _pcpParsed; - private readonly EventSubscriber _pcpCreated; - private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier; - private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections; - private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw; - private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection; - private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods; - private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection; - private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp? _getCurrentSettingsWithTemp; - private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings; - private global::Penumbra.Api.IpcSubscribers.GetAllModSettings? _getAllSettings; - private global::Penumbra.Api.IpcSubscribers.TryInheritMod? _inheritMod; - private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod; - private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority; - private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting; - private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings; - private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings? _setTemporaryModSettings; - private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer? _setTemporaryModSettingsPlayer; - private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings? _removeTemporaryModSettings; - private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer? _removeTemporaryModSettingsPlayer; - private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings? _removeAllTemporaryModSettings; - private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer? _removeAllTemporaryModSettingsPlayer; - private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings? _queryTemporaryModSettings; - private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettingsPlayer? _queryTemporaryModSettingsPlayer; - private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage; - private global::Penumbra.Api.IpcSubscribers.GetChangedItems? _getChangedItems; - private global::Penumbra.Api.IpcSubscribers.RegisterSettingsSection? _registerSettingsSection; - private global::Penumbra.Api.IpcSubscribers.UnregisterSettingsSection? _unregisterSettingsSection; - private IReadOnlyList<(string ModDirectory, IReadOnlyDictionary ChangedItems)>? _changedItems; - private Func? _checkCurrentChangedItems; - private Func? _checkCutsceneParent; - private Func? _getGameObject; + private global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier? _collectionByIdentifier; + private global::Penumbra.Api.IpcSubscribers.GetCollections? _collections; + private global::Penumbra.Api.IpcSubscribers.RedrawObject? _redraw; + private global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo? _drawObjectInfo; + private global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex? _cutsceneParent; + private global::Penumbra.Api.IpcSubscribers.GetCollectionForObject? _objectCollection; + private global::Penumbra.Api.IpcSubscribers.GetModList? _getMods; + private global::Penumbra.Api.IpcSubscribers.GetCollection? _currentCollection; + private global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings? _getCurrentSettings; + private global::Penumbra.Api.IpcSubscribers.TryInheritMod? _inheritMod; + private global::Penumbra.Api.IpcSubscribers.TrySetMod? _setMod; + private global::Penumbra.Api.IpcSubscribers.TrySetModPriority? _setModPriority; + private global::Penumbra.Api.IpcSubscribers.TrySetModSetting? _setModSetting; + private global::Penumbra.Api.IpcSubscribers.TrySetModSettings? _setModSettings; + private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings? _setTemporaryModSettings; + private global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer? _setTemporaryModSettingsPlayer; + private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings? _removeTemporaryModSettings; + private global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer? _removeTemporaryModSettingsPlayer; + private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings? _removeAllTemporaryModSettings; + private global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer? _removeAllTemporaryModSettingsPlayer; + private global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings? _queryTemporaryModSettings; + private global::Penumbra.Api.IpcSubscribers.OpenMainWindow? _openModPage; private readonly IDisposable _initializedEvent; private readonly IDisposable _disposedEvent; @@ -107,8 +93,6 @@ public class PenumbraService : IDisposable _createdCharacterBase = global::Penumbra.Api.IpcSubscribers.CreatedCharacterBase.Subscriber(pi); _creatingCharacterBase = global::Penumbra.Api.IpcSubscribers.CreatingCharacterBase.Subscriber(pi); _modSettingChanged = global::Penumbra.Api.IpcSubscribers.ModSettingChanged.Subscriber(pi); - _pcpCreated = global::Penumbra.Api.IpcSubscribers.CreatingPcp.Subscriber(pi); - _pcpParsed = global::Penumbra.Api.IpcSubscribers.ParsingPcp.Subscriber(pi); Reattach(); } @@ -143,50 +127,27 @@ public class PenumbraService : IDisposable remove => _modSettingChanged.Event -= value; } - public event Action PcpCreated - { - add => _pcpCreated.Event += value; - remove => _pcpCreated.Event -= value; - } - - public event Action PcpParsed - { - add => _pcpParsed.Event += value; - remove => _pcpParsed.Event -= value; - } - - public event Action? DrawSettingsSection; - - private void InvokeDrawSettingsSection() - => DrawSettingsSection?.Invoke(); - public Dictionary GetCollections() => Available ? _collections!.Invoke() : []; public ModSettings GetModSettings(in Mod mod, out string source) { - source = string.Empty; + source = string.Empty; if (!Available) return ModSettings.Empty; try { var collection = _currentCollection!.Invoke(ApiCollectionType.Current); - return GetSettings(collection!.Value.Id, mod.DirectoryName, mod.Name, out source); - } - catch (Exception ex) - { - Glamourer.Log.Error($"Error fetching mod settings for {mod.DirectoryName} from Penumbra:\n{ex}"); - return ModSettings.Empty; - } - } + if (_queryTemporaryModSettings != null) + { + var tempEc = _queryTemporaryModSettings.Invoke(collection!.Value.Id, mod.DirectoryName, out var tempTuple, out source); + if (tempEc is PenumbraApiEc.Success && tempTuple != null) + return new ModSettings(tempTuple.Value.Settings, tempTuple.Value.Priority, tempTuple.Value.Enabled, + tempTuple.Value.ForceInherit, false); + } - private ModSettings GetSettings(Guid collection, string modDirectory, string modName, out string source) - { - if (_getCurrentSettingsWithTemp != null) - { - source = string.Empty; - var (ec, tuple) = _getCurrentSettingsWithTemp!.Invoke(collection, modDirectory, modName, false, false, KeyFixed); + var (ec, tuple) = _getCurrentSettings!.Invoke(collection!.Value.Id, mod.DirectoryName); if (ec is not PenumbraApiEc.Success) return ModSettings.Empty; @@ -194,23 +155,11 @@ public class PenumbraService : IDisposable ? new ModSettings(tuple.Value.Item3, tuple.Value.Item2, tuple.Value.Item1, false, false) : ModSettings.Empty; } - - if (_queryTemporaryModSettings != null) + catch (Exception ex) { - var tempEc = _queryTemporaryModSettings.Invoke(collection, modDirectory, out var tempTuple, out source, 0, modName); - if (tempEc is PenumbraApiEc.Success && tempTuple != null) - return new ModSettings(tempTuple.Value.Settings, tempTuple.Value.Priority, tempTuple.Value.Enabled, - tempTuple.Value.ForceInherit, false); - } - - source = string.Empty; - var (ec2, tuple2) = _getCurrentSettings!.Invoke(collection, modDirectory, modName); - if (ec2 is not PenumbraApiEc.Success) + Glamourer.Log.Error($"Error fetching mod settings for {mod.DirectoryName} from Penumbra:\n{ex}"); return ModSettings.Empty; - - return tuple2.HasValue - ? new ModSettings(tuple2.Value.Item3, tuple2.Value.Item2, tuple2.Value.Item1, false, false) - : ModSettings.Empty; + } } public (Guid Id, string Name)? CollectionByIdentifier(string identifier) @@ -225,58 +174,27 @@ public class PenumbraService : IDisposable return ret[0]; } - public IReadOnlyList<(Mod Mod, ModSettings Settings, int Count)> GetMods(IReadOnlyList data) + public IReadOnlyList<(Mod Mod, ModSettings Settings)> GetMods() { if (!Available) return []; try { - var allMods = _getMods!.Invoke(); - var currentCollection = _currentCollection!.Invoke(ApiCollectionType.Current); - var withSettings = WithSettings(allMods, currentCollection!.Value.Id); - var withCounts = WithCounts(withSettings, allMods.Count); - return OrderList(withCounts, allMods.Count); - - IEnumerable<(Mod Mod, ModSettings Settings)> WithSettings(Dictionary mods, Guid collection) - { - if (_getAllSettings != null) - { - var allSettings = _getAllSettings.Invoke(collection, false, false, KeyFixed); - if (allSettings.Item1 is PenumbraApiEc.Success) - return mods.Select(m => (new Mod(m.Value, m.Key), - allSettings.Item2!.TryGetValue(m.Key, out var s) - ? new ModSettings(s.Item3, s.Item2, s.Item1, s is { Item4: true, Item5: true }, false) - : ModSettings.Empty)); - } - - return mods.Select(m => (new Mod(m.Value, m.Key), GetSettings(collection, m.Key, m.Value, out _))); - } - - IEnumerable<(Mod Mod, ModSettings Settings, int Count)> WithCounts(IEnumerable<(Mod Mod, ModSettings Settings)> mods, int count) - { - if (_changedItems != null && _changedItems.Count == count) - return mods.Select((m, idx) => (m.Mod, m.Settings, CountItems(_changedItems[idx].ChangedItems, data))); - - return mods.Select(p => (p.Item1, p.Item2, CountItems(_getChangedItems!.Invoke(p.Item1.DirectoryName, p.Item1.Name), data))); - - static int CountItems(IReadOnlyDictionary dict, IReadOnlyList data) - => data.Count(dict.ContainsKey); - } - - static IReadOnlyList<(Mod Mod, ModSettings Settings, int Count)> OrderList( - IEnumerable<(Mod Mod, ModSettings Settings, int Count)> enumerable, int count) - { - var array = new (Mod Mod, ModSettings Settings, int Count)[count]; - var i = 0; - foreach (var t in enumerable.OrderByDescending(p => p.Item2.Enabled) - .ThenByDescending(p => p.Item3) - .ThenBy(p => p.Item1.Name) - .ThenBy(p => p.Item1.DirectoryName) - .ThenByDescending(p => p.Item2.Priority)) - array[i++] = t; - return array; - } + var allMods = _getMods!.Invoke(); + var collection = _currentCollection!.Invoke(ApiCollectionType.Current); + return allMods + .Select(m => (m.Key, m.Value, _getCurrentSettings!.Invoke(collection!.Value.Id, m.Key))) + .Where(t => t.Item3.Item1 is PenumbraApiEc.Success) + .Select(t => (new Mod(t.Item2, t.Item1), + !t.Item3.Item2.HasValue + ? ModSettings.Empty + : new ModSettings(t.Item3.Item2!.Value.Item3, t.Item3.Item2!.Value.Item2, t.Item3.Item2!.Value.Item1, false, false))) + .OrderByDescending(p => p.Item2.Enabled) + .ThenBy(p => p.Item1.Name) + .ThenBy(p => p.Item1.DirectoryName) + .ThenByDescending(p => p.Item2.Priority) + .ToList(); } catch (Exception ex) { @@ -290,7 +208,7 @@ public class PenumbraService : IDisposable if (!Available) return; - if (_openModPage!.Invoke(TabType.Mods, mod.DirectoryName, mod.Name) == PenumbraApiEc.ModMissing) + if (_openModPage!.Invoke(TabType.Mods, mod.DirectoryName) == PenumbraApiEc.ModMissing) Glamourer.Messager.NotificationMessage($"Could not open the mod {mod.Name}, no fitting mod was found in your Penumbra install.", NotificationType.Info, false); } @@ -302,8 +220,7 @@ public class PenumbraService : IDisposable /// Try to set all mod settings as desired. Only sets when the mod should be enabled. /// If it is disabled, ignore all other settings. /// - public string SetMod(Mod mod, ModSettings settings, StateSource source, bool respectManual, Guid? collectionInput = null, - ObjectIndex? index = null) + public string SetMod(Mod mod, ModSettings settings, Guid? collectionInput = null, ObjectIndex? index = null) { if (!Available) return "Penumbra is not available."; @@ -313,7 +230,7 @@ public class PenumbraService : IDisposable { var collection = collectionInput ?? _currentCollection!.Invoke(ApiCollectionType.Current)!.Value.Id; if (_config.UseTemporarySettings && _setTemporaryModSettings != null) - SetModTemporary(sb, mod, settings, collection, respectManual, index, source); + SetModTemporary(sb, mod, settings, collection, index); else SetModPermanent(sb, mod, settings, collection); @@ -325,63 +242,34 @@ public class PenumbraService : IDisposable } } - public void RemoveAllTemporarySettings(Guid collection, StateSource source) - => _removeAllTemporaryModSettings?.Invoke(collection, source.IsFixed() ? KeyFixed : KeyManual); + public void RemoveAllTemporarySettings(Guid collection) + => _removeAllTemporaryModSettings?.Invoke(collection, Key); - public void RemoveAllTemporarySettings(ObjectIndex index, StateSource source) - => _removeAllTemporaryModSettingsPlayer?.Invoke(index.Index, source.IsFixed() ? KeyFixed : KeyManual); + public void RemoveAllTemporarySettings(ObjectIndex index) + => _removeAllTemporaryModSettingsPlayer?.Invoke(index.Index, Key); - public void ClearAllTemporarySettings(bool fix, bool manual) + public void ClearAllTemporarySettings() { if (!Available || _removeAllTemporaryModSettings == null) return; var collections = _collections!.Invoke(); foreach (var collection in collections) - { - if (fix) - RemoveAllTemporarySettings(collection.Key, StateSource.Fixed); - if (manual) - RemoveAllTemporarySettings(collection.Key, StateSource.Manual); - } + RemoveAllTemporarySettings(collection.Key); } - public (string ModDirectory, string ModName)[] CheckCurrentChangedItem(string changedItem) - => _checkCurrentChangedItems?.Invoke(changedItem) ?? []; - - private void SetModTemporary(StringBuilder sb, Mod mod, ModSettings settings, Guid collection, bool respectManual, ObjectIndex? index, - StateSource source) + private void SetModTemporary(StringBuilder sb, Mod mod, ModSettings settings, Guid collection, ObjectIndex? index) { - var (key, name) = source.IsFixed() ? (KeyFixed, NameFixed) : (KeyManual, NameManual); - // Check for existing manual settings and do not apply fixed on top of them if respecting manual changes. - if (key is KeyFixed && respectManual) - { - var existingSource = string.Empty; - var ec = index.HasValue - ? _queryTemporaryModSettingsPlayer?.Invoke(index.Value.Index, mod.DirectoryName, out _, - out existingSource, key, mod.Name) - ?? PenumbraApiEc.InvalidArgument - : _queryTemporaryModSettings?.Invoke(collection, mod.DirectoryName, out _, - out existingSource, key, mod.Name) - ?? PenumbraApiEc.InvalidArgument; - if (ec is PenumbraApiEc.Success && existingSource is NameManual) - { - Glamourer.Log.Debug( - $"Skipped applying mod settings for [{mod.Name}] through automation because manual settings from Glamourer existed."); - return; - } - } - var ex = settings.Remove ? index.HasValue - ? _removeTemporaryModSettingsPlayer!.Invoke(index.Value.Index, mod.DirectoryName, key, mod.Name) - : _removeTemporaryModSettings!.Invoke(collection, mod.DirectoryName, key, mod.Name) + ? _removeTemporaryModSettingsPlayer!.Invoke(index.Value.Index, mod.DirectoryName, Key) + : _removeTemporaryModSettings!.Invoke(collection, mod.DirectoryName, Key) : index.HasValue ? _setTemporaryModSettingsPlayer!.Invoke(index.Value.Index, mod.DirectoryName, settings.ForceInherit, settings.Enabled, settings.Priority, - settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value), name, key, mod.Name) + settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value), "Glamourer", Key) : _setTemporaryModSettings!.Invoke(collection, mod.DirectoryName, settings.ForceInherit, settings.Enabled, settings.Priority, - settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value), name, key, mod.Name); + settings.Settings.ToDictionary(kvp => kvp.Key, kvp => (IReadOnlyList)kvp.Value), "Glamourer", Key); switch (ex) { case PenumbraApiEc.InvalidArgument: @@ -409,8 +297,8 @@ public class PenumbraService : IDisposable private void SetModPermanent(StringBuilder sb, Mod mod, ModSettings settings, Guid collection) { var ec = settings.ForceInherit - ? _inheritMod!.Invoke(collection, mod.DirectoryName, true, mod.Name) - : _setMod!.Invoke(collection, mod.DirectoryName, settings.Enabled, mod.Name); + ? _inheritMod!.Invoke(collection, mod.DirectoryName, true) + : _setMod!.Invoke(collection, mod.DirectoryName, settings.Enabled); switch (ec) { case PenumbraApiEc.ModMissing: @@ -424,17 +312,19 @@ public class PenumbraService : IDisposable if (settings.ForceInherit || !settings.Enabled) return; - ec = _setModPriority!.Invoke(collection, mod.DirectoryName, settings.Priority, mod.Name); + ec = _setModPriority!.Invoke(collection, mod.DirectoryName, settings.Priority); Debug.Assert(ec is PenumbraApiEc.Success or PenumbraApiEc.NothingChanged, "Setting Priority should not be able to fail."); foreach (var (setting, list) in settings.Settings) { ec = list.Count == 1 - ? _setModSetting!.Invoke(collection, mod.DirectoryName, setting, list[0], mod.Name) - : _setModSettings!.Invoke(collection, mod.DirectoryName, setting, list, mod.Name); + ? _setModSetting!.Invoke(collection, mod.DirectoryName, setting, list[0]) + : _setModSettings!.Invoke(collection, mod.DirectoryName, setting, list); switch (ec) { - case PenumbraApiEc.OptionGroupMissing: sb.AppendLine($"Could not find the option group {setting} in mod {mod.Name}."); break; + case PenumbraApiEc.OptionGroupMissing: + sb.AppendLine($"Could not find the option group {setting} in mod {mod.Name}."); + break; case PenumbraApiEc.OptionMissing: sb.AppendLine($"Could not find all desired options in the option group {setting} in mod {mod.Name}."); break; @@ -474,11 +364,11 @@ public class PenumbraService : IDisposable /// Obtain the game object corresponding to a draw object. public Actor GameObjectFromDrawObject(Model drawObject) - => _getGameObject?.Invoke(drawObject.Address) ?? Actor.Null; + => Available ? _drawObjectInfo!.Invoke(drawObject.Address).Item1 : Actor.Null; /// Obtain the parent of a cutscene actor if it is known. public short CutsceneParent(ushort idx) - => (short)(_checkCutsceneParent?.Invoke(idx) ?? -1); + => (short)(Available ? _cutsceneParent!.Invoke(idx) : -1); /// Try to redraw the given actor. public void RedrawObject(Actor actor, RedrawType settings) @@ -539,44 +429,34 @@ public class PenumbraService : IDisposable _clickSubscriber.Enable(); _creatingCharacterBase.Enable(); _createdCharacterBase.Enable(); - _pcpCreated.Enable(); - _pcpParsed.Enable(); _modSettingChanged.Enable(); _collectionByIdentifier = new global::Penumbra.Api.IpcSubscribers.GetCollectionsByIdentifier(_pluginInterface); - _collections = new global::Penumbra.Api.IpcSubscribers.GetCollections(_pluginInterface); - _redraw = new global::Penumbra.Api.IpcSubscribers.RedrawObject(_pluginInterface); - _checkCutsceneParent = new global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndexFunc(_pluginInterface).Invoke(); - _getGameObject = new global::Penumbra.Api.IpcSubscribers.GetGameObjectFromDrawObjectFunc(_pluginInterface).Invoke(); - _objectCollection = new global::Penumbra.Api.IpcSubscribers.GetCollectionForObject(_pluginInterface); - _getMods = new global::Penumbra.Api.IpcSubscribers.GetModList(_pluginInterface); - _currentCollection = new global::Penumbra.Api.IpcSubscribers.GetCollection(_pluginInterface); - _getCurrentSettings = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings(_pluginInterface); - _inheritMod = new global::Penumbra.Api.IpcSubscribers.TryInheritMod(_pluginInterface); - _setMod = new global::Penumbra.Api.IpcSubscribers.TrySetMod(_pluginInterface); - _setModPriority = new global::Penumbra.Api.IpcSubscribers.TrySetModPriority(_pluginInterface); - _setModSetting = new global::Penumbra.Api.IpcSubscribers.TrySetModSetting(_pluginInterface); - _setModSettings = new global::Penumbra.Api.IpcSubscribers.TrySetModSettings(_pluginInterface); - _openModPage = new global::Penumbra.Api.IpcSubscribers.OpenMainWindow(_pluginInterface); - _getChangedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItems(_pluginInterface); - _setTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings(_pluginInterface); - _setTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer(_pluginInterface); - _removeTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings(_pluginInterface); - _removeTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer(_pluginInterface); - _removeAllTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings(_pluginInterface); - _removeAllTemporaryModSettingsPlayer = - new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer(_pluginInterface); - _queryTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings(_pluginInterface); - _queryTemporaryModSettingsPlayer = - new global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettingsPlayer(_pluginInterface); - _getCurrentSettingsWithTemp = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettingsWithTemp(_pluginInterface); - _getAllSettings = new global::Penumbra.Api.IpcSubscribers.GetAllModSettings(_pluginInterface); - _changedItems = new global::Penumbra.Api.IpcSubscribers.GetChangedItemAdapterList(_pluginInterface).Invoke(); - _checkCurrentChangedItems = - new global::Penumbra.Api.IpcSubscribers.CheckCurrentChangedItemFunc(_pluginInterface).Invoke(); - _registerSettingsSection = new global::Penumbra.Api.IpcSubscribers.RegisterSettingsSection(_pluginInterface); - _unregisterSettingsSection = new global::Penumbra.Api.IpcSubscribers.UnregisterSettingsSection(_pluginInterface); - - _registerSettingsSection.Invoke(InvokeDrawSettingsSection); + _collections = new global::Penumbra.Api.IpcSubscribers.GetCollections(_pluginInterface); + _redraw = new global::Penumbra.Api.IpcSubscribers.RedrawObject(_pluginInterface); + _drawObjectInfo = new global::Penumbra.Api.IpcSubscribers.GetDrawObjectInfo(_pluginInterface); + _cutsceneParent = new global::Penumbra.Api.IpcSubscribers.GetCutsceneParentIndex(_pluginInterface); + _objectCollection = new global::Penumbra.Api.IpcSubscribers.GetCollectionForObject(_pluginInterface); + _getMods = new global::Penumbra.Api.IpcSubscribers.GetModList(_pluginInterface); + _currentCollection = new global::Penumbra.Api.IpcSubscribers.GetCollection(_pluginInterface); + _getCurrentSettings = new global::Penumbra.Api.IpcSubscribers.GetCurrentModSettings(_pluginInterface); + _inheritMod = new global::Penumbra.Api.IpcSubscribers.TryInheritMod(_pluginInterface); + _setMod = new global::Penumbra.Api.IpcSubscribers.TrySetMod(_pluginInterface); + _setModPriority = new global::Penumbra.Api.IpcSubscribers.TrySetModPriority(_pluginInterface); + _setModSetting = new global::Penumbra.Api.IpcSubscribers.TrySetModSetting(_pluginInterface); + _setModSettings = new global::Penumbra.Api.IpcSubscribers.TrySetModSettings(_pluginInterface); + _openModPage = new global::Penumbra.Api.IpcSubscribers.OpenMainWindow(_pluginInterface); + if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp) + { + _setTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettings(_pluginInterface); + _setTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.SetTemporaryModSettingsPlayer(_pluginInterface); + _removeTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettings(_pluginInterface); + _removeTemporaryModSettingsPlayer = new global::Penumbra.Api.IpcSubscribers.RemoveTemporaryModSettingsPlayer(_pluginInterface); + _removeAllTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettings(_pluginInterface); + _removeAllTemporaryModSettingsPlayer = + new global::Penumbra.Api.IpcSubscribers.RemoveAllTemporaryModSettingsPlayer(_pluginInterface); + if (CurrentMinor >= RequiredPenumbraFeatureVersionTemp2) + _queryTemporaryModSettings = new global::Penumbra.Api.IpcSubscribers.QueryTemporaryModSettings(_pluginInterface); + } Available = true; _penumbraReloaded.Invoke(); @@ -597,30 +477,17 @@ public class PenumbraService : IDisposable _creatingCharacterBase.Disable(); _createdCharacterBase.Disable(); _modSettingChanged.Disable(); - _pcpCreated.Disable(); - _pcpParsed.Disable(); - try - { - _unregisterSettingsSection?.Invoke(InvokeDrawSettingsSection); - } - catch (IpcNotReadyError) - { - // Ignore. - } - if (Available) { _collectionByIdentifier = null; _collections = null; _redraw = null; - _getGameObject = null; - _checkCutsceneParent = null; + _drawObjectInfo = null; + _cutsceneParent = null; _objectCollection = null; _getMods = null; _currentCollection = null; _getCurrentSettings = null; - _getCurrentSettingsWithTemp = null; - _getAllSettings = null; _inheritMod = null; _setMod = null; _setModPriority = null; @@ -634,12 +501,6 @@ public class PenumbraService : IDisposable _removeAllTemporaryModSettings = null; _removeAllTemporaryModSettingsPlayer = null; _queryTemporaryModSettings = null; - _queryTemporaryModSettingsPlayer = null; - _getChangedItems = null; - _changedItems = null; - _checkCurrentChangedItems = null; - _registerSettingsSection = null; - _unregisterSettingsSection = null; Available = false; Glamourer.Log.Debug("Glamourer detached from Penumbra."); } @@ -647,7 +508,7 @@ public class PenumbraService : IDisposable public void Dispose() { - ClearAllTemporarySettings(true, true); + ClearAllTemporarySettings(); Unattach(); _tooltipSubscriber.Dispose(); _clickSubscriber.Dispose(); @@ -656,7 +517,5 @@ public class PenumbraService : IDisposable _initializedEvent.Dispose(); _disposedEvent.Dispose(); _modSettingChanged.Dispose(); - _pcpCreated.Dispose(); - _pcpParsed.Dispose(); } } diff --git a/Glamourer/Interop/ScalingService.cs b/Glamourer/Interop/ScalingService.cs index 2a89a25..141d5f2 100644 --- a/Glamourer/Interop/ScalingService.cs +++ b/Glamourer/Interop/ScalingService.cs @@ -1,35 +1,24 @@ -using Dalamud.Hooking; +using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Hooking; using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Penumbra.GameData; using Penumbra.GameData.Interop; using FFXIVClientStructs.FFXIV.Client.Game.Object; -using Glamourer.State; -using Penumbra.GameData.Actors; -using Penumbra.GameData.Enums; using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; -using CustomizeIndex = Dalamud.Game.ClientState.Objects.Enums.CustomizeIndex; namespace Glamourer.Interop; public unsafe class ScalingService : IDisposable { - private readonly ActorManager _actors; - private readonly StateManager _state; - - public ScalingService(IGameInteropProvider interop, StateManager state, ActorManager actors) + public ScalingService(IGameInteropProvider interop) { - _state = state; - _actors = actors; interop.InitializeFromAttributes(this); _setupMountHook = interop.HookFromAddress((nint)MountContainer.MemberFunctionPointers.SetupMount, SetupMountDetour); _calculateHeightHook = interop.HookFromAddress((nint)ModelContainer.MemberFunctionPointers.CalculateHeight, CalculateHeightDetour); - _placeMinionHook = interop.HookFromAddress((nint)Companion.MemberFunctionPointers.PlaceCompanion, PlaceMinionDetour); - //_updateOrnamentHook = - // interop.HookFromAddress((nint)Ornament.MemberFunctionPointers.UpdateOrnament, UpdateOrnamentDetour); _setupMountHook.Enable(); _updateOrnamentHook.Enable(); @@ -58,6 +47,8 @@ public unsafe class ScalingService : IDisposable private readonly Hook _calculateHeightHook; + // TODO: Use client structs sig. + [Signature(Sigs.PlaceMinion, DetourName = nameof(PlaceMinionDetour))] private readonly Hook _placeMinionHook = null!; private void SetupMountDetour(MountContainer* container, short mountId, uint unk1, uint unk2, uint unk3, byte unk4) @@ -88,16 +79,7 @@ public unsafe class ScalingService : IDisposable var mdl = owner.Model; var oldRace = owner.AsCharacter->DrawData.CustomizeData.Race; if (mdl.IsHuman) - { owner.AsCharacter->DrawData.CustomizeData.Race = mdl.AsHuman->Customize.Race; - } - else - { - var actor = _actors.FromObject(owner, out _, true, false, true); - if (_state.TryGetValue(actor, out var state)) - owner.AsCharacter->DrawData.CustomizeData.Race = (byte)state.ModelData.Customize.Race; - } - _placeMinionHook.Original(companion); owner.AsCharacter->DrawData.CustomizeData.Race = oldRace; } @@ -121,20 +103,12 @@ public unsafe class ScalingService : IDisposable character->DrawData.CustomizeData.Tribe, character->DrawData.CustomizeData[(int)CustomizeIndex.Height]); [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private void SetScaleCustomize(Character* character, Model model) + private static void SetScaleCustomize(Character* character, Model model) { - if (model.IsHuman) - { - SetScaleCustomize(character, model.AsHuman->Customize.Race, model.AsHuman->Customize.Tribe, model.AsHuman->Customize.Sex); - return; - } - - var actor = _actors.FromObject(character, out _, true, false, true); - if (!_state.TryGetValue(actor, out var state)) + if (!model.IsHuman) return; - ref var customize = ref state.ModelData.Customize; - SetScaleCustomize(character, (byte)customize.Race, (byte)customize.Clan, customize.Gender.ToGameByte()); + SetScaleCustomize(character, model.AsHuman->Customize.Race, model.AsHuman->Customize.Tribe, model.AsHuman->Customize.Sex); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] @@ -146,22 +120,13 @@ public unsafe class ScalingService : IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private void SetHeightCustomize(Character* character, Model model) + private static void SetHeightCustomize(Character* character, Model model) { - if (model.IsHuman) - { - SetHeightCustomize(character, model.AsHuman->Customize.Sex, model.AsHuman->Customize.BodyType, model.AsHuman->Customize.Tribe, - model.AsHuman->Customize[(int)CustomizeIndex.Height]); - return; - } - - var actor = _actors.FromObject(character, out _, true, false, true); - if (!_state.TryGetValue(actor, out var state)) + if (!model.IsHuman) return; - ref var customize = ref state.ModelData.Customize; - SetHeightCustomize(character, customize.Gender.ToGameByte(), customize.BodyType.Value, (byte)customize.Clan, - customize[global::Penumbra.GameData.Enums.CustomizeIndex.Height].Value); + SetHeightCustomize(character, model.AsHuman->Customize.Sex, model.AsHuman->Customize.BodyType, model.AsHuman->Customize.Tribe, + model.AsHuman->Customize[(int)CustomizeIndex.Height]); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] diff --git a/Glamourer/Interop/Structs/ActorData.cs b/Glamourer/Interop/Structs/ActorData.cs new file mode 100644 index 0000000..5cfbcbe --- /dev/null +++ b/Glamourer/Interop/Structs/ActorData.cs @@ -0,0 +1,47 @@ +using OtterGui.Log; +using Penumbra.GameData.Interop; + +namespace Glamourer.Interop.Structs; + +/// +/// A single actor with its label and the list of associated game objects. +/// +public readonly struct ActorData +{ + public readonly List Objects; + public readonly string Label; + + public bool Valid + => Objects.Count > 0; + + public ActorData(Actor actor, string label) + { + Objects = [actor]; + Label = label; + } + + public static readonly ActorData Invalid = new(false); + + private ActorData(bool _) + { + Objects = []; + Label = string.Empty; + } + + public LazyString ToLazyString(string invalid) + { + var objects = Objects; + return Valid + ? new LazyString(() => string.Join(", ", objects.Select(o => o.ToString()))) + : new LazyString(() => invalid); + } + + private ActorData(List objects, string label) + { + Objects = objects; + Label = label; + } + + public ActorData OnlyGPose() + => new(Objects.Where(o => o.IsGPoseOrCutscene).ToList(), Label); +} diff --git a/Glamourer/Interop/UpdateSlotService.cs b/Glamourer/Interop/UpdateSlotService.cs index 3ef99d9..d1004e6 100644 --- a/Glamourer/Interop/UpdateSlotService.cs +++ b/Glamourer/Interop/UpdateSlotService.cs @@ -1,8 +1,6 @@ using Dalamud.Hooking; using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; -using FFXIVClientStructs.FFXIV.Client.Game.Character; -using FFXIVClientStructs.FFXIV.Client.Game.Network; using Glamourer.Events; using Penumbra.GameData; using Penumbra.GameData.DataContainers; @@ -16,29 +14,23 @@ public unsafe class UpdateSlotService : IDisposable { public readonly EquipSlotUpdating EquipSlotUpdatingEvent; public readonly BonusSlotUpdating BonusSlotUpdatingEvent; - public readonly GearsetDataLoaded GearsetDataLoadedEvent; - private readonly DictBonusItems _bonusItems; + private readonly DictBonusItems _bonusItems; - public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded, - IGameInteropProvider interop, DictBonusItems bonusItems) + public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, IGameInteropProvider interop, + DictBonusItems bonusItems) { EquipSlotUpdatingEvent = equipSlotUpdating; BonusSlotUpdatingEvent = bonusSlotUpdating; - GearsetDataLoadedEvent = gearsetDataLoaded; - _bonusItems = bonusItems; - + _bonusItems = bonusItems; interop.InitializeFromAttributes(this); - _loadGearsetDataHook = interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour); _flagSlotForUpdateHook.Enable(); _flagBonusSlotForUpdateHook.Enable(); - _loadGearsetDataHook.Enable(); } public void Dispose() { _flagSlotForUpdateHook.Dispose(); _flagBonusSlotForUpdateHook.Dispose(); - _loadGearsetDataHook.Dispose(); } public void UpdateEquipSlot(Model drawObject, EquipSlot slot, CharacterArmor data) @@ -87,12 +79,6 @@ public unsafe class UpdateSlotService : IDisposable [Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))] private readonly Hook _flagBonusSlotForUpdateHook = null!; - /// Detours the func that makes all FlagSlotForUpdate calls on a gearset change or initial render of a given actor (Only Cases this is Called). - /// Logic done after returning the original hook executes After all equipment/weapon/crest data is loaded into the Actors BaseData. - /// - private delegate ulong LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData); - private readonly Hook _loadGearsetDataHook; - private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data) { var slot = slotIdx.ToEquipSlot(); @@ -112,35 +98,5 @@ public unsafe class UpdateSlotService : IDisposable } private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor) - { - Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Glamourer-Invoked on 0x{drawObject.Address:X} on {slot} with item data {armor}."); - return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor); - } - private ulong LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData) - { - var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData); - var drawObject = drawDataContainer->OwnerObject->DrawObject; - GearsetDataLoadedEvent.Invoke(drawDataContainer->OwnerObject, drawObject); - Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}"); - return ret; - } - - - private static string FormatGearsetItemDataStruct(PacketPlayerGearsetData gearsetData) - { - var ret = - $"\nMainhandWeaponData: Id: {gearsetData.MainhandWeaponData.Id}, Type: {gearsetData.MainhandWeaponData.Type}, " + - $"Variant: {gearsetData.MainhandWeaponData.Variant}, Stain0: {gearsetData.MainhandWeaponData.Stain0}, Stain1: {gearsetData.MainhandWeaponData.Stain1}" + - $"\nOffhandWeaponData: Id: {gearsetData.OffhandWeaponData.Id}, Type: {gearsetData.OffhandWeaponData.Type}, " + - $"Variant: {gearsetData.OffhandWeaponData.Variant}, Stain0: {gearsetData.OffhandWeaponData.Stain0}, Stain1: {gearsetData.OffhandWeaponData.Stain1}" + - $"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId}"; - for (var offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor)) - { - var equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset); - var dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset - var dyePtr = (byte*)&gearsetData + dyeOffset; - ret += $"\nEquipSlot {(EquipSlot)(dyeOffset - 60)}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}"; - } - return ret; - } + => _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor); } diff --git a/Glamourer/Interop/VieraEarService.cs b/Glamourer/Interop/VieraEarService.cs deleted file mode 100644 index a6afd1d..0000000 --- a/Glamourer/Interop/VieraEarService.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Dalamud.Hooking; -using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game.Character; -using Glamourer.Events; -using Penumbra.GameData; -using Penumbra.GameData.Interop; - -namespace Glamourer.Interop; - -public unsafe class VieraEarService : IDisposable -{ - private readonly PenumbraReloaded _penumbra; - private readonly IGameInteropProvider _interop; - public readonly VieraEarStateChanged Event; - - public VieraEarService(VieraEarStateChanged visorStateChanged, IGameInteropProvider interop, PenumbraReloaded penumbra) - { - _interop = interop; - _penumbra = penumbra; - Event = visorStateChanged; - _setupVieraEarHook = Create(); - _penumbra.Subscribe(Restore, PenumbraReloaded.Priority.VieraEarService); - } - - public void Dispose() - { - _setupVieraEarHook.Dispose(); - _penumbra.Unsubscribe(Restore); - } - - /// Obtain the current state of viera ears for the given draw object (true: toggled). - public static unsafe bool GetVieraEarState(Model characterBase) - => characterBase is { IsCharacterBase: true, VieraEarsVisible: true }; - - /// Manually set the state of the Visor for the given draw object. - /// The draw object. - /// The desired state (true: toggled). - /// Whether the state was changed. - public bool SetVieraEarState(Model human, bool on) - { - if (!human.IsHuman) - return false; - - var oldState = GetVieraEarState(human); - Glamourer.Log.Verbose($"[SetVieraEarState] Invoked manually on 0x{human.Address:X} switching from {oldState} to {on}."); - if (oldState == on) - return false; - - human.VieraEarsVisible = on; - return true; - } - - private delegate void UpdateVieraEarDelegateInternal(DrawDataContainer* drawData, byte on); - - private Hook _setupVieraEarHook; - - private void SetupVieraEarDetour(DrawDataContainer* drawData, byte value) - { - Actor actor = drawData->OwnerObject; - var originalOn = value != 0; - var on = originalOn; - // Invoke an event that can change the requested value - Event.Invoke(actor, ref on); - - Glamourer.Log.Verbose( - $"[SetVieraEarState] Invoked from game on 0x{actor.Address:X} switching to {on} (original {originalOn} from {value})."); - - _setupVieraEarHook.Original(drawData, on ? (byte)1 : (byte)0); - } - - private unsafe Hook Create() - { - var hook = _interop.HookFromSignature(Sigs.SetupVieraEars, SetupVieraEarDetour); - hook.Enable(); - return hook; - } - - private void Restore() - { - _setupVieraEarHook.Dispose(); - _setupVieraEarHook = Create(); - } -} diff --git a/Glamourer/Interop/VisorService.cs b/Glamourer/Interop/VisorService.cs index 83262e4..9763682 100644 --- a/Glamourer/Interop/VisorService.cs +++ b/Glamourer/Interop/VisorService.cs @@ -36,7 +36,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,8 +46,6 @@ public class VisorService : IDisposable if (oldState == on) return false; - // No clue what this flag does, but it's necessary for toggling static visors on or off, e.g. Alternate Cap (6229-1). - human.AsHuman->StateFlags |= (CharacterBase.StateFlag)0x40000000; SetupVisorDetour(human, human.GetArmor(EquipSlot.Head).Set.Id, on); return true; } diff --git a/Glamourer/Interop/WeaponService.cs b/Glamourer/Interop/WeaponService.cs index 54f318b..b0bdd19 100644 --- a/Glamourer/Interop/WeaponService.cs +++ b/Glamourer/Interop/WeaponService.cs @@ -13,7 +13,7 @@ public unsafe class WeaponService : IDisposable private readonly WeaponLoading _event; private readonly ThreadLocal _inUpdate = new(() => false); - private readonly delegate* unmanaged[Stdcall] + private readonly delegate* unmanaged[Stdcall] _original; public WeaponService(WeaponLoading @event, IGameInteropProvider interop) @@ -22,7 +22,7 @@ public unsafe class WeaponService : IDisposable _loadWeaponHook = interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.LoadWeapon, LoadWeaponDetour); _original = - (delegate* unmanaged[Stdcall] < DrawDataContainer*, uint, ulong, byte, byte, byte, byte, int, void >) + (delegate* unmanaged[Stdcall] < DrawDataContainer*, uint, ulong, byte, byte, byte, byte, void >) DrawDataContainer.MemberFunctionPointers.LoadWeapon; _loadWeaponHook.Enable(); } @@ -36,14 +36,13 @@ public unsafe class WeaponService : IDisposable // redrawOnEquality controls whether the game does anything if the new weapon is identical to the old one. // skipGameObject seems to control whether the new weapons are written to the game object or just influence the draw object. (1 = skip, 0 = change) // unk4 seemed to be the same as unk1. - // unk5 is new in 7.30 and is checked at the beginning of the function to call some timeline related function. private delegate void LoadWeaponDelegate(DrawDataContainer* drawData, uint slot, ulong weapon, byte redrawOnEquality, byte unk2, - byte skipGameObject, byte unk4, byte unk5); + byte skipGameObject, byte unk4); private readonly Hook _loadWeaponHook; private void LoadWeaponDetour(DrawDataContainer* drawData, uint slot, ulong weaponValue, byte redrawOnEquality, byte unk2, - byte skipGameObject, byte unk4, byte unk5) + byte skipGameObject, byte unk4) { if (!_inUpdate.Value) { @@ -65,21 +64,21 @@ public unsafe class WeaponService : IDisposable else if (weaponValue == actor.GetMainhand().Value && weaponValue != 0) _event.Invoke(actor, EquipSlot.MainHand, ref tmpWeapon); - _loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4, unk5); + _loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4); if (tmpWeapon.Value != weapon.Value) { if (tmpWeapon.Skeleton.Id == 0) tmpWeapon.Stains = StainIds.None; - _loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4, unk5); + _loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4); } Glamourer.Log.Excessive( - $"Weapon reloaded for 0x{actor.Address:X} ({actor.Utf8Name}) with attributes {slot} {weapon.Value:X14}, {redrawOnEquality}, {unk2}, {skipGameObject}, {unk4}, {unk5}"); + $"Weapon reloaded for 0x{actor.Address:X} ({actor.Utf8Name}) with attributes {slot} {weapon.Value:X14}, {redrawOnEquality}, {unk2}, {skipGameObject}, {unk4}"); } else { - _loadWeaponHook.Original(drawData, slot, weaponValue, redrawOnEquality, unk2, skipGameObject, unk4, unk5); + _loadWeaponHook.Original(drawData, slot, weaponValue, redrawOnEquality, unk2, skipGameObject, unk4); } } @@ -90,18 +89,18 @@ public unsafe class WeaponService : IDisposable { case EquipSlot.MainHand: _inUpdate.Value = true; - _original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0, 0); + _original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0); _inUpdate.Value = false; return; case EquipSlot.OffHand: _inUpdate.Value = true; - _original(&character.AsCharacter->DrawData, 1, weapon.Value, 1, 0, 1, 0, 0); + _original(&character.AsCharacter->DrawData, 1, weapon.Value, 1, 0, 1, 0); _inUpdate.Value = false; return; case EquipSlot.BothHand: _inUpdate.Value = true; - _original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0, 0); - _original(&character.AsCharacter->DrawData, 1, CharacterWeapon.Empty.Value, 1, 0, 1, 0, 0); + _original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0); + _original(&character.AsCharacter->DrawData, 1, CharacterWeapon.Empty.Value, 1, 0, 1, 0); _inUpdate.Value = false; return; } diff --git a/Glamourer/Services/BackupService.cs b/Glamourer/Services/BackupService.cs index 511cca6..3abf13a 100644 --- a/Glamourer/Services/BackupService.cs +++ b/Glamourer/Services/BackupService.cs @@ -1,10 +1,9 @@ using OtterGui.Classes; using OtterGui.Log; -using OtterGui.Services; namespace Glamourer.Services; -public class BackupService : IAsyncService +public class BackupService { private readonly Logger _logger; private readonly DirectoryInfo _configDirectory; @@ -15,7 +14,7 @@ public class BackupService : IAsyncService _logger = logger; _fileNames = GlamourerFiles(fileNames); _configDirectory = new DirectoryInfo(fileNames.ConfigDirectory); - Awaiter = Task.Run(() => Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), _fileNames)); + Backup.CreateAutomaticBackup(logger, _configDirectory, _fileNames); } /// Create a permanent backup with a given name for migrations. @@ -41,9 +40,4 @@ public class BackupService : IAsyncService return list; } - - public Task Awaiter { get; } - - public bool Finished - => Awaiter.IsCompletedSuccessfully; } diff --git a/Glamourer/Services/CodeService.cs b/Glamourer/Services/CodeService.cs index 4a82f0e..af2e88b 100644 --- a/Glamourer/Services/CodeService.cs +++ b/Glamourer/Services/CodeService.cs @@ -50,8 +50,7 @@ public class CodeService | CodeFlag.OopsMiqote | CodeFlag.OopsRoegadyn | CodeFlag.OopsAuRa - | CodeFlag.OopsHrothgar - | CodeFlag.OopsViera; + | CodeFlag.OopsHrothgar; public const CodeFlag FullCodes = CodeFlag.Face | CodeFlag.Manderville | CodeFlag.Smiles; @@ -251,4 +250,3 @@ public class CodeService _ => (false, 0, string.Empty, string.Empty, string.Empty), }; } - diff --git a/Glamourer/Services/CollectionOverrideService.cs b/Glamourer/Services/CollectionOverrideService.cs index 99635d8..fcc9998 100644 --- a/Glamourer/Services/CollectionOverrideService.cs +++ b/Glamourer/Services/CollectionOverrideService.cs @@ -3,7 +3,6 @@ using Glamourer.Interop.Penumbra; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OtterGui; -using OtterGui.Extensions; using OtterGui.Filesystem; using OtterGui.Services; using Penumbra.GameData.Actors; diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index d2feac0..10f68ee 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -9,15 +9,15 @@ using Glamourer.Gui; using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Interop.Penumbra; using Glamourer.State; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using OtterGui.Services; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.Services; @@ -26,28 +26,27 @@ public class CommandService : IDisposable, IApiService private const string MainCommandString = "/glamourer"; private const string ApplyCommandString = "/glamour"; - private readonly ICommandManager _commands; - private readonly MainWindow _mainWindow; - private readonly IChatGui _chat; - private readonly ActorManager _actors; - private readonly ActorObjectManager _objects; - private readonly StateManager _stateManager; - private readonly AutoDesignApplier _autoDesignApplier; - private readonly AutoDesignManager _autoDesignManager; - private readonly Configuration _config; - private readonly ModSettingApplier _modApplier; - private readonly ItemManager _items; - private readonly CustomizeService _customizeService; - private readonly DesignManager _designManager; - private readonly DesignConverter _converter; - private readonly DesignResolver _resolver; - private readonly PenumbraService _penumbra; + private readonly ICommandManager _commands; + private readonly MainWindow _mainWindow; + private readonly IChatGui _chat; + private readonly ActorManager _actors; + private readonly ObjectManager _objects; + private readonly StateManager _stateManager; + private readonly AutoDesignApplier _autoDesignApplier; + private readonly AutoDesignManager _autoDesignManager; + private readonly Configuration _config; + private readonly ModSettingApplier _modApplier; + private readonly ItemManager _items; + private readonly CustomizeService _customizeService; + private readonly DesignManager _designManager; + private readonly DesignConverter _converter; + private readonly DesignResolver _resolver; - public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorManager actors, ActorObjectManager objects, + public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorManager actors, ObjectManager objects, AutoDesignApplier autoDesignApplier, StateManager stateManager, DesignManager designManager, DesignConverter converter, DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config, ModSettingApplier modApplier, ItemManager items, RandomDesignGenerator randomDesign, CustomizeService customizeService, DesignFileSystemSelector designSelector, - QuickDesignCombo quickDesignCombo, DesignResolver resolver, PenumbraService penumbra) + QuickDesignCombo quickDesignCombo, DesignResolver resolver) { _commands = commands; _mainWindow = mainWindow; @@ -64,7 +63,6 @@ public class CommandService : IDisposable, IApiService _items = items; _customizeService = customizeService; _resolver = resolver; - _penumbra = penumbra; _commands.AddHandler(MainCommandString, new CommandInfo(OnGlamourer) { HelpMessage = "Open or close the Glamourer window." }); _commands.AddHandler(ApplyCommandString, @@ -96,12 +94,6 @@ public class CommandService : IDisposable, IApiService _config.Ephemeral.LockMainWindow = !_config.Ephemeral.LockMainWindow; _config.Ephemeral.Save(); return; - case "automation": - var newValue = !_config.EnableAutoDesigns; - _config.EnableAutoDesigns = newValue; - _autoDesignApplier.OnEnableAutoDesignsChanged(newValue); - _config.Save(); - return; default: _chat.Print("Use without argument to toggle the main window."); _chat.Print(new SeStringBuilder().AddText("Use ").AddPurple("/glamour").AddText(" instead of ").AddRed("/glamourer") @@ -129,10 +121,8 @@ public class CommandService : IDisposable, IApiService "apply" => Apply(argument), "reapply" => ReapplyState(argument), "revert" => Revert(argument), - "reapplyautomation" => ReapplyAutomation(argument, "reapplyautomation", false, false), - "reverttoautomation" => ReapplyAutomation(argument, "reverttoautomation", true, false), - "resetdesign" => ReapplyAutomation(argument, "resetdesign", false, true), - "clearsettings" => ClearSettings(argument), + "reapplyautomation" => ReapplyAutomation(argument, "reapplyautomation", false), + "reverttoautomation" => ReapplyAutomation(argument, "reverttoautomation", true), "automation" => SetAutomation(argument), "copy" => CopyState(argument), "save" => SaveState(argument), @@ -161,10 +151,6 @@ public class CommandService : IDisposable, IApiService "Reapplies the current automation state on top of the characters current state.. Use without arguments for help.").BuiltString); _chat.Print(new SeStringBuilder().AddCommand("reverttoautomation", "Reverts a given character to its supposed state using automated designs. Use without arguments for help.").BuiltString); - _chat.Print(new SeStringBuilder().AddCommand("resetdesign", - "Reapplies the current automation and resets the random design. Use without arguments for help.").BuiltString); - _chat.Print(new SeStringBuilder() - .AddCommand("clearsettings", "Clears all temporary settings applied by Glamourer. Use without arguments for help.").BuiltString); _chat.Print(new SeStringBuilder() .AddCommand("copy", "Copy the current state of a character to clipboard. Use without arguments for help.").BuiltString); _chat.Print(new SeStringBuilder() @@ -179,64 +165,6 @@ public class CommandService : IDisposable, IApiService return true; } - private bool ClearSettings(string argument) - { - var argumentList = argument.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - if (argumentList.Length < 1) - { - _chat.Print(new SeStringBuilder().AddText("Use with /glamour clearsettings ").AddGreen("[Character Identifier]").AddText(" | ") - .AddPurple("").AddText(" | ").AddBlue("").BuiltString); - PlayerIdentifierHelp(false, true); - _chat.Print(new SeStringBuilder() - .AddText(" 》 The character identifier specifies the collection to clear settings from. It also accepts '").AddGreen("all") - .AddText("' to clear all collections.").BuiltString); - _chat.Print(new SeStringBuilder().AddText(" 》 The booleans are optional and default to 'true', the ").AddPurple("first") - .AddText(" determines whether ").AddPurple("manually").AddText(" applied settings are cleared, the ").AddBlue("second") - .AddText(" determines whether ").AddBlue("automatically").AddText(" applied settings are cleared.").BuiltString); - return false; - } - - var clearManual = true; - var clearAutomatic = true; - if (argumentList.Length > 1 && bool.TryParse(argumentList[1], out var m)) - clearManual = m; - if (argumentList.Length > 2 && bool.TryParse(argumentList[2], out var a)) - clearAutomatic = a; - - if (!clearManual && !clearAutomatic) - return true; - - if (argumentList[0].ToLowerInvariant() is "all") - { - _penumbra.ClearAllTemporarySettings(clearAutomatic, clearManual); - return true; - } - - if (!IdentifierHandling(argumentList[0], out var identifiers, false, true)) - return false; - - var set = new HashSet(); - foreach (var id in identifiers) - { - if (!_objects.TryGetValue(id, out var data) || !data.Valid) - continue; - - foreach (var obj in data.Objects) - { - var guid = _penumbra.GetActorCollection(obj, out _); - if (!set.Add(guid)) - continue; - - if (clearManual) - _penumbra.RemoveAllTemporarySettings(guid, StateSource.Manual); - if (clearAutomatic) - _penumbra.RemoveAllTemporarySettings(guid, StateSource.Fixed); - } - } - - return true; - } - private bool SetAutomation(string arguments) { var argumentList = arguments.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); @@ -352,11 +280,21 @@ public class CommandService : IDisposable, IApiService { switch (char.ToLowerInvariant(character)) { - case 'c': applicationFlags |= ApplicationType.Customizations; break; - case 'e': applicationFlags |= ApplicationType.Armor; break; - case 'a': applicationFlags |= ApplicationType.Accessories; break; - case 'd': applicationFlags |= ApplicationType.GearCustomization; break; - case 'w': applicationFlags |= ApplicationType.Weapons; break; + case 'c': + applicationFlags |= ApplicationType.Customizations; + break; + case 'e': + applicationFlags |= ApplicationType.Armor; + break; + case 'a': + applicationFlags |= ApplicationType.Accessories; + break; + case 'd': + applicationFlags |= ApplicationType.GearCustomization; + break; + case 'w': + applicationFlags |= ApplicationType.Weapons; + break; default: _chat.Print(new SeStringBuilder().AddText("The value ").AddPurple(split2[1], true) .AddText(" is not a valid set of application flags.").BuiltString); @@ -368,7 +306,7 @@ public class CommandService : IDisposable, IApiService return true; } - private bool ReapplyAutomation(string argument, string command, bool revert, bool forcedNew) + private bool ReapplyAutomation(string argument, string command, bool revert) { if (argument.Length == 0) { @@ -380,6 +318,7 @@ public class CommandService : IDisposable, IApiService if (!IdentifierHandling(argument, out var identifiers, false, true)) return false; + _objects.Update(); foreach (var identifier in identifiers) { if (!_objects.TryGetValue(identifier, out var data)) @@ -389,8 +328,8 @@ public class CommandService : IDisposable, IApiService { if (_stateManager.GetOrCreate(identifier, actor, out var state)) { - _autoDesignApplier.ReapplyAutomation(actor, identifier, state, revert, forcedNew, out var forcedRedraw); - _stateManager.ReapplyAutomationState(actor, forcedRedraw, revert, StateSource.Manual); + _autoDesignApplier.ReapplyAutomation(actor, identifier, state, revert, out var forcedRedraw); + _stateManager.ReapplyState(actor, forcedRedraw, StateSource.Manual); } } } @@ -413,7 +352,7 @@ public class CommandService : IDisposable, IApiService foreach (var identifier in identifiers) { if (_stateManager.TryGetValue(identifier, out var state)) - _stateManager.ResetState(state, StateSource.Manual, isFinal: true); + _stateManager.ResetState(state, StateSource.Manual); } @@ -432,13 +371,14 @@ public class CommandService : IDisposable, IApiService if (!IdentifierHandling(argument, out var identifiers, false, true)) return false; + _objects.Update(); foreach (var identifier in identifiers) { if (!_objects.TryGetValue(identifier, out var data)) return true; foreach (var actor in data.Objects) - _stateManager.ReapplyState(actor, false, StateSource.Manual, true); + _stateManager.ReapplyState(actor, false, StateSource.Manual); } @@ -491,6 +431,7 @@ public class CommandService : IDisposable, IApiService if (!IdentifierHandling(split[1], out var identifiers, false, true)) return false; + _objects.Update(); foreach (var identifier in identifiers) { if (!_objects.TryGetValue(identifier, out var actors)) @@ -573,6 +514,7 @@ public class CommandService : IDisposable, IApiService if (!IdentifierHandling(split[1], out var identifiers, false, true)) return false; + _objects.Update(); foreach (var identifier in identifiers) { if (!_objects.TryGetValue(identifier, out var actors)) @@ -720,12 +662,13 @@ public class CommandService : IDisposable, IApiService if (!_resolver.GetDesign(split[0], out var design, true) || !IdentifierHandling(split2[0], out var identifiers, false, true)) return false; + _objects.Update(); foreach (var identifier in identifiers) { if (!_objects.TryGetValue(identifier, out var actors)) { if (_stateManager.TryGetValue(identifier, out var state)) - _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true }); + _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks); } else { @@ -734,7 +677,7 @@ public class CommandService : IDisposable, IApiService if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state)) { ApplyModSettings(design, actor, applyMods); - _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true }); + _stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks); } } } @@ -748,8 +691,7 @@ public class CommandService : IDisposable, IApiService if (!applyMods || design is not Design d) return; - var (messages, appliedMods, _, name, overridden) = - _modApplier.ApplyModSettings(d.AssociatedMods, actor, StateSource.Manual, d.ResetTemporarySettings); + var (messages, appliedMods, _, name, overridden) = _modApplier.ApplyModSettings(d.AssociatedMods, actor, d.ResetTemporarySettings); foreach (var message in messages) Glamourer.Messager.Chat.Print($"Error applying mod settings: {message}"); @@ -797,6 +739,7 @@ public class CommandService : IDisposable, IApiService if (!IdentifierHandling(argument, out var identifiers, false, true)) return false; + _objects.Update(); foreach (var identifier in identifiers) { if (!_stateManager.TryGetValue(identifier, out var state) @@ -837,6 +780,7 @@ public class CommandService : IDisposable, IApiService if (!IdentifierHandling(split[1], out var identifiers, false, true)) return false; + _objects.Update(); foreach (var identifier in identifiers) { if (!_stateManager.TryGetValue(identifier, out var state) diff --git a/Glamourer/Services/ConfigMigrationService.cs b/Glamourer/Services/ConfigMigrationService.cs index ef39f1a..3f997c9 100644 --- a/Glamourer/Services/ConfigMigrationService.cs +++ b/Glamourer/Services/ConfigMigrationService.cs @@ -24,20 +24,9 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator MigrateV4To5(); MigrateV5To6(); MigrateV6To7(); - MigrateV7To8(); AddColors(config, true); } - private void MigrateV7To8() - { - if (_config.Version > 7) - return; - - if (_config.QdbButtons.HasFlag(QdbButtons.RevertAdvancedDyes)) - _config.QdbButtons |= QdbButtons.RevertAdvancedCustomization; - _config.Version = 8; - } - private void MigrateV6To7() { if (_config.Version > 6) @@ -54,7 +43,7 @@ public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator return; if (_data["ShowRevertAdvancedParametersButton"]?.ToObject() ?? true) - _config.QdbButtons |= QdbButtons.RevertAdvancedCustomization; + _config.QdbButtons |= QdbButtons.RevertAdvanced; _config.Version = 6; } diff --git a/Glamourer/Services/DalamudServices.cs b/Glamourer/Services/DalamudServices.cs index e8a9f55..02df634 100644 --- a/Glamourer/Services/DalamudServices.cs +++ b/Glamourer/Services/DalamudServices.cs @@ -1,3 +1,4 @@ +using Dalamud.Game.ClientState.Objects; using Dalamud.Interface.DragDrop; using Dalamud.Plugin; using Dalamud.Plugin.Services; @@ -5,8 +6,6 @@ using OtterGui.Services; namespace Glamourer.Services; -#pragma warning disable SeStringEvaluator - public class DalamudServices { public static void AddServices(ServiceManager services, IDalamudPluginInterface pi) @@ -16,7 +15,6 @@ public class DalamudServices services.AddDalamudService(pi); services.AddDalamudService(pi); services.AddDalamudService(pi); - services.AddDalamudService(pi); services.AddDalamudService(pi); services.AddDalamudService(pi); services.AddDalamudService(pi); @@ -30,6 +28,5 @@ public class DalamudServices services.AddDalamudService(pi); services.AddDalamudService(pi); services.AddDalamudService(pi); - services.AddDalamudService(pi); } } diff --git a/Glamourer/Services/DesignApplier.cs b/Glamourer/Services/DesignApplier.cs index f0a9ba4..e0134d4 100644 --- a/Glamourer/Services/DesignApplier.cs +++ b/Glamourer/Services/DesignApplier.cs @@ -1,12 +1,13 @@ using Glamourer.Designs; +using Glamourer.Interop; +using Glamourer.Interop.Structs; using Glamourer.State; using OtterGui.Services; using Penumbra.GameData.Actors; -using Penumbra.GameData.Interop; namespace Glamourer.Services; -public sealed class DesignApplier(StateManager stateManager, ActorObjectManager objects) : IService +public sealed class DesignApplier(StateManager stateManager, ObjectManager objects) : IService { public void ApplyToPlayer(DesignBase design) { @@ -33,10 +34,16 @@ public sealed class DesignApplier(StateManager stateManager, ActorObjectManager } public void Apply(ActorIdentifier actor, DesignBase design) - => Apply(actor, objects.TryGetValue(actor, out var d) ? d : ActorData.Invalid, design, ApplySettings.ManualWithLinks); + { + objects.Update(); + Apply(actor, objects.TryGetValue(actor, out var d) ? d : ActorData.Invalid, design, ApplySettings.ManualWithLinks); + } public void Apply(ActorIdentifier actor, DesignBase design, ApplySettings settings) - => Apply(actor, objects.TryGetValue(actor, out var d) ? d : ActorData.Invalid, design, settings); + { + objects.Update(); + Apply(actor, objects.TryGetValue(actor, out var d) ? d : ActorData.Invalid, design, settings); + } public void Apply(ActorIdentifier actor, ActorData data, DesignBase design) => Apply(actor, data, design, ApplySettings.ManualWithLinks); diff --git a/Glamourer/Services/DesignResolver.cs b/Glamourer/Services/DesignResolver.cs index 8bb5cd2..68b54bb 100644 --- a/Glamourer/Services/DesignResolver.cs +++ b/Glamourer/Services/DesignResolver.cs @@ -4,7 +4,7 @@ using Glamourer.Designs; using Glamourer.Designs.Special; using Glamourer.Gui; using Glamourer.Gui.Tabs.DesignTab; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui.Services; using OtterGui.Classes; diff --git a/Glamourer/Services/ItemManager.cs b/Glamourer/Services/ItemManager.cs index a885b54..5d6f074 100644 --- a/Glamourer/Services/ItemManager.cs +++ b/Glamourer/Services/ItemManager.cs @@ -145,10 +145,8 @@ public class ItemManager // Only from early designs as migration. if (!id.IsBonusItem || id.Id == 0) { - if (IsBonusItemValid(slot, (BonusItemId)id.Id, out var item)) - return item; - - return EquipItem.BonusItemNothing(slot); + IsBonusItemValid(slot, (BonusItemId)id.Id, out var item); + return item; } if (!id.IsCustom) @@ -176,36 +174,6 @@ public class ItemManager return NothingItem(offhandType); } - public bool FindClosestShield(ItemId id, out EquipItem item) - { - var list = ItemData.ByType[FullEquipType.Shield]; - try - { - item = list.Where(i => i.ItemId.Id > id.Id && i.ItemId.Id - id.Id < 50).MinBy(i => i.ItemId.Id); - return true; - } - catch - { - item = default; - return false; - } - } - - public bool FindClosestSword(ItemId id, out EquipItem item) - { - var list = ItemData.ByType[FullEquipType.Sword]; - try - { - item = list.Where(i => i.ItemId.Id < id.Id && id.Id - i.ItemId.Id < 50).MaxBy(i => i.ItemId.Id); - return true; - } - catch - { - item = default; - return false; - } - } - public EquipItem Identify(EquipSlot slot, PrimaryId id, SecondaryId type, Variant variant, FullEquipType mainhandType = FullEquipType.Unknown) { diff --git a/Glamourer/Services/PcpService.cs b/Glamourer/Services/PcpService.cs deleted file mode 100644 index 3363172..0000000 --- a/Glamourer/Services/PcpService.cs +++ /dev/null @@ -1,119 +0,0 @@ -using Glamourer.Designs; -using Glamourer.Interop.Penumbra; -using Glamourer.State; -using Newtonsoft.Json.Linq; -using OtterGui.Services; -using Penumbra.GameData.Actors; -using Penumbra.GameData.Interop; - -namespace Glamourer.Services; - -public class PcpService : IRequiredService -{ - private readonly Configuration _config; - private readonly PenumbraService _penumbra; - private readonly ActorObjectManager _objects; - private readonly StateManager _state; - private readonly DesignConverter _designConverter; - private readonly DesignManager _designManager; - - public PcpService(Configuration config, PenumbraService penumbra, ActorObjectManager objects, StateManager state, - DesignConverter designConverter, DesignManager designManager) - { - _config = config; - _penumbra = penumbra; - _objects = objects; - _state = state; - _designConverter = designConverter; - _designManager = designManager; - - _config.AttachToPcp = !_config.AttachToPcp; - Set(!_config.AttachToPcp); - } - - public void CleanPcpDesigns() - { - var designs = _designManager.Designs.Where(d => d.Tags.Contains("PCP")).ToList(); - Glamourer.Log.Information($"[PCPService] Deleting {designs.Count} designs containing the tag PCP."); - foreach (var design in designs) - _designManager.Delete(design); - } - - public void Set(bool value) - { - if (value == _config.AttachToPcp) - return; - - _config.AttachToPcp = value; - _config.Save(); - if (value) - { - Glamourer.Log.Information("[PCPService] Attached to PCP handling."); - _penumbra.PcpCreated += OnPcpCreation; - _penumbra.PcpParsed += OnPcpParse; - } - else - { - Glamourer.Log.Information("[PCPService] Detached from PCP handling."); - _penumbra.PcpCreated -= OnPcpCreation; - _penumbra.PcpParsed -= OnPcpParse; - } - } - - private void OnPcpParse(JObject jObj, string modDirectory, Guid collection) - { - Glamourer.Log.Debug("[PCPService] Parsing PCP file."); - if (jObj["Glamourer"] is not JObject glamourer) - return; - - if (glamourer["Version"]!.ToObject() is not 1) - return; - - if (_designConverter.FromJObject(glamourer["Design"] as JObject, true, true) is not { } designBase) - return; - - var actorIdentifier = _objects.Actors.FromJson(jObj["Actor"] as JObject); - if (!actorIdentifier.IsValid) - return; - - var time = new DateTimeOffset(jObj["Time"]?.ToObject() ?? DateTime.UtcNow); - var design = _designManager.CreateClone(designBase, - $"{_config.PcpFolder}/{actorIdentifier} - {jObj["Note"]?.ToObject() ?? string.Empty}", true); - _designManager.AddTag(design, "PCP"); - _designManager.SetWriteProtection(design, true); - _designManager.AddMod(design, new Mod(modDirectory, modDirectory), new ModSettings([], 0, true, false, false)); - _designManager.ChangeDescription(design, $"PCP design created for {actorIdentifier} on {time}."); - _designManager.ChangeResetAdvancedDyes(design, true); - _designManager.SetQuickDesign(design, false); - _designManager.ChangeColor(design, _config.PcpColor); - - Glamourer.Log.Debug("[PCPService] Created PCP design."); - if (_state.GetOrCreate(actorIdentifier, _objects.TryGetValue(actorIdentifier, out var data) ? data.Objects[0] : Actor.Null, - out var state)) - { - _state.ApplyDesign(state!, design, ApplySettings.Manual); - Glamourer.Log.Debug($"[PCPService] Applied PCP design to {actorIdentifier.Incognito(null)}"); - } - } - - private void OnPcpCreation(JObject jObj, ushort index, string path) - { - Glamourer.Log.Debug("[PCPService] Adding Glamourer data to PCP file."); - var actorIdentifier = _objects.Actors.FromJson(jObj["Actor"] as JObject); - if (!actorIdentifier.IsValid) - return; - - if (!_state.GetOrCreate(actorIdentifier, _objects.Objects[(int)index], out var state)) - { - Glamourer.Log.Debug($"[PCPService] Could not get or create state for actor {index}."); - return; - } - - var design = _designConverter.Convert(state, ApplicationRules.All); - jObj["Glamourer"] = new JObject - { - ["Version"] = 1, - ["Design"] = design.JsonSerialize(), - }; - } -} diff --git a/Glamourer/Services/ServiceManager.cs b/Glamourer/Services/ServiceManager.cs index 6cfb4b6..baae507 100644 --- a/Glamourer/Services/ServiceManager.cs +++ b/Glamourer/Services/ServiceManager.cs @@ -27,7 +27,6 @@ using OtterGui.Services; using Penumbra.GameData.Actors; using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers; -using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Services; @@ -71,7 +70,6 @@ public static class StaticServiceManager private static ServiceManager AddEvents(this ServiceManager services) => services.AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -97,7 +95,6 @@ public static class StaticServiceManager private static ServiceManager AddInterop(this ServiceManager services) => services.AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -105,7 +102,6 @@ public static class StaticServiceManager .AddSingleton() .AddSingleton(p => new CutsceneResolver(p.GetRequiredService().CutsceneParent)) .AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -114,8 +110,7 @@ public static class StaticServiceManager .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() - .AddSingleton(); + .AddSingleton(); private static ServiceManager AddDesigns(this ServiceManager services) => services.AddSingleton() diff --git a/Glamourer/Services/TextureService.cs b/Glamourer/Services/TextureService.cs index a0ec443..29c2343 100644 --- a/Glamourer/Services/TextureService.cs +++ b/Glamourer/Services/TextureService.cs @@ -1,4 +1,3 @@ -using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Plugin.Services; @@ -13,30 +12,30 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage { private readonly IDalamudTextureWrap?[] _slotIcons = CreateSlotIcons(uiBuilder); - public (ImTextureID, Vector2, bool) GetIcon(EquipItem item, EquipSlot slot) + public (nint, Vector2, bool) GetIcon(EquipItem item, EquipSlot slot) { if (item.IconId.Id != 0 && TryLoadIcon(item.IconId.Id, out var ret)) - return (ret.Handle, new Vector2(ret.Width, ret.Height), false); + return (ret.ImGuiHandle, new Vector2(ret.Width, ret.Height), false); var idx = slot.ToIndex(); return idx < 12 && _slotIcons[idx] != null - ? (_slotIcons[idx]!.Handle, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true) - : (default, Vector2.Zero, true); + ? (_slotIcons[idx]!.ImGuiHandle, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true) + : (nint.Zero, Vector2.Zero, true); } - public (ImTextureID, Vector2, bool) GetIcon(EquipItem item, BonusItemFlag slot) + public (nint, Vector2, bool) GetIcon(EquipItem item, BonusItemFlag slot) { if (item.IconId.Id != 0 && TryLoadIcon(item.IconId.Id, out var ret)) - return (ret.Handle, new Vector2(ret.Width, ret.Height), false); + return (ret.ImGuiHandle, new Vector2(ret.Width, ret.Height), false); var idx = slot.ToIndex(); if (idx == uint.MaxValue) - return (default, Vector2.Zero, true); + return (nint.Zero, Vector2.Zero, true); idx += 12; return idx < 13 && _slotIcons[idx] != null - ? (_slotIcons[idx]!.Handle, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true) - : (default, Vector2.Zero, true); + ? (_slotIcons[idx]!.ImGuiHandle, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true) + : (nint.Zero, Vector2.Zero, true); } public void Dispose() diff --git a/Glamourer/State/FunModule.cs b/Glamourer/State/FunModule.cs index 6abb03a..1ca5c48 100644 --- a/Glamourer/State/FunModule.cs +++ b/Glamourer/State/FunModule.cs @@ -4,14 +4,14 @@ using Glamourer.Designs; using Glamourer.GameData; using Glamourer.Gui; using Glamourer.Services; -using Dalamud.Bindings.ImGui; +using ImGuiNET; using OtterGui; using OtterGui.Classes; -using OtterGui.Extensions; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; using CustomizeIndex = Penumbra.GameData.Enums.CustomizeIndex; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.State; @@ -35,7 +35,7 @@ public unsafe class FunModule : IDisposable private readonly StateManager _stateManager; private readonly DesignConverter _designConverter; private readonly DesignManager _designManager; - private readonly ActorObjectManager _objects; + private readonly ObjectManager _objects; private readonly NpcCustomizeSet _npcs; private readonly StainId[] _stains; @@ -69,7 +69,7 @@ public unsafe class FunModule : IDisposable => OnDayChange(DateTime.Now.Day, DateTime.Now.Month, DateTime.Now.Year); public FunModule(CodeService codes, CustomizeService customizations, ItemManager items, Configuration config, - GenericPopupWindow popupWindow, StateManager stateManager, ActorObjectManager objects, DesignConverter designConverter, + GenericPopupWindow popupWindow, StateManager stateManager, ObjectManager objects, DesignConverter designConverter, DesignManager designManager, NpcCustomizeSet npcs) { _codes = codes; @@ -125,7 +125,9 @@ public unsafe class FunModule : IDisposable switch (_codes.Masked(CodeService.GearCodes)) { - case CodeService.CodeFlag.Emperor: SetRandomItem(slot, ref armor); break; + case CodeService.CodeFlag.Emperor: + SetRandomItem(slot, ref armor); + break; case CodeService.CodeFlag.Elephants: case CodeService.CodeFlag.Dolphins: case CodeService.CodeFlag.World when actor.Index != 0: @@ -135,7 +137,9 @@ public unsafe class FunModule : IDisposable switch (_codes.Masked(CodeService.DyeCodes)) { - case CodeService.CodeFlag.Clown: SetRandomDye(ref armor); break; + case CodeService.CodeFlag.Clown: + SetRandomDye(ref armor); + break; } } @@ -302,7 +306,9 @@ public unsafe class FunModule : IDisposable SetDolphin(EquipSlot.Body, ref armor[1]); SetDolphin(EquipSlot.Head, ref armor[0]); break; - case CodeService.CodeFlag.World when actor.Index != 0: _worldSets.Apply(actor, _rng, armor); break; + case CodeService.CodeFlag.World when actor.Index != 0: + _worldSets.Apply(actor, _rng, armor); + break; } switch (_codes.Masked(CodeService.DyeCodes)) @@ -362,17 +368,17 @@ public unsafe class FunModule : IDisposable private static IReadOnlyList DolphinBodies => [ - new(6089, 1, new StainIds(4)), // Toad - new(6089, 1, new StainIds(4)), // Toad - new(6089, 1, new StainIds(4)), // Toad - new(6023, 1, new StainIds(4)), // Swine - new(6023, 1, new StainIds(4)), // Swine - new(6023, 1, new StainIds(4)), // Swine - new(6133, 1, new StainIds(4)), // Gaja - new(6182, 1, new StainIds(3)), // Imp - new(6182, 1, new StainIds(3)), // Imp - new(6182, 1, new StainIds(4)), // Imp - new(6182, 1, new StainIds(4)), // Imp + new CharacterArmor(6089, 1, new StainIds(4)), // Toad + new CharacterArmor(6089, 1, new StainIds(4)), // Toad + new CharacterArmor(6089, 1, new StainIds(4)), // Toad + new CharacterArmor(6023, 1, new StainIds(4)), // Swine + new CharacterArmor(6023, 1, new StainIds(4)), // Swine + new CharacterArmor(6023, 1, new StainIds(4)), // Swine + new CharacterArmor(6133, 1, new StainIds(4)), // Gaja + new CharacterArmor(6182, 1, new StainIds(3)), // Imp + new CharacterArmor(6182, 1, new StainIds(3)), // Imp + new CharacterArmor(6182, 1, new StainIds(4)), // Imp + new CharacterArmor(6182, 1, new StainIds(4)), // Imp ]; private void SetDolphin(EquipSlot slot, ref CharacterArmor armor) diff --git a/Glamourer/State/StateApplier.cs b/Glamourer/State/StateApplier.cs index 9800445..2e086d6 100644 --- a/Glamourer/State/StateApplier.cs +++ b/Glamourer/State/StateApplier.cs @@ -7,7 +7,6 @@ using Glamourer.Interop.Structs; using Glamourer.Services; using Penumbra.Api.Enums; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.State; @@ -24,8 +23,9 @@ public class StateApplier( ItemManager _items, PenumbraService _penumbra, MetaService _metaService, - ActorObjectManager _objects, + ObjectManager _objects, CrestService _crests, + Configuration _config, DirectXService _directX) { /// Simply force a redraw regardless of conditions. @@ -262,14 +262,6 @@ public class StateApplier( _visor.SetVisorState(actor.Model, value); return; } - case MetaIndex.EarState: - foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) - { - var model = actor.Model; - model.VieraEarsVisible = value; - } - - return; } } @@ -299,26 +291,30 @@ public class StateApplier( } /// Change the customize parameters on models. Can change multiple at once. - public void ChangeParameters(ActorData data, CustomizeParameterFlag flags, in CustomizeParameterData values) + public void ChangeParameters(ActorData data, CustomizeParameterFlag flags, in CustomizeParameterData values, bool force) { - if (flags == 0) + if (!force && !_config.UseAdvancedParameters || flags == 0) return; foreach (var actor in data.Objects.Where(a => a is { IsCharacter: true, Model.IsHuman: true })) actor.Model.ApplyParameterData(flags, values); } - /// + /// public ActorData ChangeParameters(ActorState state, CustomizeParameterFlag flags, bool apply) { var data = GetData(state); if (apply) - ChangeParameters(data, flags, state.ModelData.Parameters); + ChangeParameters(data, flags, state.ModelData.Parameters, state.IsLocked); return data; } - public unsafe void ChangeMaterialValue(ActorState state, ActorData data, MaterialValueIndex changedIndex, ColorRow? changedValue) + public unsafe void ChangeMaterialValue(ActorState state, ActorData data, MaterialValueIndex changedIndex, ColorRow? changedValue, + bool force) { + if (!force && !_config.UseAdvancedDyes) + return; + foreach (var actor in data.Objects.Where(a => a is { IsCharacter: true, Model.IsHuman: true })) { if (!changedIndex.TryGetTexture(actor, out var texture)) @@ -345,13 +341,16 @@ public class StateApplier( { var data = GetData(state); if (apply) - ChangeMaterialValue(state, data, index, state.Materials.TryGetValue(index, out var v) ? v.Model : null); + ChangeMaterialValue(state, data, index, state.Materials.TryGetValue(index, out var v) ? v.Model : null, state.IsLocked); return data; } - public unsafe void ChangeMaterialValues(ActorData data, in StateMaterialManager materials) + public unsafe void ChangeMaterialValues(ActorData data, in StateMaterialManager materials, bool force) { + if (!force && !_config.UseAdvancedDyes) + return; + var groupedMaterialValues = materials.Values.Select(p => (MaterialValueIndex.FromKey(p.Key), p.Value)) .GroupBy(p => (p.Item1.DrawObject, p.Item1.SlotIndex, p.Item1.MaterialIndex)); @@ -385,7 +384,7 @@ public class StateApplier( var actors = ChangeMetaState(state, MetaIndex.Wetness, true); if (redraw) { - if (withLock && actors.Valid) + if (withLock) state.TempLock(); ForceRedraw(actors); } @@ -410,16 +409,18 @@ public class StateApplier( ChangeMetaState(actors, MetaIndex.HatState, state.ModelData.IsHatVisible()); ChangeMetaState(actors, MetaIndex.WeaponState, state.ModelData.IsWeaponVisible()); ChangeMetaState(actors, MetaIndex.VisorState, state.ModelData.IsVisorToggled()); - ChangeMetaState(actors, MetaIndex.EarState, state.ModelData.AreEarsVisible()); ChangeCrests(actors, state.ModelData.CrestVisibility); - ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters); - ChangeMaterialValues(actors, state.Materials); + ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters, state.IsLocked); + ChangeMaterialValues(actors, state.Materials, state.IsLocked); } } return actors; } - public ActorData GetData(ActorState state) - => _objects.TryGetValue(state.Identifier, out var data) ? data : ActorData.Invalid; + private ActorData GetData(ActorState state) + { + _objects.Update(); + return _objects.TryGetValue(state.Identifier, out var data) ? data : ActorData.Invalid; + } } diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index 986bdc2..f9ddb89 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -9,7 +9,6 @@ using Glamourer.Interop.Penumbra; using Glamourer.Interop.Structs; using Glamourer.Services; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.State; @@ -18,7 +17,6 @@ public class StateEditor( InternalStateEditor editor, StateApplier applier, StateChanged stateChanged, - StateFinalized stateFinalized, JobChangeState jobChange, Configuration config, ItemManager items, @@ -26,12 +24,11 @@ public class StateEditor( ModSettingApplier modApplier, GPoseService gPose) : IDesignEditor { - protected readonly InternalStateEditor Editor = editor; - protected readonly StateApplier Applier = applier; - protected readonly StateChanged StateChanged = stateChanged; - protected readonly StateFinalized StateFinalized = stateFinalized; - protected readonly Configuration Config = config; - protected readonly ItemManager Items = items; + protected readonly InternalStateEditor Editor = editor; + protected readonly StateApplier Applier = applier; + protected readonly StateChanged StateChanged = stateChanged; + protected readonly Configuration Config = config; + protected readonly ItemManager Items = items; /// Turn an actor to. public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source, @@ -44,7 +41,6 @@ public class StateEditor( Glamourer.Log.Verbose( $"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]"); StateChanged.Invoke(StateChangeType.Model, source, state, actors, null); - StateFinalized.Invoke(StateFinalizationType.ModelChange, actors); } /// @@ -263,7 +259,7 @@ public class StateEditor( public void ApplyDesign(object data, MergedDesign mergedDesign, ApplySettings settings) { var state = (ActorState)data; - modApplier.HandleStateApplication(state, mergedDesign, settings.Source, true, settings.RespectManual); + modApplier.HandleStateApplication(state, mergedDesign); if (!Editor.ChangeModelId(state, mergedDesign.Design.DesignData.ModelId, mergedDesign.Design.DesignData.Customize, mergedDesign.Design.GetDesignDataRef().GetEquipmentPtr(), settings.Source, out var oldModelId, settings.Key)) return; @@ -384,7 +380,7 @@ public class StateEditor( Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key); } - if (settings.ResetMaterials || !settings.RespectManual && mergedDesign.ResetAdvancedDyes) + if (settings.ResetMaterials || (!settings.RespectManual && mergedDesign.ResetAdvancedDyes)) state.Materials.Clear(); foreach (var (key, value) in mergedDesign.Design.Materials) @@ -408,8 +404,7 @@ public class StateEditor( } else if (!value.Revert) { - Editor.ChangeMaterialValue(state, idx, - new MaterialValueState(ColorRow.Empty, value.Value, CharacterWeapon.Empty, source), + Editor.ChangeMaterialValue(state, idx, new MaterialValueState(ColorRow.Empty, value.Value, CharacterWeapon.Empty, source), settings.Source, out _, settings.Key); } } @@ -422,8 +417,6 @@ public class StateEditor( Glamourer.Log.Verbose( $"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]"); StateChanged.Invoke(StateChangeType.Design, state.Sources[MetaIndex.Wetness], state, actors, null); // FIXME: maybe later - if (settings.IsFinal) - StateFinalized.Invoke(StateFinalizationType.DesignApplied, actors); return; @@ -444,8 +437,7 @@ public class StateEditor( if (!settings.MergeLinks || design is not Design d) merged = new MergedDesign(design); else - merged = merger.Merge(d.AllLinks(true), state.ModelData.IsHuman ? state.ModelData.Customize : CustomizeArray.Default, - state.BaseData, + merged = merger.Merge(d.AllLinks(true), state.ModelData.IsHuman ? state.ModelData.Customize : CustomizeArray.Default, state.BaseData, false, Config.AlwaysApplyAssociatedMods); ApplyDesign(data, merged, settings with @@ -463,7 +455,7 @@ public class StateEditor( if (!Config.ChangeEntireItem || !settings.Source.IsManual()) return; - var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand); + var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand); // Do not change Shields to nothing. if (mh.Type is FullEquipType.Sword) return; diff --git a/Glamourer/State/StateIndex.cs b/Glamourer/State/StateIndex.cs index dff05a3..0ac52ec 100644 --- a/Glamourer/State/StateIndex.cs +++ b/Glamourer/State/StateIndex.cs @@ -1,5 +1,4 @@ -using Glamourer.Api.Enums; -using Glamourer.Designs; +using Glamourer.Designs; using Glamourer.GameData; using Penumbra.GameData.Enums; @@ -189,9 +188,8 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators MetaFlag.HatState, MetaVisorState => MetaFlag.VisorState, MetaWeaponState => MetaFlag.WeaponState, - MetaEarState => MetaFlag.EarState, MetaModelId => true, CrestHead => CrestFlag.Head, diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 4b70718..651e63e 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -9,13 +9,12 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Glamourer.GameData; using Penumbra.GameData.DataContainers; using Glamourer.Designs; using Penumbra.GameData.Interop; -using Glamourer.Api.Enums; +using ObjectManager = Glamourer.Interop.ObjectManager; namespace Glamourer.State; @@ -28,7 +27,7 @@ public class StateListener : IDisposable { private readonly Configuration _config; private readonly ActorManager _actors; - private readonly ActorObjectManager _objects; + private readonly ObjectManager _objects; private readonly StateManager _manager; private readonly StateApplier _applier; private readonly ItemManager _items; @@ -36,13 +35,10 @@ public class StateListener : IDisposable private readonly PenumbraService _penumbra; private readonly EquipSlotUpdating _equipSlotUpdating; private readonly BonusSlotUpdating _bonusSlotUpdating; - private readonly GearsetDataLoaded _gearsetDataLoaded; private readonly WeaponLoading _weaponLoading; private readonly HeadGearVisibilityChanged _headGearVisibility; private readonly VisorStateChanged _visorState; - private readonly VieraEarStateChanged _vieraEarState; private readonly WeaponVisibilityChanged _weaponVisibility; - private readonly StateFinalized _stateFinalized; private readonly AutoDesignApplier _autoDesignApplier; private readonly FunModule _funModule; private readonly HumanModelList _humans; @@ -60,11 +56,11 @@ public class StateListener : IDisposable private ActorState? _customizeState; public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config, - EquipSlotUpdating equipSlotUpdating, GearsetDataLoaded gearsetDataLoaded, WeaponLoading weaponLoading, VisorStateChanged visorState, + EquipSlotUpdating equipSlotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, - FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ActorObjectManager objects, + FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition, - CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateFinalized stateFinalized, VieraEarStateChanged vieraEarState) + CrestService crestService, BonusSlotUpdating bonusSlotUpdating) { _manager = manager; _items = items; @@ -72,7 +68,6 @@ public class StateListener : IDisposable _actors = actors; _config = config; _equipSlotUpdating = equipSlotUpdating; - _gearsetDataLoaded = gearsetDataLoaded; _weaponLoading = weaponLoading; _visorState = visorState; _weaponVisibility = weaponVisibility; @@ -89,8 +84,6 @@ public class StateListener : IDisposable _condition = condition; _crestService = crestService; _bonusSlotUpdating = bonusSlotUpdating; - _stateFinalized = stateFinalized; - _vieraEarState = vieraEarState; Subscribe(); } @@ -207,7 +200,9 @@ public class StateListener : IDisposable } break; - case UpdateState.NoChange: customize = state.ModelData.Customize; break; + case UpdateState.NoChange: + customize = state.ModelData.Customize; + break; } } @@ -262,27 +257,16 @@ public class StateListener : IDisposable item = state.ModelData.BonusItem(slot).Armor(); break; // Use current model data. - case UpdateState.NoChange: item = state.ModelData.BonusItem(slot).Armor(); break; + case UpdateState.NoChange: + item = state.ModelData.BonusItem(slot).Armor(); + break; case UpdateState.Transformed: break; } } - private void OnGearsetDataLoaded(Actor actor, Model model) - { - if (!actor.Valid || _condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) - return; - - // ensure actor and state are valid. - if (!actor.Identifier(_actors, out var identifier)) - return; - - if (_objects.TryGetValue(identifier, out var actors) && actors.Valid) - _stateFinalized.Invoke(StateFinalizationType.Gearset, actors); - } - - private void OnMovedEquipment((EquipSlot, uint, StainIds)[] items) { + _objects.Update(); var (identifier, objects) = _objects.PlayerData; if (!identifier.IsValid || !_manager.TryGetValue(identifier, out var state)) return; @@ -305,7 +289,7 @@ public class StateListener : IDisposable var stainChanged = current.Stains == changed.Stains && !state.Sources[slot, true].IsFixed(); - switch (itemChanged, stainChanged) + switch ((itemChanged, stainChanged)) { case (true, true): _manager.ChangeEquip(state, slot, currentItem, current.Stains, ApplySettings.Game); @@ -371,7 +355,9 @@ public class StateListener : IDisposable else apply = true; break; - case UpdateState.NoChange: apply = true; break; + case UpdateState.NoChange: + apply = true; + break; } var baseType = slot is EquipSlot.OffHand ? state.BaseData.MainhandType.Offhand() : state.BaseData.MainhandType; @@ -713,44 +699,6 @@ public class StateListener : IDisposable } } - /// Handle visor state changes made by the game. - private void OnVieraEarChange(Actor actor, ref bool value) - { - // Value is inverted compared to our own handling. - - // Skip updates when in customize update. - if (ChangeCustomizeService.InUpdate.InMethod) - return; - - if (!actor.IsCharacter) - return; - - if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) - return; - - if (!actor.Identifier(_actors, out var identifier)) - return; - - if (!_manager.TryGetValue(identifier, out var state)) - return; - - // Update visor base state. - if (state.BaseData.SetEarsVisible(!value)) - { - // if base state changed, either overwrite the actual value if we have fixed values, - // or overwrite the stored model state with the new one. - if (state.Sources[MetaIndex.EarState].IsFixed()) - value = !state.ModelData.AreEarsVisible(); - else - _manager.ChangeMetaState(state, MetaIndex.EarState, !value, ApplySettings.Game); - } - else - { - // if base state did not change, overwrite the value with the model state one. - value = !state.ModelData.AreEarsVisible(); - } - } - /// Handle Hat Visibility changes. These act on the game object. private void OnHeadGearVisibilityChange(Actor actor, ref bool value) { @@ -839,11 +787,9 @@ public class StateListener : IDisposable _penumbra.CreatedCharacterBase += OnCreatedCharacterBase; _equipSlotUpdating.Subscribe(OnEquipSlotUpdating, EquipSlotUpdating.Priority.StateListener); _bonusSlotUpdating.Subscribe(OnBonusSlotUpdating, BonusSlotUpdating.Priority.StateListener); - _gearsetDataLoaded.Subscribe(OnGearsetDataLoaded, GearsetDataLoaded.Priority.StateListener); _movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener); _weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener); _visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener); - _vieraEarState.Subscribe(OnVieraEarChange, VieraEarStateChanged.Priority.StateListener); _headGearVisibility.Subscribe(OnHeadGearVisibilityChange, HeadGearVisibilityChanged.Priority.StateListener); _weaponVisibility.Subscribe(OnWeaponVisibilityChange, WeaponVisibilityChanged.Priority.StateListener); _changeCustomizeService.Subscribe(OnCustomizeChange, ChangeCustomizeService.Priority.StateListener); @@ -858,11 +804,9 @@ public class StateListener : IDisposable _penumbra.CreatedCharacterBase -= OnCreatedCharacterBase; _equipSlotUpdating.Unsubscribe(OnEquipSlotUpdating); _bonusSlotUpdating.Unsubscribe(OnBonusSlotUpdating); - _gearsetDataLoaded.Unsubscribe(OnGearsetDataLoaded); _movedEquipment.Unsubscribe(OnMovedEquipment); _weaponLoading.Unsubscribe(OnWeaponLoading); _visorState.Unsubscribe(OnVisorChange); - _vieraEarState.Unsubscribe(OnVieraEarChange); _headGearVisibility.Unsubscribe(OnHeadGearVisibilityChange); _weaponVisibility.Unsubscribe(OnWeaponVisibilityChange); _changeCustomizeService.Unsubscribe(OnCustomizeChange); @@ -922,7 +866,7 @@ public class StateListener : IDisposable case StateSource.Manual: if (state.BaseData.Parameters.Set(flag, newValue)) _manager.ChangeCustomizeParameter(state, flag, newValue, ApplySettings.Game); - else + else if (_config.UseAdvancedParameters) model.ApplySingleParameterData(flag, state.ModelData.Parameters); break; case StateSource.IpcManual: @@ -933,7 +877,8 @@ public class StateListener : IDisposable break; case StateSource.Fixed: state.BaseData.Parameters.Set(flag, newValue); - model.ApplySingleParameterData(flag, state.ModelData.Parameters); + if (_config.UseAdvancedParameters) + model.ApplySingleParameterData(flag, state.ModelData.Parameters); break; case StateSource.IpcFixed: state.BaseData.Parameters.Set(flag, newValue); @@ -942,12 +887,14 @@ public class StateListener : IDisposable case StateSource.Pending: state.BaseData.Parameters.Set(flag, newValue); state.Sources[flag] = StateSource.Manual; - model.ApplySingleParameterData(flag, state.ModelData.Parameters); + if (_config.UseAdvancedParameters) + model.ApplySingleParameterData(flag, state.ModelData.Parameters); break; case StateSource.IpcPending: state.BaseData.Parameters.Set(flag, newValue); state.Sources[flag] = StateSource.IpcManual; - model.ApplySingleParameterData(flag, state.ModelData.Parameters); + if (_config.UseAdvancedParameters) + model.ApplySingleParameterData(flag, state.ModelData.Parameters); break; } } diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index e8926d6..eabaf2f 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -18,20 +18,19 @@ using Penumbra.GameData.Interop; namespace Glamourer.State; public sealed class StateManager( - ActorManager actors, + ActorManager _actors, ItemManager items, - StateChanged changeEvent, - StateFinalized finalizeEvent, + StateChanged @event, StateApplier applier, InternalStateEditor editor, - HumanModelList humans, - IClientState clientState, + HumanModelList _humans, + IClientState _clientState, Configuration config, JobChangeState jobChange, DesignMerger merger, ModSettingApplier modApplier, GPoseService gPose) - : StateEditor(editor, applier, changeEvent, finalizeEvent, jobChange, config, items, merger, modApplier, gPose), + : StateEditor(editor, applier, @event, jobChange, config, items, merger, modApplier, gPose), IReadOnlyDictionary { private readonly Dictionary _states = []; @@ -62,7 +61,7 @@ public sealed class StateManager( /// public bool GetOrCreate(Actor actor, [NotNullWhen(true)] out ActorState? state) - => GetOrCreate(actor.GetIdentifier(actors), actor, out state); + => GetOrCreate(actor.GetIdentifier(_actors), actor, out state); /// Try to obtain or create a new state for an existing actor. Returns false if no state could be created. public unsafe bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state) @@ -82,7 +81,7 @@ public sealed class StateManager( ModelData = FromActor(actor, true, false), BaseData = FromActor(actor, false, false), LastJob = (byte)(actor.IsCharacter ? actor.AsCharacter->CharacterData.ClassJob : 0), - LastTerritory = clientState.TerritoryType, + LastTerritory = _clientState.TerritoryType, }; // state.Identifier is owned. _states.Add(state.Identifier, state); @@ -115,7 +114,7 @@ public sealed class StateManager( // Model ID is only unambiguously contained in the game object. // The draw object only has the object type. // TODO reverse search model data to get model id from model. - if (!humans.IsHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId)) + if (!_humans.IsHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId)) { ret.LoadNonHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId, *(CustomizeArray*)&actor.AsCharacter->DrawData.CustomizeData, (nint)Unsafe.AsPointer(ref actor.AsCharacter->DrawData.EquipmentModelIds[0])); @@ -158,7 +157,6 @@ public sealed class StateManager( // Visor state is a flag on the game object, but we can see the actual state on the draw object. ret.SetVisor(VisorService.GetVisorState(model)); - ret.SetEarsVisible(model.VieraEarsVisible); foreach (var slot in CrestExtensions.AllRelevantSet) ret.SetCrest(slot, CrestService.GetModelCrest(actor, slot)); @@ -187,7 +185,7 @@ public sealed class StateManager( off = actor.GetOffhand(); FistWeaponHack(ref ret, ref main, ref off); ret.SetVisor(actor.AsCharacter->DrawData.IsVisorToggled); - ret.SetEarsVisible(actor.ShowVieraEars); + foreach (var slot in CrestExtensions.AllRelevantSet) ret.SetCrest(slot, actor.GetCrest(slot)); @@ -237,7 +235,7 @@ public sealed class StateManager( public void TurnHuman(ActorState state, StateSource source, uint key = 0) => ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key); - public void ResetState(ActorState state, StateSource source, uint key = 0, bool isFinal = false) + public void ResetState(ActorState state, StateSource source, uint key = 0) { if (!state.Unlock(key)) return; @@ -271,63 +269,13 @@ public sealed class StateManager( state.Materials.Clear(); - var objects = ActorData.Invalid; + var actors = ActorData.Invalid; if (source is not StateSource.Game) - objects = Applier.ApplyAll(state, redraw, true); + actors = Applier.ApplyAll(state, redraw, true); Glamourer.Log.Verbose( - $"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {objects.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Reset, source, state, objects, null); - // only invoke if we define this reset call as the final call in our state update. - if (isFinal) - StateFinalized.Invoke(StateFinalizationType.Revert, objects); - } - - public void ResetAdvancedDyes(ActorState state, StateSource source, uint key = 0) - { - if (!state.Unlock(key) || !state.ModelData.IsHuman) - return; - - state.ModelData.Parameters = state.BaseData.Parameters; - - foreach (var flag in CustomizeParameterExtensions.AllFlags) - state.Sources[flag] = StateSource.Game; - - var objects = Applier.GetData(state); - if (source is not StateSource.Game) - foreach (var (idx, mat) in state.Materials.Values) - Applier.ChangeMaterialValue(state, objects, MaterialValueIndex.FromKey(idx), mat.Game); - - state.Materials.Clear(); - - Glamourer.Log.Verbose( - $"Reset advanced dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {objects.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Reset, source, state, objects, null); - // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, objects); - } - - public void ResetAdvancedCustomizations(ActorState state, StateSource source, uint key = 0) - { - if (!state.Unlock(key) || !state.ModelData.IsHuman) - return; - - state.ModelData.Parameters = state.BaseData.Parameters; - - foreach (var flag in CustomizeParameterExtensions.AllFlags) - state.Sources[flag] = StateSource.Game; - - var objects = ActorData.Invalid; - if (source is not StateSource.Game) - objects = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true); - - state.Materials.Clear(); - - Glamourer.Log.Verbose( - $"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {objects.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Reset, source, state, objects, null); - // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, objects); + $"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); + StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null); } public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0) @@ -345,7 +293,7 @@ public sealed class StateManager( { actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true); foreach (var (idx, mat) in state.Materials.Values) - Applier.ChangeMaterialValue(state, actors, MaterialValueIndex.FromKey(idx), mat.Game); + Applier.ChangeMaterialValue(state, actors, MaterialValueIndex.FromKey(idx), mat.Game, true); } state.Materials.Clear(); @@ -353,8 +301,6 @@ public sealed class StateManager( Glamourer.Log.Verbose( $"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null); - // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, actors); } public void ResetCustomize(ActorState state, StateSource source, uint key = 0) @@ -372,8 +318,6 @@ public sealed class StateManager( actors = Applier.ChangeCustomize(state, true); Glamourer.Log.Verbose( $"Reset customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); - // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertCustomize, actors); } public void ResetEquip(ActorState state, StateSource source, uint key = 0) @@ -423,8 +367,6 @@ public sealed class StateManager( Glamourer.Log.Verbose( $"Reset equipment state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); - // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertEquipment, actors); } public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0) @@ -501,44 +443,21 @@ public sealed class StateManager( } } - public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool isFinal = false) + public void ReapplyState(Actor actor, bool forceRedraw, StateSource source) { if (!GetOrCreate(actor, out var state)) return; - ReapplyState(actor, state, forceRedraw, source, isFinal); + ReapplyState(actor, state, forceRedraw, source); } - public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool isFinal) + public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source) { var data = Applier.ApplyAll(state, forceRedraw || !actor.Model.IsHuman || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false); StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null); - if (isFinal) - StateFinalized.Invoke(StateFinalizationType.Reapply, data); - } - - /// Automation variant for reapply, to fire the correct StateUpdateType once reapplied. - public void ReapplyAutomationState(Actor actor, bool forceRedraw, bool wasReset, StateSource source) - { - if (!GetOrCreate(actor, out var state)) - return; - - ReapplyAutomationState(actor, state, forceRedraw, wasReset, source); - } - - /// Automation variant for reapply, to fire the correct StateUpdateType once reapplied. - public void ReapplyAutomationState(Actor actor, ActorState state, bool forceRedraw, bool wasReset, StateSource source) - { - var data = Applier.ApplyAll(state, - forceRedraw - || !actor.Model.IsHuman - || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false); - StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null); - // invoke the automation update based on what reset is. - StateFinalized.Invoke(wasReset ? StateFinalizationType.RevertAutomation : StateFinalizationType.ReapplyAutomation, data); } public void DeleteState(ActorIdentifier identifier) diff --git a/Glamourer/Unlocks/CustomizeUnlockManager.cs b/Glamourer/Unlocks/CustomizeUnlockManager.cs index bd13f99..18f3cac 100644 --- a/Glamourer/Unlocks/CustomizeUnlockManager.cs +++ b/Glamourer/Unlocks/CustomizeUnlockManager.cs @@ -1,15 +1,16 @@ using Dalamud.Game; using Dalamud.Hooking; using Dalamud.Plugin.Services; +using Dalamud.Utility; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.UI; using Glamourer.GameData; using Glamourer.Events; +using Glamourer.Interop; using Glamourer.Services; using Lumina.Excel.Sheets; using Penumbra.GameData; using Penumbra.GameData.Enums; -using Penumbra.GameData.Interop; namespace Glamourer.Unlocks; @@ -18,7 +19,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable private readonly SaveService _saveService; private readonly IClientState _clientState; private readonly ObjectUnlocked _event; - private readonly ActorObjectManager _objects; + private readonly ObjectManager _objects; private readonly Dictionary _unlocked = new(); public readonly IReadOnlyDictionary Unlockable; @@ -27,7 +28,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable => _unlocked; public CustomizeUnlockManager(SaveService saveService, CustomizeService customizations, IDataManager gameData, - IClientState clientState, ObjectUnlocked @event, IGameInteropProvider interop, ActorObjectManager objects) + IClientState clientState, ObjectUnlocked @event, IGameInteropProvider interop, ObjectManager objects) { interop.InitializeFromAttributes(this); _saveService = saveService; @@ -176,7 +177,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable IDataManager gameData) { var ret = new Dictionary(); - var sheet = gameData.GetExcelSheet(ClientLanguage.English); + var sheet = gameData.GetExcelSheet(ClientLanguage.English)!; foreach (var (clan, gender) in CustomizeManager.AllSets()) { var list = customizations.Manager.GetSet(clan, gender); @@ -189,7 +190,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable ? "Eternal Bond" : x.Value.HintItem.ValueNullable?.Name.ExtractText().Replace("Modern Aesthetics - ", string.Empty) ?? string.Empty; - ret.TryAdd(hair, (x.Value.UnlockLink, name)); + ret.TryAdd(hair, (x.Value.Data, name)); } } @@ -200,7 +201,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable { var name = x.Value.HintItem.ValueNullable?.Name.ExtractText().Replace("Modern Cosmetics - ", string.Empty) ?? string.Empty; - ret.TryAdd(paint, (x.Value.UnlockLink, name)); + ret.TryAdd(paint, (x.Value.Data, name)); } } } diff --git a/Glamourer/Unlocks/ItemUnlockManager.cs b/Glamourer/Unlocks/ItemUnlockManager.cs index 6708267..0fc1675 100644 --- a/Glamourer/Unlocks/ItemUnlockManager.cs +++ b/Glamourer/Unlocks/ItemUnlockManager.cs @@ -168,7 +168,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionaryGetInventoryContainer(type); - if (container != null && container->IsLoaded && _currentInventoryIndex < container->Size) + if (container != null && container->Loaded != 0 && _currentInventoryIndex < container->Size) { Glamourer.Log.Excessive($"[UnlockScanner] Scanning {_currentInventory} {type} {_currentInventoryIndex}/{container->Size}."); var item = container->GetInventorySlot(_currentInventoryIndex++); diff --git a/Glamourer/packages.lock.json b/Glamourer/packages.lock.json deleted file mode 100644 index b15c917..0000000 --- a/Glamourer/packages.lock.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net10.0-windows7.0": { - "DalamudPackager": { - "type": "Direct", - "requested": "[14.0.1, )", - "resolved": "14.0.1", - "contentHash": "y0WWyUE6dhpGdolK3iKgwys05/nZaVf4ZPtIjpLhJBZvHxkkiE23zYRo7K7uqAgoK/QvK5cqF6l3VG5AbgC6KA==" - }, - "DotNet.ReproducibleBuilds": { - "type": "Direct", - "requested": "[1.2.39, )", - "resolved": "1.2.39", - "contentHash": "fcFN01tDTIQqDuTwr1jUQK/geofiwjG5DycJQOnC72i1SsLAk1ELe+apBOuZ11UMQG8YKFZG1FgvjZPbqHyatg==" - }, - "Vortice.Direct3D11": { - "type": "Direct", - "requested": "[3.4.2-beta, )", - "resolved": "3.4.2-beta", - "contentHash": "CWVMTF7ebylzzXbQXVp5C9UpBB/L+EpX2OxSdb2wlzcsdEmrev/Ith8wVs0WjZ6DbA0WiiybnYAWqB5v0nOO/A==", - "dependencies": { - "SharpGen.Runtime": "2.1.2-beta", - "Vortice.DXGI": "3.4.2-beta" - } - }, - "FlatSharp.Compiler": { - "type": "Transitive", - "resolved": "7.9.0", - "contentHash": "MU6808xvdbWJ3Ev+5PKalqQuzvVbn1DzzQH8txRDHGFUNDvHjd+ejqpvnYc9BSJ8Qp8VjkkpJD8OzRYilbPp3A==" - }, - "FlatSharp.Runtime": { - "type": "Transitive", - "resolved": "7.9.0", - "contentHash": "Bm8+WqzEsWNpxqrD5x4x+zQ8dyINlToCreM5FI2oNSfUVc9U9ZB+qztX/jd8rlJb3r0vBSlPwVLpw0xBtPa3Vw==" - }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2024.3.0", - "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug==" - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "9.0.2", - "contentHash": "ZffbJrskOZ40JTzcTyKwFHS5eACSWp2bUQBBApIgGV+es8RaTD4OxUG7XxFr3RIPLXtYQ1jQzF2DjKB5fZn7Qg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "9.0.2", - "contentHash": "MNe7GSTBf3jQx5vYrXF0NZvn6l7hUKF6J54ENfAgCO8y6xjN1XUmKKWG464LP2ye6QqDiA1dkaWEZBYnhoZzjg==" - }, - "SharpGen.Runtime": { - "type": "Transitive", - "resolved": "2.1.2-beta", - "contentHash": "nqZAjfEG1jX1ivvdZLsi6Pkt0DiOJyuOgRgldNFsmjXFPhxUbXQibofLSwuDZidL2kkmtTF8qLoRIeqeVdXgYw==" - }, - "SharpGen.Runtime.COM": { - "type": "Transitive", - "resolved": "2.1.2-beta", - "contentHash": "HBCrb6HfnUWx9v5/GjJeBr5DuodZLnHlFQQYXPrQs1Hbe1c6Wd0uCXf+SJp4hW8fQNxjXEu0FgiyHGlA/SRzRw==", - "dependencies": { - "SharpGen.Runtime": "2.1.2-beta" - } - }, - "Vortice.DirectX": { - "type": "Transitive", - "resolved": "3.4.2-beta", - "contentHash": "EwDbemXkmEiDGZVDem25uiEcZBYOMb+wzePuta+M/k2LXrQVGPknZhZUK56+QlHhI+Ducf/d+J75wgBzEjKi2g==", - "dependencies": { - "SharpGen.Runtime": "2.1.2-beta", - "SharpGen.Runtime.COM": "2.1.2-beta", - "Vortice.Mathematics": "1.7.6" - } - }, - "Vortice.DXGI": { - "type": "Transitive", - "resolved": "3.4.2-beta", - "contentHash": "T4S3pp6l/SGJ6SH3ebCbodN/bimGOkIBiIYKeBpVEis7+/ac1XIjyzgSTJ5XsH3o3hSH7DqSbP6Yo6mL9nyFQA==", - "dependencies": { - "SharpGen.Runtime": "2.1.2-beta", - "Vortice.DirectX": "3.4.2-beta" - } - }, - "Vortice.Mathematics": { - "type": "Transitive", - "resolved": "1.7.6", - "contentHash": "W8FNv850lPGxmHphwLyi1qnUlQHZBxh/62EenFJTaY6acPP29Fk0xMQJI60G+YNlsVJb3fSoriuW+ong5sM5UQ==" - }, - "glamourer.api": { - "type": "Project" - }, - "ottergui": { - "type": "Project", - "dependencies": { - "JetBrains.Annotations": "[2024.3.0, )", - "Microsoft.Extensions.DependencyInjection": "[9.0.2, )" - } - }, - "penumbra.api": { - "type": "Project" - }, - "penumbra.gamedata": { - "type": "Project", - "dependencies": { - "FlatSharp.Compiler": "[7.9.0, )", - "FlatSharp.Runtime": "[7.9.0, )", - "OtterGui": "[1.0.0, )", - "Penumbra.Api": "[5.13.0, )", - "Penumbra.String": "[1.0.7, )" - } - }, - "penumbra.string": { - "type": "Project" - } - } - } -} \ No newline at end of file diff --git a/OtterGui b/OtterGui index ff1e654..fd38721 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit ff1e6543845e3b8c53a5f8b240bc38faffb1b3bf +Subproject commit fd387218d2d2d237075cb35be6ca89eeb53e14e5 diff --git a/Penumbra.Api b/Penumbra.Api index 1750c41..b4e716f 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit 1750c41b53e1000c99a7fb9d8a0f082aef639a41 +Subproject commit b4e716f86d94cd4d98d8f58e580ed5f619ea87ae diff --git a/Penumbra.GameData b/Penumbra.GameData index 0e973ed..5bac66e 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 0e973ed6eace6afd31cd298f8c58f76fa8d5ef60 +Subproject commit 5bac66e5ad73e57919aff7f8b046606b75e191a2 diff --git a/Penumbra.String b/Penumbra.String index 9bd016f..0647fbc 160000 --- a/Penumbra.String +++ b/Penumbra.String @@ -1 +1 @@ -Subproject commit 9bd016fbef5fb2de467dd42165267fdd93cd9592 +Subproject commit 0647fbc5017ef9ced3f3ce1c2496eefd57c5b7a8 diff --git a/repo.json b/repo.json index 73f3e21..dab02ea 100644 --- a/repo.json +++ b/repo.json @@ -17,19 +17,19 @@ "Character" ], "InternalName": "Glamourer", - "AssemblyVersion": "1.5.1.6", - "TestingAssemblyVersion": "1.5.1.6", + "AssemblyVersion": "1.3.5.1", + "TestingAssemblyVersion": "1.3.5.2", "RepoUrl": "https://github.com/Ottermandias/Glamourer", "ApplicableVersion": "any", - "DalamudApiLevel": 14, - "TestingDalamudApiLevel": 14, + "DalamudApiLevel": 11, + "TestingDalamudApiLevel": 11, "IsHide": "False", "IsTestingExclusive": "False", "DownloadCount": 1, "LastUpdate": 1618608322, - "DownloadLinkInstall": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.6/Glamourer.zip", - "DownloadLinkUpdate": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.6/Glamourer.zip", - "DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.6/Glamourer.zip", + "DownloadLinkInstall": "https://github.com/Ottermandias/Glamourer/releases/download/1.3.5.1/Glamourer.zip", + "DownloadLinkUpdate": "https://github.com/Ottermandias/Glamourer/releases/download/1.3.5.1/Glamourer.zip", + "DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/testing_1.3.5.2/Glamourer.zip", "IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/main/images/icon.png" } ]