Reworked all of the meta, made StateIndex its own thing.

This commit is contained in:
Ottermandias 2024-01-23 18:02:53 +01:00
parent 70e4833fb5
commit 1ad70541d3
36 changed files with 747 additions and 657 deletions

View file

@ -3,6 +3,7 @@ using Dalamud.Plugin;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Interop.Structs; using Glamourer.Interop.Structs;
using Glamourer.State;
using Penumbra.Api.Helpers; using Penumbra.Api.Helpers;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
@ -130,7 +131,7 @@ public partial class GlamourerIpc
if ((hasModelId || state.ModelData.ModelId == 0) && state.CanUnlock(lockCode)) if ((hasModelId || state.ModelData.ModelId == 0) && state.CanUnlock(lockCode))
{ {
_stateManager.ApplyDesign(design, state, StateChanged.Source.Ipc, lockCode); _stateManager.ApplyDesign(design, state, StateSource.Ipc, lockCode);
state.Lock(lockCode); state.Lock(lockCode);
} }
} }

View file

@ -15,7 +15,7 @@ public partial class GlamourerIpc
private readonly EventProvider<StateChanged.Type, nint, Lazy<string>> _stateChangedProvider; private readonly EventProvider<StateChanged.Type, nint, Lazy<string>> _stateChangedProvider;
private readonly EventProvider<bool> _gPoseChangedProvider; private readonly EventProvider<bool> _gPoseChangedProvider;
private void OnStateChanged(StateChanged.Type type, StateChanged.Source source, ActorState state, ActorData actors, object? data = null) private void OnStateChanged(StateChanged.Type type, StateSource source, ActorState state, ActorData actors, object? data = null)
{ {
foreach (var actor in actors.Objects) foreach (var actor in actors.Objects)
_stateChangedProvider.Invoke(type, actor.Address, new Lazy<string>(() => _designConverter.ShareBase64(state))); _stateChangedProvider.Invoke(type, actor.Address, new Lazy<string>(() => _designConverter.ShareBase64(state)));

View file

@ -1,6 +1,7 @@
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.State;
using Penumbra.Api.Helpers; using Penumbra.Api.Helpers;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
@ -82,7 +83,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, StateChanged.Source.Ipc, lockCode); _stateManager.ResetState(state, StateSource.Ipc, lockCode);
} }
} }

View file

@ -2,6 +2,7 @@
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Events; using Glamourer.Events;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State;
using Penumbra.Api.Helpers; using Penumbra.Api.Helpers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -55,7 +56,7 @@ public partial class GlamourerIpc
if (!state.ModelData.IsHuman) if (!state.ModelData.IsHuman)
return GlamourerErrorCode.ActorNotHuman; return GlamourerErrorCode.ActorNotHuman;
_stateManager.ChangeEquip(state, slot, item, stainId, StateChanged.Source.Ipc, key); _stateManager.ChangeEquip(state, slot, item, stainId, StateSource.Ipc, key);
return GlamourerErrorCode.Success; return GlamourerErrorCode.Success;
} }
@ -82,7 +83,7 @@ public partial class GlamourerIpc
if (!state.ModelData.IsHuman) if (!state.ModelData.IsHuman)
return GlamourerErrorCode.ActorNotHuman; return GlamourerErrorCode.ActorNotHuman;
_stateManager.ChangeEquip(state, slot, item, stainId, StateChanged.Source.Ipc, key); _stateManager.ChangeEquip(state, slot, item, stainId, StateSource.Ipc, key);
found = true; found = true;
} }

View file

@ -18,9 +18,8 @@ public enum ApplicationType : byte
public static class ApplicationTypeExtensions public static class ApplicationTypeExtensions
{ {
public static (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, bool ApplyHat, bool ApplyVisor, public static (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, MetaFlag Meta) ApplyWhat(
bool this ApplicationType type, DesignBase? design)
ApplyWeapon, bool ApplyWet) ApplyWhat(this ApplicationType type, DesignBase? design)
{ {
var equipFlags = (type.HasFlag(ApplicationType.Weapons) ? WeaponFlags : 0) var equipFlags = (type.HasFlag(ApplicationType.Weapons) ? WeaponFlags : 0)
| (type.HasFlag(ApplicationType.Armor) ? ArmorFlags : 0) | (type.HasFlag(ApplicationType.Armor) ? ArmorFlags : 0)
@ -29,18 +28,15 @@ public static class ApplicationTypeExtensions
var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0; var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0;
var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0; var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0;
var crestFlag = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 0; var crestFlag = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 0;
var metaFlag = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState : 0)
| (type.HasFlag(ApplicationType.Weapons) ? MetaFlag.WeaponState : 0)
| (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0);
if (design == null) if (design == null)
return (equipFlags, customizeFlags, crestFlag, parameterFlags, type.HasFlag(ApplicationType.Armor), return (equipFlags, customizeFlags, crestFlag, parameterFlags, metaFlag);
type.HasFlag(ApplicationType.Armor),
type.HasFlag(ApplicationType.Weapons), type.HasFlag(ApplicationType.Customizations));
return (equipFlags & design!.ApplyEquip, customizeFlags & design.ApplyCustomize, crestFlag & design.ApplyCrest, return (equipFlags & design!.ApplyEquip, customizeFlags & design.ApplyCustomize, crestFlag & design.ApplyCrest,
parameterFlags & design.ApplyParameters, parameterFlags & design.ApplyParameters, metaFlag & design.ApplyMeta);
type.HasFlag(ApplicationType.Armor) && design.DoApplyHatVisible(),
type.HasFlag(ApplicationType.Armor) && design.DoApplyVisorToggle(),
type.HasFlag(ApplicationType.Weapons) && design.DoApplyWeaponVisible(),
type.HasFlag(ApplicationType.Customizations) && design.DoApplyWetness());
} }
public const EquipFlag WeaponFlags = EquipFlag.Mainhand | EquipFlag.Offhand; public const EquipFlag WeaponFlags = EquipFlag.Mainhand | EquipFlag.Offhand;
@ -59,4 +55,4 @@ public static class ApplicationTypeExtensions
| EquipFlag.WristStain | EquipFlag.WristStain
| EquipFlag.RFingerStain | EquipFlag.RFingerStain
| EquipFlag.LFingerStain; | EquipFlag.LFingerStain;
} }

View file

@ -68,7 +68,6 @@ public class AutoDesign
return ret; return ret;
} }
public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, bool ApplyHat, bool ApplyVisor, bool public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, MetaFlag Meta) ApplyWhat()
ApplyWeapon, bool ApplyWet) ApplyWhat()
=> Type.ApplyWhat(Design); => Type.ApplyWhat(Design);
} }

View file

@ -35,7 +35,7 @@ public sealed class AutoDesignApplier : IDisposable
private readonly IClientState _clientState; private readonly IClientState _clientState;
private ActorState? _jobChangeState; private ActorState? _jobChangeState;
private readonly Dictionary<FullEquipType, (EquipItem, StateChanged.Source)> _jobChange = []; private readonly Dictionary<FullEquipType, (EquipItem, StateSource)> _jobChange = [];
private void ResetJobChange() private void ResetJobChange()
{ {
@ -183,7 +183,7 @@ public sealed class AutoDesignApplier : IDisposable
} }
else if (_state.TryGetValue(id, out var state)) else if (_state.TryGetValue(id, out var state))
{ {
state.Source.RemoveFixedDesignSources(); state.Sources.RemoveFixedDesignSources();
} }
} }
} }
@ -197,9 +197,9 @@ public sealed class AutoDesignApplier : IDisposable
{ {
if (id.Type is IdentifierType.Player && id.HomeWorld == WorldId.AnyWorld) if (id.Type is IdentifierType.Player && id.HomeWorld == WorldId.AnyWorld)
foreach (var state in _state.Where(kvp => kvp.Key.PlayerName == id.PlayerName).Select(kvp => kvp.Value)) foreach (var state in _state.Where(kvp => kvp.Key.PlayerName == id.PlayerName).Select(kvp => kvp.Value))
state.Source.RemoveFixedDesignSources(); state.Sources.RemoveFixedDesignSources();
else if (_state.TryGetValue(id, out var state)) else if (_state.TryGetValue(id, out var state))
state.Source.RemoveFixedDesignSources(); state.Sources.RemoveFixedDesignSources();
} }
} }
} }
@ -256,13 +256,13 @@ public sealed class AutoDesignApplier : IDisposable
else if (!GetPlayerSet(identifier, out set!)) else if (!GetPlayerSet(identifier, out set!))
{ {
if (state.UpdateTerritory(_clientState.TerritoryType) && _config.RevertManualChangesOnZoneChange) if (state.UpdateTerritory(_clientState.TerritoryType) && _config.RevertManualChangesOnZoneChange)
_state.ResetState(state, StateChanged.Source.Game); _state.ResetState(state, StateSource.Game);
return true; return true;
} }
var respectManual = !state.UpdateTerritory(_clientState.TerritoryType) || !_config.RevertManualChangesOnZoneChange; var respectManual = !state.UpdateTerritory(_clientState.TerritoryType) || !_config.RevertManualChangesOnZoneChange;
if (!respectManual) if (!respectManual)
_state.ResetState(state, StateChanged.Source.Game); _state.ResetState(state, StateSource.Game);
Reduce(actor, state, set, respectManual, false); Reduce(actor, state, set, respectManual, false);
return true; return true;
} }
@ -272,13 +272,13 @@ public sealed class AutoDesignApplier : IDisposable
if (set.BaseState == AutoDesignSet.Base.Game) if (set.BaseState == AutoDesignSet.Base.Game)
_state.ResetStateFixed(state, respectManual); _state.ResetStateFixed(state, respectManual);
else if (!respectManual) else if (!respectManual)
state.Source.RemoveFixedDesignSources(); state.Sources.RemoveFixedDesignSources();
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId)) if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
return; return;
var mergedDesign = _designMerger.Merge(set.Designs.Where(d => d.IsActive(actor)).Select(d => ((DesignBase?) d.Design, d.Type)), state.ModelData, true); var mergedDesign = _designMerger.Merge(set.Designs.Where(d => d.IsActive(actor)).Select(d => ((DesignBase?) d.Design, d.Type)), state.ModelData, true, false);
ApplyToState(state, mergedDesign, respectManual, fromJobChange, StateChanged.Source.Fixed); ApplyToState(state, mergedDesign, respectManual, fromJobChange, StateSource.Fixed);
} }
/// <summary> Get world-specific first and all-world afterward. </summary> /// <summary> Get world-specific first and all-world afterward. </summary>
@ -304,27 +304,27 @@ public sealed class AutoDesignApplier : IDisposable
} }
} }
private void ApplyToState(ActorState state, MergedDesign mergedDesign, bool respectManual, bool fromJobChange, StateChanged.Source source) private void ApplyToState(ActorState state, MergedDesign mergedDesign, bool respectManual, bool fromJobChange, StateSource source)
{ {
foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest)) foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest))
if (!respectManual || state.Source[slot] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[slot] is not StateSource.Manual)
_state.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), mergedDesign.GetSource(slot, source)); _state.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), mergedDesign.GetSource(slot, source));
foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate()) foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
if (!respectManual || state.Source[parameter] is not StateChanged.Source.Manual and not StateChanged.Source.Pending) if (!respectManual || state.Sources[parameter] is not StateSource.Manual and not StateSource.Pending)
_state.ChangeCustomizeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], mergedDesign.GetSource(parameter, source)); _state.ChangeCustomizeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], mergedDesign.GetSource(parameter, source));
foreach (var slot in EquipSlotExtensions.EqdpSlots) foreach (var slot in EquipSlotExtensions.EqdpSlots)
{ {
if (mergedDesign.Design.DoApplyEquip(slot)) if (mergedDesign.Design.DoApplyEquip(slot))
{ {
if (!respectManual || state.Source[slot, false] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[slot, false] is not StateSource.Manual)
_state.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot), mergedDesign.GetSource(slot, false, source)); _state.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot), mergedDesign.GetSource(slot, false, source));
} }
if (mergedDesign.Design.DoApplyStain(slot)) if (mergedDesign.Design.DoApplyStain(slot))
{ {
if (!respectManual || state.Source[slot, true] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[slot, true] is not StateSource.Manual)
_state.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot), mergedDesign.GetSource(slot, true, source)); _state.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot), mergedDesign.GetSource(slot, true, source));
} }
} }
@ -333,14 +333,14 @@ public sealed class AutoDesignApplier : IDisposable
{ {
if (mergedDesign.Design.DoApplyStain(weaponSlot)) if (mergedDesign.Design.DoApplyStain(weaponSlot))
{ {
if (!respectManual || state.Source[weaponSlot, true] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[weaponSlot, true] is not StateSource.Manual)
_state.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot), mergedDesign.GetSource(weaponSlot, true, source)); _state.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot), mergedDesign.GetSource(weaponSlot, true, source));
} }
if (!mergedDesign.Design.DoApplyEquip(weaponSlot)) if (!mergedDesign.Design.DoApplyEquip(weaponSlot))
continue; continue;
if (respectManual && state.Source[weaponSlot, false] is StateChanged.Source.Manual) if (respectManual && state.Sources[weaponSlot, false] is StateSource.Manual)
continue; continue;
var currentType = state.ModelData.Item(weaponSlot).Type; var currentType = state.ModelData.Item(weaponSlot).Type;
@ -359,7 +359,7 @@ public sealed class AutoDesignApplier : IDisposable
} }
private void ReduceEquip(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags, bool respectManual, private void ReduceEquip(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags, bool respectManual,
StateChanged.Source source, bool fromJobChange) StateSource source, bool fromJobChange)
{ {
equipFlags &= ~totalEquipFlags; equipFlags &= ~totalEquipFlags;
if (equipFlags == 0) if (equipFlags == 0)
@ -373,7 +373,7 @@ public sealed class AutoDesignApplier : IDisposable
var item = design.Item(slot); var item = design.Item(slot);
if (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _)) if (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _))
{ {
if (!respectManual || state.Source[slot, false] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[slot, false] is not StateSource.Manual)
_state.ChangeItem(state, slot, item, source); _state.ChangeItem(state, slot, item, source);
totalEquipFlags |= flag; totalEquipFlags |= flag;
} }
@ -382,7 +382,7 @@ public sealed class AutoDesignApplier : IDisposable
var stainFlag = slot.ToStainFlag(); var stainFlag = slot.ToStainFlag();
if (equipFlags.HasFlag(stainFlag)) if (equipFlags.HasFlag(stainFlag))
{ {
if (!respectManual || state.Source[slot, true] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[slot, true] is not StateSource.Manual)
_state.ChangeStain(state, slot, design.Stain(slot), source); _state.ChangeStain(state, slot, design.Stain(slot), source);
totalEquipFlags |= stainFlag; totalEquipFlags |= stainFlag;
} }
@ -392,7 +392,7 @@ public sealed class AutoDesignApplier : IDisposable
{ {
var item = design.Item(EquipSlot.MainHand); var item = design.Item(EquipSlot.MainHand);
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _); var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
var checkState = !respectManual || state.Source[EquipSlot.MainHand, false] is not StateChanged.Source.Manual; var checkState = !respectManual || state.Sources[EquipSlot.MainHand, false] is not StateSource.Manual;
if (checkUnlock && checkState) if (checkUnlock && checkState)
{ {
if (fromJobChange) if (fromJobChange)
@ -412,7 +412,7 @@ public sealed class AutoDesignApplier : IDisposable
{ {
var item = design.Item(EquipSlot.OffHand); var item = design.Item(EquipSlot.OffHand);
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _); var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
var checkState = !respectManual || state.Source[EquipSlot.OffHand, false] is not StateChanged.Source.Manual; var checkState = !respectManual || state.Sources[EquipSlot.OffHand, false] is not StateSource.Manual;
if (checkUnlock && checkState) if (checkUnlock && checkState)
{ {
if (fromJobChange) if (fromJobChange)
@ -430,21 +430,21 @@ public sealed class AutoDesignApplier : IDisposable
if (equipFlags.HasFlag(EquipFlag.MainhandStain)) if (equipFlags.HasFlag(EquipFlag.MainhandStain))
{ {
if (!respectManual || state.Source[EquipSlot.MainHand, true] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[EquipSlot.MainHand, true] is not StateSource.Manual)
_state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), source); _state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), source);
totalEquipFlags |= EquipFlag.MainhandStain; totalEquipFlags |= EquipFlag.MainhandStain;
} }
if (equipFlags.HasFlag(EquipFlag.OffhandStain)) if (equipFlags.HasFlag(EquipFlag.OffhandStain))
{ {
if (!respectManual || state.Source[EquipSlot.OffHand, true] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[EquipSlot.OffHand, true] is not StateSource.Manual)
_state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), source); _state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), source);
totalEquipFlags |= EquipFlag.OffhandStain; totalEquipFlags |= EquipFlag.OffhandStain;
} }
} }
private void ReduceCustomize(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags, private void ReduceCustomize(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags,
bool respectManual, StateChanged.Source source) bool respectManual, StateSource source)
{ {
customizeFlags &= ~totalCustomizeFlags; customizeFlags &= ~totalCustomizeFlags;
if (customizeFlags == 0) if (customizeFlags == 0)
@ -459,7 +459,7 @@ public sealed class AutoDesignApplier : IDisposable
if (customizeFlags.HasFlag(CustomizeFlag.Clan)) if (customizeFlags.HasFlag(CustomizeFlag.Clan))
{ {
if (!respectManual || state.Source[CustomizeIndex.Clan] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[CustomizeIndex.Clan] is not StateSource.Manual)
fixFlags |= _customizations.ChangeClan(ref customize, design.Customize.Clan); fixFlags |= _customizations.ChangeClan(ref customize, design.Customize.Clan);
customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race); customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race);
totalCustomizeFlags |= CustomizeFlag.Clan | CustomizeFlag.Race; totalCustomizeFlags |= CustomizeFlag.Clan | CustomizeFlag.Race;
@ -467,7 +467,7 @@ public sealed class AutoDesignApplier : IDisposable
if (customizeFlags.HasFlag(CustomizeFlag.Gender)) if (customizeFlags.HasFlag(CustomizeFlag.Gender))
{ {
if (!respectManual || state.Source[CustomizeIndex.Gender] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[CustomizeIndex.Gender] is not StateSource.Manual)
fixFlags |= _customizations.ChangeGender(ref customize, design.Customize.Gender); fixFlags |= _customizations.ChangeGender(ref customize, design.Customize.Gender);
customizeFlags &= ~CustomizeFlag.Gender; customizeFlags &= ~CustomizeFlag.Gender;
totalCustomizeFlags |= CustomizeFlag.Gender; totalCustomizeFlags |= CustomizeFlag.Gender;
@ -478,7 +478,7 @@ public sealed class AutoDesignApplier : IDisposable
if (customizeFlags.HasFlag(CustomizeFlag.Face)) if (customizeFlags.HasFlag(CustomizeFlag.Face))
{ {
if (!respectManual || state.Source[CustomizeIndex.Face] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[CustomizeIndex.Face] is not StateSource.Manual)
_state.ChangeCustomize(state, CustomizeIndex.Face, design.Customize.Face, source); _state.ChangeCustomize(state, CustomizeIndex.Face, design.Customize.Face, source);
customizeFlags &= ~CustomizeFlag.Face; customizeFlags &= ~CustomizeFlag.Face;
totalCustomizeFlags |= CustomizeFlag.Face; totalCustomizeFlags |= CustomizeFlag.Face;
@ -498,42 +498,21 @@ public sealed class AutoDesignApplier : IDisposable
if (data.HasValue && _config.UnlockedItemMode && !_customizeUnlocks.IsUnlocked(data.Value, out _)) if (data.HasValue && _config.UnlockedItemMode && !_customizeUnlocks.IsUnlocked(data.Value, out _))
continue; continue;
if (!respectManual || state.Source[index] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[index] is not StateSource.Manual)
_state.ChangeCustomize(state, index, value, source); _state.ChangeCustomize(state, index, value, source);
totalCustomizeFlags |= flag; totalCustomizeFlags |= flag;
} }
} }
} }
private void ReduceMeta(ActorState state, in DesignData design, bool applyHat, bool applyVisor, bool applyWeapon, bool applyWet, private void ReduceMeta(ActorState state, in DesignData design, MetaFlag apply, ref MetaFlag totalMetaFlags, bool respectManual, StateSource source)
ref byte totalMetaFlags, bool respectManual, StateChanged.Source source)
{ {
if (applyHat && (totalMetaFlags & 0x01) == 0) apply &= ~totalMetaFlags;
foreach (var index in MetaExtensions.AllRelevant)
{ {
if (!respectManual || state.Source[MetaIndex.HatState] is not StateChanged.Source.Manual) if (!respectManual || state.Sources[index] is not StateSource.Manual)
_state.ChangeHatState(state, design.IsHatVisible(), source); _state.ChangeMeta(state, index, design.GetMeta(index), source);
totalMetaFlags |= 0x01; totalMetaFlags |= index.ToFlag();
}
if (applyVisor && (totalMetaFlags & 0x02) == 0)
{
if (!respectManual || state.Source[MetaIndex.VisorState] is not StateChanged.Source.Manual)
_state.ChangeVisorState(state, design.IsVisorToggled(), source);
totalMetaFlags |= 0x02;
}
if (applyWeapon && (totalMetaFlags & 0x04) == 0)
{
if (!respectManual || state.Source[MetaIndex.WeaponState] is not StateChanged.Source.Manual)
_state.ChangeWeaponState(state, design.IsWeaponVisible(), source);
totalMetaFlags |= 0x04;
}
if (applyWet && (totalMetaFlags & 0x08) == 0)
{
if (!respectManual || state.Source[MetaIndex.Wetness] is not StateChanged.Source.Manual)
_state.ChangeWetness(state, design.IsWet(), source);
totalMetaFlags |= 0x08;
} }
} }

View file

@ -163,16 +163,15 @@ public class DesignBase64Migration
} }
} }
public static unsafe string CreateOldBase64(in DesignData save, EquipFlag equipFlags, CustomizeFlag customizeFlags, public static unsafe string CreateOldBase64(in DesignData save, EquipFlag equipFlags, CustomizeFlag customizeFlags, MetaFlag meta, bool writeProtected, float alpha = 1.0f)
bool setHat, bool setVisor, bool setWeapon, bool writeProtected, float alpha = 1.0f)
{ {
var data = stackalloc byte[Base64SizeV4]; var data = stackalloc byte[Base64SizeV4];
data[0] = 5; data[0] = 5;
data[1] = (byte)((customizeFlags == CustomizeFlagExtensions.All ? 0x01 : 0) data[1] = (byte)((customizeFlags == CustomizeFlagExtensions.All ? 0x01 : 0)
| (save.IsWet() ? 0x02 : 0) | (save.IsWet() ? 0x02 : 0)
| (setHat ? 0x04 : 0) | (meta.HasFlag(MetaFlag.HatState) ? 0x04 : 0)
| (setWeapon ? 0x08 : 0) | (meta.HasFlag(MetaFlag.WeaponState) ? 0x08 : 0)
| (setVisor ? 0x10 : 0) | (meta.HasFlag(MetaFlag.VisorState) ? 0x10 : 0)
| (writeProtected ? 0x20 : 0)); | (writeProtected ? 0x20 : 0));
data[2] = (byte)((equipFlags.HasFlag(EquipFlag.Mainhand) ? 0x01 : 0) data[2] = (byte)((equipFlags.HasFlag(EquipFlag.Mainhand) ? 0x01 : 0)
| (equipFlags.HasFlag(EquipFlag.Offhand) ? 0x02 : 0) | (equipFlags.HasFlag(EquipFlag.Offhand) ? 0x02 : 0)

View file

@ -55,10 +55,10 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
design.ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant; design.ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
design.ApplyCrest = crestFlags & CrestExtensions.All; design.ApplyCrest = crestFlags & CrestExtensions.All;
design.ApplyParameters = parameterFlags & CustomizeParameterExtensions.All; design.ApplyParameters = parameterFlags & CustomizeParameterExtensions.All;
design.SetApplyHatVisible(design.DoApplyEquip(EquipSlot.Head)); design.SetApplyMeta(MetaIndex.HatState, design.DoApplyEquip(EquipSlot.Head));
design.SetApplyVisorToggle(design.DoApplyEquip(EquipSlot.Head)); design.SetApplyMeta(MetaIndex.VisorState, design.DoApplyEquip(EquipSlot.Head));
design.SetApplyWeaponVisible(design.DoApplyEquip(EquipSlot.MainHand) || design.DoApplyEquip(EquipSlot.OffHand)); design.SetApplyMeta(MetaIndex.WeaponState, design.DoApplyEquip(EquipSlot.MainHand) || design.DoApplyEquip(EquipSlot.OffHand));
design.SetApplyWetness(true); design.SetApplyMeta(MetaIndex.Wetness, true);
design.SetDesignData(_customize, data); design.SetDesignData(_customize, data);
return design; return design;
} }
@ -126,16 +126,14 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
return null; return null;
} }
ret.SetApplyWetness(customize); ret.SetApplyMeta(MetaIndex.Wetness, customize);
ret.ApplyCustomize = customize ? CustomizeFlagExtensions.AllRelevant : 0; ret.ApplyCustomize = customize ? CustomizeFlagExtensions.AllRelevant : 0;
if (!equip) if (!equip)
{ {
ret.ApplyEquip = 0; ret.ApplyEquip = 0;
ret.ApplyCrest = 0; ret.ApplyCrest = 0;
ret.SetApplyHatVisible(false); ret.ApplyMeta &= ~(MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState);
ret.SetApplyWeaponVisible(false);
ret.SetApplyVisorToggle(false);
} }
return ret; return ret;

View file

@ -523,15 +523,7 @@ public class DesignManager
/// <summary> Change the application value of one of the meta flags. </summary> /// <summary> Change the application value of one of the meta flags. </summary>
public void ChangeApplyMeta(Design design, MetaIndex metaIndex, bool value) public void ChangeApplyMeta(Design design, MetaIndex metaIndex, bool value)
{ {
var change = metaIndex switch if (!design.SetApplyMeta(metaIndex, value))
{
MetaIndex.Wetness => design.SetApplyWetness(value),
MetaIndex.HatState => design.SetApplyHatVisible(value),
MetaIndex.VisorState => design.SetApplyVisorToggle(value),
MetaIndex.WeaponState => design.SetApplyWeaponVisible(value),
_ => throw new ArgumentOutOfRangeException(nameof(metaIndex), metaIndex, null),
};
if (!change)
return; return;
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
@ -556,14 +548,8 @@ public class DesignManager
public void ApplyDesign(Design design, DesignBase other) public void ApplyDesign(Design design, DesignBase other)
{ {
_undoStore[design.Identifier] = design.DesignData; _undoStore[design.Identifier] = design.DesignData;
if (other.DoApplyWetness()) foreach (var index in MetaExtensions.AllRelevant.Where(other.DoApplyMeta))
design.GetDesignDataRef().SetIsWet(other.DesignData.IsWet()); design.GetDesignDataRef().SetMeta(index, other.DesignData.GetMeta(index));
if (other.DoApplyHatVisible())
design.GetDesignDataRef().SetHatVisible(other.DesignData.IsHatVisible());
if (other.DoApplyVisorToggle())
design.GetDesignDataRef().SetVisor(other.DesignData.IsVisorToggled());
if (other.DoApplyWeaponVisible())
design.GetDesignDataRef().SetWeaponVisible(other.DesignData.IsWeaponVisible());
if (design.DesignData.IsHuman) if (design.DesignData.IsHuman)
{ {

View file

@ -1,5 +1,4 @@
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Events;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
@ -28,13 +27,13 @@ public class DesignMerger(
continue; continue;
ref readonly var data = ref design == null ? ref baseRef : ref design.GetDesignDataRef(); ref readonly var data = ref design == null ? ref baseRef : ref design.GetDesignDataRef();
var source = design == null ? StateChanged.Source.Game : StateChanged.Source.Manual; var source = design == null ? StateSource.Game : StateSource.Manual;
if (!data.IsHuman) if (!data.IsHuman)
continue; continue;
var (equipFlags, customizeFlags, crestFlags, parameterFlags, applyHat, applyVisor, applyWeapon, applyWet) = type.ApplyWhat(design); var (equipFlags, customizeFlags, crestFlags, parameterFlags, applyMeta) = type.ApplyWhat(design);
ReduceMeta(data, applyHat, applyVisor, applyWeapon, applyWet, ret, source); ReduceMeta(data, applyMeta, ret, source);
ReduceCustomize(data, customizeFlags, ref fixFlags, ret, source, respectOwnership); ReduceCustomize(data, customizeFlags, ref fixFlags, ret, source, respectOwnership);
ReduceEquip(data, equipFlags, ret, source, respectOwnership); ReduceEquip(data, equipFlags, ret, source, respectOwnership);
ReduceMainhands(data, equipFlags, ret, source, respectOwnership); ReduceMainhands(data, equipFlags, ret, source, respectOwnership);
@ -58,39 +57,22 @@ public class DesignMerger(
ret.AssociatedMods.TryAdd(mod, settings); ret.AssociatedMods.TryAdd(mod, settings);
} }
private static void ReduceMeta(in DesignData design, bool applyHat, bool applyVisor, bool applyWeapon, bool applyWet, MergedDesign ret, private static void ReduceMeta(in DesignData design, MetaFlag applyMeta, MergedDesign ret, StateSource source)
StateChanged.Source source)
{ {
if (applyHat && !ret.Design.DoApplyHatVisible()) applyMeta &= ~ret.Design.ApplyMeta;
{
ret.Design.SetApplyHatVisible(true);
ret.Design.GetDesignDataRef().SetHatVisible(design.IsHatVisible());
ret.Source[MetaIndex.HatState] = source;
}
if (applyVisor && !ret.Design.DoApplyVisorToggle()) foreach (var index in MetaExtensions.AllRelevant)
{ {
ret.Design.SetApplyVisorToggle(true); if (!applyMeta.HasFlag(index.ToFlag()))
ret.Design.GetDesignDataRef().SetVisor(design.IsVisorToggled()); continue;
ret.Source[MetaIndex.VisorState] = source;
}
if (applyWeapon && !ret.Design.DoApplyWeaponVisible()) ret.Design.SetApplyMeta(index, true);
{ ret.Design.GetDesignDataRef().SetMeta(index, design.GetMeta(index));
ret.Design.SetApplyWeaponVisible(true); ret.Sources[index] = source;
ret.Design.GetDesignDataRef().SetWeaponVisible(design.IsWeaponVisible());
ret.Source[MetaIndex.WeaponState] = source;
}
if (applyWet && !ret.Design.DoApplyWetness())
{
ret.Design.SetApplyWetness(true);
ret.Design.GetDesignDataRef().SetIsWet(design.IsWet());
ret.Source[MetaIndex.Wetness] = source;
} }
} }
private static void ReduceCrests(in DesignData design, CrestFlag crestFlags, MergedDesign ret, StateChanged.Source source) private static void ReduceCrests(in DesignData design, CrestFlag crestFlags, MergedDesign ret, StateSource source)
{ {
crestFlags &= ~ret.Design.ApplyCrest; crestFlags &= ~ret.Design.ApplyCrest;
if (crestFlags == 0) if (crestFlags == 0)
@ -103,12 +85,12 @@ public class DesignMerger(
ret.Design.GetDesignDataRef().SetCrest(slot, design.Crest(slot)); ret.Design.GetDesignDataRef().SetCrest(slot, design.Crest(slot));
ret.Design.SetApplyCrest(slot, true); ret.Design.SetApplyCrest(slot, true);
ret.Source[slot] = source; ret.Sources[slot] = source;
} }
} }
private static void ReduceParameters(in DesignData design, CustomizeParameterFlag parameterFlags, MergedDesign ret, private static void ReduceParameters(in DesignData design, CustomizeParameterFlag parameterFlags, MergedDesign ret,
StateChanged.Source source) StateSource source)
{ {
parameterFlags &= ~ret.Design.ApplyParameters; parameterFlags &= ~ret.Design.ApplyParameters;
if (parameterFlags == 0) if (parameterFlags == 0)
@ -121,11 +103,11 @@ public class DesignMerger(
ret.Design.GetDesignDataRef().Parameters.Set(flag, design.Parameters[flag]); ret.Design.GetDesignDataRef().Parameters.Set(flag, design.Parameters[flag]);
ret.Design.SetApplyParameter(flag, true); ret.Design.SetApplyParameter(flag, true);
ret.Source[flag] = source; ret.Sources[flag] = source;
} }
} }
private void ReduceEquip(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source, private void ReduceEquip(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateSource source,
bool respectOwnership) bool respectOwnership)
{ {
equipFlags &= ~ret.Design.ApplyEquip; equipFlags &= ~ret.Design.ApplyEquip;
@ -142,7 +124,7 @@ public class DesignMerger(
if (!respectOwnership || _itemUnlocks.IsUnlocked(item.Id, out _)) if (!respectOwnership || _itemUnlocks.IsUnlocked(item.Id, out _))
ret.Design.GetDesignDataRef().SetItem(slot, item); ret.Design.GetDesignDataRef().SetItem(slot, item);
ret.Design.SetApplyEquip(slot, true); ret.Design.SetApplyEquip(slot, true);
ret.Source[slot, false] = source; ret.Sources[slot, false] = source;
} }
var stainFlag = slot.ToStainFlag(); var stainFlag = slot.ToStainFlag();
@ -150,7 +132,7 @@ public class DesignMerger(
{ {
ret.Design.GetDesignDataRef().SetStain(slot, design.Stain(slot)); ret.Design.GetDesignDataRef().SetStain(slot, design.Stain(slot));
ret.Design.SetApplyStain(slot, true); ret.Design.SetApplyStain(slot, true);
ret.Source[slot, true] = source; ret.Sources[slot, true] = source;
} }
} }
@ -161,12 +143,12 @@ public class DesignMerger(
{ {
ret.Design.GetDesignDataRef().SetStain(slot, design.Stain(slot)); ret.Design.GetDesignDataRef().SetStain(slot, design.Stain(slot));
ret.Design.SetApplyStain(slot, true); ret.Design.SetApplyStain(slot, true);
ret.Source[slot, true] = source; ret.Sources[slot, true] = source;
} }
} }
} }
private void ReduceMainhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source, private void ReduceMainhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateSource source,
bool respectOwnership) bool respectOwnership)
{ {
if (!equipFlags.HasFlag(EquipFlag.Mainhand)) if (!equipFlags.HasFlag(EquipFlag.Mainhand))
@ -185,7 +167,7 @@ public class DesignMerger(
ret.Weapons.TryAdd(weapon.Type, (weapon, source)); ret.Weapons.TryAdd(weapon.Type, (weapon, source));
} }
private void ReduceOffhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source, bool respectOwnership) private void ReduceOffhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateSource source, bool respectOwnership)
{ {
if (!equipFlags.HasFlag(EquipFlag.Offhand)) if (!equipFlags.HasFlag(EquipFlag.Offhand))
return; return;
@ -205,7 +187,7 @@ public class DesignMerger(
} }
private void ReduceCustomize(in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag fixFlags, MergedDesign ret, private void ReduceCustomize(in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag fixFlags, MergedDesign ret,
StateChanged.Source source, bool respectOwnership) StateSource source, bool respectOwnership)
{ {
customizeFlags &= ~ret.Design.ApplyCustomizeRaw; customizeFlags &= ~ret.Design.ApplyCustomizeRaw;
if (customizeFlags == 0) if (customizeFlags == 0)
@ -221,25 +203,25 @@ public class DesignMerger(
fixFlags |= _customize.ChangeClan(ref customize, design.Customize.Clan); fixFlags |= _customize.ChangeClan(ref customize, design.Customize.Clan);
ret.Design.SetApplyCustomize(CustomizeIndex.Clan, true); ret.Design.SetApplyCustomize(CustomizeIndex.Clan, true);
ret.Design.SetApplyCustomize(CustomizeIndex.Race, true); ret.Design.SetApplyCustomize(CustomizeIndex.Race, true);
customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race); customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race);
ret.Source[CustomizeIndex.Clan] = source; ret.Sources[CustomizeIndex.Clan] = source;
ret.Source[CustomizeIndex.Race] = source; ret.Sources[CustomizeIndex.Race] = source;
} }
if (customizeFlags.HasFlag(CustomizeFlag.Gender)) if (customizeFlags.HasFlag(CustomizeFlag.Gender))
{ {
fixFlags |= _customize.ChangeGender(ref customize, design.Customize.Gender); fixFlags |= _customize.ChangeGender(ref customize, design.Customize.Gender);
ret.Design.SetApplyCustomize(CustomizeIndex.Gender, true); ret.Design.SetApplyCustomize(CustomizeIndex.Gender, true);
customizeFlags &= ~CustomizeFlag.Gender; customizeFlags &= ~CustomizeFlag.Gender;
ret.Source[CustomizeIndex.Gender] = source; ret.Sources[CustomizeIndex.Gender] = source;
} }
if (customizeFlags.HasFlag(CustomizeFlag.Face)) if (customizeFlags.HasFlag(CustomizeFlag.Face))
{ {
customize[CustomizeIndex.Face] = design.Customize.Face; customize[CustomizeIndex.Face] = design.Customize.Face;
ret.Design.SetApplyCustomize(CustomizeIndex.Face, true); ret.Design.SetApplyCustomize(CustomizeIndex.Face, true);
customizeFlags &= ~CustomizeFlag.Face; customizeFlags &= ~CustomizeFlag.Face;
ret.Source[CustomizeIndex.Face] = source; ret.Sources[CustomizeIndex.Face] = source;
} }
var set = ret.Design.CustomizeSet; var set = ret.Design.CustomizeSet;
@ -259,8 +241,8 @@ public class DesignMerger(
customize[index] = data?.Value ?? value; customize[index] = data?.Value ?? value;
ret.Design.SetApplyCustomize(index, true); ret.Design.SetApplyCustomize(index, true);
ret.Source[index] = source; ret.Sources[index] = source;
fixFlags &= ~flag; fixFlags &= ~flag;
} }
ret.Design.SetCustomize(_customize, customize); ret.Design.SetCustomize(_customize, customize);
@ -272,15 +254,15 @@ public class DesignMerger(
return; return;
var source = ret.Design.DoApplyCustomize(CustomizeIndex.Clan) var source = ret.Design.DoApplyCustomize(CustomizeIndex.Clan)
? ret.Source[CustomizeIndex.Clan] ? ret.Sources[CustomizeIndex.Clan]
: ret.Source[CustomizeIndex.Gender]; : ret.Sources[CustomizeIndex.Gender];
foreach (var index in Enum.GetValues<CustomizeIndex>()) foreach (var index in Enum.GetValues<CustomizeIndex>())
{ {
var flag = index.ToFlag(); var flag = index.ToFlag();
if (!fixFlags.HasFlag(flag)) if (!fixFlags.HasFlag(flag))
continue; continue;
ret.Source[index] = source; ret.Sources[index] = source;
ret.Design.SetApplyCustomize(index, true); ret.Design.SetApplyCustomize(index, true);
} }
} }

View file

@ -26,14 +26,14 @@ public sealed class MergedDesign
{ {
var weapon = design.DesignData.Item(EquipSlot.MainHand); var weapon = design.DesignData.Item(EquipSlot.MainHand);
if (weapon.Valid) if (weapon.Valid)
Weapons.TryAdd(weapon.Type, (weapon, StateChanged.Source.Manual)); Weapons.TryAdd(weapon.Type, (weapon, StateSource.Manual));
} }
if (design.DoApplyEquip(EquipSlot.OffHand)) if (design.DoApplyEquip(EquipSlot.OffHand))
{ {
var weapon = design.DesignData.Item(EquipSlot.OffHand); var weapon = design.DesignData.Item(EquipSlot.OffHand);
if (weapon.Valid) if (weapon.Valid)
Weapons.TryAdd(weapon.Type, (weapon, StateChanged.Source.Manual)); Weapons.TryAdd(weapon.Type, (weapon, StateSource.Manual));
} }
} }
@ -44,26 +44,26 @@ public sealed class MergedDesign
AssociatedMods[mod] = settings; AssociatedMods[mod] = settings;
} }
public readonly DesignBase Design; public readonly DesignBase Design;
public readonly Dictionary<FullEquipType, (EquipItem, StateChanged.Source)> Weapons = new(4); public readonly Dictionary<FullEquipType, (EquipItem, StateSource)> Weapons = new(4);
public readonly StateSource Source = new(); public readonly SortedList<Mod, ModSettings> AssociatedMods = [];
public readonly SortedList<Mod, ModSettings> AssociatedMods = []; public StateSources Sources = new();
public StateChanged.Source GetSource(EquipSlot slot, bool stain, StateChanged.Source actualSource) public StateSource GetSource(EquipSlot slot, bool stain, StateSource actualSource)
=> GetSource(Source[slot, stain], actualSource); => GetSource(Sources[slot, stain], actualSource);
public StateChanged.Source GetSource(CrestFlag slot, StateChanged.Source actualSource) public StateSource GetSource(CrestFlag slot, StateSource actualSource)
=> GetSource(Source[slot], actualSource); => GetSource(Sources[slot], actualSource);
public StateChanged.Source GetSource(CustomizeIndex type, StateChanged.Source actualSource) public StateSource GetSource(CustomizeIndex type, StateSource actualSource)
=> GetSource(Source[type], actualSource); => GetSource(Sources[type], actualSource);
public StateChanged.Source GetSource(MetaIndex index, StateChanged.Source actualSource) public StateSource GetSource(MetaIndex index, StateSource actualSource)
=> GetSource(Source[index], actualSource); => GetSource(Sources[index], actualSource);
public StateChanged.Source GetSource(CustomizeParameterFlag flag, StateChanged.Source actualSource) public StateSource GetSource(CustomizeParameterFlag flag, StateSource actualSource)
=> GetSource(Source[flag], actualSource); => GetSource(Sources[flag], actualSource);
public static StateChanged.Source GetSource(StateChanged.Source given, StateChanged.Source actualSource) public static StateSource GetSource(StateSource given, StateSource actualSource)
=> given is StateChanged.Source.Game ? StateChanged.Source.Game : actualSource; => given is StateSource.Game ? StateSource.Game : actualSource;
} }

View file

@ -1,14 +1,14 @@
using Penumbra.GameData.Enums; using Glamourer.State;
namespace Glamourer.State; namespace Glamourer.Designs;
public enum MetaIndex public enum MetaIndex
{ {
Wetness = EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices, Wetness = StateIndex.MetaWetness,
HatState, HatState = StateIndex.MetaHatState,
VisorState, VisorState = StateIndex.MetaVisorState,
WeaponState, WeaponState = StateIndex.MetaWeaponState,
ModelId, ModelId = StateIndex.MetaModelId,
} }
[Flags] [Flags]
@ -34,6 +34,26 @@ public static class MetaExtensions
MetaIndex.HatState => MetaFlag.HatState, MetaIndex.HatState => MetaFlag.HatState,
MetaIndex.VisorState => MetaFlag.VisorState, MetaIndex.VisorState => MetaFlag.VisorState,
MetaIndex.WeaponState => MetaFlag.WeaponState, MetaIndex.WeaponState => MetaFlag.WeaponState,
_ => (MetaFlag) byte.MaxValue, _ => (MetaFlag)byte.MaxValue,
};
public static string ToName(this MetaIndex index)
=> index switch
{
MetaIndex.HatState => "Hat Visible",
MetaIndex.VisorState => "Visor Toggled",
MetaIndex.WeaponState => "Weapon Visible",
MetaIndex.Wetness => "Force Wetness",
_ => "Unknown Meta",
};
public static string ToTooltip(this MetaIndex index)
=> index switch
{
MetaIndex.HatState => "Hide or show the characters head gear.",
MetaIndex.VisorState => "Toggle the visor state of the characters head gear.",
MetaIndex.WeaponState => "Hide or show the characters weapons when not drawn.",
MetaIndex.Wetness => "Force the character to be wet or not.",
_ => string.Empty,
}; };
} }

View file

@ -2,68 +2,59 @@ using Glamourer.Interop.Structs;
using Glamourer.State; using Glamourer.State;
using OtterGui.Classes; using OtterGui.Classes;
namespace Glamourer.Events; namespace Glamourer.Events
/// <summary>
/// Triggered when a Design is edited in any way.
/// <list type="number">
/// <item>Parameter is the type of the change </item>
/// <item>Parameter is the changed saved state. </item>
/// <item>Parameter is the existing actors using this saved state. </item>
/// <item>Parameter is any additional data depending on the type of change. </item>
/// </list>
/// </summary>
public sealed class StateChanged()
: EventWrapper<StateChanged.Type, StateChanged.Source, ActorState, ActorData, object?, StateChanged.Priority>(nameof(StateChanged))
{ {
public enum Type /// <summary>
/// Triggered when a Design is edited in any way.
/// <list type="number">
/// <item>Parameter is the type of the change </item>
/// <item>Parameter is the changed saved state. </item>
/// <item>Parameter is the existing actors using this saved state. </item>
/// <item>Parameter is any additional data depending on the type of change. </item>
/// </list>
/// </summary>
public sealed class StateChanged()
: EventWrapper<StateChanged.Type, StateSource, ActorState, ActorData, object?, StateChanged.Priority>(nameof(StateChanged))
{ {
/// <summary> A characters saved state had the model id changed. This means everything may have changed. Data is the old model id and the new model id. [(uint, uint)] </summary> public enum Type
Model, {
/// <summary> A characters saved state had the model id changed. This means everything may have changed. Data is the old model id and the new model id. [(uint, uint)] </summary>
Model,
/// <summary> A characters saved state had multiple customization values changed. TData is the old customize array and the applied changes. [(Customize, CustomizeFlag)] </summary> /// <summary> A characters saved state had multiple customization values changed. TData is the old customize array and the applied changes. [(Customize, CustomizeFlag)] </summary>
EntireCustomize, EntireCustomize,
/// <summary> A characters saved state had a customization value changed. Data is the old value, the new value and the type. [(CustomizeValue, CustomizeValue, CustomizeIndex)]. </summary> /// <summary> A characters saved state had a customization value changed. Data is the old value, the new value and the type. [(CustomizeValue, CustomizeValue, CustomizeIndex)]. </summary>
Customize, Customize,
/// <summary> A characters saved state had an equipment piece changed. Data is the old value, the new value and the slot [(EquipItem, EquipItem, EquipSlot)]. </summary> /// <summary> A characters saved state had an equipment piece changed. Data is the old value, the new value and the slot [(EquipItem, EquipItem, EquipSlot)]. </summary>
Equip, Equip,
/// <summary> A characters saved state had its weapons changed. Data is the old mainhand, the old offhand, the new mainhand and the new offhand [(EquipItem, EquipItem, EquipItem, EquipItem)]. </summary> /// <summary> A characters saved state had its weapons changed. Data is the old mainhand, the old offhand, the new mainhand and the new offhand [(EquipItem, EquipItem, EquipItem, EquipItem)]. </summary>
Weapon, Weapon,
/// <summary> A characters saved state had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary> /// <summary> A characters saved state had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
Stain, Stain,
/// <summary> A characters saved state had a crest visibility changed. Data is the old crest visibility, the new crest visibility and the slot [(bool, bool, EquipSlot)]. </summary> /// <summary> A characters saved state had a crest visibility changed. Data is the old crest visibility, the new crest visibility and the slot [(bool, bool, EquipSlot)]. </summary>
Crest, Crest,
/// <summary> A characters saved state had its customize parameter changed. Data is the old value, the new value and the type [(CustomizeParameterValue, CustomizeParameterValue, CustomizeParameterFlag)]. </summary> /// <summary> A characters saved state had its customize parameter changed. Data is the old value, the new value and the type [(CustomizeParameterValue, CustomizeParameterValue, CustomizeParameterFlag)]. </summary>
Parameter, Parameter,
/// <summary> A characters saved state had a design applied. This means everything may have changed. Data is the applied design. [DesignBase] </summary> /// <summary> A characters saved state had a design applied. This means everything may have changed. Data is the applied design. [DesignBase] </summary>
Design, Design,
/// <summary> A characters saved state had its state reset to its game values. This means everything may have changed. Data is null. </summary> /// <summary> A characters saved state had its state reset to its game values. This means everything may have changed. Data is null. </summary>
Reset, Reset,
/// <summary> A characters saved state had a meta toggle changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary> /// <summary> A characters saved state had a meta toggle changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
Other, Other,
} }
public enum Source : byte public enum Priority
{ {
Game, GlamourerIpc = int.MinValue,
Manual, }
Fixed,
Ipc,
// Only used for CustomizeParameters.
Pending,
}
public enum Priority
{
GlamourerIpc = int.MinValue,
} }
} }

View file

@ -33,7 +33,7 @@ public ref struct CustomizeParameterDrawData(CustomizeParameterFlag flag, in Des
{ {
Locked = state.IsLocked, Locked = state.IsLocked,
DisplayApplication = false, DisplayApplication = false,
ValueSetter = v => manager.ChangeCustomizeParameter(state, flag, v, StateChanged.Source.Manual), ValueSetter = v => manager.ChangeCustomizeParameter(state, flag, v, StateSource.Manual),
GameValue = state.BaseData.Parameters[flag], GameValue = state.BaseData.Parameters[flag],
AllowRevert = true, AllowRevert = true,
}; };

View file

@ -163,7 +163,7 @@ public sealed class DesignQuickBar : Window, IDisposable
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags(); var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters); using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual); _stateManager.ApplyDesign(design, state, StateSource.Manual);
} }
public void DrawRevertButton(Vector2 buttonSize) public void DrawRevertButton(Vector2 buttonSize)
@ -189,7 +189,7 @@ public sealed class DesignQuickBar : Window, IDisposable
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, tooltip, available); var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, tooltip, available);
if (clicked) if (clicked)
_stateManager.ResetState(state!, StateChanged.Source.Manual); _stateManager.ResetState(state!, StateSource.Manual);
} }
public void DrawRevertAutomationButton(Vector2 buttonSize) public void DrawRevertAutomationButton(Vector2 buttonSize)
@ -257,7 +257,7 @@ public sealed class DesignQuickBar : Window, IDisposable
ImGui.SameLine(); ImGui.SameLine();
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Palette, buttonSize, tooltip, available); var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.Palette, buttonSize, tooltip, available);
if (clicked) if (clicked)
_stateManager.ResetAdvancedState(state!, StateChanged.Source.Manual); _stateManager.ResetAdvancedState(state!, StateSource.Manual);
} }
private (bool, ActorIdentifier, ActorData, ActorState?) ResolveTarget(FontAwesomeIcon icon, Vector2 buttonSize, string tooltip, private (bool, ActorIdentifier, ActorData, ActorState?) ResolveTarget(FontAwesomeIcon icon, Vector2 buttonSize, string tooltip,

View file

@ -46,8 +46,8 @@ public ref struct EquipDrawData(EquipSlot slot, in DesignData designData)
public static EquipDrawData FromState(StateManager manager, ActorState state, EquipSlot slot) public static EquipDrawData FromState(StateManager manager, ActorState state, EquipSlot slot)
=> new(slot, state.ModelData) => new(slot, state.ModelData)
{ {
ItemSetter = i => manager.ChangeItem(state, slot, i, StateChanged.Source.Manual), ItemSetter = i => manager.ChangeItem(state, slot, i, StateSource.Manual),
StainSetter = i => manager.ChangeStain(state, slot, i, StateChanged.Source.Manual), StainSetter = i => manager.ChangeStain(state, slot, i, StateSource.Manual),
Locked = state.IsLocked, Locked = state.IsLocked,
DisplayApplication = false, DisplayApplication = false,
GameItem = state.BaseData.Item(slot), GameItem = state.BaseData.Item(slot),

View file

@ -113,22 +113,22 @@ public class PenumbraChangedItemTooltip : IDisposable
case (false, false): case (false, false):
Glamourer.Log.Information($"Applying {item.Name} to Right Finger."); Glamourer.Log.Information($"Applying {item.Name} to Right Finger.");
SetLastItem(EquipSlot.RFinger, item, state); SetLastItem(EquipSlot.RFinger, item, state);
_stateManager.ChangeItem(state, EquipSlot.RFinger, item, StateChanged.Source.Manual); _stateManager.ChangeItem(state, EquipSlot.RFinger, item, StateSource.Manual);
break; break;
case (false, true): case (false, true):
Glamourer.Log.Information($"Applying {item.Name} to Left Finger."); Glamourer.Log.Information($"Applying {item.Name} to Left Finger.");
SetLastItem(EquipSlot.LFinger, item, state); SetLastItem(EquipSlot.LFinger, item, state);
_stateManager.ChangeItem(state, EquipSlot.LFinger, item, StateChanged.Source.Manual); _stateManager.ChangeItem(state, EquipSlot.LFinger, item, StateSource.Manual);
break; break;
case (true, false) when last.Valid: case (true, false) when last.Valid:
Glamourer.Log.Information($"Re-Applying {last.Name} to Right Finger."); Glamourer.Log.Information($"Re-Applying {last.Name} to Right Finger.");
SetLastItem(EquipSlot.RFinger, default, state); SetLastItem(EquipSlot.RFinger, default, state);
_stateManager.ChangeItem(state, EquipSlot.RFinger, last, StateChanged.Source.Manual); _stateManager.ChangeItem(state, EquipSlot.RFinger, last, StateSource.Manual);
break; break;
case (true, true) when _lastItems[EquipSlot.LFinger.ToIndex()].Valid: case (true, true) when _lastItems[EquipSlot.LFinger.ToIndex()].Valid:
Glamourer.Log.Information($"Re-Applying {last.Name} to Left Finger."); Glamourer.Log.Information($"Re-Applying {last.Name} to Left Finger.");
SetLastItem(EquipSlot.LFinger, default, state); SetLastItem(EquipSlot.LFinger, default, state);
_stateManager.ChangeItem(state, EquipSlot.LFinger, last, StateChanged.Source.Manual); _stateManager.ChangeItem(state, EquipSlot.LFinger, last, StateSource.Manual);
break; break;
} }
@ -138,13 +138,13 @@ public class PenumbraChangedItemTooltip : IDisposable
{ {
Glamourer.Log.Information($"Re-Applying {last.Name} to {slot.ToName()}."); Glamourer.Log.Information($"Re-Applying {last.Name} to {slot.ToName()}.");
SetLastItem(slot, default, state); SetLastItem(slot, default, state);
_stateManager.ChangeItem(state, slot, last, StateChanged.Source.Manual); _stateManager.ChangeItem(state, slot, last, StateSource.Manual);
} }
else else
{ {
Glamourer.Log.Information($"Applying {item.Name} to {slot.ToName()}."); Glamourer.Log.Information($"Applying {item.Name} to {slot.ToName()}.");
SetLastItem(slot, item, state); SetLastItem(slot, item, state);
_stateManager.ChangeItem(state, slot, item, StateChanged.Source.Manual); _stateManager.ChangeItem(state, slot, item, StateSource.Manual);
} }
return; return;

View file

@ -61,13 +61,13 @@ public class ActorPanel(
if (_importService.CreateDatTarget(out var dat)) if (_importService.CreateDatTarget(out var dat))
{ {
_stateManager.ChangeCustomize(_state!, dat.Customize, CustomizeApplicationFlags, StateChanged.Source.Manual); _stateManager.ChangeCustomize(_state!, dat.Customize, CustomizeApplicationFlags, StateSource.Manual);
Glamourer.Messager.NotificationMessage($"Applied games .dat file {dat.Description} customizations to {_state.Identifier}.", Glamourer.Messager.NotificationMessage($"Applied games .dat file {dat.Description} customizations to {_state.Identifier}.",
NotificationType.Success, false); NotificationType.Success, false);
} }
else if (_importService.CreateCharaTarget(out var designBase, out var name)) else if (_importService.CreateCharaTarget(out var designBase, out var name))
{ {
_stateManager.ApplyDesign(designBase, _state!, StateChanged.Source.Manual); _stateManager.ApplyDesign(designBase, _state!, StateSource.Manual);
Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_state.Identifier}.", NotificationType.Success, Glamourer.Messager.NotificationMessage($"Applied Anamnesis .chara file {name} to {_state.Identifier}.", NotificationType.Success,
false); false);
} }
@ -139,7 +139,7 @@ public class ActorPanel(
return; return;
if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked, _lockedRedraw)) if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked, _lockedRedraw))
_stateManager.ChangeCustomize(_state, _customizationDrawer.Customize, _customizationDrawer.Changed, StateChanged.Source.Manual); _stateManager.ChangeCustomize(_state, _customizationDrawer.Customize, _customizationDrawer.Changed, StateSource.Manual);
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.Wetness, _stateManager, _state)); EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.Wetness, _stateManager, _state));
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2)); ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
@ -159,7 +159,7 @@ public class ActorPanel(
var data = EquipDrawData.FromState(_stateManager, _state!, slot); var data = EquipDrawData.FromState(_stateManager, _state!, slot);
_equipmentDrawer.DrawEquip(data); _equipmentDrawer.DrawEquip(data);
if (usedAllStain) if (usedAllStain)
_stateManager.ChangeStain(_state, slot, newAllStain, StateChanged.Source.Manual); _stateManager.ChangeStain(_state, slot, newAllStain, StateSource.Manual);
} }
var mainhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.MainHand); var mainhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.MainHand);
@ -264,7 +264,7 @@ public class ActorPanel(
} }
if (turnHuman) if (turnHuman)
_stateManager.TurnHuman(_state, StateChanged.Source.Manual); _stateManager.TurnHuman(_state, StateSource.Manual);
} }
private HeaderDrawer.Button SetFromClipboardButton() private HeaderDrawer.Button SetFromClipboardButton()
@ -340,7 +340,7 @@ public class ActorPanel(
var text = ImGui.GetClipboardText(); var text = ImGui.GetClipboardText();
var design = _converter.FromBase64(text, applyCustomize, applyGear, out _) var design = _converter.FromBase64(text, applyCustomize, applyGear, out _)
?? throw new Exception("The clipboard did not contain valid data."); ?? throw new Exception("The clipboard did not contain valid data.");
_stateManager.ApplyDesign(design, _state!, StateChanged.Source.Manual); _stateManager.ApplyDesign(design, _state!, StateSource.Manual);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -368,7 +368,7 @@ public class ActorPanel(
{ {
if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.", if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.",
_state!.IsLocked)) _state!.IsLocked))
_stateManager.ResetState(_state!, StateChanged.Source.Manual); _stateManager.ResetState(_state!, StateSource.Manual);
ImGui.SameLine(); ImGui.SameLine();
if (ImGuiUtil.DrawDisabledButton("Reapply State", Vector2.Zero, "Try to reapply the configured state if something went wrong.", if (ImGuiUtil.DrawDisabledButton("Reapply State", Vector2.Zero, "Try to reapply the configured state if something went wrong.",
@ -396,7 +396,7 @@ public class ActorPanel(
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags(); var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state)) if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state, _stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state,
StateChanged.Source.Manual); StateSource.Manual);
} }
private void DrawApplyToTarget() private void DrawApplyToTarget()
@ -414,6 +414,6 @@ public class ActorPanel(
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags(); var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state)) if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state, _stateManager.ApplyDesign(_converter.Convert(_state!, applyGear, applyCustomize, applyCrest, applyParameters), state,
StateChanged.Source.Manual); StateSource.Manual);
} }
} }

View file

@ -264,7 +264,7 @@ public class SetPanel(
var size = new Vector2(ImGui.GetFrameHeight()); var size = new Vector2(ImGui.GetFrameHeight());
size.X += ImGuiHelpers.GlobalScale; size.X += ImGuiHelpers.GlobalScale;
var (equipFlags, customizeFlags, _, _, _, _, _, _) = design.ApplyWhat(); var (equipFlags, customizeFlags, _, _, _) = design.ApplyWhat();
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var slot in EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand)) foreach (var slot in EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand))
{ {

View file

@ -52,11 +52,11 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
ImGuiUtil.DrawTableColumn(state.Identifier.ToString()); ImGuiUtil.DrawTableColumn(state.Identifier.ToString());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGui.Button("Reset")) if (ImGui.Button("Reset"))
stateManager.ResetState(state, StateChanged.Source.Manual); stateManager.ResetState(state, StateSource.Manual);
ImGui.TableNextRow(); ImGui.TableNextRow();
static void PrintRow<T>(string label, T actor, T model, StateChanged.Source source) where T : notnull static void PrintRow<T>(string label, T actor, T model, StateSource source) where T : notnull
{ {
ImGuiUtil.DrawTableColumn(label); ImGuiUtil.DrawTableColumn(label);
ImGuiUtil.DrawTableColumn(actor.ToString()!); ImGuiUtil.DrawTableColumn(actor.ToString()!);
@ -70,44 +70,44 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
return $"{item.Name} ({item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})"; return $"{item.Name} ({item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})";
} }
PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state.Source[MetaIndex.ModelId]); PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state.Sources[MetaIndex.ModelId]);
ImGui.TableNextRow(); ImGui.TableNextRow();
PrintRow("Wetness", state.BaseData.IsWet(), state.ModelData.IsWet(), state.Source[MetaIndex.Wetness]); PrintRow("Wetness", state.BaseData.IsWet(), state.ModelData.IsWet(), state.Sources[MetaIndex.Wetness]);
ImGui.TableNextRow(); ImGui.TableNextRow();
if (state.BaseData.IsHuman && state.ModelData.IsHuman) if (state.BaseData.IsHuman && state.ModelData.IsHuman)
{ {
PrintRow("Hat Visible", state.BaseData.IsHatVisible(), state.ModelData.IsHatVisible(), state.Source[MetaIndex.HatState]); PrintRow("Hat Visible", state.BaseData.IsHatVisible(), state.ModelData.IsHatVisible(), state.Sources[MetaIndex.HatState]);
ImGui.TableNextRow(); ImGui.TableNextRow();
PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(), PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(),
state.Source[MetaIndex.VisorState]); state.Sources[MetaIndex.VisorState]);
ImGui.TableNextRow(); ImGui.TableNextRow();
PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(), PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(),
state.Source[MetaIndex.WeaponState]); state.Sources[MetaIndex.WeaponState]);
ImGui.TableNextRow(); ImGui.TableNextRow();
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand)) foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
{ {
PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state.Source[slot, false]); PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state.Sources[slot, false]);
ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString()); ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString());
ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString()); ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString());
ImGuiUtil.DrawTableColumn(state.Source[slot, true].ToString()); ImGuiUtil.DrawTableColumn(state.Sources[slot, true].ToString());
} }
foreach (var type in Enum.GetValues<CustomizeIndex>()) foreach (var type in Enum.GetValues<CustomizeIndex>())
{ {
PrintRow(type.ToDefaultName(), state.BaseData.Customize[type].Value, state.ModelData.Customize[type].Value, state.Source[type]); PrintRow(type.ToDefaultName(), state.BaseData.Customize[type].Value, state.ModelData.Customize[type].Value, state.Sources[type]);
ImGui.TableNextRow(); ImGui.TableNextRow();
} }
foreach (var crest in CrestExtensions.AllRelevantSet) foreach (var crest in CrestExtensions.AllRelevantSet)
{ {
PrintRow(crest.ToLabel(), state.BaseData.Crest(crest), state.ModelData.Crest(crest), state.Source[crest]); PrintRow(crest.ToLabel(), state.BaseData.Crest(crest), state.ModelData.Crest(crest), state.Sources[crest]);
ImGui.TableNextRow(); ImGui.TableNextRow();
} }
foreach (var flag in CustomizeParameterExtensions.AllFlags) foreach (var flag in CustomizeParameterExtensions.AllFlags)
{ {
PrintRow(flag.ToString(), state.BaseData.Parameters[flag], state.ModelData.Parameters[flag], state.Source[flag]); PrintRow(flag.ToString(), state.BaseData.Parameters[flag], state.ModelData.Parameters[flag], state.Sources[flag]);
ImGui.TableNextRow(); ImGui.TableNextRow();
} }
} }

View file

@ -25,9 +25,8 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
continue; continue;
DrawDesign(design, _designFileSystem); DrawDesign(design, _designFileSystem);
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.ApplyEquip, design.ApplyCustomizeRaw, var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.ApplyEquip, design.ApplyCustomizeRaw, design.ApplyMeta,
design.DoApplyHatVisible(), design.WriteProtected());
design.DoApplyVisorToggle(), design.DoApplyWeaponVisible(), design.WriteProtected());
using var font = ImRaii.PushFont(UiBuilder.MonoFont); using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGuiUtil.TextWrapped(base64); ImGuiUtil.TextWrapped(base64);
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
@ -85,18 +84,12 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGuiUtil.DrawTableColumn(applyCrest ? "Apply" : "Keep"); ImGuiUtil.DrawTableColumn(applyCrest ? "Apply" : "Keep");
} }
ImGuiUtil.DrawTableColumn("Hat Visible"); foreach (var index in MetaExtensions.AllRelevant)
ImGuiUtil.DrawTableColumn(design.DesignData.IsHatVisible().ToString()); {
ImGuiUtil.DrawTableColumn(design.DoApplyHatVisible() ? "Apply" : "Keep"); ImGuiUtil.DrawTableColumn(index.ToName());
ImGui.TableNextRow(); ImGuiUtil.DrawTableColumn(design.DesignData.GetMeta(index).ToString());
ImGuiUtil.DrawTableColumn("Visor Toggled"); ImGuiUtil.DrawTableColumn(design.DoApplyMeta(index) ? "Apply" : "Keep");
ImGuiUtil.DrawTableColumn(design.DesignData.IsVisorToggled().ToString()); }
ImGuiUtil.DrawTableColumn(design.DoApplyVisorToggle() ? "Apply" : "Keep");
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Weapon Visible");
ImGuiUtil.DrawTableColumn(design.DesignData.IsWeaponVisible().ToString());
ImGuiUtil.DrawTableColumn(design.DoApplyWeaponVisible() ? "Apply" : "Keep");
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Model ID"); ImGuiUtil.DrawTableColumn("Model ID");
ImGuiUtil.DrawTableColumn(design.DesignData.ModelId.ToString()); ImGuiUtil.DrawTableColumn(design.DesignData.ModelId.ToString());
@ -111,9 +104,5 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
ImGuiUtil.DrawTableColumn(apply ? "Apply" : "Keep"); ImGuiUtil.DrawTableColumn(apply ? "Apply" : "Keep");
ImGui.TableNextRow(); ImGui.TableNextRow();
} }
ImGuiUtil.DrawTableColumn("Is Wet");
ImGuiUtil.DrawTableColumn(design.DesignData.IsWet().ToString());
ImGui.TableNextRow();
} }
} }

View file

@ -55,10 +55,8 @@ public class DesignTesterPanel(ItemManager _items, HumanModelList _humans) : IGa
try try
{ {
_parse64 = DesignBase64Migration.MigrateBase64(_items, _humans, _base64, out var ef, out var cf, out var wp, out var ah, _parse64 = DesignBase64Migration.MigrateBase64(_items, _humans, _base64, out var ef, out var cf, out var wp, out var meta);
out var av, _restore = DesignBase64Migration.CreateOldBase64(in _parse64, ef, cf, meta, wp);
out var aw);
_restore = DesignBase64Migration.CreateOldBase64(in _parse64, ef, cf, ah, av, aw, wp);
_restoreBytes = Convert.FromBase64String(_restore); _restoreBytes = Convert.FromBase64String(_restore);
} }
catch (Exception ex) catch (Exception ex)

View file

@ -67,9 +67,9 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
if (ImGuiUtil.DrawDisabledButton("Apply", Vector2.Zero, string.Empty, disabled)) if (ImGuiUtil.DrawDisabledButton("Apply", Vector2.Zero, string.Empty, disabled))
{ {
foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true)) foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true))
_state.ChangeEquip(state!, slot, item, stain, StateChanged.Source.Manual); _state.ChangeEquip(state!, slot, item, stain, StateSource.Manual);
_state.ChangeVisorState(state!, data.VisorToggled, StateChanged.Source.Manual); _state.ChangeMeta(state!, MetaIndex.VisorState, data.VisorToggled, StateSource.Manual);
_state.ChangeCustomize(state!, data.Customize, CustomizeFlagExtensions.All, StateChanged.Source.Manual); _state.ChangeCustomize(state!, data.Customize, CustomizeFlagExtensions.All, StateSource.Manual);
} }
ImGui.TableNextColumn(); ImGui.TableNextColumn();

View file

@ -287,28 +287,25 @@ public class DesignPanel(
private void DrawMetaApplication() private void DrawMetaApplication()
{ {
using var id = ImRaii.PushId("Meta"); using var id = ImRaii.PushId("Meta");
const uint all = 0x0Fu; const uint all = (uint)MetaExtensions.All;
var flags = (_selector.Selected!.DoApplyHatVisible() ? 0x01u : 0x00) var flags = (uint)_selector.Selected!.ApplyMeta;
| (_selector.Selected!.DoApplyVisorToggle() ? 0x02u : 0x00) var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
| (_selector.Selected!.DoApplyWeaponVisible() ? 0x04u : 0x00)
| (_selector.Selected!.DoApplyWetness() ? 0x08u : 0x00);
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
var apply = bigChange ? (flags & 0x01) == 0x01 : _selector.Selected!.DoApplyHatVisible();
if (ImGui.Checkbox("Apply Hat Visibility", ref apply) || bigChange)
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, apply);
apply = bigChange ? (flags & 0x02) == 0x02 : _selector.Selected!.DoApplyVisorToggle(); var labels = new[]
if (ImGui.Checkbox("Apply Visor State", ref apply) || bigChange) {
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.VisorState, apply); "Apply Hat Visibility",
"Apply Visor State",
"Apply Weapon Visibility",
"Apply Wetness",
};
apply = bigChange ? (flags & 0x04) == 0x04 : _selector.Selected!.DoApplyWeaponVisible(); foreach (var (index, label) in MetaExtensions.AllRelevant.Zip(labels))
if (ImGui.Checkbox("Apply Weapon Visibility", ref apply) || bigChange) {
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.WeaponState, apply); var apply = bigChange ? ((MetaFlag)flags).HasFlag(index.ToFlag()) : _selector.Selected!.DoApplyMeta(index);
if (ImGui.Checkbox(label, ref apply) || bigChange)
apply = bigChange ? (flags & 0x08) == 0x08 : _selector.Selected!.DoApplyWetness(); _manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, apply);
if (ImGui.Checkbox("Apply Wetness", ref apply) || bigChange) }
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.Wetness, apply);
} }
private void DrawParameterApplication() private void DrawParameterApplication()
@ -442,7 +439,7 @@ public class DesignPanel(
{ {
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags(); var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters); using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual); _state.ApplyDesign(_selector.Selected!, state, StateSource.Manual);
} }
} }
@ -461,7 +458,7 @@ public class DesignPanel(
{ {
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags(); var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters); using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual); _state.ApplyDesign(_selector.Selected!, state, StateSource.Manual);
} }
} }

View file

@ -202,7 +202,7 @@ public class NpcPanel(
{ {
var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags(); var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags();
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0); var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0);
_state.ApplyDesign(design, state, StateChanged.Source.Manual); _state.ApplyDesign(design, state, StateSource.Manual);
} }
} }
@ -221,7 +221,7 @@ public class NpcPanel(
{ {
var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags(); var (applyGear, applyCustomize, _, _) = UiHelpers.ConvertKeysToFlags();
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0); var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, 0, 0);
_state.ApplyDesign(design, state, StateChanged.Source.Manual); _state.ApplyDesign(design, state, StateSource.Manual);
} }
} }

View file

@ -1,5 +1,4 @@
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.State; using Glamourer.State;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
@ -23,32 +22,17 @@ public ref struct ToggleDrawData
{ } { }
public static ToggleDrawData FromDesign(MetaIndex index, DesignManager manager, Design design) public static ToggleDrawData FromDesign(MetaIndex index, DesignManager manager, Design design)
{ => new()
var (label, value, apply, setValue, setApply) = index switch
{ {
MetaIndex.HatState => ("Hat Visible", design.DesignData.IsHatVisible(), design.DoApplyHatVisible(), Label = index.ToName(),
(Action<bool>)(b => manager.ChangeMeta(design, index, b)), (Action<bool>)(b => manager.ChangeApplyMeta(design, index, b))),
MetaIndex.VisorState => ("Visor Toggled", design.DesignData.IsVisorToggled(), design.DoApplyVisorToggle(),
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
MetaIndex.WeaponState => ("Weapon Visible", design.DesignData.IsWeaponVisible(), design.DoApplyWeaponVisible(),
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
MetaIndex.Wetness => ("Force Wetness", design.DesignData.IsWet(), design.DoApplyWetness(),
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
_ => throw new Exception("Unsupported meta index."),
};
return new ToggleDrawData
{
Label = label,
Tooltip = string.Empty, Tooltip = string.Empty,
Locked = design.WriteProtected(), Locked = design.WriteProtected(),
DisplayApplication = true, DisplayApplication = true,
CurrentValue = value, CurrentValue = design.DesignData.GetMeta(index),
CurrentApply = apply, CurrentApply = design.DoApplyMeta(index),
SetValue = setValue, SetValue = b => manager.ChangeMeta(design, index, b),
SetApply = setApply, SetApply = b => manager.ChangeApplyMeta(design, index, b),
}; };
}
public static ToggleDrawData CrestFromDesign(CrestFlag slot, DesignManager manager, Design design) public static ToggleDrawData CrestFromDesign(CrestFlag slot, DesignManager manager, Design design)
=> new() => new()
@ -70,52 +54,27 @@ public ref struct ToggleDrawData
Tooltip = "Hide or show your free company crest on this piece of gear.", Tooltip = "Hide or show your free company crest on this piece of gear.",
Locked = state.IsLocked, Locked = state.IsLocked,
CurrentValue = state.ModelData.Crest(slot), CurrentValue = state.ModelData.Crest(slot),
SetValue = v => manager.ChangeCrest(state, slot, v, StateChanged.Source.Manual), SetValue = v => manager.ChangeCrest(state, slot, v, StateSource.Manual),
}; };
public static ToggleDrawData FromState(MetaIndex index, StateManager manager, ActorState state) public static ToggleDrawData FromState(MetaIndex index, StateManager manager, ActorState state)
{ {
var (label, tooltip, value, setValue) = index switch
{
MetaIndex.HatState => ("Hat Visible", "Hide or show the characters head gear.", state.ModelData.IsHatVisible(),
(Action<bool>)(b => manager.ChangeHatState(state, b, StateChanged.Source.Manual))),
MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear.",
state.ModelData.IsVisorToggled(),
b => manager.ChangeVisorState(state, b, StateChanged.Source.Manual)),
MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn.",
state.ModelData.IsWeaponVisible(),
b => manager.ChangeWeaponState(state, b, StateChanged.Source.Manual)),
MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not.", state.ModelData.IsWet(),
b => manager.ChangeWetness(state, b, StateChanged.Source.Manual)),
_ => throw new Exception("Unsupported meta index."),
};
return new ToggleDrawData return new ToggleDrawData
{ {
Label = label, Label = index.ToName(),
Tooltip = tooltip, Tooltip = index.ToTooltip(),
Locked = state.IsLocked, Locked = state.IsLocked,
CurrentValue = value, CurrentValue = state.ModelData.GetMeta(index),
SetValue = setValue, SetValue = b => manager.ChangeMeta(state, index, b, StateSource.Manual),
}; };
} }
public static ToggleDrawData FromValue(MetaIndex index, bool value) public static ToggleDrawData FromValue(MetaIndex index, bool value)
{ => new()
var (label, tooltip) = index switch
{ {
MetaIndex.HatState => ("Hat Visible", "Hide or show the characters head gear."), Label = index.ToName(),
MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear."), Tooltip = index.ToTooltip(),
MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn."),
MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not."),
_ => throw new Exception("Unsupported meta index."),
};
return new ToggleDrawData
{
Label = label,
Tooltip = tooltip,
Locked = true, Locked = true,
CurrentValue = value, CurrentValue = value,
}; };
}
} }

View file

@ -118,14 +118,14 @@ public class ContextMenuService : IDisposable
return; return;
var slot = item.Type.ToSlot(); var slot = item.Type.ToSlot();
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual); _state.ChangeEquip(state, slot, item, 0, StateSource.Manual);
if (item.Type.ValidOffhand().IsOffhandType()) if (item.Type.ValidOffhand().IsOffhandType())
{ {
if (item.PrimaryId.Id is > 1600 and < 1651 if (item.PrimaryId.Id is > 1600 and < 1651
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets)) && _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual); _state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateSource.Manual);
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand)) if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual); _state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateSource.Manual);
} }
}; };
} }
@ -142,14 +142,14 @@ public class ContextMenuService : IDisposable
return; return;
var slot = item.Type.ToSlot(); var slot = item.Type.ToSlot();
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual); _state.ChangeEquip(state, slot, item, 0, StateSource.Manual);
if (item.Type.ValidOffhand().IsOffhandType()) if (item.Type.ValidOffhand().IsOffhandType())
{ {
if (item.PrimaryId.Id is > 1600 and < 1651 if (item.PrimaryId.Id is > 1600 and < 1651
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets)) && _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual); _state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateSource.Manual);
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand)) if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual); _state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateSource.Manual);
} }
}; };
} }

View file

@ -333,7 +333,7 @@ public class CommandService : IDisposable
foreach (var identifier in identifiers) foreach (var identifier in identifiers)
{ {
if (_stateManager.TryGetValue(identifier, out var state)) if (_stateManager.TryGetValue(identifier, out var state))
_stateManager.ResetState(state, StateChanged.Source.Manual); _stateManager.ResetState(state, StateSource.Manual);
} }
@ -419,7 +419,7 @@ public class CommandService : IDisposable
if (!_objects.TryGetValue(identifier, out var actors)) if (!_objects.TryGetValue(identifier, out var actors))
{ {
if (_stateManager.TryGetValue(identifier, out var state)) if (_stateManager.TryGetValue(identifier, out var state))
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual); _stateManager.ApplyDesign(design, state, StateSource.Manual);
} }
else else
{ {
@ -428,7 +428,7 @@ public class CommandService : IDisposable
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state)) if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
{ {
ApplyModSettings(design, actor, applyMods); ApplyModSettings(design, actor, applyMods);
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual); _stateManager.ApplyDesign(design, state, StateSource.Manual);
} }
} }
} }

View file

@ -68,13 +68,13 @@ public class ActorState
=> Unlock(1337); => Unlock(1337);
/// <summary> This contains whether a change to the base data was made by the game, the user via manual input or through automatic application. </summary> /// <summary> This contains whether a change to the base data was made by the game, the user via manual input or through automatic application. </summary>
public readonly StateSource Source = new(); public StateSources Sources = new();
internal ActorState(ActorIdentifier identifier) internal ActorState(ActorIdentifier identifier)
=> Identifier = identifier.CreatePermanent(); => Identifier = identifier.CreatePermanent();
public CustomizeParameterFlag OnlyChangedParameters() public CustomizeParameterFlag OnlyChangedParameters()
=> CustomizeParameterExtensions.AllFlags.Where(f => Source[f] is not StateChanged.Source.Game) => CustomizeParameterExtensions.AllFlags.Where(f => Sources[f] is not StateSource.Game)
.Aggregate((CustomizeParameterFlag)0, (a, b) => a | b); .Aggregate((CustomizeParameterFlag)0, (a, b) => a | b);
public bool UpdateTerritory(ushort territory) public bool UpdateTerritory(ushort territory)

View file

@ -1,4 +1,5 @@
using Glamourer.Events; using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.GameData; using Glamourer.GameData;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.Penumbra; using Glamourer.Interop.Penumbra;
@ -118,7 +119,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.Source[slot, false] is not StateChanged.Source.Ipc, ChangeArmor(data, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc,
state.ModelData.IsHatVisible()); state.ModelData.IsHatVisible());
return data; return data;
@ -200,67 +201,44 @@ public class StateApplier(
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain)); _weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain));
} }
/// <summary> Change the visor state of actors only on the draw object. </summary> /// <summary> Change a meta state. </summary>
public void ChangeVisor(ActorData data, bool value) public unsafe void ChangeMetaState(ActorData data, MetaIndex index, bool value)
{ {
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) switch (index)
_visor.SetVisorState(actor.Model, value); {
case MetaIndex.Wetness:
{
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
actor.AsCharacter->IsGPoseWet = value;
return;
}
case MetaIndex.HatState:
{
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
_metaService.SetHatState(actor, value);
return;
}
case MetaIndex.WeaponState:
{
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
_metaService.SetWeaponState(actor, value);
return;
}
case MetaIndex.VisorState:
{
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
_visor.SetVisorState(actor.Model, value);
return;
}
}
} }
/// <inheritdoc cref="ChangeVisor(ActorData, bool)"/> /// <inheritdoc cref="ChangeMetaState(ActorData, MetaIndex, bool)"/>
public ActorData ChangeVisor(ActorState state, bool apply) public ActorData ChangeMetaState(ActorState state, MetaIndex index, bool apply)
{ {
var data = GetData(state); var data = GetData(state);
if (apply) if (apply)
ChangeVisor(data, state.ModelData.IsVisorToggled()); ChangeMetaState(data, index, state.ModelData.GetMeta(index));
return data;
}
/// <summary> Change the forced wetness state on actors. </summary>
public unsafe void ChangeWetness(ActorData data, bool value)
{
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
actor.AsCharacter->IsGPoseWet = value;
}
/// <inheritdoc cref="ChangeWetness(ActorData, bool)"/>
public ActorData ChangeWetness(ActorState state, bool apply)
{
var data = GetData(state);
if (apply)
ChangeWetness(data, state.ModelData.IsWet());
return data;
}
/// <summary> Change the hat-visibility state on actors. </summary>
public void ChangeHatState(ActorData data, bool value)
{
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
_metaService.SetHatState(actor, value);
}
/// <inheritdoc cref="ChangeHatState(ActorData, bool)"/>
public ActorData ChangeHatState(ActorState state, bool apply)
{
var data = GetData(state);
if (apply)
ChangeHatState(data, state.ModelData.IsHatVisible());
return data;
}
/// <summary> Change the weapon-visibility state on actors. </summary>
public void ChangeWeaponState(ActorData data, bool value)
{
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
_metaService.SetWeaponState(actor, value);
}
/// <inheritdoc cref="ChangeWeaponState(ActorData, bool)"/>
public ActorData ChangeWeaponState(ActorState state, bool apply)
{
var data = GetData(state);
if (apply)
ChangeWeaponState(data, state.ModelData.IsWeaponVisible());
return data; return data;
} }

View file

@ -13,7 +13,7 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
{ {
/// <summary> Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. </summary> /// <summary> Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. </summary>
/// <remarks> We currently only allow changing things to humans, not humans to monsters. </remarks> /// <remarks> We currently only allow changing things to humans, not humans to monsters. </remarks>
public bool ChangeModelId(ActorState state, uint modelId, in CustomizeArray customize, nint equipData, StateChanged.Source source, public bool ChangeModelId(ActorState state, uint modelId, in CustomizeArray customize, nint equipData, StateSource source,
out uint oldModelId, uint key = 0) out uint oldModelId, uint key = 0)
{ {
oldModelId = state.ModelData.ModelId; oldModelId = state.ModelData.ModelId;
@ -45,21 +45,21 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
state.ModelData.SetHatVisible(true); state.ModelData.SetHatVisible(true);
state.ModelData.SetWeaponVisible(true); state.ModelData.SetWeaponVisible(true);
state.ModelData.SetVisor(false); state.ModelData.SetVisor(false);
state.Source[MetaIndex.ModelId] = source; state.Sources[MetaIndex.ModelId] = source;
state.Source[MetaIndex.HatState] = source; state.Sources[MetaIndex.HatState] = source;
state.Source[MetaIndex.WeaponState] = source; state.Sources[MetaIndex.WeaponState] = source;
state.Source[MetaIndex.VisorState] = source; state.Sources[MetaIndex.VisorState] = source;
foreach (var slot in EquipSlotExtensions.FullSlots) foreach (var slot in EquipSlotExtensions.FullSlots)
{ {
state.Source[slot, true] = source; state.Sources[slot, true] = source;
state.Source[slot, false] = source; state.Sources[slot, false] = source;
} }
state.Source[CustomizeIndex.Clan] = source; state.Sources[CustomizeIndex.Clan] = source;
state.Source[CustomizeIndex.Gender] = source; state.Sources[CustomizeIndex.Gender] = source;
var set = customizations.Manager.GetSet(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender); var set = customizations.Manager.GetSet(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(set.IsAvailable)) foreach (var index in Enum.GetValues<CustomizeIndex>().Where(set.IsAvailable))
state.Source[index] = source; state.Sources[index] = source;
} }
else else
{ {
@ -67,14 +67,14 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
return false; return false;
state.ModelData.LoadNonHuman(modelId, customize, equipData); state.ModelData.LoadNonHuman(modelId, customize, equipData);
state.Source[MetaIndex.ModelId] = source; state.Sources[MetaIndex.ModelId] = source;
} }
return true; return true;
} }
/// <summary> Change a customization value. </summary> /// <summary> Change a customization value. </summary>
public bool ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateChanged.Source source, public bool ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateSource source,
out CustomizeValue old, uint key = 0) out CustomizeValue old, uint key = 0)
{ {
old = state.ModelData.Customize[idx]; old = state.ModelData.Customize[idx];
@ -82,12 +82,12 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
return false; return false;
state.ModelData.Customize[idx] = value; state.ModelData.Customize[idx] = value;
state.Source[idx] = source; state.Sources[idx] = source;
return true; return true;
} }
/// <summary> Change an entire customization array according to flags. </summary> /// <summary> Change an entire customization array according to flags. </summary>
public bool ChangeHumanCustomize(ActorState state, in CustomizeArray customizeInput, CustomizeFlag applyWhich, StateChanged.Source source, public bool ChangeHumanCustomize(ActorState state, in CustomizeArray customizeInput, CustomizeFlag applyWhich, StateSource source,
out CustomizeArray old, out CustomizeFlag changed, uint key = 0) out CustomizeArray old, out CustomizeFlag changed, uint key = 0)
{ {
old = state.ModelData.Customize; old = state.ModelData.Customize;
@ -104,14 +104,14 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
foreach (var type in Enum.GetValues<CustomizeIndex>()) foreach (var type in Enum.GetValues<CustomizeIndex>())
{ {
if (applied.HasFlag(type.ToFlag())) if (applied.HasFlag(type.ToFlag()))
state.Source[type] = source; state.Sources[type] = source;
} }
return true; return true;
} }
/// <summary> Change a single piece of equipment without stain. </summary> /// <summary> Change a single piece of equipment without stain. </summary>
public bool ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, out EquipItem oldItem, uint key = 0) public bool ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateSource source, out EquipItem oldItem, uint key = 0)
{ {
oldItem = state.ModelData.Item(slot); oldItem = state.ModelData.Item(slot);
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
@ -128,17 +128,17 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
gPose.AddActionOnLeave(() => gPose.AddActionOnLeave(() =>
{ {
if (old.Type == state.BaseData.Item(slot).Type) if (old.Type == state.BaseData.Item(slot).Type)
ChangeItem(state, slot, old, state.Source[slot, false], out _, key); ChangeItem(state, slot, old, state.Sources[slot, false], out _, key);
}); });
} }
state.ModelData.SetItem(slot, item); state.ModelData.SetItem(slot, item);
state.Source[slot, false] = source; state.Sources[slot, false] = source;
return true; return true;
} }
/// <summary> Change a single piece of equipment including stain. </summary> /// <summary> Change a single piece of equipment including stain. </summary>
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, out EquipItem oldItem, public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateSource source, out EquipItem oldItem,
out StainId oldStain, uint key = 0) out StainId oldStain, uint key = 0)
{ {
oldItem = state.ModelData.Item(slot); oldItem = state.ModelData.Item(slot);
@ -158,43 +158,43 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
gPose.AddActionOnLeave(() => gPose.AddActionOnLeave(() =>
{ {
if (old.Type == state.BaseData.Item(slot).Type) if (old.Type == state.BaseData.Item(slot).Type)
ChangeEquip(state, slot, old, oldS, state.Source[slot, false], out _, out _, key); ChangeEquip(state, slot, old, oldS, state.Sources[slot, false], out _, out _, key);
}); });
} }
state.ModelData.SetItem(slot, item); state.ModelData.SetItem(slot, item);
state.ModelData.SetStain(slot, stain); state.ModelData.SetStain(slot, stain);
state.Source[slot, false] = source; state.Sources[slot, false] = source;
state.Source[slot, true] = source; state.Sources[slot, true] = source;
return true; return true;
} }
/// <summary> Change only the stain of an equipment piece. </summary> /// <summary> Change only the stain of an equipment piece. </summary>
public bool ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateChanged.Source source, out StainId oldStain, uint key = 0) public bool ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateSource source, out StainId oldStain, uint key = 0)
{ {
oldStain = state.ModelData.Stain(slot); oldStain = state.ModelData.Stain(slot);
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
return false; return false;
state.ModelData.SetStain(slot, stain); state.ModelData.SetStain(slot, stain);
state.Source[slot, true] = source; state.Sources[slot, true] = source;
return true; return true;
} }
/// <summary> Change the crest of an equipment piece. </summary> /// <summary> Change the crest of an equipment piece. </summary>
public bool ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateChanged.Source source, out bool oldCrest, uint key = 0) public bool ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateSource source, out bool oldCrest, uint key = 0)
{ {
oldCrest = state.ModelData.Crest(slot); oldCrest = state.ModelData.Crest(slot);
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
return false; return false;
state.ModelData.SetCrest(slot, crest); state.ModelData.SetCrest(slot, crest);
state.Source[slot] = source; state.Sources[slot] = source;
return true; return true;
} }
/// <summary> Change the customize flags of a character. </summary> /// <summary> Change the customize flags of a character. </summary>
public bool ChangeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value, StateChanged.Source source, public bool ChangeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value, StateSource source,
out CustomizeParameterValue oldValue, uint key = 0) out CustomizeParameterValue oldValue, uint key = 0)
{ {
oldValue = state.ModelData.Parameters[flag]; oldValue = state.ModelData.Parameters[flag];
@ -202,29 +202,20 @@ public class StateEditor(CustomizeService customizations, HumanModelList humans,
return false; return false;
state.ModelData.Parameters.Set(flag, value); state.ModelData.Parameters.Set(flag, value);
state.Source[flag] = source; state.Sources[flag] = source;
return true; return true;
} }
public bool ChangeMetaState(ActorState state, MetaIndex index, bool value, StateChanged.Source source, out bool oldValue, public bool ChangeMetaState(ActorState state, MetaIndex index, bool value, StateSource source, out bool oldValue,
uint key = 0) uint key = 0)
{ {
(var setter, oldValue) = index switch oldValue = state.ModelData.GetMeta(index);
{
MetaIndex.Wetness => ((Func<bool, bool>)(v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
MetaIndex.HatState => ((Func<bool, bool>)(v => state.ModelData.SetHatVisible(v)), state.ModelData.IsHatVisible()),
MetaIndex.VisorState => ((Func<bool, bool>)(v => state.ModelData.SetVisor(v)), state.ModelData.IsVisorToggled()),
MetaIndex.WeaponState => ((Func<bool, bool>)(v => state.ModelData.SetWeaponVisible(v)),
state.ModelData.IsWeaponVisible()),
_ => throw new Exception("Invalid MetaIndex."),
};
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
return false; return false;
setter(value); state.ModelData.SetMeta(index, value);
state.Source[index] = source; state.Sources[index] = source;
return true; return true;
} }
} }

View file

@ -0,0 +1,235 @@
using Glamourer.Designs;
using Glamourer.GameData;
using Penumbra.GameData.Enums;
namespace Glamourer.State;
public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIndex, StateIndex, bool>
{
public static readonly StateIndex Invalid = new(-1);
public static implicit operator StateIndex(EquipFlag flag)
=> flag switch
{
EquipFlag.Head => new StateIndex(EquipHead),
EquipFlag.Body => new StateIndex(EquipBody),
EquipFlag.Hands => new StateIndex(EquipHands),
EquipFlag.Legs => new StateIndex(EquipLegs),
EquipFlag.Feet => new StateIndex(EquipFeet),
EquipFlag.Ears => new StateIndex(EquipEars),
EquipFlag.Neck => new StateIndex(EquipNeck),
EquipFlag.Wrist => new StateIndex(EquipWrist),
EquipFlag.RFinger => new StateIndex(EquipRFinger),
EquipFlag.LFinger => new StateIndex(EquipLFinger),
EquipFlag.Mainhand => new StateIndex(EquipMainhand),
EquipFlag.Offhand => new StateIndex(EquipOffhand),
EquipFlag.HeadStain => new StateIndex(StainHead),
EquipFlag.BodyStain => new StateIndex(StainBody),
EquipFlag.HandsStain => new StateIndex(StainHands),
EquipFlag.LegsStain => new StateIndex(StainLegs),
EquipFlag.FeetStain => new StateIndex(StainFeet),
EquipFlag.EarsStain => new StateIndex(StainEars),
EquipFlag.NeckStain => new StateIndex(StainNeck),
EquipFlag.WristStain => new StateIndex(StainWrist),
EquipFlag.RFingerStain => new StateIndex(StainRFinger),
EquipFlag.LFingerStain => new StateIndex(StainLFinger),
EquipFlag.MainhandStain => new StateIndex(StainMainhand),
EquipFlag.OffhandStain => new StateIndex(StainOffhand),
_ => Invalid,
};
public static implicit operator StateIndex(CustomizeIndex index)
=> index switch
{
CustomizeIndex.Race => new StateIndex(CustomizeRace),
CustomizeIndex.Gender => new StateIndex(CustomizeGender),
CustomizeIndex.BodyType => new StateIndex(CustomizeBodyType),
CustomizeIndex.Height => new StateIndex(CustomizeHeight),
CustomizeIndex.Clan => new StateIndex(CustomizeClan),
CustomizeIndex.Face => new StateIndex(CustomizeFace),
CustomizeIndex.Hairstyle => new StateIndex(CustomizeHairstyle),
CustomizeIndex.Highlights => new StateIndex(CustomizeHighlights),
CustomizeIndex.SkinColor => new StateIndex(CustomizeSkinColor),
CustomizeIndex.EyeColorRight => new StateIndex(CustomizeEyeColorRight),
CustomizeIndex.HairColor => new StateIndex(CustomizeHairColor),
CustomizeIndex.HighlightsColor => new StateIndex(CustomizeHighlightsColor),
CustomizeIndex.FacialFeature1 => new StateIndex(CustomizeFacialFeature1),
CustomizeIndex.FacialFeature2 => new StateIndex(CustomizeFacialFeature2),
CustomizeIndex.FacialFeature3 => new StateIndex(CustomizeFacialFeature3),
CustomizeIndex.FacialFeature4 => new StateIndex(CustomizeFacialFeature4),
CustomizeIndex.FacialFeature5 => new StateIndex(CustomizeFacialFeature5),
CustomizeIndex.FacialFeature6 => new StateIndex(CustomizeFacialFeature6),
CustomizeIndex.FacialFeature7 => new StateIndex(CustomizeFacialFeature7),
CustomizeIndex.LegacyTattoo => new StateIndex(CustomizeLegacyTattoo),
CustomizeIndex.TattooColor => new StateIndex(CustomizeTattooColor),
CustomizeIndex.Eyebrows => new StateIndex(CustomizeEyebrows),
CustomizeIndex.EyeColorLeft => new StateIndex(CustomizeEyeColorLeft),
CustomizeIndex.EyeShape => new StateIndex(CustomizeEyeShape),
CustomizeIndex.SmallIris => new StateIndex(CustomizeSmallIris),
CustomizeIndex.Nose => new StateIndex(CustomizeNose),
CustomizeIndex.Jaw => new StateIndex(CustomizeJaw),
CustomizeIndex.Mouth => new StateIndex(CustomizeMouth),
CustomizeIndex.Lipstick => new StateIndex(CustomizeLipstick),
CustomizeIndex.LipColor => new StateIndex(CustomizeLipColor),
CustomizeIndex.MuscleMass => new StateIndex(CustomizeMuscleMass),
CustomizeIndex.TailShape => new StateIndex(CustomizeTailShape),
CustomizeIndex.BustSize => new StateIndex(CustomizeBustSize),
CustomizeIndex.FacePaint => new StateIndex(CustomizeFacePaint),
CustomizeIndex.FacePaintReversed => new StateIndex(CustomizeFacePaintReversed),
CustomizeIndex.FacePaintColor => new StateIndex(CustomizeFacePaintColor),
_ => Invalid,
};
public static implicit operator StateIndex(MetaIndex meta)
=> new((int)meta);
public static implicit operator StateIndex(CrestFlag crest)
=> crest switch
{
CrestFlag.OffHand => new StateIndex(CrestOffhand),
CrestFlag.Head => new StateIndex(CrestHead),
CrestFlag.Body => new StateIndex(CrestBody),
_ => Invalid,
};
public static implicit operator StateIndex(CustomizeParameterFlag param)
=> param switch
{
CustomizeParameterFlag.SkinDiffuse => new StateIndex(ParamSkinDiffuse),
CustomizeParameterFlag.MuscleTone => new StateIndex(ParamMuscleTone),
CustomizeParameterFlag.SkinSpecular => new StateIndex(ParamSkinSpecular),
CustomizeParameterFlag.LipDiffuse => new StateIndex(ParamLipDiffuse),
CustomizeParameterFlag.HairDiffuse => new StateIndex(ParamHairDiffuse),
CustomizeParameterFlag.HairSpecular => new StateIndex(ParamHairSpecular),
CustomizeParameterFlag.HairHighlight => new StateIndex(ParamHairHighlight),
CustomizeParameterFlag.LeftEye => new StateIndex(ParamLeftEye),
CustomizeParameterFlag.RightEye => new StateIndex(ParamRightEye),
CustomizeParameterFlag.FeatureColor => new StateIndex(ParamFeatureColor),
CustomizeParameterFlag.FacePaintUvMultiplier => new StateIndex(ParamFacePaintUvMultiplier),
CustomizeParameterFlag.FacePaintUvOffset => new StateIndex(ParamFacePaintUvOffset),
CustomizeParameterFlag.DecalColor => new StateIndex(ParamDecalColor),
_ => Invalid,
};
public const int EquipHead = 0;
public const int EquipBody = 1;
public const int EquipHands = 2;
public const int EquipLegs = 3;
public const int EquipFeet = 4;
public const int EquipEars = 5;
public const int EquipNeck = 6;
public const int EquipWrist = 7;
public const int EquipRFinger = 8;
public const int EquipLFinger = 9;
public const int EquipMainhand = 10;
public const int EquipOffhand = 11;
public const int StainHead = 12;
public const int StainBody = 13;
public const int StainHands = 14;
public const int StainLegs = 15;
public const int StainFeet = 16;
public const int StainEars = 17;
public const int StainNeck = 18;
public const int StainWrist = 19;
public const int StainRFinger = 20;
public const int StainLFinger = 21;
public const int StainMainhand = 22;
public const int StainOffhand = 23;
public const int CustomizeRace = 24;
public const int CustomizeGender = 25;
public const int CustomizeBodyType = 26;
public const int CustomizeHeight = 27;
public const int CustomizeClan = 28;
public const int CustomizeFace = 29;
public const int CustomizeHairstyle = 30;
public const int CustomizeHighlights = 31;
public const int CustomizeSkinColor = 32;
public const int CustomizeEyeColorRight = 33;
public const int CustomizeHairColor = 34;
public const int CustomizeHighlightsColor = 35;
public const int CustomizeFacialFeature1 = 36;
public const int CustomizeFacialFeature2 = 37;
public const int CustomizeFacialFeature3 = 38;
public const int CustomizeFacialFeature4 = 39;
public const int CustomizeFacialFeature5 = 40;
public const int CustomizeFacialFeature6 = 41;
public const int CustomizeFacialFeature7 = 42;
public const int CustomizeLegacyTattoo = 43;
public const int CustomizeTattooColor = 44;
public const int CustomizeEyebrows = 45;
public const int CustomizeEyeColorLeft = 46;
public const int CustomizeEyeShape = 47;
public const int CustomizeSmallIris = 48;
public const int CustomizeNose = 49;
public const int CustomizeJaw = 50;
public const int CustomizeMouth = 51;
public const int CustomizeLipstick = 52;
public const int CustomizeLipColor = 53;
public const int CustomizeMuscleMass = 54;
public const int CustomizeTailShape = 55;
public const int CustomizeBustSize = 56;
public const int CustomizeFacePaint = 57;
public const int CustomizeFacePaintReversed = 58;
public const int CustomizeFacePaintColor = 59;
public const int MetaWetness = 60;
public const int MetaHatState = 61;
public const int MetaVisorState = 62;
public const int MetaWeaponState = 63;
public const int MetaModelId = 64;
public const int CrestHead = 65;
public const int CrestBody = 66;
public const int CrestOffhand = 67;
public const int ParamSkinDiffuse = 68;
public const int ParamMuscleTone = 69;
public const int ParamSkinSpecular = 70;
public const int ParamLipDiffuse = 71;
public const int ParamHairDiffuse = 72;
public const int ParamHairSpecular = 73;
public const int ParamHairHighlight = 74;
public const int ParamLeftEye = 75;
public const int ParamRightEye = 76;
public const int ParamFeatureColor = 77;
public const int ParamFacePaintUvMultiplier = 78;
public const int ParamFacePaintUvOffset = 79;
public const int ParamDecalColor = 80;
public const int Size = 81;
}
public static class StateExtensions
{
public static StateIndex ToState(this EquipSlot slot, bool stain = false)
=> (slot, stain) switch
{
(EquipSlot.Head, true) => new StateIndex(StateIndex.EquipHead),
(EquipSlot.Body, true) => new StateIndex(StateIndex.EquipBody),
(EquipSlot.Hands, true) => new StateIndex(StateIndex.EquipHands),
(EquipSlot.Legs, true) => new StateIndex(StateIndex.EquipLegs),
(EquipSlot.Feet, true) => new StateIndex(StateIndex.EquipFeet),
(EquipSlot.Ears, true) => new StateIndex(StateIndex.EquipEars),
(EquipSlot.Neck, true) => new StateIndex(StateIndex.EquipNeck),
(EquipSlot.Wrists, true) => new StateIndex(StateIndex.EquipWrist),
(EquipSlot.RFinger, true) => new StateIndex(StateIndex.EquipRFinger),
(EquipSlot.LFinger, true) => new StateIndex(StateIndex.EquipLFinger),
(EquipSlot.MainHand, true) => new StateIndex(StateIndex.EquipMainhand),
(EquipSlot.OffHand, true) => new StateIndex(StateIndex.EquipOffhand),
(EquipSlot.Head, false) => new StateIndex(StateIndex.StainHead),
(EquipSlot.Body, false) => new StateIndex(StateIndex.StainBody),
(EquipSlot.Hands, false) => new StateIndex(StateIndex.StainHands),
(EquipSlot.Legs, false) => new StateIndex(StateIndex.StainLegs),
(EquipSlot.Feet, false) => new StateIndex(StateIndex.StainFeet),
(EquipSlot.Ears, false) => new StateIndex(StateIndex.StainEars),
(EquipSlot.Neck, false) => new StateIndex(StateIndex.StainNeck),
(EquipSlot.Wrists, false) => new StateIndex(StateIndex.StainWrist),
(EquipSlot.RFinger, false) => new StateIndex(StateIndex.StainRFinger),
(EquipSlot.LFinger, false) => new StateIndex(StateIndex.StainLFinger),
(EquipSlot.MainHand, false) => new StateIndex(StateIndex.StainMainhand),
(EquipSlot.OffHand, false) => new StateIndex(StateIndex.StainOffhand),
_ => StateIndex.Invalid,
};
}

View file

@ -164,21 +164,21 @@ public class StateListener : IDisposable
var model = state.ModelData.Customize; var model = state.ModelData.Customize;
if (customize.Gender != model.Gender || customize.Clan != model.Clan) if (customize.Gender != model.Gender || customize.Clan != model.Clan)
{ {
_manager.ChangeCustomize(state, in customize, CustomizeFlagExtensions.AllRelevant, StateChanged.Source.Game); _manager.ChangeCustomize(state, in customize, CustomizeFlagExtensions.AllRelevant, StateSource.Game);
return; return;
} }
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.Source[index] is not StateChanged.Source.Fixed) if (state.Sources[index] is not StateSource.Fixed)
{ {
var newValue = customize[index]; var newValue = customize[index];
var oldValue = model[index]; var oldValue = model[index];
if (newValue != oldValue) if (newValue != oldValue)
{ {
if (set.Validate(index, newValue, out _, model.Face)) if (set.Validate(index, newValue, out _, model.Face))
_manager.ChangeCustomize(state, index, newValue, StateChanged.Source.Game); _manager.ChangeCustomize(state, index, newValue, StateSource.Game);
else else
customize[index] = oldValue; customize[index] = oldValue;
} }
@ -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.Source[slot, false] is StateChanged.Source.Ipc; locked = state.Sources[slot, false] is StateSource.Ipc;
} }
_funModule.ApplyFunToSlot(actor, ref armor, slot); _funModule.ApplyFunToSlot(actor, ref armor, slot);
@ -241,10 +241,10 @@ public class StateListener : IDisposable
continue; continue;
var changed = changedItem.Weapon(stain); var changed = changedItem.Weapon(stain);
if (current.Value == changed.Value && state.Source[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc) if (current.Value == changed.Value && state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
{ {
_manager.ChangeItem(state, slot, currentItem, StateChanged.Source.Game); _manager.ChangeItem(state, slot, currentItem, StateSource.Game);
_manager.ChangeStain(state, slot, current.Stain, StateChanged.Source.Game); _manager.ChangeStain(state, slot, current.Stain, StateSource.Game);
switch (slot) switch (slot)
{ {
case EquipSlot.MainHand: case EquipSlot.MainHand:
@ -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.Source[slot, false] is not StateChanged.Source.Ipc, _applier.ChangeArmor(objects, slot, current.ToArmor(), state.Sources[slot, false] is not StateSource.Ipc,
state.ModelData.IsHatVisible()); state.ModelData.IsHatVisible());
break; break;
} }
@ -286,13 +286,13 @@ public class StateListener : IDisposable
// 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.Source[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc) if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game); _manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateSource.Game);
else else
apply = true; apply = true;
if (state.Source[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc) if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game); _manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateSource.Game);
else else
apply = true; apply = true;
break; break;
@ -385,13 +385,13 @@ 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.Source[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc) if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game); _manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateSource.Game);
else else
apply = true; apply = true;
if (state.Source[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc) if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game); _manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateSource.Game);
else else
apply = true; apply = true;
@ -419,8 +419,8 @@ public class StateListener : IDisposable
switch (UpdateBaseCrest(actor, state, slot, value)) switch (UpdateBaseCrest(actor, state, slot, value))
{ {
case UpdateState.Change: case UpdateState.Change:
if (state.Source[slot] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc) if (state.Sources[slot] is not StateSource.Fixed and not StateSource.Ipc)
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game); _manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateSource.Game);
else else
value = state.ModelData.Crest(slot); value = state.ModelData.Crest(slot);
break; break;
@ -565,10 +565,10 @@ 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.Source[MetaIndex.VisorState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc) if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed or StateSource.Ipc)
value = state.ModelData.IsVisorToggled(); value = state.ModelData.IsVisorToggled();
else else
_manager.ChangeVisorState(state, value, StateChanged.Source.Game); _manager.ChangeMeta(state, MetaIndex.VisorState, value, StateSource.Game);
} }
else else
{ {
@ -598,10 +598,10 @@ 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.Source[MetaIndex.HatState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc) if (state.Sources[MetaIndex.HatState] is StateSource.Fixed or StateSource.Ipc)
value = state.ModelData.IsHatVisible(); value = state.ModelData.IsHatVisible();
else else
_manager.ChangeHatState(state, value, StateChanged.Source.Game); _manager.ChangeMeta(state, MetaIndex.HatState, value, StateSource.Game);
} }
else else
{ {
@ -631,10 +631,10 @@ 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.Source[MetaIndex.WeaponState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc) if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed or StateSource.Ipc)
value = state.ModelData.IsWeaponVisible(); value = state.ModelData.IsWeaponVisible();
else else
_manager.ChangeWeaponState(state, value, StateChanged.Source.Game); _manager.ChangeMeta(state, MetaIndex.WeaponState, value, StateSource.Game);
} }
else else
{ {
@ -700,9 +700,9 @@ public class StateListener : IDisposable
return; return;
var data = new ActorData(gameObject, _creatingIdentifier.ToName()); var data = new ActorData(gameObject, _creatingIdentifier.ToName());
_applier.ChangeHatState(data, _creatingState.ModelData.IsHatVisible()); _applier.ChangeMetaState(data, MetaIndex.HatState, _creatingState.ModelData.IsHatVisible());
_applier.ChangeWeaponState(data, _creatingState.ModelData.IsWeaponVisible()); _applier.ChangeMetaState(data, MetaIndex.Wetness, _creatingState.ModelData.IsWet());
_applier.ChangeWetness(data, _creatingState.ModelData.IsWet()); _applier.ChangeMetaState(data, MetaIndex.WeaponState, _creatingState.ModelData.IsWeaponVisible());
ApplyParameters(_creatingState, drawObject); ApplyParameters(_creatingState, drawObject);
} }
@ -733,30 +733,30 @@ public class StateListener : IDisposable
foreach (var flag in CustomizeParameterExtensions.AllFlags) foreach (var flag in CustomizeParameterExtensions.AllFlags)
{ {
var newValue = data[flag]; var newValue = data[flag];
switch (state.Source[flag]) switch (state.Sources[flag])
{ {
case StateChanged.Source.Game: case StateSource.Game:
if (state.BaseData.Parameters.Set(flag, newValue)) if (state.BaseData.Parameters.Set(flag, newValue))
_manager.ChangeCustomizeParameter(state, flag, newValue, StateChanged.Source.Game); _manager.ChangeCustomizeParameter(state, flag, newValue, StateSource.Game);
break; break;
case StateChanged.Source.Manual: case StateSource.Manual:
if (state.BaseData.Parameters.Set(flag, newValue)) if (state.BaseData.Parameters.Set(flag, newValue))
_manager.ChangeCustomizeParameter(state, flag, newValue, StateChanged.Source.Game); _manager.ChangeCustomizeParameter(state, flag, newValue, StateSource.Game);
else if (_config.UseAdvancedParameters) else if (_config.UseAdvancedParameters)
model.ApplySingleParameterData(flag, state.ModelData.Parameters); model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break; break;
case StateChanged.Source.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 StateChanged.Source.Ipc: case StateSource.Ipc:
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;
case StateChanged.Source.Pending: case StateSource.Pending:
state.BaseData.Parameters.Set(flag, newValue); state.BaseData.Parameters.Set(flag, newValue);
state.Source[flag] = StateChanged.Source.Manual; state.Sources[flag] = StateSource.Manual;
if (_config.UseAdvancedParameters) if (_config.UseAdvancedParameters)
model.ApplySingleParameterData(flag, state.ModelData.Parameters); model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break; break;

View file

@ -207,58 +207,58 @@ public class StateManager(
#region Change Values #region Change Values
/// <summary> Turn an actor human. </summary> /// <summary> Turn an actor human. </summary>
public void TurnHuman(ActorState state, StateChanged.Source source, uint key = 0) public void TurnHuman(ActorState state, StateSource source, uint key = 0)
=> ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key); => ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key);
/// <summary> Turn an actor to. </summary> /// <summary> Turn an actor to. </summary>
public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateChanged.Source source, public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source,
uint key = 0) uint key = 0)
{ {
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 StateChanged.Source.Manual or StateChanged.Source.Ipc); var actors = _applier.ForceRedraw(state, source is StateSource.Manual or StateSource.Ipc);
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")}.]");
_event.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId)); _event.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId));
} }
/// <summary> Change a customization value. </summary> /// <summary> Change a customization value. </summary>
public void ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateChanged.Source source, uint key = 0) public void ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateSource source, uint key = 0)
{ {
if (!_editor.ChangeCustomize(state, idx, value, source, out var old, key)) if (!_editor.ChangeCustomize(state, idx, value, source, out var old, key))
return; return;
var actors = _applier.ChangeCustomize(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc); var actors = _applier.ChangeCustomize(state, source is StateSource.Manual or StateSource.Ipc);
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")}.]");
_event.Invoke(StateChanged.Type.Customize, source, state, actors, (old, value, idx)); _event.Invoke(StateChanged.Type.Customize, source, state, actors, (old, value, idx));
} }
/// <summary> Change an entire customization array according to flags. </summary> /// <summary> Change an entire customization array according to flags. </summary>
public void ChangeCustomize(ActorState state, in CustomizeArray customizeInput, CustomizeFlag apply, StateChanged.Source source, public void ChangeCustomize(ActorState state, in CustomizeArray customizeInput, CustomizeFlag apply, StateSource source,
uint key = 0) uint key = 0)
{ {
if (!_editor.ChangeHumanCustomize(state, customizeInput, apply, source, out var old, out var applied, key)) if (!_editor.ChangeHumanCustomize(state, customizeInput, apply, source, out var old, out var applied, key))
return; return;
var actors = _applier.ChangeCustomize(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc); var actors = _applier.ChangeCustomize(state, source is StateSource.Manual or StateSource.Ipc);
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")}.]");
_event.Invoke(StateChanged.Type.EntireCustomize, source, state, actors, (old, applied)); _event.Invoke(StateChanged.Type.EntireCustomize, source, state, actors, (old, applied));
} }
/// <summary> Change a single piece of equipment without stain. </summary> /// <summary> Change a single piece of equipment without stain. </summary>
/// <remarks> Do not use this in the same frame as ChangeStain, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks> /// <remarks> Do not use this in the same frame as ChangeStain, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateSource,uint)"/> instead. </remarks>
public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, uint key = 0) public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateSource source, uint key = 0)
{ {
if (!_editor.ChangeItem(state, slot, item, source, out var old, key)) if (!_editor.ChangeItem(state, slot, item, source, out var old, key))
return; return;
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, source is StateChanged.Source.Manual or StateChanged.Source.Ipc) ? _applier.ChangeArmor(state, slot, source is StateSource.Manual or StateSource.Ipc)
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc, : _applier.ChangeWeapon(state, slot, source is StateSource.Manual or StateSource.Ipc,
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType)); item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]");
@ -266,15 +266,15 @@ public class StateManager(
} }
/// <summary> Change a single piece of equipment including stain. </summary> /// <summary> Change a single piece of equipment including stain. </summary>
public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, uint key = 0) public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateSource source, uint key = 0)
{ {
if (!_editor.ChangeEquip(state, slot, item, stain, source, out var old, out var oldStain, key)) if (!_editor.ChangeEquip(state, slot, item, stain, source, out var old, out var oldStain, key))
return; return;
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, source is StateChanged.Source.Manual or StateChanged.Source.Ipc) ? _applier.ChangeArmor(state, slot, source is StateSource.Manual or StateSource.Ipc)
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc, : _applier.ChangeWeapon(state, slot, source is StateSource.Manual or StateSource.Ipc,
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType)); item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}) and its stain from {oldStain.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}) and its stain from {oldStain.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
@ -283,25 +283,25 @@ public class StateManager(
} }
/// <summary> Change only the stain of an equipment piece. </summary> /// <summary> Change only the stain of an equipment piece. </summary>
/// <remarks> Do not use this in the same frame as ChangeEquip, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks> /// <remarks> Do not use this in the same frame as ChangeEquip, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateSource,uint)"/> instead. </remarks>
public void ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateChanged.Source source, uint key = 0) public void ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateSource source, uint key = 0)
{ {
if (!_editor.ChangeStain(state, slot, stain, source, out var old, key)) if (!_editor.ChangeStain(state, slot, stain, source, out var old, key))
return; return;
var actors = _applier.ChangeStain(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc); var actors = _applier.ChangeStain(state, slot, source is StateSource.Manual or StateSource.Ipc);
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")}.]");
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot)); _event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot));
} }
/// <summary> Change the crest of an equipment piece. </summary> /// <summary> Change the crest of an equipment piece. </summary>
public void ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateChanged.Source source, uint key = 0) public void ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateSource source, uint key = 0)
{ {
if (!_editor.ChangeCrest(state, slot, crest, source, out var old, key)) if (!_editor.ChangeCrest(state, slot, crest, source, out var old, key))
return; return;
var actors = _applier.ChangeCrests(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc); var actors = _applier.ChangeCrests(state, source is StateSource.Manual or StateSource.Ipc);
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")}.]");
_event.Invoke(StateChanged.Type.Crest, source, state, actors, (old, crest, slot)); _event.Invoke(StateChanged.Type.Crest, source, state, actors, (old, crest, slot));
@ -309,7 +309,7 @@ public class StateManager(
/// <summary> Change the crest of an equipment piece. </summary> /// <summary> Change the crest of an equipment piece. </summary>
public void ChangeCustomizeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value, public void ChangeCustomizeParameter(ActorState state, CustomizeParameterFlag flag, CustomizeParameterValue value,
StateChanged.Source source, uint key = 0) StateSource source, uint key = 0)
{ {
// Also apply main color to highlights when highlights is off. // Also apply main color to highlights when highlights is off.
if (!state.ModelData.Customize.Highlights && flag is CustomizeParameterFlag.HairDiffuse) if (!state.ModelData.Customize.Highlights && flag is CustomizeParameterFlag.HairDiffuse)
@ -319,63 +319,27 @@ public class StateManager(
return; return;
var @new = state.ModelData.Parameters[flag]; var @new = state.ModelData.Parameters[flag];
var actors = _applier.ChangeParameters(state, flag, source is StateChanged.Source.Manual or StateChanged.Source.Ipc); var actors = _applier.ChangeParameters(state, flag, source is StateSource.Manual or StateSource.Ipc);
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")}.]");
_event.Invoke(StateChanged.Type.Parameter, source, state, actors, (old, @new, flag)); _event.Invoke(StateChanged.Type.Parameter, source, state, actors, (old, @new, flag));
} }
/// <summary> Change hat visibility. </summary> /// <summary> Change meta state. </summary>
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0) public void ChangeMeta(ActorState state, MetaIndex meta, bool value, StateSource source, uint key = 0)
{ {
if (!_editor.ChangeMetaState(state, MetaIndex.HatState, value, source, out var old, key)) if (!_editor.ChangeMetaState(state, meta, value, source, out var old, key))
return; return;
var actors = _applier.ChangeHatState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc); var actors = _applier.ChangeMetaState(state, meta, source is StateSource.Manual or StateSource.Ipc);
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")}.]");
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.HatState)); _event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.HatState));
} }
/// <summary> Change weapon visibility. </summary>
public void ChangeWeaponState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
{
if (!_editor.ChangeMetaState(state, MetaIndex.WeaponState, value, source, out var old, key))
return;
var actors = _applier.ChangeWeaponState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
Glamourer.Log.Verbose(
$"Set Weapon Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.WeaponState));
}
/// <summary> Change visor state. </summary>
public void ChangeVisorState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
{
if (!_editor.ChangeMetaState(state, MetaIndex.VisorState, value, source, out var old, key))
return;
var actors = _applier.ChangeVisor(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
Glamourer.Log.Verbose(
$"Set Visor State in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.VisorState));
}
/// <summary> Set GPose Wetness. </summary>
public void ChangeWetness(ActorState state, bool value, StateChanged.Source source, uint key = 0)
{
if (!_editor.ChangeMetaState(state, MetaIndex.Wetness, value, source, out var old, key))
return;
var actors = _applier.ChangeWetness(state, true);
Glamourer.Log.Verbose(
$"Set Wetness in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(StateChanged.Type.Other, state.Source[MetaIndex.Wetness], state, actors, (old, value, MetaIndex.Wetness));
}
#endregion #endregion
public void ApplyDesign(DesignBase design, ActorState state, StateChanged.Source source, uint key = 0) public void ApplyDesign(DesignBase design, ActorState state, StateSource source, uint key = 0)
{ {
if (!_editor.ChangeModelId(state, design.DesignData.ModelId, design.DesignData.Customize, design.GetDesignDataRef().GetEquipmentPtr(), if (!_editor.ChangeModelId(state, design.DesignData.ModelId, design.DesignData.Customize, design.GetDesignDataRef().GetEquipmentPtr(),
source, source,
@ -383,16 +347,16 @@ public class StateManager(
return; return;
var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman; var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman;
if (design.DoApplyWetness()) if (design.DoApplyMeta(MetaIndex.Wetness))
_editor.ChangeMetaState(state, MetaIndex.Wetness, design.DesignData.IsWet(), source, out _, key); _editor.ChangeMetaState(state, MetaIndex.Wetness, design.DesignData.IsWet(), source, out _, key);
if (state.ModelData.IsHuman) if (state.ModelData.IsHuman)
{ {
if (design.DoApplyHatVisible()) if (design.DoApplyMeta(MetaIndex.HatState))
_editor.ChangeMetaState(state, MetaIndex.HatState, design.DesignData.IsHatVisible(), source, out _, key); _editor.ChangeMetaState(state, MetaIndex.HatState, design.DesignData.IsHatVisible(), source, out _, key);
if (design.DoApplyWeaponVisible()) if (design.DoApplyMeta(MetaIndex.WeaponState))
_editor.ChangeMetaState(state, MetaIndex.WeaponState, design.DesignData.IsWeaponVisible(), source, out _, key); _editor.ChangeMetaState(state, MetaIndex.WeaponState, design.DesignData.IsWeaponVisible(), source, out _, key);
if (design.DoApplyVisorToggle()) if (design.DoApplyMeta(MetaIndex.VisorState))
_editor.ChangeMetaState(state, MetaIndex.VisorState, design.DesignData.IsVisorToggled(), source, out _, key); _editor.ChangeMetaState(state, MetaIndex.VisorState, design.DesignData.IsVisorToggled(), source, out _, key);
var flags = state.AllowsRedraw(_condition) var flags = state.AllowsRedraw(_condition)
@ -407,8 +371,8 @@ public class StateManager(
foreach (var slot in CrestExtensions.AllRelevantSet.Where(design.DoApplyCrest)) foreach (var slot in CrestExtensions.AllRelevantSet.Where(design.DoApplyCrest))
_editor.ChangeCrest(state, slot, design.DesignData.Crest(slot), source, out _, key); _editor.ChangeCrest(state, slot, design.DesignData.Crest(slot), source, out _, key);
var paramSource = source is StateChanged.Source.Manual var paramSource = source is StateSource.Manual
? StateChanged.Source.Pending ? StateSource.Pending
: source; : source;
foreach (var flag in CustomizeParameterExtensions.AllFlags.Where(design.DoApplyParameter)) foreach (var flag in CustomizeParameterExtensions.AllFlags.Where(design.DoApplyParameter))
@ -418,13 +382,13 @@ public class StateManager(
if (!state.ModelData.Customize.Highlights) if (!state.ModelData.Customize.Highlights)
_editor.ChangeParameter(state, CustomizeParameterFlag.HairHighlight, _editor.ChangeParameter(state, CustomizeParameterFlag.HairHighlight,
state.ModelData.Parameters[CustomizeParameterFlag.HairDiffuse], state.ModelData.Parameters[CustomizeParameterFlag.HairDiffuse],
state.Source[CustomizeParameterFlag.HairDiffuse], out _, key); state.Sources[CustomizeParameterFlag.HairDiffuse], out _, key);
} }
var actors = ApplyAll(state, redraw, false); var actors = ApplyAll(state, redraw, false);
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(StateChanged.Type.Design, state.Source[MetaIndex.Wetness], state, actors, design); _event.Invoke(StateChanged.Type.Design, state.Sources[MetaIndex.Wetness], state, actors, design);
return; return;
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain) void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
@ -442,7 +406,7 @@ public class StateManager(
private ActorData ApplyAll(ActorState state, bool redraw, bool withLock) private ActorData ApplyAll(ActorState state, bool redraw, bool withLock)
{ {
var actors = _applier.ChangeWetness(state, true); var actors = _applier.ChangeMetaState(state, MetaIndex.Wetness, true);
if (redraw) if (redraw)
{ {
if (withLock) if (withLock)
@ -454,7 +418,7 @@ public class StateManager(
_applier.ChangeCustomize(actors, state.ModelData.Customize); _applier.ChangeCustomize(actors, state.ModelData.Customize);
foreach (var slot in EquipSlotExtensions.EqdpSlots) foreach (var slot in EquipSlotExtensions.EqdpSlots)
{ {
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Source[slot, false] is not StateChanged.Source.Ipc, _applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc,
state.ModelData.IsHatVisible()); state.ModelData.IsHatVisible());
} }
@ -466,9 +430,9 @@ public class StateManager(
if (state.ModelData.IsHuman) if (state.ModelData.IsHuman)
{ {
_applier.ChangeHatState(actors, state.ModelData.IsHatVisible()); _applier.ChangeMetaState(actors, MetaIndex.HatState, state.ModelData.IsHatVisible());
_applier.ChangeWeaponState(actors, state.ModelData.IsWeaponVisible()); _applier.ChangeMetaState(actors, MetaIndex.WeaponState, state.ModelData.IsWeaponVisible());
_applier.ChangeVisor(actors, state.ModelData.IsVisorToggled()); _applier.ChangeMetaState(actors, MetaIndex.VisorState, state.ModelData.IsVisorToggled());
_applier.ChangeCrests(actors, state.ModelData.CrestVisibility); _applier.ChangeCrests(actors, state.ModelData.CrestVisibility);
_applier.ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters, state.IsLocked); _applier.ChangeParameters(actors, state.OnlyChangedParameters(), state.ModelData.Parameters, state.IsLocked);
} }
@ -476,7 +440,7 @@ public class StateManager(
return actors; return actors;
} }
public void ResetState(ActorState state, StateChanged.Source source, uint key = 0) public void ResetState(ActorState state, StateSource source, uint key = 0)
{ {
if (!state.Unlock(key)) if (!state.Unlock(key))
return; return;
@ -488,25 +452,25 @@ public class StateManager(
state.ModelData = state.BaseData; state.ModelData = state.BaseData;
state.ModelData.SetIsWet(false); state.ModelData.SetIsWet(false);
foreach (var index in Enum.GetValues<CustomizeIndex>()) foreach (var index in Enum.GetValues<CustomizeIndex>())
state.Source[index] = StateChanged.Source.Game; state.Sources[index] = StateSource.Game;
foreach (var slot in EquipSlotExtensions.FullSlots) foreach (var slot in EquipSlotExtensions.FullSlots)
{ {
state.Source[slot, true] = StateChanged.Source.Game; state.Sources[slot, true] = StateSource.Game;
state.Source[slot, false] = StateChanged.Source.Game; state.Sources[slot, false] = StateSource.Game;
} }
foreach (var type in Enum.GetValues<MetaIndex>()) foreach (var type in Enum.GetValues<MetaIndex>())
state.Source[type] = StateChanged.Source.Game; state.Sources[type] = StateSource.Game;
foreach (var slot in CrestExtensions.AllRelevantSet) foreach (var slot in CrestExtensions.AllRelevantSet)
state.Source[slot] = StateChanged.Source.Game; state.Sources[slot] = StateSource.Game;
foreach (var flag in CustomizeParameterExtensions.AllFlags) foreach (var flag in CustomizeParameterExtensions.AllFlags)
state.Source[flag] = StateChanged.Source.Game; state.Sources[flag] = StateSource.Game;
var actors = ActorData.Invalid; var actors = ActorData.Invalid;
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc) if (source is StateSource.Manual or StateSource.Ipc)
actors = ApplyAll(state, redraw, true); actors = ApplyAll(state, redraw, true);
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
@ -514,7 +478,7 @@ public class StateManager(
_event.Invoke(StateChanged.Type.Reset, source, state, actors, null); _event.Invoke(StateChanged.Type.Reset, source, state, actors, null);
} }
public void ResetAdvancedState(ActorState state, StateChanged.Source source, uint key = 0) public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0)
{ {
if (!state.Unlock(key) || !state.ModelData.IsHuman) if (!state.Unlock(key) || !state.ModelData.IsHuman)
return; return;
@ -522,10 +486,10 @@ public class StateManager(
state.ModelData.Parameters = state.BaseData.Parameters; state.ModelData.Parameters = state.BaseData.Parameters;
foreach (var flag in CustomizeParameterExtensions.AllFlags) foreach (var flag in CustomizeParameterExtensions.AllFlags)
state.Source[flag] = StateChanged.Source.Game; state.Sources[flag] = StateSource.Game;
var actors = ActorData.Invalid; var actors = ActorData.Invalid;
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc) if (source is StateSource.Manual or StateSource.Ipc)
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")}.]");
@ -537,69 +501,69 @@ public class StateManager(
if (!state.Unlock(key)) if (!state.Unlock(key))
return; return;
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(i => state.Source[i] is StateChanged.Source.Fixed)) foreach (var index in Enum.GetValues<CustomizeIndex>().Where(i => state.Sources[i] is StateSource.Fixed))
{ {
state.Source[index] = StateChanged.Source.Game; state.Sources[index] = StateSource.Game;
state.ModelData.Customize[index] = state.BaseData.Customize[index]; state.ModelData.Customize[index] = state.BaseData.Customize[index];
} }
foreach (var slot in EquipSlotExtensions.FullSlots) foreach (var slot in EquipSlotExtensions.FullSlots)
{ {
if (state.Source[slot, true] is StateChanged.Source.Fixed) if (state.Sources[slot, true] is StateSource.Fixed)
{ {
state.Source[slot, true] = StateChanged.Source.Game; state.Sources[slot, true] = StateSource.Game;
state.ModelData.SetStain(slot, state.BaseData.Stain(slot)); state.ModelData.SetStain(slot, state.BaseData.Stain(slot));
} }
if (state.Source[slot, false] is StateChanged.Source.Fixed) if (state.Sources[slot, false] is StateSource.Fixed)
{ {
state.Source[slot, false] = StateChanged.Source.Game; state.Sources[slot, false] = StateSource.Game;
state.ModelData.SetItem(slot, state.BaseData.Item(slot)); state.ModelData.SetItem(slot, state.BaseData.Item(slot));
} }
} }
foreach (var slot in CrestExtensions.AllRelevantSet) foreach (var slot in CrestExtensions.AllRelevantSet)
{ {
if (state.Source[slot] is StateChanged.Source.Fixed) if (state.Sources[slot] is StateSource.Fixed)
{ {
state.Source[slot] = StateChanged.Source.Game; state.Sources[slot] = StateSource.Game;
state.ModelData.SetCrest(slot, state.BaseData.Crest(slot)); state.ModelData.SetCrest(slot, state.BaseData.Crest(slot));
} }
} }
foreach (var flag in CustomizeParameterExtensions.AllFlags) foreach (var flag in CustomizeParameterExtensions.AllFlags)
{ {
switch (state.Source[flag]) switch (state.Sources[flag])
{ {
case StateChanged.Source.Fixed: case StateSource.Fixed:
case StateChanged.Source.Manual when !respectManualPalettes: case StateSource.Manual when !respectManualPalettes:
state.Source[flag] = StateChanged.Source.Game; state.Sources[flag] = StateSource.Game;
state.ModelData.Parameters[flag] = state.BaseData.Parameters[flag]; state.ModelData.Parameters[flag] = state.BaseData.Parameters[flag];
break; break;
} }
} }
if (state.Source[MetaIndex.HatState] is StateChanged.Source.Fixed) if (state.Sources[MetaIndex.HatState] is StateSource.Fixed)
{ {
state.Source[MetaIndex.HatState] = StateChanged.Source.Game; state.Sources[MetaIndex.HatState] = StateSource.Game;
state.ModelData.SetHatVisible(state.BaseData.IsHatVisible()); state.ModelData.SetHatVisible(state.BaseData.IsHatVisible());
} }
if (state.Source[MetaIndex.VisorState] is StateChanged.Source.Fixed) if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed)
{ {
state.Source[MetaIndex.VisorState] = StateChanged.Source.Game; state.Sources[MetaIndex.VisorState] = StateSource.Game;
state.ModelData.SetVisor(state.BaseData.IsVisorToggled()); state.ModelData.SetVisor(state.BaseData.IsVisorToggled());
} }
if (state.Source[MetaIndex.WeaponState] is StateChanged.Source.Fixed) if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed)
{ {
state.Source[MetaIndex.WeaponState] = StateChanged.Source.Game; state.Sources[MetaIndex.WeaponState] = StateSource.Game;
state.ModelData.SetWeaponVisible(state.BaseData.IsWeaponVisible()); state.ModelData.SetWeaponVisible(state.BaseData.IsWeaponVisible());
} }
if (state.Source[MetaIndex.Wetness] is StateChanged.Source.Fixed) if (state.Sources[MetaIndex.Wetness] is StateSource.Fixed)
{ {
state.Source[MetaIndex.Wetness] = StateChanged.Source.Game; state.Sources[MetaIndex.Wetness] = StateSource.Game;
state.ModelData.SetIsWet(state.BaseData.IsWet()); state.ModelData.SetIsWet(state.BaseData.IsWet());
} }
} }

View file

@ -1,49 +1,75 @@
using Glamourer.GameData; using Penumbra.GameData.Enums;
using Penumbra.GameData.Enums;
using static Glamourer.Events.StateChanged;
namespace Glamourer.State; namespace Glamourer.State;
public readonly struct StateSource public enum StateSource : byte
{ {
public static readonly int Size = EquipFlagExtensions.NumEquipFlags Game,
+ CustomizationExtensions.NumIndices Manual,
+ 5 Fixed,
+ CrestExtensions.AllRelevantSet.Count Ipc,
+ CustomizeParameterExtensions.AllFlags.Count;
// Only used for CustomizeParameters.
Pending,
}
public unsafe struct StateSources
{
public const int Size = (StateIndex.Size + 1) / 2;
private fixed byte _data[Size];
private readonly Source[] _data = Enumerable.Repeat(Source.Game, Size).ToArray(); public StateSources()
public StateSource()
{ } { }
public ref Source this[EquipSlot slot, bool stain] public StateSource this[StateIndex index]
=> ref _data[slot.ToIndex() + (stain ? EquipFlagExtensions.NumEquipFlags / 2 : 0)]; {
get
{
var val = _data[index.Value / 2];
return (StateSource)((index.Value & 1) == 1 ? val >> 4 : val & 0x0F);
}
set
{
var val = _data[index.Value / 2];
if ((index.Value & 1) == 1)
val = (byte)((val & 0x0F) | ((byte)value << 4));
else
val = (byte)((val & 0xF0) | (byte)value);
_data[index.Value / 2] = val;
}
}
public ref Source this[CrestFlag slot] public StateSource this[EquipSlot slot, bool stain]
=> ref _data[EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5 + slot.ToInternalIndex()]; {
get => this[slot.ToState(stain)];
public ref Source this[CustomizeIndex type] set => this[slot.ToState(stain)] = value;
=> ref _data[EquipFlagExtensions.NumEquipFlags + (int)type]; }
public ref Source this[MetaIndex index]
=> ref _data[(int)index];
public ref Source this[CustomizeParameterFlag flag]
=> ref _data[
EquipFlagExtensions.NumEquipFlags
+ CustomizationExtensions.NumIndices
+ 5
+ CrestExtensions.AllRelevantSet.Count
+ flag.ToInternalIndex()];
public void RemoveFixedDesignSources() public void RemoveFixedDesignSources()
{ {
for (var i = 0; i < _data.Length; ++i) for (var i = 0; i < Size; ++i)
{ {
if (_data[i] is Source.Fixed) var value = _data[i];
_data[i] = Source.Manual; switch (value)
{
case (byte)StateSource.Fixed | ((byte)StateSource.Fixed << 4):
_data[i] = (byte)StateSource.Manual | ((byte)StateSource.Manual << 4);
break;
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.Pending | ((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.Pending << 4) | (byte)StateSource.Fixed:
_data[i] = (byte)((value & 0xF0) | (byte)StateSource.Manual);
break;
}
} }
} }
} }