diff --git a/Glamourer.sln b/Glamourer.sln index 2eb4741..a791f23 100644 --- a/Glamourer.sln +++ b/Glamourer.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.3.11415.281 d18.3 +VisualStudioVersion = 18.3.11415.281 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{383AEE76-D423-431C-893A-7AB3DEA13630}" ProjectSection(SolutionItems) = preProject @@ -20,8 +20,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumb EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.String", "Penumbra.String\Penumbra.String.csproj", "{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OtterGui", "OtterGui\OtterGui.csproj", "{EF233CE2-F243-449E-BE05-72B9D110E419}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Glamourer.Api", "Glamourer.Api\Glamourer.Api.csproj", "{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luna", "Luna\Luna\Luna.csproj", "{DEA936D7-1386-55A1-7451-E0C240F56E9D}" @@ -68,14 +66,6 @@ Global {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|Any CPU.Build.0 = Release|x64 {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|x64.ActiveCfg = Release|x64 {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|x64.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}.Debug|x64.ActiveCfg = Debug|x64 - {EF233CE2-F243-449E-BE05-72B9D110E419}.Debug|x64.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 - {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|x64.ActiveCfg = Release|x64 - {EF233CE2-F243-449E-BE05-72B9D110E419}.Release|x64.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}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/Glamourer/Api/ApiHelpers.cs b/Glamourer/Api/ApiHelpers.cs index f8ac724..5adf76e 100644 --- a/Glamourer/Api/ApiHelpers.cs +++ b/Glamourer/Api/ApiHelpers.cs @@ -2,7 +2,6 @@ using Glamourer.Designs; using Glamourer.State; using Luna; -using OtterGui.Extensions; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; @@ -73,26 +72,26 @@ public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, A [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal static void Lock(ActorState state, uint key, ApplyFlag flags) { - if ((flags & ApplyFlag.Lock) != 0 && key != 0) + if ((flags & ApplyFlag.Lock) is not 0 && key is not 0) state.Lock(key); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] internal IEnumerable FindStates(string objectName) { - if (objectName.Length == 0 || !ByteString.FromString(objectName, out var byteString)) + if (objectName.Length is 0 || !ByteString.FromString(objectName, out var byteString)) return []; return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString) - .Concat(ArrayExtensions.SelectWhere(objects - .Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString), kvp => - { - if (stateManager.ContainsKey(kvp.Key)) - return (false, null); + .Concat(objects + .Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString).SelectWhere(kvp => + { + if (stateManager.ContainsKey(kvp.Key)) + return (false, null); - var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state); - return (ret, state); - })); + var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state); + return (ret, state); + })); } diff --git a/Glamourer/Api/DesignsApi.cs b/Glamourer/Api/DesignsApi.cs index d2da5b1..c3ec325 100644 --- a/Glamourer/Api/DesignsApi.cs +++ b/Glamourer/Api/DesignsApi.cs @@ -16,14 +16,14 @@ public class DesignsApi( : IGlamourerApiDesigns, IApiService { public Dictionary GetDesignList() - => designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text); + => designs.Designs.ToDictionary(d => d.Identifier, d => d.Name); public Dictionary GetDesignListExtended() => designs.Designs.ToDictionary(d => d.Identifier, d => (d.DisplayName, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign)); public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId) => designs.Designs.ByIdentifier(designId) is { } d - ? (d.Name.Text, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign) + ? (d.Name, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign) : (string.Empty, string.Empty, 0, false); public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags) diff --git a/Glamourer/Api/StateApi.cs b/Glamourer/Api/StateApi.cs index f400aa8..1986925 100644 --- a/Glamourer/Api/StateApi.cs +++ b/Glamourer/Api/StateApi.cs @@ -2,7 +2,6 @@ using Glamourer.Api.Enums; using Glamourer.Automation; using Glamourer.Designs; -using Glamourer.Designs.History; using Glamourer.Events; using Glamourer.State; using Luna; @@ -143,10 +142,10 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable public GlamourerApiEc ReapplyStateName(string playerName, uint key, ApplyFlag flags) { - var args = ApiHelpers.Args("Name", playerName, "Key", key, "Flags", flags); + var args = ApiHelpers.Args("Name", playerName, "Key", key, "Flags", flags); var states = _helpers.FindExistingStates(playerName); - var any = false; + var any = false; var anyReapplied = false; foreach (var state in states) { @@ -154,7 +153,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable if (!state.CanUnlock(key)) continue; - anyReapplied = true; + anyReapplied = true; anyReapplied |= Reapply(state, key, flags) is GlamourerApiEc.Success; } @@ -227,13 +226,14 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable public GlamourerApiEc CanUnlock(int objectIndex, uint key, out bool isLocked, out bool canUnlock) { var args = ApiHelpers.Args("Index", objectIndex, "Key", key); - isLocked = false; + 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; + return ApiHelpers.Return(GlamourerApiEc.Success, args); + + isLocked = state.IsLocked; canUnlock = state.CanUnlock(key); return ApiHelpers.Return(GlamourerApiEc.Success, args); } @@ -423,29 +423,31 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable }; } - private void OnAutoRedrawChange(bool autoReload) + private void OnAutoRedrawChange(in bool autoReload) => AutoReloadGearChanged?.Invoke(autoReload); - private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5) + private void OnStateChanged(in StateChanged.Arguments arguments) { - Glamourer.Log.Excessive($"[OnStateChanged] State Changed with Type {type} [Affecting {actors.ToLazyString("nothing")}.]"); - if (StateChanged != null) - foreach (var actor in actors.Objects) + Glamourer.Log.Excessive( + $"[OnStateChanged] State Changed with Type {arguments.Type} [Affecting {arguments.Actors.ToLazyString("nothing")}.]"); + if (StateChanged is not null) + foreach (var actor in arguments.Actors.Objects) StateChanged.Invoke(actor.Address); - if (StateChangedWithType != null) - foreach (var actor in actors.Objects) - StateChangedWithType.Invoke(actor.Address, type); + if (StateChangedWithType is not null) + foreach (var actor in arguments.Actors.Objects) + StateChangedWithType.Invoke(actor.Address, arguments.Type); } - private void OnStateFinalized(StateFinalizationType type, ActorData actors) + private void OnStateFinalized(in StateFinalized.Arguments arguments) { - 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); + Glamourer.Log.Verbose( + $"[OnStateUpdated] State Updated with Type {arguments.Type}. [Affecting {arguments.Actors.ToLazyString("nothing")}.]"); + if (StateFinalized is not null) + foreach (var actor in arguments.Actors.Objects) + StateFinalized.Invoke(actor.Address, arguments.Type); } - private void OnGPoseChange(bool gPose) + private void OnGPoseChange(in bool gPose) => GPoseChanged?.Invoke(gPose); } diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs index f88fd69..a41760e 100644 --- a/Glamourer/Automation/AutoDesignApplier.cs +++ b/Glamourer/Automation/AutoDesignApplier.cs @@ -7,6 +7,7 @@ using Glamourer.Events; using Glamourer.Interop; using Glamourer.Interop.Material; using Glamourer.State; +using Luna; using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; @@ -15,7 +16,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Automation; -public sealed class AutoDesignApplier : IDisposable +public sealed class AutoDesignApplier : IDisposable, IRequiredService { private readonly Configuration _config; private readonly AutoDesignManager _manager; @@ -72,38 +73,38 @@ public sealed class AutoDesignApplier : IDisposable _jobs.JobChanged -= OnJobChange; } - private void OnWeaponLoading(Actor actor, EquipSlot slot, ref CharacterWeapon weapon) + private void OnWeaponLoading(in WeaponLoading.Arguments arguments) { if (!_jobChangeState.HasState || !_config.EnableAutoDesigns) return; - var id = actor.GetIdentifier(_actors); + var id = arguments.Actor.GetIdentifier(_actors); if (id == _jobChangeState.Identifier) { var state = _jobChangeState.State!; - var current = state.BaseData.Item(slot); - switch (slot) + var current = state.BaseData.Item(arguments.Slot); + switch (arguments.Slot) { case EquipSlot.MainHand: { - if (_jobChangeState.TryGetValue(current.Type, actor.Job, false, out var data)) + if (_jobChangeState.TryGetValue(current.Type, arguments.Actor.Job, false, out var data)) { Glamourer.Log.Verbose( - $"Changing Mainhand from {state.ModelData.Weapon(EquipSlot.MainHand)} | {state.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{actor.Address:X}."); + $"Changing Mainhand from {state.ModelData.Weapon(EquipSlot.MainHand)} | {state.BaseData.Weapon(EquipSlot.MainHand)} to {data.Item1} for 0x{arguments.Actor.Address:X}."); _state.ChangeItem(state, EquipSlot.MainHand, data.Item1, new ApplySettings(Source: data.Item2)); - weapon = state.ModelData.Weapon(EquipSlot.MainHand); + arguments.Weapon = state.ModelData.Weapon(EquipSlot.MainHand); } break; } case EquipSlot.OffHand when current.Type == state.BaseData.MainhandType.Offhand(): { - if (_jobChangeState.TryGetValue(current.Type, actor.Job, false, out var data)) + if (_jobChangeState.TryGetValue(current.Type, arguments.Actor.Job, false, out var data)) { Glamourer.Log.Verbose( - $"Changing Offhand from {state.ModelData.Weapon(EquipSlot.OffHand)} | {state.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{actor.Address:X}."); + $"Changing Offhand from {state.ModelData.Weapon(EquipSlot.OffHand)} | {state.BaseData.Weapon(EquipSlot.OffHand)} to {data.Item1} for 0x{arguments.Actor.Address:X}."); _state.ChangeItem(state, EquipSlot.OffHand, data.Item1, new ApplySettings(Source: data.Item2)); - weapon = state.ModelData.Weapon(EquipSlot.OffHand); + arguments.Weapon = state.ModelData.Weapon(EquipSlot.OffHand); } _jobChangeState.Reset(); @@ -117,22 +118,22 @@ public sealed class AutoDesignApplier : IDisposable } } - private void OnAutomationChange(AutomationChanged.Type type, AutoDesignSet? set, object? bonusData) + private void OnAutomationChange(in AutomationChanged.Arguments arguments) { - if (!_config.EnableAutoDesigns || set == null) + if (!_config.EnableAutoDesigns) return; - switch (type) + switch (arguments.Type) { - case AutomationChanged.Type.ToggleSet when !set.Enabled: - case AutomationChanged.Type.DeletedDesign when set.Enabled: - // The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks. - RemoveOld(set.Identifiers); + // The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks. + case AutomationChanged.Type.ToggleSet when arguments.Set.Enabled: + case AutomationChanged.Type.DeletedDesign when arguments.Set.Enabled: + RemoveOld(arguments.Set.Identifiers); break; - case AutomationChanged.Type.ChangeIdentifier when set.Enabled: + case AutomationChanged.Type.ChangeIdentifier + when arguments.As().Set is { Enabled: true } set: // Remove fixed state from the old identifiers assigned and the old enabled set, if any. - var (oldIds, _, _) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!; - RemoveOld(oldIds); + RemoveOld(arguments.As().OldIdentifiers); ApplyNew(set); // Does not need to disable oldSet because same identifiers. break; case AutomationChanged.Type.ToggleSet: // Does not need to disable old states because same identifiers. @@ -143,7 +144,7 @@ public sealed class AutoDesignApplier : IDisposable case AutomationChanged.Type.ChangedConditions: case AutomationChanged.Type.ChangedType: case AutomationChanged.Type.ChangedData: - ApplyNew(set); + ApplyNew(arguments.Set); break; } @@ -303,7 +304,7 @@ public sealed class AutoDesignApplier : IDisposable mergedDesign.ResetTemporarySettings = true; } - _state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange, false, false, false)); + _state.ApplyDesign(state, mergedDesign, new ApplySettings(0, StateSource.Fixed, respectManual, fromJobChange)); forcedRedraw = mergedDesign.ForcedRedraw; } @@ -345,7 +346,7 @@ public sealed class AutoDesignApplier : IDisposable internal static int NewGearsetId = -1; - private void OnEquippedGearset(string name, int id, int prior, byte _, byte job) + private void OnEquippedGearset(in EquippedGearset.Arguments arguments) { if (!_config.EnableAutoDesigns) return; @@ -357,9 +358,9 @@ public sealed class AutoDesignApplier : IDisposable if (!GetPlayerSet(player, out var set) || !_state.TryGetValue(player, out var state)) return; - var respectManual = prior == id; - NewGearsetId = id; - Reduce(data.Objects[0], state, set, respectManual, job != state.LastJob, prior == id, out var forcedRedraw); + var respectManual = arguments.PriorId == arguments.Id; + NewGearsetId = arguments.Id; + Reduce(data.Objects[0], state, set, respectManual, arguments.JobId != state.LastJob, arguments.PriorId == arguments.Id, out var forcedRedraw); NewGearsetId = -1; foreach (var actor in data.Objects) _state.ReapplyState(actor, forcedRedraw, StateSource.Fixed); diff --git a/Glamourer/Automation/AutoDesignManager.cs b/Glamourer/Automation/AutoDesignManager.cs index eb76bd0..53a0a92 100644 --- a/Glamourer/Automation/AutoDesignManager.cs +++ b/Glamourer/Automation/AutoDesignManager.cs @@ -1,14 +1,12 @@ using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Interface.ImGuiNotification; using Glamourer.Designs; -using Glamourer.Designs.History; using Glamourer.Designs.Special; using Glamourer.Events; using Glamourer.Interop; using Glamourer.Services; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OtterGui.Extensions; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -16,7 +14,7 @@ using Luna; namespace Glamourer.Automation; -public class AutoDesignManager : ISavable, IReadOnlyList, IDisposable +public sealed class AutoDesignManager : ISavable, IReadOnlyList, IDisposable, IService { public const int CurrentVersion = 1; @@ -77,7 +75,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos _data.Add(newSet); Save(); Glamourer.Log.Debug($"Created new design set for {newSet.Identifiers[0].Incognito(null)}."); - _event.Invoke(AutomationChanged.Type.AddedSet, newSet, (_data.Count - 1, name)); + _event.Invoke(new AutomationChanged.AddedSetArguments(newSet, _data.Count - 1, name)); } public void DuplicateDesignSet(AutoDesignSet set) @@ -102,7 +100,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug( $"Duplicated new design set for {newSet.Identifiers[0].Incognito(null)} with {newSet.Designs.Count} auto designs from existing set."); - _event.Invoke(AutomationChanged.Type.AddedSet, newSet, (_data.Count - 1, name)); + _event.Invoke(new AutomationChanged.AddedSetArguments(newSet, _data.Count - 1, name)); } public void DeleteDesignSet(int whichSet) @@ -121,12 +119,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos _data.RemoveAt(whichSet); Save(); Glamourer.Log.Debug($"Deleted design set {whichSet + 1}."); - _event.Invoke(AutomationChanged.Type.DeletedSet, set, whichSet); + _event.Invoke(new AutomationChanged.DeletedSetArguments(set, whichSet)); } public void Rename(int whichSet, string newName) { - if (whichSet >= _data.Count || whichSet < 0 || newName.Length == 0) + if (whichSet >= _data.Count || whichSet < 0 || newName.Length is 0) return; var set = _data[whichSet]; @@ -137,7 +135,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos set.Name = newName; Save(); Glamourer.Log.Debug($"Renamed design set {whichSet + 1} from {old} to {newName}."); - _event.Invoke(AutomationChanged.Type.RenamedSet, set, (old, newName)); + _event.Invoke(new AutomationChanged.RenamedSetArguments(set, old, newName)); } @@ -148,7 +146,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug($"Moved design set {whichSet + 1} to position {toWhichSet + 1}."); - _event.Invoke(AutomationChanged.Type.MovedSet, _data[toWhichSet], (whichSet, toWhichSet)); + _event.Invoke(new AutomationChanged.MovedSetArguments(_data[toWhichSet], whichSet, toWhichSet)); } public void ChangeIdentifier(int whichSet, ActorIdentifier to) @@ -180,7 +178,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug($"Changed Identifier of design set {whichSet + 1} from {old[0].Incognito(null)} to {to.Incognito(null)}."); - _event.Invoke(AutomationChanged.Type.ChangeIdentifier, set, (old, to, oldEnabled)); + _event.Invoke(new AutomationChanged.ChangeIdentifierArguments(set, old, to, oldEnabled)); } public void SetState(int whichSet, bool value) @@ -214,7 +212,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug($"Changed enabled state of design set {whichSet + 1} to {value}."); - _event.Invoke(AutomationChanged.Type.ToggleSet, set, oldEnabled); + _event.Invoke(new AutomationChanged.ToggleSetArguments(set, oldEnabled)); } public void ChangeBaseState(int whichSet, AutoDesignSet.Base newBase) @@ -230,7 +228,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos set.BaseState = newBase; Save(); Glamourer.Log.Debug($"Changed base state of set {whichSet + 1} from {old} to {newBase}."); - _event.Invoke(AutomationChanged.Type.ChangedBase, set, (old, newBase)); + _event.Invoke(new AutomationChanged.ChangedBaseArguments(set, old, newBase)); } public void ChangeResetSettings(int whichSet, bool newValue) @@ -246,12 +244,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos 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); + _event.Invoke(new AutomationChanged.ChangedTemporarySettingsResetArguments(set, newValue)); } public void AddDesign(AutoDesignSet set, IDesignStandIn design) { - var newDesign = new AutoDesign() + var newDesign = new AutoDesign { Design = design, Type = ApplicationType.All, @@ -261,7 +259,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug( $"Added new associated design {design.ResolveName(true)} as design {set.Designs.Count} to design set."); - _event.Invoke(AutomationChanged.Type.AddedDesign, set, set.Designs.Count - 1); + _event.Invoke(new AutomationChanged.AddedDesignArguments(set, set.Designs.Count - 1)); } /// Only used to move between sets. @@ -275,8 +273,8 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos from.Designs.RemoveAt(idx); Save(); Glamourer.Log.Debug($"Moved design {idx} from design set {from.Name} to design set {to.Name}."); - _event.Invoke(AutomationChanged.Type.AddedDesign, to, to.Designs.Count - 1); - _event.Invoke(AutomationChanged.Type.DeletedDesign, from, idx); + _event.Invoke(new AutomationChanged.AddedDesignArguments(to, to.Designs.Count - 1)); + _event.Invoke(new AutomationChanged.DeletedDesignArguments(from, idx)); } public void DeleteDesign(AutoDesignSet set, int which) @@ -287,7 +285,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos set.Designs.RemoveAt(which); Save(); Glamourer.Log.Debug($"Removed associated design {which + 1} from design set."); - _event.Invoke(AutomationChanged.Type.DeletedDesign, set, which); + _event.Invoke(new AutomationChanged.DeletedDesignArguments(set, which)); } public void MoveDesign(AutoDesignSet set, int from, int to) @@ -297,7 +295,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug($"Moved design {from + 1} to {to + 1} in design set."); - _event.Invoke(AutomationChanged.Type.MovedDesign, set, (from, to)); + _event.Invoke(new AutomationChanged.MovedDesignArguments(set, from, to)); } public void ChangeDesign(AutoDesignSet set, int which, IDesignStandIn newDesign) @@ -314,7 +312,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug( $"Changed linked design from {old.ResolveName(true)} to {newDesign.ResolveName(true)} for associated design {which + 1} in design set."); - _event.Invoke(AutomationChanged.Type.ChangedDesign, set, (which, old, newDesign)); + _event.Invoke(new AutomationChanged.ChangedDesignArguments(set, which, old, newDesign)); } public void ChangeJobCondition(AutoDesignSet set, int which, JobGroup jobs) @@ -331,7 +329,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos design.Jobs = jobs; Save(); Glamourer.Log.Debug($"Changed job condition from {old.Id} to {jobs.Id} for associated design {which + 1} in design set."); - _event.Invoke(AutomationChanged.Type.ChangedConditions, set, (which, old, jobs)); + _event.Invoke(new AutomationChanged.ChangedConditionsArguments(set, which, old, jobs)); } public void ChangeGearsetCondition(AutoDesignSet set, int which, short index) @@ -347,7 +345,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos design.GearsetIndex = index; Save(); Glamourer.Log.Debug($"Changed gearset condition from {old} to {index} for associated design {which + 1} in design set."); - _event.Invoke(AutomationChanged.Type.ChangedConditions, set, (which, old, index)); + _event.Invoke(new AutomationChanged.ChangedConditionsArguments(set, which, default, default)); } public void ChangeApplicationType(AutoDesignSet set, int which, ApplicationType applicationType) @@ -364,7 +362,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos design.Type = applicationType; Save(); Glamourer.Log.Debug($"Changed application type from {old} to {applicationType} for associated design {which + 1} in design set."); - _event.Invoke(AutomationChanged.Type.ChangedType, set, (which, old, applicationType)); + _event.Invoke(new AutomationChanged.ChangedTypeArguments(set, which, old, applicationType)); } public void ChangeData(AutoDesignSet set, int which, object data) @@ -378,7 +376,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Save(); Glamourer.Log.Debug($"Changed additional design data for associated design {which + 1} in design set."); - _event.Invoke(AutomationChanged.Type.ChangedData, set, (which, data)); + _event.Invoke(new AutomationChanged.ChangedDataArguments(set, which, data)); } public string ToFilePath(FilenameService fileNames) @@ -397,7 +395,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos foreach (var set in _data) array.Add(set.Serialize()); - return new JObject() + return new JObject { ["Version"] = CurrentVersion, ["Data"] = array, @@ -424,9 +422,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos Glamourer.Messager.NotificationMessage("Failure to load automated designs: No valid version available.", NotificationType.Error); break; - case 1: - LoadV1(obj["Data"]); - break; + case 1: LoadV1(obj["Data"]); break; } } catch (Exception ex) @@ -638,17 +634,17 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos } } - private void OnDesignChange(DesignChanged.Type type, Design design, ITransaction? _) + private void OnDesignChange(in DesignChanged.Arguments arguments) { - if (type is not DesignChanged.Type.Deleted) + if (arguments.Type is not DesignChanged.Type.Deleted) return; - foreach (var (set, idx) in this.WithIndex()) + foreach (var (idx, set) in this.Index()) { var deleted = 0; for (var i = 0; i < set.Designs.Count; ++i) { - if (set.Designs[i].Design != design) + if (set.Designs[i].Design != arguments.Design) continue; DeleteDesign(set, i--); @@ -657,7 +653,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList, IDispos if (deleted > 0) Glamourer.Log.Information( - $"Removed {deleted} automated designs from automated design set {idx} due to deletion of {design.Incognito}."); + $"Removed {deleted} automated designs from automated design set {idx} due to deletion of {arguments.Design.Incognito}."); } } } diff --git a/Glamourer/Automation/FixedDesignMigrator.cs b/Glamourer/Automation/FixedDesignMigrator.cs index 3340457..7344c27 100644 --- a/Glamourer/Automation/FixedDesignMigrator.cs +++ b/Glamourer/Automation/FixedDesignMigrator.cs @@ -9,7 +9,7 @@ using Penumbra.String; namespace Glamourer.Automation; -public class FixedDesignMigrator(JobService jobs) +public sealed class FixedDesignMigrator(JobService jobs) : IRequiredService { private List<(string Name, List<(string, JobGroup, bool)> Data)>? _migratedData; diff --git a/Glamourer/Config/Configuration.cs b/Glamourer/Config/Configuration.cs index 31452bc..5f15c8b 100644 --- a/Glamourer/Config/Configuration.cs +++ b/Glamourer/Config/Configuration.cs @@ -13,7 +13,7 @@ using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Glamourer.Config; -public sealed partial class Configuration : IPluginConfiguration, ISavable +public sealed partial class Configuration : IPluginConfiguration, ISavable, IService { public const int CurrentVersion = 9; diff --git a/Glamourer/Config/EphemeralConfig.cs b/Glamourer/Config/EphemeralConfig.cs index 96699b5..6218e83 100644 --- a/Glamourer/Config/EphemeralConfig.cs +++ b/Glamourer/Config/EphemeralConfig.cs @@ -8,7 +8,7 @@ using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Glamourer.Config; -public partial class EphemeralConfig : ISavable +public partial class EphemeralConfig : ISavable, IService { public int Version { get; set; } = Configuration.CurrentVersion; @@ -16,18 +16,13 @@ public partial class EphemeralConfig : ISavable private bool _incognitoMode; public bool UnlockDetailMode { get; set; } = true; - public bool ShowDesignQuickBar { get; set; } = false; - public bool LockDesignQuickBar { get; set; } = false; - public bool LockMainWindow { get; set; } = false; + public bool ShowDesignQuickBar { get; set; } + public bool LockDesignQuickBar { get; set; } + public bool LockMainWindow { get; set; } public MainTabType SelectedMainTab { get; set; } = MainTabType.Settings; - public Guid SelectedDesign { get; set; } = Guid.Empty; 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/Designs/Design.cs b/Glamourer/Designs/Design.cs index 8804c8e..9b123d0 100644 --- a/Glamourer/Designs/Design.cs +++ b/Glamourer/Designs/Design.cs @@ -7,7 +7,6 @@ using Glamourer.Services; using Glamourer.State; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OtterGui.Classes; using Penumbra.GameData.Structs; using Luna; using Notification = Luna.Notification; @@ -47,7 +46,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa public IFileSystemData? Node { get; set; } public DateTimeOffset CreationDate { get; internal init; } public DateTimeOffset LastEdit { get; internal set; } - public LowerString Name { get; internal set; } = LowerString.Empty; + public string Name { get; internal set; } = string.Empty; public string Description { get; internal set; } = string.Empty; public string[] Tags { get; internal set; } = []; public int Index { get; internal set; } @@ -71,7 +70,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa #region IDesignStandIn public string ResolveName(bool incognito) - => incognito ? Incognito : Name.Text; + => incognito ? Incognito : Name; public string SerializeName() => Identifier.ToString(); @@ -109,7 +108,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa ["Identifier"] = Identifier, ["CreationDate"] = CreationDate, ["LastEdit"] = LastEdit, - ["Name"] = Name.Text, + ["Name"] = Name, ["Description"] = Description, ["ForcedRedraw"] = ForcedRedraw, ["ResetAdvancedDyes"] = ResetAdvancedDyes, @@ -200,7 +199,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa var hasNegativeGloss = false; var hasNonPositiveGloss = false; var specularLarger = 0; - foreach (var (key, value) in materialData.GetValues(MaterialValueIndex.Min(), MaterialValueIndex.Max())) + foreach (var (_, value) in materialData.GetValues(MaterialValueIndex.Min(), MaterialValueIndex.Max())) { hasNegativeGloss |= value.Value.GlossStrength < 0; hasNonPositiveGloss |= value.Value.GlossStrength <= 0; @@ -239,7 +238,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa Glamourer.Messager.AddMessage(new Notification( $"Swapped Gloss and Specular Strength in {materialDesignData.Values.Count} Rows in design {design.Incognito} {reason}", NotificationType.Info)); - saveService.Save(Luna.SaveType.ImmediateSync, design); + saveService.Save(SaveType.ImmediateSync, design); } } @@ -251,7 +250,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa { CreationDate = creationDate, Identifier = json["Identifier"]?.ToObject() ?? throw new ArgumentNullException("Identifier"), - Name = new LowerString(json["Name"]?.ToObject() ?? throw new ArgumentNullException("Name")), + Name = json["Name"]?.ToObject() ?? throw new ArgumentNullException("Name"), Description = json["Description"]?.ToObject() ?? string.Empty, Tags = ParseTags(json), LastEdit = json["LastEdit"]?.ToObject() ?? creationDate, @@ -359,5 +358,5 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa => Identifier.ToString(); public string DisplayName - => Name.Text; + => Name; } diff --git a/Glamourer/Designs/DesignBase64Migration.cs b/Glamourer/Designs/DesignBase64Migration.cs index 8cd137f..dda16b0 100644 --- a/Glamourer/Designs/DesignBase64Migration.cs +++ b/Glamourer/Designs/DesignBase64Migration.cs @@ -1,7 +1,5 @@ using Glamourer.Api.Enums; using Glamourer.Services; -using OtterGui; -using OtterGui.Extensions; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -107,7 +105,7 @@ public class DesignBase64Migration } data.Customize = *(CustomizeArray*)(ptr + 4); - foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex()) + foreach (var (idx, slot) in EquipSlotExtensions.EqdpSlots.Index()) { var mdl = eq[idx]; var item = items.Identify(slot, mdl.Set, mdl.Variant); @@ -121,7 +119,7 @@ public class DesignBase64Migration data.SetStain(slot, mdl.Stain); } - var main = cur[0].Skeleton.Id == 0 + var main = cur[0].Skeleton.Id is 0 ? items.DefaultSword : items.Identify(EquipSlot.MainHand, cur[0].Skeleton, cur[0].Weapon, cur[0].Variant); if (!main.Valid) diff --git a/Glamourer/Designs/DesignColors.cs b/Glamourer/Designs/DesignColors.cs index 1dec82c..44be523 100644 --- a/Glamourer/Designs/DesignColors.cs +++ b/Glamourer/Designs/DesignColors.cs @@ -1,5 +1,4 @@ using Dalamud.Interface.ImGuiNotification; -using Glamourer.Config; using Glamourer.Gui; using Glamourer.Services; using ImSharp; @@ -9,114 +8,7 @@ using Newtonsoft.Json.Linq; namespace Glamourer.Designs; -public class DesignColorUi(DesignColors colors, Configuration config) -{ - private string _newName = string.Empty; - - public void Draw() - { - using var table = Im.Table.Begin("designColors"u8, 3, TableFlags.RowBackground); - if (!table) - return; - - var changeString = string.Empty; - Rgba32? changeValue = null; - - table.SetupColumn("##Delete"u8, TableColumnFlags.WidthFixed, Im.Style.FrameHeight); - table.SetupColumn("##Select"u8, TableColumnFlags.WidthFixed, Im.Style.FrameHeight); - table.SetupColumn("Color Name"u8, TableColumnFlags.WidthStretch); - - table.HeaderRow(); - - table.NextColumn(); - if (ImEx.Icon.Button(LunaStyle.RefreshIcon, "Revert the color used for missing design colors to its default."u8, - colors.MissingColor == DesignColors.MissingColorDefault)) - { - changeString = DesignColors.MissingColorName; - changeValue = DesignColors.MissingColorDefault; - } - - table.NextColumn(); - if (DrawColorButton(DesignColors.MissingColorNameU8, colors.MissingColor, out var newColor)) - { - changeString = DesignColors.MissingColorName; - changeValue = newColor; - } - - table.NextColumn(); - Im.Cursor.X += Im.Style.FramePadding.X; - Im.Text(DesignColors.MissingColorNameU8); - Im.Tooltip.OnHover("This color is used when the color specified in a design is not available."u8); - - var disabled = !config.DeleteDesignModifier.IsActive(); - foreach (var (idx, (name, color)) in colors.Index()) - { - using var id = Im.Id.Push(idx); - table.NextColumn(); - - if (ImEx.Icon.Button(LunaStyle.DeleteIcon, "Delete this color. This does not remove it from designs using it."u8, disabled)) - { - changeString = name; - changeValue = null; - } - - if (disabled) - Im.Tooltip.OnHover($"\nHold {config.DeleteDesignModifier} to delete."); - - table.NextColumn(); - if (DrawColorButton(name, color, out newColor)) - { - changeString = name; - changeValue = newColor; - } - - table.NextColumn(); - Im.Cursor.X += Im.Style.FramePadding.X; - Im.Text(name); - } - - table.NextColumn(); - (var tt, disabled) = _newName.Length == 0 - ? ("Specify a name for a new color first.", true) - : _newName is DesignColors.MissingColorName or DesignColors.AutomaticName - ? ($"You can not use the name {DesignColors.MissingColorName} or {DesignColors.AutomaticName}, choose a different one.", true) - : colors.ContainsKey(_newName) - ? ($"The color {_newName} already exists, please choose a different name.", true) - : ($"Add a new color {_newName} to your list.", false); - if (ImEx.Icon.Button(LunaStyle.AddObjectIcon, tt, disabled)) - { - changeString = _newName; - changeValue = 0xFFFFFFFF; - } - - table.NextColumn(); - table.NextColumn(); - Im.Item.SetNextWidth(Im.ContentRegion.Available.X); - if (Im.Input.Text("##newDesignColor"u8, ref _newName, "New Color Name..."u8, InputTextFlags.EnterReturnsTrue)) - { - changeString = _newName; - changeValue = 0xFFFFFFFF; - } - - if (changeString.Length > 0) - { - if (!changeValue.HasValue) - colors.DeleteColor(changeString); - else - colors.SetColor(changeString, changeValue.Value); - } - } - - public static bool DrawColorButton(Utf8StringHandler tooltip, Rgba32 color, out Rgba32 newColor) - { - var ret = Im.Color.Editor(tooltip, ref color, ColorEditorFlags.AlphaPreviewHalf | ColorEditorFlags.NoInputs); - Im.Tooltip.OnHover(ref tooltip); - newColor = color; - return ret; - } -} - -public class DesignColors : ISavable, IReadOnlyDictionary +public sealed class DesignColors : ISavable, IReadOnlyDictionary, IService { public const string AutomaticName = "Automatic"; public static readonly StringU8 AutomaticNameU8 = new("Automatic"u8); diff --git a/Glamourer/Designs/DesignConverter.cs b/Glamourer/Designs/DesignConverter.cs index 058b023..46ee6d3 100644 --- a/Glamourer/Designs/DesignConverter.cs +++ b/Glamourer/Designs/DesignConverter.cs @@ -3,6 +3,7 @@ using Glamourer.Interop.Material; using Glamourer.Services; using Glamourer.State; using Glamourer.Utility; +using Luna; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Penumbra.GameData.DataContainers; @@ -12,13 +13,13 @@ using Penumbra.GameData.Structs; namespace Glamourer.Designs; -public class DesignConverter( +public sealed class DesignConverter( SaveService saveService, - ItemManager _items, - DesignManager _designs, - CustomizeService _customize, - HumanModelList _humans, - DesignLinkLoader _linkLoader) + ItemManager items, + DesignManager designs, + CustomizeService customizeService, + HumanModelList humans, + DesignLinkLoader linkLoader) : IService { public const byte Version = 6; @@ -54,9 +55,9 @@ public class DesignConverter( public DesignBase Convert(in DesignData data, in StateMaterialManager materials, in ApplicationRules rules) { - var design = _designs.CreateTemporary(); + var design = designs.CreateTemporary(); rules.Apply(design); - design.SetDesignData(_customize, data); + design.SetDesignData(customizeService, data); if (rules.Materials) ComputeMaterials(design.GetMaterialDataRef(), materials, rules.Equip); return design; @@ -70,8 +71,8 @@ public class DesignConverter( try { var ret = jObject["Identifier"] != null - ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObject) - : DesignBase.LoadDesignBase(_customize, _items, jObject); + ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObject) + : DesignBase.LoadDesignBase(customizeService, items, jObject); if (!customize) ret.Application.RemoveCustomize(); @@ -101,14 +102,14 @@ public class DesignConverter( case (byte)'{': var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes)); ret = jObj1["Identifier"] != null - ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj1) - : DesignBase.LoadDesignBase(_customize, _items, jObj1); + ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj1) + : DesignBase.LoadDesignBase(customizeService, items, jObj1); break; case 1: case 2: case 4: - ret = _designs.CreateTemporary(); - ret.MigrateBase64(_customize, _items, _humans, base64); + ret = designs.CreateTemporary(); + ret.MigrateBase64(customizeService, items, humans, base64); break; case 3: { @@ -116,8 +117,8 @@ public class DesignConverter( var jObj2 = JObject.Parse(decompressed); Debug.Assert(version == 3); ret = jObj2["Identifier"] != null - ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2) - : DesignBase.LoadDesignBase(_customize, _items, jObj2); + ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2) + : DesignBase.LoadDesignBase(customizeService, items, jObj2); break; } case 5: @@ -127,8 +128,8 @@ public class DesignConverter( var jObj2 = JObject.Parse(decompressed); Debug.Assert(version == 5); ret = jObj2["Identifier"] != null - ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2) - : DesignBase.LoadDesignBase(_customize, _items, jObj2); + ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2) + : DesignBase.LoadDesignBase(customizeService, items, jObj2); break; } case 6: @@ -137,8 +138,8 @@ public class DesignConverter( var jObj2 = JObject.Parse(decompressed); Debug.Assert(version == 6); ret = jObj2["Identifier"] != null - ? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2) - : DesignBase.LoadDesignBase(_customize, _items, jObj2); + ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2) + : DesignBase.LoadDesignBase(customizeService, items, jObj2); break; } @@ -177,7 +178,7 @@ public class DesignConverter( { var index = (int)slot.ToIndex(); var armor = armors[index]; - var item = _items.Identify(slot, armor.Set, armor.Variant); + var item = items.Identify(slot, armor.Set, armor.Variant); if (!item.Valid) { if (!skipWarnings) @@ -188,20 +189,20 @@ public class DesignConverter( yield return (slot, item, armor.Stains); } - var mh = _items.Identify(EquipSlot.MainHand, mainhand.Skeleton, mainhand.Weapon, mainhand.Variant); + var mh = items.Identify(EquipSlot.MainHand, mainhand.Skeleton, mainhand.Weapon, mainhand.Variant); if (!skipWarnings && !mh.Valid) { Glamourer.Log.Warning($"Appearance data {mainhand} for mainhand weapon invalid, item could not be identified."); - mh = _items.DefaultSword; + mh = items.DefaultSword; } yield return (EquipSlot.MainHand, mh, mainhand.Stains); - var oh = _items.Identify(EquipSlot.OffHand, offhand.Skeleton, offhand.Weapon, offhand.Variant, mh.Type); + var oh = items.Identify(EquipSlot.OffHand, offhand.Skeleton, offhand.Weapon, offhand.Variant, mh.Type); if (!skipWarnings && !oh.Valid) { Glamourer.Log.Warning($"Appearance data {offhand} for offhand weapon invalid, item could not be identified."); - oh = _items.GetDefaultOffhand(mh); + oh = items.GetDefaultOffhand(mh); if (!oh.Valid) oh = ItemManager.NothingItem(FullEquipType.Shield); } diff --git a/Glamourer/Designs/DesignEditor.cs b/Glamourer/Designs/DesignEditor.cs index 1bfc4d3..db5b48c 100644 --- a/Glamourer/Designs/DesignEditor.cs +++ b/Glamourer/Designs/DesignEditor.cs @@ -75,7 +75,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; Glamourer.Log.Debug($"Changed customize {idx.ToName()} in design {design.Identifier} from {oldValue.Value} to {value.Value}."); SaveService.QueueSave(design); - DesignChanged.Invoke(DesignChanged.Type.Customize, design, new CustomizeTransaction(idx, oldValue, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Customize, design, new CustomizeTransaction(idx, oldValue, value))); } /// @@ -91,7 +91,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; Glamourer.Log.Debug($"Changed entire customize with resulting flags {applied} and {changed}."); SaveService.QueueSave(design); - DesignChanged.Invoke(DesignChanged.Type.EntireCustomize, design, new EntireCustomizeTransaction(changed, oldCustomize, newCustomize)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.EntireCustomize, design, new EntireCustomizeTransaction(changed, oldCustomize, newCustomize))); } /// @@ -106,7 +106,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; Glamourer.Log.Debug($"Set customize parameter {flag} in design {design.Identifier} from {old} to {@new}."); SaveService.QueueSave(design); - DesignChanged.Invoke(DesignChanged.Type.Parameter, design, new ParameterTransaction(flag, old, @new)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Parameter, design, new ParameterTransaction(flag, old, @new))); } /// @@ -130,9 +130,9 @@ public class DesignEditor( SaveService.QueueSave(design); Glamourer.Log.Debug( $"Set {EquipSlot.MainHand.ToName()} weapon in design {design.Identifier} from {currentMain.Name} ({currentMain.ItemId}) to {item.Name} ({item.ItemId})."); - DesignChanged.Invoke(DesignChanged.Type.Weapon, design, + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Weapon, design, new WeaponTransaction(currentMain, currentOff, currentGauntlets, item, newOff ?? currentOff, - newGauntlets ?? currentGauntlets)); + newGauntlets ?? currentGauntlets))); return; } case EquipSlot.OffHand: @@ -150,8 +150,8 @@ public class DesignEditor( SaveService.QueueSave(design); Glamourer.Log.Debug( $"Set {EquipSlot.OffHand.ToName()} weapon in design {design.Identifier} from {currentOff.Name} ({currentOff.ItemId}) to {item.Name} ({item.ItemId})."); - DesignChanged.Invoke(DesignChanged.Type.Weapon, design, - new WeaponTransaction(currentMain, currentOff, currentGauntlets, currentMain, item, currentGauntlets)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Weapon, design, + new WeaponTransaction(currentMain, currentOff, currentGauntlets, currentMain, item, currentGauntlets))); return; } default: @@ -167,7 +167,7 @@ public class DesignEditor( Glamourer.Log.Debug( $"Set {slot.ToName()} equipment piece in design {design.Identifier} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId})."); SaveService.QueueSave(design); - DesignChanged.Invoke(DesignChanged.Type.Equip, design, new EquipTransaction(slot, old, item)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Equip, design, new EquipTransaction(slot, old, item))); return; } } @@ -187,7 +187,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set {slot} bonus item to {item}."); - DesignChanged.Invoke(DesignChanged.Type.BonusItem, design, new BonusItemTransaction(slot, oldItem, item)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.BonusItem, design, new BonusItemTransaction(slot, oldItem, item))); } /// @@ -204,7 +204,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stains}."); - DesignChanged.Invoke(DesignChanged.Type.Stains, design, new StainTransaction(slot, oldStain, stains)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Stains, design, new StainTransaction(slot, oldStain, stains))); } /// @@ -227,7 +227,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set crest visibility of {slot} equipment piece to {crest}."); - DesignChanged.Invoke(DesignChanged.Type.Crest, design, new CrestTransaction(slot, oldCrest, crest)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Crest, design, new CrestTransaction(slot, oldCrest, crest))); } /// @@ -240,7 +240,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set value of {metaIndex} to {value}."); - DesignChanged.Invoke(DesignChanged.Type.Other, design, new MetaTransaction(metaIndex, !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Other, design, new MetaTransaction(metaIndex, !value, value))); } public void ChangeMaterialRevert(Design design, MaterialValueIndex index, bool revert) @@ -253,7 +253,7 @@ public class DesignEditor( Glamourer.Log.Debug($"Changed advanced dye value for {index} to {(revert ? "Revert." : "no longer Revert.")}"); design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); - DesignChanged.Invoke(DesignChanged.Type.MaterialRevert, design, new MaterialRevertTransaction(index, !revert, revert)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.MaterialRevert, design, new MaterialRevertTransaction(index, !revert, revert))); } public void ChangeMaterialValue(Design design, MaterialValueIndex index, ColorRow? row) @@ -288,7 +288,7 @@ public class DesignEditor( design.LastEdit = DateTimeOffset.UtcNow; SaveService.DelaySave(design); - DesignChanged.Invoke(DesignChanged.Type.Material, design, new MaterialTransaction(index, oldValue.Value, row)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Material, design, new MaterialTransaction(index, oldValue.Value, row))); } public void ChangeApplyMaterialValue(Design design, MaterialValueIndex index, bool value) @@ -301,7 +301,7 @@ public class DesignEditor( Glamourer.Log.Debug($"Changed application of advanced dye for {index} to {value}."); design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); - DesignChanged.Invoke(DesignChanged.Type.ApplyMaterial, design, new ApplicationTransaction(index, !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyMaterial, design, new ApplicationTransaction(index, !value, value))); } diff --git a/Glamourer/Designs/DesignFileSystem.cs b/Glamourer/Designs/DesignFileSystem.cs index 4810174..c2dc048 100644 --- a/Glamourer/Designs/DesignFileSystem.cs +++ b/Glamourer/Designs/DesignFileSystem.cs @@ -1,5 +1,4 @@ using Dalamud.Interface.ImGuiNotification; -using Glamourer.Designs.History; using Glamourer.Events; using Glamourer.Services; using Luna; @@ -10,41 +9,50 @@ public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredSer { private readonly DesignFileSystemSaver _saver; private readonly DesignChanged _designChanged; + private readonly TabSelected _tabSelected; - public DesignFileSystem(Logger log, SaveService saveService, DesignStorage designs, DesignChanged designChanged) + public DesignFileSystem(Logger log, SaveService saveService, DesignStorage designs, DesignChanged designChanged, TabSelected tabSelected) : base("DesignFileSystem", log, true) { _designChanged = designChanged; + _tabSelected = tabSelected; _saver = new DesignFileSystemSaver(log, this, saveService, designs); _saver.Load(); _designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignFileSystem); + _tabSelected.Subscribe(OnTabSelected, TabSelected.Priority.DesignSelector); } - private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? _) + private void OnTabSelected(in TabSelected.Arguments arguments) { - switch (type) + if (arguments.Design?.Node is { } node) + Selection.Select(node); + } + + private void OnDesignChanged(in DesignChanged.Arguments arguments) + { + switch (arguments.Type) { case DesignChanged.Type.ReloadedAll: _saver.Load(); break; case DesignChanged.Type.Created: var parent = Root; - if (design.Path.Folder.Length > 0) + if (arguments.Design.Path.Folder.Length > 0) try { - parent = FindOrCreateAllFolders(design.Path.Folder); + parent = FindOrCreateAllFolders(arguments.Design.Path.Folder); } catch (Exception ex) { Glamourer.Messager.NotificationMessage(ex, - $"Could not move design to {design.Path} because the folder could not be created.", + $"Could not move design to {arguments.Design.Path} because the folder could not be created.", NotificationType.Error); } - var (data, _) = CreateDuplicateDataNode(parent, design.Path.SortName ?? design.Name, design); + var (data, _) = CreateDuplicateDataNode(parent, arguments.Design.Path.SortName ?? arguments.Design.Name, arguments.Design); Selection.Select(data); break; case DesignChanged.Type.Deleted: - if (design.Node is { } node) + if (arguments.Design.Node is { } node) { if (node.Selected) Selection.UnselectAll(); @@ -52,8 +60,8 @@ public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredSer } break; - case DesignChanged.Type.Renamed when design.Path.SortName is null: - RenameWithDuplicates(design.Node!, design.Path.GetIntendedName(design.Name.Text)); + case DesignChanged.Type.Renamed when arguments.Design.Path.SortName is null: + RenameWithDuplicates(arguments.Design.Node!, arguments.Design.Path.GetIntendedName(arguments.Design.Name)); break; // TODO: Maybe add path changes? } @@ -61,6 +69,7 @@ public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredSer public void Dispose() { + _tabSelected.Unsubscribe(OnTabSelected); _designChanged.Unsubscribe(OnDesignChanged); } } diff --git a/Glamourer/Designs/DesignManager.cs b/Glamourer/Designs/DesignManager.cs index ab0551b..5e12b57 100644 --- a/Glamourer/Designs/DesignManager.cs +++ b/Glamourer/Designs/DesignManager.cs @@ -7,7 +7,7 @@ using Glamourer.GameData; using Glamourer.Interop.Material; using Glamourer.Interop.Penumbra; using Glamourer.Services; -using OtterGui.Extensions; +using Luna; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Penumbra.GameData.DataContainers; @@ -16,7 +16,7 @@ using Penumbra.GameData.Enums; namespace Glamourer.Designs; -public sealed class DesignManager : DesignEditor +public sealed class DesignManager : DesignEditor, IService { public readonly DesignStorage Designs; private readonly HumanModelList _humans; @@ -89,7 +89,7 @@ public sealed class DesignManager : DesignEditor Glamourer.Log.Information( $"Loaded {Designs.Count} designs in {stopwatch.ElapsedMilliseconds} ms.{(skipped > 0 ? $" Skipped loading {skipped} designs due to errors." : string.Empty)}"); - DesignChanged.Invoke(DesignChanged.Type.ReloadedAll, null!, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ReloadedAll, null!)); } /// Create a new temporary design without adding it to the manager. @@ -116,7 +116,7 @@ public sealed class DesignManager : DesignEditor Designs.Add(design); Glamourer.Log.Debug($"Added new design {design.Identifier}."); SaveService.ImmediateSave(design); - DesignChanged.Invoke(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path))); return design; } @@ -141,7 +141,7 @@ public sealed class DesignManager : DesignEditor Designs.Add(design); Glamourer.Log.Debug($"Added new design {design.Identifier} by cloning Temporary Design."); SaveService.ImmediateSave(design); - DesignChanged.Invoke(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path))); return design; } @@ -162,7 +162,7 @@ public sealed class DesignManager : DesignEditor Glamourer.Log.Debug( $"Added new design {design.Identifier} by cloning {clone.Identifier.ToString()}."); SaveService.ImmediateSave(design); - DesignChanged.Invoke(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design, new CreationTransaction(actualName, path))); return design; } @@ -173,7 +173,7 @@ public sealed class DesignManager : DesignEditor --d.Index; Designs.RemoveAt(design.Index); SaveService.ImmediateDelete(design); - DesignChanged.Invoke(DesignChanged.Type.Deleted, design, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Deleted, design)); } #endregion @@ -183,7 +183,7 @@ public sealed class DesignManager : DesignEditor /// Rename a design. public void Rename(Design design, string newName) { - var oldName = design.Name.Text; + var oldName = design.Name; if (oldName == newName) return; @@ -191,7 +191,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Renamed design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.Renamed, design, new RenameTransaction(oldName, newName)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Renamed, design, new RenameTransaction(oldName, newName))); } /// Change the description of a design. @@ -205,7 +205,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Changed description of design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.ChangedDescription, design, new DescriptionTransaction(oldDescription, description)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedDescription, design, new DescriptionTransaction(oldDescription, description))); } /// Change the associated color of a design. @@ -219,7 +219,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Changed color of design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.ChangedColor, design, new DesignColorTransaction(oldColor, newColor)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedColor, design, new DesignColorTransaction(oldColor, newColor))); } /// Add a new tag to a design. The tags remain sorted. @@ -233,7 +233,7 @@ public sealed class DesignManager : DesignEditor var idx = design.Tags.AsEnumerable().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)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx))); } /// Remove a tag from a design by its index. @@ -247,7 +247,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Removed tag {oldTag} at {tagIdx} from design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.RemovedTag, design, new TagRemovedTransaction(oldTag, tagIdx)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.RemovedTag, design, new TagRemovedTransaction(oldTag, tagIdx))); } /// Rename a tag from a design by its index. The tags stay sorted. @@ -262,8 +262,8 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; 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))); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedTag, design, + new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag)))); } /// Add an associated mod to a design. @@ -275,7 +275,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Added associated mod {mod.DirectoryName} to design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings))); } /// Remove an associated mod from a design. @@ -287,7 +287,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Removed associated mod {mod.DirectoryName} from design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.RemovedMod, design, new ModRemovedTransaction(mod, settings)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.RemovedMod, design, new ModRemovedTransaction(mod, settings))); } /// Add or update an associated mod to a design. @@ -300,12 +300,12 @@ public sealed class DesignManager : DesignEditor if (hasOldSettings) { Glamourer.Log.Debug($"Updated associated mod {mod.DirectoryName} from design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.UpdatedMod, design, new ModUpdatedTransaction(mod, oldSettings, settings)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.UpdatedMod, design, new ModUpdatedTransaction(mod, oldSettings, settings))); } else { Glamourer.Log.Debug($"Added associated mod {mod.DirectoryName} from design {design.Identifier}."); - DesignChanged.Invoke(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.AddedMod, design, new ModAddedTransaction(mod, settings))); } } @@ -317,7 +317,7 @@ public sealed class DesignManager : DesignEditor SaveService.QueueSave(design); Glamourer.Log.Debug($"Set design {design.Identifier} to {(value ? "no longer be " : string.Empty)} write-protected."); - DesignChanged.Invoke(DesignChanged.Type.WriteProtection, design, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.WriteProtection, design)); } /// Set the quick design bar display status of a design. @@ -330,7 +330,7 @@ public sealed class DesignManager : DesignEditor SaveService.QueueSave(design); Glamourer.Log.Debug( $"Set design {design.Identifier} to {(!value ? "no longer be " : string.Empty)} displayed in the quick design bar."); - DesignChanged.Invoke(DesignChanged.Type.QuickDesignBar, design, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.QuickDesignBar, design)); } #endregion @@ -345,7 +345,7 @@ public sealed class DesignManager : DesignEditor design.ForcedRedraw = forcedRedraw; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set {design.Identifier} to {(forcedRedraw ? string.Empty : "not")} force redraws."); - DesignChanged.Invoke(DesignChanged.Type.ForceRedraw, design, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ForceRedraw, design)); } public void ChangeResetAdvancedDyes(Design design, bool resetAdvancedDyes) @@ -356,7 +356,7 @@ public sealed class DesignManager : DesignEditor design.ResetAdvancedDyes = resetAdvancedDyes; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set {design.Identifier} to {(resetAdvancedDyes ? string.Empty : "not")} reset advanced dyes."); - DesignChanged.Invoke(DesignChanged.Type.ResetAdvancedDyes, design, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ResetAdvancedDyes, design)); } public void ChangeResetTemporarySettings(Design design, bool resetTemporarySettings) @@ -367,7 +367,7 @@ public sealed class DesignManager : DesignEditor design.ResetTemporarySettings = resetTemporarySettings; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set {design.Identifier} to {(resetTemporarySettings ? string.Empty : "not")} reset temporary settings."); - DesignChanged.Invoke(DesignChanged.Type.ResetTemporarySettings, design, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ResetTemporarySettings, design)); } /// Change whether to apply a specific customize value. @@ -379,7 +379,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set applying of customization {idx.ToName()} to {value}."); - DesignChanged.Invoke(DesignChanged.Type.ApplyCustomize, design, new ApplicationTransaction(idx, !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyCustomize, design, new ApplicationTransaction(idx, !value, value))); } /// Change whether to apply a specific equipment piece. @@ -391,7 +391,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set applying of {slot} equipment piece to {value}."); - DesignChanged.Invoke(DesignChanged.Type.ApplyEquip, design, new ApplicationTransaction((slot, false), !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyEquip, design, new ApplicationTransaction((slot, false), !value, value))); } /// Change whether to apply a specific equipment piece. @@ -403,7 +403,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set applying of {slot} bonus item to {value}."); - DesignChanged.Invoke(DesignChanged.Type.ApplyBonusItem, design, new ApplicationTransaction(slot, !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyBonusItem, design, new ApplicationTransaction(slot, !value, value))); } /// Change whether to apply a specific stain. @@ -415,7 +415,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set applying of stain of {slot} equipment piece to {value}."); - DesignChanged.Invoke(DesignChanged.Type.ApplyStain, design, new ApplicationTransaction((slot, true), !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyStain, design, new ApplicationTransaction((slot, true), !value, value))); } /// Change whether to apply a specific crest visibility. @@ -427,7 +427,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set applying of crest visibility of {slot} equipment piece to {value}."); - DesignChanged.Invoke(DesignChanged.Type.ApplyCrest, design, new ApplicationTransaction(slot, !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyCrest, design, new ApplicationTransaction(slot, !value, value))); } /// Change the application value of one of the meta flags. @@ -439,7 +439,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set applying of {metaIndex} to {value}."); - DesignChanged.Invoke(DesignChanged.Type.Other, design, new ApplicationTransaction(metaIndex, !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Other, design, new ApplicationTransaction(metaIndex, !value, value))); } /// Change the application value of a customize parameter. @@ -451,7 +451,7 @@ public sealed class DesignManager : DesignEditor design.LastEdit = DateTimeOffset.UtcNow; SaveService.QueueSave(design); Glamourer.Log.Debug($"Set applying of parameter {flag} to {value}."); - DesignChanged.Invoke(DesignChanged.Type.ApplyParameter, design, new ApplicationTransaction(flag, !value, value)); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ApplyParameter, design, new ApplicationTransaction(flag, !value, value))); } /// Change multiple application values at once. @@ -512,7 +512,6 @@ public sealed class DesignManager : DesignEditor { var text = File.ReadAllText(SaveService.FileNames.MigrationDesignFile); var dict = JsonConvert.DeserializeObject>(text) ?? new Dictionary(); - var migratedFileSystemPaths = new Dictionary(dict.Count); foreach (var (name, base64) in dict) { try @@ -529,7 +528,6 @@ public sealed class DesignManager : DesignEditor if (!oldDesigns.Any(d => d.Name == design.Name && d.CreationDate == design.CreationDate)) { Add(design, $"Migrated old design to {design.Identifier}."); - migratedFileSystemPaths.Add(design.Identifier.ToString(), name); ++successes; } else @@ -632,7 +630,7 @@ public sealed class DesignManager : DesignEditor if (!message.IsNullOrEmpty()) Glamourer.Log.Debug(message); SaveService.ImmediateSave(design); - DesignChanged.Invoke(DesignChanged.Type.Created, design, null); + DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design)); } /// Split a given string into its folder path and its name, if is true. diff --git a/Glamourer/Designs/History/EditorHistory.cs b/Glamourer/Designs/History/EditorHistory.cs index 2676a84..4fc418b 100644 --- a/Glamourer/Designs/History/EditorHistory.cs +++ b/Glamourer/Designs/History/EditorHistory.cs @@ -1,8 +1,6 @@ -using Glamourer.Api.Enums; using Glamourer.Events; using Glamourer.State; using Luna; -using Penumbra.GameData.Interop; namespace Glamourer.Designs.History; @@ -171,21 +169,21 @@ public class EditorHistory : IDisposable, IService } - private void OnStateChanged(StateChangeType type, StateSource source, ActorState state, ActorData actors, ITransaction? data) + private void OnStateChanged(in StateChanged.Arguments arguments) { - if (_undoMode || source is not StateSource.Manual) + if (_undoMode || arguments.Source is not StateSource.Manual) return; - if (data is not null) - AddStateTransaction(state, data); + if (arguments.Transaction is not null) + AddStateTransaction(arguments.State, arguments.Transaction); } - private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? data) + private void OnDesignChanged(in DesignChanged.Arguments arguments) { if (_undoMode) return; - if (data is not null) - AddDesignTransaction(design, data); + if (arguments.Transaction is not null) + AddDesignTransaction(arguments.Design, arguments.Transaction); } } diff --git a/Glamourer/Designs/History/Transaction.cs b/Glamourer/Designs/History/Transaction.cs index 47b10bf..234c7bb 100644 --- a/Glamourer/Designs/History/Transaction.cs +++ b/Glamourer/Designs/History/Transaction.cs @@ -10,7 +10,7 @@ public interface ITransaction public void Revert(IDesignEditor editor, object data); } -public readonly record struct CustomizeTransaction(CustomizeIndex Slot, CustomizeValue Old, CustomizeValue New) +public record CustomizeTransaction(CustomizeIndex Slot, CustomizeValue Old, CustomizeValue New) : ITransaction { public ITransaction? Merge(ITransaction older) @@ -20,7 +20,7 @@ public readonly record struct CustomizeTransaction(CustomizeIndex Slot, Customiz => editor.ChangeCustomize(data, Slot, Old, ApplySettings.Manual); } -public readonly record struct EntireCustomizeTransaction(CustomizeFlag Apply, CustomizeArray Old, CustomizeArray New) +public record EntireCustomizeTransaction(CustomizeFlag Apply, CustomizeArray Old, CustomizeArray New) : ITransaction { public ITransaction? Merge(ITransaction older) @@ -30,7 +30,7 @@ public readonly record struct EntireCustomizeTransaction(CustomizeFlag Apply, Cu => editor.ChangeEntireCustomize(data, Old, Apply, ApplySettings.Manual); } -public readonly record struct EquipTransaction(EquipSlot Slot, EquipItem Old, EquipItem New) +public record EquipTransaction(EquipSlot Slot, EquipItem Old, EquipItem New) : ITransaction { public ITransaction? Merge(ITransaction older) @@ -40,7 +40,7 @@ public readonly record struct EquipTransaction(EquipSlot Slot, EquipItem Old, Eq => editor.ChangeItem(data, Slot, Old, ApplySettings.Manual); } -public readonly record struct BonusItemTransaction(BonusItemFlag Slot, EquipItem Old, EquipItem New) +public record BonusItemTransaction(BonusItemFlag Slot, EquipItem Old, EquipItem New) : ITransaction { public ITransaction? Merge(ITransaction older) @@ -50,7 +50,7 @@ public readonly record struct BonusItemTransaction(BonusItemFlag Slot, EquipItem => editor.ChangeBonusItem(data, Slot, Old, ApplySettings.Manual); } -public readonly record struct WeaponTransaction( +public record WeaponTransaction( EquipItem OldMain, EquipItem OldOff, EquipItem OldGauntlets, @@ -72,7 +72,7 @@ public readonly record struct WeaponTransaction( } } -public readonly record struct StainTransaction(EquipSlot Slot, StainIds Old, StainIds New) +public record StainTransaction(EquipSlot Slot, StainIds Old, StainIds New) : ITransaction { public ITransaction? Merge(ITransaction older) @@ -82,7 +82,7 @@ public readonly record struct StainTransaction(EquipSlot Slot, StainIds Old, Sta => editor.ChangeStains(data, Slot, Old, ApplySettings.Manual); } -public readonly record struct CrestTransaction(CrestFlag Slot, bool Old, bool New) +public record CrestTransaction(CrestFlag Slot, bool Old, bool New) : ITransaction { public ITransaction? Merge(ITransaction older) @@ -92,7 +92,7 @@ public readonly record struct CrestTransaction(CrestFlag Slot, bool Old, bool Ne => editor.ChangeCrest(data, Slot, Old, ApplySettings.Manual); } -public readonly record struct ParameterTransaction(CustomizeParameterFlag Slot, CustomizeParameterValue Old, CustomizeParameterValue New) +public record ParameterTransaction(CustomizeParameterFlag Slot, CustomizeParameterValue Old, CustomizeParameterValue New) : ITransaction { public ITransaction? Merge(ITransaction older) @@ -102,7 +102,7 @@ public readonly record struct ParameterTransaction(CustomizeParameterFlag Slot, => editor.ChangeCustomizeParameter(data, Slot, Old, ApplySettings.Manual); } -public readonly record struct MetaTransaction(MetaIndex Slot, bool Old, bool New) +public record MetaTransaction(MetaIndex Slot, bool Old, bool New) : ITransaction { public ITransaction? Merge(ITransaction older) diff --git a/Glamourer/Designs/Links/DesignLinkManager.cs b/Glamourer/Designs/Links/DesignLinkManager.cs index efed8ab..a5833e6 100644 --- a/Glamourer/Designs/Links/DesignLinkManager.cs +++ b/Glamourer/Designs/Links/DesignLinkManager.cs @@ -1,5 +1,4 @@ using Glamourer.Automation; -using Glamourer.Designs.History; using Glamourer.Events; using Glamourer.Services; using Luna; @@ -32,7 +31,7 @@ public sealed class DesignLinkManager : IService, IDisposable parent.LastEdit = DateTimeOffset.UtcNow; _saveService.QueueSave(parent); Glamourer.Log.Debug($"Moved link from {orderFrom} {idxFrom} to {idxTo} {orderTo}."); - _event.Invoke(DesignChanged.Type.ChangedLink, parent, null); + _event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent)); } public void AddDesignLink(Design parent, Design child, LinkOrder order) @@ -43,7 +42,7 @@ public sealed class DesignLinkManager : IService, IDisposable parent.LastEdit = DateTimeOffset.UtcNow; _saveService.QueueSave(parent); Glamourer.Log.Debug($"Added new {order} link to {child.Identifier} for {parent.Identifier}."); - _event.Invoke(DesignChanged.Type.ChangedLink, parent, null); + _event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent)); } public void RemoveDesignLink(Design parent, int idx, LinkOrder order) @@ -54,7 +53,7 @@ public sealed class DesignLinkManager : IService, IDisposable parent.LastEdit = DateTimeOffset.UtcNow; _saveService.QueueSave(parent); Glamourer.Log.Debug($"Removed the {order} link at {idx} for {parent.Identifier}."); - _event.Invoke(DesignChanged.Type.ChangedLink, parent, null); + _event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent)); } public void ChangeApplicationType(Design parent, int idx, LinkOrder order, ApplicationType applicationType) @@ -64,22 +63,23 @@ public sealed class DesignLinkManager : IService, IDisposable return; _saveService.QueueSave(parent); - Glamourer.Log.Debug($"Changed link application type from {old} to {applicationType} for design link {order} {idx + 1} in design {parent.Identifier}."); - _event.Invoke(DesignChanged.Type.ChangedLink, parent, null); + Glamourer.Log.Debug( + $"Changed link application type from {old} to {applicationType} for design link {order} {idx + 1} in design {parent.Identifier}."); + _event.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedLink, parent)); } - private void OnDesignChanged(DesignChanged.Type type, Design deletedDesign, ITransaction? _) + private void OnDesignChanged(in DesignChanged.Arguments arguments) { - if (type is not DesignChanged.Type.Deleted) + if (arguments.Type is not DesignChanged.Type.Deleted) return; foreach (var design in _storage) { - if (!design.Links.Remove(deletedDesign)) + if (!design.Links.Remove(arguments.Design)) continue; design.LastEdit = DateTimeOffset.UtcNow; - Glamourer.Log.Debug($"Removed {deletedDesign.Identifier} from {design.Identifier} links due to deletion."); + Glamourer.Log.Debug($"Removed {arguments.Design.Identifier} from {design.Identifier} links due to deletion."); _saveService.QueueSave(design); } } diff --git a/Glamourer/Designs/Links/LinkContainer.cs b/Glamourer/Designs/Links/LinkContainer.cs index 6cfc121..10e2416 100644 --- a/Glamourer/Designs/Links/LinkContainer.cs +++ b/Glamourer/Designs/Links/LinkContainer.cs @@ -1,6 +1,6 @@ using Glamourer.Automation; +using Luna; using Newtonsoft.Json.Linq; -using OtterGui.Filesystem; namespace Glamourer.Designs.Links; diff --git a/Glamourer/Designs/Special/RandomDesignGenerator.cs b/Glamourer/Designs/Special/RandomDesignGenerator.cs index 0c62aa2..e45122a 100644 --- a/Glamourer/Designs/Special/RandomDesignGenerator.cs +++ b/Glamourer/Designs/Special/RandomDesignGenerator.cs @@ -3,7 +3,7 @@ using Luna; namespace Glamourer.Designs.Special; -public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileSystem, Configuration config) : IService +public class RandomDesignGenerator(DesignStorage designs, Configuration config) : IService { private readonly Random _rng = new(); private readonly WeakReference _lastDesign = new(null!, false); @@ -34,7 +34,7 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS => Design(designs); public Design? Design(IDesignPredicate predicate) - => Design(predicate.Get(designs, fileSystem).ToList()); + => Design(predicate.Get(designs).ToList()); public Design? Design(IReadOnlyList predicates) { @@ -42,7 +42,7 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS { 0 => Design(), 1 => Design(predicates[0]), - _ => Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()), + _ => Design(IDesignPredicate.Get(predicates, designs).ToList()), }; } diff --git a/Glamourer/Designs/Special/RandomPredicate.cs b/Glamourer/Designs/Special/RandomPredicate.cs index 172d249..9039eaa 100644 --- a/Glamourer/Designs/Special/RandomPredicate.cs +++ b/Glamourer/Designs/Special/RandomPredicate.cs @@ -1,61 +1,59 @@ -using OtterGui.Classes; - -namespace Glamourer.Designs.Special; +namespace Glamourer.Designs.Special; public interface IDesignPredicate { - public bool Invoke(Design design, string lowerName, string identifier, string lowerPath); + bool Invoke(Design design, string name, string identifier, string path); - public bool Invoke((Design Design, string LowerName, string Identifier, string LowerPath) args) - => Invoke(args.Design, args.LowerName, args.Identifier, args.LowerPath); + bool Invoke((Design Design, string Name, string Identifier, string Path) args) + => Invoke(args.Design, args.Name, args.Identifier, args.Path); - public IEnumerable Get(IEnumerable designs, DesignFileSystem fileSystem) + IEnumerable Get(IEnumerable designs) => designs.Select(Transform) .Where(Invoke) .Select(t => t.Design); - public static IEnumerable Get(IReadOnlyList predicates, IEnumerable designs, DesignFileSystem fileSystem) + static IEnumerable Get(IReadOnlyList predicates, IEnumerable designs) => predicates.Count > 0 ? designs.Select(Transform) .Where(t => predicates.Any(p => p.Invoke(t))) .Select(t => t.Design) : designs; - private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d) - => (d, d.Name.Lower, d.Identifier.ToString(), d.Path.CurrentPath.ToLowerInvariant()); + private static (Design Design, string Name, string Identifier, string Path) Transform(Design d) + => (d, d.Name, d.Identifier.ToString(), d.Path.CurrentPath); } public static class RandomPredicate { public readonly struct StartsWith(string value) : IDesignPredicate { - public LowerString Value { get; } = value; + public string Value { get; } = value; - public bool Invoke(Design design, string lowerName, string identifier, string lowerPath) - => lowerPath.StartsWith(Value.Lower); + public bool Invoke(Design design, string name, string identifier, string path) + => path.StartsWith(Value, StringComparison.OrdinalIgnoreCase); public override string ToString() - => $"/{Value.Text}"; + => $"/{Value}"; } public readonly struct Contains(string value) : IDesignPredicate { - public LowerString Value { get; } = value; + public string Value { get; } = value; - public bool Invoke(Design design, string lowerName, string identifier, string lowerPath) + public bool Invoke(Design design, string name, string identifier, string path) { - if (lowerName.Contains(Value.Lower)) + if (name.Contains(Value, StringComparison.OrdinalIgnoreCase)) return true; - if (identifier.Contains(Value.Lower)) + if (identifier.Contains(Value, StringComparison.OrdinalIgnoreCase)) return true; - if (lowerPath.Contains(Value.Lower)) + if (path.Contains(Value, StringComparison.OrdinalIgnoreCase)) return true; return false; } public override string ToString() - => Value.Text; + => Value; } public readonly struct Exact(Exact.Type type, string value) : IDesignPredicate @@ -69,25 +67,25 @@ public static class RandomPredicate Color, } - public Type Which { get; } = type; - public LowerString Value { get; } = value; + public Type Which { get; } = type; + public string Value { get; } = value; - public bool Invoke(Design design, string lowerName, string identifier, string lowerPath) + public bool Invoke(Design design, string name, string identifier, string path) => Which switch { - Type.Name => lowerName == Value.Lower, - Type.Path => lowerPath == Value.Lower, - Type.Identifier => identifier == Value.Lower, + Type.Name => string.Equals(name, Value, StringComparison.OrdinalIgnoreCase), + Type.Path => string.Equals(path, Value, StringComparison.OrdinalIgnoreCase), + Type.Identifier => string.Equals(identifier, Value, StringComparison.OrdinalIgnoreCase), Type.Tag => IsContained(Value, design.Tags), - Type.Color => design.Color == Value, + Type.Color => string.Equals(design.Color, Value, StringComparison.OrdinalIgnoreCase), _ => false, }; - private static bool IsContained(LowerString value, IEnumerable data) - => data.Any(t => t == value); + private static bool IsContained(string value, IEnumerable data) + => data.Any(t => string.Equals(t, value, StringComparison.OrdinalIgnoreCase)); public override string ToString() - => $"\"{Which switch { Type.Name => 'n', Type.Identifier => 'i', Type.Path => 'p', Type.Tag => 't', Type.Color => 'c', _ => '?' }}?{Value.Text}\""; + => $"\"{Which switch { Type.Name => 'n', Type.Identifier => 'i', Type.Path => 'p', Type.Tag => 't', Type.Color => 'c', _ => '?' }}?{Value}\""; } public static IDesignPredicate CreateSinglePredicate(string restriction) @@ -125,7 +123,7 @@ public static class RandomPredicate public static List GeneratePredicates(string restrictions) { - if (restrictions.Length == 0) + if (restrictions.Length is 0) return []; List predicates = new(1); @@ -153,9 +151,9 @@ public static class RandomPredicate public static string GeneratePredicateString(IReadOnlyCollection predicates) { - if (predicates.Count == 0) + if (predicates.Count is 0) return string.Empty; - if (predicates.Count == 1) + if (predicates.Count is 1) return predicates.First()!.ToString()!; return $"{{{string.Join("; ", predicates)}}}"; diff --git a/Glamourer/Events/AutoRedrawChanged.cs b/Glamourer/Events/AutoRedrawChanged.cs index a8dd03a..5f9eebb 100644 --- a/Glamourer/Events/AutoRedrawChanged.cs +++ b/Glamourer/Events/AutoRedrawChanged.cs @@ -1,16 +1,16 @@ -using OtterGui.Classes; +using Luna; namespace Glamourer.Events; /// /// Triggered when the auto-reload gear setting is changed in glamourer configuration. /// -public sealed class AutoRedrawChanged() - : EventWrapper(nameof(AutoRedrawChanged)) +public sealed class AutoRedrawChanged(Logger log) + : EventBase(nameof(AutoRedrawChanged), log) { 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 9ee3e1b..67c2c76 100644 --- a/Glamourer/Events/AutomationChanged.cs +++ b/Glamourer/Events/AutomationChanged.cs @@ -1,64 +1,60 @@ using Glamourer.Automation; -using OtterGui.Classes; +using Glamourer.Designs; +using Luna; +using Penumbra.GameData.Actors; +using Penumbra.GameData.Structs; namespace Glamourer.Events; -/// -/// Triggered when an automated design is changed in any way. -/// -/// Parameter is the type of the change -/// Parameter is the added or changed design set or null on deletion. -/// Parameter is additional data depending on the type of change. -/// -/// -public sealed class AutomationChanged() - : EventWrapper(nameof(AutomationChanged)) +/// Triggered when an automated design is changed in any way. +public sealed class AutomationChanged(Logger log) + : EventBase(nameof(AutomationChanged), log) { public enum Type { - /// Add a new set. Names and identifiers do not have to be unique. It is not enabled by default. Additional data is the index it gets added at and the name [(int, string)]. + /// Add a new set. Names and identifiers do not have to be unique. It is not enabled by default. AddedSet, - /// Delete a given set. Additional data is the index it got removed from [int]. + /// Delete a given set. DeletedSet, - /// Rename a given set. Names do not have to be unique. Additional data is the old name and the new name [(string, string)]. + /// Rename a given set. Names do not have to be unique. RenamedSet, - /// Move a given set to a different position. Additional data is the old index of the set and the new index of the set [(int, int)]. + /// Move a given set to a different position. MovedSet, - /// Change the identifier a given set is associated with to another one. Additional data is the old identifier and the new one, and a potentially disabled other design set. [(ActorIdentifier[], ActorIdentifier, AutoDesignSet?)]. + /// Change the identifier a given set is associated with to another one. ChangeIdentifier, - /// Toggle the enabled state of a given set. Additional data is the thus disabled other set, if any [AutoDesignSet?]. + /// Toggle the enabled state of a given set. ToggleSet, - /// Change the used base state of a given set. Additional data is prior and new base. [(AutoDesignSet.Base, AutoDesignSet.Base)]. + /// Change the used base state of a given set. ChangedBase, - /// Change the resetting of temporary settings for a given set. Additional data is the new value. + /// Change the resetting of temporary settings for a given set. ChangedTemporarySettingsReset, - /// Add a new associated design to a given set. Additional data is the index it got added at [int]. + /// Add a new associated design to a given set. AddedDesign, - /// Remove a given associated design from a given set. Additional data is the index it got removed from [int]. + /// Remove a given associated design from a given set. DeletedDesign, - /// Move a given associated design in the list of a given set. Additional data is the index that got moved and the index it got moved to [(int, int)]. + /// Move a given associated design in the list of a given set. MovedDesign, - /// Change the linked design in an associated design for a given set. Additional data is the index of the changed associated design, the old linked design and the new linked design [(int, IDesignStandIn, IDesignStandIn)]. + /// Change the linked design in an associated design for a given set. ChangedDesign, - /// Change the job condition in an associated design for a given set. Additional data is the index of the changed associated design, the old job group and the new job group [(int, JobGroup, JobGroup)]. + /// Change the job condition in an associated design for a given set. ChangedConditions, - /// Change the application type in an associated design for a given set. Additional data is the index of the changed associated design, the old type and the new type. [(int, AutoDesign.Type, AutoDesign.Type)]. + /// Change the application type in an associated design for a given set. ChangedType, - /// Change the additional data for a specific design type. Additional data is the index of the changed associated design and the new data. [(int, object)] + /// Change the additional data for a specific design type. ChangedData, } @@ -76,4 +72,92 @@ public sealed class AutomationChanged() /// RandomRestrictionDrawer = -1, } + + /// The type of change. + public record Arguments(Type Type, AutoDesignSet Set) + { + public T As() where T : Arguments + => (T)this; + } + + /// The added set. + /// The index the set was added at. + /// The name of the added set. + public sealed record AddedSetArguments(AutoDesignSet Set, int Index, string Name) : Arguments(Type.AddedSet, Set); + + /// The deleted set. + /// The index the set was deleted from. + public sealed record DeletedSetArguments(AutoDesignSet Set, int Index) : Arguments(Type.DeletedSet, Set); + + /// The renamed set. + /// The old name of the set. + /// The new name of the set. + public sealed record RenamedSetArguments(AutoDesignSet Set, string OldName, string NewName) : Arguments(Type.RenamedSet, Set); + + /// The moved set. + /// The index the set was moved from. + /// The index the set was moved to. + public sealed record MovedSetArguments(AutoDesignSet Set, int OldIndex, int NewIndex) : Arguments(Type.MovedSet, Set); + + /// The set that got its associated identifiers changed. + /// The prior identifiers that got removed. + /// The new identifiers associated with the set. + /// A set that was associated with the new identifiers before and got disabled through this change, or null. + public sealed record ChangeIdentifierArguments( + AutoDesignSet Set, + ActorIdentifier[] OldIdentifiers, + ActorIdentifier NewIdentifier, + AutoDesignSet? DisabledSet) : Arguments(Type.ChangeIdentifier, Set); + + /// The set that got toggled on or off. + /// A set with the same association that got disabled due to this change, or null. + public sealed record ToggleSetArguments(AutoDesignSet Set, AutoDesignSet? DisabledSet) : Arguments(Type.ToggleSet, Set); + + /// The set that changed its base state. + /// The old base state of the set. + /// The new base state of the set. + public sealed record ChangedBaseArguments(AutoDesignSet Set, AutoDesignSet.Base OldBase, AutoDesignSet.Base NewBase) : Arguments(Type.ChangedBase, Set); + + /// The set that changed whether it resets all temporary settings. + /// The new state of resetting temporary settings. + public sealed record ChangedTemporarySettingsResetArguments(AutoDesignSet Set, bool NewValue) : Arguments(Type.ChangedTemporarySettingsReset, Set); + + /// The set that added a new design. + /// The index the new design was added in the set. + public sealed record AddedDesignArguments(AutoDesignSet Set, int Index) : Arguments(Type.AddedDesign, Set); + + /// The set that removed a design. + /// The index the design was removed from. + public sealed record DeletedDesignArguments(AutoDesignSet Set, int Index) : Arguments(Type.DeletedDesign, Set); + + /// The set that moved a design. + /// The index the design was moved from in the set. + /// The index the design was moved from to the set. + public sealed record MovedDesignArguments(AutoDesignSet Set, int OldIndex, int NewIndex) : Arguments(Type.MovedDesign, Set); + + /// The set that changed a design. + /// The index of the changed design. + /// The design previously assigned to the set. + /// The design the old one was changed to. + public sealed record ChangedDesignArguments(AutoDesignSet Set, int DesignIndex, IDesignStandIn OldDesign, IDesignStandIn NewDesign) + : Arguments(Type.ChangedDesign, Set); + + /// The set that changed a job group condition. + /// The index of the changed design. + /// The prior job condition for the design. + /// The new job condition for the design. + public sealed record ChangedConditionsArguments(AutoDesignSet Set, int DesignIndex, JobGroup OldGroup, JobGroup NewGroup) + : Arguments(Type.ChangedConditions, Set); + + /// The set that changed its application type. + /// The index of the changed design. + /// The old application flags. + /// The new application flags. + public sealed record ChangedTypeArguments(AutoDesignSet Set, int DesignIndex, ApplicationType OldType, ApplicationType NewType) + : Arguments(Type.ChangedType, Set); + + /// The set that got a design data changed. + /// The index of the changed design. + /// The new additional data for the changed design. + public sealed record ChangedDataArguments(AutoDesignSet Set, int DesignIndex, object NewData) : Arguments(Type.ChangedData, Set); } diff --git a/Glamourer/Events/BonusSlotUpdating.cs b/Glamourer/Events/BonusSlotUpdating.cs index 3f6e761..b796744 100644 --- a/Glamourer/Events/BonusSlotUpdating.cs +++ b/Glamourer/Events/BonusSlotUpdating.cs @@ -1,25 +1,32 @@ -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Events; -/// -/// Triggered when a model flags a bonus slot for an update. -/// -/// Parameter is the model with a flagged slot. -/// Parameter is the bonus slot changed. -/// Parameter is the model values to change the bonus piece to. -/// Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. -/// -/// -public sealed class BonusSlotUpdating() - : EventWrapperRef34(nameof(BonusSlotUpdating)) +/// Triggered when a model flags a bonus slot for an update. +public sealed class BonusSlotUpdating(Logger log) + : EventBase(nameof(BonusSlotUpdating), log) { public enum Priority { /// StateListener = 0, } + + public ref struct Arguments(Model model, BonusItemFlag flag, ref CharacterArmor armor, ref ulong returnValue) + { + /// The draw object with an updated bonus slot. + public readonly Model Model = model; + + /// The updated slot. + public readonly BonusItemFlag Slot = flag; + + /// The model data for the new bonus slot. This can be changed. + public ref CharacterArmor Armor = ref armor; + + /// The return value of the event. If this is , the original function will be called and its return value used, otherwise this value will be returned. + public ref ulong ReturnValue = ref returnValue; + } } diff --git a/Glamourer/Events/DesignChanged.cs b/Glamourer/Events/DesignChanged.cs index 83fc18b..1d9e897 100644 --- a/Glamourer/Events/DesignChanged.cs +++ b/Glamourer/Events/DesignChanged.cs @@ -2,20 +2,13 @@ using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Gui; using Glamourer.Gui.Tabs.DesignTab; -using OtterGui.Classes; +using Luna; namespace Glamourer.Events; -/// -/// Triggered when a Design is edited in any way. -/// -/// Parameter is the type of the change -/// Parameter is the changed Design. -/// Parameter is any additional data depending on the type of change. -/// -/// -public sealed class DesignChanged() - : EventWrapper(nameof(DesignChanged)) +/// Triggered when a Design is edited in any way. +public sealed class DesignChanged(Logger log) + : EventBase(nameof(DesignChanged), log) { public enum Type { @@ -151,4 +144,10 @@ public sealed class DesignChanged() /// EditorHistory = -1000, } + + /// Arguments for the DesignChanged event. + /// The type of changed. + /// The changed design. + /// A transaction with further data corresponding to the change. + public readonly record struct Arguments(Type Type, Design Design, ITransaction? Transaction = null); } diff --git a/Glamourer/Events/EquipSlotUpdating.cs b/Glamourer/Events/EquipSlotUpdating.cs index a2daf85..da2face 100644 --- a/Glamourer/Events/EquipSlotUpdating.cs +++ b/Glamourer/Events/EquipSlotUpdating.cs @@ -1,25 +1,32 @@ -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Events; -/// -/// Triggered when a model flags an equipment slot for an update. -/// -/// Parameter is the model with a flagged slot. -/// Parameter is the equipment slot changed. -/// Parameter is the model values to change the equipment piece to. -/// Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. -/// -/// -public sealed class EquipSlotUpdating() - : EventWrapperRef34(nameof(EquipSlotUpdating)) +/// Triggered when a model flags an equipment slot for an update. +public sealed class EquipSlotUpdating(Logger log) + : EventBase(nameof(EquipSlotUpdating), log) { public enum Priority { /// StateListener = 0, } + + public ref struct Arguments(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue) + { + /// The draw object with an updated equipment slot. + public readonly Model Model = model; + + /// The updated slot. + public readonly EquipSlot Slot = slot; + + /// The model data for the new equipment slot. This can be changed. + public ref CharacterArmor Armor = ref armor; + + /// The return value of the event. If this is , the original function will be called and its return value used, otherwise this value will be returned. + public ref ulong ReturnValue = ref returnValue; + } } \ No newline at end of file diff --git a/Glamourer/Events/EquippedGearset.cs b/Glamourer/Events/EquippedGearset.cs index c4252eb..012b59f 100644 --- a/Glamourer/Events/EquippedGearset.cs +++ b/Glamourer/Events/EquippedGearset.cs @@ -1,23 +1,23 @@ -using OtterGui.Classes; +using Luna; +using Penumbra.String; namespace Glamourer.Events; -/// -/// Triggered when the player equips a gear set. -/// -/// Parameter is the name of the gear set. -/// Parameter is the id of the gear set. -/// Parameter is the id of the prior gear set. -/// Parameter is the id of the associated glamour. -/// Parameter is the job id of the associated job. -/// -/// -public sealed class EquippedGearset() - : EventWrapper(nameof(EquippedGearset)) +/// Triggered when the player equips a gear set. +public sealed class EquippedGearset(Logger log) + : EventBase(nameof(EquippedGearset), log) { public enum Priority { /// AutoDesignApplier = 0, } + + /// Arguments for the EquippedGearset event. + /// The name of the equipped gear set. + /// The ID of the equipped gear set. + /// The ID of the gear set previously equipped. + /// The ID of the associated glamour plate. + /// The job ID of the associated job. + public readonly record struct Arguments(ByteString Name, int Id, int PriorId, int GlamourId, byte JobId); } diff --git a/Glamourer/Events/GPoseService.cs b/Glamourer/Events/GPoseService.cs index 44421a0..08a75a6 100644 --- a/Glamourer/Events/GPoseService.cs +++ b/Glamourer/Events/GPoseService.cs @@ -1,9 +1,9 @@ using Dalamud.Plugin.Services; -using OtterGui.Classes; +using Luna; namespace Glamourer.Events; -public sealed class GPoseService : EventWrapper +public sealed class GPoseService : EventBase { private readonly IFramework _framework; private readonly IClientState _state; @@ -19,8 +19,8 @@ public sealed class GPoseService : EventWrapper public bool InGPose { get; private set; } - public GPoseService(IFramework framework, IClientState state) - : base(nameof(GPoseService)) + public GPoseService(IFramework framework, IClientState state, Logger log) + : base(nameof(GPoseService), log) { _framework = framework; _state = state; diff --git a/Glamourer/Events/GearsetDataLoaded.cs b/Glamourer/Events/GearsetDataLoaded.cs index 620bdab..31daae1 100644 --- a/Glamourer/Events/GearsetDataLoaded.cs +++ b/Glamourer/Events/GearsetDataLoaded.cs @@ -1,21 +1,23 @@ -using OtterGui.Classes; +using Luna; 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) -/// +/// 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. /// -public sealed class GearsetDataLoaded() - : EventWrapper(nameof(GearsetDataLoaded)) +public sealed class GearsetDataLoaded(Logger log) + : EventBase(nameof(GearsetDataLoaded), log) { public enum Priority { /// StateListener = 0, } + + /// Arguments for the GearsetDataLoaded event. + /// The actor that loaded a gear set. + /// The draw object that finished the load. + public readonly record struct Arguments(Actor Actor, Model Model); } diff --git a/Glamourer/Events/HeadGearVisibilityChanged.cs b/Glamourer/Events/HeadGearVisibilityChanged.cs index d8f722b..eaf067c 100644 --- a/Glamourer/Events/HeadGearVisibilityChanged.cs +++ b/Glamourer/Events/HeadGearVisibilityChanged.cs @@ -1,21 +1,24 @@ -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Interop; namespace Glamourer.Events; -/// -/// Triggered when the visibility of head gear is changed. -/// -/// Parameter is the actor with changed head gear visibility. -/// Parameter is the new state. -/// -/// -public sealed class HeadGearVisibilityChanged() - : EventWrapperRef2(nameof(HeadGearVisibilityChanged)) +/// Triggered when the visibility of head gear is changed. +public sealed class HeadGearVisibilityChanged(Logger log) + : EventBase(nameof(HeadGearVisibilityChanged), log) { public enum Priority { /// StateListener = 0, } + + public ref struct Arguments(Actor actor, ref bool visible) + { + /// The actor whose head gear visibility changed. + public readonly Actor Actor = actor; + + /// The new visibility state. + public ref bool Visible = ref visible; + } } diff --git a/Glamourer/Events/MovedEquipment.cs b/Glamourer/Events/MovedEquipment.cs index 9d24a03..6f2952c 100644 --- a/Glamourer/Events/MovedEquipment.cs +++ b/Glamourer/Events/MovedEquipment.cs @@ -1,4 +1,4 @@ -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -10,12 +10,14 @@ namespace Glamourer.Events; /// Parameter is an array of slots updated and corresponding item ids and stains. /// /// -public sealed class MovedEquipment() - : EventWrapper<(EquipSlot, uint, StainIds)[], MovedEquipment.Priority>(nameof(MovedEquipment)) +public sealed class MovedEquipment(Logger log) + : EventBase(nameof(MovedEquipment), log) { public enum Priority { /// StateListener = 0, } + + public readonly record struct Arguments(params (EquipSlot, uint, StainIds)[] Items); } diff --git a/Glamourer/Events/ObjectUnlocked.cs b/Glamourer/Events/ObjectUnlocked.cs index b15acd2..e2d103c 100644 --- a/Glamourer/Events/ObjectUnlocked.cs +++ b/Glamourer/Events/ObjectUnlocked.cs @@ -1,17 +1,10 @@ -using OtterGui.Classes; +using Luna; namespace Glamourer.Events; -/// -/// Triggered when a new item or customization is unlocked. -/// -/// Parameter is the type of the unlocked object -/// Parameter is the id of the unlocked object. -/// Parameter is the timestamp of the unlock. -/// -/// -public sealed class ObjectUnlocked() - : EventWrapper(nameof(ObjectUnlocked)) +/// Triggered when a new item or customization is unlocked. +public sealed class ObjectUnlocked(Logger log) + : EventBase(nameof(ObjectUnlocked), log), IRequiredService { public enum Type { @@ -21,8 +14,15 @@ public sealed class ObjectUnlocked() public enum Priority { - /// + /// /// Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. UnlockTable = 0, } + + + /// Arguments for the ObjectUnlocked event. + /// The type of the unlocked object. + /// The ID of the unlocked object. + /// The timestamp of the unlock event. + public readonly record struct Arguments(Type Type, uint Id, DateTimeOffset Timestamp); } diff --git a/Glamourer/Events/PenumbraReloaded.cs b/Glamourer/Events/PenumbraReloaded.cs index 0975670..0546daa 100644 --- a/Glamourer/Events/PenumbraReloaded.cs +++ b/Glamourer/Events/PenumbraReloaded.cs @@ -1,22 +1,22 @@ -using OtterGui.Classes; +using Luna; namespace Glamourer.Events; /// /// Triggered when Penumbra is reloaded. /// -public sealed class PenumbraReloaded() - : EventWrapper(nameof(PenumbraReloaded)) +public sealed class PenumbraReloaded(Logger log) + : EventBase(nameof(PenumbraReloaded), log) { public enum Priority { - /// + /// ChangeCustomizeService = 0, - /// + /// VisorService = 0, - /// + /// VieraEarService = 0, } } diff --git a/Glamourer/Events/StateChanged.cs b/Glamourer/Events/StateChanged.cs index 2bcc6fe..a1bf6df 100644 --- a/Glamourer/Events/StateChanged.cs +++ b/Glamourer/Events/StateChanged.cs @@ -1,33 +1,37 @@ using Glamourer.Api.Enums; using Glamourer.Designs.History; -using Glamourer.Interop.Structs; using Glamourer.State; -using OtterGui.Classes; using Penumbra.GameData.Interop; +using Luna; namespace Glamourer.Events; -/// -/// Triggered when a Design is edited in any way. -/// -/// Parameter is the type of the change -/// Parameter is the changed saved state. -/// Parameter is the existing actors using this saved state. -/// Parameter is any additional data depending on the type of change. -/// -/// -public sealed class StateChanged() - : EventWrapper(nameof(StateChanged)) +/// Triggered when a state changes in any way. +public sealed class StateChanged(Logger log) + : EventBase(nameof(StateChanged), log) { public enum Priority { /// GlamourerIpc = int.MinValue, - /// + /// PenumbraAutoRedraw = 0, /// EditorHistory = -1000, } + + /// Arguments for the StateChanged event. + /// The type of change that occured. + /// The source of the change. + /// The changed state. + /// Any currently affected actors. + /// Additional data depending on the type of change. + public readonly record struct Arguments( + StateChangeType Type, + StateSource Source, + ActorState State, + ActorData Actors, + ITransaction? Transaction = null); } diff --git a/Glamourer/Events/StateFinalized.cs b/Glamourer/Events/StateFinalized.cs index 0ccaa8b..8944835 100644 --- a/Glamourer/Events/StateFinalized.cs +++ b/Glamourer/Events/StateFinalized.cs @@ -1,24 +1,22 @@ using Glamourer.Api; using Glamourer.Api.Enums; -using Glamourer.Interop.Structs; -using OtterGui.Classes; +using Luna; 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)) +/// Triggered when a set of grouped changes finishes being applied to a Glamourer state. +public sealed class StateFinalized(Logger log) + : EventBase(nameof(StateFinalized), log) { public enum Priority { /// StateApi = int.MinValue, } + + /// Arguments for the StateFinalized event. + /// The operation that finished updating the saved state. + /// The existing actors using this saved state at the moment. + public readonly record struct Arguments(StateFinalizationType Type, ActorData Actors); } diff --git a/Glamourer/Events/TabSelected.cs b/Glamourer/Events/TabSelected.cs index 1880d37..1b81d46 100644 --- a/Glamourer/Events/TabSelected.cs +++ b/Glamourer/Events/TabSelected.cs @@ -1,26 +1,24 @@ using Glamourer.Designs; using Glamourer.Gui; -using Glamourer.Gui.Tabs.DesignTab; -using OtterGui.Classes; +using Luna; namespace Glamourer.Events; -/// -/// Triggered when an automated design is changed in any way. -/// -/// Parameter is the tab to select. -/// Parameter is the design to select if the tab is the designs tab. -/// -/// -public sealed class TabSelected() - : EventWrapper(nameof(TabSelected)) +/// Triggered to select a tab or design. +public sealed class TabSelected(Logger log) + : EventBase(nameof(TabSelected), log) { public enum Priority { - /// + /// DesignSelector = 0, - /// + /// MainWindow = 1, } + + /// Arguments for the TabSelected event. + /// The tab to be selected. + /// The design to be selected in the Designs tab. + public readonly record struct Arguments(MainTabType Type, Design? Design = null); } diff --git a/Glamourer/Events/VieraEarStateChanged.cs b/Glamourer/Events/VieraEarStateChanged.cs index 65730b8..5390469 100644 --- a/Glamourer/Events/VieraEarStateChanged.cs +++ b/Glamourer/Events/VieraEarStateChanged.cs @@ -1,22 +1,24 @@ -using OtterGui.Classes; +using Luna; 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)) +/// Triggered when the state of viera ear visibility for any draw object is changed. +public sealed class VieraEarStateChanged(Logger log) + : EventBase(nameof(VieraEarStateChanged), log) { public enum Priority { /// StateListener = 0, } + + public ref struct Arguments(Actor actor, ref bool state) + { + /// The actor with a changed viera ear visibility state. + public readonly Actor Actor = actor; + + /// The new ear visibility state. + public ref bool State = ref state; + } } diff --git a/Glamourer/Events/VisorStateChanged.cs b/Glamourer/Events/VisorStateChanged.cs index 03b7336..b070bda 100644 --- a/Glamourer/Events/VisorStateChanged.cs +++ b/Glamourer/Events/VisorStateChanged.cs @@ -1,22 +1,27 @@ -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Interop; namespace Glamourer.Events; -/// -/// Triggered when the state of a visor for any draw object is changed. -/// -/// Parameter is the model with a changed visor state. -/// Parameter is the new state. -/// Parameter is whether to call the original function. -/// -/// -public sealed class VisorStateChanged() - : EventWrapperRef3(nameof(VisorStateChanged)) +/// Triggered when the state of a visor for any draw object is changed. +public sealed class VisorStateChanged(Logger log) + : EventBase(nameof(VisorStateChanged), log) { public enum Priority { /// StateListener = 0, } -} \ No newline at end of file + + public ref struct Arguments(Model model, bool visorState, ref bool value) + { + /// The model with a changed visor state. + public readonly Model Model = model; + + /// The game's visor state. + public readonly bool NewVisorState = visorState; + + /// The actual new value + public ref bool Value = ref value; + } +} diff --git a/Glamourer/Events/WeaponLoading.cs b/Glamourer/Events/WeaponLoading.cs index fda0b2f..fe29702 100644 --- a/Glamourer/Events/WeaponLoading.cs +++ b/Glamourer/Events/WeaponLoading.cs @@ -1,20 +1,13 @@ -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Events; -/// -/// Triggered when a model flags an equipment slot for an update. -/// -/// Parameter is the actor that has its weapons changed. -/// Parameter is the equipment slot changed (Mainhand or Offhand). -/// Parameter is the model values to change the weapon to. -/// -/// -public sealed class WeaponLoading() - : EventWrapperRef3(nameof(WeaponLoading)) +/// Triggered when a model flags an equipment slot for an update. +public sealed class WeaponLoading(Logger log) + : EventBase(nameof(WeaponLoading), log) { public enum Priority { @@ -24,4 +17,16 @@ public sealed class WeaponLoading() /// AutoDesignApplier = -1, } + + public ref struct Arguments(Actor actor, EquipSlot slot, ref CharacterWeapon weapon) + { + /// The actor that has its weapons changed. + public readonly Actor Actor = actor; + + /// The changed equipment slot (either Mainhand or Offhand). + public readonly EquipSlot Slot = slot; + + /// The model data for the new weapon. + public ref CharacterWeapon Weapon = ref weapon; + } } diff --git a/Glamourer/Events/WeaponVisibilityChanged.cs b/Glamourer/Events/WeaponVisibilityChanged.cs index f75fa68..5683336 100644 --- a/Glamourer/Events/WeaponVisibilityChanged.cs +++ b/Glamourer/Events/WeaponVisibilityChanged.cs @@ -1,20 +1,24 @@ -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Interop; namespace Glamourer.Events; -/// -/// Triggered when the visibility of weapons is changed. -/// -/// Parameter is the actor with changed weapon visibility. -/// Parameter is the new state. -/// -/// -public sealed class WeaponVisibilityChanged() : EventWrapperRef2(nameof(WeaponVisibilityChanged)) +/// Triggered when the visibility of weapons is changed. +public sealed class WeaponVisibilityChanged(Logger log) + : EventBase(nameof(WeaponVisibilityChanged), log) { public enum Priority { /// StateListener = 0, } + + public ref struct Arguments(Actor actor, ref bool value) + { + /// The actor with changed weapon visibility. + public readonly Actor Actor = actor; + + /// The new state. + public ref bool Value = ref value; + } } diff --git a/Glamourer/GameData/CustomizeSet.cs b/Glamourer/GameData/CustomizeSet.cs index abcc90d..c388a5f 100644 --- a/Glamourer/GameData/CustomizeSet.cs +++ b/Glamourer/GameData/CustomizeSet.cs @@ -1,5 +1,4 @@ using ImSharp; -using OtterGui.Extensions; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Race = Penumbra.GameData.Enums.Race; @@ -139,10 +138,10 @@ public class CustomizeSet _ => Invalid(out custom), }; - int Get(IEnumerable list, CustomizeValue v, out CustomizeData? output) + static int Get(IEnumerable list, CustomizeValue v, out CustomizeData? output) { - var (val, idx) = list.Cast().WithIndex().FirstOrDefault(p => p.Value!.Value.Value == v); - if (val == null) + var (idx, val) = list.Cast().Index().FirstOrDefault(p => p.Item!.Value.Value == v); + if (val is null) { output = null; return -1; diff --git a/Glamourer/Glamourer.cs b/Glamourer/Glamourer.cs index bb6dd4f..fc5d67d 100644 --- a/Glamourer/Glamourer.cs +++ b/Glamourer/Glamourer.cs @@ -10,7 +10,7 @@ using Glamourer.State; using ImSharp; using Luna; using Penumbra.GameData.Interop; -using Logger = OtterGui.Log.Logger; +using Vortice.Direct3D11.Debug; namespace Glamourer; diff --git a/Glamourer/Glamourer.csproj b/Glamourer/Glamourer.csproj index 0c1602c..7ce1901 100644 --- a/Glamourer/Glamourer.csproj +++ b/Glamourer/Glamourer.csproj @@ -30,14 +30,12 @@ - - + @@ -56,8 +54,7 @@ - + diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs index a5b22b3..287176b 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs @@ -12,13 +12,13 @@ using Penumbra.GameData.Structs; namespace Glamourer.Gui.Customization; -public partial class CustomizationDrawer( +public sealed partial class CustomizationDrawer( ITextureProvider textures, CustomizeService service, Configuration config, FavoriteManager favorites, HeightService heightService) - : IDisposable + : IDisposable, IUiService { private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f); private readonly IDalamudTextureWrap? _legacyTattoo = GetLegacyTattooIcon(textures); diff --git a/Glamourer/Gui/DesignCombo.cs b/Glamourer/Gui/DesignCombo.cs index 6763cfe..f19ea5c 100644 --- a/Glamourer/Gui/DesignCombo.cs +++ b/Glamourer/Gui/DesignCombo.cs @@ -1,6 +1,5 @@ using Glamourer.Automation; using Glamourer.Designs; -using Glamourer.Designs.History; using Glamourer.Designs.Special; using Glamourer.Events; using ImSharp; @@ -54,7 +53,7 @@ public abstract class DesignComboBase( if (CurrentDesign is Design design) { if (Im.Item.RightClicked() && Im.Io.KeyControl) - TabSelected.Invoke(MainTabType.Designs, design); + TabSelected.Invoke(new TabSelected.Arguments(MainTabType.Designs, design)); Im.Tooltip.OnHover("Control + Right-Click to move to design."u8); } else @@ -81,7 +80,7 @@ public abstract class DesignComboBase( Im.Text("Currently resolving to "u8); using var color = ImGuiColor.Text.Push(DesignColors.GetColor(linkedDesign)); Im.Line.NoSpacing(); - Im.Text(linkedDesign.Name.Text); + Im.Text(linkedDesign.Name); } else { @@ -133,9 +132,9 @@ public abstract class DesignComboBase( private void OnDesignColorChanged() => Dirty |= IManagedCache.DirtyFlags.Custom; - private void OnDesignChanged(DesignChanged.Type type, Design? _1, ITransaction? _2 = null) + private void OnDesignChanged(in DesignChanged.Arguments arguments) { - if (type switch + if (arguments.Type switch { DesignChanged.Type.Created => true, DesignChanged.Type.Renamed => true, @@ -215,18 +214,18 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo); } - private void OnDesignChanged(DesignChanged.Type type, Design changedDesign, ITransaction? _) + private void OnDesignChanged(in DesignChanged.Arguments arguments) { - switch (type) + switch (arguments.Type) { case DesignChanged.Type.Created: // If the quick design bar has no selection, select the new design if it supports the bar. - if (QuickDesign is null && changedDesign.QuickDesign) - QuickDesign = changedDesign; + if (QuickDesign is null && arguments.Design.QuickDesign) + QuickDesign = arguments.Design; break; case DesignChanged.Type.Deleted: // If the deleted design was selected, select the first design that supports the bar, if any. - if (QuickDesign == changedDesign) + if (QuickDesign == arguments.Design) QuickDesign = Designs.Designs.FirstOrDefault(d => d.QuickDesign); break; case DesignChanged.Type.ReloadedAll: @@ -235,10 +234,10 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService break; case DesignChanged.Type.QuickDesignBar: // If the quick design support of a design was changed, select the new design if the bar has no selection and the design now supports it, - if (QuickDesign is null && changedDesign.QuickDesign) - QuickDesign = changedDesign; + if (QuickDesign is null && arguments.Design.QuickDesign) + QuickDesign = arguments.Design; // or select the first design that supports the bar, if any, if the support was removed from the currently selected design. - else if (QuickDesign == changedDesign && !changedDesign.QuickDesign) + else if (QuickDesign == arguments.Design && !arguments.Design.QuickDesign) QuickDesign = Designs.Designs.FirstOrDefault(d => d.QuickDesign); break; } @@ -290,9 +289,10 @@ public sealed class LinkDesignCombo : DesignComboBase, IUiService, IDisposable public void Dispose() => DesignChanged.Unsubscribe(OnDesignChanged); - private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? _) + private void OnDesignChanged(in DesignChanged.Arguments arguments) { - if (type is DesignChanged.Type.Deleted && design == NewSelection || type is DesignChanged.Type.ReloadedAll) + if (arguments.Type is DesignChanged.Type.Deleted && arguments.Design == NewSelection + || arguments.Type is DesignChanged.Type.ReloadedAll) NewSelection = null; } } @@ -311,8 +311,8 @@ public sealed class RandomDesignCombo( return exact.Which switch { RandomPredicate.Exact.Type.Name => Designs.Designs.FirstOrDefault(d => d.Name == exact.Value), - RandomPredicate.Exact.Type.Path => Designs.Designs.FirstOrDefault(d => d.Node!.FullPath == exact.Value.Text), - RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value.Text, out var g) ? g : Guid.Empty), + RandomPredicate.Exact.Type.Path => Designs.Designs.FirstOrDefault(d => d.Node!.FullPath == exact.Value), + RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value, out var g) ? g : Guid.Empty), _ => null, }; } @@ -320,7 +320,7 @@ public sealed class RandomDesignCombo( public bool Draw(RandomPredicate.Exact exact, [NotNullWhen(true)] out Design? newDesign, float width) { var design = GetDesign(exact); - if (Draw(StringU8.Empty, design?.ResolveName(Config.IncognitoMode) ?? $"Not Found [{exact.Value.Text}]", StringU8.Empty, width, + if (Draw(StringU8.Empty, design?.ResolveName(Config.IncognitoMode) ?? $"Not Found [{exact.Value}]", StringU8.Empty, width, out var newItem) && newItem.Design is Design d) { diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.cs index ba69c20..5c874bd 100644 --- a/Glamourer/Gui/Equipment/EquipmentDrawer.cs +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.cs @@ -12,7 +12,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Gui.Equipment; -public class EquipmentDrawer +public sealed class EquipmentDrawer : IUiService { private const float DefaultWidth = 280; diff --git a/Glamourer/Gui/GenericPopupWindow.cs b/Glamourer/Gui/GenericPopupWindow.cs index f0a6441..ab53622 100644 --- a/Glamourer/Gui/GenericPopupWindow.cs +++ b/Glamourer/Gui/GenericPopupWindow.cs @@ -2,10 +2,11 @@ using Dalamud.Plugin.Services; using Glamourer.Config; using ImSharp; +using Luna; namespace Glamourer.Gui; -public class GenericPopupWindow : Luna.Window +public sealed class GenericPopupWindow : Window { private readonly Configuration _config; private readonly ICondition _condition; diff --git a/Glamourer/Gui/GlamourerChangelog.cs b/Glamourer/Gui/GlamourerChangelog.cs index a6c3e48..b0195a4 100644 --- a/Glamourer/Gui/GlamourerChangelog.cs +++ b/Glamourer/Gui/GlamourerChangelog.cs @@ -4,7 +4,7 @@ using Luna; namespace Glamourer.Gui; -public class GlamourerChangelog +public sealed class GlamourerChangelog : IUiService { public const int LastChangelogVersion = 0; private readonly Configuration _config; diff --git a/Glamourer/Gui/GlamourerWindowSystem.cs b/Glamourer/Gui/GlamourerWindowSystem.cs index 820927c..9ed596e 100644 --- a/Glamourer/Gui/GlamourerWindowSystem.cs +++ b/Glamourer/Gui/GlamourerWindowSystem.cs @@ -1,11 +1,12 @@ using Dalamud.Interface; -using Dalamud.Interface.Windowing; using Glamourer.Config; using Glamourer.Gui.Tabs.UnlocksTab; +using Luna; +using WindowSystem = Dalamud.Interface.Windowing.WindowSystem; namespace Glamourer.Gui; -public class GlamourerWindowSystem : IDisposable +public sealed class GlamourerWindowSystem : IDisposable, IUiService { private readonly WindowSystem _windowSystem = new("Glamourer"); private readonly IUiBuilder _uiBuilder; diff --git a/Glamourer/Gui/MainTabBar.cs b/Glamourer/Gui/MainTabBar.cs index 23f5180..4abf279 100644 --- a/Glamourer/Gui/MainTabBar.cs +++ b/Glamourer/Gui/MainTabBar.cs @@ -1,4 +1,4 @@ -using Glamourer.Designs; +using Glamourer.Config; using Glamourer.Events; using Glamourer.Gui.Tabs; using Glamourer.Gui.Tabs.ActorTab; @@ -14,11 +14,11 @@ namespace Glamourer.Gui; public sealed class MainTabBar : TabBar { - private readonly Config.EphemeralConfig _config; - public readonly TabSelected Event; - public readonly SettingsTab Settings; + private readonly EphemeralConfig _config; + public readonly TabSelected Event; + public readonly SettingsTab Settings; - public MainTabBar(Logger log, Config.EphemeralConfig config, SettingsTab settings, ActorTab actors, DesignTab designs, + public MainTabBar(Logger log, EphemeralConfig config, SettingsTab settings, ActorTab actors, DesignTab designs, AutomationTab automation, UnlocksTab unlocks, NpcTab npcs, MessagesTab messages, DebugTab debug, TabSelected @event) : base("MainTabBar", log, settings, actors, designs, automation, unlocks, npcs, messages, debug) { @@ -30,8 +30,8 @@ public sealed class MainTabBar : TabBar NextTab = _config.SelectedMainTab; } - private void OnEvent(MainTabType tab, Design? _) - => NextTab = tab; + private void OnEvent(in TabSelected.Arguments arguments) + => NextTab = arguments.Type; private void OnTabSelected(in MainTabType arguments) { diff --git a/Glamourer/Gui/Tabs/AutomationTab/AutomationSelection.cs b/Glamourer/Gui/Tabs/AutomationTab/AutomationSelection.cs index 5682895..89d520d 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/AutomationSelection.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/AutomationSelection.cs @@ -13,7 +13,7 @@ public sealed class AutomationSelection : IUiService, IDisposable public int DraggedDesignIndex = -1; - public AutoDesignSet? Set { get; private set; } + public AutoDesignSet? Set { get; private set; } public int Index { get; private set; } = -1; public StringU8 Name { get; private set; } = NoSelection; public StringU8 Incognito { get; private set; } = NoSelection; @@ -29,16 +29,16 @@ public sealed class AutomationSelection : IUiService, IDisposable _automationChanged.Unsubscribe(OnAutomationChanged); } - private void OnAutomationChanged(AutomationChanged.Type type, AutoDesignSet? set, object? data) + private void OnAutomationChanged(in AutomationChanged.Arguments arguments) { - if (set != Set) + if (arguments.Set != Set) return; - switch (type) + switch (arguments.Type) { case AutomationChanged.Type.DeletedSet: Update(null); break; - case AutomationChanged.Type.RenamedSet: Name = new StringU8(set!.Name); break; - case AutomationChanged.Type.MovedSet: Index = (((int, int))data!).Item2; break; + case AutomationChanged.Type.RenamedSet: Name = new StringU8(arguments.Set!.Name); break; + case AutomationChanged.Type.MovedSet: Index = arguments.As().NewIndex; break; } } diff --git a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs index 441ae26..4f70ab8 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/AutomationTab.cs @@ -4,7 +4,7 @@ using Luna; namespace Glamourer.Gui.Tabs.AutomationTab; -public class AutomationTab : TwoPanelLayout, ITab +public sealed class AutomationTab : TwoPanelLayout, ITab { private readonly Configuration _config; diff --git a/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs b/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs index c808ba9..f86c942 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/IdentifierDrawer.cs @@ -1,5 +1,6 @@ using Dalamud.Game.ClientState.Objects.Enums; using ImSharp; +using Luna; using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Gui; @@ -7,13 +8,13 @@ using Penumbra.String; namespace Glamourer.Gui.Tabs.AutomationTab; -public class IdentifierDrawer( +public sealed class IdentifierDrawer( ActorManager actors, DictWorld dictWorld, DictModelChara dictModelChara, DictBNpcNames bNpcNames, DictBNpc bNpc, - HumanModelList humans) + HumanModelList humans) : IUiService { private readonly WorldCombo _worldCombo = new(dictWorld); private readonly HumanNpcCombo _humanNpcCombo = new(bNpcNames, dictModelChara, humans, bNpc); diff --git a/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs b/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs index 87bf442..fc23aba 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/RandomRestrictionDrawer.cs @@ -20,21 +20,19 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable private readonly RandomDesignCombo _randomDesignCombo; private readonly AutomationSelection _selection; private readonly DesignStorage _designs; - private readonly DesignFileSystem _designFileSystem; private string _newText = string.Empty; private string? _newDefinition; private Design? _newDesign; public RandomRestrictionDrawer(AutomationChanged automationChanged, Configuration config, AutoDesignManager autoDesignManager, - RandomDesignCombo randomDesignCombo, AutomationSelection selection, DesignFileSystem designFileSystem, DesignStorage designs) + RandomDesignCombo randomDesignCombo, AutomationSelection selection, DesignStorage designs) { _automationChanged = automationChanged; _config = config; _autoDesignManager = autoDesignManager; _randomDesignCombo = randomDesignCombo; _selection = selection; - _designFileSystem = designFileSystem; _designs = designs; _automationChanged.Subscribe(OnAutomationChange, AutomationChanged.Priority.RandomRestrictionDrawer); } @@ -169,7 +167,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable { ImEx.TextFrameAligned("that contain"u8); table.NextColumn(); - var data = contains.Value.Text; + var data = contains.Value; Im.Item.SetNextWidthFull(); if (Im.Input.Text("##match"u8, ref data, "Name, Path, or Identifier Contains..."u8)) { @@ -186,7 +184,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable { ImEx.TextFrameAligned("whose path starts with"u8); table.NextColumn(); - var data = startsWith.Value.Text; + var data = startsWith.Value; Im.Item.SetNextWidthFull(); if (Im.Input.Text("##startsWith"u8, ref data, "Path Starts With..."u8)) { @@ -204,7 +202,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable ImEx.TextFrameAligned("that contain the tag"u8); table.NextColumn(); Im.Item.SetNextWidthFull(); - var data = exact.Value.Text; + var data = exact.Value; if (Im.Input.Text("##color"u8, ref data, "Contained tag..."u8)) { if (data.Length is 0) @@ -221,7 +219,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable ImEx.TextFrameAligned("that are set to the color"u8); table.NextColumn(); Im.Item.SetNextWidthFull(); - var data = exact.Value.Text; + var data = exact.Value; if (Im.Input.Text("##color"u8, ref data, "Assigned Color is..."u8)) { if (data.Length is 0) @@ -265,7 +263,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable if (!Im.Item.Hovered()) return; - var designs = predicate.Get(_designs, _designFileSystem); + var designs = predicate.Get(_designs); LookupTooltip(designs); } @@ -376,7 +374,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable private void DrawTotalPreview(IReadOnlyList list) { - var designs = IDesignPredicate.Get(list, _designs, _designFileSystem).ToList(); + var designs = IDesignPredicate.Get(list, _designs).ToList(); Im.Button(designs.Count > 0 ? $"All Restrictions Combined Match {designs.Count} Designs" : "None of the Restrictions Matches Any Designs"u8, Im.ContentRegion.Available with { Y = 0 }); @@ -414,26 +412,27 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable DrawManualInput(list); } - private void OnAutomationChange(AutomationChanged.Type type, AutoDesignSet? set, object? data) + private void OnAutomationChange(in AutomationChanged.Arguments arguments) { - if (set != _set || _set is null) + if (arguments.Set != _set || _set is null) return; - switch (type) + switch (arguments.Type) { case AutomationChanged.Type.DeletedSet: - case AutomationChanged.Type.DeletedDesign when data is int index && _designIndex == index: + case AutomationChanged.Type.DeletedDesign when arguments.As().Index == _designIndex: Close(); break; - case AutomationChanged.Type.MovedDesign when data is (int from, int to): - if (_designIndex == from) - _designIndex = to; - else if (_designIndex < from && _designIndex > to) + case AutomationChanged.Type.MovedDesign: + var data = arguments.As(); + if (_designIndex == data.OldIndex) + _designIndex = data.NewIndex; + else if (_designIndex < data.OldIndex && _designIndex > data.NewIndex) _designIndex++; - else if (_designIndex > to && _designIndex < from) + else if (_designIndex > data.NewIndex && _designIndex < data.OldIndex) _designIndex--; break; - case AutomationChanged.Type.ChangedDesign when data is (int index, IDesignStandIn _, IDesignStandIn _) && index == _designIndex: + case AutomationChanged.Type.ChangedDesign when arguments.As().DesignIndex == _designIndex: Close(); break; } diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs index e2ee40e..c6026a8 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetPanel.cs @@ -12,7 +12,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Gui.Tabs.AutomationTab; -public class SetPanel( +public sealed class SetPanel( AutoDesignManager manager, JobService jobs, ItemUnlockManager itemUnlocks, diff --git a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs index 10fb70d..64b3f5d 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/SetSelector.cs @@ -125,9 +125,9 @@ public sealed class SetSelector( base.Dispose(disposing); } - private void OnAutomationChanged(AutomationChanged.Type type, AutoDesignSet? set, object? data) + private void OnAutomationChanged(in AutomationChanged.Arguments arguments) { - switch (type) + switch (arguments.Type) { case AutomationChanged.Type.DeletedSet: case AutomationChanged.Type.AddedSet: diff --git a/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs b/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs index d6f2369..e627395 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DebugTabHeader.cs @@ -1,19 +1,18 @@ using Glamourer.Gui.Tabs.DebugTab.IpcTester; using ImSharp; using Microsoft.Extensions.DependencyInjection; -using OtterGui.Raii; using Penumbra.GameData.Gui.Debug; namespace Glamourer.Gui.Tabs.DebugTab; -public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) +public class DebugTabHeader(ReadOnlySpan label, params IGameDataDrawer[] subTrees) { - public string Label { get; } = label; + public StringU8 Label { get; } = new(label); public IReadOnlyList SubTrees { get; } = subTrees; public void Draw() { - using var h = ImRaii.CollapsingHeader(Label); + using var h = Im.Tree.HeaderId(Label); if (!h) return; @@ -32,7 +31,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) public static DebugTabHeader CreateInterop(IServiceProvider provider) => new ( - "Interop", + "Interop"u8, provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), @@ -46,7 +45,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) public static DebugTabHeader CreateGameData(IServiceProvider provider) => new ( - "Game Data", + "Game Data"u8, provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), @@ -62,7 +61,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) public static DebugTabHeader CreateDesigns(IServiceProvider provider) => new ( - "Designs", + "Designs"u8, provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), @@ -72,7 +71,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) public static DebugTabHeader CreateState(IServiceProvider provider) => new ( - "State", + "State"u8, provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService() @@ -81,7 +80,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) public static DebugTabHeader CreateUnlocks(IServiceProvider provider) => new ( - "Unlocks", + "Unlocks"u8, provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), diff --git a/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs b/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs index db45c52..79fe516 100644 --- a/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/DesignManagerPanel.cs @@ -20,7 +20,7 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy foreach (var (idx, design) in designManager.Designs.Index()) { using var id = Im.Id.Push(idx); - using var t = Im.Tree.Node(design.Name.Text); + using var t = Im.Tree.Node(design.Name); if (!t) continue; @@ -61,7 +61,7 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy if (design is Design d) { table.DrawColumn("Name"u8); - table.DrawColumn(d.Name.Text); + table.DrawColumn(d.Name); table.DrawColumn($"({d.Index})"); table.DrawColumn("Description (Hover)"u8); Im.Tooltip.OnHover(d.Description); diff --git a/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs b/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs index 40a73d1..7ca377d 100644 --- a/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/RetainedStatePanel.cs @@ -1,5 +1,5 @@ using Glamourer.State; -using OtterGui.Raii; +using ImSharp; using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Interop; @@ -17,7 +17,7 @@ public sealed class RetainedStatePanel(StateManager stateManager, ActorObjectMan { foreach (var (identifier, state) in stateManager.Where(kvp => !objectManager.ContainsKey(kvp.Key))) { - using var t = ImRaii.TreeNode(identifier.ToString()); + using var t = Im.Tree.Node($"{identifier}"); if (t) ActiveStatePanel.DrawState(stateManager, ActorData.Invalid, state); } diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs index f06cbd8..6316092 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs @@ -1,13 +1,14 @@ using Dalamud.Interface.ImGuiNotification; using Glamourer.Config; using Glamourer.Designs; +using Glamourer.Gui.Tabs.SettingsTab; using Glamourer.Services; using ImSharp; using Luna; namespace Glamourer.Gui.Tabs.DesignTab; -public class DesignDetailTab +public sealed class DesignDetailTab : IUiService { private readonly SaveService _saveService; private readonly Configuration _config; @@ -57,7 +58,7 @@ public class DesignDetailTab table.NextColumn(); var width = Im.ContentRegion.Available with { Y = 0 }; Im.Item.SetNextWidth(width.X); - if (ImEx.InputOnDeactivation.Text("##Name"u8, Selected.Name.Text, out string newName)) + if (ImEx.InputOnDeactivation.Text("##Name"u8, Selected.Name, out string newName)) _manager.Rename(Selected, newName); var identifier = Selected.Identifier.ToString(); diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs b/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs index 5b75fe3..b38a2b5 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignHeader.cs @@ -1,5 +1,4 @@ -using System.Security.AccessControl; -using Glamourer.Config; +using Glamourer.Config; using Glamourer.Designs; using Glamourer.Designs.History; using Glamourer.Events; @@ -31,29 +30,29 @@ public sealed class DesignHeader : SplitButtonHeader, IDisposable LeftButtons.AddButton(new ApplyCharacterButton(fileSystem, manager, objects, stateManager, converter), 70); LeftButtons.AddButton(new UndoButton(fileSystem, history), 60); - RightButtons.AddButton(incognito, 50); + RightButtons.AddButton(incognito, 50); RightButtons.AddButton(new LockedButton(fileSystem, manager), 100); _fileSystem.Selection.Changed += OnSelectionChanged; OnSelectionChanged(); designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignHeader); } - private void OnDesignChanged(DesignChanged.Type arg1, Design arg2, ITransaction? arg3) + private void OnDesignChanged(in DesignChanged.Arguments arguments) { - if (arg1 is not DesignChanged.Type.Renamed) + if (arguments.Type is not DesignChanged.Type.Renamed) return; - if (arg2 != _fileSystem.Selection.Selection?.Value) + if (arguments.Design != _fileSystem.Selection.Selection?.Value) return; - _header = new StringU8(arg2.Name.Text); + _header = new StringU8(arguments.Design.Name); } private void OnSelectionChanged() { if (_fileSystem.Selection.Selection?.GetValue() is { } selection) { - _header = new StringU8(selection.Name.Text); + _header = new StringU8(selection.Name); _incognito = new StringU8(selection.Incognito); } else if (_fileSystem.Selection.OrderedNodes.Count > 0) diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs index ee568f7..1cf485e 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs @@ -107,7 +107,7 @@ public class DesignLinkDrawer( using (ImGuiColor.Text.Push(color)) { Im.Cursor.FrameAlign(); - Im.Selectable(config.Ephemeral.IncognitoMode ? Selected.Incognito : Selected.Name.Text); + Im.Selectable(config.Ephemeral.IncognitoMode ? Selected.Incognito : Selected.Name); } Im.Tooltip.OnHover("Current Design"u8); @@ -137,7 +137,7 @@ public class DesignLinkDrawer( using (ImGuiColor.Text.Push(colorManager.GetColor(design))) { Im.Cursor.FrameAlign(); - Im.Selectable(config.Ephemeral.IncognitoMode ? design.Incognito : design.Name.Text); + Im.Selectable(config.Ephemeral.IncognitoMode ? design.Incognito : design.Name); } DrawDragDrop(design, order, i); diff --git a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs index 16d6f8d..ac8a326 100644 --- a/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/ModAssociationsTab.cs @@ -8,7 +8,7 @@ using Luna; namespace Glamourer.Gui.Tabs.DesignTab; -public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystem fileSystem, DesignManager manager, Configuration config) +public sealed class ModAssociationsTab(PenumbraService penumbra, DesignFileSystem fileSystem, DesignManager manager, Configuration config) : IUiService { private readonly ModCombo _modCombo = new(penumbra, fileSystem); private (Mod, ModSettings)[]? _copy; diff --git a/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs b/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs index 71e0e30..4d1c483 100644 --- a/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs +++ b/Glamourer/Gui/Tabs/DesignTab/MultiDesignPanel.cs @@ -6,11 +6,11 @@ using Luna; namespace Glamourer.Gui.Tabs.DesignTab; -public class MultiDesignPanel( +public sealed class MultiDesignPanel( DesignFileSystem fileSystem, DesignManager editor, DesignColors colors, - Configuration config) + Configuration config) : IUiService { private readonly DesignColorCombo _colorCombo = new(colors, true); @@ -109,7 +109,7 @@ public class MultiDesignPanel( { using var id = Im.Id.Push(index); var (icon, text) = node is IFileSystemData l - ? (LunaStyle.RemoveFileIcon, l.Value.Name.Text) + ? (LunaStyle.RemoveFileIcon, l.Value.Name) : (LunaStyle.RemoveFolderIcon, string.Empty); table.NextColumn(); if (ImEx.Icon.Button(icon, "Remove from selection."u8, sizeType)) @@ -152,7 +152,7 @@ public class MultiDesignPanel( ? _tag.Length is 0 ? "No tag specified."u8 : $"All designs selected already contain the tag \"{_tag}\"." - : $"Add the tag \"{_tag}\" to {_addDesigns.Count} designs as a local tag:\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name.Text))}", + : $"Add the tag \"{_tag}\" to {_addDesigns.Count} designs as a local tag:\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name))}", _addDesigns.Count is 0)) foreach (var design in _addDesigns) editor.AddTag(design, _tag); @@ -164,7 +164,7 @@ public class MultiDesignPanel( ? _tag.Length is 0 ? "No tag specified."u8 : $"No selected design contains the tag \"{_tag}\" locally." - : $"Remove the local tag \"{_tag}\" from {_removeDesigns.Count} designs:\n\n\t{string.Join("\n\t", _removeDesigns.Select(m => m.Item1.Name.Text))}", + : $"Remove the local tag \"{_tag}\" from {_removeDesigns.Count} designs:\n\n\t{string.Join("\n\t", _removeDesigns.Select(m => m.Item1.Name))}", _removeDesigns.Count is 0)) foreach (var (design, index) in _removeDesigns) editor.RemoveTag(design, index); @@ -306,7 +306,7 @@ public class MultiDesignPanel( DesignColors.AutomaticName => "Use the other button to set to automatic."u8, _ => $"All designs selected are already set to the color \"{_colorComboSelection}\".", } - : $"Set the color of {_addDesigns.Count} designs to \"{_colorComboSelection}\"\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name.Text))}", + : $"Set the color of {_addDesigns.Count} designs to \"{_colorComboSelection}\"\n\n\t{StringU8.Join("\n\t"u8, _addDesigns.Select(m => m.Name))}", _addDesigns.Count is 0)) foreach (var design in _addDesigns) editor.ChangeColor(design, _colorComboSelection!); @@ -316,7 +316,7 @@ public class MultiDesignPanel( ? $"Unset {_removeDesigns.Count} Designs" : "Unset"u8, width, _removeDesigns.Count is 0 ? "No selected design is set to a non-automatic color."u8 - : $"Set {_removeDesigns.Count} designs to use automatic color again:\n\n\t{StringU8.Join("\n\t"u8, _removeDesigns.Select(m => m.Item1.Name.Text))}", + : $"Set {_removeDesigns.Count} designs to use automatic color again:\n\n\t{StringU8.Join("\n\t"u8, _removeDesigns.Select(m => m.Item1.Name))}", _removeDesigns.Count is 0)) foreach (var (design, _) in _removeDesigns) editor.ChangeColor(design, string.Empty); diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs index 91310b7..4bbc7d6 100644 --- a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFileSystemCache.cs @@ -1,5 +1,4 @@ using Glamourer.Designs; -using Glamourer.Designs.History; using Glamourer.Events; using ImSharp; using Luna; @@ -21,9 +20,9 @@ public sealed class DesignFileSystemCache : FileSystemCache Node = node; public Vector4 Color; - public StringU8 Name = new(node.Value.Name.Text); + public StringU8 Name = new(node.Value.Name); public StringU8 Incognito = new(node.Value.Incognito); public override void Update(FileSystemCache cache, IFileSystemNode node) { var drawer = (DesignFileSystemDrawer)cache.Parent; Color = drawer.DesignColors.GetColor(Node.Value).ToVector(); - Name = new StringU8(Node.Value.Name.Text); + Name = new StringU8(Node.Value.Name); } protected override void DrawInternal(FileSystemCache cache, IFileSystemNode node) diff --git a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs index e8feaac..2fa7ef8 100644 --- a/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs +++ b/Glamourer/Gui/Tabs/DesignTab/Selector/DesignFilter.cs @@ -42,12 +42,12 @@ public sealed class DesignFilter : TokenizedFilter token.Type switch { DesignFilterTokenType.Default => cacheItem.Node.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase) - || cacheItem.Node.Value.Name.Text.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + || cacheItem.Node.Value.Name.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), DesignFilterTokenType.Mod => CheckMods(token.Needle, cacheItem), DesignFilterTokenType.Tag => CheckTags(token.Needle, cacheItem), DesignFilterTokenType.Color => cacheItem.Node.Value.Color.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), DesignFilterTokenType.Item => cacheItem.Node.Value.DesignData.ContainsName(token.Needle), - DesignFilterTokenType.Name => cacheItem.Node.Value.Name.Text.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), + DesignFilterTokenType.Name => cacheItem.Node.Value.Name.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), DesignFilterTokenType.FullContext => CheckFullContext(token.Needle, cacheItem), _ => true, }; @@ -77,7 +77,7 @@ public sealed class DesignFilter : TokenizedFilter 0) + { + if (!changeValue.HasValue) + colors.DeleteColor(changeString); + else + colors.SetColor(changeString, changeValue.Value); + } + } + + public static bool DrawColorButton(Utf8StringHandler tooltip, Rgba32 color, out Rgba32 newColor) + { + var ret = Im.Color.Editor(tooltip, ref color, ColorEditorFlags.AlphaPreviewHalf | ColorEditorFlags.NoInputs); + Im.Tooltip.OnHover(ref tooltip); + newColor = color; + return ret; + } +} diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index 7d645a9..2002604 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -11,7 +11,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Gui.Tabs.UnlocksTab; -public class UnlockOverview( +public sealed class UnlockOverview( ItemManager items, CustomizeService customizations, ItemUnlockManager itemUnlocks, @@ -21,7 +21,7 @@ public class UnlockOverview( CodeService codes, JobService jobs, FavoriteManager favorites, - PenumbraService penumbra) + PenumbraService penumbra) : IUiService { private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f); diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs index 6d16d8b..961f65f 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs @@ -16,7 +16,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Gui.Tabs.UnlocksTab; -public sealed class UnlockTable : TableBase +public sealed class UnlockTable : TableBase, IUiService { private readonly JobService _jobs; private readonly ItemManager _items; @@ -554,16 +554,17 @@ public sealed class UnlockTable : TableBase } } - private void OnItemUnlock(ObjectUnlocked.Type type, uint id, DateTimeOffset timestamp) + private void OnItemUnlock(in ObjectUnlocked.Arguments arguments) { - if (type is not ObjectUnlocked.Type.Item) + if (arguments.Type is not ObjectUnlocked.Type.Item) return; FilterDirty = true; SortDirty = true; + var id = arguments.Id; var idx = UnfilteredItems.IndexOf(i => i.Item.ItemId == id); if (idx >= 0) - UpdateSingleItem(idx, UnfilteredItems[idx] with { UnlockTimestamp = timestamp }, false); + UpdateSingleItem(idx, UnfilteredItems[idx] with { UnlockTimestamp = arguments.Timestamp }, false); } public override void Update() diff --git a/Glamourer/Interop/ChangeCustomizeService.cs b/Glamourer/Interop/ChangeCustomizeService.cs index 495d69c..06acd2f 100644 --- a/Glamourer/Interop/ChangeCustomizeService.cs +++ b/Glamourer/Interop/ChangeCustomizeService.cs @@ -2,7 +2,7 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Glamourer.Events; -using OtterGui.Classes; +using Luna; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; @@ -13,13 +13,19 @@ namespace Glamourer.Interop; /// Changes in Race, body type or Gender are probably ignored. /// This operates on draw objects, not game objects. /// -public unsafe class ChangeCustomizeService : EventWrapperRef2 +public sealed unsafe class ChangeCustomizeService : EventBase { - private readonly PenumbraReloaded _penumbraReloaded; - private readonly IGameInteropProvider _interop; + private readonly PenumbraReloaded _penumbraReloaded; + private readonly IGameInteropProvider _interop; private readonly delegate* unmanaged _original; - private readonly Post _postEvent = new(); - + private readonly Post _postEvent; + + public ref struct Arguments(Model model, ref CustomizeArray customize) + { + public readonly Model Model = model; + public ref CustomizeArray Customize = ref customize; + } + /// Check whether we in a manual customize update, in which case we need to not toggle certain flags. public static readonly InMethodChecker InUpdate = new(); @@ -30,8 +36,8 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2 action, Post.Priority priority) + public void Subscribe(InAction action, Post.Priority priority) => _postEvent.Subscribe(action, priority); - public void Unsubscribe(Action action) + public void Unsubscribe(InAction action) => _postEvent.Unsubscribe(action); - public sealed class Post() : EventWrapper(nameof(ChangeCustomizeService) + '.' + nameof(Post)) + public sealed class Post(Logger log) : EventBase(nameof(ChangeCustomizeService) + '.' + nameof(Post), log) { public enum Priority { diff --git a/Glamourer/Interop/ContextMenuService.cs b/Glamourer/Interop/ContextMenuService.cs index 4805caf..be728b5 100644 --- a/Glamourer/Interop/ContextMenuService.cs +++ b/Glamourer/Interop/ContextMenuService.cs @@ -5,13 +5,14 @@ using Glamourer.Config; using Glamourer.Designs; using Glamourer.Services; using Glamourer.State; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Interop; -public class ContextMenuService : IDisposable +public sealed class ContextMenuService : IDisposable, IRequiredService { public const int ChatLogContextItemId = 0x958; diff --git a/Glamourer/Interop/CrestService.cs b/Glamourer/Interop/CrestService.cs index 2b55f94..6e56f49 100644 --- a/Glamourer/Interop/CrestService.cs +++ b/Glamourer/Interop/CrestService.cs @@ -2,24 +2,16 @@ using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Character; -using FFXIVClientStructs.FFXIV.Client.Game.Event; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Classes; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; namespace Glamourer.Interop; -/// -/// Triggered when the crest visibility is updated on a model. -/// -/// Parameter is the model with an update. -/// Parameter is the equipment slot changed. -/// Parameter is whether the crest will be shown. -/// -/// -public sealed unsafe class CrestService : EventWrapperRef3 +/// Triggered when the crest visibility is updated on a model. +public sealed unsafe class CrestService : EventBase { public enum Priority { @@ -27,8 +19,20 @@ public sealed unsafe class CrestService : EventWrapperRef3 The game object with a crest update. + public readonly Actor Actor = actor; + + /// The equipment slot changed. + public readonly CrestFlag Slot = slot; + + /// The new value. + public ref bool Value = ref value; + } + + public CrestService(IGameInteropProvider interop, Logger log) + : base(nameof(CrestService), log) { interop.InitializeFromAttributes(this); _humanSetFreeCompanyCrestVisibleOnSlot = @@ -76,7 +80,7 @@ public sealed unsafe class CrestService : EventWrapperRef3 dragDropManager.CreateImGuiSource("DatDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".dat"), m => diff --git a/Glamourer/Interop/InventoryService.cs b/Glamourer/Interop/InventoryService.cs index 886fb58..2aa3a06 100644 --- a/Glamourer/Interop/InventoryService.cs +++ b/Glamourer/Interop/InventoryService.cs @@ -19,10 +19,12 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent) { _movedItemsEvent = movedItemsEvent; - _gearsetEvent = gearsetEvent; + _gearsetEvent = gearsetEvent; _moveItemHook = interop.HookFromAddress((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour); - _equipGearsetHook = interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetDetour); + _equipGearsetHook = + interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, + EquipGearSetDetour); _moveItemHook.Enable(); _equipGearsetHook.Enable(); @@ -36,14 +38,14 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService private delegate nint EquipGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId); - private readonly Hook _equipGearsetHook = null!; + private readonly Hook _equipGearsetHook; private nint EquipGearSetDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId) { var prior = module->CurrentGearsetIndex; - var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId); - var set = module->GetGearset((int)gearsetId); - _gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob); + var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId); + var set = module->GetGearset((int)gearsetId); + _gearsetEvent.Invoke(new EquippedGearset.Arguments(new ByteString(set->Name), (int)gearsetId, prior, glamourPlateId, set->ClassJob)); Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})"); if (ret == 0) { @@ -111,7 +113,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService Add(EquipSlot.LFinger, ref entry->Items[12]); } - _movedItemsEvent.Invoke(_itemList.ToArray()); + _movedItemsEvent.Invoke(new MovedEquipment.Arguments(_itemList.ToArray())); } return ret; @@ -130,25 +132,15 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService { var ret = _moveItemHook.Original(manager, sourceContainer, sourceSlot, targetContainer, targetSlot, unk); Glamourer.Log.Excessive($"[InventoryService] Moved {sourceContainer} {sourceSlot} {targetContainer} {targetSlot} (Returned {ret})"); - if (ret == 0) + if (ret is 0) { if (InvokeSource(sourceContainer, sourceSlot, out var source)) if (InvokeTarget(manager, targetContainer, targetSlot, out var target)) - _movedItemsEvent.Invoke(new[] - { - source, - target, - }); + _movedItemsEvent.Invoke(new MovedEquipment.Arguments(source, target)); else - _movedItemsEvent.Invoke(new[] - { - source, - }); + _movedItemsEvent.Invoke(new MovedEquipment.Arguments(source)); else if (InvokeTarget(manager, targetContainer, targetSlot, out var target)) - _movedItemsEvent.Invoke(new[] - { - target, - }); + _movedItemsEvent.Invoke(new MovedEquipment.Arguments(target)); } return ret; diff --git a/Glamourer/Interop/JobService.cs b/Glamourer/Interop/JobService.cs index 1797809..5315e35 100644 --- a/Glamourer/Interop/JobService.cs +++ b/Glamourer/Interop/JobService.cs @@ -2,6 +2,7 @@ using Dalamud.Hooking; using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Character; +using Luna; using Penumbra.GameData; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Interop; @@ -9,7 +10,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Interop; -public class JobService : IDisposable +public sealed class JobService : IDisposable, IRequiredService { private readonly nint _characterDataOffset; diff --git a/Glamourer/Interop/Material/MaterialManager.cs b/Glamourer/Interop/Material/MaterialManager.cs index 04f4d2e..e504187 100644 --- a/Glamourer/Interop/Material/MaterialManager.cs +++ b/Glamourer/Interop/Material/MaterialManager.cs @@ -37,11 +37,11 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable public void Dispose() => _event.Unsubscribe(OnPrepareColorSet); - private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHandle* material, ref StainIds stain, ref nint ret) + private void OnPrepareColorSet(in PrepareColorSet.Arguments arguments) { - var actor = _penumbra.GameObjectFromDrawObject(characterBase); - var validType = FindType(characterBase, actor, out var type); - var (slotId, materialId) = FindMaterial(characterBase, material); + var actor = _penumbra.GameObjectFromDrawObject(arguments.Model); + var validType = FindType(arguments.Model.AsCharacterBase, actor, out var type); + var (slotId, materialId) = FindMaterial(arguments.Model.AsCharacterBase, arguments.Handle); if (!validType || type is not MaterialValueIndex.DrawObjectType.Human && slotId > 0 @@ -55,19 +55,19 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable if (values.Length == 0) return; - if (!PrepareColorSet.TryGetColorTable(material, stain, out var baseColorSet)) + if (!PrepareColorSet.TryGetColorTable(arguments.Handle, arguments.Ids, out var baseColorSet)) return; var drawData = type switch { - MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, (HumanSlot)slotId), - _ => GetTempSlot((Weapon*)characterBase), + MaterialValueIndex.DrawObjectType.Human => GetTempSlot(arguments.Model.AsHuman, (HumanSlot)slotId), + _ => GetTempSlot(arguments.Model.AsWeapon), }; - var mode = PrepareColorSet.GetMode(material); + var mode = PrepareColorSet.GetMode(arguments.Handle); UpdateMaterialValues(state, values, drawData, ref baseColorSet, mode); if (MaterialService.GenerateNewColorTable(baseColorSet, out var texture)) - ret = (nint)texture; + arguments.ReturnValue = (nint)texture; } /// Update and apply the glamourer state of an actor according to the application sources when updated by the game. diff --git a/Glamourer/Interop/Material/PrepareColorSet.cs b/Glamourer/Interop/Material/PrepareColorSet.cs index 83c8b9b..eac45b3 100644 --- a/Glamourer/Interop/Material/PrepareColorSet.cs +++ b/Glamourer/Interop/Material/PrepareColorSet.cs @@ -3,7 +3,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using Luna; -using OtterGui.Classes; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.GameData.Files.MaterialStructs; @@ -13,7 +12,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Interop.Material; public sealed unsafe class PrepareColorSet - : EventWrapperPtr12Ref34, IHookService + : EventBase, IHookService { private readonly UpdateColorSets _updateColorSets; @@ -23,11 +22,19 @@ public sealed unsafe class PrepareColorSet MaterialManager = 0, } - public PrepareColorSet(HookManager hooks, UpdateColorSets updateColorSets) - : base("Prepare Color Set ") + public ref struct Arguments(Model model, MaterialResourceHandle* handle, ref StainIds ids, ref nint returnValue) + { + public readonly Model Model = model; + public readonly MaterialResourceHandle* Handle = handle; + public ref StainIds Ids = ref ids; + public ref nint ReturnValue = ref returnValue; + } + + public PrepareColorSet(HookManager hooks, UpdateColorSets updateColorSets, Logger log) + : base("Prepare Color Set", log) { _updateColorSets = updateColorSets; - _task = hooks.CreateHook(Name, Sigs.PrepareColorSet, Detour, true); + _task = hooks.CreateHook(Name, Sigs.PrepareColorSet, Detour, true); } private readonly Task> _task; @@ -64,7 +71,7 @@ public sealed unsafe class PrepareColorSet var ret = nint.Zero; var stainIds = new StainIds(stainId1, stainId2); - Invoke(characterBase.AsCharacterBase, material, ref stainIds, ref ret); + Invoke(new Arguments(characterBase.AsCharacterBase, material, ref stainIds, ref ret)); if (ret != nint.Zero) return (Texture*)ret; @@ -74,7 +81,7 @@ 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->DataSet is null || material->DataSetSize < sizeof(ColorTable.Table) || !material->HasColorTable) { table = default; return false; @@ -83,10 +90,10 @@ public sealed unsafe class PrepareColorSet var newTable = *(ColorTable.Table*)material->DataSet; if (GetDyeTable(material, out var dyeTable)) { - if (stainIds.Stain1.Id != 0) + if (stainIds.Stain1.Id is not 0) material->ReadStainingTemplate(dyeTable, stainIds.Stain1.Id, (Half*)&newTable, 0); - if (stainIds.Stain2.Id != 0) + if (stainIds.Stain2.Id is not 0) material->ReadStainingTemplate(dyeTable, stainIds.Stain2.Id, (Half*)&newTable, 1); } diff --git a/Glamourer/Interop/MetaService.cs b/Glamourer/Interop/MetaService.cs index 6225986..7baa255 100644 --- a/Glamourer/Interop/MetaService.cs +++ b/Glamourer/Interop/MetaService.cs @@ -2,11 +2,12 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Glamourer.Events; +using Luna; using Penumbra.GameData.Interop; namespace Glamourer.Interop; -public unsafe class MetaService : IDisposable +public sealed unsafe class MetaService : IDisposable, IRequiredService { private readonly HeadGearVisibilityChanged _headGearEvent; private readonly WeaponVisibilityChanged _weaponEvent; @@ -75,8 +76,8 @@ public unsafe class MetaService : IDisposable } Actor actor = drawData->OwnerObject; - var v = value == 0; - _headGearEvent.Invoke(actor, ref v); + var v = value is 0; + _headGearEvent.Invoke(new HeadGearVisibilityChanged.Arguments(actor, ref v)); value = (byte)(v ? 0 : 1); Glamourer.Log.Verbose($"[MetaService] Hide Hat triggered with 0x{(nint)drawData:X} {id} {value} for {actor.Utf8Name}."); _hideHatGearHook.Original(drawData, id, value); @@ -85,8 +86,8 @@ public unsafe class MetaService : IDisposable private void HideWeaponsDetour(DrawDataContainer* drawData, byte value) { Actor actor = drawData->OwnerObject; - var v = value == 0; - _weaponEvent.Invoke(actor, ref v); + var v = value is 0; + _weaponEvent.Invoke(new WeaponVisibilityChanged.Arguments(actor, ref v)); Glamourer.Log.Verbose($"[MetaService] Hide Weapon triggered with 0x{(nint)drawData:X} {value} for {actor.Utf8Name}."); _hideWeaponsHook.Original(drawData, (byte)(v ? 0 : 1)); } @@ -94,8 +95,8 @@ public unsafe class MetaService : IDisposable private void ToggleVisorDetour(DrawDataContainer* drawData, byte value) { Actor actor = drawData->OwnerObject; - var v = value != 0; - _visorEvent.Invoke(actor.Model, true, ref v); + var v = value is not 0; + _visorEvent.Invoke(new VisorStateChanged.Arguments(actor.Model, true, ref v)); Glamourer.Log.Verbose($"[MetaService] Toggle Visor triggered with 0x{(nint)drawData:X} {value} for {actor.Utf8Name}."); _toggleVisorHook.Original(drawData, (byte)(v ? 1 : 0)); } diff --git a/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs b/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs index ec81508..546400d 100644 --- a/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs +++ b/Glamourer/Interop/Penumbra/PenumbraAutoRedraw.cs @@ -1,7 +1,6 @@ using Dalamud.Plugin.Services; using Glamourer.Api.Enums; using Glamourer.Config; -using Glamourer.Designs.History; using Glamourer.Events; using Glamourer.State; using Luna; @@ -10,7 +9,7 @@ using Penumbra.GameData.Interop; namespace Glamourer.Interop.Penumbra; -public class PenumbraAutoRedraw : IDisposable, IRequiredService +public sealed class PenumbraAutoRedraw : IDisposable, IRequiredService { private const int WaitFrames = 5; private readonly Configuration _config; @@ -46,13 +45,13 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService } private readonly ConcurrentQueue<(ActorState, Action, int)> _actions = []; - private readonly OtterGui.Classes.ConcurrentSet _skips = []; + private readonly ConcurrentSet _skips = []; private DateTime _frame; - private void OnStateChanged(StateChangeType type, StateSource source, ActorState state, ActorData _1, ITransaction? _2) + private void OnStateChanged(in StateChanged.Arguments arguments) { - if (type is StateChangeType.Design && source.IsIpc()) - _skips.TryAdd(state); + if (arguments.Type is StateChangeType.Design && arguments.Source.IsIpc()) + _skips.TryAdd(arguments.State); } private void OnFramework(IFramework _) diff --git a/Glamourer/Interop/Penumbra/PenumbraService.cs b/Glamourer/Interop/Penumbra/PenumbraService.cs index 0438cbc..382960d 100644 --- a/Glamourer/Interop/Penumbra/PenumbraService.cs +++ b/Glamourer/Interop/Penumbra/PenumbraService.cs @@ -35,7 +35,7 @@ public readonly record struct ModSettings(Dictionary> Setti => new(); } -public class PenumbraService : IDisposable +public sealed class PenumbraService : IDisposable, IService { public const int RequiredPenumbraBreakingVersion = 5; public const int RequiredPenumbraFeatureVersion = 13; diff --git a/Glamourer/Interop/ScalingService.cs b/Glamourer/Interop/ScalingService.cs index 2a89a25..92acb83 100644 --- a/Glamourer/Interop/ScalingService.cs +++ b/Glamourer/Interop/ScalingService.cs @@ -6,6 +6,7 @@ using Penumbra.GameData; using Penumbra.GameData.Interop; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Glamourer.State; +using Luna; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; @@ -13,7 +14,7 @@ using CustomizeIndex = Dalamud.Game.ClientState.Objects.Enums.CustomizeIndex; namespace Glamourer.Interop; -public unsafe class ScalingService : IDisposable +public sealed unsafe class ScalingService : IDisposable, IRequiredService { private readonly ActorManager _actors; private readonly StateManager _state; diff --git a/Glamourer/Interop/UpdateSlotService.cs b/Glamourer/Interop/UpdateSlotService.cs index 3ef99d9..4a9dd95 100644 --- a/Glamourer/Interop/UpdateSlotService.cs +++ b/Glamourer/Interop/UpdateSlotService.cs @@ -4,6 +4,7 @@ using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Network; using Glamourer.Events; +using Luna; using Penumbra.GameData; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; @@ -12,7 +13,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Interop; -public unsafe class UpdateSlotService : IDisposable +public sealed unsafe class UpdateSlotService : IDisposable, IRequiredService { public readonly EquipSlotUpdating EquipSlotUpdatingEvent; public readonly BonusSlotUpdating BonusSlotUpdatingEvent; @@ -97,18 +98,18 @@ public unsafe class UpdateSlotService : IDisposable { var slot = slotIdx.ToEquipSlot(); var returnValue = ulong.MaxValue; - EquipSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue); + EquipSlotUpdatingEvent.Invoke(new EquipSlotUpdating.Arguments(drawObject, slot, ref *data, ref returnValue)); Glamourer.Log.Excessive($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X})."); - return returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue; + return returnValue is ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue; } private ulong FlagBonusSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data) { var slot = slotIdx.ToBonusSlot(); var returnValue = ulong.MaxValue; - BonusSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue); + BonusSlotUpdatingEvent.Invoke(new BonusSlotUpdating.Arguments(drawObject, slot, ref *data, ref returnValue)); Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X})."); - return returnValue == ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue; + return returnValue is ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue; } private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor) @@ -120,7 +121,7 @@ public unsafe class UpdateSlotService : IDisposable { var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData); var drawObject = drawDataContainer->OwnerObject->DrawObject; - GearsetDataLoadedEvent.Invoke(drawDataContainer->OwnerObject, drawObject); + GearsetDataLoadedEvent.Invoke(new GearsetDataLoaded.Arguments(drawDataContainer->OwnerObject, drawObject)); Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}"); return ret; } diff --git a/Glamourer/Interop/VieraEarService.cs b/Glamourer/Interop/VieraEarService.cs index a6afd1d..c909486 100644 --- a/Glamourer/Interop/VieraEarService.cs +++ b/Glamourer/Interop/VieraEarService.cs @@ -2,12 +2,13 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Glamourer.Events; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Interop; namespace Glamourer.Interop; -public unsafe class VieraEarService : IDisposable +public unsafe sealed class VieraEarService : IDisposable, IRequiredService { private readonly PenumbraReloaded _penumbra; private readonly IGameInteropProvider _interop; @@ -57,10 +58,10 @@ public unsafe class VieraEarService : IDisposable private void SetupVieraEarDetour(DrawDataContainer* drawData, byte value) { Actor actor = drawData->OwnerObject; - var originalOn = value != 0; + var originalOn = value is not 0; var on = originalOn; // Invoke an event that can change the requested value - Event.Invoke(actor, ref on); + Event.Invoke(new VieraEarStateChanged.Arguments(actor, ref on)); Glamourer.Log.Verbose( $"[SetVieraEarState] Invoked from game on 0x{actor.Address:X} switching to {on} (original {originalOn} from {value})."); diff --git a/Glamourer/Interop/VisorService.cs b/Glamourer/Interop/VisorService.cs index 83262e4..b13287c 100644 --- a/Glamourer/Interop/VisorService.cs +++ b/Glamourer/Interop/VisorService.cs @@ -2,12 +2,13 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Glamourer.Events; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; namespace Glamourer.Interop; -public class VisorService : IDisposable +public sealed class VisorService : IDisposable, IRequiredService { private readonly PenumbraReloaded _penumbra; private readonly IGameInteropProvider _interop; @@ -58,11 +59,11 @@ public class VisorService : IDisposable private void SetupVisorDetour(nint human, ushort modelId, byte value) { - var originalOn = value != 0; + var originalOn = value is not 0; var on = originalOn; // Invoke an event that can change the requested value // and also control whether the function should be called at all. - Event.Invoke(human, false, ref on); + Event.Invoke(new VisorStateChanged.Arguments(human, false, ref on)); Glamourer.Log.Verbose( $"[SetVisorState] Invoked from game on 0x{human:X} switching to {on} (original {originalOn} from {value} with {modelId})."); diff --git a/Glamourer/Interop/WeaponService.cs b/Glamourer/Interop/WeaponService.cs index 54f318b..a5e1ee1 100644 --- a/Glamourer/Interop/WeaponService.cs +++ b/Glamourer/Interop/WeaponService.cs @@ -2,13 +2,14 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; using Glamourer.Events; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; namespace Glamourer.Interop; -public unsafe class WeaponService : IDisposable +public sealed unsafe class WeaponService : IDisposable, IRequiredService { private readonly WeaponLoading _event; private readonly ThreadLocal _inUpdate = new(() => false); @@ -59,17 +60,17 @@ public unsafe class WeaponService : IDisposable var tmpWeapon = weapon; // First call the regular function. if (equipSlot is not EquipSlot.Unknown) - _event.Invoke(actor, equipSlot, ref tmpWeapon); + _event.Invoke(new WeaponLoading.Arguments(actor, equipSlot, ref tmpWeapon)); // Sage hack for weapons appearing in animations? // Check for weapon value 0 for certain cases (e.g. carbuncles transforming to humans) because that breaks some stuff (weapon hiding?) otherwise. - else if (weaponValue == actor.GetMainhand().Value && weaponValue != 0) - _event.Invoke(actor, EquipSlot.MainHand, ref tmpWeapon); + else if (weaponValue == actor.GetMainhand().Value && weaponValue is not 0) + _event.Invoke(new WeaponLoading.Arguments(actor, EquipSlot.MainHand, ref tmpWeapon)); _loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4, unk5); if (tmpWeapon.Value != weapon.Value) { - if (tmpWeapon.Skeleton.Id == 0) + if (tmpWeapon.Skeleton.Id is 0) tmpWeapon.Stains = StainIds.None; _loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4, unk5); } diff --git a/Glamourer/Services/BackupService.cs b/Glamourer/Services/BackupService.cs index 1103b59..fdd3f90 100644 --- a/Glamourer/Services/BackupService.cs +++ b/Glamourer/Services/BackupService.cs @@ -2,7 +2,7 @@ using Luna; namespace Glamourer.Services; -public class BackupService(Logger log, FilenameService provider) : BaseBackupService(log, provider) +public sealed class BackupService(Logger log, FilenameService provider) : BaseBackupService(log, provider) { /// Collect all relevant files for glamourer configuration. private static IReadOnlyList GlamourerFiles(FilenameService fileNames) diff --git a/Glamourer/Services/CodeService.cs b/Glamourer/Services/CodeService.cs index 750c335..0616fd4 100644 --- a/Glamourer/Services/CodeService.cs +++ b/Glamourer/Services/CodeService.cs @@ -1,10 +1,11 @@ using Glamourer.Config; using ImSharp; +using Luna; using Penumbra.GameData.Enums; namespace Glamourer.Services; -public class CodeService +public sealed class CodeService : IService { private readonly Configuration _config; private readonly SHA256 _hasher = SHA256.Create(); diff --git a/Glamourer/Services/CollectionOverrideService.cs b/Glamourer/Services/CollectionOverrideService.cs index 164e4b1..b754fa5 100644 --- a/Glamourer/Services/CollectionOverrideService.cs +++ b/Glamourer/Services/CollectionOverrideService.cs @@ -1,12 +1,9 @@ -using Dalamud.Interface.ImGuiNotification; using Glamourer.Interop.Penumbra; using Luna; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OtterGui.Extensions; using Penumbra.GameData.Actors; using Penumbra.GameData.Interop; -using Extensions = OtterGui.Filesystem.Extensions; using Notification = Luna.Notification; namespace Glamourer.Services; @@ -31,7 +28,7 @@ public sealed class CollectionOverrideService : IService, ISavable if (!identifier.IsValid) identifier = _actors.FromObject(actor.AsObject, out _, true, true, true); - return ArrayExtensions.FindFirst(_overrides, p => p.Actor.Matches(identifier), out var ret) + return _overrides.FindFirst(p => p.Actor.Matches(identifier), out var ret) ? (ret.CollectionId, ret.DisplayName, true) : (_penumbra.GetActorCollection(actor, out var name), name, false); } @@ -78,7 +75,7 @@ public sealed class CollectionOverrideService : IService, ISavable if (idx < 0 || idx >= _overrides.Count) return; - if (newCollectionId == Guid.Empty || newDisplayName.Length == 0) + if (newCollectionId == Guid.Empty || newDisplayName.Length is 0) return; var current = _overrides[idx]; @@ -106,7 +103,7 @@ public sealed class CollectionOverrideService : IService, ISavable public void MoveOverride(int idxFrom, int idxTo) { - if (!Extensions.Move(_overrides, idxFrom, idxTo)) + if (!_overrides.Move(idxFrom, idxTo)) return; Glamourer.Log.Debug($"Moved collection override {idxFrom + 1} to {idxTo + 1}."); @@ -192,7 +189,7 @@ public sealed class CollectionOverrideService : IService, ISavable public void Save(StreamWriter writer) { - var jObj = new JObject() + var jObj = new JObject { ["Version"] = Version, ["Overrides"] = SerializeOverrides(), diff --git a/Glamourer/Services/CommandService.cs b/Glamourer/Services/CommandService.cs index 019424c..575ea6f 100644 --- a/Glamourer/Services/CommandService.cs +++ b/Glamourer/Services/CommandService.cs @@ -16,7 +16,6 @@ using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; -using SeStringBuilderExtensions = OtterGui.Classes.SeStringBuilderExtensions; namespace Glamourer.Services; diff --git a/Glamourer/Services/ConfigMigrationService.cs b/Glamourer/Services/ConfigMigrationService.cs index 2fefe15..1c6dcf6 100644 --- a/Glamourer/Services/ConfigMigrationService.cs +++ b/Glamourer/Services/ConfigMigrationService.cs @@ -2,11 +2,12 @@ using Glamourer.Config; using Glamourer.Gui; using ImSharp; +using Luna; using Newtonsoft.Json.Linq; namespace Glamourer.Services; -public class ConfigMigrationService(SaveService saveService, FixedDesignMigrator fixedDesignMigrator, BackupService backupService) +public sealed class ConfigMigrationService(SaveService saveService, FixedDesignMigrator fixedDesignMigrator, BackupService backupService) : IRequiredService { private Configuration _config = null!; private JObject _data = null!; diff --git a/Glamourer/Services/DesignResolver.cs b/Glamourer/Services/DesignResolver.cs index 9849ad9..9ce09b0 100644 --- a/Glamourer/Services/DesignResolver.cs +++ b/Glamourer/Services/DesignResolver.cs @@ -142,11 +142,10 @@ public class DesignResolver( } else { - var lower = argument.ToLowerInvariant(); // Search for design by name and partial identifier. - design = manager.Designs.FirstOrDefault(MatchNameAndIdentifier(lower)); + design = manager.Designs.FirstOrDefault(MatchNameAndIdentifier(argument)); // Search for design by path, if nothing was found. - if (design is null && designFileSystem.Find(lower, out var child) && child is IFileSystemData leaf) + if (design is null && designFileSystem.Find(argument, out var child) && child is IFileSystemData leaf) design = leaf.Value; } @@ -159,13 +158,13 @@ public class DesignResolver( } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Func MatchNameAndIdentifier(string lower) + private static Func MatchNameAndIdentifier(string text) { // Check for names and identifiers, prefer names - if (lower.Length > 3) - return d => d.Name.Lower == lower || d.Identifier.ToString().StartsWith(lower); + if (text.Length > 3) + return d => string.Equals(d.Name, text, StringComparison.OrdinalIgnoreCase) || d.Identifier.ToString().StartsWith(text, StringComparison.OrdinalIgnoreCase); // Check only for names. - return d => d.Name.Lower == lower; + return d => string.Equals(d.Name, text, StringComparison.OrdinalIgnoreCase); } } diff --git a/Glamourer/Services/FilenameService.cs b/Glamourer/Services/FilenameService.cs index 831c6ad..e59d97c 100644 --- a/Glamourer/Services/FilenameService.cs +++ b/Glamourer/Services/FilenameService.cs @@ -4,7 +4,7 @@ using Luna; namespace Glamourer.Services; -public class FilenameService(IDalamudPluginInterface pi) : BaseFilePathProvider(pi) +public sealed class FilenameService(IDalamudPluginInterface pi) : BaseFilePathProvider(pi) { public readonly string MigrationDesignFileSystem = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json"); public readonly string MigrationDesignFile = Path.Combine(pi.ConfigDirectory.FullName, "Designs.json"); diff --git a/Glamourer/Services/ItemManager.cs b/Glamourer/Services/ItemManager.cs index 4f0a83d..b81e1b1 100644 --- a/Glamourer/Services/ItemManager.cs +++ b/Glamourer/Services/ItemManager.cs @@ -2,6 +2,7 @@ using Dalamud.Plugin.Services; using Glamourer.Config; using Lumina.Excel; using Lumina.Excel.Sheets; +using Luna; using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; @@ -10,7 +11,7 @@ using Race = Penumbra.GameData.Enums.Race; namespace Glamourer.Services; -public class ItemManager +public sealed class ItemManager : IService { public const string Nothing = EquipItem.Nothing; public const string SmallClothesNpc = "Smallclothes (NPC)"; diff --git a/Glamourer/Services/SaveService.cs b/Glamourer/Services/SaveService.cs index 1ccaaea..ac0ad8f 100644 --- a/Glamourer/Services/SaveService.cs +++ b/Glamourer/Services/SaveService.cs @@ -8,4 +8,4 @@ namespace Glamourer.Services; public interface ISavable : ISavable; public sealed class SaveService(Logger log, FrameworkManager framework, FilenameService fileNames) - : BaseSaveService(log, framework, fileNames); + : BaseSaveService(log, framework, fileNames), IService; diff --git a/Glamourer/Services/ServiceManager.cs b/Glamourer/Services/ServiceManager.cs index 6b36d23..e06cdb4 100644 --- a/Glamourer/Services/ServiceManager.cs +++ b/Glamourer/Services/ServiceManager.cs @@ -1,37 +1,12 @@ using Dalamud.Plugin; using Glamourer.Api; using Glamourer.Api.Api; -using Glamourer.Automation; -using Glamourer.Config; -using Glamourer.Designs; -using Glamourer.Events; -using Glamourer.Gui; -using Glamourer.Gui.Customization; -using Glamourer.Gui.Equipment; -using Glamourer.Gui.Tabs; -using Glamourer.Gui.Tabs.ActorTab; -using Glamourer.Gui.Tabs.AutomationTab; -using Glamourer.Gui.Tabs.DebugTab; -using Glamourer.Gui.Tabs.DesignTab; -using Glamourer.Gui.Tabs.NpcTab; -using Glamourer.Gui.Tabs.SettingsTab; -using Glamourer.Gui.Tabs.UnlocksTab; -using Glamourer.Interop; using Glamourer.Interop.Penumbra; -using Glamourer.State; -using Glamourer.Unlocks; using Luna; using Microsoft.Extensions.DependencyInjection; -using OtterGui.Classes; -using OtterGui.Raii; using Penumbra.GameData.Actors; -using Penumbra.GameData.Data; -using Penumbra.GameData.DataContainers; using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; -using FrameworkManager = OtterGui.Classes.FrameworkManager; -using Logger = OtterGui.Log.Logger; -using MessageService = Luna.MessageService; namespace Glamourer.Services; @@ -39,146 +14,19 @@ public static class StaticServiceManager { public static ServiceManager CreateProvider(IDalamudPluginInterface pi, Logger log, Glamourer glamourer) { - EventWrapperBase.ChangeLogger(log); - var services = new ServiceManager(new Luna.Logger("Glamourer")) + var services = new ServiceManager(log) .AddExistingService(log) - .AddMeta() - .AddInterop() - .AddEvents() - .AddData() - .AddDesigns() - .AddState() - .AddUi() + .AddSingleton() + .AddSingleton() + .AddSingleton(p => new CutsceneResolver(p.GetRequiredService().CutsceneParent)) .AddExistingService(glamourer); - DalamudServices.AddServices(services, pi); services.AddIServices(typeof(EquipItem).Assembly); services.AddIServices(typeof(Glamourer).Assembly); - services.AddIServices(typeof(ImRaii).Assembly); services.AddIServices(typeof(ServiceManager).Assembly); - services.AddIServices(typeof(Glamourer).Assembly); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(p => p.GetRequiredService()); + DalamudServices.AddServices(services, pi); + services.BuildProvider(); return services; } - - private static ServiceManager AddMeta(this ServiceManager services) - => services.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); - - private static ServiceManager AddEvents(this ServiceManager services) - => services.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); - - private static ServiceManager AddData(this ServiceManager services) - => services.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); - - private static ServiceManager AddInterop(this ServiceManager services) - => services.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(p => new CutsceneResolver(p.GetRequiredService().CutsceneParent)) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); - - private static ServiceManager AddDesigns(this ServiceManager services) - => services.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); - - private static ServiceManager AddState(this ServiceManager services) - => services.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); - - private static ServiceManager AddUi(this ServiceManager services) - => services.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(); } diff --git a/Glamourer/Services/TextureService.cs b/Glamourer/Services/TextureService.cs index 635584f..ba11c9a 100644 --- a/Glamourer/Services/TextureService.cs +++ b/Glamourer/Services/TextureService.cs @@ -1,41 +1,56 @@ using Dalamud.Interface; +using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Plugin.Services; using ImSharp; using Luna; -using OtterGui.Classes; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; namespace Glamourer.Services; -public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManager, ITextureProvider textureProvider) - : TextureCache(dataManager, textureProvider), IDisposable +public sealed class TextureService(IUiBuilder uiBuilder, ITextureProvider textureProvider) + : IDisposable, IUiService { private readonly IDalamudTextureWrap?[] _slotIcons = CreateSlotIcons(uiBuilder); + public IDalamudTextureWrap? LoadIcon(uint iconId) + { + var icon = textureProvider.GetFromGameIcon(new GameIconLookup(iconId)); + if (!icon.TryGetWrap(out var wrap, out _)) + return null; + + return wrap; + } + + public bool TryLoadIcon(uint iconId, [NotNullWhen(true)] out IDalamudTextureWrap? wrap) + { + wrap = LoadIcon(iconId); + return wrap is not null; + } + public (ImTextureId, Vector2, bool) GetIcon(EquipItem item, EquipSlot slot) { - if (item.IconId.Id != 0 && TryLoadIcon(item.IconId.Id, out var ret)) + if (item.IconId.Id is not 0 && TryLoadIcon(item.IconId.Id, out var ret)) return (ret.Id, new Vector2(ret.Width, ret.Height), false); var idx = slot.ToIndex(); - return idx < 12 && _slotIcons[idx] != null + return idx < 12 && _slotIcons[idx] is not null ? (_slotIcons[idx]!.Id, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true) : (default, Vector2.Zero, true); } public (ImTextureId, Vector2, bool) GetIcon(EquipItem item, BonusItemFlag slot) { - if (item.IconId.Id != 0 && TryLoadIcon(item.IconId.Id, out var ret)) + if (item.IconId.Id is not 0 && TryLoadIcon(item.IconId.Id, out var ret)) return (ret.Id, new Vector2(ret.Width, ret.Height), false); var idx = slot.ToIndex(); - if (idx == uint.MaxValue) + if (idx is uint.MaxValue) return (default, Vector2.Zero, true); idx += 12; - return idx < 13 && _slotIcons[idx] != null + return idx < 13 && _slotIcons[idx] is not null ? (_slotIcons[idx]!.Id, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true) : (default, Vector2.Zero, true); } @@ -72,7 +87,7 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage SetIcon(EquipSlot.RFinger, 28); SetIcon(EquipSlot.MainHand, 17); SetIcon(EquipSlot.OffHand, 18); - Set(BonusItemFlag.Glasses.ToName(), (int) BonusItemFlag.Glasses.ToIndex() + 12, 55); + Set(BonusItemFlag.Glasses.ToName(), (int)BonusItemFlag.Glasses.ToIndex() + 12, 55); ret[EquipSlot.LFinger.ToIndex()] = ret[EquipSlot.RFinger.ToIndex()]; return ret; diff --git a/Glamourer/State/FunModule.cs b/Glamourer/State/FunModule.cs index 26b6610..e129376 100644 --- a/Glamourer/State/FunModule.cs +++ b/Glamourer/State/FunModule.cs @@ -14,7 +14,7 @@ using Luna; namespace Glamourer.State; -public unsafe class FunModule : IDisposable +public sealed unsafe class FunModule : IDisposable, IRequiredService { public enum FestivalType { diff --git a/Glamourer/State/InternalStateEditor.cs b/Glamourer/State/InternalStateEditor.cs index eb3403d..72520ec 100644 --- a/Glamourer/State/InternalStateEditor.cs +++ b/Glamourer/State/InternalStateEditor.cs @@ -5,6 +5,7 @@ using Glamourer.GameData; using Glamourer.Interop.Material; using Glamourer.Services; using ImSharp; +using Luna; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -16,7 +17,7 @@ public class InternalStateEditor( HumanModelList humans, ItemManager items, GPoseService gPose, - ICondition condition) + ICondition condition) : IService { /// Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. /// We currently only allow changing things to humans, not humans to monsters. @@ -168,7 +169,7 @@ public class InternalStateEditor( public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainIds stains, StateSource source, out EquipItem oldItem, out StainIds oldStains, uint key = 0) { - oldItem = state.ModelData.Item(slot); + oldItem = state.ModelData.Item(slot); oldStains = state.ModelData.Stain(slot); if (!state.CanUnlock(key)) return false; diff --git a/Glamourer/State/StateApplier.cs b/Glamourer/State/StateApplier.cs index 9800445..75992f2 100644 --- a/Glamourer/State/StateApplier.cs +++ b/Glamourer/State/StateApplier.cs @@ -5,6 +5,7 @@ using Glamourer.Interop.Material; using Glamourer.Interop.Penumbra; using Glamourer.Interop.Structs; using Glamourer.Services; +using Luna; using Penumbra.Api.Enums; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; @@ -17,22 +18,22 @@ namespace Glamourer.State; /// It handles applying those changes as well as redrawing the actor if necessary. /// public class StateApplier( - UpdateSlotService _updateSlot, - VisorService _visor, - WeaponService _weapon, - ChangeCustomizeService _changeCustomize, - ItemManager _items, - PenumbraService _penumbra, - MetaService _metaService, - ActorObjectManager _objects, - CrestService _crests, - DirectXService _directX) + UpdateSlotService updateSlot, + VisorService visor, + WeaponService weaponService, + ChangeCustomizeService changeCustomize, + ItemManager items, + PenumbraService penumbra, + MetaService metaService, + ActorObjectManager objects, + CrestService crests, + DirectXService directX) : IRequiredService { /// Simply force a redraw regardless of conditions. public void ForceRedraw(ActorData data) { foreach (var actor in data.Objects) - _penumbra.RedrawObject(actor, RedrawType.Redraw); + penumbra.RedrawObject(actor, RedrawType.Redraw); } /// @@ -60,17 +61,17 @@ public class StateApplier( var flags = CustomizeArray.Compare(mdl.GetCustomize(), customize); if (!flags.RequiresRedraw() || !mdl.IsHuman) { - _changeCustomize.UpdateCustomize(mdl, customize); + changeCustomize.UpdateCustomize(mdl, customize); } - else if (data.Objects.Count > 1 && _objects.IsInGPose && !actor.IsGPoseOrCutscene) + else if (data.Objects.Count > 1 && objects.IsInGPose && !actor.IsGPoseOrCutscene) { var mdlCustomize = (CustomizeArray*)&mdl.AsHuman->Customize; *mdlCustomize = customize; - _penumbra.RedrawObject(actor, RedrawType.AfterGPose); + penumbra.RedrawObject(actor, RedrawType.AfterGPose); } else { - _penumbra.RedrawObject(actor, RedrawType.Redraw); + penumbra.RedrawObject(actor, RedrawType.Redraw); } } } @@ -104,12 +105,12 @@ public class StateApplier( if (checkRestrictions) { var customize = mdl.GetCustomize(); - var (_, resolvedItem) = _items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender); - _updateSlot.UpdateEquipSlot(actor.Model, slot, resolvedItem); + var (_, resolvedItem) = items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender); + updateSlot.UpdateEquipSlot(actor.Model, slot, resolvedItem); } else { - _updateSlot.UpdateEquipSlot(actor.Model, slot, armor); + updateSlot.UpdateEquipSlot(actor.Model, slot, armor); } } } @@ -134,7 +135,7 @@ public class StateApplier( if (!mdl.IsHuman) continue; - _updateSlot.UpdateBonusSlot(actor.Model, slot, item); + updateSlot.UpdateBonusSlot(actor.Model, slot, item); } } @@ -164,15 +165,15 @@ public class StateApplier( { case < 10: foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) - _updateSlot.UpdateStain(actor.Model, slot, stains); + updateSlot.UpdateStain(actor.Model, slot, stains); break; case 10: foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) - _weapon.LoadStain(actor, EquipSlot.MainHand, stains); + weaponService.LoadStain(actor, EquipSlot.MainHand, stains); break; case 11: foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) - _weapon.LoadStain(actor, EquipSlot.OffHand, stains); + weaponService.LoadStain(actor, EquipSlot.OffHand, stains); break; } } @@ -217,7 +218,7 @@ public class StateApplier( { var slot = weapon.Type.ValidOffhand() == FullEquipType.Unknown ? EquipSlot.BothHand : EquipSlot.MainHand; foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) - _weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stains)); + weaponService.LoadWeapon(actor, slot, weapon.Weapon().With(stains)); } /// Apply a weapon to the offhand. @@ -225,7 +226,7 @@ public class StateApplier( { stains = weapon.PrimaryId.Id == 0 ? StainIds.None : stains; foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) - _weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stains)); + weaponService.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stains)); } /// Change a meta state. @@ -242,24 +243,24 @@ public class StateApplier( case MetaIndex.HatState: { foreach (var actor in data.Objects.Where(a => a.IsCharacter)) - _metaService.SetHatState(actor, value); + metaService.SetHatState(actor, value); return; } case MetaIndex.WeaponState: { // Only apply to the GPose character because otherwise we get some weird incompatibility when leaving GPose. - if (_objects.IsInGPose) + if (objects.IsInGPose) foreach (var actor in data.Objects.Where(a => a.IsGPoseOrCutscene)) - _metaService.SetWeaponState(actor, value); + metaService.SetWeaponState(actor, value); else foreach (var actor in data.Objects.Where(a => a.IsCharacter)) - _metaService.SetWeaponState(actor, value); + metaService.SetWeaponState(actor, value); return; } case MetaIndex.VisorState: { foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) - _visor.SetVisorState(actor.Model, value); + visor.SetVisorState(actor.Model, value); return; } case MetaIndex.EarState: @@ -286,7 +287,7 @@ public class StateApplier( public void ChangeCrests(ActorData data, CrestFlag flags) { foreach (var actor in data.Objects.Where(a => a.IsCharacter)) - _crests.UpdateCrests(actor, flags); + crests.UpdateCrests(actor, flags); } /// @@ -337,7 +338,7 @@ public class StateApplier( value.Model.Apply(ref baseTable[MaterialValueIndex.FromKey(index).RowIndex], mode); } - _directX.ReplaceColorTable(texture, baseTable); + directX.ReplaceColorTable(texture, baseTable); } } @@ -370,7 +371,7 @@ public class StateApplier( foreach (var (key, value) in values) value.Model.Apply(ref table[key.RowIndex], mode); - _directX.ReplaceColorTable(texture, table); + directX.ReplaceColorTable(texture, table); } } } @@ -421,5 +422,5 @@ public class StateApplier( } public ActorData GetData(ActorState state) - => _objects.TryGetValue(state.Identifier, out var data) ? data : ActorData.Invalid; + => objects.TryGetValue(state.Identifier, out var data) ? data : ActorData.Invalid; } diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index 1bfb66a..a325f15 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -43,8 +43,8 @@ public class StateEditor( var actors = Applier.ForceRedraw(state, source.RequiresChange()); 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); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Model, source, state, actors)); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.ModelChange, actors)); } /// @@ -57,7 +57,7 @@ public class StateEditor( var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set {idx.ToName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Customize, settings.Source, state, actors, new CustomizeTransaction(idx, old, value)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Customize, settings.Source, state, actors, new CustomizeTransaction(idx, old, value))); } /// @@ -70,8 +70,8 @@ public class StateEditor( var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.EntireCustomize, settings.Source, state, actors, - new EntireCustomizeTransaction(applied, old, customizeInput)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.EntireCustomize, settings.Source, state, actors, + new EntireCustomizeTransaction(applied, old, customizeInput))); } /// @@ -95,21 +95,21 @@ public class StateEditor( if (type is StateChangeType.Equip) { - StateChanged.Invoke(type, settings.Source, state, actors, new EquipTransaction(slot, old, item)); + StateChanged.Invoke(new StateChanged.Arguments(type, settings.Source, state, actors, new EquipTransaction(slot, old, item))); } else if (slot is EquipSlot.MainHand) { var oldOff = state.ModelData.Item(EquipSlot.OffHand); var oldGauntlets = state.ModelData.Item(EquipSlot.Hands); - StateChanged.Invoke(type, settings.Source, state, actors, - new WeaponTransaction(old, oldOff, oldGauntlets, item, oldOff, oldGauntlets)); + StateChanged.Invoke(new StateChanged.Arguments(type, settings.Source, state, actors, + new WeaponTransaction(old, oldOff, oldGauntlets, item, oldOff, oldGauntlets))); } else { var oldMain = state.ModelData.Item(EquipSlot.MainHand); var oldGauntlets = state.ModelData.Item(EquipSlot.Hands); - StateChanged.Invoke(type, settings.Source, state, actors, - new WeaponTransaction(oldMain, old, oldGauntlets, oldMain, item, oldGauntlets)); + StateChanged.Invoke(new StateChanged.Arguments(type, settings.Source, state, actors, + new WeaponTransaction(oldMain, old, oldGauntlets, oldMain, item, oldGauntlets))); } } @@ -122,7 +122,7 @@ public class StateEditor( var actors = Applier.ChangeBonusItem(state, slot, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.BonusItem, settings.Source, state, actors, new BonusItemTransaction(slot, old, item)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.BonusItem, settings.Source, state, actors, new BonusItemTransaction(slot, old, item))); } /// @@ -157,24 +157,24 @@ public class StateEditor( $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stain from {oldStains} to {stains!.Value}. [Affecting {actors.ToLazyString("nothing")}.]"); if (type is StateChangeType.Equip) { - StateChanged.Invoke(type, settings.Source, state, actors, new EquipTransaction(slot, old, item!.Value)); + StateChanged.Invoke(new StateChanged.Arguments(type, settings.Source, state, actors, new EquipTransaction(slot, old, item!.Value))); } else if (slot is EquipSlot.MainHand) { var oldOff = state.ModelData.Item(EquipSlot.OffHand); var oldGauntlets = state.ModelData.Item(EquipSlot.Hands); - StateChanged.Invoke(type, settings.Source, state, actors, - new WeaponTransaction(old, oldOff, oldGauntlets, item!.Value, oldOff, oldGauntlets)); + StateChanged.Invoke(new StateChanged.Arguments(type, settings.Source, state, actors, + new WeaponTransaction(old, oldOff, oldGauntlets, item!.Value, oldOff, oldGauntlets))); } else { var oldMain = state.ModelData.Item(EquipSlot.MainHand); var oldGauntlets = state.ModelData.Item(EquipSlot.Hands); - StateChanged.Invoke(type, settings.Source, state, actors, - new WeaponTransaction(oldMain, old, oldGauntlets, oldMain, item!.Value, oldGauntlets)); + StateChanged.Invoke(new StateChanged.Arguments(type, settings.Source, state, actors, + new WeaponTransaction(oldMain, old, oldGauntlets, oldMain, item!.Value, oldGauntlets))); } - StateChanged.Invoke(StateChangeType.Stains, settings.Source, state, actors, new StainTransaction(slot, oldStains, stains!.Value)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Stains, settings.Source, state, actors, new StainTransaction(slot, oldStains, stains!.Value))); } /// @@ -187,7 +187,7 @@ public class StateEditor( var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old} to {stains}. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Stains, settings.Source, state, actors, new StainTransaction(slot, old, stains)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Stains, settings.Source, state, actors, new StainTransaction(slot, old, stains))); } /// @@ -200,7 +200,7 @@ public class StateEditor( var actors = Applier.ChangeCrests(state, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set {slot.ToLabel()} crest in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Crest, settings.Source, state, actors, new CrestTransaction(slot, old, crest)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Crest, settings.Source, state, actors, new CrestTransaction(slot, old, crest))); } /// @@ -218,7 +218,7 @@ public class StateEditor( var actors = Applier.ChangeParameters(state, flag, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set {flag} in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Parameter, settings.Source, state, actors, new ParameterTransaction(flag, old, @new)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Parameter, settings.Source, state, actors, new ParameterTransaction(flag, old, @new))); } public void ChangeMaterialValue(object data, MaterialValueIndex index, in MaterialValueState newValue, ApplySettings settings) @@ -230,8 +230,8 @@ public class StateEditor( var actors = Applier.ChangeMaterialValue(state, index, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set material value in state {state.Identifier.Incognito(null)} from {oldValue} to {newValue.Game}. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.MaterialValue, settings.Source, state, actors, - new MaterialTransaction(index, oldValue, newValue.Game)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.MaterialValue, settings.Source, state, actors, + new MaterialTransaction(index, oldValue, newValue.Game))); } public void ResetMaterialValue(object data, MaterialValueIndex index, ApplySettings settings) @@ -243,7 +243,7 @@ public class StateEditor( var actors = Applier.ChangeMaterialValue(state, index, true); Glamourer.Log.Verbose( $"Reset material value in state {state.Identifier.Incognito(null)} to game value. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.MaterialValue, settings.Source, state, actors, new MaterialTransaction(index, null, null)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.MaterialValue, settings.Source, state, actors, new MaterialTransaction(index, null, null))); } /// @@ -256,7 +256,7 @@ public class StateEditor( var actors = Applier.ChangeMetaState(state, index, settings.Source.RequiresChange()); Glamourer.Log.Verbose( $"Set {index.ToName()} in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]"); - StateChanged.Invoke(StateChangeType.Other, settings.Source, state, actors, new MetaTransaction(index, old, value)); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Other, settings.Source, state, actors, new MetaTransaction(index, old, value))); } /// @@ -421,9 +421,9 @@ 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 + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Design, state.Sources[MetaIndex.Wetness], state, actors)); // FIXME: maybe later if (settings.IsFinal) - StateFinalized.Invoke(StateFinalizationType.DesignApplied, actors); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.DesignApplied, actors)); return; diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 6856d4e..0991e62 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -16,6 +16,7 @@ using Glamourer.Designs; using Penumbra.GameData.Interop; using Glamourer.Api.Enums; using Glamourer.Config; +using Luna; namespace Glamourer.State; @@ -24,7 +25,7 @@ namespace Glamourer.State; /// it always updates the base state for existing states, /// and either discards the changes or updates the model state too. /// -public class StateListener : IDisposable +public sealed class StateListener : IDisposable, IRequiredService { private readonly Configuration _config; private readonly ActorManager _actors; @@ -156,12 +157,12 @@ public class StateListener : IDisposable ProtectRestrictedGear(equipDataPtr, customize.Race, customize.Gender); } - private void OnCustomizeChange(Model model, ref CustomizeArray customize) + private void OnCustomizeChange(in ChangeCustomizeService.Arguments arguments) { - if (!model.IsHuman) + if (!arguments.Model.IsHuman) return; - var actor = _penumbra.GameObjectFromDrawObject(model); + var actor = _penumbra.GameObjectFromDrawObject(arguments.Model); if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) return; @@ -169,7 +170,7 @@ public class StateListener : IDisposable || !_manager.TryGetValue(identifier, out _customizeState)) return; - UpdateCustomize(actor, _customizeState, ref customize, false); + UpdateCustomize(actor, _customizeState, ref arguments.Customize, false); } private void UpdateCustomize(Actor actor, ActorState state, ref CustomizeArray customize, bool checkTransform) @@ -215,9 +216,9 @@ public class StateListener : IDisposable /// A draw model loads a new equipment piece. /// Update base data, apply or update model data, and protect against restricted gear. /// - private void OnEquipSlotUpdating(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue) + private void OnEquipSlotUpdating(in EquipSlotUpdating.Arguments arguments) { - var actor = _penumbra.GameObjectFromDrawObject(model); + var actor = _penumbra.GameObjectFromDrawObject(arguments.Model); if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) return; @@ -228,66 +229,66 @@ public class StateListener : IDisposable if (actor.Identifier(_actors, out var identifier) && _manager.TryGetValue(identifier, out var state)) { - HandleEquipSlot(actor, state, slot, ref armor); - locked = state.Sources[slot, false] is StateSource.IpcFixed; + HandleEquipSlot(actor, state, arguments.Slot, ref arguments.Armor); + locked = state.Sources[arguments.Slot, false] is StateSource.IpcFixed; } - _funModule.ApplyFunToSlot(actor, ref armor, slot); + _funModule.ApplyFunToSlot(actor, ref arguments.Armor, arguments.Slot); if (!_config.UseRestrictedGearProtection || locked) return; - var customize = model.GetCustomize(); - (_, armor) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender); + var customize = arguments.Model.GetCustomize(); + (_, arguments.Armor) = _items.RestrictedGear.ResolveRestricted(arguments.Armor, arguments.Slot, customize.Race, customize.Gender); } - private void OnBonusSlotUpdating(Model model, BonusItemFlag slot, ref CharacterArmor item, ref ulong returnValue) + private void OnBonusSlotUpdating(in BonusSlotUpdating.Arguments arguments) { - var actor = _penumbra.GameObjectFromDrawObject(model); + var actor = _penumbra.GameObjectFromDrawObject(arguments.Model); if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) return; if (actor.Identifier(_actors, out var identifier) && _manager.TryGetValue(identifier, out var state)) - switch (UpdateBaseData(actor, state, slot, item)) + switch (UpdateBaseData(actor, state, arguments.Slot, arguments.Armor)) { // Base data changed equipment while actors were not there. // Update model state if not on fixed design. case UpdateState.Change: var apply = false; - if (!state.Sources[slot].IsFixed()) - _manager.ChangeBonusItem(state, slot, state.BaseData.BonusItem(slot), ApplySettings.Game); + if (!state.Sources[arguments.Slot].IsFixed()) + _manager.ChangeBonusItem(state, arguments.Slot, state.BaseData.BonusItem(arguments.Slot), ApplySettings.Game); else apply = true; if (apply) - item = state.ModelData.BonusItem(slot).Armor(); + arguments.Armor = state.ModelData.BonusItem(arguments.Slot).Armor(); break; // Use current model data. - case UpdateState.NoChange: item = state.ModelData.BonusItem(slot).Armor(); break; + case UpdateState.NoChange: arguments.Armor = state.ModelData.BonusItem(arguments.Slot).Armor(); break; case UpdateState.Transformed: break; } } - private void OnGearsetDataLoaded(Actor actor, Model model) + private void OnGearsetDataLoaded(in GearsetDataLoaded.Arguments arguments) { - if (!actor.Valid || _condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) + if (!arguments.Actor.Valid || _condition[ConditionFlag.CreatingCharacter] && arguments.Actor.Index >= ObjectIndex.CutsceneStart) return; // ensure actor and state are valid. - if (!actor.Identifier(_actors, out var identifier)) + if (!arguments.Actor.Identifier(_actors, out var identifier)) return; if (_objects.TryGetValue(identifier, out var actors) && actors.Valid) - _stateFinalized.Invoke(StateFinalizationType.Gearset, actors); + _stateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.Gearset, actors)); } - private void OnMovedEquipment((EquipSlot, uint, StainIds)[] items) + private void OnMovedEquipment(in MovedEquipment.Arguments arguments) { var (identifier, objects) = _objects.PlayerData; if (!identifier.IsValid || !_manager.TryGetValue(identifier, out var state)) return; - foreach (var (slot, item, stain) in items) + foreach (var (slot, item, stain) in arguments.Items) { var currentItem = state.BaseData.Item(slot); var model = slot is EquipSlot.MainHand or EquipSlot.OffHand @@ -336,75 +337,75 @@ public class StateListener : IDisposable /// Update base data, apply or update model data. /// Verify consistent weapon types. /// - private void OnWeaponLoading(Actor actor, EquipSlot slot, ref CharacterWeapon weapon) + private void OnWeaponLoading(in WeaponLoading.Arguments arguments) { - if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) + if (_condition[ConditionFlag.CreatingCharacter] && arguments.Actor.Index >= ObjectIndex.CutsceneStart) return; // Fist weapon gauntlet hack. - if (slot is EquipSlot.OffHand - && weapon.Variant == 0 - && weapon.Weapon.Id != 0 - && _fistOffhands.TryGetValue(actor, out var lastFistOffhand)) + if (arguments.Slot is EquipSlot.OffHand + && arguments.Weapon.Variant.Id is 0 + && arguments.Weapon.Weapon.Id is not 0 + && _fistOffhands.TryGetValue(arguments.Actor, out var lastFistOffhand)) { - Glamourer.Log.Verbose($"Applying stored fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}."); - weapon = lastFistOffhand; + Glamourer.Log.Verbose($"Applying stored fist weapon offhand {lastFistOffhand} for 0x{arguments.Actor.Address:X}."); + arguments.Weapon = lastFistOffhand; } - if (!actor.Identifier(_actors, out var identifier) + if (!arguments.Actor.Identifier(_actors, out var identifier) || !_manager.TryGetValue(identifier, out var state)) return; var apply = false; - switch (UpdateBaseData(actor, state, slot, weapon)) + switch (UpdateBaseData(arguments.Actor, state, arguments.Slot, arguments.Weapon)) { // Do nothing. But this usually can not happen because the hooked function also writes to game objects later. case UpdateState.Transformed: break; case UpdateState.Change: - if (!state.Sources[slot, false].IsFixed()) - _manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game); + if (!state.Sources[arguments.Slot, false].IsFixed()) + _manager.ChangeItem(state, arguments.Slot, state.BaseData.Item(arguments.Slot), ApplySettings.Game); else apply = true; - if (!state.Sources[slot, true].IsFixed()) - _manager.ChangeStains(state, slot, state.BaseData.Stain(slot), ApplySettings.Game); + if (!state.Sources[arguments.Slot, true].IsFixed()) + _manager.ChangeStains(state, arguments.Slot, state.BaseData.Stain(arguments.Slot), ApplySettings.Game); else apply = true; break; case UpdateState.NoChange: apply = true; break; } - var baseType = slot is EquipSlot.OffHand ? state.BaseData.MainhandType.Offhand() : state.BaseData.MainhandType; - var modelType = state.ModelData.Item(slot).Type; + var baseType = arguments.Slot is EquipSlot.OffHand ? state.BaseData.MainhandType.Offhand() : state.BaseData.MainhandType; + var modelType = state.ModelData.Item(arguments.Slot).Type; if (apply) { // Only allow overwriting identical weapons var canApply = baseType == modelType - || _gPose.InGPose && actor.IsGPoseOrCutscene; - var newWeapon = state.ModelData.Weapon(slot); + || _gPose.InGPose && arguments.Actor.IsGPoseOrCutscene; + var newWeapon = state.ModelData.Weapon(arguments.Slot); if (canApply) { - weapon = newWeapon; + arguments.Weapon = newWeapon; } else { - if (weapon.Skeleton.Id != 0) - weapon = weapon.With(newWeapon.Stains); + if (arguments.Weapon.Skeleton.Id is not 0) + arguments.Weapon = arguments.Weapon.With(newWeapon.Stains); // Force unlock if necessary. - _manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game with { Key = state.Combination }); + _manager.ChangeItem(state, arguments.Slot, state.BaseData.Item(arguments.Slot), ApplySettings.Game with { Key = state.Combination }); } } // Fist Weapon Offhand hack. - if (slot is EquipSlot.MainHand && weapon.Skeleton.Id is > 1600 and < 1651) + if (arguments.Slot is EquipSlot.MainHand && arguments.Weapon.Skeleton.Id is > 1600 and < 1651) { - lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant, - weapon.Stains); - _fistOffhands[actor] = lastFistOffhand; - Glamourer.Log.Excessive($"Storing fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}."); + lastFistOffhand = new CharacterWeapon((PrimaryId)(arguments.Weapon.Skeleton.Id + 50), arguments.Weapon.Weapon, arguments.Weapon.Variant, + arguments.Weapon.Stains); + _fistOffhands[arguments.Actor] = lastFistOffhand; + Glamourer.Log.Excessive($"Storing fist weapon offhand {lastFistOffhand} for 0x{arguments.Actor.Address:X}."); } - _funModule.ApplyFunToWeapon(actor, ref weapon, slot); + _funModule.ApplyFunToWeapon(arguments.Actor, ref arguments.Weapon, arguments.Slot); } /// Update base data for a single changed equipment slot. @@ -527,26 +528,26 @@ public class StateListener : IDisposable } } - private void OnCrestChange(Actor actor, CrestFlag slot, ref bool value) + private void OnCrestChange(in CrestService.Arguments arguments) { - if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) + if (_condition[ConditionFlag.CreatingCharacter] && arguments.Actor.Index >= ObjectIndex.CutsceneStart) return; - if (!actor.Identifier(_actors, out var identifier) + if (!arguments.Actor.Identifier(_actors, out var identifier) || !_manager.TryGetValue(identifier, out var state)) return; - switch (UpdateBaseCrest(actor, state, slot, value)) + switch (UpdateBaseCrest(arguments.Actor, state, arguments.Slot, arguments.Value)) { case UpdateState.Change: - if (!state.Sources[slot].IsFixed()) - _manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), ApplySettings.Game); + if (!state.Sources[arguments.Slot].IsFixed()) + _manager.ChangeCrest(state, arguments.Slot, state.BaseData.Crest(arguments.Slot), ApplySettings.Game); else - value = state.ModelData.Crest(slot); + arguments.Value = state.ModelData.Crest(arguments.Slot); break; case UpdateState.NoChange: case UpdateState.HatHack: - value = state.ModelData.Crest(slot); + arguments.Value = state.ModelData.Crest(arguments.Slot); break; case UpdateState.Transformed: break; } @@ -675,7 +676,7 @@ public class StateListener : IDisposable } /// Handle visor state changes made by the game. - private unsafe void OnVisorChange(Model model, bool game, ref bool value) + private unsafe void OnVisorChange(in VisorStateChanged.Arguments arguments) { // Skip updates when in customize update. if (ChangeCustomizeService.InUpdate.InMethod) @@ -684,14 +685,14 @@ public class StateListener : IDisposable // Find appropriate actor and state. // We do not need to handle fixed designs, // since a fixed design would already have established state-tracking. - var actor = _penumbra.GameObjectFromDrawObject(model); + var actor = _penumbra.GameObjectFromDrawObject(arguments.Model); if (!actor.IsCharacter) return; // Only actually change anything if the actor state changed, // when equipping headgear the method is called with the current draw object state, // which corrupts Glamourer's assumed game state otherwise. - if (!game && actor.AsCharacter->DrawData.IsVisorToggled != value) + if (!arguments.NewVisorState && actor.AsCharacter->DrawData.IsVisorToggled != arguments.Value) return; if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) @@ -704,24 +705,24 @@ public class StateListener : IDisposable return; // Update visor base state. - if (state.BaseData.SetVisor(value)) + if (state.BaseData.SetVisor(arguments.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.VisorState].IsFixed()) - value = state.ModelData.IsVisorToggled(); + arguments.Value = state.ModelData.IsVisorToggled(); else - _manager.ChangeMetaState(state, MetaIndex.VisorState, value, ApplySettings.Game); + _manager.ChangeMetaState(state, MetaIndex.VisorState, arguments.Value, ApplySettings.Game); } else { // if base state did not change, overwrite the value with the model state one. - value = state.ModelData.IsVisorToggled(); + arguments.Value = state.ModelData.IsVisorToggled(); } } /// Handle visor state changes made by the game. - private void OnVieraEarChange(Actor actor, ref bool value) + private void OnVieraEarChange(in VieraEarStateChanged.Arguments arguments) { // Value is inverted compared to our own handling. @@ -729,98 +730,98 @@ public class StateListener : IDisposable if (ChangeCustomizeService.InUpdate.InMethod) return; - if (!actor.IsCharacter) + if (!arguments.Actor.IsCharacter) return; - if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) + if (_condition[ConditionFlag.CreatingCharacter] && arguments.Actor.Index >= ObjectIndex.CutsceneStart) return; - if (!actor.Identifier(_actors, out var identifier)) + if (!arguments.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 (state.BaseData.SetEarsVisible(!arguments.State)) { // 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(); + arguments.State = !state.ModelData.AreEarsVisible(); else - _manager.ChangeMetaState(state, MetaIndex.EarState, !value, ApplySettings.Game); + _manager.ChangeMetaState(state, MetaIndex.EarState, !arguments.State, ApplySettings.Game); } else { // if base state did not change, overwrite the value with the model state one. - value = !state.ModelData.AreEarsVisible(); + arguments.State = !state.ModelData.AreEarsVisible(); } } /// Handle Hat Visibility changes. These act on the game object. - private void OnHeadGearVisibilityChange(Actor actor, ref bool value) + private void OnHeadGearVisibilityChange(in HeadGearVisibilityChanged.Arguments arguments) { - if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) + if (_condition[ConditionFlag.CreatingCharacter] && arguments.Actor.Index >= ObjectIndex.CutsceneStart) return; // Find appropriate state. // We do not need to handle fixed designs, // if there is no model that caused a fixed design to exist yet, // we also do not care about the invisible model. - if (!actor.Identifier(_actors, out var identifier)) + if (!arguments.Actor.Identifier(_actors, out var identifier)) return; if (!_manager.TryGetValue(identifier, out var state)) return; // Update hat visibility state. - if (state.BaseData.SetHatVisible(value)) + if (state.BaseData.SetHatVisible(arguments.Visible)) { // 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.HatState].IsFixed()) - value = state.ModelData.IsHatVisible(); + arguments.Visible = state.ModelData.IsHatVisible(); else - _manager.ChangeMetaState(state, MetaIndex.HatState, value, ApplySettings.Game); + _manager.ChangeMetaState(state, MetaIndex.HatState, arguments.Visible, ApplySettings.Game); } else { // if base state did not change, overwrite the value with the model state one. - value = state.ModelData.IsHatVisible(); + arguments.Visible = state.ModelData.IsHatVisible(); } } /// Handle Weapon Visibility changes. These act on the game object. - private void OnWeaponVisibilityChange(Actor actor, ref bool value) + private void OnWeaponVisibilityChange(in WeaponVisibilityChanged.Arguments arguments) { - if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) + if (_condition[ConditionFlag.CreatingCharacter] && arguments.Actor.Index >= ObjectIndex.CutsceneStart) return; // Find appropriate state. // We do not need to handle fixed designs, // if there is no model that caused a fixed design to exist yet, // we also do not care about the invisible model. - if (!actor.Identifier(_actors, out var identifier)) + if (!arguments.Actor.Identifier(_actors, out var identifier)) return; if (!_manager.TryGetValue(identifier, out var state)) return; // Update weapon visibility state. - if (state.BaseData.SetWeaponVisible(value)) + if (state.BaseData.SetWeaponVisible(arguments.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.WeaponState].IsFixed()) - value = state.ModelData.IsWeaponVisible(); + arguments.Value = state.ModelData.IsWeaponVisible(); else - _manager.ChangeMetaState(state, MetaIndex.WeaponState, value, ApplySettings.Game); + _manager.ChangeMetaState(state, MetaIndex.WeaponState, arguments.Value, ApplySettings.Game); } else { // if base state did not change, overwrite the value with the model state one. - value = state.ModelData.IsWeaponVisible(); + arguments.Value = state.ModelData.IsWeaponVisible(); } } @@ -894,9 +895,9 @@ public class StateListener : IDisposable ApplyParameters(_creatingState, drawObject); } - private void OnCustomizeChanged(Model model) + private void OnCustomizeChanged(in Model model) { - if (_customizeState == null) + if (_customizeState is null) { var actor = _penumbra.GameObjectFromDrawObject(model); if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart) diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index b767f48..df2029b 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -11,6 +11,7 @@ using Glamourer.Interop.Penumbra; using Glamourer.Interop.Structs; using Glamourer.Services; using ImSharp; +using Luna; using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; @@ -34,7 +35,7 @@ public sealed class StateManager( ModSettingApplier modApplier, GPoseService gPose) : StateEditor(editor, applier, changeEvent, finalizeEvent, jobChange, config, items, merger, modApplier, gPose), - IReadOnlyDictionary + IReadOnlyDictionary, IService { private readonly Dictionary _states = []; @@ -279,10 +280,10 @@ public sealed class StateManager( 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); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Reset, source, state, objects)); // only invoke if we define this reset call as the final call in our state update. if (isFinal) - StateFinalized.Invoke(StateFinalizationType.Revert, objects); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.Revert, objects)); } public void ResetAdvancedDyes(ActorState state, StateSource source, uint key = 0) @@ -304,9 +305,9 @@ public sealed class StateManager( 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); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Reset, source, state, objects)); // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, objects); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.RevertAdvanced, objects)); } public void ResetAdvancedCustomizations(ActorState state, StateSource source, uint key = 0) @@ -327,9 +328,9 @@ public sealed class StateManager( 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); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Reset, source, state, objects)); // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, objects); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.RevertAdvanced, objects)); } public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0) @@ -342,21 +343,21 @@ public sealed class StateManager( foreach (var flag in CustomizeParameterExtensions.AllFlags) state.Sources[flag] = StateSource.Game; - var actors = ActorData.Invalid; + var data = ActorData.Invalid; if (source is not StateSource.Game) { - actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true); + data = 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, data, MaterialValueIndex.FromKey(idx), mat.Game); } state.Materials.Clear(); 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); + $"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {data.ToLazyString("nothing")}.]"); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Reset, source, state, data)); // Update that we have completed a full operation. (We can do this directly as nothing else is linked) - StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, actors); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.RevertAdvanced, data)); } public void ResetCustomize(ActorState state, StateSource source, uint key = 0) @@ -369,13 +370,13 @@ public sealed class StateManager( state.ModelData.ModelId = state.BaseData.ModelId; state.ModelData.Customize = state.BaseData.Customize; - var actors = ActorData.Invalid; + var data = ActorData.Invalid; if (source is not StateSource.Game) - actors = Applier.ChangeCustomize(state, true); + data = Applier.ChangeCustomize(state, true); Glamourer.Log.Verbose( - $"Reset customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); + $"Reset customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {data.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); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.RevertCustomize, data)); } public void ResetEquip(ActorState state, StateSource source, uint key = 0) @@ -401,32 +402,32 @@ public sealed class StateManager( state.ModelData.SetBonusItem(slot, state.BaseData.BonusItem(slot)); } - var actors = ActorData.Invalid; + var data = ActorData.Invalid; if (source is not StateSource.Game) { - actors = Applier.ChangeArmor(state, EquipSlotExtensions.EqdpSlots[0], true); + data = Applier.ChangeArmor(state, EquipSlotExtensions.EqdpSlots[0], true); foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1)) { - Applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), + Applier.ChangeArmor(data, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible()); } foreach (var slot in BonusExtensions.AllFlags) { var item = state.ModelData.BonusItem(slot); - Applier.ChangeBonusItem(actors, slot, item.PrimaryId, item.Variant); + Applier.ChangeBonusItem(data, slot, item.PrimaryId, item.Variant); } - var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors; + var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? data.OnlyGPose() : data; Applier.ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand)); - var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? actors.OnlyGPose() : actors; + var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? data.OnlyGPose() : data; Applier.ChangeOffhand(offhandActors, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand)); } Glamourer.Log.Verbose( - $"Reset equipment state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); + $"Reset equipment state of {state.Identifier.Incognito(null)} to game base. [Affecting {data.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); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.RevertEquipment, data)); } public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0) @@ -517,9 +518,9 @@ public sealed class StateManager( forceRedraw || !actor.Model.IsHuman || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false); - StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Reapply, source, state, data)); if (isFinal) - StateFinalized.Invoke(StateFinalizationType.Reapply, data); + StateFinalized.Invoke(new StateFinalized.Arguments(StateFinalizationType.Reapply, data)); } /// Automation variant for reapply, to fire the correct StateUpdateType once reapplied. @@ -538,9 +539,9 @@ public sealed class StateManager( forceRedraw || !actor.Model.IsHuman || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false); - StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null); + StateChanged.Invoke(new StateChanged.Arguments(StateChangeType.Reapply, source, state, data)); // invoke the automation update based on what reset is. - StateFinalized.Invoke(wasReset ? StateFinalizationType.RevertAutomation : StateFinalizationType.ReapplyAutomation, data); + StateFinalized.Invoke(new StateFinalized.Arguments(wasReset ? StateFinalizationType.RevertAutomation : StateFinalizationType.ReapplyAutomation, data)); } public void DeleteState(ActorIdentifier identifier) diff --git a/Glamourer/Unlocks/CustomizeUnlockManager.cs b/Glamourer/Unlocks/CustomizeUnlockManager.cs index 71e99b3..66d2d67 100644 --- a/Glamourer/Unlocks/CustomizeUnlockManager.cs +++ b/Glamourer/Unlocks/CustomizeUnlockManager.cs @@ -7,6 +7,7 @@ using Glamourer.GameData; using Glamourer.Events; using Glamourer.Services; using Lumina.Excel.Sheets; +using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; @@ -14,7 +15,7 @@ using StringU8 = ImSharp.StringU8; namespace Glamourer.Unlocks; -public class CustomizeUnlockManager : IDisposable, ISavable +public sealed class CustomizeUnlockManager : IDisposable, ISavable, IRequiredService { private readonly SaveService _saveService; private readonly IClientState _clientState; @@ -78,7 +79,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable _unlocked.TryAdd(pair.Data, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); time = DateTimeOffset.UtcNow; - _event.Invoke(ObjectUnlocked.Type.Customization, pair.Data, time); + _event.Invoke(new ObjectUnlocked.Arguments(ObjectUnlocked.Type.Customization, pair.Data, time)); Save(); return true; } @@ -87,7 +88,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable public unsafe bool IsUnlockedGame(uint dataId) { var instance = UIState.Instance(); - if (instance == null) + if (instance is null) return false; return UIState.Instance()->IsUnlockLinkUnlocked(dataId); @@ -101,7 +102,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable Glamourer.Log.Debug("[UnlockManager] Scanning for new unlocked customizations."); var instance = UIState.Instance(); - if (instance == null) + if (instance is null) return; try @@ -112,7 +113,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable { if (instance->IsUnlockLinkUnlocked(id) && _unlocked.TryAdd(id, time)) { - _event.Invoke(ObjectUnlocked.Type.Customization, id, DateTimeOffset.FromUnixTimeMilliseconds(time)); + _event.Invoke(new ObjectUnlocked.Arguments(ObjectUnlocked.Type.Customization, id, DateTimeOffset.FromUnixTimeMilliseconds(time))); ++count; } } @@ -148,7 +149,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable if (id != data || !_unlocked.TryAdd(id, time)) continue; - _event.Invoke(ObjectUnlocked.Type.Customization, id, DateTimeOffset.FromUnixTimeMilliseconds(time)); + _event.Invoke(new ObjectUnlocked.Arguments(ObjectUnlocked.Type.Customization, id, DateTimeOffset.FromUnixTimeMilliseconds(time))); Save(); break; } diff --git a/Glamourer/Unlocks/FavoriteManager.cs b/Glamourer/Unlocks/FavoriteManager.cs index 1f6f3f5..87ce720 100644 --- a/Glamourer/Unlocks/FavoriteManager.cs +++ b/Glamourer/Unlocks/FavoriteManager.cs @@ -7,7 +7,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Unlocks; -public sealed class FavoriteManager : ISavable +public sealed class FavoriteManager : ISavable, IService { public readonly record struct FavoriteHairStyle(Gender Gender, SubRace Race, CustomizeIndex Type, CustomizeValue Id) { diff --git a/Glamourer/Unlocks/ItemUnlockManager.cs b/Glamourer/Unlocks/ItemUnlockManager.cs index 6f8e097..fbc35c9 100644 --- a/Glamourer/Unlocks/ItemUnlockManager.cs +++ b/Glamourer/Unlocks/ItemUnlockManager.cs @@ -4,6 +4,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.UI; using Glamourer.Events; using Glamourer.Services; using Lumina.Excel.Sheets; +using Luna; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -11,13 +12,13 @@ using Cabinet = Lumina.Excel.Sheets.Cabinet; namespace Glamourer.Unlocks; -public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary +public sealed class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary, IService { - private readonly SaveService _saveService; - private readonly ItemManager _items; - private readonly IClientState _clientState; - private readonly IFramework _framework; - private readonly ObjectUnlocked _event; + private readonly SaveService _saveService; + private readonly ItemManager _items; + private readonly IClientState _clientState; + private readonly IFramework _framework; + private readonly ObjectUnlocked _event; private readonly ObjectIdentification _identifier; private readonly Dictionary _unlocked = new(); @@ -100,12 +101,13 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary= (uint) _items.ItemSheet.Count) + if (itemId.Id >= (uint)_items.ItemSheet.Count) { time = DateTimeOffset.MinValue; return true; @@ -210,7 +212,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary