diff --git a/Glamourer/Designs/DesignBase.cs b/Glamourer/Designs/DesignBase.cs
index 2363060..a533bc4 100644
--- a/Glamourer/Designs/DesignBase.cs
+++ b/Glamourer/Designs/DesignBase.cs
@@ -1,6 +1,7 @@
using Dalamud.Interface.Internal.Notifications;
using Glamourer.GameData;
using Glamourer.Services;
+using Glamourer.State;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Enums;
@@ -34,7 +35,7 @@ public class DesignBase
_designData = designData;
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
ApplyEquip = equipFlags & EquipFlagExtensions.All;
- _designFlags = 0;
+ ApplyMeta = 0;
CustomizeSet = SetCustomizationSet(customize);
}
@@ -45,7 +46,7 @@ public class DesignBase
ApplyCustomize = clone.ApplyCustomizeRaw;
ApplyEquip = clone.ApplyEquip & EquipFlagExtensions.All;
ApplyParameters = clone.ApplyParameters & CustomizeParameterExtensions.All;
- _designFlags = clone._designFlags & (DesignFlags)0x0F;
+ ApplyMeta = clone.ApplyMeta & MetaExtensions.All;
}
/// Ensure that the customization set is updated when the design data changes.
@@ -57,16 +58,6 @@ public class DesignBase
#region Application Data
- [Flags]
- private enum DesignFlags : byte
- {
- ApplyHatVisible = 0x01,
- ApplyVisorState = 0x02,
- ApplyWeaponVisible = 0x04,
- ApplyWetness = 0x08,
- WriteProtected = 0x10,
- }
-
private CustomizeFlag _applyCustomize = CustomizeFlagExtensions.AllRelevant;
public CustomizeSet CustomizeSet { get; private set; }
@@ -84,9 +75,10 @@ public class DesignBase
internal CustomizeFlag ApplyCustomizeRaw
=> _applyCustomize;
- internal EquipFlag ApplyEquip = EquipFlagExtensions.All;
- internal CrestFlag ApplyCrest = CrestExtensions.AllRelevant;
- private DesignFlags _designFlags = DesignFlags.ApplyHatVisible | DesignFlags.ApplyVisorState | DesignFlags.ApplyWeaponVisible;
+ internal EquipFlag ApplyEquip = EquipFlagExtensions.All;
+ internal CrestFlag ApplyCrest = CrestExtensions.AllRelevant;
+ internal MetaFlag ApplyMeta = MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState;
+ private bool _writeProtected;
public bool SetCustomize(CustomizeService customizeService, CustomizeArray customize)
{
@@ -98,69 +90,30 @@ public class DesignBase
return true;
}
- public bool DoApplyHatVisible()
- => _designFlags.HasFlag(DesignFlags.ApplyHatVisible);
-
- public bool DoApplyVisorToggle()
- => _designFlags.HasFlag(DesignFlags.ApplyVisorState);
-
- public bool DoApplyWeaponVisible()
- => _designFlags.HasFlag(DesignFlags.ApplyWeaponVisible);
-
- public bool DoApplyWetness()
- => _designFlags.HasFlag(DesignFlags.ApplyWetness);
+ public bool DoApplyMeta(MetaIndex index)
+ => ApplyMeta.HasFlag(index.ToFlag());
public bool WriteProtected()
- => _designFlags.HasFlag(DesignFlags.WriteProtected);
+ => _writeProtected;
- public bool SetApplyHatVisible(bool value)
+ public bool SetApplyMeta(MetaIndex index, bool value)
{
- var newFlag = value ? _designFlags | DesignFlags.ApplyHatVisible : _designFlags & ~DesignFlags.ApplyHatVisible;
- if (newFlag == _designFlags)
+ var newFlag = value ? ApplyMeta | index.ToFlag() : ApplyMeta & ~index.ToFlag();
+ if (newFlag == ApplyMeta)
return false;
- _designFlags = newFlag;
- return true;
- }
-
- public bool SetApplyVisorToggle(bool value)
- {
- var newFlag = value ? _designFlags | DesignFlags.ApplyVisorState : _designFlags & ~DesignFlags.ApplyVisorState;
- if (newFlag == _designFlags)
- return false;
-
- _designFlags = newFlag;
- return true;
- }
-
- public bool SetApplyWeaponVisible(bool value)
- {
- var newFlag = value ? _designFlags | DesignFlags.ApplyWeaponVisible : _designFlags & ~DesignFlags.ApplyWeaponVisible;
- if (newFlag == _designFlags)
- return false;
-
- _designFlags = newFlag;
- return true;
- }
-
- public bool SetApplyWetness(bool value)
- {
- var newFlag = value ? _designFlags | DesignFlags.ApplyWetness : _designFlags & ~DesignFlags.ApplyWetness;
- if (newFlag == _designFlags)
- return false;
-
- _designFlags = newFlag;
+ ApplyMeta = newFlag;
return true;
}
public bool SetWriteProtected(bool value)
{
- var newFlag = value ? _designFlags | DesignFlags.WriteProtected : _designFlags & ~DesignFlags.WriteProtected;
- if (newFlag == _designFlags)
+ if (value == _writeProtected)
return false;
- _designFlags = newFlag;
+ _writeProtected = value;
return true;
+
}
public bool DoApplyEquip(EquipSlot slot)
@@ -298,9 +251,9 @@ public class DesignBase
ret[slot.ToString()] = Serialize(item.Id, stain, crest, DoApplyEquip(slot), DoApplyStain(slot), DoApplyCrest(crestSlot));
}
- ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyHatVisible()).ToJObject("Show", "Apply");
- ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyVisorToggle()).ToJObject("IsToggled", "Apply");
- ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyWeaponVisible()).ToJObject("Show", "Apply");
+ ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
+ ret["Visor"] = new QuadBool(_designData.IsVisorToggled(), DoApplyMeta(MetaIndex.VisorState)).ToJObject("IsToggled", "Apply");
+ ret["Weapon"] = new QuadBool(_designData.IsWeaponVisible(), DoApplyMeta(MetaIndex.WeaponState)).ToJObject("Show", "Apply");
}
else
{
@@ -344,7 +297,7 @@ public class DesignBase
ret["Wetness"] = new JObject()
{
["Value"] = _designData.IsWet(),
- ["Apply"] = DoApplyWetness(),
+ ["Apply"] = DoApplyMeta(MetaIndex.Wetness),
};
return ret;
@@ -478,7 +431,7 @@ public class DesignBase
// Load the token and set application.
bool TryGetToken(CustomizeParameterFlag flag, [NotNullWhen(true)] out JToken? token)
{
- token = parameters![flag.ToString()];
+ token = parameters[flag.ToString()];
if (token != null)
{
var apply = token["Apply"]?.ToObject() ?? false;
@@ -493,8 +446,8 @@ public class DesignBase
void MigrateLipOpacity()
{
- var token = parameters!["LipOpacity"]?["Percentage"]?.ToObject();
- var actualToken = parameters![CustomizeParameterFlag.LipDiffuse.ToString()]?["Alpha"];
+ var token = parameters["LipOpacity"]?["Percentage"]?.ToObject();
+ var actualToken = parameters[CustomizeParameterFlag.LipDiffuse.ToString()]?["Alpha"];
if (token != null && actualToken == null)
design.GetDesignDataRef().Parameters.LipDiffuse.W = token.Value;
}
@@ -575,15 +528,15 @@ public class DesignBase
design.SetApplyCrest(CrestFlag.OffHand, applyCrestOff);
}
var metaValue = QuadBool.FromJObject(equip["Hat"], "Show", "Apply", QuadBool.NullFalse);
- design.SetApplyHatVisible(metaValue.Enabled);
+ design.SetApplyMeta(MetaIndex.HatState, metaValue.Enabled);
design._designData.SetHatVisible(metaValue.ForcedValue);
metaValue = QuadBool.FromJObject(equip["Weapon"], "Show", "Apply", QuadBool.NullFalse);
- design.SetApplyWeaponVisible(metaValue.Enabled);
+ design.SetApplyMeta(MetaIndex.WeaponState, metaValue.Enabled);
design._designData.SetWeaponVisible(metaValue.ForcedValue);
metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
- design.SetApplyVisorToggle(metaValue.Enabled);
+ design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled);
design._designData.SetVisor(metaValue.ForcedValue);
}
@@ -610,7 +563,7 @@ public class DesignBase
var wetness = QuadBool.FromJObject(json["Wetness"], "Value", "Apply", QuadBool.NullFalse);
design._designData.SetIsWet(wetness.ForcedValue);
- design.SetApplyWetness(wetness.Enabled);
+ design.SetApplyMeta(MetaIndex.Wetness, wetness.Enabled);
design._designData.ModelId = json["ModelId"]?.ToObject() ?? 0;
PrintWarning(customizations.ValidateModelId(design._designData.ModelId, out design._designData.ModelId,
@@ -664,17 +617,13 @@ public class DesignBase
try
{
_designData = DesignBase64Migration.MigrateBase64(items, humans, base64, out var equipFlags, out var customizeFlags,
- out var writeProtected,
- out var applyHat, out var applyVisor, out var applyWeapon);
+ out var writeProtected, out var applyMeta);
ApplyEquip = equipFlags;
ApplyCustomize = customizeFlags;
ApplyParameters = 0;
ApplyCrest = 0;
+ ApplyMeta = applyMeta;
SetWriteProtected(writeProtected);
- SetApplyHatVisible(applyHat);
- SetApplyVisorToggle(applyVisor);
- SetApplyWeaponVisible(applyWeapon);
- SetApplyWetness(true);
CustomizeSet = SetCustomizationSet(customize);
}
catch (Exception ex)
diff --git a/Glamourer/Designs/DesignBase64Migration.cs b/Glamourer/Designs/DesignBase64Migration.cs
index 2d85924..ec4beb1 100644
--- a/Glamourer/Designs/DesignBase64Migration.cs
+++ b/Glamourer/Designs/DesignBase64Migration.cs
@@ -1,4 +1,5 @@
using Glamourer.Services;
+using Glamourer.State;
using OtterGui;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
@@ -13,7 +14,7 @@ public class DesignBase64Migration
public const int Base64SizeV4 = 95;
public static unsafe DesignData MigrateBase64(ItemManager items, HumanModelList humans, string base64, out EquipFlag equipFlags,
- out CustomizeFlag customizeFlags, out bool writeProtected, out bool applyHat, out bool applyVisor, out bool applyWeapon)
+ out CustomizeFlag customizeFlags, out bool writeProtected, out MetaFlag metaFlags)
{
static void CheckSize(int length, int requiredLength)
{
@@ -25,9 +26,7 @@ public class DesignBase64Migration
byte applicationFlags;
ushort equipFlagsS;
var bytes = Convert.FromBase64String(base64);
- applyHat = false;
- applyVisor = false;
- applyWeapon = false;
+ metaFlags = MetaFlag.Wetness;
var data = new DesignData();
switch (bytes[0])
{
@@ -77,9 +76,12 @@ public class DesignBase64Migration
customizeFlags = (applicationFlags & 0x01) != 0 ? CustomizeFlagExtensions.All : 0;
data.SetIsWet((applicationFlags & 0x02) != 0);
- applyHat = (applicationFlags & 0x04) != 0;
- applyWeapon = (applicationFlags & 0x08) != 0;
- applyVisor = (applicationFlags & 0x10) != 0;
+ if ((applicationFlags & 0x04) != 0)
+ metaFlags |= MetaFlag.HatState;
+ if ((applicationFlags & 0x08) != 0)
+ metaFlags |= MetaFlag.WeaponState;
+ if ((applicationFlags & 0x10) != 0)
+ metaFlags |= MetaFlag.VisorState;
writeProtected = (applicationFlags & 0x20) != 0;
equipFlags = 0;
diff --git a/Glamourer/Designs/DesignData.cs b/Glamourer/Designs/DesignData.cs
index edc35e1..5b573d3 100644
--- a/Glamourer/Designs/DesignData.cs
+++ b/Glamourer/Designs/DesignData.cs
@@ -186,6 +186,26 @@ public unsafe struct DesignData
return true;
}
+ public readonly bool GetMeta(MetaIndex index)
+ => index switch
+ {
+ MetaIndex.Wetness => IsWet(),
+ MetaIndex.HatState => IsHatVisible(),
+ MetaIndex.VisorState => IsVisorToggled(),
+ MetaIndex.WeaponState => IsWeaponVisible(),
+ _ => false,
+ };
+
+ public bool SetMeta(MetaIndex index, bool value)
+ => index switch
+ {
+ MetaIndex.Wetness => SetIsWet(value),
+ MetaIndex.HatState => SetHatVisible(value),
+ MetaIndex.VisorState => SetVisor(value),
+ MetaIndex.WeaponState => SetWeaponVisible(value),
+ _ => false,
+ };
+
public readonly bool IsWet()
=> (_states & 0x01) == 0x01;
diff --git a/Glamourer/Designs/Links/DesignMerger.cs b/Glamourer/Designs/Links/DesignMerger.cs
index abf1163..2aef9bc 100644
--- a/Glamourer/Designs/Links/DesignMerger.cs
+++ b/Glamourer/Designs/Links/DesignMerger.cs
@@ -6,50 +6,9 @@ using Glamourer.State;
using Glamourer.Unlocks;
using OtterGui.Services;
using Penumbra.GameData.Enums;
-using Penumbra.GameData.Structs;
namespace Glamourer.Designs.Links;
-using WeaponDict = Dictionary;
-
-public sealed class MergedDesign
-{
- public MergedDesign(DesignManager designManager)
- {
- Design = designManager.CreateTemporary();
- Design.ApplyEquip = 0;
- Design.ApplyCustomize = 0;
- Design.ApplyCrest = 0;
- Design.ApplyParameters = 0;
- Design.SetApplyWetness(false);
- Design.SetApplyVisorToggle(false);
- Design.SetApplyWeaponVisible(false);
- Design.SetApplyHatVisible(false);
- }
-
- public readonly DesignBase Design;
- public readonly WeaponDict Weapons = new(4);
- public readonly StateSource Source = new();
-
- public StateChanged.Source GetSource(EquipSlot slot, bool stain, StateChanged.Source actualSource)
- => GetSource(Source[slot, stain], actualSource);
-
- public StateChanged.Source GetSource(CrestFlag slot, StateChanged.Source actualSource)
- => GetSource(Source[slot], actualSource);
-
- public StateChanged.Source GetSource(CustomizeIndex type, StateChanged.Source actualSource)
- => GetSource(Source[type], actualSource);
-
- public StateChanged.Source GetSource(MetaIndex index, StateChanged.Source actualSource)
- => GetSource(Source[index], actualSource);
-
- public StateChanged.Source GetSource(CustomizeParameterFlag flag, StateChanged.Source actualSource)
- => GetSource(Source[flag], actualSource);
-
- public static StateChanged.Source GetSource(StateChanged.Source given, StateChanged.Source actualSource)
- => given is StateChanged.Source.Game ? StateChanged.Source.Game : actualSource;
-}
-
public class DesignMerger(
DesignManager designManager,
CustomizeService _customize,
@@ -57,7 +16,8 @@ public class DesignMerger(
ItemUnlockManager _itemUnlocks,
CustomizeUnlockManager _customizeUnlocks) : IService
{
- public MergedDesign Merge(IEnumerable<(DesignBase?, ApplicationType)> designs, in DesignData baseRef, bool respectOwnership)
+ public MergedDesign Merge(IEnumerable<(DesignBase?, ApplicationType)> designs, in DesignData baseRef, bool respectOwnership,
+ bool modAssociations)
{
var ret = new MergedDesign(designManager);
CustomizeFlag fixFlags = 0;
@@ -81,6 +41,7 @@ public class DesignMerger(
ReduceOffhands(data, equipFlags, ret, source, respectOwnership);
ReduceCrests(data, crestFlags, ret, source);
ReduceParameters(data, parameterFlags, ret, source);
+ ReduceMods(design as Design, ret, modAssociations);
}
ApplyFixFlags(ret, fixFlags);
@@ -88,6 +49,15 @@ public class DesignMerger(
}
+ private static void ReduceMods(Design? design, MergedDesign ret, bool modAssociations)
+ {
+ if (design == null || !modAssociations)
+ return;
+
+ foreach (var (mod, settings) in design.AssociatedMods)
+ ret.AssociatedMods.TryAdd(mod, settings);
+ }
+
private static void ReduceMeta(in DesignData design, bool applyHat, bool applyVisor, bool applyWeapon, bool applyWet, MergedDesign ret,
StateChanged.Source source)
{
@@ -196,7 +166,8 @@ public class DesignMerger(
}
}
- private void ReduceMainhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source, bool respectOwnership)
+ private void ReduceMainhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source,
+ bool respectOwnership)
{
if (!equipFlags.HasFlag(EquipFlag.Mainhand))
return;
@@ -271,7 +242,7 @@ public class DesignMerger(
ret.Source[CustomizeIndex.Face] = source;
}
- var set = _customize.Manager.GetSet(customize.Clan, customize.Gender);
+ var set = ret.Design.CustomizeSet;
var face = customize.Face;
foreach (var index in Enum.GetValues())
{
@@ -291,6 +262,8 @@ public class DesignMerger(
ret.Source[index] = source;
fixFlags &= ~flag;
}
+
+ ret.Design.SetCustomize(_customize, customize);
}
private static void ApplyFixFlags(MergedDesign ret, CustomizeFlag fixFlags)
diff --git a/Glamourer/Designs/Links/MergedDesign.cs b/Glamourer/Designs/Links/MergedDesign.cs
index eccc46f..d4b3cab 100644
--- a/Glamourer/Designs/Links/MergedDesign.cs
+++ b/Glamourer/Designs/Links/MergedDesign.cs
@@ -1,5 +1,6 @@
using Glamourer.Events;
using Glamourer.GameData;
+using Glamourer.Interop.Penumbra;
using Glamourer.State;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@@ -15,15 +16,38 @@ public sealed class MergedDesign
Design.ApplyCustomize = 0;
Design.ApplyCrest = 0;
Design.ApplyParameters = 0;
- Design.SetApplyWetness(false);
- Design.SetApplyVisorToggle(false);
- Design.SetApplyWeaponVisible(false);
- Design.SetApplyHatVisible(false);
+ Design.ApplyMeta = 0;
+ }
+
+ public MergedDesign(DesignBase design)
+ {
+ Design = design;
+ if (design.DoApplyEquip(EquipSlot.MainHand))
+ {
+ var weapon = design.DesignData.Item(EquipSlot.MainHand);
+ if (weapon.Valid)
+ Weapons.TryAdd(weapon.Type, (weapon, StateChanged.Source.Manual));
+ }
+
+ if (design.DoApplyEquip(EquipSlot.OffHand))
+ {
+ var weapon = design.DesignData.Item(EquipSlot.OffHand);
+ if (weapon.Valid)
+ Weapons.TryAdd(weapon.Type, (weapon, StateChanged.Source.Manual));
+ }
+ }
+
+ public MergedDesign(Design design)
+ : this((DesignBase)design)
+ {
+ foreach (var (mod, settings) in design.AssociatedMods)
+ AssociatedMods[mod] = settings;
}
public readonly DesignBase Design;
- public readonly Dictionary Weapons = new(4);
- public readonly StateSource Source = new();
+ public readonly Dictionary Weapons = new(4);
+ public readonly StateSource Source = new();
+ public readonly SortedList AssociatedMods = [];
public StateChanged.Source GetSource(EquipSlot slot, bool stain, StateChanged.Source actualSource)
=> GetSource(Source[slot, stain], actualSource);
diff --git a/Glamourer/Designs/MetaIndex.cs b/Glamourer/Designs/MetaIndex.cs
new file mode 100644
index 0000000..4fac98d
--- /dev/null
+++ b/Glamourer/Designs/MetaIndex.cs
@@ -0,0 +1,39 @@
+using Penumbra.GameData.Enums;
+
+namespace Glamourer.State;
+
+public enum MetaIndex
+{
+ Wetness = EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices,
+ HatState,
+ VisorState,
+ WeaponState,
+ ModelId,
+}
+
+[Flags]
+public enum MetaFlag : byte
+{
+ Wetness = 0x01,
+ HatState = 0x02,
+ VisorState = 0x04,
+ WeaponState = 0x08,
+}
+
+public static class MetaExtensions
+{
+ public static readonly IReadOnlyList AllRelevant =
+ [MetaIndex.Wetness, MetaIndex.HatState, MetaIndex.VisorState, MetaIndex.WeaponState];
+
+ public const MetaFlag All = MetaFlag.Wetness | MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState;
+
+ public static MetaFlag ToFlag(this MetaIndex index)
+ => index switch
+ {
+ MetaIndex.Wetness => MetaFlag.Wetness,
+ MetaIndex.HatState => MetaFlag.HatState,
+ MetaIndex.VisorState => MetaFlag.VisorState,
+ MetaIndex.WeaponState => MetaFlag.WeaponState,
+ _ => (MetaFlag) byte.MaxValue,
+ };
+}
diff --git a/Glamourer/Interop/PalettePlus/PaletteImport.cs b/Glamourer/Interop/PalettePlus/PaletteImport.cs
index b26f8f9..8513036 100644
--- a/Glamourer/Interop/PalettePlus/PaletteImport.cs
+++ b/Glamourer/Interop/PalettePlus/PaletteImport.cs
@@ -1,7 +1,6 @@
using Dalamud.Plugin;
using Glamourer.Designs;
using Glamourer.GameData;
-using Glamourer.State;
using Newtonsoft.Json.Linq;
using OtterGui.Services;
diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs
index 1d23cff..abb3716 100644
--- a/Glamourer/State/StateEditor.cs
+++ b/Glamourer/State/StateEditor.cs
@@ -1,4 +1,5 @@
using Dalamud.Plugin.Services;
+using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.GameData;
using Glamourer.Services;
diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs
index d529206..a8c8dd5 100644
--- a/Glamourer/State/StateListener.cs
+++ b/Glamourer/State/StateListener.cs
@@ -11,6 +11,7 @@ using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using Glamourer.GameData;
using Penumbra.GameData.DataContainers;
+using Glamourer.Designs;
namespace Glamourer.State;
diff --git a/Glamourer/State/StateSource.cs b/Glamourer/State/StateSource.cs
index 04c934d..80899d2 100644
--- a/Glamourer/State/StateSource.cs
+++ b/Glamourer/State/StateSource.cs
@@ -4,15 +4,6 @@ using static Glamourer.Events.StateChanged;
namespace Glamourer.State;
-public enum MetaIndex
-{
- Wetness = EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices,
- HatState,
- VisorState,
- WeaponState,
- ModelId,
-}
-
public readonly struct StateSource
{
public static readonly int Size = EquipFlagExtensions.NumEquipFlags