diff --git a/Glamourer/Api/GlamourerIpc.Apply.cs b/Glamourer/Api/GlamourerIpc.Apply.cs index d155cf2..f09d491 100644 --- a/Glamourer/Api/GlamourerIpc.Apply.cs +++ b/Glamourer/Api/GlamourerIpc.Apply.cs @@ -11,7 +11,9 @@ namespace Glamourer.Api; public partial class GlamourerIpc { public const string LabelApplyAll = "Glamourer.ApplyAll"; + public const string LabelApplyAllOnce = "Glamourer.ApplyAllOnce"; public const string LabelApplyAllToCharacter = "Glamourer.ApplyAllToCharacter"; + public const string LabelApplyAllOnceToCharacter = "Glamourer.ApplyAllOnceToCharacter"; public const string LabelApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment"; public const string LabelApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter"; public const string LabelApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization"; @@ -24,11 +26,15 @@ public partial class GlamourerIpc public const string LabelApplyOnlyCustomizationLock = "Glamourer.ApplyOnlyCustomizationLock"; public const string LabelApplyOnlyCustomizationToCharacterLock = "Glamourer.ApplyOnlyCustomizationToCharacterLock"; - public const string LabelApplyByGuid = "Glamourer.ApplyByGuid"; - public const string LabelApplyByGuidToCharacter = "Glamourer.ApplyByGuidToCharacter"; + public const string LabelApplyByGuid = "Glamourer.ApplyByGuid"; + public const string LabelApplyByGuidOnce = "Glamourer.ApplyByGuidOnce"; + public const string LabelApplyByGuidToCharacter = "Glamourer.ApplyByGuidToCharacter"; + public const string LabelApplyByGuidOnceToCharacter = "Glamourer.ApplyByGuidOnceToCharacter"; private readonly ActionProvider _applyAllProvider; + private readonly ActionProvider _applyAllOnceProvider; private readonly ActionProvider _applyAllToCharacterProvider; + private readonly ActionProvider _applyAllOnceToCharacterProvider; private readonly ActionProvider _applyOnlyEquipmentProvider; private readonly ActionProvider _applyOnlyEquipmentToCharacterProvider; private readonly ActionProvider _applyOnlyCustomizationProvider; @@ -42,14 +48,22 @@ public partial class GlamourerIpc private readonly ActionProvider _applyOnlyCustomizationToCharacterProviderLock; private readonly ActionProvider _applyByGuidProvider; + private readonly ActionProvider _applyByGuidOnceProvider; private readonly ActionProvider _applyByGuidToCharacterProvider; + private readonly ActionProvider _applyByGuidOnceToCharacterProvider; public static ActionSubscriber ApplyAllSubscriber(DalamudPluginInterface pi) => new(pi, LabelApplyAll); + public static ActionSubscriber ApplyAllOnceSubscriber(DalamudPluginInterface pi) + => new(pi, LabelApplyAllOnce); + public static ActionSubscriber ApplyAllToCharacterSubscriber(DalamudPluginInterface pi) => new(pi, LabelApplyAllToCharacter); + public static ActionSubscriber ApplyAllOnceToCharacterSubscriber(DalamudPluginInterface pi) + => new(pi, LabelApplyAllOnceToCharacter); + public static ActionSubscriber ApplyOnlyEquipmentSubscriber(DalamudPluginInterface pi) => new(pi, LabelApplyOnlyEquipment); @@ -65,15 +79,27 @@ public partial class GlamourerIpc public static ActionSubscriber ApplyByGuidSubscriber(DalamudPluginInterface pi) => new(pi, LabelApplyByGuid); + public static ActionSubscriber ApplyByGuidOnceSubscriber(DalamudPluginInterface pi) + => new(pi, LabelApplyByGuidOnce); + public static ActionSubscriber ApplyByGuidToCharacterSubscriber(DalamudPluginInterface pi) => new(pi, LabelApplyByGuidToCharacter); + public static ActionSubscriber ApplyByGuidOnceToCharacterSubscriber(DalamudPluginInterface pi) + => new(pi, LabelApplyByGuidOnceToCharacter); + public void ApplyAll(string base64, string characterName) => 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) => 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) => 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) - => 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) - => ApplyDesignByGuid(identifier, FindActors(character), 0); + => ApplyDesignByGuid(identifier, FindActors(character), 0, false); - private void ApplyDesign(DesignBase? design, IEnumerable actors, byte version, uint lockCode) + public void ApplyByGuidOnceToCharacter(Guid identifier, Character? character) + => ApplyDesignByGuid(identifier, FindActors(character), 0, true); + + private void ApplyDesign(DesignBase? design, IEnumerable actors, byte version, uint lockCode, bool once = false) { if (design == null) return; @@ -130,12 +162,13 @@ public partial class GlamourerIpc 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); } } } - private void ApplyDesignByGuid(Guid identifier, IEnumerable actors, uint lockCode) - => ApplyDesign(_designManager.Designs.ByIdentifier(identifier), actors, DesignConverter.Version, lockCode); + private void ApplyDesignByGuid(Guid identifier, IEnumerable actors, uint lockCode, bool once) + => ApplyDesign(_designManager.Designs.ByIdentifier(identifier), actors, DesignConverter.Version, lockCode, once); } diff --git a/Glamourer/Api/GlamourerIpc.Revert.cs b/Glamourer/Api/GlamourerIpc.Revert.cs index 44a03aa..c5ca3b3 100644 --- a/Glamourer/Api/GlamourerIpc.Revert.cs +++ b/Glamourer/Api/GlamourerIpc.Revert.cs @@ -1,6 +1,5 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin; -using Glamourer.Events; using Glamourer.State; using Penumbra.Api.Helpers; using Penumbra.GameData.Actors; @@ -83,7 +82,7 @@ public partial class GlamourerIpc foreach (var id in actors) { if (_stateManager.TryGetValue(id, out var state)) - _stateManager.ResetState(state, StateSource.Ipc, lockCode); + _stateManager.ResetState(state, StateSource.IpcFixed, lockCode); } } diff --git a/Glamourer/Api/GlamourerIpc.Set.cs b/Glamourer/Api/GlamourerIpc.Set.cs index db5941f..93428da 100644 --- a/Glamourer/Api/GlamourerIpc.Set.cs +++ b/Glamourer/Api/GlamourerIpc.Set.cs @@ -20,20 +20,30 @@ public partial class GlamourerIpc ItemInvalid, } - public const string LabelSetItem = "Glamourer.SetItem"; - public const string LabelSetItemByActorName = "Glamourer.SetItemByActorName"; + public const string LabelSetItem = "Glamourer.SetItem"; + public const string LabelSetItemOnce = "Glamourer.SetItemOnce"; + public const string LabelSetItemByActorName = "Glamourer.SetItemByActorName"; + public const string LabelSetItemOnceByActorName = "Glamourer.SetItemOnceByActorName"; private readonly FuncProvider _setItemProvider; + private readonly FuncProvider _setItemOnceProvider; private readonly FuncProvider _setItemByActorNameProvider; + private readonly FuncProvider _setItemOnceByActorNameProvider; public static FuncSubscriber SetItemSubscriber(DalamudPluginInterface pi) => new(pi, LabelSetItem); + public static FuncSubscriber SetItemOnceSubscriber(DalamudPluginInterface pi) + => new(pi, LabelSetItemOnce); + public static FuncSubscriber SetItemByActorNameSubscriber(DalamudPluginInterface pi) => new(pi, LabelSetItemByActorName); - private GlamourerErrorCode SetItem(Character? character, EquipSlot slot, CustomItemId itemId, StainId stainId, uint key) + public static FuncSubscriber 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) itemId = ItemManager.NothingId(slot); @@ -57,11 +67,12 @@ public partial class GlamourerIpc if (!state.ModelData.IsHuman) 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; } - 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) itemId = ItemManager.NothingId(slot); @@ -84,7 +95,8 @@ public partial class GlamourerIpc if (!state.ModelData.IsHuman) 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; } diff --git a/Glamourer/Api/GlamourerIpc.cs b/Glamourer/Api/GlamourerIpc.cs index 36d248b..550b7e2 100644 --- a/Glamourer/Api/GlamourerIpc.cs +++ b/Glamourer/Api/GlamourerIpc.cs @@ -46,9 +46,11 @@ public sealed partial class GlamourerIpc : IDisposable _getAllCustomizationFromCharacterProvider = new FuncProvider(pi, LabelGetAllCustomizationFromCharacter, GetAllCustomizationFromCharacter); - _applyAllProvider = new ActionProvider(pi, LabelApplyAll, ApplyAll); - _applyAllToCharacterProvider = new ActionProvider(pi, LabelApplyAllToCharacter, ApplyAllToCharacter); - _applyOnlyEquipmentProvider = new ActionProvider(pi, LabelApplyOnlyEquipment, ApplyOnlyEquipment); + _applyAllProvider = new ActionProvider(pi, LabelApplyAll, ApplyAll); + _applyAllOnceProvider = new ActionProvider(pi, LabelApplyAll, ApplyAllOnce); + _applyAllToCharacterProvider = new ActionProvider(pi, LabelApplyAllToCharacter, ApplyAllToCharacter); + _applyAllOnceToCharacterProvider = new ActionProvider(pi, LabelApplyAllToCharacter, ApplyAllOnceToCharacter); + _applyOnlyEquipmentProvider = new ActionProvider(pi, LabelApplyOnlyEquipment, ApplyOnlyEquipment); _applyOnlyEquipmentToCharacterProvider = new ActionProvider(pi, LabelApplyOnlyEquipmentToCharacter, ApplyOnlyEquipmentToCharacter); _applyOnlyCustomizationProvider = new ActionProvider(pi, LabelApplyOnlyCustomization, ApplyOnlyCustomization); @@ -66,8 +68,11 @@ public sealed partial class GlamourerIpc : IDisposable _applyOnlyCustomizationToCharacterProviderLock = new ActionProvider(pi, LabelApplyOnlyCustomizationToCharacterLock, ApplyOnlyCustomizationToCharacterLock); - _applyByGuidProvider = new ActionProvider(pi, LabelApplyByGuid, ApplyByGuid); + _applyByGuidProvider = new ActionProvider(pi, LabelApplyByGuid, ApplyByGuid); + _applyByGuidOnceProvider = new ActionProvider(pi, LabelApplyByGuidOnce, ApplyByGuidOnce); _applyByGuidToCharacterProvider = new ActionProvider(pi, LabelApplyByGuidToCharacter, ApplyByGuidToCharacter); + _applyByGuidOnceToCharacterProvider = + new ActionProvider(pi, LabelApplyByGuidOnceToCharacter, ApplyByGuidOnceToCharacter); _revertProvider = new ActionProvider(pi, LabelRevert, Revert); _revertCharacterProvider = new ActionProvider(pi, LabelRevertCharacter, RevertCharacter); @@ -83,9 +88,14 @@ public sealed partial class GlamourerIpc : IDisposable _gPoseChangedProvider = new EventProvider(pi, LabelGPoseChanged); _setItemProvider = new FuncProvider(pi, LabelSetItem, - (idx, slot, item, stain, key) => (int)SetItem(idx, (EquipSlot)slot, item, stain, key)); - _setItemByActorNameProvider = new FuncProvider(pi, LabelSetItemByActorName, - (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, false)); + _setItemOnceProvider = new FuncProvider(pi, LabelSetItem, + (idx, slot, item, stain, key) => (int)SetItem(idx, (EquipSlot)slot, item, stain, key, true)); + + _setItemByActorNameProvider = new FuncProvider(pi, LabelSetItemOnceByActorName, + (name, slot, item, stain, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, stain, key, false)); + _setItemOnceByActorNameProvider = new FuncProvider(pi, LabelSetItemOnceByActorName, + (name, slot, item, stain, key) => (int)SetItemByActorName(name, (EquipSlot)slot, item, stain, key, true)); _stateChangedEvent.Subscribe(OnStateChanged, StateChanged.Priority.GlamourerIpc); _gPose.Subscribe(OnGPoseChanged, GPoseService.Priority.GlamourerIpc); @@ -102,7 +112,9 @@ public sealed partial class GlamourerIpc : IDisposable _getAllCustomizationFromCharacterProvider.Dispose(); _applyAllProvider.Dispose(); + _applyAllOnceProvider.Dispose(); _applyAllToCharacterProvider.Dispose(); + _applyAllOnceToCharacterProvider.Dispose(); _applyOnlyEquipmentProvider.Dispose(); _applyOnlyEquipmentToCharacterProvider.Dispose(); _applyOnlyCustomizationProvider.Dispose(); @@ -113,8 +125,11 @@ public sealed partial class GlamourerIpc : IDisposable _applyOnlyEquipmentToCharacterProviderLock.Dispose(); _applyOnlyCustomizationProviderLock.Dispose(); _applyOnlyCustomizationToCharacterProviderLock.Dispose(); + _applyByGuidProvider.Dispose(); + _applyByGuidOnceProvider.Dispose(); _applyByGuidToCharacterProvider.Dispose(); + _applyByGuidOnceToCharacterProvider.Dispose(); _revertProvider.Dispose(); _revertCharacterProvider.Dispose(); @@ -133,7 +148,9 @@ public sealed partial class GlamourerIpc : IDisposable _getDesignListProvider.Dispose(); _setItemProvider.Dispose(); + _setItemOnceProvider.Dispose(); _setItemByActorNameProvider.Dispose(); + _setItemOnceByActorNameProvider.Dispose(); } private IEnumerable FindActors(string actorName) diff --git a/Glamourer/Configuration.cs b/Glamourer/Configuration.cs index 78d1a3b..29b0646 100644 --- a/Glamourer/Configuration.cs +++ b/Glamourer/Configuration.cs @@ -42,6 +42,7 @@ public class Configuration : IPluginConfiguration, ISavable public bool UseRgbForColors { get; set; } = true; public bool ShowColorConfig { get; set; } = true; public bool ChangeEntireItem { get; set; } = false; + public bool AlwaysApplyAssociatedMods { get; set; } = false; public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY); public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; diff --git a/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs b/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs index 7b3f594..00df06b 100644 --- a/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/ActiveStatePanel.cs @@ -1,7 +1,6 @@ using Dalamud.Interface; using Glamourer.GameData; using Glamourer.Designs; -using Glamourer.Events; using Glamourer.Interop; using Glamourer.Interop.Structs; using Glamourer.State; diff --git a/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs b/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs index 02348a2..d447f9f 100644 --- a/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs +++ b/Glamourer/Gui/Tabs/DebugTab/IpcTesterPanel.cs @@ -28,7 +28,9 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag private string _base64Apply = string.Empty; private string _designIdentifier = string.Empty; private GlamourerIpc.GlamourerErrorCode _setItemEc; + private GlamourerIpc.GlamourerErrorCode _setItemOnceEc; private GlamourerIpc.GlamourerErrorCode _setItemByActorNameEc; + private GlamourerIpc.GlamourerErrorCode _setItemOnceByActorNameEc; public unsafe void Draw() { @@ -77,12 +79,23 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag if (ImGui.Button("Apply##AllName")) 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); ImGui.TableNextColumn(); if (ImGui.Button("Apply##AllCharacter")) GlamourerIpc.ApplyAllToCharacterSubscriber(_pluginInterface) .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); ImGui.TableNextColumn(); 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)) 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); ImGui.TableNextColumn(); if (ImGui.Button("Apply##ByGuidCharacter") && Guid.TryParse(_designIdentifier, out var guid2)) GlamourerIpc.ApplyByGuidToCharacterSubscriber(_pluginInterface) .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); ImGui.TableNextColumn(); @@ -149,6 +173,17 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag 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); ImGui.TableNextColumn(); if (ImGui.Button("Set##SetItemByActorName")) @@ -159,6 +194,17 @@ public class IpcTesterPanel(DalamudPluginInterface _pluginInterface, ObjectManag ImGui.SameLine(); 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() diff --git a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs index 00a23cc..ecbf0e7 100644 --- a/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs +++ b/Glamourer/Gui/Tabs/DesignTab/DesignDetailTab.cs @@ -95,9 +95,13 @@ public class DesignDetailTab Glamourer.Messager.NotificationMessage(ex, $"Could not open file {fileName}.", $"Could not open file {fileName}", 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"); ImGui.TableNextColumn(); @@ -131,9 +135,10 @@ public class DesignDetailTab colorName = _colorCombo.CurrentSelection is DesignColors.AutomaticName ? string.Empty : _colorCombo.CurrentSelection; _manager.ChangeColor(_selector.Selected!, colorName); } + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) _manager.ChangeColor(_selector.Selected!, string.Empty); - + if (_colors.TryGetValue(_selector.Selected!.Color, out var currentColor)) { ImGui.SameLine(); diff --git a/Glamourer/Gui/Tabs/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab.cs index e8435ce..ef75245 100644 --- a/Glamourer/Gui/Tabs/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab.cs @@ -89,6 +89,11 @@ public class SettingsTab( Checkbox("Enable Advanced Customization Options", "Enable the display and editing of advanced customization options like arbitrary colors.", 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(); ImGui.NewLine(); } diff --git a/Glamourer/Interop/Material/PrepareColorSet.cs b/Glamourer/Interop/Material/PrepareColorSet.cs index 7cfbf6c..4230fc1 100644 --- a/Glamourer/Interop/Material/PrepareColorSet.cs +++ b/Glamourer/Interop/Material/PrepareColorSet.cs @@ -131,19 +131,16 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable } else { - switch (source) + switch (source.Base()) { case StateSource.Manual: - case StateSource.Pending: state.Materials.RemoveValue(idx); --i; break; - case StateSource.Ipc: case StateSource.Fixed: idx.DataIndex.SetValue(ref row, model); state.Materials.UpdateValue(idx, new MaterialValueState(newGame, model, source), out _); break; - } } } diff --git a/Glamourer/State/StateApplier.cs b/Glamourer/State/StateApplier.cs index 7222a4b..e9b74fd 100644 --- a/Glamourer/State/StateApplier.cs +++ b/Glamourer/State/StateApplier.cs @@ -118,8 +118,7 @@ public class StateApplier( // If the source is not IPC we do not want to apply restrictions. var data = GetData(state); if (apply) - ChangeArmor(data, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc, - state.ModelData.IsHatVisible()); + ChangeArmor(data, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible()); return data; } @@ -267,7 +266,7 @@ public class StateApplier( actor.Model.ApplyParameterData(flags, values); } - /// + /// public ActorData ChangeParameters(ActorState state, CustomizeParameterFlag flags, bool apply) { var data = GetData(state); @@ -294,10 +293,7 @@ public class StateApplier( { ChangeCustomize(actors, state.ModelData.Customize); foreach (var slot in EquipSlotExtensions.EqdpSlots) - { - ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc, - state.ModelData.IsHatVisible()); - } + ChangeArmor(actors, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible()); var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors; ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand)); diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs index 2b1337e..48b74ab 100644 --- a/Glamourer/State/StateEditor.cs +++ b/Glamourer/State/StateEditor.cs @@ -31,7 +31,7 @@ public class StateEditor( if (!Editor.ChangeModelId(state, modelId, customize, equipData, source, out var old, key)) return; - var actors = Applier.ForceRedraw(state, source is StateSource.Manual or StateSource.Ipc); + var actors = Applier.ForceRedraw(state, source.RequiresChange()); Glamourer.Log.Verbose( $"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]"); StateChanged.Invoke(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)) 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( $"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)); @@ -57,7 +57,7 @@ public class StateEditor( if (!Editor.ChangeHumanCustomize(state, customizeInput, apply, _ => settings.Source, out var old, out var applied, settings.Key)) 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( $"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)); @@ -72,8 +72,8 @@ public class StateEditor( var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon; var actors = type is StateChanged.Type.Equip - ? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc) - : Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc, + ? Applier.ChangeArmor(state, slot, settings.Source.RequiresChange()) + : Applier.ChangeWeapon(state, slot, settings.Source.RequiresChange(), item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType)); if (slot is EquipSlot.MainHand) @@ -105,8 +105,8 @@ public class StateEditor( var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon; var actors = type is StateChanged.Type.Equip - ? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc) - : Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc, + ? Applier.ChangeArmor(state, slot, settings.Source.RequiresChange()) + : Applier.ChangeWeapon(state, slot, settings.Source.RequiresChange(), item!.Value.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType)); 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)) 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( $"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)); @@ -138,7 +138,7 @@ public class StateEditor( if (!Editor.ChangeCrest(state, slot, crest, settings.Source, out var old, settings.Key)) 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( $"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)); @@ -158,7 +158,7 @@ public class StateEditor( return; 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( $"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)); @@ -171,7 +171,7 @@ public class StateEditor( if (!Editor.ChangeMetaState(state, index, value, settings.Source, out var old, settings.Key)) 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( $"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)); @@ -191,7 +191,7 @@ public class StateEditor( { 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), out _, settings.Key); } @@ -201,7 +201,7 @@ public class StateEditor( customizeFlags |= CustomizeFlag.Race; Func 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()); 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()) { - if (settings.RespectManual && state.Sources[parameter] is StateSource.Manual or StateSource.Pending) + if (settings.RespectManual && state.Sources[parameter].IsManual()) continue; - var source = Source(parameter); - if (source is StateSource.Manual) - source = StateSource.Pending; + var source = Source(parameter).SetPending(); 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) { 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), Source(slot.ToState()), out _, settings.Key); 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), Source(slot.ToState(true)), out _, settings.Key); } @@ -241,14 +239,14 @@ public class StateEditor( foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots) { 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), Source(weaponSlot.ToState(true)), out _, settings.Key); if (!mergedDesign.Design.DoApplyEquip(weaponSlot)) continue; - if (settings.RespectManual && state.Sources[weaponSlot, false] is StateSource.Manual) + if (settings.RespectManual && !state.Sources[weaponSlot, false].IsManual()) continue; var currentType = state.ModelData.Item(weaponSlot).Type; @@ -268,12 +266,12 @@ public class StateEditor( 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); } } - var actors = settings.Source is StateSource.Manual or StateSource.Ipc + var actors = settings.Source.RequiresChange() ? Applier.ApplyAll(state, requiresRedraw, false) : ActorData.Invalid; @@ -311,7 +309,7 @@ public class StateEditor( /// Apply offhand item and potentially gauntlets if configured. 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; var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand); diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 1833660..23e0f3c 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -171,7 +171,7 @@ public class StateListener : IDisposable var set = _customizations.Manager.GetSet(model.Clan, model.Gender); foreach (var index in CustomizationExtensions.AllBasic) { - if (state.Sources[index] is not StateSource.Fixed) + if (!state.Sources[index].IsFixed()) { var newValue = customize[index]; var oldValue = model[index]; @@ -214,7 +214,7 @@ public class StateListener : IDisposable && _manager.TryGetValue(identifier, out var state)) { 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); @@ -241,7 +241,7 @@ public class StateListener : IDisposable continue; 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.ChangeStain(state, slot, current.Stain, ApplySettings.Game); @@ -252,7 +252,7 @@ public class StateListener : IDisposable _applier.ChangeWeapon(objects, slot, currentItem, stain); break; 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()); break; } @@ -278,20 +278,19 @@ public class StateListener : IDisposable || !_manager.TryGetValue(identifier, out var state)) return; - ref var actorWeapon = ref weapon; - var baseType = state.BaseData.Item(slot).Type; - var apply = false; - switch (UpdateBaseData(actor, state, slot, actorWeapon)) + var baseType = state.BaseData.Item(slot).Type; + var apply = false; + switch (UpdateBaseData(actor, state, slot, weapon)) { // Do nothing. But this usually can not happen because the hooked function also writes to game objects later. case UpdateState.Transformed: break; case UpdateState.Change: - if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc) + if (!state.Sources[slot, false].IsFixed()) _manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game); else 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); else apply = true; @@ -306,9 +305,9 @@ public class StateListener : IDisposable // Only allow overwriting identical weapons var newWeapon = state.ModelData.Weapon(slot); if (baseType is FullEquipType.Unknown || baseType == state.ModelData.Item(slot).Type || _gPose.InGPose && actor.IsGPoseOrCutscene) - actorWeapon = newWeapon; - else if (actorWeapon.Skeleton.Id != 0) - actorWeapon = actorWeapon.With(newWeapon.Stain); + weapon = newWeapon; + else if (weapon.Skeleton.Id != 0) + weapon = weapon.With(newWeapon.Stain); } // Fist Weapon Offhand hack. @@ -385,12 +384,12 @@ public class StateListener : IDisposable // Update model state if not on fixed design. case UpdateState.Change: 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); else 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); else apply = true; @@ -419,7 +418,7 @@ public class StateListener : IDisposable switch (UpdateBaseCrest(actor, state, slot, value)) { 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); else 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, // 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(); else _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, // 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(); else _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, // 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(); else _manager.ChangeMetaState(state, MetaIndex.WeaponState, value, ApplySettings.Game); @@ -700,8 +699,8 @@ public class StateListener : IDisposable return; var data = new ActorData(gameObject, _creatingIdentifier.ToName()); - _applier.ChangeMetaState(data, MetaIndex.HatState, _creatingState.ModelData.IsHatVisible()); - _applier.ChangeMetaState(data, MetaIndex.Wetness, _creatingState.ModelData.IsWet()); + _applier.ChangeMetaState(data, MetaIndex.HatState, _creatingState.ModelData.IsHatVisible()); + _applier.ChangeMetaState(data, MetaIndex.Wetness, _creatingState.ModelData.IsWet()); _applier.ChangeMetaState(data, MetaIndex.WeaponState, _creatingState.ModelData.IsWeaponVisible()); ApplyParameters(_creatingState, drawObject); @@ -745,12 +744,18 @@ public class StateListener : IDisposable else if (_config.UseAdvancedParameters) model.ApplySingleParameterData(flag, state.ModelData.Parameters); 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: state.BaseData.Parameters.Set(flag, newValue); if (_config.UseAdvancedParameters) model.ApplySingleParameterData(flag, state.ModelData.Parameters); break; - case StateSource.Ipc: + case StateSource.IpcFixed: state.BaseData.Parameters.Set(flag, newValue); model.ApplySingleParameterData(flag, state.ModelData.Parameters); break; diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index c6658c5..e6ed657 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -243,7 +243,7 @@ public sealed class StateManager( state.Materials.Clear(); var actors = ActorData.Invalid; - if (source is StateSource.Manual or StateSource.Ipc) + if (source is not StateSource.Game) actors = Applier.ApplyAll(state, redraw, true); Glamourer.Log.Verbose( @@ -262,7 +262,7 @@ public sealed class StateManager( state.Sources[flag] = StateSource.Game; 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); Glamourer.Log.Verbose( $"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.ModelData.SetHatVisible(state.BaseData.IsHatVisible()); - } - - 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()); + state.Sources[meta] = StateSource.Game; + state.ModelData.SetMeta(meta, state.BaseData.GetMeta(meta)); } } diff --git a/Glamourer/State/StateSource.cs b/Glamourer/State/StateSource.cs index 1d66a46..d489814 100644 --- a/Glamourer/State/StateSource.cs +++ b/Glamourer/State/StateSource.cs @@ -7,12 +7,48 @@ public enum StateSource : byte Game, Manual, Fixed, - Ipc, + IpcFixed, + IpcManual, // Only used for CustomizeParameters. 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 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.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.IpcManual | ((byte)StateSource.Fixed << 4): _data[i] = (byte)((value & 0x0F) | ((byte)StateSource.Manual << 4)); break; case (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.IpcManual << 4) | (byte)StateSource.Fixed: _data[i] = (byte)((value & 0xF0) | (byte)StateSource.Manual); break; }