Actually push what I said in the message before.

This commit is contained in:
Ottermandias 2026-02-21 23:20:58 +01:00
parent 8012c92704
commit f1d377c9dc
109 changed files with 1201 additions and 1208 deletions

View file

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18 # Visual Studio Version 18
VisualStudioVersion = 18.3.11415.281 d18.3 VisualStudioVersion = 18.3.11415.281
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{383AEE76-D423-431C-893A-7AB3DEA13630}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{383AEE76-D423-431C-893A-7AB3DEA13630}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
@ -20,8 +20,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumb
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.String", "Penumbra.String\Penumbra.String.csproj", "{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.String", "Penumbra.String\Penumbra.String.csproj", "{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Glamourer.Api", "Glamourer.Api\Glamourer.Api.csproj", "{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luna", "Luna\Luna\Luna.csproj", "{DEA936D7-1386-55A1-7451-E0C240F56E9D}" 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|Any CPU.Build.0 = Release|x64
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|x64.ActiveCfg = Release|x64 {AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|x64.ActiveCfg = Release|x64
{AAFE22E7-0F9B-462A-AAA3-6EE3B268F3F8}.Release|x64.Build.0 = 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.ActiveCfg = Debug|x64
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.Build.0 = Debug|x64 {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|Any CPU.Build.0 = Debug|x64
{9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|x64.ActiveCfg = Debug|x64 {9B46691B-FAB2-4CC3-9B89-C8B91A590F47}.Debug|x64.ActiveCfg = Debug|x64

View file

@ -2,7 +2,6 @@
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.State; using Glamourer.State;
using Luna; using Luna;
using OtterGui.Extensions;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
@ -73,26 +72,26 @@ public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, A
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void Lock(ActorState state, uint key, ApplyFlag flags) 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); state.Lock(key);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal IEnumerable<ActorState> FindStates(string objectName) internal IEnumerable<ActorState> 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 [];
return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString) return stateManager.Values.Where(state => state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString)
.Concat(ArrayExtensions.SelectWhere(objects .Concat(objects
.Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString), kvp => .Where(kvp => kvp.Key is { IsValid: true, Type: IdentifierType.Player } && kvp.Key.PlayerName == byteString).SelectWhere(kvp =>
{ {
if (stateManager.ContainsKey(kvp.Key)) if (stateManager.ContainsKey(kvp.Key))
return (false, null); return (false, null);
var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state); var ret = stateManager.GetOrCreate(kvp.Key, kvp.Value.Objects[0], out var state);
return (ret, state); return (ret, state);
})); }));
} }

View file

@ -16,14 +16,14 @@ public class DesignsApi(
: IGlamourerApiDesigns, IApiService : IGlamourerApiDesigns, IApiService
{ {
public Dictionary<Guid, string> GetDesignList() public Dictionary<Guid, string> GetDesignList()
=> designs.Designs.ToDictionary(d => d.Identifier, d => d.Name.Text); => designs.Designs.ToDictionary(d => d.Identifier, d => d.Name);
public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended() public Dictionary<Guid, (string DisplayName, string FullPath, uint DisplayColor, bool ShownInQdb)> GetDesignListExtended()
=> designs.Designs.ToDictionary(d => d.Identifier, d => (d.DisplayName, d.Path.CurrentPath, color.GetColor(d).Color, d.QuickDesign)); => 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) public (string DisplayName, string FullPath, uint DisplayColor, bool ShowInQdb) GetExtendedDesignData(Guid designId)
=> designs.Designs.ByIdentifier(designId) is { } d => 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); : (string.Empty, string.Empty, 0, false);
public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags) public GlamourerApiEc ApplyDesign(Guid designId, int objectIndex, uint key, ApplyFlag flags)

View file

@ -2,7 +2,6 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.History;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.State; using Glamourer.State;
using Luna; using Luna;
@ -143,10 +142,10 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
public GlamourerApiEc ReapplyStateName(string playerName, uint key, ApplyFlag flags) 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 states = _helpers.FindExistingStates(playerName);
var any = false; var any = false;
var anyReapplied = false; var anyReapplied = false;
foreach (var state in states) foreach (var state in states)
{ {
@ -154,7 +153,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
continue; continue;
anyReapplied = true; anyReapplied = true;
anyReapplied |= Reapply(state, key, flags) is GlamourerApiEc.Success; 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) public GlamourerApiEc CanUnlock(int objectIndex, uint key, out bool isLocked, out bool canUnlock)
{ {
var args = ApiHelpers.Args("Index", objectIndex, "Key", key); var args = ApiHelpers.Args("Index", objectIndex, "Key", key);
isLocked = false; isLocked = false;
canUnlock = true; canUnlock = true;
if (_helpers.FindExistingState(objectIndex, out var state) is not GlamourerApiEc.Success) if (_helpers.FindExistingState(objectIndex, out var state) is not GlamourerApiEc.Success)
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args); return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
if (state is null) if (state is null)
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
isLocked = state.IsLocked;
isLocked = state.IsLocked;
canUnlock = state.CanUnlock(key); canUnlock = state.CanUnlock(key);
return ApiHelpers.Return(GlamourerApiEc.Success, args); 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); => 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")}.]"); Glamourer.Log.Excessive(
if (StateChanged != null) $"[OnStateChanged] State Changed with Type {arguments.Type} [Affecting {arguments.Actors.ToLazyString("nothing")}.]");
foreach (var actor in actors.Objects) if (StateChanged is not null)
foreach (var actor in arguments.Actors.Objects)
StateChanged.Invoke(actor.Address); StateChanged.Invoke(actor.Address);
if (StateChangedWithType != null) if (StateChangedWithType is not null)
foreach (var actor in actors.Objects) foreach (var actor in arguments.Actors.Objects)
StateChangedWithType.Invoke(actor.Address, type); 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")}.]"); Glamourer.Log.Verbose(
if (StateFinalized != null) $"[OnStateUpdated] State Updated with Type {arguments.Type}. [Affecting {arguments.Actors.ToLazyString("nothing")}.]");
foreach (var actor in actors.Objects) if (StateFinalized is not null)
StateFinalized.Invoke(actor.Address, type); 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); => GPoseChanged?.Invoke(gPose);
} }

View file

@ -7,6 +7,7 @@ using Glamourer.Events;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Material; using Glamourer.Interop.Material;
using Glamourer.State; using Glamourer.State;
using Luna;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -15,7 +16,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Automation; namespace Glamourer.Automation;
public sealed class AutoDesignApplier : IDisposable public sealed class AutoDesignApplier : IDisposable, IRequiredService
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly AutoDesignManager _manager; private readonly AutoDesignManager _manager;
@ -72,38 +73,38 @@ public sealed class AutoDesignApplier : IDisposable
_jobs.JobChanged -= OnJobChange; _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) if (!_jobChangeState.HasState || !_config.EnableAutoDesigns)
return; return;
var id = actor.GetIdentifier(_actors); var id = arguments.Actor.GetIdentifier(_actors);
if (id == _jobChangeState.Identifier) if (id == _jobChangeState.Identifier)
{ {
var state = _jobChangeState.State!; var state = _jobChangeState.State!;
var current = state.BaseData.Item(slot); var current = state.BaseData.Item(arguments.Slot);
switch (slot) switch (arguments.Slot)
{ {
case EquipSlot.MainHand: 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( 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)); _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; break;
} }
case EquipSlot.OffHand when current.Type == state.BaseData.MainhandType.Offhand(): 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( 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)); _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(); _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; return;
switch (type) switch (arguments.Type)
{ {
case AutomationChanged.Type.ToggleSet when !set.Enabled: // The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks.
case AutomationChanged.Type.DeletedDesign when set.Enabled: case AutomationChanged.Type.ToggleSet when arguments.Set.Enabled:
// The automation set was disabled or deleted, no other for those identifiers can be enabled, remove existing Fixed Locks. case AutomationChanged.Type.DeletedDesign when arguments.Set.Enabled:
RemoveOld(set.Identifiers); RemoveOld(arguments.Set.Identifiers);
break; break;
case AutomationChanged.Type.ChangeIdentifier when set.Enabled: case AutomationChanged.Type.ChangeIdentifier
when arguments.As<AutomationChanged.ChangeIdentifierArguments>().Set is { Enabled: true } set:
// Remove fixed state from the old identifiers assigned and the old enabled set, if any. // Remove fixed state from the old identifiers assigned and the old enabled set, if any.
var (oldIds, _, _) = ((ActorIdentifier[], ActorIdentifier, AutoDesignSet?))bonusData!; RemoveOld(arguments.As<AutomationChanged.ChangeIdentifierArguments>().OldIdentifiers);
RemoveOld(oldIds);
ApplyNew(set); // Does not need to disable oldSet because same identifiers. ApplyNew(set); // Does not need to disable oldSet because same identifiers.
break; break;
case AutomationChanged.Type.ToggleSet: // Does not need to disable old states because same identifiers. 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.ChangedConditions:
case AutomationChanged.Type.ChangedType: case AutomationChanged.Type.ChangedType:
case AutomationChanged.Type.ChangedData: case AutomationChanged.Type.ChangedData:
ApplyNew(set); ApplyNew(arguments.Set);
break; break;
} }
@ -303,7 +304,7 @@ public sealed class AutoDesignApplier : IDisposable
mergedDesign.ResetTemporarySettings = true; 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; forcedRedraw = mergedDesign.ForcedRedraw;
} }
@ -345,7 +346,7 @@ public sealed class AutoDesignApplier : IDisposable
internal static int NewGearsetId = -1; 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) if (!_config.EnableAutoDesigns)
return; return;
@ -357,9 +358,9 @@ public sealed class AutoDesignApplier : IDisposable
if (!GetPlayerSet(player, out var set) || !_state.TryGetValue(player, out var state)) if (!GetPlayerSet(player, out var set) || !_state.TryGetValue(player, out var state))
return; return;
var respectManual = prior == id; var respectManual = arguments.PriorId == arguments.Id;
NewGearsetId = id; NewGearsetId = arguments.Id;
Reduce(data.Objects[0], state, set, respectManual, job != state.LastJob, prior == id, out var forcedRedraw); Reduce(data.Objects[0], state, set, respectManual, arguments.JobId != state.LastJob, arguments.PriorId == arguments.Id, out var forcedRedraw);
NewGearsetId = -1; NewGearsetId = -1;
foreach (var actor in data.Objects) foreach (var actor in data.Objects)
_state.ReapplyState(actor, forcedRedraw, StateSource.Fixed); _state.ReapplyState(actor, forcedRedraw, StateSource.Fixed);

View file

@ -1,14 +1,12 @@
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.History;
using Glamourer.Designs.Special; using Glamourer.Designs.Special;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Services; using Glamourer.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Extensions;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -16,7 +14,7 @@ using Luna;
namespace Glamourer.Automation; namespace Glamourer.Automation;
public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDisposable public sealed class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDisposable, IService
{ {
public const int CurrentVersion = 1; public const int CurrentVersion = 1;
@ -77,7 +75,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
_data.Add(newSet); _data.Add(newSet);
Save(); Save();
Glamourer.Log.Debug($"Created new design set for {newSet.Identifiers[0].Incognito(null)}."); 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) public void DuplicateDesignSet(AutoDesignSet set)
@ -102,7 +100,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Duplicated new design set for {newSet.Identifiers[0].Incognito(null)} with {newSet.Designs.Count} auto designs from existing set."); $"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) public void DeleteDesignSet(int whichSet)
@ -121,12 +119,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
_data.RemoveAt(whichSet); _data.RemoveAt(whichSet);
Save(); Save();
Glamourer.Log.Debug($"Deleted design set {whichSet + 1}."); 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) 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; return;
var set = _data[whichSet]; var set = _data[whichSet];
@ -137,7 +135,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
set.Name = newName; set.Name = newName;
Save(); Save();
Glamourer.Log.Debug($"Renamed design set {whichSet + 1} from {old} to {newName}."); 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<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug($"Moved design set {whichSet + 1} to position {toWhichSet + 1}."); 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) public void ChangeIdentifier(int whichSet, ActorIdentifier to)
@ -180,7 +178,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug($"Changed Identifier of design set {whichSet + 1} from {old[0].Incognito(null)} to {to.Incognito(null)}."); 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) public void SetState(int whichSet, bool value)
@ -214,7 +212,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug($"Changed enabled state of design set {whichSet + 1} to {value}."); 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) public void ChangeBaseState(int whichSet, AutoDesignSet.Base newBase)
@ -230,7 +228,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
set.BaseState = newBase; set.BaseState = newBase;
Save(); Save();
Glamourer.Log.Debug($"Changed base state of set {whichSet + 1} from {old} to {newBase}."); 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) public void ChangeResetSettings(int whichSet, bool newValue)
@ -246,12 +244,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
set.ResetTemporarySettings = newValue; set.ResetTemporarySettings = newValue;
Save(); Save();
Glamourer.Log.Debug($"Changed resetting of temporary settings of set {whichSet + 1} from {old} to {newValue}."); 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) public void AddDesign(AutoDesignSet set, IDesignStandIn design)
{ {
var newDesign = new AutoDesign() var newDesign = new AutoDesign
{ {
Design = design, Design = design,
Type = ApplicationType.All, Type = ApplicationType.All,
@ -261,7 +259,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Added new associated design {design.ResolveName(true)} as design {set.Designs.Count} to design set."); $"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));
} }
/// <remarks> Only used to move between sets. </remarks> /// <remarks> Only used to move between sets. </remarks>
@ -275,8 +273,8 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
from.Designs.RemoveAt(idx); from.Designs.RemoveAt(idx);
Save(); Save();
Glamourer.Log.Debug($"Moved design {idx} from design set {from.Name} to design set {to.Name}."); 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(new AutomationChanged.AddedDesignArguments(to, to.Designs.Count - 1));
_event.Invoke(AutomationChanged.Type.DeletedDesign, from, idx); _event.Invoke(new AutomationChanged.DeletedDesignArguments(from, idx));
} }
public void DeleteDesign(AutoDesignSet set, int which) public void DeleteDesign(AutoDesignSet set, int which)
@ -287,7 +285,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
set.Designs.RemoveAt(which); set.Designs.RemoveAt(which);
Save(); Save();
Glamourer.Log.Debug($"Removed associated design {which + 1} from design set."); 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) public void MoveDesign(AutoDesignSet set, int from, int to)
@ -297,7 +295,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug($"Moved design {from + 1} to {to + 1} in design set."); 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) public void ChangeDesign(AutoDesignSet set, int which, IDesignStandIn newDesign)
@ -314,7 +312,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Changed linked design from {old.ResolveName(true)} to {newDesign.ResolveName(true)} for associated design {which + 1} in design set."); $"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) public void ChangeJobCondition(AutoDesignSet set, int which, JobGroup jobs)
@ -331,7 +329,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
design.Jobs = jobs; design.Jobs = jobs;
Save(); Save();
Glamourer.Log.Debug($"Changed job condition from {old.Id} to {jobs.Id} for associated design {which + 1} in design set."); 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) public void ChangeGearsetCondition(AutoDesignSet set, int which, short index)
@ -347,7 +345,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
design.GearsetIndex = index; design.GearsetIndex = index;
Save(); Save();
Glamourer.Log.Debug($"Changed gearset condition from {old} to {index} for associated design {which + 1} in design set."); 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) public void ChangeApplicationType(AutoDesignSet set, int which, ApplicationType applicationType)
@ -364,7 +362,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
design.Type = applicationType; design.Type = applicationType;
Save(); Save();
Glamourer.Log.Debug($"Changed application type from {old} to {applicationType} for associated design {which + 1} in design set."); 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) public void ChangeData(AutoDesignSet set, int which, object data)
@ -378,7 +376,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Save(); Save();
Glamourer.Log.Debug($"Changed additional design data for associated design {which + 1} in design set."); 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) public string ToFilePath(FilenameService fileNames)
@ -397,7 +395,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
foreach (var set in _data) foreach (var set in _data)
array.Add(set.Serialize()); array.Add(set.Serialize());
return new JObject() return new JObject
{ {
["Version"] = CurrentVersion, ["Version"] = CurrentVersion,
["Data"] = array, ["Data"] = array,
@ -424,9 +422,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
Glamourer.Messager.NotificationMessage("Failure to load automated designs: No valid version available.", Glamourer.Messager.NotificationMessage("Failure to load automated designs: No valid version available.",
NotificationType.Error); NotificationType.Error);
break; break;
case 1: case 1: LoadV1(obj["Data"]); break;
LoadV1(obj["Data"]);
break;
} }
} }
catch (Exception ex) catch (Exception ex)
@ -638,17 +634,17 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, 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; return;
foreach (var (set, idx) in this.WithIndex()) foreach (var (idx, set) in this.Index())
{ {
var deleted = 0; var deleted = 0;
for (var i = 0; i < set.Designs.Count; ++i) for (var i = 0; i < set.Designs.Count; ++i)
{ {
if (set.Designs[i].Design != design) if (set.Designs[i].Design != arguments.Design)
continue; continue;
DeleteDesign(set, i--); DeleteDesign(set, i--);
@ -657,7 +653,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
if (deleted > 0) if (deleted > 0)
Glamourer.Log.Information( 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}.");
} }
} }
} }

View file

@ -9,7 +9,7 @@ using Penumbra.String;
namespace Glamourer.Automation; 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; private List<(string Name, List<(string, JobGroup, bool)> Data)>? _migratedData;

View file

@ -13,7 +13,7 @@ using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
namespace Glamourer.Config; namespace Glamourer.Config;
public sealed partial class Configuration : IPluginConfiguration, ISavable public sealed partial class Configuration : IPluginConfiguration, ISavable, IService
{ {
public const int CurrentVersion = 9; public const int CurrentVersion = 9;

View file

@ -8,7 +8,7 @@ using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
namespace Glamourer.Config; namespace Glamourer.Config;
public partial class EphemeralConfig : ISavable public partial class EphemeralConfig : ISavable, IService
{ {
public int Version { get; set; } = Configuration.CurrentVersion; public int Version { get; set; } = Configuration.CurrentVersion;
@ -16,18 +16,13 @@ public partial class EphemeralConfig : ISavable
private bool _incognitoMode; private bool _incognitoMode;
public bool UnlockDetailMode { get; set; } = true; public bool UnlockDetailMode { get; set; } = true;
public bool ShowDesignQuickBar { get; set; } = false; public bool ShowDesignQuickBar { get; set; }
public bool LockDesignQuickBar { get; set; } = false; public bool LockDesignQuickBar { get; set; }
public bool LockMainWindow { get; set; } = false; public bool LockMainWindow { get; set; }
public MainTabType SelectedMainTab { get; set; } = MainTabType.Settings; public MainTabType SelectedMainTab { get; set; } = MainTabType.Settings;
public Guid SelectedDesign { get; set; } = Guid.Empty;
public Guid SelectedQuickDesign { get; set; } = Guid.Empty; public Guid SelectedQuickDesign { get; set; } = Guid.Empty;
public int LastSeenVersion { get; set; } = GlamourerChangelog.LastChangelogVersion; 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] [JsonIgnore]
private readonly SaveService _saveService; private readonly SaveService _saveService;

View file

@ -7,7 +7,6 @@ using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Luna; using Luna;
using Notification = Luna.Notification; using Notification = Luna.Notification;
@ -47,7 +46,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa
public IFileSystemData<Design>? Node { get; set; } public IFileSystemData<Design>? Node { get; set; }
public DateTimeOffset CreationDate { get; internal init; } public DateTimeOffset CreationDate { get; internal init; }
public DateTimeOffset LastEdit { get; internal set; } 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 Description { get; internal set; } = string.Empty;
public string[] Tags { get; internal set; } = []; public string[] Tags { get; internal set; } = [];
public int Index { get; internal set; } public int Index { get; internal set; }
@ -71,7 +70,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa
#region IDesignStandIn #region IDesignStandIn
public string ResolveName(bool incognito) public string ResolveName(bool incognito)
=> incognito ? Incognito : Name.Text; => incognito ? Incognito : Name;
public string SerializeName() public string SerializeName()
=> Identifier.ToString(); => Identifier.ToString();
@ -109,7 +108,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa
["Identifier"] = Identifier, ["Identifier"] = Identifier,
["CreationDate"] = CreationDate, ["CreationDate"] = CreationDate,
["LastEdit"] = LastEdit, ["LastEdit"] = LastEdit,
["Name"] = Name.Text, ["Name"] = Name,
["Description"] = Description, ["Description"] = Description,
["ForcedRedraw"] = ForcedRedraw, ["ForcedRedraw"] = ForcedRedraw,
["ResetAdvancedDyes"] = ResetAdvancedDyes, ["ResetAdvancedDyes"] = ResetAdvancedDyes,
@ -200,7 +199,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa
var hasNegativeGloss = false; var hasNegativeGloss = false;
var hasNonPositiveGloss = false; var hasNonPositiveGloss = false;
var specularLarger = 0; 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; hasNegativeGloss |= value.Value.GlossStrength < 0;
hasNonPositiveGloss |= 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( Glamourer.Messager.AddMessage(new Notification(
$"Swapped Gloss and Specular Strength in {materialDesignData.Values.Count} Rows in design {design.Incognito} {reason}", $"Swapped Gloss and Specular Strength in {materialDesignData.Values.Count} Rows in design {design.Incognito} {reason}",
NotificationType.Info)); 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, CreationDate = creationDate,
Identifier = json["Identifier"]?.ToObject<Guid>() ?? throw new ArgumentNullException("Identifier"), Identifier = json["Identifier"]?.ToObject<Guid>() ?? throw new ArgumentNullException("Identifier"),
Name = new LowerString(json["Name"]?.ToObject<string>() ?? throw new ArgumentNullException("Name")), Name = json["Name"]?.ToObject<string>() ?? throw new ArgumentNullException("Name"),
Description = json["Description"]?.ToObject<string>() ?? string.Empty, Description = json["Description"]?.ToObject<string>() ?? string.Empty,
Tags = ParseTags(json), Tags = ParseTags(json),
LastEdit = json["LastEdit"]?.ToObject<DateTimeOffset>() ?? creationDate, LastEdit = json["LastEdit"]?.ToObject<DateTimeOffset>() ?? creationDate,
@ -359,5 +358,5 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn, IFileSystemVa
=> Identifier.ToString(); => Identifier.ToString();
public string DisplayName public string DisplayName
=> Name.Text; => Name;
} }

View file

@ -1,7 +1,5 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Services; using Glamourer.Services;
using OtterGui;
using OtterGui.Extensions;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -107,7 +105,7 @@ public class DesignBase64Migration
} }
data.Customize = *(CustomizeArray*)(ptr + 4); 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 mdl = eq[idx];
var item = items.Identify(slot, mdl.Set, mdl.Variant); var item = items.Identify(slot, mdl.Set, mdl.Variant);
@ -121,7 +119,7 @@ public class DesignBase64Migration
data.SetStain(slot, mdl.Stain); data.SetStain(slot, mdl.Stain);
} }
var main = cur[0].Skeleton.Id == 0 var main = cur[0].Skeleton.Id is 0
? items.DefaultSword ? items.DefaultSword
: items.Identify(EquipSlot.MainHand, cur[0].Skeleton, cur[0].Weapon, cur[0].Variant); : items.Identify(EquipSlot.MainHand, cur[0].Skeleton, cur[0].Weapon, cur[0].Variant);
if (!main.Valid) if (!main.Valid)

View file

@ -1,5 +1,4 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Glamourer.Config;
using Glamourer.Gui; using Glamourer.Gui;
using Glamourer.Services; using Glamourer.Services;
using ImSharp; using ImSharp;
@ -9,114 +8,7 @@ using Newtonsoft.Json.Linq;
namespace Glamourer.Designs; namespace Glamourer.Designs;
public class DesignColorUi(DesignColors colors, Configuration config) public sealed class DesignColors : ISavable, IReadOnlyDictionary<string, Rgba32>, IService
{
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<LabelStringHandlerBuffer> 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<string, Rgba32>
{ {
public const string AutomaticName = "Automatic"; public const string AutomaticName = "Automatic";
public static readonly StringU8 AutomaticNameU8 = new("Automatic"u8); public static readonly StringU8 AutomaticNameU8 = new("Automatic"u8);

View file

@ -3,6 +3,7 @@ using Glamourer.Interop.Material;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using Glamourer.Utility; using Glamourer.Utility;
using Luna;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
@ -12,13 +13,13 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Designs; namespace Glamourer.Designs;
public class DesignConverter( public sealed class DesignConverter(
SaveService saveService, SaveService saveService,
ItemManager _items, ItemManager items,
DesignManager _designs, DesignManager designs,
CustomizeService _customize, CustomizeService customizeService,
HumanModelList _humans, HumanModelList humans,
DesignLinkLoader _linkLoader) DesignLinkLoader linkLoader) : IService
{ {
public const byte Version = 6; public const byte Version = 6;
@ -54,9 +55,9 @@ public class DesignConverter(
public DesignBase Convert(in DesignData data, in StateMaterialManager materials, in ApplicationRules rules) public DesignBase Convert(in DesignData data, in StateMaterialManager materials, in ApplicationRules rules)
{ {
var design = _designs.CreateTemporary(); var design = designs.CreateTemporary();
rules.Apply(design); rules.Apply(design);
design.SetDesignData(_customize, data); design.SetDesignData(customizeService, data);
if (rules.Materials) if (rules.Materials)
ComputeMaterials(design.GetMaterialDataRef(), materials, rules.Equip); ComputeMaterials(design.GetMaterialDataRef(), materials, rules.Equip);
return design; return design;
@ -70,8 +71,8 @@ public class DesignConverter(
try try
{ {
var ret = jObject["Identifier"] != null var ret = jObject["Identifier"] != null
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObject) ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObject)
: DesignBase.LoadDesignBase(_customize, _items, jObject); : DesignBase.LoadDesignBase(customizeService, items, jObject);
if (!customize) if (!customize)
ret.Application.RemoveCustomize(); ret.Application.RemoveCustomize();
@ -101,14 +102,14 @@ public class DesignConverter(
case (byte)'{': case (byte)'{':
var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes)); var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes));
ret = jObj1["Identifier"] != null ret = jObj1["Identifier"] != null
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj1) ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj1)
: DesignBase.LoadDesignBase(_customize, _items, jObj1); : DesignBase.LoadDesignBase(customizeService, items, jObj1);
break; break;
case 1: case 1:
case 2: case 2:
case 4: case 4:
ret = _designs.CreateTemporary(); ret = designs.CreateTemporary();
ret.MigrateBase64(_customize, _items, _humans, base64); ret.MigrateBase64(customizeService, items, humans, base64);
break; break;
case 3: case 3:
{ {
@ -116,8 +117,8 @@ public class DesignConverter(
var jObj2 = JObject.Parse(decompressed); var jObj2 = JObject.Parse(decompressed);
Debug.Assert(version == 3); Debug.Assert(version == 3);
ret = jObj2["Identifier"] != null ret = jObj2["Identifier"] != null
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2) ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2)
: DesignBase.LoadDesignBase(_customize, _items, jObj2); : DesignBase.LoadDesignBase(customizeService, items, jObj2);
break; break;
} }
case 5: case 5:
@ -127,8 +128,8 @@ public class DesignConverter(
var jObj2 = JObject.Parse(decompressed); var jObj2 = JObject.Parse(decompressed);
Debug.Assert(version == 5); Debug.Assert(version == 5);
ret = jObj2["Identifier"] != null ret = jObj2["Identifier"] != null
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2) ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2)
: DesignBase.LoadDesignBase(_customize, _items, jObj2); : DesignBase.LoadDesignBase(customizeService, items, jObj2);
break; break;
} }
case 6: case 6:
@ -137,8 +138,8 @@ public class DesignConverter(
var jObj2 = JObject.Parse(decompressed); var jObj2 = JObject.Parse(decompressed);
Debug.Assert(version == 6); Debug.Assert(version == 6);
ret = jObj2["Identifier"] != null ret = jObj2["Identifier"] != null
? Design.LoadDesign(saveService, _customize, _items, _linkLoader, jObj2) ? Design.LoadDesign(saveService, customizeService, items, linkLoader, jObj2)
: DesignBase.LoadDesignBase(_customize, _items, jObj2); : DesignBase.LoadDesignBase(customizeService, items, jObj2);
break; break;
} }
@ -177,7 +178,7 @@ public class DesignConverter(
{ {
var index = (int)slot.ToIndex(); var index = (int)slot.ToIndex();
var armor = armors[index]; 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 (!item.Valid)
{ {
if (!skipWarnings) if (!skipWarnings)
@ -188,20 +189,20 @@ public class DesignConverter(
yield return (slot, item, armor.Stains); 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) if (!skipWarnings && !mh.Valid)
{ {
Glamourer.Log.Warning($"Appearance data {mainhand} for mainhand weapon invalid, item could not be identified."); 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); 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) if (!skipWarnings && !oh.Valid)
{ {
Glamourer.Log.Warning($"Appearance data {offhand} for offhand weapon invalid, item could not be identified."); 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) if (!oh.Valid)
oh = ItemManager.NothingItem(FullEquipType.Shield); oh = ItemManager.NothingItem(FullEquipType.Shield);
} }

View file

@ -75,7 +75,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
Glamourer.Log.Debug($"Changed customize {idx.ToName()} in design {design.Identifier} from {oldValue.Value} to {value.Value}."); Glamourer.Log.Debug($"Changed customize {idx.ToName()} in design {design.Identifier} from {oldValue.Value} to {value.Value}.");
SaveService.QueueSave(design); 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)));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -91,7 +91,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
Glamourer.Log.Debug($"Changed entire customize with resulting flags {applied} and {changed}."); Glamourer.Log.Debug($"Changed entire customize with resulting flags {applied} and {changed}.");
SaveService.QueueSave(design); 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)));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -106,7 +106,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
Glamourer.Log.Debug($"Set customize parameter {flag} in design {design.Identifier} from {old} to {@new}."); Glamourer.Log.Debug($"Set customize parameter {flag} in design {design.Identifier} from {old} to {@new}.");
SaveService.QueueSave(design); 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)));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -130,9 +130,9 @@ public class DesignEditor(
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Set {EquipSlot.MainHand.ToName()} weapon in design {design.Identifier} from {currentMain.Name} ({currentMain.ItemId}) to {item.Name} ({item.ItemId})."); $"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, new WeaponTransaction(currentMain, currentOff, currentGauntlets, item, newOff ?? currentOff,
newGauntlets ?? currentGauntlets)); newGauntlets ?? currentGauntlets)));
return; return;
} }
case EquipSlot.OffHand: case EquipSlot.OffHand:
@ -150,8 +150,8 @@ public class DesignEditor(
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Set {EquipSlot.OffHand.ToName()} weapon in design {design.Identifier} from {currentOff.Name} ({currentOff.ItemId}) to {item.Name} ({item.ItemId})."); $"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, DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Weapon, design,
new WeaponTransaction(currentMain, currentOff, currentGauntlets, currentMain, item, currentGauntlets)); new WeaponTransaction(currentMain, currentOff, currentGauntlets, currentMain, item, currentGauntlets)));
return; return;
} }
default: default:
@ -167,7 +167,7 @@ public class DesignEditor(
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Set {slot.ToName()} equipment piece in design {design.Identifier} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId})."); $"Set {slot.ToName()} equipment piece in design {design.Identifier} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}).");
SaveService.QueueSave(design); 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; return;
} }
} }
@ -187,7 +187,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set {slot} bonus item to {item}."); 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)));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -204,7 +204,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stains}."); 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)));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -227,7 +227,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set crest visibility of {slot} equipment piece to {crest}."); 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)));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -240,7 +240,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set value of {metaIndex} to {value}."); 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) 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.")}"); Glamourer.Log.Debug($"Changed advanced dye value for {index} to {(revert ? "Revert." : "no longer Revert.")}");
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); 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) public void ChangeMaterialValue(Design design, MaterialValueIndex index, ColorRow? row)
@ -288,7 +288,7 @@ public class DesignEditor(
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.DelaySave(design); 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) 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}."); Glamourer.Log.Debug($"Changed application of advanced dye for {index} to {value}.");
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); 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)));
} }

View file

@ -1,5 +1,4 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Glamourer.Designs.History;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Services; using Glamourer.Services;
using Luna; using Luna;
@ -10,41 +9,50 @@ public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredSer
{ {
private readonly DesignFileSystemSaver _saver; private readonly DesignFileSystemSaver _saver;
private readonly DesignChanged _designChanged; 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) : base("DesignFileSystem", log, true)
{ {
_designChanged = designChanged; _designChanged = designChanged;
_tabSelected = tabSelected;
_saver = new DesignFileSystemSaver(log, this, saveService, designs); _saver = new DesignFileSystemSaver(log, this, saveService, designs);
_saver.Load(); _saver.Load();
_designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignFileSystem); _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.ReloadedAll: _saver.Load(); break;
case DesignChanged.Type.Created: case DesignChanged.Type.Created:
var parent = Root; var parent = Root;
if (design.Path.Folder.Length > 0) if (arguments.Design.Path.Folder.Length > 0)
try try
{ {
parent = FindOrCreateAllFolders(design.Path.Folder); parent = FindOrCreateAllFolders(arguments.Design.Path.Folder);
} }
catch (Exception ex) catch (Exception ex)
{ {
Glamourer.Messager.NotificationMessage(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); 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); Selection.Select(data);
break; break;
case DesignChanged.Type.Deleted: case DesignChanged.Type.Deleted:
if (design.Node is { } node) if (arguments.Design.Node is { } node)
{ {
if (node.Selected) if (node.Selected)
Selection.UnselectAll(); Selection.UnselectAll();
@ -52,8 +60,8 @@ public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredSer
} }
break; break;
case DesignChanged.Type.Renamed when design.Path.SortName is null: case DesignChanged.Type.Renamed when arguments.Design.Path.SortName is null:
RenameWithDuplicates(design.Node!, design.Path.GetIntendedName(design.Name.Text)); RenameWithDuplicates(arguments.Design.Node!, arguments.Design.Path.GetIntendedName(arguments.Design.Name));
break; break;
// TODO: Maybe add path changes? // TODO: Maybe add path changes?
} }
@ -61,6 +69,7 @@ public sealed class DesignFileSystem : BaseFileSystem, IDisposable, IRequiredSer
public void Dispose() public void Dispose()
{ {
_tabSelected.Unsubscribe(OnTabSelected);
_designChanged.Unsubscribe(OnDesignChanged); _designChanged.Unsubscribe(OnDesignChanged);
} }
} }

View file

@ -7,7 +7,7 @@ using Glamourer.GameData;
using Glamourer.Interop.Material; using Glamourer.Interop.Material;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
using Glamourer.Services; using Glamourer.Services;
using OtterGui.Extensions; using Luna;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
@ -16,7 +16,7 @@ using Penumbra.GameData.Enums;
namespace Glamourer.Designs; namespace Glamourer.Designs;
public sealed class DesignManager : DesignEditor public sealed class DesignManager : DesignEditor, IService
{ {
public readonly DesignStorage Designs; public readonly DesignStorage Designs;
private readonly HumanModelList _humans; private readonly HumanModelList _humans;
@ -89,7 +89,7 @@ public sealed class DesignManager : DesignEditor
Glamourer.Log.Information( Glamourer.Log.Information(
$"Loaded {Designs.Count} designs in {stopwatch.ElapsedMilliseconds} ms.{(skipped > 0 ? $" Skipped loading {skipped} designs due to errors." : string.Empty)}"); $"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!));
} }
/// <summary> Create a new temporary design without adding it to the manager. </summary> /// <summary> Create a new temporary design without adding it to the manager. </summary>
@ -116,7 +116,7 @@ public sealed class DesignManager : DesignEditor
Designs.Add(design); Designs.Add(design);
Glamourer.Log.Debug($"Added new design {design.Identifier}."); Glamourer.Log.Debug($"Added new design {design.Identifier}.");
SaveService.ImmediateSave(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; return design;
} }
@ -141,7 +141,7 @@ public sealed class DesignManager : DesignEditor
Designs.Add(design); Designs.Add(design);
Glamourer.Log.Debug($"Added new design {design.Identifier} by cloning Temporary Design."); Glamourer.Log.Debug($"Added new design {design.Identifier} by cloning Temporary Design.");
SaveService.ImmediateSave(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; return design;
} }
@ -162,7 +162,7 @@ public sealed class DesignManager : DesignEditor
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Added new design {design.Identifier} by cloning {clone.Identifier.ToString()}."); $"Added new design {design.Identifier} by cloning {clone.Identifier.ToString()}.");
SaveService.ImmediateSave(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; return design;
} }
@ -173,7 +173,7 @@ public sealed class DesignManager : DesignEditor
--d.Index; --d.Index;
Designs.RemoveAt(design.Index); Designs.RemoveAt(design.Index);
SaveService.ImmediateDelete(design); SaveService.ImmediateDelete(design);
DesignChanged.Invoke(DesignChanged.Type.Deleted, design, null); DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Deleted, design));
} }
#endregion #endregion
@ -183,7 +183,7 @@ public sealed class DesignManager : DesignEditor
/// <summary> Rename a design. </summary> /// <summary> Rename a design. </summary>
public void Rename(Design design, string newName) public void Rename(Design design, string newName)
{ {
var oldName = design.Name.Text; var oldName = design.Name;
if (oldName == newName) if (oldName == newName)
return; return;
@ -191,7 +191,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Renamed design {design.Identifier}."); 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)));
} }
/// <summary> Change the description of a design. </summary> /// <summary> Change the description of a design. </summary>
@ -205,7 +205,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Changed description of design {design.Identifier}."); 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)));
} }
/// <summary> Change the associated color of a design. </summary> /// <summary> Change the associated color of a design. </summary>
@ -219,7 +219,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Changed color of design {design.Identifier}."); 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)));
} }
/// <summary> Add a new tag to a design. The tags remain sorted. </summary> /// <summary> Add a new tag to a design. The tags remain sorted. </summary>
@ -233,7 +233,7 @@ public sealed class DesignManager : DesignEditor
var idx = design.Tags.AsEnumerable().IndexOf(tag); var idx = design.Tags.AsEnumerable().IndexOf(tag);
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}."); 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)));
} }
/// <summary> Remove a tag from a design by its index. </summary> /// <summary> Remove a tag from a design by its index. </summary>
@ -247,7 +247,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Removed tag {oldTag} at {tagIdx} from design {design.Identifier}."); 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)));
} }
/// <summary> Rename a tag from a design by its index. The tags stay sorted.</summary> /// <summary> Rename a tag from a design by its index. The tags stay sorted.</summary>
@ -262,8 +262,8 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags."); Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags.");
DesignChanged.Invoke(DesignChanged.Type.ChangedTag, design, DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.ChangedTag, design,
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag))); new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag))));
} }
/// <summary> Add an associated mod to a design. </summary> /// <summary> Add an associated mod to a design. </summary>
@ -275,7 +275,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Added associated mod {mod.DirectoryName} to design {design.Identifier}."); 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)));
} }
/// <summary> Remove an associated mod from a design. </summary> /// <summary> Remove an associated mod from a design. </summary>
@ -287,7 +287,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Removed associated mod {mod.DirectoryName} from design {design.Identifier}."); 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)));
} }
/// <summary> Add or update an associated mod to a design. </summary> /// <summary> Add or update an associated mod to a design. </summary>
@ -300,12 +300,12 @@ public sealed class DesignManager : DesignEditor
if (hasOldSettings) if (hasOldSettings)
{ {
Glamourer.Log.Debug($"Updated associated mod {mod.DirectoryName} from design {design.Identifier}."); 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 else
{ {
Glamourer.Log.Debug($"Added associated mod {mod.DirectoryName} from design {design.Identifier}."); 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); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set design {design.Identifier} to {(value ? "no longer be " : string.Empty)} write-protected."); 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));
} }
/// <summary> Set the quick design bar display status of a design. </summary> /// <summary> Set the quick design bar display status of a design. </summary>
@ -330,7 +330,7 @@ public sealed class DesignManager : DesignEditor
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug( Glamourer.Log.Debug(
$"Set design {design.Identifier} to {(!value ? "no longer be " : string.Empty)} displayed in the quick design bar."); $"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 #endregion
@ -345,7 +345,7 @@ public sealed class DesignManager : DesignEditor
design.ForcedRedraw = forcedRedraw; design.ForcedRedraw = forcedRedraw;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set {design.Identifier} to {(forcedRedraw ? string.Empty : "not")} force redraws."); 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) public void ChangeResetAdvancedDyes(Design design, bool resetAdvancedDyes)
@ -356,7 +356,7 @@ public sealed class DesignManager : DesignEditor
design.ResetAdvancedDyes = resetAdvancedDyes; design.ResetAdvancedDyes = resetAdvancedDyes;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set {design.Identifier} to {(resetAdvancedDyes ? string.Empty : "not")} reset advanced dyes."); 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) public void ChangeResetTemporarySettings(Design design, bool resetTemporarySettings)
@ -367,7 +367,7 @@ public sealed class DesignManager : DesignEditor
design.ResetTemporarySettings = resetTemporarySettings; design.ResetTemporarySettings = resetTemporarySettings;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set {design.Identifier} to {(resetTemporarySettings ? string.Empty : "not")} reset temporary settings."); 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));
} }
/// <summary> Change whether to apply a specific customize value. </summary> /// <summary> Change whether to apply a specific customize value. </summary>
@ -379,7 +379,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of customization {idx.ToName()} to {value}."); 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)));
} }
/// <summary> Change whether to apply a specific equipment piece. </summary> /// <summary> Change whether to apply a specific equipment piece. </summary>
@ -391,7 +391,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of {slot} equipment piece to {value}."); 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)));
} }
/// <summary> Change whether to apply a specific equipment piece. </summary> /// <summary> Change whether to apply a specific equipment piece. </summary>
@ -403,7 +403,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of {slot} bonus item to {value}."); 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)));
} }
/// <summary> Change whether to apply a specific stain. </summary> /// <summary> Change whether to apply a specific stain. </summary>
@ -415,7 +415,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of stain of {slot} equipment piece to {value}."); 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)));
} }
/// <summary> Change whether to apply a specific crest visibility. </summary> /// <summary> Change whether to apply a specific crest visibility. </summary>
@ -427,7 +427,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of crest visibility of {slot} equipment piece to {value}."); 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)));
} }
/// <summary> Change the application value of one of the meta flags. </summary> /// <summary> Change the application value of one of the meta flags. </summary>
@ -439,7 +439,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of {metaIndex} to {value}."); 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)));
} }
/// <summary> Change the application value of a customize parameter. </summary> /// <summary> Change the application value of a customize parameter. </summary>
@ -451,7 +451,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of parameter {flag} to {value}."); 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)));
} }
/// <summary> Change multiple application values at once. </summary> /// <summary> Change multiple application values at once. </summary>
@ -512,7 +512,6 @@ public sealed class DesignManager : DesignEditor
{ {
var text = File.ReadAllText(SaveService.FileNames.MigrationDesignFile); var text = File.ReadAllText(SaveService.FileNames.MigrationDesignFile);
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(text) ?? new Dictionary<string, string>(); var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(text) ?? new Dictionary<string, string>();
var migratedFileSystemPaths = new Dictionary<string, string>(dict.Count);
foreach (var (name, base64) in dict) foreach (var (name, base64) in dict)
{ {
try try
@ -529,7 +528,6 @@ public sealed class DesignManager : DesignEditor
if (!oldDesigns.Any(d => d.Name == design.Name && d.CreationDate == design.CreationDate)) if (!oldDesigns.Any(d => d.Name == design.Name && d.CreationDate == design.CreationDate))
{ {
Add(design, $"Migrated old design to {design.Identifier}."); Add(design, $"Migrated old design to {design.Identifier}.");
migratedFileSystemPaths.Add(design.Identifier.ToString(), name);
++successes; ++successes;
} }
else else
@ -632,7 +630,7 @@ public sealed class DesignManager : DesignEditor
if (!message.IsNullOrEmpty()) if (!message.IsNullOrEmpty())
Glamourer.Log.Debug(message); Glamourer.Log.Debug(message);
SaveService.ImmediateSave(design); SaveService.ImmediateSave(design);
DesignChanged.Invoke(DesignChanged.Type.Created, design, null); DesignChanged.Invoke(new DesignChanged.Arguments(DesignChanged.Type.Created, design));
} }
/// <summary> Split a given string into its folder path and its name, if <paramref name="handlePath"/> is true. </summary> /// <summary> Split a given string into its folder path and its name, if <paramref name="handlePath"/> is true. </summary>

View file

@ -1,8 +1,6 @@
using Glamourer.Api.Enums;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.State; using Glamourer.State;
using Luna; using Luna;
using Penumbra.GameData.Interop;
namespace Glamourer.Designs.History; 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; return;
if (data is not null) if (arguments.Transaction is not null)
AddStateTransaction(state, data); AddStateTransaction(arguments.State, arguments.Transaction);
} }
private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? data) private void OnDesignChanged(in DesignChanged.Arguments arguments)
{ {
if (_undoMode) if (_undoMode)
return; return;
if (data is not null) if (arguments.Transaction is not null)
AddDesignTransaction(design, data); AddDesignTransaction(arguments.Design, arguments.Transaction);
} }
} }

View file

@ -10,7 +10,7 @@ public interface ITransaction
public void Revert(IDesignEditor editor, object data); 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) public ITransaction? Merge(ITransaction older)
@ -20,7 +20,7 @@ public readonly record struct CustomizeTransaction(CustomizeIndex Slot, Customiz
=> editor.ChangeCustomize(data, Slot, Old, ApplySettings.Manual); => 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) public ITransaction? Merge(ITransaction older)
@ -30,7 +30,7 @@ public readonly record struct EntireCustomizeTransaction(CustomizeFlag Apply, Cu
=> editor.ChangeEntireCustomize(data, Old, Apply, ApplySettings.Manual); => 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) 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); => 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) public ITransaction? Merge(ITransaction older)
@ -50,7 +50,7 @@ public readonly record struct BonusItemTransaction(BonusItemFlag Slot, EquipItem
=> editor.ChangeBonusItem(data, Slot, Old, ApplySettings.Manual); => editor.ChangeBonusItem(data, Slot, Old, ApplySettings.Manual);
} }
public readonly record struct WeaponTransaction( public record WeaponTransaction(
EquipItem OldMain, EquipItem OldMain,
EquipItem OldOff, EquipItem OldOff,
EquipItem OldGauntlets, 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) 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); => 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) 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); => 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) public ITransaction? Merge(ITransaction older)
@ -102,7 +102,7 @@ public readonly record struct ParameterTransaction(CustomizeParameterFlag Slot,
=> editor.ChangeCustomizeParameter(data, Slot, Old, ApplySettings.Manual); => 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 : ITransaction
{ {
public ITransaction? Merge(ITransaction older) public ITransaction? Merge(ITransaction older)

View file

@ -1,5 +1,4 @@
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Designs.History;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Services; using Glamourer.Services;
using Luna; using Luna;
@ -32,7 +31,7 @@ public sealed class DesignLinkManager : IService, IDisposable
parent.LastEdit = DateTimeOffset.UtcNow; parent.LastEdit = DateTimeOffset.UtcNow;
_saveService.QueueSave(parent); _saveService.QueueSave(parent);
Glamourer.Log.Debug($"Moved link from {orderFrom} {idxFrom} to {idxTo} {orderTo}."); 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) public void AddDesignLink(Design parent, Design child, LinkOrder order)
@ -43,7 +42,7 @@ public sealed class DesignLinkManager : IService, IDisposable
parent.LastEdit = DateTimeOffset.UtcNow; parent.LastEdit = DateTimeOffset.UtcNow;
_saveService.QueueSave(parent); _saveService.QueueSave(parent);
Glamourer.Log.Debug($"Added new {order} link to {child.Identifier} for {parent.Identifier}."); 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) public void RemoveDesignLink(Design parent, int idx, LinkOrder order)
@ -54,7 +53,7 @@ public sealed class DesignLinkManager : IService, IDisposable
parent.LastEdit = DateTimeOffset.UtcNow; parent.LastEdit = DateTimeOffset.UtcNow;
_saveService.QueueSave(parent); _saveService.QueueSave(parent);
Glamourer.Log.Debug($"Removed the {order} link at {idx} for {parent.Identifier}."); 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) public void ChangeApplicationType(Design parent, int idx, LinkOrder order, ApplicationType applicationType)
@ -64,22 +63,23 @@ public sealed class DesignLinkManager : IService, IDisposable
return; return;
_saveService.QueueSave(parent); _saveService.QueueSave(parent);
Glamourer.Log.Debug($"Changed link application type from {old} to {applicationType} for design link {order} {idx + 1} in design {parent.Identifier}."); Glamourer.Log.Debug(
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null); $"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; return;
foreach (var design in _storage) foreach (var design in _storage)
{ {
if (!design.Links.Remove(deletedDesign)) if (!design.Links.Remove(arguments.Design))
continue; continue;
design.LastEdit = DateTimeOffset.UtcNow; 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); _saveService.QueueSave(design);
} }
} }

View file

@ -1,6 +1,6 @@
using Glamourer.Automation; using Glamourer.Automation;
using Luna;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Filesystem;
namespace Glamourer.Designs.Links; namespace Glamourer.Designs.Links;

View file

@ -3,7 +3,7 @@ using Luna;
namespace Glamourer.Designs.Special; 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 Random _rng = new();
private readonly WeakReference<Design> _lastDesign = new(null!, false); private readonly WeakReference<Design> _lastDesign = new(null!, false);
@ -34,7 +34,7 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS
=> Design(designs); => Design(designs);
public Design? Design(IDesignPredicate predicate) public Design? Design(IDesignPredicate predicate)
=> Design(predicate.Get(designs, fileSystem).ToList()); => Design(predicate.Get(designs).ToList());
public Design? Design(IReadOnlyList<IDesignPredicate> predicates) public Design? Design(IReadOnlyList<IDesignPredicate> predicates)
{ {
@ -42,7 +42,7 @@ public class RandomDesignGenerator(DesignStorage designs, DesignFileSystem fileS
{ {
0 => Design(), 0 => Design(),
1 => Design(predicates[0]), 1 => Design(predicates[0]),
_ => Design(IDesignPredicate.Get(predicates, designs, fileSystem).ToList()), _ => Design(IDesignPredicate.Get(predicates, designs).ToList()),
}; };
} }

View file

@ -1,61 +1,59 @@
using OtterGui.Classes; namespace Glamourer.Designs.Special;
namespace Glamourer.Designs.Special;
public interface IDesignPredicate 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) bool Invoke((Design Design, string Name, string Identifier, string Path) args)
=> Invoke(args.Design, args.LowerName, args.Identifier, args.LowerPath); => Invoke(args.Design, args.Name, args.Identifier, args.Path);
public IEnumerable<Design> Get(IEnumerable<Design> designs, DesignFileSystem fileSystem) IEnumerable<Design> Get(IEnumerable<Design> designs)
=> designs.Select(Transform) => designs.Select(Transform)
.Where(Invoke) .Where(Invoke)
.Select(t => t.Design); .Select(t => t.Design);
public static IEnumerable<Design> Get(IReadOnlyList<IDesignPredicate> predicates, IEnumerable<Design> designs, DesignFileSystem fileSystem) static IEnumerable<Design> Get(IReadOnlyList<IDesignPredicate> predicates, IEnumerable<Design> designs)
=> predicates.Count > 0 => predicates.Count > 0
? designs.Select(Transform) ? designs.Select(Transform)
.Where(t => predicates.Any(p => p.Invoke(t))) .Where(t => predicates.Any(p => p.Invoke(t)))
.Select(t => t.Design) .Select(t => t.Design)
: designs; : designs;
private static (Design Design, string LowerName, string Identifier, string LowerPath) Transform(Design d) private static (Design Design, string Name, string Identifier, string Path) Transform(Design d)
=> (d, d.Name.Lower, d.Identifier.ToString(), d.Path.CurrentPath.ToLowerInvariant()); => (d, d.Name, d.Identifier.ToString(), d.Path.CurrentPath);
} }
public static class RandomPredicate public static class RandomPredicate
{ {
public readonly struct StartsWith(string value) : IDesignPredicate 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) public bool Invoke(Design design, string name, string identifier, string path)
=> lowerPath.StartsWith(Value.Lower); => path.StartsWith(Value, StringComparison.OrdinalIgnoreCase);
public override string ToString() public override string ToString()
=> $"/{Value.Text}"; => $"/{Value}";
} }
public readonly struct Contains(string value) : IDesignPredicate 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; return true;
if (identifier.Contains(Value.Lower)) if (identifier.Contains(Value, StringComparison.OrdinalIgnoreCase))
return true; return true;
if (lowerPath.Contains(Value.Lower)) if (path.Contains(Value, StringComparison.OrdinalIgnoreCase))
return true; return true;
return false; return false;
} }
public override string ToString() public override string ToString()
=> Value.Text; => Value;
} }
public readonly struct Exact(Exact.Type type, string value) : IDesignPredicate public readonly struct Exact(Exact.Type type, string value) : IDesignPredicate
@ -69,25 +67,25 @@ public static class RandomPredicate
Color, Color,
} }
public Type Which { get; } = type; public Type Which { get; } = type;
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)
=> Which switch => Which switch
{ {
Type.Name => lowerName == Value.Lower, Type.Name => string.Equals(name, Value, StringComparison.OrdinalIgnoreCase),
Type.Path => lowerPath == Value.Lower, Type.Path => string.Equals(path, Value, StringComparison.OrdinalIgnoreCase),
Type.Identifier => identifier == Value.Lower, Type.Identifier => string.Equals(identifier, Value, StringComparison.OrdinalIgnoreCase),
Type.Tag => IsContained(Value, design.Tags), Type.Tag => IsContained(Value, design.Tags),
Type.Color => design.Color == Value, Type.Color => string.Equals(design.Color, Value, StringComparison.OrdinalIgnoreCase),
_ => false, _ => false,
}; };
private static bool IsContained(LowerString value, IEnumerable<string> data) private static bool IsContained(string value, IEnumerable<string> data)
=> data.Any(t => t == value); => data.Any(t => string.Equals(t, value, StringComparison.OrdinalIgnoreCase));
public override string ToString() 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) public static IDesignPredicate CreateSinglePredicate(string restriction)
@ -125,7 +123,7 @@ public static class RandomPredicate
public static List<IDesignPredicate> GeneratePredicates(string restrictions) public static List<IDesignPredicate> GeneratePredicates(string restrictions)
{ {
if (restrictions.Length == 0) if (restrictions.Length is 0)
return []; return [];
List<IDesignPredicate> predicates = new(1); List<IDesignPredicate> predicates = new(1);
@ -153,9 +151,9 @@ public static class RandomPredicate
public static string GeneratePredicateString(IReadOnlyCollection<IDesignPredicate> predicates) public static string GeneratePredicateString(IReadOnlyCollection<IDesignPredicate> predicates)
{ {
if (predicates.Count == 0) if (predicates.Count is 0)
return string.Empty; return string.Empty;
if (predicates.Count == 1) if (predicates.Count is 1)
return predicates.First()!.ToString()!; return predicates.First()!.ToString()!;
return $"{{{string.Join("; ", predicates)}}}"; return $"{{{string.Join("; ", predicates)}}}";

View file

@ -1,16 +1,16 @@
using OtterGui.Classes; using Luna;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary>
/// Triggered when the auto-reload gear setting is changed in glamourer configuration. /// Triggered when the auto-reload gear setting is changed in glamourer configuration.
/// </summary> /// </summary>
public sealed class AutoRedrawChanged() public sealed class AutoRedrawChanged(Logger log)
: EventWrapper<bool, AutoRedrawChanged.Priority>(nameof(AutoRedrawChanged)) : EventBase<bool, AutoRedrawChanged.Priority>(nameof(AutoRedrawChanged), log)
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="Api.StateApi.OnGPoseChange"/> /// <seealso cref="Api.StateApi.OnGPoseChange"/>
StateApi = int.MinValue, StateApi = int.MinValue,
} }
} }

View file

@ -1,64 +1,60 @@
using Glamourer.Automation; using Glamourer.Automation;
using OtterGui.Classes; using Glamourer.Designs;
using Luna;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Structs;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when an automated design is changed in any way. </summary>
/// Triggered when an automated design is changed in any way. public sealed class AutomationChanged(Logger log)
/// <list type="number"> : EventBase<AutomationChanged.Arguments, AutomationChanged.Priority>(nameof(AutomationChanged), log)
/// <item>Parameter is the type of the change </item>
/// <item>Parameter is the added or changed design set or null on deletion. </item>
/// <item>Parameter is additional data depending on the type of change. </item>
/// </list>
/// </summary>
public sealed class AutomationChanged()
: EventWrapper<AutomationChanged.Type, AutoDesignSet?, object?, AutomationChanged.Priority>(nameof(AutomationChanged))
{ {
public enum Type public enum Type
{ {
/// <summary> 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)]. </summary> /// <summary> Add a new set. Names and identifiers do not have to be unique. It is not enabled by default. </summary>
AddedSet, AddedSet,
/// <summary> Delete a given set. Additional data is the index it got removed from [int].</summary> /// <summary> Delete a given set. </summary>
DeletedSet, DeletedSet,
/// <summary> Rename a given set. Names do not have to be unique. Additional data is the old name and the new name [(string, string)]. </summary> /// <summary> Rename a given set. Names do not have to be unique. </summary>
RenamedSet, RenamedSet,
/// <summary> 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)]. </summary> /// <summary> Move a given set to a different position. </summary>
MovedSet, MovedSet,
/// <summary> 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?)]. </summary> /// <summary> Change the identifier a given set is associated with to another one. </summary>
ChangeIdentifier, ChangeIdentifier,
/// <summary> Toggle the enabled state of a given set. Additional data is the thus disabled other set, if any [AutoDesignSet?]. </summary> /// <summary> Toggle the enabled state of a given set. </summary>
ToggleSet, ToggleSet,
/// <summary> Change the used base state of a given set. Additional data is prior and new base. [(AutoDesignSet.Base, AutoDesignSet.Base)]. </summary> /// <summary> Change the used base state of a given set. </summary>
ChangedBase, ChangedBase,
/// <summary> Change the resetting of temporary settings for a given set. Additional data is the new value. </summary> /// <summary> Change the resetting of temporary settings for a given set. </summary>
ChangedTemporarySettingsReset, ChangedTemporarySettingsReset,
/// <summary> Add a new associated design to a given set. Additional data is the index it got added at [int]. </summary> /// <summary> Add a new associated design to a given set. </summary>
AddedDesign, AddedDesign,
/// <summary> Remove a given associated design from a given set. Additional data is the index it got removed from [int]. </summary> /// <summary> Remove a given associated design from a given set. </summary>
DeletedDesign, DeletedDesign,
/// <summary> 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)]. </summary> /// <summary> Move a given associated design in the list of a given set. </summary>
MovedDesign, MovedDesign,
/// <summary> 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)]. </summary> /// <summary> Change the linked design in an associated design for a given set. </summary>
ChangedDesign, ChangedDesign,
/// <summary> 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)]. </summary> /// <summary> Change the job condition in an associated design for a given set. </summary>
ChangedConditions, ChangedConditions,
/// <summary> 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)]. </summary> /// <summary> Change the application type in an associated design for a given set. </summary>
ChangedType, ChangedType,
/// <summary> 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)] </summary> /// <summary> Change the additional data for a specific design type. </summary>
ChangedData, ChangedData,
} }
@ -76,4 +72,92 @@ public sealed class AutomationChanged()
/// <seealso cref="Gui.Tabs.AutomationTab.RandomRestrictionDrawer.OnAutomationChange"/> /// <seealso cref="Gui.Tabs.AutomationTab.RandomRestrictionDrawer.OnAutomationChange"/>
RandomRestrictionDrawer = -1, RandomRestrictionDrawer = -1,
} }
/// <param name="Type"> The type of change. </param>
public record Arguments(Type Type, AutoDesignSet Set)
{
public T As<T>() where T : Arguments
=> (T)this;
}
/// <param name="Set"> The added set. </param>
/// <param name="Index"> The index the set was added at. </param>
/// <param name="Name"> The name of the added set. </param>
public sealed record AddedSetArguments(AutoDesignSet Set, int Index, string Name) : Arguments(Type.AddedSet, Set);
/// <param name="Set"> The deleted set. </param>
/// <param name="Index"> The index the set was deleted from. </param>
public sealed record DeletedSetArguments(AutoDesignSet Set, int Index) : Arguments(Type.DeletedSet, Set);
/// <param name="Set"> The renamed set. </param>
/// <param name="OldName"> The old name of the set. </param>
/// <param name="NewName"> The new name of the set. </param>
public sealed record RenamedSetArguments(AutoDesignSet Set, string OldName, string NewName) : Arguments(Type.RenamedSet, Set);
/// <param name="Set"> The moved set. </param>
/// <param name="OldIndex"> The index the set was moved from. </param>
/// <param name="NewIndex"> The index the set was moved to. </param>
public sealed record MovedSetArguments(AutoDesignSet Set, int OldIndex, int NewIndex) : Arguments(Type.MovedSet, Set);
/// <param name="Set"> The set that got its associated identifiers changed. </param>
/// <param name="OldIdentifiers"> The prior identifiers that got removed. </param>
/// <param name="NewIdentifier"> The new identifiers associated with the set. </param>
/// <param name="DisabledSet"> A set that was associated with the new identifiers before and got disabled through this change, or null. </param>
public sealed record ChangeIdentifierArguments(
AutoDesignSet Set,
ActorIdentifier[] OldIdentifiers,
ActorIdentifier NewIdentifier,
AutoDesignSet? DisabledSet) : Arguments(Type.ChangeIdentifier, Set);
/// <param name="Set"> The set that got toggled on or off. </param>
/// <param name="DisabledSet"> A set with the same association that got disabled due to this change, or null. </param>
public sealed record ToggleSetArguments(AutoDesignSet Set, AutoDesignSet? DisabledSet) : Arguments(Type.ToggleSet, Set);
/// <param name="Set"> The set that changed its base state. </param>
/// <param name="OldBase"> The old base state of the set. </param>
/// <param name="NewBase"> The new base state of the set. </param>
public sealed record ChangedBaseArguments(AutoDesignSet Set, AutoDesignSet.Base OldBase, AutoDesignSet.Base NewBase) : Arguments(Type.ChangedBase, Set);
/// <param name="Set"> The set that changed whether it resets all temporary settings. </param>
/// <param name="NewValue"> The new state of resetting temporary settings. </param>
public sealed record ChangedTemporarySettingsResetArguments(AutoDesignSet Set, bool NewValue) : Arguments(Type.ChangedTemporarySettingsReset, Set);
/// <param name="Set"> The set that added a new design. </param>
/// <param name="Index"> The index the new design was added in the set. </param>
public sealed record AddedDesignArguments(AutoDesignSet Set, int Index) : Arguments(Type.AddedDesign, Set);
/// <param name="Set"> The set that removed a design. </param>
/// <param name="Index"> The index the design was removed from. </param>
public sealed record DeletedDesignArguments(AutoDesignSet Set, int Index) : Arguments(Type.DeletedDesign, Set);
/// <param name="Set"> The set that moved a design. </param>
/// <param name="OldIndex"> The index the design was moved from in the set. </param>
/// <param name="NewIndex"> The index the design was moved from to the set. </param>
public sealed record MovedDesignArguments(AutoDesignSet Set, int OldIndex, int NewIndex) : Arguments(Type.MovedDesign, Set);
/// <param name="Set"> The set that changed a design. </param>
/// <param name="DesignIndex"> The index of the changed design. </param>
/// <param name="OldDesign"> The design previously assigned to the set. </param>
/// <param name="NewDesign"> The design the old one was changed to. </param>
public sealed record ChangedDesignArguments(AutoDesignSet Set, int DesignIndex, IDesignStandIn OldDesign, IDesignStandIn NewDesign)
: Arguments(Type.ChangedDesign, Set);
/// <param name="Set"> The set that changed a job group condition. </param>
/// <param name="DesignIndex"> The index of the changed design. </param>
/// <param name="OldGroup"> The prior job condition for the design. </param>
/// <param name="NewGroup"> The new job condition for the design. </param>
public sealed record ChangedConditionsArguments(AutoDesignSet Set, int DesignIndex, JobGroup OldGroup, JobGroup NewGroup)
: Arguments(Type.ChangedConditions, Set);
/// <param name="Set"> The set that changed its application type. </param>
/// <param name="DesignIndex"> The index of the changed design. </param>
/// <param name="OldType"> The old application flags. </param>
/// <param name="NewType"> The new application flags. </param>
public sealed record ChangedTypeArguments(AutoDesignSet Set, int DesignIndex, ApplicationType OldType, ApplicationType NewType)
: Arguments(Type.ChangedType, Set);
/// <param name="Set"> The set that got a design data changed. </param>
/// <param name="DesignIndex"> The index of the changed design. </param>
/// <param name="NewData"> The new additional data for the changed design. </param>
public sealed record ChangedDataArguments(AutoDesignSet Set, int DesignIndex, object NewData) : Arguments(Type.ChangedData, Set);
} }

View file

@ -1,25 +1,32 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when a model flags a bonus slot for an update. </summary>
/// Triggered when a model flags a bonus slot for an update. public sealed class BonusSlotUpdating(Logger log)
/// <list type="number"> : EventBase<BonusSlotUpdating.Arguments, BonusSlotUpdating.Priority>(nameof(BonusSlotUpdating), log)
/// <item>Parameter is the model with a flagged slot. </item>
/// <item>Parameter is the bonus slot changed. </item>
/// <item>Parameter is the model values to change the bonus piece to. </item>
/// <item>Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. </item>
/// </list>
/// </summary>
public sealed class BonusSlotUpdating()
: EventWrapperRef34<Model, BonusItemFlag, CharacterArmor, ulong, BonusSlotUpdating.Priority>(nameof(BonusSlotUpdating))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnBonusSlotUpdating"/> /// <seealso cref="State.StateListener.OnBonusSlotUpdating"/>
StateListener = 0, StateListener = 0,
} }
public ref struct Arguments(Model model, BonusItemFlag flag, ref CharacterArmor armor, ref ulong returnValue)
{
/// <summary> The draw object with an updated bonus slot. </summary>
public readonly Model Model = model;
/// <summary> The updated slot. </summary>
public readonly BonusItemFlag Slot = flag;
/// <summary> The model data for the new bonus slot. This can be changed. </summary>
public ref CharacterArmor Armor = ref armor;
/// <summary> The return value of the event. If this is <see cref="ulong.MaxValue"/>, the original function will be called and its return value used, otherwise this value will be returned. </summary>
public ref ulong ReturnValue = ref returnValue;
}
} }

View file

@ -2,20 +2,13 @@ using Glamourer.Designs;
using Glamourer.Designs.History; using Glamourer.Designs.History;
using Glamourer.Gui; using Glamourer.Gui;
using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Gui.Tabs.DesignTab;
using OtterGui.Classes; using Luna;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when a Design is edited in any way. </summary>
/// Triggered when a Design is edited in any way. public sealed class DesignChanged(Logger log)
/// <list type="number"> : EventBase<DesignChanged.Arguments, DesignChanged.Priority>(nameof(DesignChanged), log)
/// <item>Parameter is the type of the change </item>
/// <item>Parameter is the changed Design. </item>
/// <item>Parameter is any additional data depending on the type of change. </item>
/// </list>
/// </summary>
public sealed class DesignChanged()
: EventWrapper<DesignChanged.Type, Design, ITransaction?, DesignChanged.Priority>(nameof(DesignChanged))
{ {
public enum Type public enum Type
{ {
@ -151,4 +144,10 @@ public sealed class DesignChanged()
/// <seealso cref="EditorHistory.OnDesignChanged" /> /// <seealso cref="EditorHistory.OnDesignChanged" />
EditorHistory = -1000, EditorHistory = -1000,
} }
/// <summary> Arguments for the DesignChanged event. </summary>
/// <param name="Type"> The type of changed. </param>
/// <param name="Design"> The changed design. </param>
/// <param name="Transaction"> A transaction with further data corresponding to the change. </param>
public readonly record struct Arguments(Type Type, Design Design, ITransaction? Transaction = null);
} }

View file

@ -1,25 +1,32 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when a model flags an equipment slot for an update. </summary>
/// Triggered when a model flags an equipment slot for an update. public sealed class EquipSlotUpdating(Logger log)
/// <list type="number"> : EventBase<EquipSlotUpdating.Arguments, EquipSlotUpdating.Priority>(nameof(EquipSlotUpdating), log)
/// <item>Parameter is the model with a flagged slot. </item>
/// <item>Parameter is the equipment slot changed. </item>
/// <item>Parameter is the model values to change the equipment piece to. </item>
/// <item>Parameter is the return value the function should return, if it is ulong.MaxValue, the original will be called and returned. </item>
/// </list>
/// </summary>
public sealed class EquipSlotUpdating()
: EventWrapperRef34<Model, EquipSlot, CharacterArmor, ulong, EquipSlotUpdating.Priority>(nameof(EquipSlotUpdating))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnEquipSlotUpdating"/> /// <seealso cref="State.StateListener.OnEquipSlotUpdating"/>
StateListener = 0, StateListener = 0,
} }
public ref struct Arguments(Model model, EquipSlot slot, ref CharacterArmor armor, ref ulong returnValue)
{
/// <summary> The draw object with an updated equipment slot. </summary>
public readonly Model Model = model;
/// <summary> The updated slot. </summary>
public readonly EquipSlot Slot = slot;
/// <summary> The model data for the new equipment slot. This can be changed. </summary>
public ref CharacterArmor Armor = ref armor;
/// <summary> The return value of the event. If this is <see cref="ulong.MaxValue"/>, the original function will be called and its return value used, otherwise this value will be returned. </summary>
public ref ulong ReturnValue = ref returnValue;
}
} }

View file

@ -1,23 +1,23 @@
using OtterGui.Classes; using Luna;
using Penumbra.String;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when the player equips a gear set. </summary>
/// Triggered when the player equips a gear set. public sealed class EquippedGearset(Logger log)
/// <list type="number"> : EventBase<EquippedGearset.Arguments, EquippedGearset.Priority>(nameof(EquippedGearset), log)
/// <item>Parameter is the name of the gear set. </item>
/// <item>Parameter is the id of the gear set. </item>
/// <item>Parameter is the id of the prior gear set. </item>
/// <item>Parameter is the id of the associated glamour. </item>
/// <item>Parameter is the job id of the associated job. </item>
/// </list>
/// </summary>
public sealed class EquippedGearset()
: EventWrapper<string, int, int, byte, byte, EquippedGearset.Priority>(nameof(EquippedGearset))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="Automation.AutoDesignApplier.OnEquippedGearset"/> /// <seealso cref="Automation.AutoDesignApplier.OnEquippedGearset"/>
AutoDesignApplier = 0, AutoDesignApplier = 0,
} }
/// <summary> Arguments for the EquippedGearset event. </summary>
/// <param name="Name"> The name of the equipped gear set. </param>
/// <param name="Id"> The ID of the equipped gear set.</param>
/// <param name="PriorId"> The ID of the gear set previously equipped.</param>
/// <param name="GlamourId"> The ID of the associated glamour plate. </param>
/// <param name="JobId"> The job ID of the associated job. </param>
public readonly record struct Arguments(ByteString Name, int Id, int PriorId, int GlamourId, byte JobId);
} }

View file

@ -1,9 +1,9 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using OtterGui.Classes; using Luna;
namespace Glamourer.Events; namespace Glamourer.Events;
public sealed class GPoseService : EventWrapper<bool, GPoseService.Priority> public sealed class GPoseService : EventBase<bool, GPoseService.Priority>
{ {
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly IClientState _state; private readonly IClientState _state;
@ -19,8 +19,8 @@ public sealed class GPoseService : EventWrapper<bool, GPoseService.Priority>
public bool InGPose { get; private set; } public bool InGPose { get; private set; }
public GPoseService(IFramework framework, IClientState state) public GPoseService(IFramework framework, IClientState state, Logger log)
: base(nameof(GPoseService)) : base(nameof(GPoseService), log)
{ {
_framework = framework; _framework = framework;
_state = state; _state = state;

View file

@ -1,21 +1,23 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary>
/// Triggers when the equipped gearset finished all LoadEquipment, LoadWeapon, and LoadCrest calls. (All Non-MetaData) /// 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. /// This defines an endpoint for when the gameState is updated.
/// <list type="number">
/// <item>The model draw object associated with the finished load (Also fired by other players on render) </item>
/// </list>
/// </summary> /// </summary>
public sealed class GearsetDataLoaded() public sealed class GearsetDataLoaded(Logger log)
: EventWrapper<Actor, Model, GearsetDataLoaded.Priority>(nameof(GearsetDataLoaded)) : EventBase<GearsetDataLoaded.Arguments, GearsetDataLoaded.Priority>(nameof(GearsetDataLoaded), log)
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnGearsetDataLoaded"/> /// <seealso cref="State.StateListener.OnGearsetDataLoaded"/>
StateListener = 0, StateListener = 0,
} }
/// <summary> Arguments for the GearsetDataLoaded event. </summary>
/// <param name="Actor"> The actor that loaded a gear set. </param>
/// <param name="Model"> The draw object that finished the load. </param>
public readonly record struct Arguments(Actor Actor, Model Model);
} }

View file

@ -1,21 +1,24 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when the visibility of head gear is changed. </summary>
/// Triggered when the visibility of head gear is changed. public sealed class HeadGearVisibilityChanged(Logger log)
/// <list type="number"> : EventBase<HeadGearVisibilityChanged.Arguments, HeadGearVisibilityChanged.Priority>(nameof(HeadGearVisibilityChanged), log)
/// <item>Parameter is the actor with changed head gear visibility. </item>
/// <item>Parameter is the new state. </item>
/// </list>
/// </summary>
public sealed class HeadGearVisibilityChanged()
: EventWrapperRef2<Actor, bool, HeadGearVisibilityChanged.Priority>(nameof(HeadGearVisibilityChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnHeadGearVisibilityChange"/> /// <seealso cref="State.StateListener.OnHeadGearVisibilityChange"/>
StateListener = 0, StateListener = 0,
} }
public ref struct Arguments(Actor actor, ref bool visible)
{
/// <summary> The actor whose head gear visibility changed. </summary>
public readonly Actor Actor = actor;
/// <summary> The new visibility state. </summary>
public ref bool Visible = ref visible;
}
} }

View file

@ -1,4 +1,4 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -10,12 +10,14 @@ namespace Glamourer.Events;
/// <item>Parameter is an array of slots updated and corresponding item ids and stains. </item> /// <item>Parameter is an array of slots updated and corresponding item ids and stains. </item>
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class MovedEquipment() public sealed class MovedEquipment(Logger log)
: EventWrapper<(EquipSlot, uint, StainIds)[], MovedEquipment.Priority>(nameof(MovedEquipment)) : EventBase<MovedEquipment.Arguments, MovedEquipment.Priority>(nameof(MovedEquipment), log)
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnMovedEquipment"/> /// <seealso cref="State.StateListener.OnMovedEquipment"/>
StateListener = 0, StateListener = 0,
} }
public readonly record struct Arguments(params (EquipSlot, uint, StainIds)[] Items);
} }

View file

@ -1,17 +1,10 @@
using OtterGui.Classes; using Luna;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when a new item or customization is unlocked. </summary>
/// Triggered when a new item or customization is unlocked. public sealed class ObjectUnlocked(Logger log)
/// <list type="number"> : EventBase<ObjectUnlocked.Arguments, ObjectUnlocked.Priority>(nameof(ObjectUnlocked), log), IRequiredService
/// <item>Parameter is the type of the unlocked object </item>
/// <item>Parameter is the id of the unlocked object. </item>
/// <item>Parameter is the timestamp of the unlock. </item>
/// </list>
/// </summary>
public sealed class ObjectUnlocked()
: EventWrapper<ObjectUnlocked.Type, uint, DateTimeOffset, ObjectUnlocked.Priority>(nameof(ObjectUnlocked))
{ {
public enum Type public enum Type
{ {
@ -21,8 +14,15 @@ public sealed class ObjectUnlocked()
public enum Priority public enum Priority
{ {
/// <seealso cref="Gui.Tabs.UnlocksTab.UnlockTable.OnObjectUnlock"/> /// <seealso cref="Gui.Tabs.UnlocksTab.UnlockTable.Cache.OnItemUnlock"/>
/// <remarks> Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. </remarks> /// <remarks> Currently used as a hack to make the unlock table dirty in it. If anything else starts using this, rework. </remarks>
UnlockTable = 0, UnlockTable = 0,
} }
/// <summary> Arguments for the ObjectUnlocked event. </summary>
/// <param name="Type"> The type of the unlocked object. </param>
/// <param name="Id"> The ID of the unlocked object. </param>
/// <param name="Timestamp"> The timestamp of the unlock event.</param>
public readonly record struct Arguments(Type Type, uint Id, DateTimeOffset Timestamp);
} }

View file

@ -1,22 +1,22 @@
using OtterGui.Classes; using Luna;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary>
/// Triggered when Penumbra is reloaded. /// Triggered when Penumbra is reloaded.
/// </summary> /// </summary>
public sealed class PenumbraReloaded() public sealed class PenumbraReloaded(Logger log)
: EventWrapper<PenumbraReloaded.Priority>(nameof(PenumbraReloaded)) : EventBase<PenumbraReloaded.Priority>(nameof(PenumbraReloaded), log)
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="Interop.ChangeCustomizeService.Restore"/> /// <seealso cref="global::Glamourer.Interop.ChangeCustomizeService.Restore"/>
ChangeCustomizeService = 0, ChangeCustomizeService = 0,
/// <seealso cref="Interop.VisorService.Restore"/> /// <seealso cref="global::Glamourer.Interop.VisorService.Restore"/>
VisorService = 0, VisorService = 0,
/// <seealso cref="Interop.VieraEarService.Restore"/> /// <seealso cref="global::Glamourer.Interop.VieraEarService.Restore"/>
VieraEarService = 0, VieraEarService = 0,
} }
} }

View file

@ -1,33 +1,37 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Designs.History; using Glamourer.Designs.History;
using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using OtterGui.Classes;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Luna;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when a state changes in any way. </summary>
/// Triggered when a Design is edited in any way. public sealed class StateChanged(Logger log)
/// <list type="number"> : EventBase<StateChanged.Arguments, StateChanged.Priority>(nameof(StateChanged), log)
/// <item>Parameter is the type of the change </item>
/// <item>Parameter is the changed saved state. </item>
/// <item>Parameter is the existing actors using this saved state. </item>
/// <item>Parameter is any additional data depending on the type of change. </item>
/// </list>
/// </summary>
public sealed class StateChanged()
: EventWrapper<StateChangeType, StateSource, ActorState, ActorData, ITransaction?, StateChanged.Priority>(nameof(StateChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="Api.StateApi.OnStateChanged" /> /// <seealso cref="Api.StateApi.OnStateChanged" />
GlamourerIpc = int.MinValue, GlamourerIpc = int.MinValue,
/// <seealso cref="Interop.Penumbra.PenumbraAutoRedraw.OnStateChanged" /> /// <seealso cref="global::Glamourer.Interop.Penumbra.PenumbraAutoRedraw.OnStateChanged" />
PenumbraAutoRedraw = 0, PenumbraAutoRedraw = 0,
/// <seealso cref="EditorHistory.OnStateChanged" /> /// <seealso cref="EditorHistory.OnStateChanged" />
EditorHistory = -1000, EditorHistory = -1000,
} }
/// <summary> Arguments for the StateChanged event. </summary>
/// <param name="Type"> The type of change that occured. </param>
/// <param name="Source"> The source of the change. </param>
/// <param name="State"> The changed state. </param>
/// <param name="Actors"> Any currently affected actors. </param>
/// <param name="Transaction"> Additional data depending on the type of change. </param>
public readonly record struct Arguments(
StateChangeType Type,
StateSource Source,
ActorState State,
ActorData Actors,
ITransaction? Transaction = null);
} }

View file

@ -1,24 +1,22 @@
using Glamourer.Api; using Glamourer.Api;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Interop.Structs; using Luna;
using OtterGui.Classes;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when a set of grouped changes finishes being applied to a Glamourer state. </summary>
/// Triggered when a set of grouped changes finishes being applied to a Glamourer state. public sealed class StateFinalized(Logger log)
/// <list type="number"> : EventBase<StateFinalized.Arguments, StateFinalized.Priority>(nameof(StateFinalized), log)
/// <item>Parameter is the operation that finished updating the saved state. </item>
/// <item>Parameter is the existing actors using this saved state. </item>
/// </list>
/// </summary>
public sealed class StateFinalized()
: EventWrapper<StateFinalizationType, ActorData, StateFinalized.Priority>(nameof(StateFinalized))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="StateApi.OnStateFinalized"/> /// <seealso cref="StateApi.OnStateFinalized"/>
StateApi = int.MinValue, StateApi = int.MinValue,
} }
/// <summary> Arguments for the StateFinalized event. </summary>
/// <param name="Type"> The operation that finished updating the saved state. </param>
/// <param name="Actors"> The existing actors using this saved state at the moment. </param>
public readonly record struct Arguments(StateFinalizationType Type, ActorData Actors);
} }

View file

@ -1,26 +1,24 @@
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Gui; using Glamourer.Gui;
using Glamourer.Gui.Tabs.DesignTab; using Luna;
using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered to select a tab or design. </summary>
/// Triggered when an automated design is changed in any way. public sealed class TabSelected(Logger log)
/// <list type="number"> : EventBase<TabSelected.Arguments, TabSelected.Priority>(nameof(TabSelected), log)
/// <item>Parameter is the tab to select. </item>
/// <item>Parameter is the design to select if the tab is the designs tab. </item>
/// </list>
/// </summary>
public sealed class TabSelected()
: EventWrapper<MainTabType, Design?, TabSelected.Priority>(nameof(TabSelected))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="DesignFileSystemDrawer.OnTabSelected"/> /// <seealso cref="DesignFileSystem.OnTabSelected"/>
DesignSelector = 0, DesignSelector = 0,
/// <seealso cref="Gui.MainWindow.OnTabSelected"/> /// <seealso cref="Gui.MainTabBar.OnEvent"/>
MainWindow = 1, MainWindow = 1,
} }
/// <summary> Arguments for the TabSelected event. </summary>
/// <param name="Type"> The tab to be selected. </param>
/// <param name="Design"> The design to be selected in the Designs tab. </param>
public readonly record struct Arguments(MainTabType Type, Design? Design = null);
} }

View file

@ -1,22 +1,24 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when the state of viera ear visibility for any draw object is changed. </summary>
/// Triggered when the state of viera ear visibility for any draw object is changed. public sealed class VieraEarStateChanged(Logger log)
/// <list type="number"> : EventBase<VieraEarStateChanged.Arguments, VieraEarStateChanged.Priority>(nameof(VieraEarStateChanged), log)
/// <item>Parameter is the model with a changed viera ear visibility state. </item>
/// <item>Parameter is the new state. </item>
/// <item>Parameter is whether to call the original function. </item>
/// </list>
/// </summary>
public sealed class VieraEarStateChanged()
: EventWrapperRef2<Actor, bool, VieraEarStateChanged.Priority>(nameof(VieraEarStateChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnVieraEarChange"/> /// <seealso cref="State.StateListener.OnVieraEarChange"/>
StateListener = 0, StateListener = 0,
} }
public ref struct Arguments(Actor actor, ref bool state)
{
/// <summary> The actor with a changed viera ear visibility state. </summary>
public readonly Actor Actor = actor;
/// <summary> The new ear visibility state. </summary>
public ref bool State = ref state;
}
} }

View file

@ -1,22 +1,27 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when the state of a visor for any draw object is changed. </summary>
/// Triggered when the state of a visor for any draw object is changed. public sealed class VisorStateChanged(Logger log)
/// <list type="number"> : EventBase<VisorStateChanged.Arguments, VisorStateChanged.Priority>(nameof(VisorStateChanged), log)
/// <item>Parameter is the model with a changed visor state. </item>
/// <item>Parameter is the new state. </item>
/// <item>Parameter is whether to call the original function. </item>
/// </list>
/// </summary>
public sealed class VisorStateChanged()
: EventWrapperRef3<Model, bool, bool, VisorStateChanged.Priority>(nameof(VisorStateChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnVisorChange"/> /// <seealso cref="State.StateListener.OnVisorChange"/>
StateListener = 0, StateListener = 0,
} }
}
public ref struct Arguments(Model model, bool visorState, ref bool value)
{
/// <summary> The model with a changed visor state. </summary>
public readonly Model Model = model;
/// <summary> The game's visor state. </summary>
public readonly bool NewVisorState = visorState;
/// <summary> The actual new value </summary>
public ref bool Value = ref value;
}
}

View file

@ -1,20 +1,13 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when a model flags an equipment slot for an update. </summary>
/// Triggered when a model flags an equipment slot for an update. public sealed class WeaponLoading(Logger log)
/// <list type="number"> : EventBase<WeaponLoading.Arguments, WeaponLoading.Priority>(nameof(WeaponLoading), log)
/// <item>Parameter is the actor that has its weapons changed. </item>
/// <item>Parameter is the equipment slot changed (Mainhand or Offhand). </item>
/// <item>Parameter is the model values to change the weapon to. </item>
/// </list>
/// </summary>
public sealed class WeaponLoading()
: EventWrapperRef3<Actor, EquipSlot, CharacterWeapon, WeaponLoading.Priority>(nameof(WeaponLoading))
{ {
public enum Priority public enum Priority
{ {
@ -24,4 +17,16 @@ public sealed class WeaponLoading()
/// <seealso cref="Automation.AutoDesignApplier.OnWeaponLoading"/> /// <seealso cref="Automation.AutoDesignApplier.OnWeaponLoading"/>
AutoDesignApplier = -1, AutoDesignApplier = -1,
} }
public ref struct Arguments(Actor actor, EquipSlot slot, ref CharacterWeapon weapon)
{
/// <summary> The actor that has its weapons changed. </summary>
public readonly Actor Actor = actor;
/// <summary> The changed equipment slot (either Mainhand or Offhand). </summary>
public readonly EquipSlot Slot = slot;
/// <summary> The model data for the new weapon. </summary>
public ref CharacterWeapon Weapon = ref weapon;
}
} }

View file

@ -1,20 +1,24 @@
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Events; namespace Glamourer.Events;
/// <summary> /// <summary> Triggered when the visibility of weapons is changed. </summary>
/// Triggered when the visibility of weapons is changed. public sealed class WeaponVisibilityChanged(Logger log)
/// <list type="number"> : EventBase<WeaponVisibilityChanged.Arguments, WeaponVisibilityChanged.Priority>(nameof(WeaponVisibilityChanged), log)
/// <item>Parameter is the actor with changed weapon visibility. </item>
/// <item>Parameter is the new state. </item>
/// </list>
/// </summary>
public sealed class WeaponVisibilityChanged() : EventWrapperRef2<Actor, bool, WeaponVisibilityChanged.Priority>(nameof(WeaponVisibilityChanged))
{ {
public enum Priority public enum Priority
{ {
/// <seealso cref="State.StateListener.OnWeaponVisibilityChange"/> /// <seealso cref="State.StateListener.OnWeaponVisibilityChange"/>
StateListener = 0, StateListener = 0,
} }
public ref struct Arguments(Actor actor, ref bool value)
{
/// <summary> The actor with changed weapon visibility. </summary>
public readonly Actor Actor = actor;
/// <summary> The new state. </summary>
public ref bool Value = ref value;
}
} }

View file

@ -1,5 +1,4 @@
using ImSharp; using ImSharp;
using OtterGui.Extensions;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Race = Penumbra.GameData.Enums.Race; using Race = Penumbra.GameData.Enums.Race;
@ -139,10 +138,10 @@ public class CustomizeSet
_ => Invalid(out custom), _ => Invalid(out custom),
}; };
int Get(IEnumerable<CustomizeData> list, CustomizeValue v, out CustomizeData? output) static int Get(IEnumerable<CustomizeData> list, CustomizeValue v, out CustomizeData? output)
{ {
var (val, idx) = list.Cast<CustomizeData?>().WithIndex().FirstOrDefault(p => p.Value!.Value.Value == v); var (idx, val) = list.Cast<CustomizeData?>().Index().FirstOrDefault(p => p.Item!.Value.Value == v);
if (val == null) if (val is null)
{ {
output = null; output = null;
return -1; return -1;

View file

@ -10,7 +10,7 @@ using Glamourer.State;
using ImSharp; using ImSharp;
using Luna; using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Logger = OtterGui.Log.Logger; using Vortice.Direct3D11.Debug;
namespace Glamourer; namespace Glamourer;

View file

@ -30,14 +30,12 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Glamourer.Api\Glamourer.Api.csproj" /> <ProjectReference Include="..\Glamourer.Api\Glamourer.Api.csproj" />
<ProjectReference Include="..\Luna\Luna\Luna.csproj" /> <ProjectReference Include="..\Luna\Luna\Luna.csproj" />
<ProjectReference Include="..\OtterGui\OtterGui.csproj" />
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" /> <ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
<ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" /> <ProjectReference Include="..\Penumbra.String\Penumbra.String.csproj" />
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" /> <ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
<PackageReference Include="Vortice.Direct3D11" Version="3.4.2-beta" /> <PackageReference Include="Vortice.Direct3D11" Version="3.4.2-beta" />
<ProjectReference Include="..\Luna\Luna.Generators\Luna.Generators.csproj" OutputItemType="Analyzer" <ProjectReference Include="..\Luna\Luna.Generators\Luna.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -56,8 +54,7 @@
</ItemGroup> </ItemGroup>
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion"> <Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low" <Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low" ContinueOnError="true">
ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="GitCommitHashSuccess" /> <Output TaskParameter="ExitCode" PropertyName="GitCommitHashSuccess" />
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" Condition="$(GitCommitHashSuccess) == 0" /> <Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" Condition="$(GitCommitHashSuccess) == 0" />
</Exec> </Exec>

View file

@ -12,13 +12,13 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Customization; namespace Glamourer.Gui.Customization;
public partial class CustomizationDrawer( public sealed partial class CustomizationDrawer(
ITextureProvider textures, ITextureProvider textures,
CustomizeService service, CustomizeService service,
Configuration config, Configuration config,
FavoriteManager favorites, FavoriteManager favorites,
HeightService heightService) HeightService heightService)
: IDisposable : IDisposable, IUiService
{ {
private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f); private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f);
private readonly IDalamudTextureWrap? _legacyTattoo = GetLegacyTattooIcon(textures); private readonly IDalamudTextureWrap? _legacyTattoo = GetLegacyTattooIcon(textures);

View file

@ -1,6 +1,5 @@
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.History;
using Glamourer.Designs.Special; using Glamourer.Designs.Special;
using Glamourer.Events; using Glamourer.Events;
using ImSharp; using ImSharp;
@ -54,7 +53,7 @@ public abstract class DesignComboBase(
if (CurrentDesign is Design design) if (CurrentDesign is Design design)
{ {
if (Im.Item.RightClicked() && Im.Io.KeyControl) 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); Im.Tooltip.OnHover("Control + Right-Click to move to design."u8);
} }
else else
@ -81,7 +80,7 @@ public abstract class DesignComboBase(
Im.Text("Currently resolving to "u8); Im.Text("Currently resolving to "u8);
using var color = ImGuiColor.Text.Push(DesignColors.GetColor(linkedDesign)); using var color = ImGuiColor.Text.Push(DesignColors.GetColor(linkedDesign));
Im.Line.NoSpacing(); Im.Line.NoSpacing();
Im.Text(linkedDesign.Name.Text); Im.Text(linkedDesign.Name);
} }
else else
{ {
@ -133,9 +132,9 @@ public abstract class DesignComboBase(
private void OnDesignColorChanged() private void OnDesignColorChanged()
=> Dirty |= IManagedCache.DirtyFlags.Custom; => 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.Created => true,
DesignChanged.Type.Renamed => true, DesignChanged.Type.Renamed => true,
@ -215,18 +214,18 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService
DesignChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignCombo); 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: case DesignChanged.Type.Created:
// If the quick design bar has no selection, select the new design if it supports the bar. // If the quick design bar has no selection, select the new design if it supports the bar.
if (QuickDesign is null && changedDesign.QuickDesign) if (QuickDesign is null && arguments.Design.QuickDesign)
QuickDesign = changedDesign; QuickDesign = arguments.Design;
break; break;
case DesignChanged.Type.Deleted: case DesignChanged.Type.Deleted:
// If the deleted design was selected, select the first design that supports the bar, if any. // 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); QuickDesign = Designs.Designs.FirstOrDefault(d => d.QuickDesign);
break; break;
case DesignChanged.Type.ReloadedAll: case DesignChanged.Type.ReloadedAll:
@ -235,10 +234,10 @@ public sealed class QuickDesignCombo : DesignComboBase, IDisposable, IUiService
break; break;
case DesignChanged.Type.QuickDesignBar: 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 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) if (QuickDesign is null && arguments.Design.QuickDesign)
QuickDesign = changedDesign; QuickDesign = arguments.Design;
// or select the first design that supports the bar, if any, if the support was removed from the currently selected 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); QuickDesign = Designs.Designs.FirstOrDefault(d => d.QuickDesign);
break; break;
} }
@ -290,9 +289,10 @@ public sealed class LinkDesignCombo : DesignComboBase, IUiService, IDisposable
public void Dispose() public void Dispose()
=> DesignChanged.Unsubscribe(OnDesignChanged); => 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; NewSelection = null;
} }
} }
@ -311,8 +311,8 @@ public sealed class RandomDesignCombo(
return exact.Which switch return exact.Which switch
{ {
RandomPredicate.Exact.Type.Name => Designs.Designs.FirstOrDefault(d => d.Name == exact.Value), 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.Path => Designs.Designs.FirstOrDefault(d => d.Node!.FullPath == exact.Value),
RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value.Text, out var g) ? g : Guid.Empty), RandomPredicate.Exact.Type.Identifier => Designs.Designs.ByIdentifier(Guid.TryParse(exact.Value, out var g) ? g : Guid.Empty),
_ => null, _ => null,
}; };
} }
@ -320,7 +320,7 @@ public sealed class RandomDesignCombo(
public bool Draw(RandomPredicate.Exact exact, [NotNullWhen(true)] out Design? newDesign, float width) public bool Draw(RandomPredicate.Exact exact, [NotNullWhen(true)] out Design? newDesign, float width)
{ {
var design = GetDesign(exact); 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) out var newItem)
&& newItem.Design is Design d) && newItem.Design is Design d)
{ {

View file

@ -12,7 +12,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment; namespace Glamourer.Gui.Equipment;
public class EquipmentDrawer public sealed class EquipmentDrawer : IUiService
{ {
private const float DefaultWidth = 280; private const float DefaultWidth = 280;

View file

@ -2,10 +2,11 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Config; using Glamourer.Config;
using ImSharp; using ImSharp;
using Luna;
namespace Glamourer.Gui; namespace Glamourer.Gui;
public class GenericPopupWindow : Luna.Window public sealed class GenericPopupWindow : Window
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly ICondition _condition; private readonly ICondition _condition;

View file

@ -4,7 +4,7 @@ using Luna;
namespace Glamourer.Gui; namespace Glamourer.Gui;
public class GlamourerChangelog public sealed class GlamourerChangelog : IUiService
{ {
public const int LastChangelogVersion = 0; public const int LastChangelogVersion = 0;
private readonly Configuration _config; private readonly Configuration _config;

View file

@ -1,11 +1,12 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Windowing;
using Glamourer.Config; using Glamourer.Config;
using Glamourer.Gui.Tabs.UnlocksTab; using Glamourer.Gui.Tabs.UnlocksTab;
using Luna;
using WindowSystem = Dalamud.Interface.Windowing.WindowSystem;
namespace Glamourer.Gui; namespace Glamourer.Gui;
public class GlamourerWindowSystem : IDisposable public sealed class GlamourerWindowSystem : IDisposable, IUiService
{ {
private readonly WindowSystem _windowSystem = new("Glamourer"); private readonly WindowSystem _windowSystem = new("Glamourer");
private readonly IUiBuilder _uiBuilder; private readonly IUiBuilder _uiBuilder;

View file

@ -1,4 +1,4 @@
using Glamourer.Designs; using Glamourer.Config;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Gui.Tabs; using Glamourer.Gui.Tabs;
using Glamourer.Gui.Tabs.ActorTab; using Glamourer.Gui.Tabs.ActorTab;
@ -14,11 +14,11 @@ namespace Glamourer.Gui;
public sealed class MainTabBar : TabBar<MainTabType> public sealed class MainTabBar : TabBar<MainTabType>
{ {
private readonly Config.EphemeralConfig _config; private readonly EphemeralConfig _config;
public readonly TabSelected Event; public readonly TabSelected Event;
public readonly SettingsTab Settings; 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) AutomationTab automation, UnlocksTab unlocks, NpcTab npcs, MessagesTab messages, DebugTab debug, TabSelected @event)
: base("MainTabBar", log, settings, actors, designs, automation, unlocks, npcs, messages, debug) : base("MainTabBar", log, settings, actors, designs, automation, unlocks, npcs, messages, debug)
{ {
@ -30,8 +30,8 @@ public sealed class MainTabBar : TabBar<MainTabType>
NextTab = _config.SelectedMainTab; NextTab = _config.SelectedMainTab;
} }
private void OnEvent(MainTabType tab, Design? _) private void OnEvent(in TabSelected.Arguments arguments)
=> NextTab = tab; => NextTab = arguments.Type;
private void OnTabSelected(in MainTabType arguments) private void OnTabSelected(in MainTabType arguments)
{ {

View file

@ -13,7 +13,7 @@ public sealed class AutomationSelection : IUiService, IDisposable
public int DraggedDesignIndex = -1; public int DraggedDesignIndex = -1;
public AutoDesignSet? Set { get; private set; } public AutoDesignSet? Set { get; private set; }
public int Index { get; private set; } = -1; public int Index { get; private set; } = -1;
public StringU8 Name { get; private set; } = NoSelection; public StringU8 Name { get; private set; } = NoSelection;
public StringU8 Incognito { get; private set; } = NoSelection; public StringU8 Incognito { get; private set; } = NoSelection;
@ -29,16 +29,16 @@ public sealed class AutomationSelection : IUiService, IDisposable
_automationChanged.Unsubscribe(OnAutomationChanged); _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; return;
switch (type) switch (arguments.Type)
{ {
case AutomationChanged.Type.DeletedSet: Update(null); break; case AutomationChanged.Type.DeletedSet: Update(null); break;
case AutomationChanged.Type.RenamedSet: Name = new StringU8(set!.Name); break; case AutomationChanged.Type.RenamedSet: Name = new StringU8(arguments.Set!.Name); break;
case AutomationChanged.Type.MovedSet: Index = (((int, int))data!).Item2; break; case AutomationChanged.Type.MovedSet: Index = arguments.As<AutomationChanged.MovedSetArguments>().NewIndex; break;
} }
} }

View file

@ -4,7 +4,7 @@ using Luna;
namespace Glamourer.Gui.Tabs.AutomationTab; namespace Glamourer.Gui.Tabs.AutomationTab;
public class AutomationTab : TwoPanelLayout, ITab<MainTabType> public sealed class AutomationTab : TwoPanelLayout, ITab<MainTabType>
{ {
private readonly Configuration _config; private readonly Configuration _config;

View file

@ -1,5 +1,6 @@
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using ImSharp; using ImSharp;
using Luna;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Gui; using Penumbra.GameData.Gui;
@ -7,13 +8,13 @@ using Penumbra.String;
namespace Glamourer.Gui.Tabs.AutomationTab; namespace Glamourer.Gui.Tabs.AutomationTab;
public class IdentifierDrawer( public sealed class IdentifierDrawer(
ActorManager actors, ActorManager actors,
DictWorld dictWorld, DictWorld dictWorld,
DictModelChara dictModelChara, DictModelChara dictModelChara,
DictBNpcNames bNpcNames, DictBNpcNames bNpcNames,
DictBNpc bNpc, DictBNpc bNpc,
HumanModelList humans) HumanModelList humans) : IUiService
{ {
private readonly WorldCombo _worldCombo = new(dictWorld); private readonly WorldCombo _worldCombo = new(dictWorld);
private readonly HumanNpcCombo _humanNpcCombo = new(bNpcNames, dictModelChara, humans, bNpc); private readonly HumanNpcCombo _humanNpcCombo = new(bNpcNames, dictModelChara, humans, bNpc);

View file

@ -20,21 +20,19 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
private readonly RandomDesignCombo _randomDesignCombo; private readonly RandomDesignCombo _randomDesignCombo;
private readonly AutomationSelection _selection; private readonly AutomationSelection _selection;
private readonly DesignStorage _designs; private readonly DesignStorage _designs;
private readonly DesignFileSystem _designFileSystem;
private string _newText = string.Empty; private string _newText = string.Empty;
private string? _newDefinition; private string? _newDefinition;
private Design? _newDesign; private Design? _newDesign;
public RandomRestrictionDrawer(AutomationChanged automationChanged, Configuration config, AutoDesignManager autoDesignManager, public RandomRestrictionDrawer(AutomationChanged automationChanged, Configuration config, AutoDesignManager autoDesignManager,
RandomDesignCombo randomDesignCombo, AutomationSelection selection, DesignFileSystem designFileSystem, DesignStorage designs) RandomDesignCombo randomDesignCombo, AutomationSelection selection, DesignStorage designs)
{ {
_automationChanged = automationChanged; _automationChanged = automationChanged;
_config = config; _config = config;
_autoDesignManager = autoDesignManager; _autoDesignManager = autoDesignManager;
_randomDesignCombo = randomDesignCombo; _randomDesignCombo = randomDesignCombo;
_selection = selection; _selection = selection;
_designFileSystem = designFileSystem;
_designs = designs; _designs = designs;
_automationChanged.Subscribe(OnAutomationChange, AutomationChanged.Priority.RandomRestrictionDrawer); _automationChanged.Subscribe(OnAutomationChange, AutomationChanged.Priority.RandomRestrictionDrawer);
} }
@ -169,7 +167,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
{ {
ImEx.TextFrameAligned("that contain"u8); ImEx.TextFrameAligned("that contain"u8);
table.NextColumn(); table.NextColumn();
var data = contains.Value.Text; var data = contains.Value;
Im.Item.SetNextWidthFull(); Im.Item.SetNextWidthFull();
if (Im.Input.Text("##match"u8, ref data, "Name, Path, or Identifier Contains..."u8)) 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); ImEx.TextFrameAligned("whose path starts with"u8);
table.NextColumn(); table.NextColumn();
var data = startsWith.Value.Text; var data = startsWith.Value;
Im.Item.SetNextWidthFull(); Im.Item.SetNextWidthFull();
if (Im.Input.Text("##startsWith"u8, ref data, "Path Starts With..."u8)) 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); ImEx.TextFrameAligned("that contain the tag"u8);
table.NextColumn(); table.NextColumn();
Im.Item.SetNextWidthFull(); Im.Item.SetNextWidthFull();
var data = exact.Value.Text; var data = exact.Value;
if (Im.Input.Text("##color"u8, ref data, "Contained tag..."u8)) if (Im.Input.Text("##color"u8, ref data, "Contained tag..."u8))
{ {
if (data.Length is 0) if (data.Length is 0)
@ -221,7 +219,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
ImEx.TextFrameAligned("that are set to the color"u8); ImEx.TextFrameAligned("that are set to the color"u8);
table.NextColumn(); table.NextColumn();
Im.Item.SetNextWidthFull(); 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 (Im.Input.Text("##color"u8, ref data, "Assigned Color is..."u8))
{ {
if (data.Length is 0) if (data.Length is 0)
@ -265,7 +263,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
if (!Im.Item.Hovered()) if (!Im.Item.Hovered())
return; return;
var designs = predicate.Get(_designs, _designFileSystem); var designs = predicate.Get(_designs);
LookupTooltip(designs); LookupTooltip(designs);
} }
@ -376,7 +374,7 @@ public sealed class RandomRestrictionDrawer : IService, IDisposable
private void DrawTotalPreview(IReadOnlyList<IDesignPredicate> list) private void DrawTotalPreview(IReadOnlyList<IDesignPredicate> list)
{ {
var designs = IDesignPredicate.Get(list, _designs, _designFileSystem).ToList(); var designs = IDesignPredicate.Get(list, _designs).ToList();
Im.Button(designs.Count > 0 Im.Button(designs.Count > 0
? $"All Restrictions Combined Match {designs.Count} Designs" ? $"All Restrictions Combined Match {designs.Count} Designs"
: "None of the Restrictions Matches Any Designs"u8, Im.ContentRegion.Available with { Y = 0 }); : "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); 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; return;
switch (type) switch (arguments.Type)
{ {
case AutomationChanged.Type.DeletedSet: case AutomationChanged.Type.DeletedSet:
case AutomationChanged.Type.DeletedDesign when data is int index && _designIndex == index: case AutomationChanged.Type.DeletedDesign when arguments.As<AutomationChanged.DeletedDesignArguments>().Index == _designIndex:
Close(); Close();
break; break;
case AutomationChanged.Type.MovedDesign when data is (int from, int to): case AutomationChanged.Type.MovedDesign:
if (_designIndex == from) var data = arguments.As<AutomationChanged.MovedDesignArguments>();
_designIndex = to; if (_designIndex == data.OldIndex)
else if (_designIndex < from && _designIndex > to) _designIndex = data.NewIndex;
else if (_designIndex < data.OldIndex && _designIndex > data.NewIndex)
_designIndex++; _designIndex++;
else if (_designIndex > to && _designIndex < from) else if (_designIndex > data.NewIndex && _designIndex < data.OldIndex)
_designIndex--; _designIndex--;
break; break;
case AutomationChanged.Type.ChangedDesign when data is (int index, IDesignStandIn _, IDesignStandIn _) && index == _designIndex: case AutomationChanged.Type.ChangedDesign when arguments.As<AutomationChanged.ChangedDesignArguments>().DesignIndex == _designIndex:
Close(); Close();
break; break;
} }

View file

@ -12,7 +12,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Tabs.AutomationTab; namespace Glamourer.Gui.Tabs.AutomationTab;
public class SetPanel( public sealed class SetPanel(
AutoDesignManager manager, AutoDesignManager manager,
JobService jobs, JobService jobs,
ItemUnlockManager itemUnlocks, ItemUnlockManager itemUnlocks,

View file

@ -125,9 +125,9 @@ public sealed class SetSelector(
base.Dispose(disposing); 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.DeletedSet:
case AutomationChanged.Type.AddedSet: case AutomationChanged.Type.AddedSet:

View file

@ -1,19 +1,18 @@
using Glamourer.Gui.Tabs.DebugTab.IpcTester; using Glamourer.Gui.Tabs.DebugTab.IpcTester;
using ImSharp; using ImSharp;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OtterGui.Raii;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
namespace Glamourer.Gui.Tabs.DebugTab; namespace Glamourer.Gui.Tabs.DebugTab;
public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees) public class DebugTabHeader(ReadOnlySpan<byte> label, params IGameDataDrawer[] subTrees)
{ {
public string Label { get; } = label; public StringU8 Label { get; } = new(label);
public IReadOnlyList<IGameDataDrawer> SubTrees { get; } = subTrees; public IReadOnlyList<IGameDataDrawer> SubTrees { get; } = subTrees;
public void Draw() public void Draw()
{ {
using var h = ImRaii.CollapsingHeader(Label); using var h = Im.Tree.HeaderId(Label);
if (!h) if (!h)
return; return;
@ -32,7 +31,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
public static DebugTabHeader CreateInterop(IServiceProvider provider) public static DebugTabHeader CreateInterop(IServiceProvider provider)
=> new => new
( (
"Interop", "Interop"u8,
provider.GetRequiredService<ModelEvaluationPanel>(), provider.GetRequiredService<ModelEvaluationPanel>(),
provider.GetRequiredService<ObjectManagerPanel>(), provider.GetRequiredService<ObjectManagerPanel>(),
provider.GetRequiredService<PenumbraPanel>(), provider.GetRequiredService<PenumbraPanel>(),
@ -46,7 +45,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
public static DebugTabHeader CreateGameData(IServiceProvider provider) public static DebugTabHeader CreateGameData(IServiceProvider provider)
=> new => new
( (
"Game Data", "Game Data"u8,
provider.GetRequiredService<DataServiceDiagnosticsDrawer>(), provider.GetRequiredService<DataServiceDiagnosticsDrawer>(),
provider.GetRequiredService<IdentificationDrawer>(), provider.GetRequiredService<IdentificationDrawer>(),
provider.GetRequiredService<RestrictedGearDrawer>(), provider.GetRequiredService<RestrictedGearDrawer>(),
@ -62,7 +61,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
public static DebugTabHeader CreateDesigns(IServiceProvider provider) public static DebugTabHeader CreateDesigns(IServiceProvider provider)
=> new => new
( (
"Designs", "Designs"u8,
provider.GetRequiredService<DesignManagerPanel>(), provider.GetRequiredService<DesignManagerPanel>(),
provider.GetRequiredService<DesignConverterPanel>(), provider.GetRequiredService<DesignConverterPanel>(),
provider.GetRequiredService<DesignTesterPanel>(), provider.GetRequiredService<DesignTesterPanel>(),
@ -72,7 +71,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
public static DebugTabHeader CreateState(IServiceProvider provider) public static DebugTabHeader CreateState(IServiceProvider provider)
=> new => new
( (
"State", "State"u8,
provider.GetRequiredService<ActiveStatePanel>(), provider.GetRequiredService<ActiveStatePanel>(),
provider.GetRequiredService<RetainedStatePanel>(), provider.GetRequiredService<RetainedStatePanel>(),
provider.GetRequiredService<FunPanel>() provider.GetRequiredService<FunPanel>()
@ -81,7 +80,7 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
public static DebugTabHeader CreateUnlocks(IServiceProvider provider) public static DebugTabHeader CreateUnlocks(IServiceProvider provider)
=> new => new
( (
"Unlocks", "Unlocks"u8,
provider.GetRequiredService<CustomizationUnlockPanel>(), provider.GetRequiredService<CustomizationUnlockPanel>(),
provider.GetRequiredService<ItemUnlockPanel>(), provider.GetRequiredService<ItemUnlockPanel>(),
provider.GetRequiredService<UnlockableItemsPanel>(), provider.GetRequiredService<UnlockableItemsPanel>(),

View file

@ -20,7 +20,7 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy
foreach (var (idx, design) in designManager.Designs.Index()) foreach (var (idx, design) in designManager.Designs.Index())
{ {
using var id = Im.Id.Push(idx); 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) if (!t)
continue; continue;
@ -61,7 +61,7 @@ public sealed class DesignManagerPanel(DesignManager designManager, DesignFileSy
if (design is Design d) if (design is Design d)
{ {
table.DrawColumn("Name"u8); table.DrawColumn("Name"u8);
table.DrawColumn(d.Name.Text); table.DrawColumn(d.Name);
table.DrawColumn($"({d.Index})"); table.DrawColumn($"({d.Index})");
table.DrawColumn("Description (Hover)"u8); table.DrawColumn("Description (Hover)"u8);
Im.Tooltip.OnHover(d.Description); Im.Tooltip.OnHover(d.Description);

View file

@ -1,5 +1,5 @@
using Glamourer.State; using Glamourer.State;
using OtterGui.Raii; using ImSharp;
using Penumbra.GameData.Gui.Debug; using Penumbra.GameData.Gui.Debug;
using Penumbra.GameData.Interop; 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))) 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) if (t)
ActiveStatePanel.DrawState(stateManager, ActorData.Invalid, state); ActiveStatePanel.DrawState(stateManager, ActorData.Invalid, state);
} }

View file

@ -1,13 +1,14 @@
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Glamourer.Config; using Glamourer.Config;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Gui.Tabs.SettingsTab;
using Glamourer.Services; using Glamourer.Services;
using ImSharp; using ImSharp;
using Luna; using Luna;
namespace Glamourer.Gui.Tabs.DesignTab; namespace Glamourer.Gui.Tabs.DesignTab;
public class DesignDetailTab public sealed class DesignDetailTab : IUiService
{ {
private readonly SaveService _saveService; private readonly SaveService _saveService;
private readonly Configuration _config; private readonly Configuration _config;
@ -57,7 +58,7 @@ public class DesignDetailTab
table.NextColumn(); table.NextColumn();
var width = Im.ContentRegion.Available with { Y = 0 }; var width = Im.ContentRegion.Available with { Y = 0 };
Im.Item.SetNextWidth(width.X); 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); _manager.Rename(Selected, newName);
var identifier = Selected.Identifier.ToString(); var identifier = Selected.Identifier.ToString();

View file

@ -1,5 +1,4 @@
using System.Security.AccessControl; using Glamourer.Config;
using Glamourer.Config;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.History; using Glamourer.Designs.History;
using Glamourer.Events; 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 ApplyCharacterButton(fileSystem, manager, objects, stateManager, converter), 70);
LeftButtons.AddButton(new UndoButton(fileSystem, history), 60); LeftButtons.AddButton(new UndoButton(fileSystem, history), 60);
RightButtons.AddButton(incognito, 50); RightButtons.AddButton(incognito, 50);
RightButtons.AddButton(new LockedButton(fileSystem, manager), 100); RightButtons.AddButton(new LockedButton(fileSystem, manager), 100);
_fileSystem.Selection.Changed += OnSelectionChanged; _fileSystem.Selection.Changed += OnSelectionChanged;
OnSelectionChanged(); OnSelectionChanged();
designChanged.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignHeader); 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; return;
if (arg2 != _fileSystem.Selection.Selection?.Value) if (arguments.Design != _fileSystem.Selection.Selection?.Value)
return; return;
_header = new StringU8(arg2.Name.Text); _header = new StringU8(arguments.Design.Name);
} }
private void OnSelectionChanged() private void OnSelectionChanged()
{ {
if (_fileSystem.Selection.Selection?.GetValue<Design>() is { } selection) if (_fileSystem.Selection.Selection?.GetValue<Design>() is { } selection)
{ {
_header = new StringU8(selection.Name.Text); _header = new StringU8(selection.Name);
_incognito = new StringU8(selection.Incognito); _incognito = new StringU8(selection.Incognito);
} }
else if (_fileSystem.Selection.OrderedNodes.Count > 0) else if (_fileSystem.Selection.OrderedNodes.Count > 0)

View file

@ -107,7 +107,7 @@ public class DesignLinkDrawer(
using (ImGuiColor.Text.Push(color)) using (ImGuiColor.Text.Push(color))
{ {
Im.Cursor.FrameAlign(); 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); Im.Tooltip.OnHover("Current Design"u8);
@ -137,7 +137,7 @@ public class DesignLinkDrawer(
using (ImGuiColor.Text.Push(colorManager.GetColor(design))) using (ImGuiColor.Text.Push(colorManager.GetColor(design)))
{ {
Im.Cursor.FrameAlign(); 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); DrawDragDrop(design, order, i);

View file

@ -8,7 +8,7 @@ using Luna;
namespace Glamourer.Gui.Tabs.DesignTab; 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 readonly ModCombo _modCombo = new(penumbra, fileSystem);
private (Mod, ModSettings)[]? _copy; private (Mod, ModSettings)[]? _copy;

View file

@ -6,11 +6,11 @@ using Luna;
namespace Glamourer.Gui.Tabs.DesignTab; namespace Glamourer.Gui.Tabs.DesignTab;
public class MultiDesignPanel( public sealed class MultiDesignPanel(
DesignFileSystem fileSystem, DesignFileSystem fileSystem,
DesignManager editor, DesignManager editor,
DesignColors colors, DesignColors colors,
Configuration config) Configuration config) : IUiService
{ {
private readonly DesignColorCombo _colorCombo = new(colors, true); private readonly DesignColorCombo _colorCombo = new(colors, true);
@ -109,7 +109,7 @@ public class MultiDesignPanel(
{ {
using var id = Im.Id.Push(index); using var id = Im.Id.Push(index);
var (icon, text) = node is IFileSystemData<Design> l var (icon, text) = node is IFileSystemData<Design> l
? (LunaStyle.RemoveFileIcon, l.Value.Name.Text) ? (LunaStyle.RemoveFileIcon, l.Value.Name)
: (LunaStyle.RemoveFolderIcon, string.Empty); : (LunaStyle.RemoveFolderIcon, string.Empty);
table.NextColumn(); table.NextColumn();
if (ImEx.Icon.Button(icon, "Remove from selection."u8, sizeType)) if (ImEx.Icon.Button(icon, "Remove from selection."u8, sizeType))
@ -152,7 +152,7 @@ public class MultiDesignPanel(
? _tag.Length is 0 ? _tag.Length is 0
? "No tag specified."u8 ? "No tag specified."u8
: $"All designs selected already contain the tag \"{_tag}\"." : $"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)) _addDesigns.Count is 0))
foreach (var design in _addDesigns) foreach (var design in _addDesigns)
editor.AddTag(design, _tag); editor.AddTag(design, _tag);
@ -164,7 +164,7 @@ public class MultiDesignPanel(
? _tag.Length is 0 ? _tag.Length is 0
? "No tag specified."u8 ? "No tag specified."u8
: $"No selected design contains the tag \"{_tag}\" locally." : $"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)) _removeDesigns.Count is 0))
foreach (var (design, index) in _removeDesigns) foreach (var (design, index) in _removeDesigns)
editor.RemoveTag(design, index); editor.RemoveTag(design, index);
@ -306,7 +306,7 @@ public class MultiDesignPanel(
DesignColors.AutomaticName => "Use the other button to set to automatic."u8, DesignColors.AutomaticName => "Use the other button to set to automatic."u8,
_ => $"All designs selected are already set to the color \"{_colorComboSelection}\".", _ => $"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)) _addDesigns.Count is 0))
foreach (var design in _addDesigns) foreach (var design in _addDesigns)
editor.ChangeColor(design, _colorComboSelection!); editor.ChangeColor(design, _colorComboSelection!);
@ -316,7 +316,7 @@ public class MultiDesignPanel(
? $"Unset {_removeDesigns.Count} Designs" ? $"Unset {_removeDesigns.Count} Designs"
: "Unset"u8, width, _removeDesigns.Count is 0 : "Unset"u8, width, _removeDesigns.Count is 0
? "No selected design is set to a non-automatic color."u8 ? "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)) _removeDesigns.Count is 0))
foreach (var (design, _) in _removeDesigns) foreach (var (design, _) in _removeDesigns)
editor.ChangeColor(design, string.Empty); editor.ChangeColor(design, string.Empty);

View file

@ -1,5 +1,4 @@
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Designs.History;
using Glamourer.Events; using Glamourer.Events;
using ImSharp; using ImSharp;
using Luna; using Luna;
@ -21,9 +20,9 @@ public sealed class DesignFileSystemCache : FileSystemCache<DesignFileSystemCach
node.Dirty = true; node.Dirty = true;
} }
private void OnDesignChanged(DesignChanged.Type type, Design design, ITransaction? _2) private void OnDesignChanged(in DesignChanged.Arguments arguments)
{ {
switch (type) switch (arguments.Type)
{ {
case DesignChanged.Type.Created: case DesignChanged.Type.Created:
case DesignChanged.Type.Deleted: case DesignChanged.Type.Deleted:
@ -45,7 +44,7 @@ public sealed class DesignFileSystemCache : FileSystemCache<DesignFileSystemCach
break; break;
} }
if (design.Node is { } node && AllNodes.TryGetValue(node, out var cache)) if (arguments.Design.Node is { } node && AllNodes.TryGetValue(node, out var cache))
cache.Dirty = true; cache.Dirty = true;
} }
@ -78,14 +77,14 @@ public sealed class DesignFileSystemCache : FileSystemCache<DesignFileSystemCach
{ {
public readonly IFileSystemData<Design> Node = node; public readonly IFileSystemData<Design> Node = node;
public Vector4 Color; 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 StringU8 Incognito = new(node.Value.Incognito);
public override void Update(FileSystemCache cache, IFileSystemNode node) public override void Update(FileSystemCache cache, IFileSystemNode node)
{ {
var drawer = (DesignFileSystemDrawer)cache.Parent; var drawer = (DesignFileSystemDrawer)cache.Parent;
Color = drawer.DesignColors.GetColor(Node.Value).ToVector(); 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<DesignData> cache, IFileSystemNode node) protected override void DrawInternal(FileSystemCache<DesignData> cache, IFileSystemNode node)

View file

@ -42,12 +42,12 @@ public sealed class DesignFilter : TokenizedFilter<DesignFilterTokenType, Design
=> token.Type switch => token.Type switch
{ {
DesignFilterTokenType.Default => cacheItem.Node.FullPath.Contains(token.Needle, StringComparison.OrdinalIgnoreCase) 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.Mod => CheckMods(token.Needle, cacheItem),
DesignFilterTokenType.Tag => CheckTags(token.Needle, cacheItem), DesignFilterTokenType.Tag => CheckTags(token.Needle, cacheItem),
DesignFilterTokenType.Color => cacheItem.Node.Value.Color.Contains(token.Needle, StringComparison.OrdinalIgnoreCase), DesignFilterTokenType.Color => cacheItem.Node.Value.Color.Contains(token.Needle, StringComparison.OrdinalIgnoreCase),
DesignFilterTokenType.Item => cacheItem.Node.Value.DesignData.ContainsName(token.Needle), 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), DesignFilterTokenType.FullContext => CheckFullContext(token.Needle, cacheItem),
_ => true, _ => true,
}; };
@ -77,7 +77,7 @@ public sealed class DesignFilter : TokenizedFilter<DesignFilterTokenType, Design
return true; return true;
var design = cacheItem.Node.Value; var design = cacheItem.Node.Value;
if (design.Name.Text.Contains(needle, StringComparison.OrdinalIgnoreCase)) if (design.Name.Contains(needle, StringComparison.OrdinalIgnoreCase))
return true; return true;
if (design.Description.Contains(needle, StringComparison.OrdinalIgnoreCase)) if (design.Description.Contains(needle, StringComparison.OrdinalIgnoreCase))

View file

@ -15,7 +15,7 @@ public sealed class RenameDesignInput(DesignFileSystemDrawer fileSystem) : BaseB
public override bool DrawMenuItem(in IFileSystemData data) public override bool DrawMenuItem(in IFileSystemData data)
{ {
var design = (Design)data.Value; var design = (Design)data.Value;
var currentName = design.Name.Text; var currentName = design.Name;
using var style = Im.Style.PushDefault(ImStyleDouble.FramePadding); using var style = Im.Style.PushDefault(ImStyleDouble.FramePadding);
MenuSeparator.DrawSeparator(); MenuSeparator.DrawSeparator();
Im.Text("Rename Design:"u8); Im.Text("Rename Design:"u8);

View file

@ -3,12 +3,13 @@ using Glamourer.Designs;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Services; using Glamourer.Services;
using ImSharp; using ImSharp;
using Luna;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace Glamourer.Gui.Tabs.NpcTab; namespace Glamourer.Gui.Tabs.NpcTab;
public class LocalNpcAppearanceData : ISavable public sealed class LocalNpcAppearanceData : ISavable, IUiService
{ {
private readonly DesignColors _colors; private readonly DesignColors _colors;

View file

@ -4,6 +4,7 @@ using Glamourer.Designs;
using Glamourer.Gui.Customization; using Glamourer.Gui.Customization;
using Glamourer.Gui.Equipment; using Glamourer.Gui.Equipment;
using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Gui.Tabs.SettingsTab;
using Glamourer.State; using Glamourer.State;
using ImSharp; using ImSharp;
using Luna; using Luna;

View file

@ -0,0 +1,113 @@
using Glamourer.Config;
using Glamourer.Designs;
using ImSharp;
using Luna;
namespace Glamourer.Gui.Tabs.SettingsTab;
public sealed class DesignColorUi(DesignColors colors, Configuration config) : IUiService
{
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<LabelStringHandlerBuffer> 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;
}
}

View file

@ -11,7 +11,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Tabs.UnlocksTab; namespace Glamourer.Gui.Tabs.UnlocksTab;
public class UnlockOverview( public sealed class UnlockOverview(
ItemManager items, ItemManager items,
CustomizeService customizations, CustomizeService customizations,
ItemUnlockManager itemUnlocks, ItemUnlockManager itemUnlocks,
@ -21,7 +21,7 @@ public class UnlockOverview(
CodeService codes, CodeService codes,
JobService jobs, JobService jobs,
FavoriteManager favorites, FavoriteManager favorites,
PenumbraService penumbra) PenumbraService penumbra) : IUiService
{ {
private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f); private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f);

View file

@ -16,7 +16,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Tabs.UnlocksTab; namespace Glamourer.Gui.Tabs.UnlocksTab;
public sealed class UnlockTable : TableBase<UnlockCacheItem, UnlockTable.Cache> public sealed class UnlockTable : TableBase<UnlockCacheItem, UnlockTable.Cache>, IUiService
{ {
private readonly JobService _jobs; private readonly JobService _jobs;
private readonly ItemManager _items; private readonly ItemManager _items;
@ -554,16 +554,17 @@ public sealed class UnlockTable : TableBase<UnlockCacheItem, UnlockTable.Cache>
} }
} }
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; return;
FilterDirty = true; FilterDirty = true;
SortDirty = true; SortDirty = true;
var id = arguments.Id;
var idx = UnfilteredItems.IndexOf(i => i.Item.ItemId == id); var idx = UnfilteredItems.IndexOf(i => i.Item.ItemId == id);
if (idx >= 0) if (idx >= 0)
UpdateSingleItem(idx, UnfilteredItems[idx] with { UnlockTimestamp = timestamp }, false); UpdateSingleItem(idx, UnfilteredItems[idx] with { UnlockTimestamp = arguments.Timestamp }, false);
} }
public override void Update() public override void Update()

View file

@ -2,7 +2,7 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Events; using Glamourer.Events;
using OtterGui.Classes; using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -13,13 +13,19 @@ namespace Glamourer.Interop;
/// Changes in Race, body type or Gender are probably ignored. /// Changes in Race, body type or Gender are probably ignored.
/// This operates on draw objects, not game objects. /// This operates on draw objects, not game objects.
/// </summary> /// </summary>
public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeArray, ChangeCustomizeService.Priority> public sealed unsafe class ChangeCustomizeService : EventBase<ChangeCustomizeService.Arguments, ChangeCustomizeService.Priority>
{ {
private readonly PenumbraReloaded _penumbraReloaded; private readonly PenumbraReloaded _penumbraReloaded;
private readonly IGameInteropProvider _interop; private readonly IGameInteropProvider _interop;
private readonly delegate* unmanaged<Human*, byte*, bool, bool> _original; private readonly delegate* unmanaged<Human*, byte*, bool, bool> _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;
}
/// <summary> Check whether we in a manual customize update, in which case we need to not toggle certain flags. </summary> /// <summary> Check whether we in a manual customize update, in which case we need to not toggle certain flags. </summary>
public static readonly InMethodChecker InUpdate = new(); public static readonly InMethodChecker InUpdate = new();
@ -30,8 +36,8 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
StateListener = 0, StateListener = 0,
} }
public ChangeCustomizeService(PenumbraReloaded penumbraReloaded, IGameInteropProvider interop) public ChangeCustomizeService(PenumbraReloaded penumbraReloaded, IGameInteropProvider interop, Logger log)
: base("ChangeCustomize") : base("ChangeCustomize", log)
{ {
_penumbraReloaded = penumbraReloaded; _penumbraReloaded = penumbraReloaded;
_interop = interop; _interop = interop;
@ -39,6 +45,7 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
_original = Human.MemberFunctionPointers.UpdateDrawData; _original = Human.MemberFunctionPointers.UpdateDrawData;
interop.InitializeFromAttributes(this); interop.InitializeFromAttributes(this);
_penumbraReloaded.Subscribe(Restore, PenumbraReloaded.Priority.ChangeCustomizeService); _penumbraReloaded.Subscribe(Restore, PenumbraReloaded.Priority.ChangeCustomizeService);
_postEvent = new Post(log);
} }
protected override void Dispose(bool _) protected override void Dispose(bool _)
@ -81,20 +88,20 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
private bool ChangeCustomizeDetour(Human* human, byte* data, byte skipEquipment) private bool ChangeCustomizeDetour(Human* human, byte* data, byte skipEquipment)
{ {
if (!InUpdate.InMethod) if (!InUpdate.InMethod)
Invoke(human, ref *(CustomizeArray*)data); Invoke(new Arguments(human, ref *(CustomizeArray*)data));
var ret = _changeCustomizeHook.Original(human, data, skipEquipment); var ret = _changeCustomizeHook.Original(human, data, skipEquipment);
_postEvent.Invoke(human); _postEvent.Invoke(human);
return ret; return ret;
} }
public void Subscribe(Action<Model> action, Post.Priority priority) public void Subscribe(InAction<Model> action, Post.Priority priority)
=> _postEvent.Subscribe(action, priority); => _postEvent.Subscribe(action, priority);
public void Unsubscribe(Action<Model> action) public void Unsubscribe(InAction<Model> action)
=> _postEvent.Unsubscribe(action); => _postEvent.Unsubscribe(action);
public sealed class Post() : EventWrapper<Model, Post.Priority>(nameof(ChangeCustomizeService) + '.' + nameof(Post)) public sealed class Post(Logger log) : EventBase<Model, Post.Priority>(nameof(ChangeCustomizeService) + '.' + nameof(Post), log)
{ {
public enum Priority public enum Priority
{ {

View file

@ -5,13 +5,14 @@ using Glamourer.Config;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public class ContextMenuService : IDisposable public sealed class ContextMenuService : IDisposable, IRequiredService
{ {
public const int ChatLogContextItemId = 0x958; public const int ChatLogContextItemId = 0x958;

View file

@ -2,24 +2,16 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using OtterGui.Classes; using Luna;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Interop; namespace Glamourer.Interop;
/// <summary> /// <summary> Triggered when the crest visibility is updated on a model. </summary>
/// Triggered when the crest visibility is updated on a model. public sealed unsafe class CrestService : EventBase<CrestService.Arguments, CrestService.Priority>
/// <list type="number">
/// <item>Parameter is the model with an update. </item>
/// <item>Parameter is the equipment slot changed. </item>
/// <item>Parameter is whether the crest will be shown. </item>
/// </list>
/// </summary>
public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, bool, CrestService.Priority>
{ {
public enum Priority public enum Priority
{ {
@ -27,8 +19,20 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
StateListener = 0, StateListener = 0,
} }
public CrestService(IGameInteropProvider interop) public ref struct Arguments(Actor actor, CrestFlag slot, ref bool value)
: base(nameof(CrestService)) {
/// <summary> The game object with a crest update. </summary>
public readonly Actor Actor = actor;
/// <summary> The equipment slot changed. </summary>
public readonly CrestFlag Slot = slot;
/// <summary> The new value. </summary>
public ref bool Value = ref value;
}
public CrestService(IGameInteropProvider interop, Logger log)
: base(nameof(CrestService), log)
{ {
interop.InitializeFromAttributes(this); interop.InitializeFromAttributes(this);
_humanSetFreeCompanyCrestVisibleOnSlot = _humanSetFreeCompanyCrestVisibleOnSlot =
@ -76,7 +80,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
foreach (var slot in CrestExtensions.AllRelevantSet) foreach (var slot in CrestExtensions.AllRelevantSet)
{ {
var newValue = ((CrestFlag)crestFlags).HasFlag(slot); var newValue = ((CrestFlag)crestFlags).HasFlag(slot);
Invoke(actor, slot, ref newValue); Invoke(new Arguments(actor, slot, ref newValue));
crestFlags = (byte)(newValue ? crestFlags | (byte)slot : crestFlags & (byte)~slot); crestFlags = (byte)(newValue ? crestFlags | (byte)slot : crestFlags & (byte)~slot);
} }
@ -98,7 +102,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
foreach (var slot in CrestExtensions.AllRelevantSet) foreach (var slot in CrestExtensions.AllRelevantSet)
{ {
var newValue = ((CrestFlag)flags).HasFlag(slot); var newValue = ((CrestFlag)flags).HasFlag(slot);
Invoke(actor, slot, ref newValue); Invoke(new Arguments(actor, slot, ref newValue));
flags = (byte)(newValue ? flags | (byte)slot : flags & (byte)~slot); flags = (byte)(newValue ? flags | (byte)slot : flags & (byte)~slot);
} }
Glamourer.Log.Verbose( Glamourer.Log.Verbose(

View file

@ -11,7 +11,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public class ImportService(CustomizeService customizations, IDragDropManager dragDropManager, ItemManager items) public sealed class ImportService(CustomizeService customizations, IDragDropManager dragDropManager, ItemManager items) : IService
{ {
public void CreateDatSource() public void CreateDatSource()
=> dragDropManager.CreateImGuiSource("DatDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".dat"), m => => dragDropManager.CreateImGuiSource("DatDragger", m => m.Files.Count == 1 && m.Extensions.Contains(".dat"), m =>

View file

@ -19,10 +19,12 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent) public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent)
{ {
_movedItemsEvent = movedItemsEvent; _movedItemsEvent = movedItemsEvent;
_gearsetEvent = gearsetEvent; _gearsetEvent = gearsetEvent;
_moveItemHook = interop.HookFromAddress<MoveItemDelegate>((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour); _moveItemHook = interop.HookFromAddress<MoveItemDelegate>((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour);
_equipGearsetHook = interop.HookFromAddress<EquipGearsetInternalDelegate>((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetDetour); _equipGearsetHook =
interop.HookFromAddress<EquipGearsetInternalDelegate>((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal,
EquipGearSetDetour);
_moveItemHook.Enable(); _moveItemHook.Enable();
_equipGearsetHook.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 delegate nint EquipGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId);
private readonly Hook<EquipGearsetInternalDelegate> _equipGearsetHook = null!; private readonly Hook<EquipGearsetInternalDelegate> _equipGearsetHook;
private nint EquipGearSetDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId) private nint EquipGearSetDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId)
{ {
var prior = module->CurrentGearsetIndex; var prior = module->CurrentGearsetIndex;
var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId); var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
var set = module->GetGearset((int)gearsetId); var set = module->GetGearset((int)gearsetId);
_gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob); _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})"); Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
if (ret == 0) if (ret == 0)
{ {
@ -111,7 +113,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
Add(EquipSlot.LFinger, ref entry->Items[12]); Add(EquipSlot.LFinger, ref entry->Items[12]);
} }
_movedItemsEvent.Invoke(_itemList.ToArray()); _movedItemsEvent.Invoke(new MovedEquipment.Arguments(_itemList.ToArray()));
} }
return ret; return ret;
@ -130,25 +132,15 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
{ {
var ret = _moveItemHook.Original(manager, sourceContainer, sourceSlot, targetContainer, targetSlot, unk); var ret = _moveItemHook.Original(manager, sourceContainer, sourceSlot, targetContainer, targetSlot, unk);
Glamourer.Log.Excessive($"[InventoryService] Moved {sourceContainer} {sourceSlot} {targetContainer} {targetSlot} (Returned {ret})"); 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 (InvokeSource(sourceContainer, sourceSlot, out var source))
if (InvokeTarget(manager, targetContainer, targetSlot, out var target)) if (InvokeTarget(manager, targetContainer, targetSlot, out var target))
_movedItemsEvent.Invoke(new[] _movedItemsEvent.Invoke(new MovedEquipment.Arguments(source, target));
{
source,
target,
});
else else
_movedItemsEvent.Invoke(new[] _movedItemsEvent.Invoke(new MovedEquipment.Arguments(source));
{
source,
});
else if (InvokeTarget(manager, targetContainer, targetSlot, out var target)) else if (InvokeTarget(manager, targetContainer, targetSlot, out var target))
_movedItemsEvent.Invoke(new[] _movedItemsEvent.Invoke(new MovedEquipment.Arguments(target));
{
target,
});
} }
return ret; return ret;

View file

@ -2,6 +2,7 @@ using Dalamud.Hooking;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Luna;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
@ -9,7 +10,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public class JobService : IDisposable public sealed class JobService : IDisposable, IRequiredService
{ {
private readonly nint _characterDataOffset; private readonly nint _characterDataOffset;

View file

@ -37,11 +37,11 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
public void Dispose() public void Dispose()
=> _event.Unsubscribe(OnPrepareColorSet); => _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 actor = _penumbra.GameObjectFromDrawObject(arguments.Model);
var validType = FindType(characterBase, actor, out var type); var validType = FindType(arguments.Model.AsCharacterBase, actor, out var type);
var (slotId, materialId) = FindMaterial(characterBase, material); var (slotId, materialId) = FindMaterial(arguments.Model.AsCharacterBase, arguments.Handle);
if (!validType if (!validType
|| type is not MaterialValueIndex.DrawObjectType.Human && slotId > 0 || type is not MaterialValueIndex.DrawObjectType.Human && slotId > 0
@ -55,19 +55,19 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
if (values.Length == 0) if (values.Length == 0)
return; return;
if (!PrepareColorSet.TryGetColorTable(material, stain, out var baseColorSet)) if (!PrepareColorSet.TryGetColorTable(arguments.Handle, arguments.Ids, out var baseColorSet))
return; return;
var drawData = type switch var drawData = type switch
{ {
MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, (HumanSlot)slotId), MaterialValueIndex.DrawObjectType.Human => GetTempSlot(arguments.Model.AsHuman, (HumanSlot)slotId),
_ => GetTempSlot((Weapon*)characterBase), _ => GetTempSlot(arguments.Model.AsWeapon),
}; };
var mode = PrepareColorSet.GetMode(material); var mode = PrepareColorSet.GetMode(arguments.Handle);
UpdateMaterialValues(state, values, drawData, ref baseColorSet, mode); UpdateMaterialValues(state, values, drawData, ref baseColorSet, mode);
if (MaterialService.GenerateNewColorTable(baseColorSet, out var texture)) if (MaterialService.GenerateNewColorTable(baseColorSet, out var texture))
ret = (nint)texture; arguments.ReturnValue = (nint)texture;
} }
/// <summary> Update and apply the glamourer state of an actor according to the application sources when updated by the game. </summary> /// <summary> Update and apply the glamourer state of an actor according to the application sources when updated by the game. </summary>

View file

@ -3,7 +3,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using Luna; using Luna;
using OtterGui.Classes;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Files.MaterialStructs; using Penumbra.GameData.Files.MaterialStructs;
@ -13,7 +12,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Interop.Material; namespace Glamourer.Interop.Material;
public sealed unsafe class PrepareColorSet public sealed unsafe class PrepareColorSet
: EventWrapperPtr12Ref34<CharacterBase, MaterialResourceHandle, StainIds, nint, PrepareColorSet.Priority>, IHookService : EventBase<PrepareColorSet.Arguments, PrepareColorSet.Priority>, IHookService
{ {
private readonly UpdateColorSets _updateColorSets; private readonly UpdateColorSets _updateColorSets;
@ -23,11 +22,19 @@ public sealed unsafe class PrepareColorSet
MaterialManager = 0, MaterialManager = 0,
} }
public PrepareColorSet(HookManager hooks, UpdateColorSets updateColorSets) public ref struct Arguments(Model model, MaterialResourceHandle* handle, ref StainIds ids, ref nint returnValue)
: base("Prepare Color Set ") {
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; _updateColorSets = updateColorSets;
_task = hooks.CreateHook<Delegate>(Name, Sigs.PrepareColorSet, Detour, true); _task = hooks.CreateHook<Delegate>(Name, Sigs.PrepareColorSet, Detour, true);
} }
private readonly Task<Hook<Delegate>> _task; private readonly Task<Hook<Delegate>> _task;
@ -64,7 +71,7 @@ public sealed unsafe class PrepareColorSet
var ret = nint.Zero; var ret = nint.Zero;
var stainIds = new StainIds(stainId1, stainId2); 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) if (ret != nint.Zero)
return (Texture*)ret; return (Texture*)ret;
@ -74,7 +81,7 @@ public sealed unsafe class PrepareColorSet
public static bool TryGetColorTable(MaterialResourceHandle* material, StainIds stainIds, public static bool TryGetColorTable(MaterialResourceHandle* material, StainIds stainIds,
out ColorTable.Table table) 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; table = default;
return false; return false;
@ -83,10 +90,10 @@ public sealed unsafe class PrepareColorSet
var newTable = *(ColorTable.Table*)material->DataSet; var newTable = *(ColorTable.Table*)material->DataSet;
if (GetDyeTable(material, out var dyeTable)) 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); 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); material->ReadStainingTemplate(dyeTable, stainIds.Stain2.Id, (Half*)&newTable, 1);
} }

View file

@ -2,11 +2,12 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Glamourer.Events; using Glamourer.Events;
using Luna;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public unsafe class MetaService : IDisposable public sealed unsafe class MetaService : IDisposable, IRequiredService
{ {
private readonly HeadGearVisibilityChanged _headGearEvent; private readonly HeadGearVisibilityChanged _headGearEvent;
private readonly WeaponVisibilityChanged _weaponEvent; private readonly WeaponVisibilityChanged _weaponEvent;
@ -75,8 +76,8 @@ public unsafe class MetaService : IDisposable
} }
Actor actor = drawData->OwnerObject; Actor actor = drawData->OwnerObject;
var v = value == 0; var v = value is 0;
_headGearEvent.Invoke(actor, ref v); _headGearEvent.Invoke(new HeadGearVisibilityChanged.Arguments(actor, ref v));
value = (byte)(v ? 0 : 1); value = (byte)(v ? 0 : 1);
Glamourer.Log.Verbose($"[MetaService] Hide Hat triggered with 0x{(nint)drawData:X} {id} {value} for {actor.Utf8Name}."); Glamourer.Log.Verbose($"[MetaService] Hide Hat triggered with 0x{(nint)drawData:X} {id} {value} for {actor.Utf8Name}.");
_hideHatGearHook.Original(drawData, id, value); _hideHatGearHook.Original(drawData, id, value);
@ -85,8 +86,8 @@ public unsafe class MetaService : IDisposable
private void HideWeaponsDetour(DrawDataContainer* drawData, byte value) private void HideWeaponsDetour(DrawDataContainer* drawData, byte value)
{ {
Actor actor = drawData->OwnerObject; Actor actor = drawData->OwnerObject;
var v = value == 0; var v = value is 0;
_weaponEvent.Invoke(actor, ref v); _weaponEvent.Invoke(new WeaponVisibilityChanged.Arguments(actor, ref v));
Glamourer.Log.Verbose($"[MetaService] Hide Weapon triggered with 0x{(nint)drawData:X} {value} for {actor.Utf8Name}."); Glamourer.Log.Verbose($"[MetaService] Hide Weapon triggered with 0x{(nint)drawData:X} {value} for {actor.Utf8Name}.");
_hideWeaponsHook.Original(drawData, (byte)(v ? 0 : 1)); _hideWeaponsHook.Original(drawData, (byte)(v ? 0 : 1));
} }
@ -94,8 +95,8 @@ public unsafe class MetaService : IDisposable
private void ToggleVisorDetour(DrawDataContainer* drawData, byte value) private void ToggleVisorDetour(DrawDataContainer* drawData, byte value)
{ {
Actor actor = drawData->OwnerObject; Actor actor = drawData->OwnerObject;
var v = value != 0; var v = value is not 0;
_visorEvent.Invoke(actor.Model, true, ref v); _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}."); Glamourer.Log.Verbose($"[MetaService] Toggle Visor triggered with 0x{(nint)drawData:X} {value} for {actor.Utf8Name}.");
_toggleVisorHook.Original(drawData, (byte)(v ? 1 : 0)); _toggleVisorHook.Original(drawData, (byte)(v ? 1 : 0));
} }

View file

@ -1,7 +1,6 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Config; using Glamourer.Config;
using Glamourer.Designs.History;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.State; using Glamourer.State;
using Luna; using Luna;
@ -10,7 +9,7 @@ using Penumbra.GameData.Interop;
namespace Glamourer.Interop.Penumbra; namespace Glamourer.Interop.Penumbra;
public class PenumbraAutoRedraw : IDisposable, IRequiredService public sealed class PenumbraAutoRedraw : IDisposable, IRequiredService
{ {
private const int WaitFrames = 5; private const int WaitFrames = 5;
private readonly Configuration _config; private readonly Configuration _config;
@ -46,13 +45,13 @@ public class PenumbraAutoRedraw : IDisposable, IRequiredService
} }
private readonly ConcurrentQueue<(ActorState, Action, int)> _actions = []; private readonly ConcurrentQueue<(ActorState, Action, int)> _actions = [];
private readonly OtterGui.Classes.ConcurrentSet<ActorState> _skips = []; private readonly ConcurrentSet<ActorState> _skips = [];
private DateTime _frame; 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()) if (arguments.Type is StateChangeType.Design && arguments.Source.IsIpc())
_skips.TryAdd(state); _skips.TryAdd(arguments.State);
} }
private void OnFramework(IFramework _) private void OnFramework(IFramework _)

View file

@ -35,7 +35,7 @@ public readonly record struct ModSettings(Dictionary<string, List<string>> Setti
=> new(); => new();
} }
public class PenumbraService : IDisposable public sealed class PenumbraService : IDisposable, IService
{ {
public const int RequiredPenumbraBreakingVersion = 5; public const int RequiredPenumbraBreakingVersion = 5;
public const int RequiredPenumbraFeatureVersion = 13; public const int RequiredPenumbraFeatureVersion = 13;

View file

@ -6,6 +6,7 @@ using Penumbra.GameData;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Glamourer.State; using Glamourer.State;
using Luna;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
@ -13,7 +14,7 @@ using CustomizeIndex = Dalamud.Game.ClientState.Objects.Enums.CustomizeIndex;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public unsafe class ScalingService : IDisposable public sealed unsafe class ScalingService : IDisposable, IRequiredService
{ {
private readonly ActorManager _actors; private readonly ActorManager _actors;
private readonly StateManager _state; private readonly StateManager _state;

View file

@ -4,6 +4,7 @@ using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Network; using FFXIVClientStructs.FFXIV.Client.Game.Network;
using Glamourer.Events; using Glamourer.Events;
using Luna;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -12,7 +13,7 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public unsafe class UpdateSlotService : IDisposable public sealed unsafe class UpdateSlotService : IDisposable, IRequiredService
{ {
public readonly EquipSlotUpdating EquipSlotUpdatingEvent; public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
public readonly BonusSlotUpdating BonusSlotUpdatingEvent; public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
@ -97,18 +98,18 @@ public unsafe class UpdateSlotService : IDisposable
{ {
var slot = slotIdx.ToEquipSlot(); var slot = slotIdx.ToEquipSlot();
var returnValue = ulong.MaxValue; 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})."); 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) private ulong FlagBonusSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
{ {
var slot = slotIdx.ToBonusSlot(); var slot = slotIdx.ToBonusSlot();
var returnValue = ulong.MaxValue; 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})."); 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) 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 ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
var drawObject = drawDataContainer->OwnerObject->DrawObject; 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)}"); Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
return ret; return ret;
} }

View file

@ -2,12 +2,13 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Glamourer.Events; using Glamourer.Events;
using Luna;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public unsafe class VieraEarService : IDisposable public unsafe sealed class VieraEarService : IDisposable, IRequiredService
{ {
private readonly PenumbraReloaded _penumbra; private readonly PenumbraReloaded _penumbra;
private readonly IGameInteropProvider _interop; private readonly IGameInteropProvider _interop;
@ -57,10 +58,10 @@ public unsafe class VieraEarService : IDisposable
private void SetupVieraEarDetour(DrawDataContainer* drawData, byte value) private void SetupVieraEarDetour(DrawDataContainer* drawData, byte value)
{ {
Actor actor = drawData->OwnerObject; Actor actor = drawData->OwnerObject;
var originalOn = value != 0; var originalOn = value is not 0;
var on = originalOn; var on = originalOn;
// Invoke an event that can change the requested value // 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( Glamourer.Log.Verbose(
$"[SetVieraEarState] Invoked from game on 0x{actor.Address:X} switching to {on} (original {originalOn} from {value})."); $"[SetVieraEarState] Invoked from game on 0x{actor.Address:X} switching to {on} (original {originalOn} from {value}).");

View file

@ -2,12 +2,13 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Events; using Glamourer.Events;
using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public class VisorService : IDisposable public sealed class VisorService : IDisposable, IRequiredService
{ {
private readonly PenumbraReloaded _penumbra; private readonly PenumbraReloaded _penumbra;
private readonly IGameInteropProvider _interop; private readonly IGameInteropProvider _interop;
@ -58,11 +59,11 @@ public class VisorService : IDisposable
private void SetupVisorDetour(nint human, ushort modelId, byte value) private void SetupVisorDetour(nint human, ushort modelId, byte value)
{ {
var originalOn = value != 0; var originalOn = value is not 0;
var on = originalOn; var on = originalOn;
// Invoke an event that can change the requested value // Invoke an event that can change the requested value
// and also control whether the function should be called at all. // 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( Glamourer.Log.Verbose(
$"[SetVisorState] Invoked from game on 0x{human:X} switching to {on} (original {originalOn} from {value} with {modelId})."); $"[SetVisorState] Invoked from game on 0x{human:X} switching to {on} (original {originalOn} from {value} with {modelId}).");

View file

@ -2,13 +2,14 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Glamourer.Events; using Glamourer.Events;
using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Interop; namespace Glamourer.Interop;
public unsafe class WeaponService : IDisposable public sealed unsafe class WeaponService : IDisposable, IRequiredService
{ {
private readonly WeaponLoading _event; private readonly WeaponLoading _event;
private readonly ThreadLocal<bool> _inUpdate = new(() => false); private readonly ThreadLocal<bool> _inUpdate = new(() => false);
@ -59,17 +60,17 @@ public unsafe class WeaponService : IDisposable
var tmpWeapon = weapon; var tmpWeapon = weapon;
// First call the regular function. // First call the regular function.
if (equipSlot is not EquipSlot.Unknown) 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? // 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. // 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) else if (weaponValue == actor.GetMainhand().Value && weaponValue is not 0)
_event.Invoke(actor, EquipSlot.MainHand, ref tmpWeapon); _event.Invoke(new WeaponLoading.Arguments(actor, EquipSlot.MainHand, ref tmpWeapon));
_loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4, unk5); _loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4, unk5);
if (tmpWeapon.Value != weapon.Value) if (tmpWeapon.Value != weapon.Value)
{ {
if (tmpWeapon.Skeleton.Id == 0) if (tmpWeapon.Skeleton.Id is 0)
tmpWeapon.Stains = StainIds.None; tmpWeapon.Stains = StainIds.None;
_loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4, unk5); _loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4, unk5);
} }

View file

@ -2,7 +2,7 @@ using Luna;
namespace Glamourer.Services; namespace Glamourer.Services;
public class BackupService(Logger log, FilenameService provider) : BaseBackupService<FilenameService>(log, provider) public sealed class BackupService(Logger log, FilenameService provider) : BaseBackupService<FilenameService>(log, provider)
{ {
/// <summary> Collect all relevant files for glamourer configuration. </summary> /// <summary> Collect all relevant files for glamourer configuration. </summary>
private static IReadOnlyList<FileInfo> GlamourerFiles(FilenameService fileNames) private static IReadOnlyList<FileInfo> GlamourerFiles(FilenameService fileNames)

View file

@ -1,10 +1,11 @@
using Glamourer.Config; using Glamourer.Config;
using ImSharp; using ImSharp;
using Luna;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
namespace Glamourer.Services; namespace Glamourer.Services;
public class CodeService public sealed class CodeService : IService
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly SHA256 _hasher = SHA256.Create(); private readonly SHA256 _hasher = SHA256.Create();

View file

@ -1,12 +1,9 @@
using Dalamud.Interface.ImGuiNotification;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
using Luna; using Luna;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Extensions;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Extensions = OtterGui.Filesystem.Extensions;
using Notification = Luna.Notification; using Notification = Luna.Notification;
namespace Glamourer.Services; namespace Glamourer.Services;
@ -31,7 +28,7 @@ public sealed class CollectionOverrideService : IService, ISavable
if (!identifier.IsValid) if (!identifier.IsValid)
identifier = _actors.FromObject(actor.AsObject, out _, true, true, true); 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) ? (ret.CollectionId, ret.DisplayName, true)
: (_penumbra.GetActorCollection(actor, out var name), name, false); : (_penumbra.GetActorCollection(actor, out var name), name, false);
} }
@ -78,7 +75,7 @@ public sealed class CollectionOverrideService : IService, ISavable
if (idx < 0 || idx >= _overrides.Count) if (idx < 0 || idx >= _overrides.Count)
return; return;
if (newCollectionId == Guid.Empty || newDisplayName.Length == 0) if (newCollectionId == Guid.Empty || newDisplayName.Length is 0)
return; return;
var current = _overrides[idx]; var current = _overrides[idx];
@ -106,7 +103,7 @@ public sealed class CollectionOverrideService : IService, ISavable
public void MoveOverride(int idxFrom, int idxTo) public void MoveOverride(int idxFrom, int idxTo)
{ {
if (!Extensions.Move(_overrides, idxFrom, idxTo)) if (!_overrides.Move(idxFrom, idxTo))
return; return;
Glamourer.Log.Debug($"Moved collection override {idxFrom + 1} to {idxTo + 1}."); 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) public void Save(StreamWriter writer)
{ {
var jObj = new JObject() var jObj = new JObject
{ {
["Version"] = Version, ["Version"] = Version,
["Overrides"] = SerializeOverrides(), ["Overrides"] = SerializeOverrides(),

View file

@ -16,7 +16,6 @@ using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using SeStringBuilderExtensions = OtterGui.Classes.SeStringBuilderExtensions;
namespace Glamourer.Services; namespace Glamourer.Services;

View file

@ -2,11 +2,12 @@
using Glamourer.Config; using Glamourer.Config;
using Glamourer.Gui; using Glamourer.Gui;
using ImSharp; using ImSharp;
using Luna;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace Glamourer.Services; 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 Configuration _config = null!;
private JObject _data = null!; private JObject _data = null!;

View file

@ -142,11 +142,10 @@ public class DesignResolver(
} }
else else
{ {
var lower = argument.ToLowerInvariant();
// Search for design by name and partial identifier. // 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. // Search for design by path, if nothing was found.
if (design is null && designFileSystem.Find(lower, out var child) && child is IFileSystemData<Design> leaf) if (design is null && designFileSystem.Find(argument, out var child) && child is IFileSystemData<Design> leaf)
design = leaf.Value; design = leaf.Value;
} }
@ -159,13 +158,13 @@ public class DesignResolver(
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Func<Design, bool> MatchNameAndIdentifier(string lower) private static Func<Design, bool> MatchNameAndIdentifier(string text)
{ {
// Check for names and identifiers, prefer names // Check for names and identifiers, prefer names
if (lower.Length > 3) if (text.Length > 3)
return d => d.Name.Lower == lower || d.Identifier.ToString().StartsWith(lower); return d => string.Equals(d.Name, text, StringComparison.OrdinalIgnoreCase) || d.Identifier.ToString().StartsWith(text, StringComparison.OrdinalIgnoreCase);
// Check only for names. // Check only for names.
return d => d.Name.Lower == lower; return d => string.Equals(d.Name, text, StringComparison.OrdinalIgnoreCase);
} }
} }

View file

@ -4,7 +4,7 @@ using Luna;
namespace Glamourer.Services; 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 MigrationDesignFileSystem = Path.Combine(pi.ConfigDirectory.FullName, "sort_order.json");
public readonly string MigrationDesignFile = Path.Combine(pi.ConfigDirectory.FullName, "Designs.json"); public readonly string MigrationDesignFile = Path.Combine(pi.ConfigDirectory.FullName, "Designs.json");

View file

@ -2,6 +2,7 @@ using Dalamud.Plugin.Services;
using Glamourer.Config; using Glamourer.Config;
using Lumina.Excel; using Lumina.Excel;
using Lumina.Excel.Sheets; using Lumina.Excel.Sheets;
using Luna;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -10,7 +11,7 @@ using Race = Penumbra.GameData.Enums.Race;
namespace Glamourer.Services; namespace Glamourer.Services;
public class ItemManager public sealed class ItemManager : IService
{ {
public const string Nothing = EquipItem.Nothing; public const string Nothing = EquipItem.Nothing;
public const string SmallClothesNpc = "Smallclothes (NPC)"; public const string SmallClothesNpc = "Smallclothes (NPC)";

View file

@ -8,4 +8,4 @@ namespace Glamourer.Services;
public interface ISavable : ISavable<FilenameService>; public interface ISavable : ISavable<FilenameService>;
public sealed class SaveService(Logger log, FrameworkManager framework, FilenameService fileNames) public sealed class SaveService(Logger log, FrameworkManager framework, FilenameService fileNames)
: BaseSaveService<FilenameService>(log, framework, fileNames); : BaseSaveService<FilenameService>(log, framework, fileNames), IService;

View file

@ -1,37 +1,12 @@
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Api; using Glamourer.Api;
using Glamourer.Api.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.Interop.Penumbra;
using Glamourer.State;
using Glamourer.Unlocks;
using Luna; using Luna;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OtterGui.Classes;
using OtterGui.Raii;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Interop; using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using FrameworkManager = OtterGui.Classes.FrameworkManager;
using Logger = OtterGui.Log.Logger;
using MessageService = Luna.MessageService;
namespace Glamourer.Services; namespace Glamourer.Services;
@ -39,146 +14,19 @@ public static class StaticServiceManager
{ {
public static ServiceManager CreateProvider(IDalamudPluginInterface pi, Logger log, Glamourer glamourer) public static ServiceManager CreateProvider(IDalamudPluginInterface pi, Logger log, Glamourer glamourer)
{ {
EventWrapperBase.ChangeLogger(log); var services = new ServiceManager(log)
var services = new ServiceManager(new Luna.Logger("Glamourer"))
.AddExistingService(log) .AddExistingService(log)
.AddMeta() .AddSingleton<MessageService>()
.AddInterop() .AddSingleton<ActorObjectManager>()
.AddEvents() .AddSingleton(p => new CutsceneResolver(p.GetRequiredService<PenumbraService>().CutsceneParent))
.AddData()
.AddDesigns()
.AddState()
.AddUi()
.AddExistingService(glamourer); .AddExistingService(glamourer);
DalamudServices.AddServices(services, pi);
services.AddIServices(typeof(EquipItem).Assembly); services.AddIServices(typeof(EquipItem).Assembly);
services.AddIServices(typeof(Glamourer).Assembly); services.AddIServices(typeof(Glamourer).Assembly);
services.AddIServices(typeof(ImRaii).Assembly);
services.AddIServices(typeof(ServiceManager).Assembly); services.AddIServices(typeof(ServiceManager).Assembly);
services.AddIServices<OtterGui.Services.IService>(typeof(Glamourer).Assembly);
services.AddSingleton<DictJob>();
services.AddSingleton<DictJobGroup>();
services.AddSingleton<NameDicts>();
services.AddSingleton<DictWorld>();
services.AddSingleton<IGlamourerApi>(p => p.GetRequiredService<GlamourerApi>()); services.AddSingleton<IGlamourerApi>(p => p.GetRequiredService<GlamourerApi>());
DalamudServices.AddServices(services, pi);
services.BuildProvider(); services.BuildProvider();
return services; return services;
} }
private static ServiceManager AddMeta(this ServiceManager services)
=> services.AddSingleton<MessageService>()
.AddSingleton<FilenameService>()
.AddSingleton<BackupService>()
.AddSingleton<FrameworkManager>()
.AddSingleton<SaveService>()
.AddSingleton<CodeService>()
.AddSingleton<ConfigMigrationService>()
.AddSingleton<Configuration>()
.AddSingleton<EphemeralConfig>()
.AddSingleton<TextureService>()
.AddSingleton<FavoriteManager>();
private static ServiceManager AddEvents(this ServiceManager services)
=> services.AddSingleton<VisorStateChanged>()
.AddSingleton<VieraEarStateChanged>()
.AddSingleton<EquipSlotUpdating>()
.AddSingleton<DesignChanged>()
.AddSingleton<AutomationChanged>()
.AddSingleton<StateChanged>()
.AddSingleton<WeaponLoading>()
.AddSingleton<HeadGearVisibilityChanged>()
.AddSingleton<WeaponVisibilityChanged>()
.AddSingleton<ObjectUnlocked>()
.AddSingleton<TabSelected>()
.AddSingleton<MovedEquipment>()
.AddSingleton<EquippedGearset>()
.AddSingleton<GPoseService>()
.AddSingleton<PenumbraReloaded>();
private static ServiceManager AddData(this ServiceManager services)
=> services.AddSingleton<ObjectIdentification>()
.AddSingleton<ItemData>()
.AddSingleton<ActorManager>()
.AddSingleton<CustomizeService>()
.AddSingleton<ItemManager>()
.AddSingleton<GamePathParser>()
.AddSingleton<HumanModelList>();
private static ServiceManager AddInterop(this ServiceManager services)
=> services.AddSingleton<VisorService>()
.AddSingleton<VieraEarService>()
.AddSingleton<ChangeCustomizeService>()
.AddSingleton<MetaService>()
.AddSingleton<UpdateSlotService>()
.AddSingleton<WeaponService>()
.AddSingleton<PenumbraService>()
.AddSingleton(p => new CutsceneResolver(p.GetRequiredService<PenumbraService>().CutsceneParent))
.AddSingleton<ObjectManager>()
.AddSingleton<ActorObjectManager>()
.AddSingleton<PenumbraAutoRedraw>()
.AddSingleton<JobService>()
.AddSingleton<CustomizeUnlockManager>()
.AddSingleton<ItemUnlockManager>()
.AddSingleton<ImportService>()
.AddSingleton<CrestService>()
.AddSingleton<InventoryService>()
.AddSingleton<ContextMenuService>()
.AddSingleton<ScalingService>()
.AddSingleton<DynamisIpc>();
private static ServiceManager AddDesigns(this ServiceManager services)
=> services.AddSingleton<DesignManager>()
.AddSingleton<DesignFileSystem>()
.AddSingleton<AutoDesignManager>()
.AddSingleton<AutoDesignApplier>()
.AddSingleton<FixedDesignMigrator>()
.AddSingleton<DesignConverter>()
.AddSingleton<DesignColors>();
private static ServiceManager AddState(this ServiceManager services)
=> services.AddSingleton<StateManager>()
.AddSingleton<StateApplier>()
.AddSingleton<InternalStateEditor>()
.AddSingleton<StateListener>()
.AddSingleton<FunModule>();
private static ServiceManager AddUi(this ServiceManager services)
=> services.AddSingleton<DebugTab>()
.AddSingleton<MessagesTab>()
.AddSingleton<SettingsTab>()
.AddSingleton<ActorTab>()
.AddSingleton<ActorSelector>()
.AddSingleton<ActorPanel>()
.AddSingleton<NpcPanel>()
.AddSingleton<NpcSelector>()
.AddSingleton<LocalNpcAppearanceData>()
.AddSingleton<NpcTab>()
.AddSingleton<MainWindow>()
.AddSingleton<GenericPopupWindow>()
.AddSingleton<GlamourerWindowSystem>()
.AddSingleton<CustomizationDrawer>()
.AddSingleton<EquipmentDrawer>()
.AddSingleton<DesignFileSystemDrawer>()
.AddSingleton<MultiDesignPanel>()
.AddSingleton<DesignPanel>()
.AddSingleton<DesignTab>()
.AddSingleton<QuickDesignCombo>()
.AddSingleton<LinkDesignCombo>()
.AddSingleton<RandomDesignCombo>()
.AddSingleton<SpecialDesignCombo>()
.AddSingleton<ModAssociationsTab>()
.AddSingleton<DesignDetailTab>()
.AddSingleton<UnlockTable>()
.AddSingleton<UnlockOverview>()
.AddSingleton<UnlocksTab>()
.AddSingleton<PenumbraChangedItemTooltip>()
.AddSingleton<AutomationTab>()
.AddSingleton<SetSelector>()
.AddSingleton<SetPanel>()
.AddSingleton<IdentifierDrawer>()
.AddSingleton<GlamourerChangelog>()
.AddSingleton<DesignQuickBar>()
.AddSingleton<DesignColorUi>()
.AddSingleton<TextureCache>();
} }

View file

@ -1,41 +1,56 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Textures;
using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using ImSharp; using ImSharp;
using Luna; using Luna;
using OtterGui.Classes;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Glamourer.Services; namespace Glamourer.Services;
public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManager, ITextureProvider textureProvider) public sealed class TextureService(IUiBuilder uiBuilder, ITextureProvider textureProvider)
: TextureCache(dataManager, textureProvider), IDisposable : IDisposable, IUiService
{ {
private readonly IDalamudTextureWrap?[] _slotIcons = CreateSlotIcons(uiBuilder); 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) 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); return (ret.Id, new Vector2(ret.Width, ret.Height), false);
var idx = slot.ToIndex(); 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) ? (_slotIcons[idx]!.Id, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true)
: (default, Vector2.Zero, true); : (default, Vector2.Zero, true);
} }
public (ImTextureId, Vector2, bool) GetIcon(EquipItem item, BonusItemFlag slot) 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); return (ret.Id, new Vector2(ret.Width, ret.Height), false);
var idx = slot.ToIndex(); var idx = slot.ToIndex();
if (idx == uint.MaxValue) if (idx is uint.MaxValue)
return (default, Vector2.Zero, true); return (default, Vector2.Zero, true);
idx += 12; 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) ? (_slotIcons[idx]!.Id, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true)
: (default, Vector2.Zero, true); : (default, Vector2.Zero, true);
} }
@ -72,7 +87,7 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage
SetIcon(EquipSlot.RFinger, 28); SetIcon(EquipSlot.RFinger, 28);
SetIcon(EquipSlot.MainHand, 17); SetIcon(EquipSlot.MainHand, 17);
SetIcon(EquipSlot.OffHand, 18); 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()]; ret[EquipSlot.LFinger.ToIndex()] = ret[EquipSlot.RFinger.ToIndex()];
return ret; return ret;

Some files were not shown because too many files have changed in this diff Show more