Add IpcManual state.

This commit is contained in:
Ottermandias 2024-01-30 17:51:52 +01:00
parent 994b7bfb6c
commit 818bf71032
15 changed files with 242 additions and 109 deletions

View file

@ -11,7 +11,9 @@ namespace Glamourer.Api;
public partial class GlamourerIpc public partial class GlamourerIpc
{ {
public const string LabelApplyAll = "Glamourer.ApplyAll"; public const string LabelApplyAll = "Glamourer.ApplyAll";
public const string LabelApplyAllOnce = "Glamourer.ApplyAllOnce";
public const string LabelApplyAllToCharacter = "Glamourer.ApplyAllToCharacter"; public const string LabelApplyAllToCharacter = "Glamourer.ApplyAllToCharacter";
public const string LabelApplyAllOnceToCharacter = "Glamourer.ApplyAllOnceToCharacter";
public const string LabelApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment"; public const string LabelApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment";
public const string LabelApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter"; public const string LabelApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter";
public const string LabelApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization"; public const string LabelApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization";
@ -24,11 +26,15 @@ public partial class GlamourerIpc
public const string LabelApplyOnlyCustomizationLock = "Glamourer.ApplyOnlyCustomizationLock"; public const string LabelApplyOnlyCustomizationLock = "Glamourer.ApplyOnlyCustomizationLock";
public const string LabelApplyOnlyCustomizationToCharacterLock = "Glamourer.ApplyOnlyCustomizationToCharacterLock"; public const string LabelApplyOnlyCustomizationToCharacterLock = "Glamourer.ApplyOnlyCustomizationToCharacterLock";
public const string LabelApplyByGuid = "Glamourer.ApplyByGuid"; public const string LabelApplyByGuid = "Glamourer.ApplyByGuid";
public const string LabelApplyByGuidToCharacter = "Glamourer.ApplyByGuidToCharacter"; public const string LabelApplyByGuidOnce = "Glamourer.ApplyByGuidOnce";
public const string LabelApplyByGuidToCharacter = "Glamourer.ApplyByGuidToCharacter";
public const string LabelApplyByGuidOnceToCharacter = "Glamourer.ApplyByGuidOnceToCharacter";
private readonly ActionProvider<string, string> _applyAllProvider; private readonly ActionProvider<string, string> _applyAllProvider;
private readonly ActionProvider<string, string> _applyAllOnceProvider;
private readonly ActionProvider<string, Character?> _applyAllToCharacterProvider; private readonly ActionProvider<string, Character?> _applyAllToCharacterProvider;
private readonly ActionProvider<string, Character?> _applyAllOnceToCharacterProvider;
private readonly ActionProvider<string, string> _applyOnlyEquipmentProvider; private readonly ActionProvider<string, string> _applyOnlyEquipmentProvider;
private readonly ActionProvider<string, Character?> _applyOnlyEquipmentToCharacterProvider; private readonly ActionProvider<string, Character?> _applyOnlyEquipmentToCharacterProvider;
private readonly ActionProvider<string, string> _applyOnlyCustomizationProvider; private readonly ActionProvider<string, string> _applyOnlyCustomizationProvider;
@ -42,14 +48,22 @@ public partial class GlamourerIpc
private readonly ActionProvider<string, Character?, uint> _applyOnlyCustomizationToCharacterProviderLock; private readonly ActionProvider<string, Character?, uint> _applyOnlyCustomizationToCharacterProviderLock;
private readonly ActionProvider<Guid, string> _applyByGuidProvider; private readonly ActionProvider<Guid, string> _applyByGuidProvider;
private readonly ActionProvider<Guid, string> _applyByGuidOnceProvider;
private readonly ActionProvider<Guid, Character?> _applyByGuidToCharacterProvider; private readonly ActionProvider<Guid, Character?> _applyByGuidToCharacterProvider;
private readonly ActionProvider<Guid, Character?> _applyByGuidOnceToCharacterProvider;
public static ActionSubscriber<string, string> ApplyAllSubscriber(DalamudPluginInterface pi) public static ActionSubscriber<string, string> ApplyAllSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyAll); => new(pi, LabelApplyAll);
public static ActionSubscriber<string, string> ApplyAllOnceSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyAllOnce);
public static ActionSubscriber<string, Character?> ApplyAllToCharacterSubscriber(DalamudPluginInterface pi) public static ActionSubscriber<string, Character?> ApplyAllToCharacterSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyAllToCharacter); => new(pi, LabelApplyAllToCharacter);
public static ActionSubscriber<string, Character?> ApplyAllOnceToCharacterSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyAllOnceToCharacter);
public static ActionSubscriber<string, string> ApplyOnlyEquipmentSubscriber(DalamudPluginInterface pi) public static ActionSubscriber<string, string> ApplyOnlyEquipmentSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyOnlyEquipment); => new(pi, LabelApplyOnlyEquipment);
@ -65,15 +79,27 @@ public partial class GlamourerIpc
public static ActionSubscriber<Guid, string> ApplyByGuidSubscriber(DalamudPluginInterface pi) public static ActionSubscriber<Guid, string> ApplyByGuidSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyByGuid); => new(pi, LabelApplyByGuid);
public static ActionSubscriber<Guid, string> ApplyByGuidOnceSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyByGuidOnce);
public static ActionSubscriber<Guid, Character?> ApplyByGuidToCharacterSubscriber(DalamudPluginInterface pi) public static ActionSubscriber<Guid, Character?> ApplyByGuidToCharacterSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyByGuidToCharacter); => new(pi, LabelApplyByGuidToCharacter);
public static ActionSubscriber<Guid, Character?> ApplyByGuidOnceToCharacterSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelApplyByGuidOnceToCharacter);
public void ApplyAll(string base64, string characterName) public void ApplyAll(string base64, string characterName)
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(characterName), version, 0); => ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(characterName), version, 0);
public void ApplyAllOnce(string base64, string characterName)
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(characterName), version, 0, true);
public void ApplyAllToCharacter(string base64, Character? character) public void ApplyAllToCharacter(string base64, Character? character)
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(character), version, 0); => ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(character), version, 0);
public void ApplyAllOnceToCharacter(string base64, Character? character)
=> ApplyDesign(_designConverter.FromBase64(base64, true, true, out var version), FindActors(character), version, 0, true);
public void ApplyOnlyEquipment(string base64, string characterName) public void ApplyOnlyEquipment(string base64, string characterName)
=> ApplyDesign(_designConverter.FromBase64(base64, false, true, out var version), FindActors(characterName), version, 0); => ApplyDesign(_designConverter.FromBase64(base64, false, true, out var version), FindActors(characterName), version, 0);
@ -107,12 +133,18 @@ public partial class GlamourerIpc
public void ApplyByGuid(Guid identifier, string characterName) public void ApplyByGuid(Guid identifier, string characterName)
=> ApplyDesignByGuid(identifier, FindActors(characterName), 0); => ApplyDesignByGuid(identifier, FindActors(characterName), 0, false);
public void ApplyByGuidOnce(Guid identifier, string characterName)
=> ApplyDesignByGuid(identifier, FindActors(characterName), 0, true);
public void ApplyByGuidToCharacter(Guid identifier, Character? character) public void ApplyByGuidToCharacter(Guid identifier, Character? character)
=> ApplyDesignByGuid(identifier, FindActors(character), 0); => ApplyDesignByGuid(identifier, FindActors(character), 0, false);
private void ApplyDesign(DesignBase? design, IEnumerable<ActorIdentifier> actors, byte version, uint lockCode) public void ApplyByGuidOnceToCharacter(Guid identifier, Character? character)
=> ApplyDesignByGuid(identifier, FindActors(character), 0, true);
private void ApplyDesign(DesignBase? design, IEnumerable<ActorIdentifier> actors, byte version, uint lockCode, bool once = false)
{ {
if (design == null) if (design == null)
return; return;
@ -130,12 +162,13 @@ public partial class GlamourerIpc
if ((hasModelId || state.ModelData.ModelId == 0) && state.CanUnlock(lockCode)) if ((hasModelId || state.ModelData.ModelId == 0) && state.CanUnlock(lockCode))
{ {
_stateManager.ApplyDesign(state, design, new ApplySettings(Source:StateSource.Ipc, Key:lockCode)); _stateManager.ApplyDesign(state, design,
new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: lockCode));
state.Lock(lockCode); state.Lock(lockCode);
} }
} }
} }
private void ApplyDesignByGuid(Guid identifier, IEnumerable<ActorIdentifier> actors, uint lockCode) private void ApplyDesignByGuid(Guid identifier, IEnumerable<ActorIdentifier> actors, uint lockCode, bool once)
=> ApplyDesign(_designManager.Designs.ByIdentifier(identifier), actors, DesignConverter.Version, lockCode); => ApplyDesign(_designManager.Designs.ByIdentifier(identifier), actors, DesignConverter.Version, lockCode, once);
} }

View file

@ -1,6 +1,5 @@
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Events;
using Glamourer.State; using Glamourer.State;
using Penumbra.Api.Helpers; using Penumbra.Api.Helpers;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
@ -83,7 +82,7 @@ public partial class GlamourerIpc
foreach (var id in actors) foreach (var id in actors)
{ {
if (_stateManager.TryGetValue(id, out var state)) if (_stateManager.TryGetValue(id, out var state))
_stateManager.ResetState(state, StateSource.Ipc, lockCode); _stateManager.ResetState(state, StateSource.IpcFixed, lockCode);
} }
} }

View file

@ -20,20 +20,30 @@ public partial class GlamourerIpc
ItemInvalid, ItemInvalid,
} }
public const string LabelSetItem = "Glamourer.SetItem"; public const string LabelSetItem = "Glamourer.SetItem";
public const string LabelSetItemByActorName = "Glamourer.SetItemByActorName"; public const string LabelSetItemOnce = "Glamourer.SetItemOnce";
public const string LabelSetItemByActorName = "Glamourer.SetItemByActorName";
public const string LabelSetItemOnceByActorName = "Glamourer.SetItemOnceByActorName";
private readonly FuncProvider<Character?, byte, ulong, byte, uint, int> _setItemProvider; private readonly FuncProvider<Character?, byte, ulong, byte, uint, int> _setItemProvider;
private readonly FuncProvider<Character?, byte, ulong, byte, uint, int> _setItemOnceProvider;
private readonly FuncProvider<string, byte, ulong, byte, uint, int> _setItemByActorNameProvider; private readonly FuncProvider<string, byte, ulong, byte, uint, int> _setItemByActorNameProvider;
private readonly FuncProvider<string, byte, ulong, byte, uint, int> _setItemOnceByActorNameProvider;
public static FuncSubscriber<Character?, byte, ulong, byte, uint, int> SetItemSubscriber(DalamudPluginInterface pi) public static FuncSubscriber<Character?, byte, ulong, byte, uint, int> SetItemSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelSetItem); => new(pi, LabelSetItem);
public static FuncSubscriber<Character?, byte, ulong, byte, uint, int> SetItemOnceSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelSetItemOnce);
public static FuncSubscriber<string, byte, ulong, byte, uint, int> SetItemByActorNameSubscriber(DalamudPluginInterface pi) public static FuncSubscriber<string, byte, ulong, byte, uint, int> SetItemByActorNameSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelSetItemByActorName); => new(pi, LabelSetItemByActorName);
private GlamourerErrorCode SetItem(Character? character, EquipSlot slot, CustomItemId itemId, StainId stainId, uint key) public static FuncSubscriber<string, byte, ulong, byte, uint, int> SetItemOnceByActorNameSubscriber(DalamudPluginInterface pi)
=> new(pi, LabelSetItemOnceByActorName);
private GlamourerErrorCode SetItem(Character? character, EquipSlot slot, CustomItemId itemId, StainId stainId, uint key, bool once)
{ {
if (itemId.Id == 0) if (itemId.Id == 0)
itemId = ItemManager.NothingId(slot); itemId = ItemManager.NothingId(slot);
@ -57,11 +67,12 @@ public partial class GlamourerIpc
if (!state.ModelData.IsHuman) if (!state.ModelData.IsHuman)
return GlamourerErrorCode.ActorNotHuman; return GlamourerErrorCode.ActorNotHuman;
_stateManager.ChangeEquip(state, slot, item, stainId, new ApplySettings(Source: StateSource.Ipc, Key:key)); _stateManager.ChangeEquip(state, slot, item, stainId,
new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key));
return GlamourerErrorCode.Success; return GlamourerErrorCode.Success;
} }
private GlamourerErrorCode SetItemByActorName(string name, EquipSlot slot, CustomItemId itemId, StainId stainId, uint key) private GlamourerErrorCode SetItemByActorName(string name, EquipSlot slot, CustomItemId itemId, StainId stainId, uint key, bool once)
{ {
if (itemId.Id == 0) if (itemId.Id == 0)
itemId = ItemManager.NothingId(slot); itemId = ItemManager.NothingId(slot);
@ -84,7 +95,8 @@ public partial class GlamourerIpc
if (!state.ModelData.IsHuman) if (!state.ModelData.IsHuman)
return GlamourerErrorCode.ActorNotHuman; return GlamourerErrorCode.ActorNotHuman;
_stateManager.ChangeEquip(state, slot, item, stainId, new ApplySettings(Source: StateSource.Ipc, Key: key)); _stateManager.ChangeEquip(state, slot, item, stainId,
new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key));
found = true; found = true;
} }

View file

@ -46,9 +46,11 @@ public sealed partial class GlamourerIpc : IDisposable
_getAllCustomizationFromCharacterProvider = _getAllCustomizationFromCharacterProvider =
new FuncProvider<Character?, string?>(pi, LabelGetAllCustomizationFromCharacter, GetAllCustomizationFromCharacter); new FuncProvider<Character?, string?>(pi, LabelGetAllCustomizationFromCharacter, GetAllCustomizationFromCharacter);
_applyAllProvider = new ActionProvider<string, string>(pi, LabelApplyAll, ApplyAll); _applyAllProvider = new ActionProvider<string, string>(pi, LabelApplyAll, ApplyAll);
_applyAllToCharacterProvider = new ActionProvider<string, Character?>(pi, LabelApplyAllToCharacter, ApplyAllToCharacter); _applyAllOnceProvider = new ActionProvider<string, string>(pi, LabelApplyAll, ApplyAllOnce);
_applyOnlyEquipmentProvider = new ActionProvider<string, string>(pi, LabelApplyOnlyEquipment, ApplyOnlyEquipment); _applyAllToCharacterProvider = new ActionProvider<string, Character?>(pi, LabelApplyAllToCharacter, ApplyAllToCharacter);
_applyAllOnceToCharacterProvider = new ActionProvider<string, Character?>(pi, LabelApplyAllToCharacter, ApplyAllOnceToCharacter);
_applyOnlyEquipmentProvider = new ActionProvider<string, string>(pi, LabelApplyOnlyEquipment, ApplyOnlyEquipment);
_applyOnlyEquipmentToCharacterProvider = _applyOnlyEquipmentToCharacterProvider =
new ActionProvider<string, Character?>(pi, LabelApplyOnlyEquipmentToCharacter, ApplyOnlyEquipmentToCharacter); new ActionProvider<string, Character?>(pi, LabelApplyOnlyEquipmentToCharacter, ApplyOnlyEquipmentToCharacter);
_applyOnlyCustomizationProvider = new ActionProvider<string, string>(pi, LabelApplyOnlyCustomization, ApplyOnlyCustomization); _applyOnlyCustomizationProvider = new ActionProvider<string, string>(pi, LabelApplyOnlyCustomization, ApplyOnlyCustomization);
@ -66,8 +68,11 @@ public sealed partial class GlamourerIpc : IDisposable
_applyOnlyCustomizationToCharacterProviderLock = _applyOnlyCustomizationToCharacterProviderLock =
new ActionProvider<string, Character?, uint>(pi, LabelApplyOnlyCustomizationToCharacterLock, ApplyOnlyCustomizationToCharacterLock); new ActionProvider<string, Character?, uint>(pi, LabelApplyOnlyCustomizationToCharacterLock, ApplyOnlyCustomizationToCharacterLock);
_applyByGuidProvider = new ActionProvider<Guid, string>(pi, LabelApplyByGuid, ApplyByGuid); _applyByGuidProvider = new ActionProvider<Guid, string>(pi, LabelApplyByGuid, ApplyByGuid);
_applyByGuidOnceProvider = new ActionProvider<Guid, string>(pi, LabelApplyByGuidOnce, ApplyByGuidOnce);
_applyByGuidToCharacterProvider = new ActionProvider<Guid, Character?>(pi, LabelApplyByGuidToCharacter, ApplyByGuidToCharacter); _applyByGuidToCharacterProvider = new ActionProvider<Guid, Character?>(pi, LabelApplyByGuidToCharacter, ApplyByGuidToCharacter);
_applyByGuidOnceToCharacterProvider =
new ActionProvider<Guid, Character?>(pi, LabelApplyByGuidOnceToCharacter, ApplyByGuidOnceToCharacter);
_revertProvider = new ActionProvider<string>(pi, LabelRevert, Revert); _revertProvider = new ActionProvider<string>(pi, LabelRevert, Revert);
_revertCharacterProvider = new ActionProvider<Character?>(pi, LabelRevertCharacter, RevertCharacter); _revertCharacterProvider = new ActionProvider<Character?>(pi, LabelRevertCharacter, RevertCharacter);
@ -83,9 +88,14 @@ public sealed partial class GlamourerIpc : IDisposable
_gPoseChangedProvider = new EventProvider<bool>(pi, LabelGPoseChanged); _gPoseChangedProvider = new EventProvider<bool>(pi, LabelGPoseChanged);
_setItemProvider = new FuncProvider<Character?, byte, ulong, byte, uint, int>(pi, LabelSetItem, _setItemProvider = new FuncProvider<Character?, byte, ulong, byte, uint, int>(pi, LabelSetItem,
(idx, slot, item, stain, key) => (int)SetItem(idx, (EquipSlot)slot, item, stain, key)); (idx, slot, item, stain, key) => (int)SetItem(idx, (EquipSlot)slot, item, stain, key, false));
_setItemByActorNameProvider = new FuncProvider<string, byte, ulong, byte, uint, int>(pi, LabelSetItemByActorName, _setItemOnceProvider = new FuncProvider<Character?, byte, ulong, byte, uint, int>(pi, LabelSetItem,
(name, slot, item, stain, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, stain, key)); (idx, slot, item, stain, key) => (int)SetItem(idx, (EquipSlot)slot, item, stain, key, true));
_setItemByActorNameProvider = new FuncProvider<string, byte, ulong, byte, uint, int>(pi, LabelSetItemOnceByActorName,
(name, slot, item, stain, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, stain, key, false));
_setItemOnceByActorNameProvider = new FuncProvider<string, byte, ulong, byte, uint, int>(pi, LabelSetItemOnceByActorName,
(name, slot, item, stain, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, stain, key, true));
_stateChangedEvent.Subscribe(OnStateChanged, StateChanged.Priority.GlamourerIpc); _stateChangedEvent.Subscribe(OnStateChanged, StateChanged.Priority.GlamourerIpc);
_gPose.Subscribe(OnGPoseChanged, GPoseService.Priority.GlamourerIpc); _gPose.Subscribe(OnGPoseChanged, GPoseService.Priority.GlamourerIpc);
@ -102,7 +112,9 @@ public sealed partial class GlamourerIpc : IDisposable
_getAllCustomizationFromCharacterProvider.Dispose(); _getAllCustomizationFromCharacterProvider.Dispose();
_applyAllProvider.Dispose(); _applyAllProvider.Dispose();
_applyAllOnceProvider.Dispose();
_applyAllToCharacterProvider.Dispose(); _applyAllToCharacterProvider.Dispose();
_applyAllOnceToCharacterProvider.Dispose();
_applyOnlyEquipmentProvider.Dispose(); _applyOnlyEquipmentProvider.Dispose();
_applyOnlyEquipmentToCharacterProvider.Dispose(); _applyOnlyEquipmentToCharacterProvider.Dispose();
_applyOnlyCustomizationProvider.Dispose(); _applyOnlyCustomizationProvider.Dispose();
@ -113,8 +125,11 @@ public sealed partial class GlamourerIpc : IDisposable
_applyOnlyEquipmentToCharacterProviderLock.Dispose(); _applyOnlyEquipmentToCharacterProviderLock.Dispose();
_applyOnlyCustomizationProviderLock.Dispose(); _applyOnlyCustomizationProviderLock.Dispose();
_applyOnlyCustomizationToCharacterProviderLock.Dispose(); _applyOnlyCustomizationToCharacterProviderLock.Dispose();
_applyByGuidProvider.Dispose(); _applyByGuidProvider.Dispose();
_applyByGuidOnceProvider.Dispose();
_applyByGuidToCharacterProvider.Dispose(); _applyByGuidToCharacterProvider.Dispose();
_applyByGuidOnceToCharacterProvider.Dispose();
_revertProvider.Dispose(); _revertProvider.Dispose();
_revertCharacterProvider.Dispose(); _revertCharacterProvider.Dispose();
@ -133,7 +148,9 @@ public sealed partial class GlamourerIpc : IDisposable
_getDesignListProvider.Dispose(); _getDesignListProvider.Dispose();
_setItemProvider.Dispose(); _setItemProvider.Dispose();
_setItemOnceProvider.Dispose();
_setItemByActorNameProvider.Dispose(); _setItemByActorNameProvider.Dispose();
_setItemOnceByActorNameProvider.Dispose();
} }
private IEnumerable<ActorIdentifier> FindActors(string actorName) private IEnumerable<ActorIdentifier> FindActors(string actorName)

View file

@ -42,6 +42,7 @@ public class Configuration : IPluginConfiguration, ISavable
public bool UseRgbForColors { get; set; } = true; public bool UseRgbForColors { get; set; } = true;
public bool ShowColorConfig { get; set; } = true; public bool ShowColorConfig { get; set; } = true;
public bool ChangeEntireItem { get; set; } = false; public bool ChangeEntireItem { get; set; } = false;
public bool AlwaysApplyAssociatedMods { get; set; } = false;
public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY); public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY);
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New;

View file

@ -1,7 +1,6 @@
using Dalamud.Interface; using Dalamud.Interface;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;

View file

@ -28,7 +28,9 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag
private string _base64Apply = string.Empty; private string _base64Apply = string.Empty;
private string _designIdentifier = string.Empty; private string _designIdentifier = string.Empty;
private GlamourerIpc.GlamourerErrorCode _setItemEc; private GlamourerIpc.GlamourerErrorCode _setItemEc;
private GlamourerIpc.GlamourerErrorCode _setItemOnceEc;
private GlamourerIpc.GlamourerErrorCode _setItemByActorNameEc; private GlamourerIpc.GlamourerErrorCode _setItemByActorNameEc;
private GlamourerIpc.GlamourerErrorCode _setItemOnceByActorNameEc;
public unsafe void Draw() public unsafe void Draw()
{ {
@ -77,12 +79,23 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag
if (ImGui.Button("Apply##AllName")) if (ImGui.Button("Apply##AllName"))
GlamourerIpc.ApplyAllSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName); GlamourerIpc.ApplyAllSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllOnce);
ImGui.TableNextColumn();
if (ImGui.Button("Apply Once##AllName"))
GlamourerIpc.ApplyAllOnceSubscriber(_pluginInterface).Invoke(_base64Apply, _gameObjectName);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllToCharacter); ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllToCharacter);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGui.Button("Apply##AllCharacter")) if (ImGui.Button("Apply##AllCharacter"))
GlamourerIpc.ApplyAllToCharacterSubscriber(_pluginInterface) GlamourerIpc.ApplyAllToCharacterSubscriber(_pluginInterface)
.Invoke(_base64Apply, _objectManager.Objects[_gameObjectIndex] as Character); .Invoke(_base64Apply, _objectManager.Objects[_gameObjectIndex] as Character);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyAllOnceToCharacter);
ImGui.TableNextColumn();
if (ImGui.Button("Apply Once##AllCharacter"))
GlamourerIpc.ApplyAllOnceToCharacterSubscriber(_pluginInterface)
.Invoke(_base64Apply, _objectManager.Objects[_gameObjectIndex] as Character);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyEquipment); ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyOnlyEquipment);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGui.Button("Apply##EquipName")) if (ImGui.Button("Apply##EquipName"))
@ -111,12 +124,23 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag
if (ImGui.Button("Apply##ByGuidName") && Guid.TryParse(_designIdentifier, out var guid1)) if (ImGui.Button("Apply##ByGuidName") && Guid.TryParse(_designIdentifier, out var guid1))
GlamourerIpc.ApplyByGuidSubscriber(_pluginInterface).Invoke(guid1, _gameObjectName); GlamourerIpc.ApplyByGuidSubscriber(_pluginInterface).Invoke(guid1, _gameObjectName);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidOnce);
ImGui.TableNextColumn();
if (ImGui.Button("Apply Once##ByGuidName") && Guid.TryParse(_designIdentifier, out var guid1Once))
GlamourerIpc.ApplyByGuidOnceSubscriber(_pluginInterface).Invoke(guid1Once, _gameObjectName);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidToCharacter); ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidToCharacter);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGui.Button("Apply##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2)) if (ImGui.Button("Apply##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2))
GlamourerIpc.ApplyByGuidToCharacterSubscriber(_pluginInterface) GlamourerIpc.ApplyByGuidToCharacterSubscriber(_pluginInterface)
.Invoke(guid2, _objectManager.Objects[_gameObjectIndex] as Character); .Invoke(guid2, _objectManager.Objects[_gameObjectIndex] as Character);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelApplyByGuidOnceToCharacter);
ImGui.TableNextColumn();
if (ImGui.Button("Apply Once##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2Once))
GlamourerIpc.ApplyByGuidOnceToCharacterSubscriber(_pluginInterface)
.Invoke(guid2Once, _objectManager.Objects[_gameObjectIndex] as Character);
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelUnlock); ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelUnlock);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
@ -149,6 +173,17 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag
ImGui.TextUnformatted(_setItemEc.ToString()); ImGui.TextUnformatted(_setItemEc.ToString());
} }
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemOnce);
ImGui.TableNextColumn();
if (ImGui.Button("Set Once##SetItem"))
_setItemOnceEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemOnceSubscriber(_pluginInterface)
.Invoke(_objectManager.Objects[_gameObjectIndex] as Character, (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
if (_setItemOnceEc != GlamourerIpc.GlamourerErrorCode.Success)
{
ImGui.SameLine();
ImGui.TextUnformatted(_setItemOnceEc.ToString());
}
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemByActorName); ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemByActorName);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGui.Button("Set##SetItemByActorName")) if (ImGui.Button("Set##SetItemByActorName"))
@ -159,6 +194,17 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted(_setItemByActorNameEc.ToString()); ImGui.TextUnformatted(_setItemByActorNameEc.ToString());
} }
ImGuiUtil.DrawTableColumn(GlamourerIpc.LabelSetItemOnceByActorName);
ImGui.TableNextColumn();
if (ImGui.Button("Set Once##SetItemByActorName"))
_setItemOnceByActorNameEc = (GlamourerIpc.GlamourerErrorCode)GlamourerIpc.SetItemOnceByActorNameSubscriber(_pluginInterface)
.Invoke(_gameObjectName, (byte)_slot, _customItemId.Id, _stainId.Id, 1337);
if (_setItemOnceByActorNameEc != GlamourerIpc.GlamourerErrorCode.Success)
{
ImGui.SameLine();
ImGui.TextUnformatted(_setItemOnceByActorNameEc.ToString());
}
} }
private void DrawItemInput() private void DrawItemInput()

View file

@ -95,9 +95,13 @@ public class DesignDetailTab
Glamourer.Messager.NotificationMessage(ex, $"Could not open file {fileName}.", $"Could not open file {fileName}", Glamourer.Messager.NotificationMessage(ex, $"Could not open file {fileName}.", $"Could not open file {fileName}",
NotificationType.Warning); NotificationType.Warning);
} }
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
ImGui.SetClipboardText(identifier);
} }
ImGuiUtil.HoverTooltip($"Open the file\n\t{fileName}\ncontaining this design in the .json-editor of your choice."); ImGuiUtil.HoverTooltip(
$"Open the file\n\t{fileName}\ncontaining this design in the .json-editor of your choice.\n\nRight-Click to copy identifier to clipboard.");
ImGuiUtil.DrawFrameColumn("Full Selector Path"); ImGuiUtil.DrawFrameColumn("Full Selector Path");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
@ -131,9 +135,10 @@ public class DesignDetailTab
colorName = _colorCombo.CurrentSelection is DesignColors.AutomaticName ? string.Empty : _colorCombo.CurrentSelection; colorName = _colorCombo.CurrentSelection is DesignColors.AutomaticName ? string.Empty : _colorCombo.CurrentSelection;
_manager.ChangeColor(_selector.Selected!, colorName); _manager.ChangeColor(_selector.Selected!, colorName);
} }
if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
_manager.ChangeColor(_selector.Selected!, string.Empty); _manager.ChangeColor(_selector.Selected!, string.Empty);
if (_colors.TryGetValue(_selector.Selected!.Color, out var currentColor)) if (_colors.TryGetValue(_selector.Selected!.Color, out var currentColor))
{ {
ImGui.SameLine(); ImGui.SameLine();

View file

@ -89,6 +89,11 @@ public class SettingsTab(
Checkbox("Enable Advanced Customization Options", Checkbox("Enable Advanced Customization Options",
"Enable the display and editing of advanced customization options like arbitrary colors.", "Enable the display and editing of advanced customization options like arbitrary colors.",
config.UseAdvancedParameters, paletteChecker.SetAdvancedParameters); config.UseAdvancedParameters, paletteChecker.SetAdvancedParameters);
Checkbox("Always Apply Associated Mods",
"Whenever a design is applied to a character (including via automation), Glamourer will try to apply its associated mod settings to the collection currently associated with that character, if it is available.\n\n"
+ "Glamourer will NOT revert these applied settings automatically. This may mess up your collection and configuration.\n\n"
+ "If you enable this setting, you are aware that any resulting misconfiguration is your own fault.",
config.AlwaysApplyAssociatedMods, v => config.AlwaysApplyAssociatedMods = v);
PaletteImportButton(); PaletteImportButton();
ImGui.NewLine(); ImGui.NewLine();
} }

View file

@ -131,19 +131,16 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
} }
else else
{ {
switch (source) switch (source.Base())
{ {
case StateSource.Manual: case StateSource.Manual:
case StateSource.Pending:
state.Materials.RemoveValue(idx); state.Materials.RemoveValue(idx);
--i; --i;
break; break;
case StateSource.Ipc:
case StateSource.Fixed: case StateSource.Fixed:
idx.DataIndex.SetValue(ref row, model); idx.DataIndex.SetValue(ref row, model);
state.Materials.UpdateValue(idx, new MaterialValueState(newGame, model, source), out _); state.Materials.UpdateValue(idx, new MaterialValueState(newGame, model, source), out _);
break; break;
} }
} }
} }

View file

@ -118,8 +118,7 @@ public class StateApplier(
// If the source is not IPC we do not want to apply restrictions. // If the source is not IPC we do not want to apply restrictions.
var data = GetData(state); var data = GetData(state);
if (apply) if (apply)
ChangeArmor(data, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc, ChangeArmor(data, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible());
state.ModelData.IsHatVisible());
return data; return data;
} }
@ -267,7 +266,7 @@ public class StateApplier(
actor.Model.ApplyParameterData(flags, values); actor.Model.ApplyParameterData(flags, values);
} }
/// <inheritdoc cref="ChangeParameters(ActorData,CustomizeParameterFlag,in CustomizeParameterData)"/> /// <inheritdoc cref="ChangeParameters(ActorData,CustomizeParameterFlag,in CustomizeParameterData,bool)"/>
public ActorData ChangeParameters(ActorState state, CustomizeParameterFlag flags, bool apply) public ActorData ChangeParameters(ActorState state, CustomizeParameterFlag flags, bool apply)
{ {
var data = GetData(state); var data = GetData(state);
@ -294,10 +293,7 @@ public class StateApplier(
{ {
ChangeCustomize(actors, state.ModelData.Customize); ChangeCustomize(actors, state.ModelData.Customize);
foreach (var slot in EquipSlotExtensions.EqdpSlots) foreach (var slot in EquipSlotExtensions.EqdpSlots)
{ ChangeArmor(actors, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible());
ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc,
state.ModelData.IsHatVisible());
}
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors; var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand)); ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));

View file

@ -31,7 +31,7 @@ public class StateEditor(
if (!Editor.ChangeModelId(state, modelId, customize, equipData, source, out var old, key)) if (!Editor.ChangeModelId(state, modelId, customize, equipData, source, out var old, key))
return; return;
var actors = Applier.ForceRedraw(state, source is StateSource.Manual or StateSource.Ipc); var actors = Applier.ForceRedraw(state, source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId)); StateChanged.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId));
@ -44,7 +44,7 @@ public class StateEditor(
if (!Editor.ChangeCustomize(state, idx, value, settings.Source, out var old, settings.Key)) if (!Editor.ChangeCustomize(state, idx, value, settings.Source, out var old, settings.Key))
return; return;
var actors = Applier.ChangeCustomize(state, settings.Source is StateSource.Manual or StateSource.Ipc); var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Customize, settings.Source, state, actors, (old, value, idx)); StateChanged.Invoke(StateChanged.Type.Customize, settings.Source, state, actors, (old, value, idx));
@ -57,7 +57,7 @@ public class StateEditor(
if (!Editor.ChangeHumanCustomize(state, customizeInput, apply, _ => settings.Source, out var old, out var applied, settings.Key)) if (!Editor.ChangeHumanCustomize(state, customizeInput, apply, _ => settings.Source, out var old, out var applied, settings.Key))
return; return;
var actors = Applier.ChangeCustomize(state, settings.Source is StateSource.Manual or StateSource.Ipc); var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.EntireCustomize, settings.Source, state, actors, (old, applied)); StateChanged.Invoke(StateChanged.Type.EntireCustomize, settings.Source, state, actors, (old, applied));
@ -72,8 +72,8 @@ public class StateEditor(
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon; var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
var actors = type is StateChanged.Type.Equip var actors = type is StateChanged.Type.Equip
? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc) ? Applier.ChangeArmor(state, slot, settings.Source.RequiresChange())
: Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc, : Applier.ChangeWeapon(state, slot, settings.Source.RequiresChange(),
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType)); item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
if (slot is EquipSlot.MainHand) if (slot is EquipSlot.MainHand)
@ -105,8 +105,8 @@ public class StateEditor(
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon; var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
var actors = type is StateChanged.Type.Equip var actors = type is StateChanged.Type.Equip
? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc) ? Applier.ChangeArmor(state, slot, settings.Source.RequiresChange())
: Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc, : Applier.ChangeWeapon(state, slot, settings.Source.RequiresChange(),
item!.Value.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType)); item!.Value.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
if (slot is EquipSlot.MainHand) if (slot is EquipSlot.MainHand)
@ -125,7 +125,7 @@ public class StateEditor(
if (!Editor.ChangeStain(state, slot, stain, settings.Source, out var old, settings.Key)) if (!Editor.ChangeStain(state, slot, stain, settings.Source, out var old, settings.Key))
return; return;
var actors = Applier.ChangeStain(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc); var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Stain, settings.Source, state, actors, (old, stain, slot)); StateChanged.Invoke(StateChanged.Type.Stain, settings.Source, state, actors, (old, stain, slot));
@ -138,7 +138,7 @@ public class StateEditor(
if (!Editor.ChangeCrest(state, slot, crest, settings.Source, out var old, settings.Key)) if (!Editor.ChangeCrest(state, slot, crest, settings.Source, out var old, settings.Key))
return; return;
var actors = Applier.ChangeCrests(state, settings.Source is StateSource.Manual or StateSource.Ipc); var actors = Applier.ChangeCrests(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {slot.ToLabel()} crest in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {slot.ToLabel()} crest in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Crest, settings.Source, state, actors, (old, crest, slot)); StateChanged.Invoke(StateChanged.Type.Crest, settings.Source, state, actors, (old, crest, slot));
@ -158,7 +158,7 @@ public class StateEditor(
return; return;
var @new = state.ModelData.Parameters[flag]; var @new = state.ModelData.Parameters[flag];
var actors = Applier.ChangeParameters(state, flag, settings.Source is StateSource.Manual or StateSource.Ipc); var actors = Applier.ChangeParameters(state, flag, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {flag} crest in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {flag} crest in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Parameter, settings.Source, state, actors, (old, @new, flag)); StateChanged.Invoke(StateChanged.Type.Parameter, settings.Source, state, actors, (old, @new, flag));
@ -171,7 +171,7 @@ public class StateEditor(
if (!Editor.ChangeMetaState(state, index, value, settings.Source, out var old, settings.Key)) if (!Editor.ChangeMetaState(state, index, value, settings.Source, out var old, settings.Key))
return; return;
var actors = Applier.ChangeMetaState(state, index, settings.Source is StateSource.Manual or StateSource.Ipc); var actors = Applier.ChangeMetaState(state, index, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set Head Gear Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set Head Gear Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Other, settings.Source, state, actors, (old, value, MetaIndex.HatState)); StateChanged.Invoke(StateChanged.Type.Other, settings.Source, state, actors, (old, value, MetaIndex.HatState));
@ -191,7 +191,7 @@ public class StateEditor(
{ {
foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest)) foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest))
{ {
if (!settings.RespectManual || state.Sources[slot] is not StateSource.Manual) if (!settings.RespectManual || !state.Sources[slot].IsManual())
Editor.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), Source(slot), Editor.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), Source(slot),
out _, settings.Key); out _, settings.Key);
} }
@ -201,7 +201,7 @@ public class StateEditor(
customizeFlags |= CustomizeFlag.Race; customizeFlags |= CustomizeFlag.Race;
Func<CustomizeIndex, bool> applyWhich = settings.RespectManual Func<CustomizeIndex, bool> applyWhich = settings.RespectManual
? i => customizeFlags.HasFlag(i.ToFlag()) && state.Sources[i] is not StateSource.Manual ? i => customizeFlags.HasFlag(i.ToFlag()) && !state.Sources[i].IsManual()
: i => customizeFlags.HasFlag(i.ToFlag()); : i => customizeFlags.HasFlag(i.ToFlag());
if (Editor.ChangeHumanCustomize(state, mergedDesign.Design.DesignData.Customize, applyWhich, i => Source(i), out _, out var changed, if (Editor.ChangeHumanCustomize(state, mergedDesign.Design.DesignData.Customize, applyWhich, i => Source(i), out _, out var changed,
@ -210,12 +210,10 @@ public class StateEditor(
foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate()) foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
{ {
if (settings.RespectManual && state.Sources[parameter] is StateSource.Manual or StateSource.Pending) if (settings.RespectManual && state.Sources[parameter].IsManual())
continue; continue;
var source = Source(parameter); var source = Source(parameter).SetPending();
if (source is StateSource.Manual)
source = StateSource.Pending;
Editor.ChangeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], source, out _, settings.Key); Editor.ChangeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], source, out _, settings.Key);
} }
@ -228,12 +226,12 @@ public class StateEditor(
foreach (var slot in EquipSlotExtensions.EqdpSlots) foreach (var slot in EquipSlotExtensions.EqdpSlots)
{ {
if (mergedDesign.Design.DoApplyEquip(slot)) if (mergedDesign.Design.DoApplyEquip(slot))
if (!settings.RespectManual || state.Sources[slot, false] is not StateSource.Manual) if (!settings.RespectManual || !state.Sources[slot, false].IsManual())
Editor.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot), Editor.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot),
Source(slot.ToState()), out _, settings.Key); Source(slot.ToState()), out _, settings.Key);
if (mergedDesign.Design.DoApplyStain(slot)) if (mergedDesign.Design.DoApplyStain(slot))
if (!settings.RespectManual || state.Sources[slot, true] is not StateSource.Manual) if (!settings.RespectManual || !state.Sources[slot, true].IsManual())
Editor.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot), Editor.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot),
Source(slot.ToState(true)), out _, settings.Key); Source(slot.ToState(true)), out _, settings.Key);
} }
@ -241,14 +239,14 @@ public class StateEditor(
foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots) foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots)
{ {
if (mergedDesign.Design.DoApplyStain(weaponSlot)) if (mergedDesign.Design.DoApplyStain(weaponSlot))
if (!settings.RespectManual || state.Sources[weaponSlot, true] is not StateSource.Manual) if (!settings.RespectManual || !state.Sources[weaponSlot, true].IsManual())
Editor.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot), Editor.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot),
Source(weaponSlot.ToState(true)), out _, settings.Key); Source(weaponSlot.ToState(true)), out _, settings.Key);
if (!mergedDesign.Design.DoApplyEquip(weaponSlot)) if (!mergedDesign.Design.DoApplyEquip(weaponSlot))
continue; continue;
if (settings.RespectManual && state.Sources[weaponSlot, false] is StateSource.Manual) if (settings.RespectManual && !state.Sources[weaponSlot, false].IsManual())
continue; continue;
var currentType = state.ModelData.Item(weaponSlot).Type; var currentType = state.ModelData.Item(weaponSlot).Type;
@ -268,12 +266,12 @@ public class StateEditor(
foreach (var meta in MetaExtensions.AllRelevant) foreach (var meta in MetaExtensions.AllRelevant)
{ {
if (!settings.RespectManual || state.Sources[meta] is not StateSource.Manual) if (!settings.RespectManual || !state.Sources[meta].IsManual())
Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key); Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key);
} }
} }
var actors = settings.Source is StateSource.Manual or StateSource.Ipc var actors = settings.Source.RequiresChange()
? Applier.ApplyAll(state, requiresRedraw, false) ? Applier.ApplyAll(state, requiresRedraw, false)
: ActorData.Invalid; : ActorData.Invalid;
@ -311,7 +309,7 @@ public class StateEditor(
/// <summary> Apply offhand item and potentially gauntlets if configured. </summary> /// <summary> Apply offhand item and potentially gauntlets if configured. </summary>
private void ApplyMainhandPeriphery(ActorState state, EquipItem? newMainhand, ApplySettings settings) private void ApplyMainhandPeriphery(ActorState state, EquipItem? newMainhand, ApplySettings settings)
{ {
if (!Config.ChangeEntireItem || settings.Source is not StateSource.Manual) if (!Config.ChangeEntireItem || !settings.Source.IsManual())
return; return;
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand); var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);

View file

@ -171,7 +171,7 @@ public class StateListener : IDisposable
var set = _customizations.Manager.GetSet(model.Clan, model.Gender); var set = _customizations.Manager.GetSet(model.Clan, model.Gender);
foreach (var index in CustomizationExtensions.AllBasic) foreach (var index in CustomizationExtensions.AllBasic)
{ {
if (state.Sources[index] is not StateSource.Fixed) if (!state.Sources[index].IsFixed())
{ {
var newValue = customize[index]; var newValue = customize[index];
var oldValue = model[index]; var oldValue = model[index];
@ -214,7 +214,7 @@ public class StateListener : IDisposable
&& _manager.TryGetValue(identifier, out var state)) && _manager.TryGetValue(identifier, out var state))
{ {
HandleEquipSlot(actor, state, slot, ref armor); HandleEquipSlot(actor, state, slot, ref armor);
locked = state.Sources[slot, false] is StateSource.Ipc; locked = state.Sources[slot, false] is StateSource.IpcFixed;
} }
_funModule.ApplyFunToSlot(actor, ref armor, slot); _funModule.ApplyFunToSlot(actor, ref armor, slot);
@ -241,7 +241,7 @@ public class StateListener : IDisposable
continue; continue;
var changed = changedItem.Weapon(stain); var changed = changedItem.Weapon(stain);
if (current.Value == changed.Value && state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc) if (current.Value == changed.Value && !state.Sources[slot, false].IsFixed())
{ {
_manager.ChangeItem(state, slot, currentItem, ApplySettings.Game); _manager.ChangeItem(state, slot, currentItem, ApplySettings.Game);
_manager.ChangeStain(state, slot, current.Stain, ApplySettings.Game); _manager.ChangeStain(state, slot, current.Stain, ApplySettings.Game);
@ -252,7 +252,7 @@ public class StateListener : IDisposable
_applier.ChangeWeapon(objects, slot, currentItem, stain); _applier.ChangeWeapon(objects, slot, currentItem, stain);
break; break;
default: default:
_applier.ChangeArmor(objects, slot, current.ToArmor(), state.Sources[slot, false] is not StateSource.Ipc, _applier.ChangeArmor(objects, slot, current.ToArmor(), !state.Sources[slot, false].IsFixed(),
state.ModelData.IsHatVisible()); state.ModelData.IsHatVisible());
break; break;
} }
@ -278,20 +278,19 @@ public class StateListener : IDisposable
|| !_manager.TryGetValue(identifier, out var state)) || !_manager.TryGetValue(identifier, out var state))
return; return;
ref var actorWeapon = ref weapon; var baseType = state.BaseData.Item(slot).Type;
var baseType = state.BaseData.Item(slot).Type; var apply = false;
var apply = false; switch (UpdateBaseData(actor, state, slot, weapon))
switch (UpdateBaseData(actor, state, slot, actorWeapon))
{ {
// Do nothing. But this usually can not happen because the hooked function also writes to game objects later. // Do nothing. But this usually can not happen because the hooked function also writes to game objects later.
case UpdateState.Transformed: break; case UpdateState.Transformed: break;
case UpdateState.Change: case UpdateState.Change:
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc) if (!state.Sources[slot, false].IsFixed())
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game); _manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game);
else else
apply = true; apply = true;
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc) if (!state.Sources[slot, true].IsFixed())
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game); _manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
else else
apply = true; apply = true;
@ -306,9 +305,9 @@ public class StateListener : IDisposable
// Only allow overwriting identical weapons // Only allow overwriting identical weapons
var newWeapon = state.ModelData.Weapon(slot); var newWeapon = state.ModelData.Weapon(slot);
if (baseType is FullEquipType.Unknown || baseType == state.ModelData.Item(slot).Type || _gPose.InGPose && actor.IsGPoseOrCutscene) if (baseType is FullEquipType.Unknown || baseType == state.ModelData.Item(slot).Type || _gPose.InGPose && actor.IsGPoseOrCutscene)
actorWeapon = newWeapon; weapon = newWeapon;
else if (actorWeapon.Skeleton.Id != 0) else if (weapon.Skeleton.Id != 0)
actorWeapon = actorWeapon.With(newWeapon.Stain); weapon = weapon.With(newWeapon.Stain);
} }
// Fist Weapon Offhand hack. // Fist Weapon Offhand hack.
@ -385,12 +384,12 @@ public class StateListener : IDisposable
// Update model state if not on fixed design. // Update model state if not on fixed design.
case UpdateState.Change: case UpdateState.Change:
var apply = false; var apply = false;
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc) if (!state.Sources[slot, false].IsFixed())
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game); _manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game);
else else
apply = true; apply = true;
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc) if (!state.Sources[slot, true].IsFixed())
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game); _manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
else else
apply = true; apply = true;
@ -419,7 +418,7 @@ public class StateListener : IDisposable
switch (UpdateBaseCrest(actor, state, slot, value)) switch (UpdateBaseCrest(actor, state, slot, value))
{ {
case UpdateState.Change: case UpdateState.Change:
if (state.Sources[slot] is not StateSource.Fixed and not StateSource.Ipc) if (!state.Sources[slot].IsFixed())
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), ApplySettings.Game); _manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), ApplySettings.Game);
else else
value = state.ModelData.Crest(slot); value = state.ModelData.Crest(slot);
@ -565,7 +564,7 @@ public class StateListener : IDisposable
{ {
// if base state changed, either overwrite the actual value if we have fixed values, // if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one. // or overwrite the stored model state with the new one.
if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed or StateSource.Ipc) if (!state.Sources[MetaIndex.VisorState].IsFixed())
value = state.ModelData.IsVisorToggled(); value = state.ModelData.IsVisorToggled();
else else
_manager.ChangeMetaState(state, MetaIndex.VisorState, value, ApplySettings.Game); _manager.ChangeMetaState(state, MetaIndex.VisorState, value, ApplySettings.Game);
@ -598,7 +597,7 @@ public class StateListener : IDisposable
{ {
// if base state changed, either overwrite the actual value if we have fixed values, // if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one. // or overwrite the stored model state with the new one.
if (state.Sources[MetaIndex.HatState] is StateSource.Fixed or StateSource.Ipc) if (!state.Sources[MetaIndex.HatState].IsFixed())
value = state.ModelData.IsHatVisible(); value = state.ModelData.IsHatVisible();
else else
_manager.ChangeMetaState(state, MetaIndex.HatState, value, ApplySettings.Game); _manager.ChangeMetaState(state, MetaIndex.HatState, value, ApplySettings.Game);
@ -631,7 +630,7 @@ public class StateListener : IDisposable
{ {
// if base state changed, either overwrite the actual value if we have fixed values, // if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one. // or overwrite the stored model state with the new one.
if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed or StateSource.Ipc) if (!state.Sources[MetaIndex.WeaponState].IsFixed())
value = state.ModelData.IsWeaponVisible(); value = state.ModelData.IsWeaponVisible();
else else
_manager.ChangeMetaState(state, MetaIndex.WeaponState, value, ApplySettings.Game); _manager.ChangeMetaState(state, MetaIndex.WeaponState, value, ApplySettings.Game);
@ -700,8 +699,8 @@ public class StateListener : IDisposable
return; return;
var data = new ActorData(gameObject, _creatingIdentifier.ToName()); var data = new ActorData(gameObject, _creatingIdentifier.ToName());
_applier.ChangeMetaState(data, MetaIndex.HatState, _creatingState.ModelData.IsHatVisible()); _applier.ChangeMetaState(data, MetaIndex.HatState, _creatingState.ModelData.IsHatVisible());
_applier.ChangeMetaState(data, MetaIndex.Wetness, _creatingState.ModelData.IsWet()); _applier.ChangeMetaState(data, MetaIndex.Wetness, _creatingState.ModelData.IsWet());
_applier.ChangeMetaState(data, MetaIndex.WeaponState, _creatingState.ModelData.IsWeaponVisible()); _applier.ChangeMetaState(data, MetaIndex.WeaponState, _creatingState.ModelData.IsWeaponVisible());
ApplyParameters(_creatingState, drawObject); ApplyParameters(_creatingState, drawObject);
@ -745,12 +744,18 @@ public class StateListener : IDisposable
else if (_config.UseAdvancedParameters) else if (_config.UseAdvancedParameters)
model.ApplySingleParameterData(flag, state.ModelData.Parameters); model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break; break;
case StateSource.IpcManual:
if (state.BaseData.Parameters.Set(flag, newValue))
_manager.ChangeCustomizeParameter(state, flag, newValue, ApplySettings.Game);
else
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break;
case StateSource.Fixed: case StateSource.Fixed:
state.BaseData.Parameters.Set(flag, newValue); state.BaseData.Parameters.Set(flag, newValue);
if (_config.UseAdvancedParameters) if (_config.UseAdvancedParameters)
model.ApplySingleParameterData(flag, state.ModelData.Parameters); model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break; break;
case StateSource.Ipc: case StateSource.IpcFixed:
state.BaseData.Parameters.Set(flag, newValue); state.BaseData.Parameters.Set(flag, newValue);
model.ApplySingleParameterData(flag, state.ModelData.Parameters); model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break; break;

View file

@ -243,7 +243,7 @@ public sealed class StateManager(
state.Materials.Clear(); state.Materials.Clear();
var actors = ActorData.Invalid; var actors = ActorData.Invalid;
if (source is StateSource.Manual or StateSource.Ipc) if (source is not StateSource.Game)
actors = Applier.ApplyAll(state, redraw, true); actors = Applier.ApplyAll(state, redraw, true);
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
@ -262,7 +262,7 @@ public sealed class StateManager(
state.Sources[flag] = StateSource.Game; state.Sources[flag] = StateSource.Game;
var actors = ActorData.Invalid; var actors = ActorData.Invalid;
if (source is StateSource.Manual or StateSource.Ipc) if (source is not StateSource.Game)
actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true); actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true);
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Reset advanced customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]"); $"Reset advanced customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
@ -316,28 +316,10 @@ public sealed class StateManager(
} }
} }
if (state.Sources[MetaIndex.HatState] is StateSource.Fixed) foreach (var meta in MetaExtensions.AllRelevant.Where(f => state.Sources[f] is StateSource.Fixed))
{ {
state.Sources[MetaIndex.HatState] = StateSource.Game; state.Sources[meta] = StateSource.Game;
state.ModelData.SetHatVisible(state.BaseData.IsHatVisible()); state.ModelData.SetMeta(meta, state.BaseData.GetMeta(meta));
}
if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed)
{
state.Sources[MetaIndex.VisorState] = StateSource.Game;
state.ModelData.SetVisor(state.BaseData.IsVisorToggled());
}
if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed)
{
state.Sources[MetaIndex.WeaponState] = StateSource.Game;
state.ModelData.SetWeaponVisible(state.BaseData.IsWeaponVisible());
}
if (state.Sources[MetaIndex.Wetness] is StateSource.Fixed)
{
state.Sources[MetaIndex.Wetness] = StateSource.Game;
state.ModelData.SetIsWet(state.BaseData.IsWet());
} }
} }

View file

@ -7,12 +7,48 @@ public enum StateSource : byte
Game, Game,
Manual, Manual,
Fixed, Fixed,
Ipc, IpcFixed,
IpcManual,
// Only used for CustomizeParameters. // Only used for CustomizeParameters.
Pending, Pending,
} }
public static class StateSourceExtensions
{
public static StateSource Base(this StateSource source)
=> source switch
{
StateSource.Manual or StateSource.IpcManual or StateSource.Pending => StateSource.Manual,
StateSource.Fixed or StateSource.IpcFixed => StateSource.Fixed,
_ => StateSource.Game,
};
public static bool IsGame(this StateSource source)
=> source.Base() is StateSource.Game;
public static bool IsManual(this StateSource source)
=> source.Base() is StateSource.Manual;
public static bool IsFixed(this StateSource source)
=> source.Base() is StateSource.Fixed;
public static StateSource SetPending(this StateSource source)
=> source is StateSource.Manual ? StateSource.Pending : source;
public static bool RequiresChange(this StateSource source)
=> source switch
{
StateSource.Manual => true,
StateSource.IpcFixed => true,
StateSource.IpcManual => true,
_ => false,
};
public static bool IsIpc(this StateSource source)
=> source is StateSource.IpcManual or StateSource.IpcFixed;
}
public unsafe struct StateSources public unsafe struct StateSources
{ {
public const int Size = (StateIndex.Size + 1) / 2; public const int Size = (StateIndex.Size + 1) / 2;
@ -59,14 +95,16 @@ public unsafe struct StateSources
case (byte)StateSource.Game | ((byte)StateSource.Fixed << 4): case (byte)StateSource.Game | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.Manual | ((byte)StateSource.Fixed << 4): case (byte)StateSource.Manual | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.Ipc | ((byte)StateSource.Fixed << 4): case (byte)StateSource.IpcFixed | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.Pending | ((byte)StateSource.Fixed << 4): case (byte)StateSource.Pending | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.IpcManual | ((byte)StateSource.Fixed << 4):
_data[i] = (byte)((value & 0x0F) | ((byte)StateSource.Manual << 4)); _data[i] = (byte)((value & 0x0F) | ((byte)StateSource.Manual << 4));
break; break;
case (byte)StateSource.Fixed: case (byte)StateSource.Fixed:
case ((byte)StateSource.Manual << 4) | (byte)StateSource.Fixed: case ((byte)StateSource.Manual << 4) | (byte)StateSource.Fixed:
case ((byte)StateSource.Ipc << 4) | (byte)StateSource.Fixed: case ((byte)StateSource.IpcFixed << 4) | (byte)StateSource.Fixed:
case ((byte)StateSource.Pending << 4) | (byte)StateSource.Fixed: case ((byte)StateSource.Pending << 4) | (byte)StateSource.Fixed:
case ((byte)StateSource.IpcManual << 4) | (byte)StateSource.Fixed:
_data[i] = (byte)((value & 0xF0) | (byte)StateSource.Manual); _data[i] = (byte)((value & 0xF0) | (byte)StateSource.Manual);
break; break;
} }