mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-13 12:14:18 +01:00
Start
This commit is contained in:
parent
1a409d475a
commit
c7430e59b3
35 changed files with 1118 additions and 376 deletions
62
Glamourer/Automation/ApplicationType.cs
Normal file
62
Glamourer/Automation/ApplicationType.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.GameData;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
|
||||||
|
namespace Glamourer.Automation;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ApplicationType : byte
|
||||||
|
{
|
||||||
|
Armor = 0x01,
|
||||||
|
Customizations = 0x02,
|
||||||
|
Weapons = 0x04,
|
||||||
|
GearCustomization = 0x08,
|
||||||
|
Accessories = 0x10,
|
||||||
|
|
||||||
|
All = Armor | Accessories | Customizations | Weapons | GearCustomization,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ApplicationTypeExtensions
|
||||||
|
{
|
||||||
|
public static (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, bool ApplyHat, bool ApplyVisor,
|
||||||
|
bool
|
||||||
|
ApplyWeapon, bool ApplyWet) ApplyWhat(this ApplicationType type, DesignBase? design)
|
||||||
|
{
|
||||||
|
var equipFlags = (type.HasFlag(ApplicationType.Weapons) ? WeaponFlags : 0)
|
||||||
|
| (type.HasFlag(ApplicationType.Armor) ? ArmorFlags : 0)
|
||||||
|
| (type.HasFlag(ApplicationType.Accessories) ? AccessoryFlags : 0)
|
||||||
|
| (type.HasFlag(ApplicationType.GearCustomization) ? StainFlags : 0);
|
||||||
|
var customizeFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeFlagExtensions.All : 0;
|
||||||
|
var parameterFlags = type.HasFlag(ApplicationType.Customizations) ? CustomizeParameterExtensions.All : 0;
|
||||||
|
var crestFlag = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 0;
|
||||||
|
|
||||||
|
if (design == null)
|
||||||
|
return (equipFlags, customizeFlags, crestFlag, parameterFlags, type.HasFlag(ApplicationType.Armor),
|
||||||
|
type.HasFlag(ApplicationType.Armor),
|
||||||
|
type.HasFlag(ApplicationType.Weapons), type.HasFlag(ApplicationType.Customizations));
|
||||||
|
|
||||||
|
return (equipFlags & design!.ApplyEquip, customizeFlags & design.ApplyCustomize, crestFlag & design.ApplyCrest,
|
||||||
|
parameterFlags & design.ApplyParameters,
|
||||||
|
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 ArmorFlags = EquipFlag.Head | EquipFlag.Body | EquipFlag.Hands | EquipFlag.Legs | EquipFlag.Feet;
|
||||||
|
public const EquipFlag AccessoryFlags = EquipFlag.Ears | EquipFlag.Neck | EquipFlag.Wrist | EquipFlag.RFinger | EquipFlag.LFinger;
|
||||||
|
|
||||||
|
public const EquipFlag StainFlags = EquipFlag.MainhandStain
|
||||||
|
| EquipFlag.OffhandStain
|
||||||
|
| EquipFlag.HeadStain
|
||||||
|
| EquipFlag.BodyStain
|
||||||
|
| EquipFlag.HandsStain
|
||||||
|
| EquipFlag.LegsStain
|
||||||
|
| EquipFlag.FeetStain
|
||||||
|
| EquipFlag.EarsStain
|
||||||
|
| EquipFlag.NeckStain
|
||||||
|
| EquipFlag.WristStain
|
||||||
|
| EquipFlag.RFingerStain
|
||||||
|
| EquipFlag.LFingerStain;
|
||||||
|
}
|
||||||
|
|
@ -12,22 +12,10 @@ public class AutoDesign
|
||||||
{
|
{
|
||||||
public const string RevertName = "Revert";
|
public const string RevertName = "Revert";
|
||||||
|
|
||||||
[Flags]
|
public Design? Design;
|
||||||
public enum Type : byte
|
public JobGroup Jobs;
|
||||||
{
|
public ApplicationType Type;
|
||||||
Armor = 0x01,
|
public short GearsetIndex = -1;
|
||||||
Customizations = 0x02,
|
|
||||||
Weapons = 0x04,
|
|
||||||
GearCustomization = 0x08,
|
|
||||||
Accessories = 0x10,
|
|
||||||
|
|
||||||
All = Armor | Accessories | Customizations | Weapons | GearCustomization,
|
|
||||||
}
|
|
||||||
|
|
||||||
public Design? Design;
|
|
||||||
public JobGroup Jobs;
|
|
||||||
public Type ApplicationType;
|
|
||||||
public short GearsetIndex = -1;
|
|
||||||
|
|
||||||
public string Name(bool incognito)
|
public string Name(bool incognito)
|
||||||
=> Revert ? RevertName : incognito ? Design!.Incognito : Design!.Name.Text;
|
=> Revert ? RevertName : incognito ? Design!.Incognito : Design!.Name.Text;
|
||||||
|
|
@ -41,10 +29,10 @@ public class AutoDesign
|
||||||
public AutoDesign Clone()
|
public AutoDesign Clone()
|
||||||
=> new()
|
=> new()
|
||||||
{
|
{
|
||||||
Design = Design,
|
Design = Design,
|
||||||
ApplicationType = ApplicationType,
|
Type = Type,
|
||||||
Jobs = Jobs,
|
Jobs = Jobs,
|
||||||
GearsetIndex = GearsetIndex,
|
GearsetIndex = GearsetIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
public unsafe bool IsActive(Actor actor)
|
public unsafe bool IsActive(Actor actor)
|
||||||
|
|
@ -64,9 +52,9 @@ public class AutoDesign
|
||||||
public JObject Serialize()
|
public JObject Serialize()
|
||||||
=> new()
|
=> new()
|
||||||
{
|
{
|
||||||
["Design"] = Design?.Identifier.ToString(),
|
["Design"] = Design?.Identifier.ToString(),
|
||||||
["ApplicationType"] = (uint)ApplicationType,
|
["Type"] = (uint)Type,
|
||||||
["Conditions"] = CreateConditionObject(),
|
["Conditions"] = CreateConditionObject(),
|
||||||
};
|
};
|
||||||
|
|
||||||
private JObject CreateConditionObject()
|
private JObject CreateConditionObject()
|
||||||
|
|
@ -82,42 +70,5 @@ public class AutoDesign
|
||||||
|
|
||||||
public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, bool ApplyHat, bool ApplyVisor, bool
|
public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, bool ApplyHat, bool ApplyVisor, bool
|
||||||
ApplyWeapon, bool ApplyWet) ApplyWhat()
|
ApplyWeapon, bool ApplyWet) ApplyWhat()
|
||||||
{
|
=> Type.ApplyWhat(Design);
|
||||||
var equipFlags = (ApplicationType.HasFlag(Type.Weapons) ? WeaponFlags : 0)
|
|
||||||
| (ApplicationType.HasFlag(Type.Armor) ? ArmorFlags : 0)
|
|
||||||
| (ApplicationType.HasFlag(Type.Accessories) ? AccessoryFlags : 0)
|
|
||||||
| (ApplicationType.HasFlag(Type.GearCustomization) ? StainFlags : 0);
|
|
||||||
var customizeFlags = ApplicationType.HasFlag(Type.Customizations) ? CustomizeFlagExtensions.All : 0;
|
|
||||||
var parameterFlags = ApplicationType.HasFlag(Type.Customizations) ? CustomizeParameterExtensions.All : 0;
|
|
||||||
var crestFlag = ApplicationType.HasFlag(Type.GearCustomization) ? CrestExtensions.AllRelevant : 0;
|
|
||||||
|
|
||||||
if (Revert)
|
|
||||||
return (equipFlags, customizeFlags, crestFlag, parameterFlags, ApplicationType.HasFlag(Type.Armor),
|
|
||||||
ApplicationType.HasFlag(Type.Armor),
|
|
||||||
ApplicationType.HasFlag(Type.Weapons), ApplicationType.HasFlag(Type.Customizations));
|
|
||||||
|
|
||||||
return (equipFlags & Design!.ApplyEquip, customizeFlags & Design.ApplyCustomize, crestFlag & Design.ApplyCrest,
|
|
||||||
parameterFlags & Design.ApplyParameters,
|
|
||||||
ApplicationType.HasFlag(Type.Armor) && Design.DoApplyHatVisible(),
|
|
||||||
ApplicationType.HasFlag(Type.Armor) && Design.DoApplyVisorToggle(),
|
|
||||||
ApplicationType.HasFlag(Type.Weapons) && Design.DoApplyWeaponVisible(),
|
|
||||||
ApplicationType.HasFlag(Type.Customizations) && Design.DoApplyWetness());
|
|
||||||
}
|
|
||||||
|
|
||||||
public const EquipFlag WeaponFlags = EquipFlag.Mainhand | EquipFlag.Offhand;
|
|
||||||
public const EquipFlag ArmorFlags = EquipFlag.Head | EquipFlag.Body | EquipFlag.Hands | EquipFlag.Legs | EquipFlag.Feet;
|
|
||||||
public const EquipFlag AccessoryFlags = EquipFlag.Ears | EquipFlag.Neck | EquipFlag.Wrist | EquipFlag.RFinger | EquipFlag.LFinger;
|
|
||||||
|
|
||||||
public const EquipFlag StainFlags = EquipFlag.MainhandStain
|
|
||||||
| EquipFlag.OffhandStain
|
|
||||||
| EquipFlag.HeadStain
|
|
||||||
| EquipFlag.BodyStain
|
|
||||||
| EquipFlag.HandsStain
|
|
||||||
| EquipFlag.LegsStain
|
|
||||||
| EquipFlag.FeetStain
|
|
||||||
| EquipFlag.EarsStain
|
|
||||||
| EquipFlag.NeckStain
|
|
||||||
| EquipFlag.WristStain
|
|
||||||
| EquipFlag.RFingerStain
|
|
||||||
| EquipFlag.LFingerStain;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Designs.Links;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
|
@ -15,7 +16,7 @@ using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.Automation;
|
namespace Glamourer.Automation;
|
||||||
|
|
||||||
public class AutoDesignApplier : IDisposable
|
public sealed class AutoDesignApplier : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private readonly AutoDesignManager _manager;
|
private readonly AutoDesignManager _manager;
|
||||||
|
|
@ -30,6 +31,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
private readonly ObjectManager _objects;
|
private readonly ObjectManager _objects;
|
||||||
private readonly WeaponLoading _weapons;
|
private readonly WeaponLoading _weapons;
|
||||||
private readonly HumanModelList _humans;
|
private readonly HumanModelList _humans;
|
||||||
|
private readonly DesignMerger _designMerger;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
|
|
||||||
private ActorState? _jobChangeState;
|
private ActorState? _jobChangeState;
|
||||||
|
|
@ -182,7 +184,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
}
|
}
|
||||||
else if (_state.TryGetValue(id, out var state))
|
else if (_state.TryGetValue(id, out var state))
|
||||||
{
|
{
|
||||||
state.RemoveFixedDesignSources();
|
state.Source.RemoveFixedDesignSources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -196,9 +198,9 @@ public 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.RemoveFixedDesignSources();
|
state.Source.RemoveFixedDesignSources();
|
||||||
else if (_state.TryGetValue(id, out var state))
|
else if (_state.TryGetValue(id, out var state))
|
||||||
state.RemoveFixedDesignSources();
|
state.Source.RemoveFixedDesignSources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,7 +278,7 @@ public 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.RemoveFixedDesignSources();
|
state.Source.RemoveFixedDesignSources();
|
||||||
|
|
||||||
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
||||||
return;
|
return;
|
||||||
|
|
@ -286,7 +288,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
if (!design.IsActive(actor))
|
if (!design.IsActive(actor))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (design.ApplicationType is 0)
|
if (design.Type is 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ref readonly var data = ref design.GetDesignData(state);
|
ref readonly var data = ref design.GetDesignData(state);
|
||||||
|
|
@ -342,7 +344,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
if (!crestFlags.HasFlag(slot))
|
if (!crestFlags.HasFlag(slot))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!respectManual || state[slot] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[slot] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeCrest(state, slot, design.Crest(slot), source);
|
_state.ChangeCrest(state, slot, design.Crest(slot), source);
|
||||||
totalCrestFlags |= slot;
|
totalCrestFlags |= slot;
|
||||||
}
|
}
|
||||||
|
|
@ -360,7 +362,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
if (!parameterFlags.HasFlag(flag))
|
if (!parameterFlags.HasFlag(flag))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!respectManual || state[flag] is not StateChanged.Source.Manual and not StateChanged.Source.Pending)
|
if (!respectManual || state.Source[flag] is not StateChanged.Source.Manual and not StateChanged.Source.Pending)
|
||||||
_state.ChangeCustomizeParameter(state, flag, design.Parameters[flag], source);
|
_state.ChangeCustomizeParameter(state, flag, design.Parameters[flag], source);
|
||||||
totalParameterFlags |= flag;
|
totalParameterFlags |= flag;
|
||||||
}
|
}
|
||||||
|
|
@ -381,7 +383,7 @@ public 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[slot, false] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[slot, false] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeItem(state, slot, item, source);
|
_state.ChangeItem(state, slot, item, source);
|
||||||
totalEquipFlags |= flag;
|
totalEquipFlags |= flag;
|
||||||
}
|
}
|
||||||
|
|
@ -390,7 +392,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
var stainFlag = slot.ToStainFlag();
|
var stainFlag = slot.ToStainFlag();
|
||||||
if (equipFlags.HasFlag(stainFlag))
|
if (equipFlags.HasFlag(stainFlag))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[slot, true] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[slot, true] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeStain(state, slot, design.Stain(slot), source);
|
_state.ChangeStain(state, slot, design.Stain(slot), source);
|
||||||
totalEquipFlags |= stainFlag;
|
totalEquipFlags |= stainFlag;
|
||||||
}
|
}
|
||||||
|
|
@ -400,7 +402,7 @@ public 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[EquipSlot.MainHand, false] is not StateChanged.Source.Manual;
|
var checkState = !respectManual || state.Source[EquipSlot.MainHand, false] is not StateChanged.Source.Manual;
|
||||||
if (checkUnlock && checkState)
|
if (checkUnlock && checkState)
|
||||||
{
|
{
|
||||||
if (fromJobChange)
|
if (fromJobChange)
|
||||||
|
|
@ -420,7 +422,7 @@ public 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[EquipSlot.OffHand, false] is not StateChanged.Source.Manual;
|
var checkState = !respectManual || state.Source[EquipSlot.OffHand, false] is not StateChanged.Source.Manual;
|
||||||
if (checkUnlock && checkState)
|
if (checkUnlock && checkState)
|
||||||
{
|
{
|
||||||
if (fromJobChange)
|
if (fromJobChange)
|
||||||
|
|
@ -438,14 +440,14 @@ public class AutoDesignApplier : IDisposable
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.MainhandStain))
|
if (equipFlags.HasFlag(EquipFlag.MainhandStain))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[EquipSlot.MainHand, true] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[EquipSlot.MainHand, true] is not StateChanged.Source.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[EquipSlot.OffHand, true] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[EquipSlot.OffHand, true] is not StateChanged.Source.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;
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +469,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
|
|
||||||
if (customizeFlags.HasFlag(CustomizeFlag.Clan))
|
if (customizeFlags.HasFlag(CustomizeFlag.Clan))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[CustomizeIndex.Clan] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[CustomizeIndex.Clan] is not StateChanged.Source.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;
|
||||||
|
|
@ -475,7 +477,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
|
|
||||||
if (customizeFlags.HasFlag(CustomizeFlag.Gender))
|
if (customizeFlags.HasFlag(CustomizeFlag.Gender))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[CustomizeIndex.Gender] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[CustomizeIndex.Gender] is not StateChanged.Source.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;
|
||||||
|
|
@ -486,7 +488,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
|
|
||||||
if (customizeFlags.HasFlag(CustomizeFlag.Face))
|
if (customizeFlags.HasFlag(CustomizeFlag.Face))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[CustomizeIndex.Face] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[CustomizeIndex.Face] is not StateChanged.Source.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;
|
||||||
|
|
@ -506,7 +508,7 @@ public 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[index] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[index] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeCustomize(state, index, value, source);
|
_state.ChangeCustomize(state, index, value, source);
|
||||||
totalCustomizeFlags |= flag;
|
totalCustomizeFlags |= flag;
|
||||||
}
|
}
|
||||||
|
|
@ -518,28 +520,28 @@ public class AutoDesignApplier : IDisposable
|
||||||
{
|
{
|
||||||
if (applyHat && (totalMetaFlags & 0x01) == 0)
|
if (applyHat && (totalMetaFlags & 0x01) == 0)
|
||||||
{
|
{
|
||||||
if (!respectManual || state[ActorState.MetaIndex.HatState] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[MetaIndex.HatState] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeHatState(state, design.IsHatVisible(), source);
|
_state.ChangeHatState(state, design.IsHatVisible(), source);
|
||||||
totalMetaFlags |= 0x01;
|
totalMetaFlags |= 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applyVisor && (totalMetaFlags & 0x02) == 0)
|
if (applyVisor && (totalMetaFlags & 0x02) == 0)
|
||||||
{
|
{
|
||||||
if (!respectManual || state[ActorState.MetaIndex.VisorState] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[MetaIndex.VisorState] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeVisorState(state, design.IsVisorToggled(), source);
|
_state.ChangeVisorState(state, design.IsVisorToggled(), source);
|
||||||
totalMetaFlags |= 0x02;
|
totalMetaFlags |= 0x02;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applyWeapon && (totalMetaFlags & 0x04) == 0)
|
if (applyWeapon && (totalMetaFlags & 0x04) == 0)
|
||||||
{
|
{
|
||||||
if (!respectManual || state[ActorState.MetaIndex.WeaponState] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[MetaIndex.WeaponState] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeWeaponState(state, design.IsWeaponVisible(), source);
|
_state.ChangeWeaponState(state, design.IsWeaponVisible(), source);
|
||||||
totalMetaFlags |= 0x04;
|
totalMetaFlags |= 0x04;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applyWet && (totalMetaFlags & 0x08) == 0)
|
if (applyWet && (totalMetaFlags & 0x08) == 0)
|
||||||
{
|
{
|
||||||
if (!respectManual || state[ActorState.MetaIndex.Wetness] is not StateChanged.Source.Manual)
|
if (!respectManual || state.Source[MetaIndex.Wetness] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeWetness(state, design.IsWet(), source);
|
_state.ChangeWetness(state, design.IsWet(), source);
|
||||||
totalMetaFlags |= 0x08;
|
totalMetaFlags |= 0x08;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
||||||
var newDesign = new AutoDesign()
|
var newDesign = new AutoDesign()
|
||||||
{
|
{
|
||||||
Design = design,
|
Design = design,
|
||||||
ApplicationType = AutoDesign.Type.All,
|
Type = ApplicationType.All,
|
||||||
Jobs = _jobs.JobGroups[1],
|
Jobs = _jobs.JobGroups[1],
|
||||||
};
|
};
|
||||||
set.Designs.Add(newDesign);
|
set.Designs.Add(newDesign);
|
||||||
|
|
@ -328,21 +328,21 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
||||||
_event.Invoke(AutomationChanged.Type.ChangedConditions, set, (which, old, index));
|
_event.Invoke(AutomationChanged.Type.ChangedConditions, set, (which, old, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeApplicationType(AutoDesignSet set, int which, AutoDesign.Type type)
|
public void ChangeApplicationType(AutoDesignSet set, int which, ApplicationType applicationType)
|
||||||
{
|
{
|
||||||
if (which >= set.Designs.Count || which < 0)
|
if (which >= set.Designs.Count || which < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
type &= AutoDesign.Type.All;
|
applicationType &= ApplicationType.All;
|
||||||
var design = set.Designs[which];
|
var design = set.Designs[which];
|
||||||
if (design.ApplicationType == type)
|
if (design.Type == applicationType)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var old = design.ApplicationType;
|
var old = design.Type;
|
||||||
design.ApplicationType = type;
|
design.Type = applicationType;
|
||||||
Save();
|
Save();
|
||||||
Glamourer.Log.Debug($"Changed application type from {old} to {type} for associated design {which + 1} in design set.");
|
Glamourer.Log.Debug($"Changed application type from {old} to {applicationType} for associated design {which + 1} in design set.");
|
||||||
_event.Invoke(AutomationChanged.Type.ChangedType, set, (which, old, type));
|
_event.Invoke(AutomationChanged.Type.ChangedType, set, (which, old, applicationType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ToFilename(FilenameService fileNames)
|
public string ToFilename(FilenameService fileNames)
|
||||||
|
|
@ -490,12 +490,13 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var applicationType = (AutoDesign.Type)(jObj["ApplicationType"]?.ToObject<uint>() ?? 0);
|
// ApplicationType is a migration from an older property name.
|
||||||
|
var applicationType = (ApplicationType)(jObj["Type"]?.ToObject<uint>() ?? jObj["ApplicationType"]?.ToObject<uint>() ?? 0);
|
||||||
|
|
||||||
var ret = new AutoDesign()
|
var ret = new AutoDesign
|
||||||
{
|
{
|
||||||
Design = design,
|
Design = design,
|
||||||
ApplicationType = applicationType & AutoDesign.Type.All,
|
Type = applicationType & ApplicationType.All,
|
||||||
};
|
};
|
||||||
return ParseConditions(setName, jObj, ret) ? ret : null;
|
return ParseConditions(setName, jObj, ret) ? ret : null;
|
||||||
}
|
}
|
||||||
|
|
@ -550,7 +551,24 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
||||||
private ActorIdentifier[] GetGroup(ActorIdentifier identifier)
|
private ActorIdentifier[] GetGroup(ActorIdentifier identifier)
|
||||||
{
|
{
|
||||||
if (!identifier.IsValid)
|
if (!identifier.IsValid)
|
||||||
return Array.Empty<ActorIdentifier>();
|
return [];
|
||||||
|
|
||||||
|
return identifier.Type switch
|
||||||
|
{
|
||||||
|
IdentifierType.Player =>
|
||||||
|
[
|
||||||
|
identifier.CreatePermanent(),
|
||||||
|
],
|
||||||
|
IdentifierType.Retainer =>
|
||||||
|
[
|
||||||
|
_actors.CreateRetainer(identifier.PlayerName,
|
||||||
|
identifier.Retainer == ActorIdentifier.RetainerType.Mannequin
|
||||||
|
? ActorIdentifier.RetainerType.Mannequin
|
||||||
|
: ActorIdentifier.RetainerType.Bell).CreatePermanent(),
|
||||||
|
],
|
||||||
|
IdentifierType.Npc => CreateNpcs(_actors, identifier),
|
||||||
|
_ => [],
|
||||||
|
};
|
||||||
|
|
||||||
static ActorIdentifier[] CreateNpcs(ActorManager manager, ActorIdentifier identifier)
|
static ActorIdentifier[] CreateNpcs(ActorManager manager, ActorIdentifier identifier)
|
||||||
{
|
{
|
||||||
|
|
@ -566,23 +584,6 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
||||||
identifier.Kind,
|
identifier.Kind,
|
||||||
kvp.Key)).ToArray();
|
kvp.Key)).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
return identifier.Type switch
|
|
||||||
{
|
|
||||||
IdentifierType.Player => new[]
|
|
||||||
{
|
|
||||||
identifier.CreatePermanent(),
|
|
||||||
},
|
|
||||||
IdentifierType.Retainer => new[]
|
|
||||||
{
|
|
||||||
_actors.CreateRetainer(identifier.PlayerName,
|
|
||||||
identifier.Retainer == ActorIdentifier.RetainerType.Mannequin
|
|
||||||
? ActorIdentifier.RetainerType.Mannequin
|
|
||||||
: ActorIdentifier.RetainerType.Bell).CreatePermanent(),
|
|
||||||
},
|
|
||||||
IdentifierType.Npc => CreateNpcs(_actors, identifier),
|
|
||||||
_ => Array.Empty<ActorIdentifier>(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDesignChange(DesignChanged.Type type, Design design, object? data)
|
private void OnDesignChange(DesignChanged.Type type, Design design, object? data)
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Glamourer.Automation;
|
namespace Glamourer.Automation;
|
||||||
|
|
||||||
public class AutoDesignSet
|
public class AutoDesignSet(string name, ActorIdentifier[] identifiers, List<AutoDesign> designs)
|
||||||
{
|
{
|
||||||
public readonly List<AutoDesign> Designs;
|
public readonly List<AutoDesign> Designs = designs;
|
||||||
|
|
||||||
public string Name;
|
public string Name = name;
|
||||||
public ActorIdentifier[] Identifiers;
|
public ActorIdentifier[] Identifiers = identifiers;
|
||||||
public bool Enabled;
|
public bool Enabled;
|
||||||
public Base BaseState = Base.Current;
|
public Base BaseState = Base.Current;
|
||||||
|
|
||||||
|
|
@ -32,13 +32,6 @@ public class AutoDesignSet
|
||||||
: this(name, identifiers, new List<AutoDesign>())
|
: this(name, identifiers, new List<AutoDesign>())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public AutoDesignSet(string name, ActorIdentifier[] identifiers, List<AutoDesign> designs)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Identifiers = identifiers;
|
|
||||||
Designs = designs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Base : byte
|
public enum Base : byte
|
||||||
{
|
{
|
||||||
Current,
|
Current,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ public class FixedDesignMigrator(JobService jobs)
|
||||||
|
|
||||||
autoManager.AddDesign(set, leaf.Value);
|
autoManager.AddDesign(set, leaf.Value);
|
||||||
autoManager.ChangeJobCondition(set, set.Designs.Count - 1, design.Item2);
|
autoManager.ChangeJobCondition(set, set.Designs.Count - 1, design.Item2);
|
||||||
autoManager.ChangeApplicationType(set, set.Designs.Count - 1, design.Item3 ? AutoDesign.Type.All : 0);
|
autoManager.ChangeApplicationType(set, set.Designs.Count - 1, design.Item3 ? ApplicationType.All : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using Glamourer.Automation;
|
||||||
|
using Glamourer.Designs.Links;
|
||||||
using Glamourer.Interop.Penumbra;
|
using Glamourer.Interop.Penumbra;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
@ -35,10 +37,11 @@ public sealed class Design : DesignBase, ISavable
|
||||||
public DateTimeOffset LastEdit { get; internal set; }
|
public DateTimeOffset LastEdit { get; internal set; }
|
||||||
public LowerString Name { get; internal set; } = LowerString.Empty;
|
public LowerString Name { get; internal set; } = LowerString.Empty;
|
||||||
public string Description { get; internal set; } = string.Empty;
|
public string Description { get; internal set; } = string.Empty;
|
||||||
public string[] Tags { get; internal set; } = Array.Empty<string>();
|
public string[] Tags { get; internal set; } = [];
|
||||||
public int Index { get; internal set; }
|
public int Index { get; internal set; }
|
||||||
public string Color { get; internal set; } = string.Empty;
|
public string Color { get; internal set; } = string.Empty;
|
||||||
public SortedList<Mod, ModSettings> AssociatedMods { get; private set; } = new();
|
public SortedList<Mod, ModSettings> AssociatedMods { get; private set; } = [];
|
||||||
|
public LinkContainer Links { get; private set; } = [];
|
||||||
|
|
||||||
public string Incognito
|
public string Incognito
|
||||||
=> Identifier.ToString()[..8];
|
=> Identifier.ToString()[..8];
|
||||||
|
|
@ -64,6 +67,7 @@ public sealed class Design : DesignBase, ISavable
|
||||||
["Customize"] = SerializeCustomize(),
|
["Customize"] = SerializeCustomize(),
|
||||||
["Parameters"] = SerializeParameters(),
|
["Parameters"] = SerializeParameters(),
|
||||||
["Mods"] = SerializeMods(),
|
["Mods"] = SerializeMods(),
|
||||||
|
["Links"] = Links.Serialize(),
|
||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -95,24 +99,18 @@ public sealed class Design : DesignBase, ISavable
|
||||||
|
|
||||||
#region Deserialization
|
#region Deserialization
|
||||||
|
|
||||||
public static Design LoadDesign(CustomizeService customizations, ItemManager items, JObject json)
|
public static Design LoadDesign(CustomizeService customizations, ItemManager items, DesignLinkLoader linkLoader, JObject json)
|
||||||
{
|
{
|
||||||
var version = json["FileVersion"]?.ToObject<int>() ?? 0;
|
var version = json["FileVersion"]?.ToObject<int>() ?? 0;
|
||||||
return version switch
|
return version switch
|
||||||
{
|
{
|
||||||
FileVersion => LoadDesignV1(customizations, items, json),
|
FileVersion => LoadDesignV1(customizations, items, linkLoader, json),
|
||||||
_ => throw new Exception("The design to be loaded has no valid Version."),
|
_ => throw new Exception("The design to be loaded has no valid Version."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Design LoadDesignV1(CustomizeService customizations, ItemManager items, JObject json)
|
private static Design LoadDesignV1(CustomizeService customizations, ItemManager items, DesignLinkLoader linkLoader, JObject json)
|
||||||
{
|
{
|
||||||
static string[] ParseTags(JObject json)
|
|
||||||
{
|
|
||||||
var tags = json["Tags"]?.ToObject<string[]>() ?? Array.Empty<string>();
|
|
||||||
return tags.OrderBy(t => t).Distinct().ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
var creationDate = json["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate");
|
var creationDate = json["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate");
|
||||||
|
|
||||||
var design = new Design(customizations, items)
|
var design = new Design(customizations, items)
|
||||||
|
|
@ -131,8 +129,15 @@ public sealed class Design : DesignBase, ISavable
|
||||||
LoadEquip(items, json["Equipment"], design, design.Name, true);
|
LoadEquip(items, json["Equipment"], design, design.Name, true);
|
||||||
LoadMods(json["Mods"], design);
|
LoadMods(json["Mods"], design);
|
||||||
LoadParameters(json["Parameters"], design, design.Name);
|
LoadParameters(json["Parameters"], design, design.Name);
|
||||||
|
LoadLinks(linkLoader, json["Links"], design);
|
||||||
design.Color = json["Color"]?.ToObject<string>() ?? string.Empty;
|
design.Color = json["Color"]?.ToObject<string>() ?? string.Empty;
|
||||||
return design;
|
return design;
|
||||||
|
|
||||||
|
static string[] ParseTags(JObject json)
|
||||||
|
{
|
||||||
|
var tags = json["Tags"]?.ToObject<string[]>() ?? Array.Empty<string>();
|
||||||
|
return tags.OrderBy(t => t).Distinct().ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LoadMods(JToken? mods, Design design)
|
private static void LoadMods(JToken? mods, Design design)
|
||||||
|
|
@ -161,6 +166,29 @@ public sealed class Design : DesignBase, ISavable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void LoadLinks(DesignLinkLoader linkLoader, JToken? links, Design design)
|
||||||
|
{
|
||||||
|
if (links is not JObject obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Parse(obj["Before"] as JArray, LinkOrder.Before);
|
||||||
|
Parse(obj["After"] as JArray, LinkOrder.After);
|
||||||
|
return;
|
||||||
|
|
||||||
|
void Parse(JArray? array, LinkOrder order)
|
||||||
|
{
|
||||||
|
if (array == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var obj in array.OfType<JObject>())
|
||||||
|
{
|
||||||
|
var identifier = obj["Design"]?.ToObject<Guid>() ?? throw new ArgumentNullException("Design");
|
||||||
|
var type = (ApplicationType)(obj["Type"]?.ToObject<uint>() ?? 0);
|
||||||
|
linkLoader.AddObject(design, new LinkData(identifier, type, order));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ISavable
|
#region ISavable
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Glamourer.GameData;
|
using Glamourer.Designs.Links;
|
||||||
|
using Glamourer.GameData;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using Glamourer.Utility;
|
using Glamourer.Utility;
|
||||||
|
|
@ -10,7 +11,7 @@ using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.Designs;
|
namespace Glamourer.Designs;
|
||||||
|
|
||||||
public class DesignConverter(ItemManager _items, DesignManager _designs, CustomizeService _customize, HumanModelList _humans)
|
public class DesignConverter(ItemManager _items, DesignManager _designs, CustomizeService _customize, HumanModelList _humans, DesignLinkLoader _linkLoader)
|
||||||
{
|
{
|
||||||
public const byte Version = 6;
|
public const byte Version = 6;
|
||||||
|
|
||||||
|
|
@ -75,7 +76,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
||||||
case (byte)'{':
|
case (byte)'{':
|
||||||
var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes));
|
var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes));
|
||||||
ret = jObj1["Identifier"] != null
|
ret = jObj1["Identifier"] != null
|
||||||
? Design.LoadDesign(_customize, _items, jObj1)
|
? Design.LoadDesign(_customize, _items, _linkLoader, jObj1)
|
||||||
: DesignBase.LoadDesignBase(_customize, _items, jObj1);
|
: DesignBase.LoadDesignBase(_customize, _items, jObj1);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
|
@ -90,7 +91,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
||||||
var jObj2 = JObject.Parse(decompressed);
|
var jObj2 = JObject.Parse(decompressed);
|
||||||
Debug.Assert(version == 3);
|
Debug.Assert(version == 3);
|
||||||
ret = jObj2["Identifier"] != null
|
ret = jObj2["Identifier"] != null
|
||||||
? Design.LoadDesign(_customize, _items, jObj2)
|
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2)
|
||||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +102,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
||||||
var jObj2 = JObject.Parse(decompressed);
|
var jObj2 = JObject.Parse(decompressed);
|
||||||
Debug.Assert(version == 5);
|
Debug.Assert(version == 5);
|
||||||
ret = jObj2["Identifier"] != null
|
ret = jObj2["Identifier"] != null
|
||||||
? Design.LoadDesign(_customize, _items, jObj2)
|
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2)
|
||||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +112,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
||||||
var jObj2 = JObject.Parse(decompressed);
|
var jObj2 = JObject.Parse(decompressed);
|
||||||
Debug.Assert(version == 6);
|
Debug.Assert(version == 6);
|
||||||
ret = jObj2["Identifier"] != null
|
ret = jObj2["Identifier"] != null
|
||||||
? Design.LoadDesign(_customize, _items, jObj2)
|
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2)
|
||||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
using Glamourer.Designs.Links;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.GameData;
|
using Glamourer.GameData;
|
||||||
using Glamourer.Interop.Penumbra;
|
using Glamourer.Interop.Penumbra;
|
||||||
|
|
@ -20,30 +21,33 @@ public class DesignManager
|
||||||
private readonly HumanModelList _humans;
|
private readonly HumanModelList _humans;
|
||||||
private readonly SaveService _saveService;
|
private readonly SaveService _saveService;
|
||||||
private readonly DesignChanged _event;
|
private readonly DesignChanged _event;
|
||||||
private readonly List<Design> _designs = [];
|
private readonly DesignStorage _designs;
|
||||||
private readonly Dictionary<Guid, DesignData> _undoStore = [];
|
private readonly Dictionary<Guid, DesignData> _undoStore = [];
|
||||||
|
|
||||||
public IReadOnlyList<Design> Designs
|
public IReadOnlyList<Design> Designs
|
||||||
=> _designs;
|
=> _designs;
|
||||||
|
|
||||||
public DesignManager(SaveService saveService, ItemManager items, CustomizeService customizations,
|
public DesignManager(SaveService saveService, ItemManager items, CustomizeService customizations,
|
||||||
DesignChanged @event, HumanModelList humans)
|
DesignChanged @event, HumanModelList humans, DesignStorage storage, DesignLinkLoader designLinkLoader)
|
||||||
{
|
{
|
||||||
_saveService = saveService;
|
_designs = storage;
|
||||||
_items = items;
|
_saveService = saveService;
|
||||||
_customizations = customizations;
|
_items = items;
|
||||||
_event = @event;
|
_customizations = customizations;
|
||||||
_humans = humans;
|
_event = @event;
|
||||||
|
_humans = humans;
|
||||||
|
|
||||||
|
LoadDesigns(designLinkLoader);
|
||||||
CreateDesignFolder(saveService);
|
CreateDesignFolder(saveService);
|
||||||
LoadDesigns();
|
|
||||||
MigrateOldDesigns();
|
MigrateOldDesigns();
|
||||||
|
designLinkLoader.SetAllObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clear currently loaded designs and load all designs anew from file.
|
/// Clear currently loaded designs and load all designs anew from file.
|
||||||
/// Invalid data is fixed, but changes are not saved until manual changes.
|
/// Invalid data is fixed, but changes are not saved until manual changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LoadDesigns()
|
public void LoadDesigns(DesignLinkLoader linkLoader)
|
||||||
{
|
{
|
||||||
_humans.Awaiter.Wait();
|
_humans.Awaiter.Wait();
|
||||||
_customizations.Awaiter.Wait();
|
_customizations.Awaiter.Wait();
|
||||||
|
|
@ -59,7 +63,7 @@ public class DesignManager
|
||||||
{
|
{
|
||||||
var text = File.ReadAllText(f.FullName);
|
var text = File.ReadAllText(f.FullName);
|
||||||
var data = JObject.Parse(text);
|
var data = JObject.Parse(text);
|
||||||
var design = Design.LoadDesign(_customizations, _items, data);
|
var design = Design.LoadDesign(_customizations, _items, linkLoader, data);
|
||||||
designs.Value!.Add((design, f.FullName));
|
designs.Value!.Add((design, f.FullName));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -497,14 +501,14 @@ public class DesignManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the bool value of one of the meta flags. </summary>
|
/// <summary> Change the bool value of one of the meta flags. </summary>
|
||||||
public void ChangeMeta(Design design, ActorState.MetaIndex metaIndex, bool value)
|
public void ChangeMeta(Design design, MetaIndex metaIndex, bool value)
|
||||||
{
|
{
|
||||||
var change = metaIndex switch
|
var change = metaIndex switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.Wetness => design.GetDesignDataRef().SetIsWet(value),
|
MetaIndex.Wetness => design.GetDesignDataRef().SetIsWet(value),
|
||||||
ActorState.MetaIndex.HatState => design.GetDesignDataRef().SetHatVisible(value),
|
MetaIndex.HatState => design.GetDesignDataRef().SetHatVisible(value),
|
||||||
ActorState.MetaIndex.VisorState => design.GetDesignDataRef().SetVisor(value),
|
MetaIndex.VisorState => design.GetDesignDataRef().SetVisor(value),
|
||||||
ActorState.MetaIndex.WeaponState => design.GetDesignDataRef().SetWeaponVisible(value),
|
MetaIndex.WeaponState => design.GetDesignDataRef().SetWeaponVisible(value),
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(metaIndex), metaIndex, null),
|
_ => throw new ArgumentOutOfRangeException(nameof(metaIndex), metaIndex, null),
|
||||||
};
|
};
|
||||||
if (!change)
|
if (!change)
|
||||||
|
|
@ -517,14 +521,14 @@ 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, ActorState.MetaIndex metaIndex, bool value)
|
public void ChangeApplyMeta(Design design, MetaIndex metaIndex, bool value)
|
||||||
{
|
{
|
||||||
var change = metaIndex switch
|
var change = metaIndex switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.Wetness => design.SetApplyWetness(value),
|
MetaIndex.Wetness => design.SetApplyWetness(value),
|
||||||
ActorState.MetaIndex.HatState => design.SetApplyHatVisible(value),
|
MetaIndex.HatState => design.SetApplyHatVisible(value),
|
||||||
ActorState.MetaIndex.VisorState => design.SetApplyVisorToggle(value),
|
MetaIndex.VisorState => design.SetApplyVisorToggle(value),
|
||||||
ActorState.MetaIndex.WeaponState => design.SetApplyWeaponVisible(value),
|
MetaIndex.WeaponState => design.SetApplyWeaponVisible(value),
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(metaIndex), metaIndex, null),
|
_ => throw new ArgumentOutOfRangeException(nameof(metaIndex), metaIndex, null),
|
||||||
};
|
};
|
||||||
if (!change)
|
if (!change)
|
||||||
|
|
|
||||||
6
Glamourer/Designs/DesignStorage.cs
Normal file
6
Glamourer/Designs/DesignStorage.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
using OtterGui.Services;
|
||||||
|
|
||||||
|
namespace Glamourer.Designs;
|
||||||
|
|
||||||
|
public class DesignStorage : List<Design>, IService
|
||||||
|
{}
|
||||||
18
Glamourer/Designs/Links/DesignLink.cs
Normal file
18
Glamourer/Designs/Links/DesignLink.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Glamourer.Automation;
|
||||||
|
|
||||||
|
namespace Glamourer.Designs.Links;
|
||||||
|
|
||||||
|
public record struct DesignLink(Design Link, ApplicationType Type);
|
||||||
|
|
||||||
|
public readonly record struct LinkData(Guid Identity, ApplicationType Type, LinkOrder Order)
|
||||||
|
{
|
||||||
|
public override string ToString()
|
||||||
|
=> Identity.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LinkOrder : byte
|
||||||
|
{
|
||||||
|
Self,
|
||||||
|
After,
|
||||||
|
Before,
|
||||||
|
};
|
||||||
27
Glamourer/Designs/Links/DesignLinkLoader.cs
Normal file
27
Glamourer/Designs/Links/DesignLinkLoader.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
using OtterGui.Services;
|
||||||
|
|
||||||
|
namespace Glamourer.Designs.Links;
|
||||||
|
|
||||||
|
public sealed class DesignLinkLoader(DesignStorage designStorage, MessageService messager)
|
||||||
|
: DelayedReferenceLoader<Design, LinkData>(messager), IService
|
||||||
|
{
|
||||||
|
protected override bool TryGetObject(LinkData data, [NotNullWhen(true)] out Design? obj)
|
||||||
|
=> designStorage.FindFirst(d => d.Identifier == data.Identity, out obj);
|
||||||
|
|
||||||
|
protected override bool SetObject(Design parent, Design child, LinkData data, out string error)
|
||||||
|
=> LinkContainer.AddLink(parent, child, data.Type, data.Order, out error);
|
||||||
|
|
||||||
|
protected override void HandleChildNotFound(Design parent, LinkData data)
|
||||||
|
{
|
||||||
|
Messager.AddMessage(new Notification(
|
||||||
|
$"Could not find the design {data.Identity}. If this design was deleted, please re-save {parent.Identifier}.",
|
||||||
|
NotificationType.Warning));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleChildNotSet(Design parent, Design child, string error)
|
||||||
|
=> Messager.AddMessage(new Notification($"Could not link {child.Identifier} to {parent.Identifier}: {error}",
|
||||||
|
NotificationType.Warning));
|
||||||
|
}
|
||||||
74
Glamourer/Designs/Links/DesignLinkManager.cs
Normal file
74
Glamourer/Designs/Links/DesignLinkManager.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
using Glamourer.Automation;
|
||||||
|
using Glamourer.Events;
|
||||||
|
using Glamourer.Services;
|
||||||
|
using OtterGui.Services;
|
||||||
|
|
||||||
|
namespace Glamourer.Designs.Links;
|
||||||
|
|
||||||
|
public sealed class DesignLinkManager : IService, IDisposable
|
||||||
|
{
|
||||||
|
private readonly DesignStorage _storage;
|
||||||
|
private readonly DesignChanged _event;
|
||||||
|
private readonly SaveService _saveService;
|
||||||
|
|
||||||
|
public DesignLinkManager(DesignStorage storage, DesignChanged @event, SaveService saveService)
|
||||||
|
{
|
||||||
|
_storage = storage;
|
||||||
|
_event = @event;
|
||||||
|
_saveService = saveService;
|
||||||
|
|
||||||
|
_event.Subscribe(OnDesignChanged, DesignChanged.Priority.DesignLinkManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
=> _event.Unsubscribe(OnDesignChanged);
|
||||||
|
|
||||||
|
public void MoveDesignLink(Design parent, int idxFrom, LinkOrder orderFrom, int idxTo, LinkOrder orderTo)
|
||||||
|
{
|
||||||
|
if (!parent.Links.Reorder(idxFrom, orderFrom, idxTo, orderTo))
|
||||||
|
return;
|
||||||
|
|
||||||
|
parent.LastEdit = DateTimeOffset.UtcNow;
|
||||||
|
_saveService.QueueSave(parent);
|
||||||
|
Glamourer.Log.Debug($"Moved link from {orderFrom} {idxFrom} to {idxTo} {orderTo}.");
|
||||||
|
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddDesignLink(Design parent, Design child, LinkOrder order)
|
||||||
|
{
|
||||||
|
if (!LinkContainer.AddLink(parent, child, ApplicationType.All, order, out _))
|
||||||
|
return;
|
||||||
|
|
||||||
|
parent.LastEdit = DateTimeOffset.UtcNow;
|
||||||
|
_saveService.QueueSave(parent);
|
||||||
|
Glamourer.Log.Debug($"Added new {order} link to {child.Identifier} for {parent.Identifier}.");
|
||||||
|
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveDesignLink(Design parent, int idx, LinkOrder order)
|
||||||
|
{
|
||||||
|
if (!parent.Links.Remove(idx, order))
|
||||||
|
return;
|
||||||
|
|
||||||
|
parent.LastEdit = DateTimeOffset.UtcNow;
|
||||||
|
_saveService.QueueSave(parent);
|
||||||
|
Glamourer.Log.Debug($"Removed the {order} link at {idx} for {parent.Identifier}.");
|
||||||
|
_event.Invoke(DesignChanged.Type.ChangedLink, parent, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDesignChanged(DesignChanged.Type type, Design deletedDesign, object? _)
|
||||||
|
{
|
||||||
|
if (type is not DesignChanged.Type.Deleted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var design in _storage)
|
||||||
|
{
|
||||||
|
if (design.Links.Remove(deletedDesign))
|
||||||
|
{
|
||||||
|
design.LastEdit = DateTimeOffset.UtcNow;
|
||||||
|
Glamourer.Log.Debug($"Removed {deletedDesign.Identifier} from {design.Identifier} links due to deletion.");
|
||||||
|
_saveService.QueueSave(design);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
265
Glamourer/Designs/Links/DesignMerger.cs
Normal file
265
Glamourer/Designs/Links/DesignMerger.cs
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
using Glamourer.Automation;
|
||||||
|
using Glamourer.Events;
|
||||||
|
using Glamourer.GameData;
|
||||||
|
using Glamourer.Services;
|
||||||
|
using Glamourer.State;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
namespace Glamourer.Designs.Links;
|
||||||
|
|
||||||
|
using WeaponDict = Dictionary<FullEquipType, (EquipItem, StateChanged.Source)>;
|
||||||
|
|
||||||
|
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 class DesignMerger(DesignManager designManager, CustomizeService _customize)
|
||||||
|
{
|
||||||
|
public MergedDesign Merge(IEnumerable<(DesignBase?, ApplicationType)> designs, in DesignData baseRef)
|
||||||
|
{
|
||||||
|
var ret = new MergedDesign(designManager);
|
||||||
|
CustomizeFlag fixFlags = 0;
|
||||||
|
foreach (var (design, type) in designs)
|
||||||
|
{
|
||||||
|
if (type is 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ref readonly var data = ref design == null ? ref baseRef : ref design.GetDesignDataRef();
|
||||||
|
var source = design == null ? StateChanged.Source.Game : StateChanged.Source.Manual;
|
||||||
|
|
||||||
|
if (!data.IsHuman)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var (equipFlags, customizeFlags, crestFlags, parameterFlags, applyHat, applyVisor, applyWeapon, applyWet) = type.ApplyWhat(design);
|
||||||
|
ReduceMeta(data, applyHat, applyVisor, applyWeapon, applyWet, ret, source);
|
||||||
|
ReduceCustomize(data, customizeFlags, ref fixFlags, ret, source);
|
||||||
|
ReduceEquip(data, equipFlags, ret, source);
|
||||||
|
ReduceMainhands(data, equipFlags, ret, source);
|
||||||
|
ReduceOffhands(data, equipFlags, ret, source);
|
||||||
|
ReduceCrests(data, crestFlags, ret, source);
|
||||||
|
ReduceParameters(data, parameterFlags, ret, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyFixFlags(ret, fixFlags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void ReduceMeta(in DesignData design, bool applyHat, bool applyVisor, bool applyWeapon, bool applyWet, MergedDesign ret,
|
||||||
|
StateChanged.Source source)
|
||||||
|
{
|
||||||
|
if (applyHat && !ret.Design.DoApplyHatVisible())
|
||||||
|
{
|
||||||
|
ret.Design.SetApplyHatVisible(true);
|
||||||
|
ret.Design.GetDesignDataRef().SetHatVisible(design.IsHatVisible());
|
||||||
|
ret.Source[MetaIndex.HatState] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applyVisor && !ret.Design.DoApplyVisorToggle())
|
||||||
|
{
|
||||||
|
ret.Design.SetApplyVisorToggle(true);
|
||||||
|
ret.Design.GetDesignDataRef().SetVisor(design.IsVisorToggled());
|
||||||
|
ret.Source[MetaIndex.VisorState] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applyWeapon && !ret.Design.DoApplyWeaponVisible())
|
||||||
|
{
|
||||||
|
ret.Design.SetApplyWeaponVisible(true);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
crestFlags &= ~ret.Design.ApplyCrest;
|
||||||
|
if (crestFlags == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||||
|
{
|
||||||
|
if (!crestFlags.HasFlag(slot))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret.Design.GetDesignDataRef().SetCrest(slot, design.Crest(slot));
|
||||||
|
ret.Design.SetApplyCrest(slot, true);
|
||||||
|
ret.Source[slot] = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReduceParameters(in DesignData design, CustomizeParameterFlag parameterFlags, MergedDesign ret,
|
||||||
|
StateChanged.Source source)
|
||||||
|
{
|
||||||
|
parameterFlags &= ~ret.Design.ApplyParameters;
|
||||||
|
if (parameterFlags == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||||
|
{
|
||||||
|
if (!parameterFlags.HasFlag(flag))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret.Design.GetDesignDataRef().Parameters.Set(flag, design.Parameters[flag]);
|
||||||
|
ret.Design.SetApplyParameter(flag, true);
|
||||||
|
ret.Source[flag] = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReduceEquip(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source)
|
||||||
|
{
|
||||||
|
equipFlags &= ~ret.Design.ApplyEquip;
|
||||||
|
if (equipFlags == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
|
{
|
||||||
|
var flag = slot.ToFlag();
|
||||||
|
if (equipFlags.HasFlag(flag))
|
||||||
|
{
|
||||||
|
ret.Design.GetDesignDataRef().SetItem(slot, design.Item(slot));
|
||||||
|
ret.Design.SetApplyEquip(slot, true);
|
||||||
|
ret.Source[slot, false] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stainFlag = slot.ToStainFlag();
|
||||||
|
if (equipFlags.HasFlag(stainFlag))
|
||||||
|
{
|
||||||
|
ret.Design.GetDesignDataRef().SetStain(slot, design.Stain(slot));
|
||||||
|
ret.Design.SetApplyStain(slot, true);
|
||||||
|
ret.Source[slot, true] = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var slot in EquipSlotExtensions.WeaponSlots)
|
||||||
|
{
|
||||||
|
var stainFlag = slot.ToStainFlag();
|
||||||
|
if (equipFlags.HasFlag(stainFlag))
|
||||||
|
{
|
||||||
|
ret.Design.GetDesignDataRef().SetStain(slot, design.Stain(slot));
|
||||||
|
ret.Design.SetApplyStain(slot, true);
|
||||||
|
ret.Source[slot, true] = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReduceMainhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source)
|
||||||
|
{
|
||||||
|
if (!equipFlags.HasFlag(EquipFlag.Mainhand))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret.Design.SetApplyEquip(EquipSlot.MainHand, true);
|
||||||
|
var weapon = design.Item(EquipSlot.MainHand);
|
||||||
|
ret.Weapons.TryAdd(weapon.Type, (weapon, source));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReduceOffhands(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateChanged.Source source)
|
||||||
|
{
|
||||||
|
if (!equipFlags.HasFlag(EquipFlag.Offhand))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret.Design.SetApplyEquip(EquipSlot.OffHand, true);
|
||||||
|
var weapon = design.Item(EquipSlot.OffHand);
|
||||||
|
if (weapon.Valid)
|
||||||
|
ret.Weapons.TryAdd(weapon.Type, (weapon, source));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReduceCustomize(in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag fixFlags, MergedDesign ret,
|
||||||
|
StateChanged.Source source)
|
||||||
|
{
|
||||||
|
customizeFlags &= ~ret.Design.ApplyCustomizeRaw;
|
||||||
|
if (customizeFlags == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Skip anything not human.
|
||||||
|
if (!ret.Design.DesignData.IsHuman || !design.IsHuman)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var customize = ret.Design.DesignData.Customize;
|
||||||
|
if (customizeFlags.HasFlag(CustomizeFlag.Clan))
|
||||||
|
{
|
||||||
|
fixFlags |= _customize.ChangeClan(ref customize, design.Customize.Clan);
|
||||||
|
ret.Design.SetApplyCustomize(CustomizeIndex.Clan, true);
|
||||||
|
ret.Design.SetApplyCustomize(CustomizeIndex.Race, true);
|
||||||
|
customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race);
|
||||||
|
ret.Source[CustomizeIndex.Clan] = source;
|
||||||
|
ret.Source[CustomizeIndex.Race] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customizeFlags.HasFlag(CustomizeFlag.Gender))
|
||||||
|
{
|
||||||
|
fixFlags |= _customize.ChangeGender(ref customize, design.Customize.Gender);
|
||||||
|
ret.Design.SetApplyCustomize(CustomizeIndex.Gender, true);
|
||||||
|
customizeFlags &= ~CustomizeFlag.Gender;
|
||||||
|
ret.Source[CustomizeIndex.Gender] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customizeFlags.HasFlag(CustomizeFlag.Face))
|
||||||
|
{
|
||||||
|
customize[CustomizeIndex.Face] = design.Customize.Face;
|
||||||
|
ret.Design.SetApplyCustomize(CustomizeIndex.Face, true);
|
||||||
|
customizeFlags &= ~CustomizeFlag.Face;
|
||||||
|
ret.Source[CustomizeIndex.Face] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
var set = _customize.Manager.GetSet(customize.Clan, customize.Gender);
|
||||||
|
var face = customize.Face;
|
||||||
|
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||||
|
{
|
||||||
|
var flag = index.ToFlag();
|
||||||
|
if (!customizeFlags.HasFlag(flag))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var value = design.Customize[index];
|
||||||
|
if (!CustomizeService.IsCustomizationValid(set, face, index, value, out var data))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
customize[index] = data?.Value ?? value;
|
||||||
|
ret.Design.SetApplyCustomize(index, true);
|
||||||
|
ret.Source[index] = source;
|
||||||
|
fixFlags &= ~flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyFixFlags(MergedDesign ret, CustomizeFlag fixFlags)
|
||||||
|
{
|
||||||
|
if (fixFlags == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var source = ret.Design.DoApplyCustomize(CustomizeIndex.Clan)
|
||||||
|
? ret.Source[CustomizeIndex.Clan]
|
||||||
|
: ret.Source[CustomizeIndex.Gender];
|
||||||
|
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||||
|
{
|
||||||
|
var flag = index.ToFlag();
|
||||||
|
if (!fixFlags.HasFlag(flag))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret.Source[index] = source;
|
||||||
|
ret.Design.SetApplyCustomize(index, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
177
Glamourer/Designs/Links/LinkContainer.cs
Normal file
177
Glamourer/Designs/Links/LinkContainer.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
using Glamourer.Automation;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using OtterGui.Filesystem;
|
||||||
|
|
||||||
|
namespace Glamourer.Designs.Links;
|
||||||
|
|
||||||
|
public sealed class LinkContainer : List<DesignLink>
|
||||||
|
{
|
||||||
|
public List<DesignLink> Before
|
||||||
|
=> this;
|
||||||
|
|
||||||
|
public readonly List<DesignLink> After = [];
|
||||||
|
|
||||||
|
public new int Count
|
||||||
|
=> base.Count + After.Count;
|
||||||
|
|
||||||
|
public bool Reorder(int fromIndex, LinkOrder fromOrder, int toIndex, LinkOrder toOrder)
|
||||||
|
{
|
||||||
|
var fromList = fromOrder switch
|
||||||
|
{
|
||||||
|
LinkOrder.Before => Before,
|
||||||
|
LinkOrder.After => After,
|
||||||
|
_ => throw new ArgumentException("Invalid link order."),
|
||||||
|
};
|
||||||
|
|
||||||
|
var toList = toOrder switch
|
||||||
|
{
|
||||||
|
LinkOrder.Before => Before,
|
||||||
|
LinkOrder.After => After,
|
||||||
|
_ => throw new ArgumentException("Invalid link order."),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fromList == toList)
|
||||||
|
return fromList.Move(fromIndex, toIndex);
|
||||||
|
|
||||||
|
if (fromIndex < 0 || fromIndex >= fromList.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
toIndex = Math.Clamp(toIndex, 0, toList.Count);
|
||||||
|
toList.Insert(toIndex, fromList[fromIndex]);
|
||||||
|
fromList.RemoveAt(fromIndex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(int idx, LinkOrder order)
|
||||||
|
{
|
||||||
|
var list = order switch
|
||||||
|
{
|
||||||
|
LinkOrder.Before => Before,
|
||||||
|
LinkOrder.After => After,
|
||||||
|
_ => throw new ArgumentException("Invalid link order."),
|
||||||
|
};
|
||||||
|
if (idx < 0 || idx >= list.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
list.RemoveAt(idx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanAddLink(Design parent, Design child, LinkOrder order, out string error)
|
||||||
|
{
|
||||||
|
if (parent == child)
|
||||||
|
{
|
||||||
|
error = $"Can not link {parent.Identifier} with itself.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.Links.Contains(child))
|
||||||
|
{
|
||||||
|
error = $"Design {parent.Identifier} already contains a direct link to {child.Identifier}.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetAllLinks(parent).Any(l => l.Link.Link == child && l.Order != order))
|
||||||
|
{
|
||||||
|
error = $"Adding {child.Identifier} to {parent.Identifier}s links would create a circle, the parent already links to the child in the opposite direction.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetAllLinks(child).Any(l => l.Link.Link == parent && l.Order == order))
|
||||||
|
{
|
||||||
|
error = $"Adding {child.Identifier} to {parent.Identifier}s links would create a circle, the child already links to the parent in the opposite direction.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = string.Empty;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AddLink(Design parent, Design child, ApplicationType type, LinkOrder order, out string error)
|
||||||
|
{
|
||||||
|
if (!CanAddLink(parent, child, order, out error))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var list = order switch
|
||||||
|
{
|
||||||
|
LinkOrder.Before => parent.Links.Before,
|
||||||
|
LinkOrder.After => parent.Links.After,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
error = $"Order {order} is invalid.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
type &= ApplicationType.All;
|
||||||
|
list.Add(new DesignLink(child, type));
|
||||||
|
error = string.Empty;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(Design child)
|
||||||
|
=> Before.Any(l => l.Link == child) || After.Any(l => l.Link == child);
|
||||||
|
|
||||||
|
public bool Remove(Design child)
|
||||||
|
=> Before.RemoveAll(l => l.Link == child) + After.RemoveAll(l => l.Link == child) > 0;
|
||||||
|
|
||||||
|
public static IEnumerable<(DesignLink Link, LinkOrder Order)> GetAllLinks(Design design)
|
||||||
|
{
|
||||||
|
var set = new HashSet<Design>(design.Links.Count * 4);
|
||||||
|
return GetAllLinks(new DesignLink(design, ApplicationType.All), LinkOrder.Self, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<(DesignLink Link, LinkOrder Order)> GetAllLinks(DesignLink design, LinkOrder currentOrder, ISet<Design> visited)
|
||||||
|
{
|
||||||
|
if (design.Link.Links.Count == 0)
|
||||||
|
{
|
||||||
|
if (visited.Add(design.Link))
|
||||||
|
yield return (design, currentOrder);
|
||||||
|
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var link in design.Link.Links.Before
|
||||||
|
.Where(l => !visited.Contains(l.Link))
|
||||||
|
.SelectMany(l => GetAllLinks(l, currentOrder == LinkOrder.After ? LinkOrder.After : LinkOrder.Before, visited)))
|
||||||
|
yield return link;
|
||||||
|
|
||||||
|
if (visited.Add(design.Link))
|
||||||
|
yield return (design, currentOrder);
|
||||||
|
|
||||||
|
foreach (var link in design.Link.Links.After.Where(l => !visited.Contains(l.Link))
|
||||||
|
.SelectMany(l => GetAllLinks(l, currentOrder == LinkOrder.Before ? LinkOrder.Before : LinkOrder.After, visited)))
|
||||||
|
yield return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JObject Serialize()
|
||||||
|
{
|
||||||
|
var before = new JArray();
|
||||||
|
foreach (var link in Before)
|
||||||
|
{
|
||||||
|
before.Add(new JObject
|
||||||
|
{
|
||||||
|
["Design"] = link.Link.Identifier,
|
||||||
|
["Type"] = (uint)link.Type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var after = new JArray();
|
||||||
|
foreach (var link in After)
|
||||||
|
{
|
||||||
|
before.Add(new JObject
|
||||||
|
{
|
||||||
|
["Design"] = link.Link.Identifier,
|
||||||
|
["Type"] = (uint)link.Type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JObject
|
||||||
|
{
|
||||||
|
[nameof(Before)] = before,
|
||||||
|
[nameof(After)] = after,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Glamourer.Events;
|
||||||
/// <item>Parameter is any additional data depending on the type of change. </item>
|
/// <item>Parameter is any additional data depending on the type of change. </item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class DesignChanged()
|
public sealed class DesignChanged()
|
||||||
: EventWrapper<DesignChanged.Type, Design, object?, DesignChanged.Priority>(nameof(DesignChanged))
|
: EventWrapper<DesignChanged.Type, Design, object?, DesignChanged.Priority>(nameof(DesignChanged))
|
||||||
{
|
{
|
||||||
public enum Type
|
public enum Type
|
||||||
|
|
@ -50,6 +50,9 @@ public sealed class DesignChanged()
|
||||||
/// <summary> An existing design had an existing associated mod removed. Data is the Mod and its Settings [(Mod, ModSettings)]. </summary>
|
/// <summary> An existing design had an existing associated mod removed. Data is the Mod and its Settings [(Mod, ModSettings)]. </summary>
|
||||||
RemovedMod,
|
RemovedMod,
|
||||||
|
|
||||||
|
/// <summary> An existing design had a link to a different design added, removed or moved. Data is null. </summary>
|
||||||
|
ChangedLink,
|
||||||
|
|
||||||
/// <summary> An existing design had a customization changed. Data is the old value, the new value and the type [(CustomizeValue, CustomizeValue, CustomizeIndex)]. </summary>
|
/// <summary> An existing design had a customization changed. Data is the old value, the new value and the type [(CustomizeValue, CustomizeValue, CustomizeIndex)]. </summary>
|
||||||
Customize,
|
Customize,
|
||||||
|
|
||||||
|
|
@ -92,6 +95,9 @@ public sealed class DesignChanged()
|
||||||
|
|
||||||
public enum Priority
|
public enum Priority
|
||||||
{
|
{
|
||||||
|
/// <seealso cref="Designs.Links.DesignLinkManager.OnDesignChange"/>
|
||||||
|
DesignLinkManager = 1,
|
||||||
|
|
||||||
/// <seealso cref="Automation.AutoDesignManager.OnDesignChange"/>
|
/// <seealso cref="Automation.AutoDesignManager.OnDesignChange"/>
|
||||||
AutoDesignManager = 1,
|
AutoDesignManager = 1,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ public class ActorPanel(
|
||||||
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, StateChanged.Source.Manual);
|
||||||
|
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(ActorState.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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,21 +187,21 @@ public class ActorPanel(
|
||||||
{
|
{
|
||||||
using (_ = ImRaii.Group())
|
using (_ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(ActorState.MetaIndex.HatState, _stateManager, _state!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.HatState, _stateManager, _state!));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Head, _stateManager, _state!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Head, _stateManager, _state!));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (_ = ImRaii.Group())
|
using (_ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(ActorState.MetaIndex.VisorState, _stateManager, _state!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.VisorState, _stateManager, _state!));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Body, _stateManager, _state!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.Body, _stateManager, _state!));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (_ = ImRaii.Group())
|
using (_ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(ActorState.MetaIndex.WeaponState, _stateManager, _state!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromState(MetaIndex.WeaponState, _stateManager, _state!));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _stateManager, _state!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromState(CrestFlag.OffHand, _stateManager, _state!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -369,13 +369,13 @@ public class SetPanel(
|
||||||
private void DrawApplicationTypeBoxes(AutoDesignSet set, AutoDesign design, int autoDesignIndex, bool singleLine)
|
private void DrawApplicationTypeBoxes(AutoDesignSet set, AutoDesign design, int autoDesignIndex, bool singleLine)
|
||||||
{
|
{
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(2 * ImGuiHelpers.GlobalScale));
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(2 * ImGuiHelpers.GlobalScale));
|
||||||
var newType = design.ApplicationType;
|
var newType = design.Type;
|
||||||
var newTypeInt = (uint)newType;
|
var newTypeInt = (uint)newType;
|
||||||
style.Push(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale);
|
style.Push(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale);
|
||||||
using (_ = ImRaii.PushColor(ImGuiCol.Border, ColorId.FolderLine.Value()))
|
using (_ = ImRaii.PushColor(ImGuiCol.Border, ColorId.FolderLine.Value()))
|
||||||
{
|
{
|
||||||
if (ImGui.CheckboxFlags("##all", ref newTypeInt, (uint)AutoDesign.Type.All))
|
if (ImGui.CheckboxFlags("##all", ref newTypeInt, (uint)ApplicationType.All))
|
||||||
newType = (AutoDesign.Type)newTypeInt;
|
newType = (ApplicationType)newTypeInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
style.Pop();
|
style.Pop();
|
||||||
|
|
@ -385,7 +385,7 @@ public class SetPanel(
|
||||||
void Box(int idx)
|
void Box(int idx)
|
||||||
{
|
{
|
||||||
var (type, description) = Types[idx];
|
var (type, description) = Types[idx];
|
||||||
var value = design.ApplicationType.HasFlag(type);
|
var value = design.Type.HasFlag(type);
|
||||||
if (ImGui.Checkbox($"##{(byte)type}", ref value))
|
if (ImGui.Checkbox($"##{(byte)type}", ref value))
|
||||||
newType = value ? newType | type : newType & ~type;
|
newType = value ? newType | type : newType & ~type;
|
||||||
ImGuiUtil.HoverTooltip(description);
|
ImGuiUtil.HoverTooltip(description);
|
||||||
|
|
@ -429,14 +429,14 @@ public class SetPanel(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static readonly IReadOnlyList<(AutoDesign.Type, string)> Types = new[]
|
private static readonly IReadOnlyList<(ApplicationType, string)> Types = new[]
|
||||||
{
|
{
|
||||||
(AutoDesign.Type.Customizations,
|
(ApplicationType.Customizations,
|
||||||
"Apply all customization changes that are enabled in this design and that are valid in a fixed design and for the given race and gender."),
|
"Apply all customization changes that are enabled in this design and that are valid in a fixed design and for the given race and gender."),
|
||||||
(AutoDesign.Type.Armor, "Apply all armor piece changes that are enabled in this design and that are valid in a fixed design."),
|
(ApplicationType.Armor, "Apply all armor piece changes that are enabled in this design and that are valid in a fixed design."),
|
||||||
(AutoDesign.Type.Accessories, "Apply all accessory changes that are enabled in this design and that are valid in a fixed design."),
|
(ApplicationType.Accessories, "Apply all accessory changes that are enabled in this design and that are valid in a fixed design."),
|
||||||
(AutoDesign.Type.GearCustomization, "Apply all dye and crest changes that are enabled in this design."),
|
(ApplicationType.GearCustomization, "Apply all dye and crest changes that are enabled in this design."),
|
||||||
(AutoDesign.Type.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
|
(ApplicationType.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
|
||||||
};
|
};
|
||||||
|
|
||||||
private sealed class JobGroupCombo : FilterComboCache<JobGroup>
|
private sealed class JobGroupCombo : FilterComboCache<JobGroup>
|
||||||
|
|
|
||||||
|
|
@ -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[ActorState.MetaIndex.ModelId]);
|
PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state.Source[MetaIndex.ModelId]);
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
PrintRow("Wetness", state.BaseData.IsWet(), state.ModelData.IsWet(), state[ActorState.MetaIndex.Wetness]);
|
PrintRow("Wetness", state.BaseData.IsWet(), state.ModelData.IsWet(), state.Source[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[ActorState.MetaIndex.HatState]);
|
PrintRow("Hat Visible", state.BaseData.IsHatVisible(), state.ModelData.IsHatVisible(), state.Source[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[ActorState.MetaIndex.VisorState]);
|
state.Source[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[ActorState.MetaIndex.WeaponState]);
|
state.Source[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[slot, false]);
|
PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state.Source[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[slot, true].ToString());
|
ImGuiUtil.DrawTableColumn(state.Source[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[type]);
|
PrintRow(type.ToDefaultName(), state.BaseData.Customize[type].Value, state.ModelData.Customize[type].Value, state.Source[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[crest]);
|
PrintRow(crest.ToLabel(), state.BaseData.Crest(crest), state.ModelData.Crest(crest), state.Source[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[flag]);
|
PrintRow(flag.ToString(), state.BaseData.Parameters[flag], state.ModelData.Parameters[flag], state.Source[flag]);
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public class AutoDesignPanel(AutoDesignManager _autoDesignManager) : IGameDataDr
|
||||||
foreach (var (design, designIdx) in set.Designs.WithIndex())
|
foreach (var (design, designIdx) in set.Designs.WithIndex())
|
||||||
{
|
{
|
||||||
ImGuiUtil.DrawTableColumn($"{design.Name(false)} ({designIdx})");
|
ImGuiUtil.DrawTableColumn($"{design.Name(false)} ({designIdx})");
|
||||||
ImGuiUtil.DrawTableColumn($"{design.ApplicationType} {design.Jobs.Name}");
|
ImGuiUtil.DrawTableColumn($"{design.Type} {design.Jobs.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
120
Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs
Normal file
120
Glamourer/Gui/Tabs/DesignTab/DesignLinkDrawer.cs
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Designs.Links;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui.Services;
|
||||||
|
|
||||||
|
namespace Glamourer.Gui.Tabs.DesignTab;
|
||||||
|
|
||||||
|
public class DesignLinkDrawer(DesignLinkManager _linkManager, DesignFileSystemSelector _selector, DesignCombo _combo) : IUiService
|
||||||
|
{
|
||||||
|
private int _dragDropIndex = -1;
|
||||||
|
private LinkOrder _dragDropOrder = LinkOrder.Self;
|
||||||
|
private int _dragDropTargetIndex = -1;
|
||||||
|
private LinkOrder _dragDropTargetOrder = LinkOrder.Self;
|
||||||
|
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
using var header = ImRaii.CollapsingHeader("Design Links");
|
||||||
|
if (!header)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var width = ImGui.GetContentRegionAvail().X / 2;
|
||||||
|
DrawList(_selector.Selected!.Links.Before, LinkOrder.Before, width);
|
||||||
|
ImGui.SameLine();
|
||||||
|
DrawList(_selector.Selected!.Links.After, LinkOrder.After, width);
|
||||||
|
|
||||||
|
if (_dragDropTargetIndex < 0
|
||||||
|
|| _dragDropIndex < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_linkManager.MoveDesignLink(_selector.Selected!, _dragDropIndex, _dragDropOrder, _dragDropTargetIndex, _dragDropTargetOrder);
|
||||||
|
_dragDropIndex = -1;
|
||||||
|
_dragDropTargetIndex = -1;
|
||||||
|
_dragDropOrder = LinkOrder.Self;
|
||||||
|
_dragDropTargetOrder = LinkOrder.Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawList(IReadOnlyList<DesignLink> list, LinkOrder order, float width)
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId((int)order);
|
||||||
|
using var table = ImRaii.Table("table", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersOuter,
|
||||||
|
new Vector2(width, list.Count * ImGui.GetFrameHeightWithSpacing()));
|
||||||
|
if (!table)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var buttonSize = new Vector2(ImGui.GetFrameHeight());
|
||||||
|
for (var i = 0; i < list.Count; ++i)
|
||||||
|
{
|
||||||
|
id.Push(i);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
var delete = ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), buttonSize, "Delete this link.", false, true);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted($"#{i:D2}");
|
||||||
|
|
||||||
|
var (design, flags) = list[i];
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Selectable(_selector.IncognitoMode ? design.Incognito : design.Name.Text);
|
||||||
|
DrawDragDrop(design, order, i);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(flags.ToString());
|
||||||
|
|
||||||
|
if (delete)
|
||||||
|
_linkManager.RemoveDesignLink(_selector.Selected!, i--, order);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
string tt;
|
||||||
|
bool canAdd;
|
||||||
|
if (_combo.Design == null)
|
||||||
|
{
|
||||||
|
tt = "Select a design first.";
|
||||||
|
canAdd = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
canAdd = LinkContainer.CanAddLink(_selector.Selected!, _combo.Design, order, out var error);
|
||||||
|
tt = canAdd ? $"Add a link to {_combo.Design.Name}." : $"Can not add a link to {_combo.Design.Name}: {error}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), buttonSize, tt, !canAdd, true))
|
||||||
|
_linkManager.AddDesignLink(_selector.Selected!, _combo.Design!, order);
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
_combo.Draw(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawDragDrop(Design design, LinkOrder order, int index)
|
||||||
|
{
|
||||||
|
using (var source = ImRaii.DragDropSource())
|
||||||
|
{
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
ImGui.SetDragDropPayload("DraggingLink", IntPtr.Zero, 0);
|
||||||
|
ImGui.TextUnformatted($"Reordering {design.Name}...");
|
||||||
|
_dragDropIndex = index;
|
||||||
|
_dragDropOrder = order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using var target = ImRaii.DragDropTarget();
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ImGuiUtil.IsDropping("DraggingLink"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_dragDropTargetIndex = index;
|
||||||
|
_dragDropTargetOrder = order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,7 +31,8 @@ public class DesignPanel(
|
||||||
DesignConverter _converter,
|
DesignConverter _converter,
|
||||||
ImportService _importService,
|
ImportService _importService,
|
||||||
MultiDesignPanel _multiDesignPanel,
|
MultiDesignPanel _multiDesignPanel,
|
||||||
CustomizeParameterDrawer _parameterDrawer)
|
CustomizeParameterDrawer _parameterDrawer,
|
||||||
|
DesignLinkDrawer _designLinkDrawer)
|
||||||
{
|
{
|
||||||
private readonly FileDialogManager _fileDialog = new();
|
private readonly FileDialogManager _fileDialog = new();
|
||||||
|
|
||||||
|
|
@ -119,21 +120,21 @@ public class DesignPanel(
|
||||||
{
|
{
|
||||||
using (var _ = ImRaii.Group())
|
using (var _ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(ActorState.MetaIndex.HatState, _manager, _selector.Selected!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.HatState, _manager, _selector.Selected!));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Head, _manager, _selector.Selected!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Head, _manager, _selector.Selected!));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (var _ = ImRaii.Group())
|
using (var _ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(ActorState.MetaIndex.VisorState, _manager, _selector.Selected!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.VisorState, _manager, _selector.Selected!));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Body, _manager, _selector.Selected!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.Body, _manager, _selector.Selected!));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (var _ = ImRaii.Group())
|
using (var _ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(ActorState.MetaIndex.WeaponState, _manager, _selector.Selected!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.WeaponState, _manager, _selector.Selected!));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.OffHand, _manager, _selector.Selected!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.CrestFromDesign(CrestFlag.OffHand, _manager, _selector.Selected!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +159,7 @@ public class DesignPanel(
|
||||||
_manager.ChangeCustomize(_selector.Selected, idx, _customizationDrawer.Customize[idx]);
|
_manager.ChangeCustomize(_selector.Selected, idx, _customizationDrawer.Customize[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(ActorState.MetaIndex.Wetness, _manager, _selector.Selected!));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromDesign(MetaIndex.Wetness, _manager, _selector.Selected!));
|
||||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,6 +167,7 @@ public class DesignPanel(
|
||||||
{
|
{
|
||||||
if (!_config.UseAdvancedParameters)
|
if (!_config.UseAdvancedParameters)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using var h = ImRaii.CollapsingHeader("Advanced Customizations");
|
using var h = ImRaii.CollapsingHeader("Advanced Customizations");
|
||||||
if (!h)
|
if (!h)
|
||||||
return;
|
return;
|
||||||
|
|
@ -259,20 +261,20 @@ public class DesignPanel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyEquip("Weapons", AutoDesign.WeaponFlags, false, new[]
|
ApplyEquip("Weapons", ApplicationTypeExtensions.WeaponFlags, false, new[]
|
||||||
{
|
{
|
||||||
EquipSlot.MainHand,
|
EquipSlot.MainHand,
|
||||||
EquipSlot.OffHand,
|
EquipSlot.OffHand,
|
||||||
});
|
});
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
ApplyEquip("Armor", AutoDesign.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots);
|
ApplyEquip("Armor", ApplicationTypeExtensions.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots);
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
ApplyEquip("Accessories", AutoDesign.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots);
|
ApplyEquip("Accessories", ApplicationTypeExtensions.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots);
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
ApplyEquip("Dyes", AutoDesign.StainFlags, true,
|
ApplyEquip("Dyes", ApplicationTypeExtensions.StainFlags, true,
|
||||||
EquipSlotExtensions.FullSlots);
|
EquipSlotExtensions.FullSlots);
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
|
|
@ -294,19 +296,19 @@ public class DesignPanel(
|
||||||
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
|
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
|
||||||
var apply = bigChange ? (flags & 0x01) == 0x01 : _selector.Selected!.DoApplyHatVisible();
|
var apply = bigChange ? (flags & 0x01) == 0x01 : _selector.Selected!.DoApplyHatVisible();
|
||||||
if (ImGui.Checkbox("Apply Hat Visibility", ref apply) || bigChange)
|
if (ImGui.Checkbox("Apply Hat Visibility", ref apply) || bigChange)
|
||||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.HatState, apply);
|
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, apply);
|
||||||
|
|
||||||
apply = bigChange ? (flags & 0x02) == 0x02 : _selector.Selected!.DoApplyVisorToggle();
|
apply = bigChange ? (flags & 0x02) == 0x02 : _selector.Selected!.DoApplyVisorToggle();
|
||||||
if (ImGui.Checkbox("Apply Visor State", ref apply) || bigChange)
|
if (ImGui.Checkbox("Apply Visor State", ref apply) || bigChange)
|
||||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.VisorState, apply);
|
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.VisorState, apply);
|
||||||
|
|
||||||
apply = bigChange ? (flags & 0x04) == 0x04 : _selector.Selected!.DoApplyWeaponVisible();
|
apply = bigChange ? (flags & 0x04) == 0x04 : _selector.Selected!.DoApplyWeaponVisible();
|
||||||
if (ImGui.Checkbox("Apply Weapon Visibility", ref apply) || bigChange)
|
if (ImGui.Checkbox("Apply Weapon Visibility", ref apply) || bigChange)
|
||||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.WeaponState, apply);
|
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.WeaponState, apply);
|
||||||
|
|
||||||
apply = bigChange ? (flags & 0x08) == 0x08 : _selector.Selected!.DoApplyWetness();
|
apply = bigChange ? (flags & 0x08) == 0x08 : _selector.Selected!.DoApplyWetness();
|
||||||
if (ImGui.Checkbox("Apply Wetness", ref apply) || bigChange)
|
if (ImGui.Checkbox("Apply Wetness", ref apply) || bigChange)
|
||||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.Wetness, apply);
|
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.Wetness, apply);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawParameterApplication()
|
private void DrawParameterApplication()
|
||||||
|
|
@ -370,6 +372,7 @@ public class DesignPanel(
|
||||||
_designDetails.Draw();
|
_designDetails.Draw();
|
||||||
DrawApplicationRules();
|
DrawApplicationRules();
|
||||||
_modAssociations.Draw();
|
_modAssociations.Draw();
|
||||||
|
_designLinkDrawer.Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawButtonRow()
|
private void DrawButtonRow()
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ public class NpcPanel(
|
||||||
_equipDrawer.DrawWeapons(mainhandData, offhandData, false);
|
_equipDrawer.DrawWeapons(mainhandData, offhandData, false);
|
||||||
|
|
||||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromValue(ActorState.MetaIndex.VisorState, _selector.Selection.VisorToggled));
|
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromValue(MetaIndex.VisorState, _selector.Selection.VisorToggled));
|
||||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,17 @@ public ref struct ToggleDrawData
|
||||||
public ToggleDrawData()
|
public ToggleDrawData()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public static ToggleDrawData FromDesign(ActorState.MetaIndex index, DesignManager manager, Design design)
|
public static ToggleDrawData FromDesign(MetaIndex index, DesignManager manager, Design design)
|
||||||
{
|
{
|
||||||
var (label, value, apply, setValue, setApply) = index switch
|
var (label, value, apply, setValue, setApply) = index switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.HatState => ("Hat Visible", design.DesignData.IsHatVisible(), design.DoApplyHatVisible(),
|
MetaIndex.HatState => ("Hat Visible", design.DesignData.IsHatVisible(), design.DoApplyHatVisible(),
|
||||||
(Action<bool>)(b => manager.ChangeMeta(design, index, b)), (Action<bool>)(b => manager.ChangeApplyMeta(design, index, b))),
|
(Action<bool>)(b => manager.ChangeMeta(design, index, b)), (Action<bool>)(b => manager.ChangeApplyMeta(design, index, b))),
|
||||||
ActorState.MetaIndex.VisorState => ("Visor Toggled", design.DesignData.IsVisorToggled(), design.DoApplyVisorToggle(),
|
MetaIndex.VisorState => ("Visor Toggled", design.DesignData.IsVisorToggled(), design.DoApplyVisorToggle(),
|
||||||
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
|
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
|
||||||
ActorState.MetaIndex.WeaponState => ("Weapon Visible", design.DesignData.IsWeaponVisible(), design.DoApplyWeaponVisible(),
|
MetaIndex.WeaponState => ("Weapon Visible", design.DesignData.IsWeaponVisible(), design.DoApplyWeaponVisible(),
|
||||||
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
|
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
|
||||||
ActorState.MetaIndex.Wetness => ("Force Wetness", design.DesignData.IsWet(), design.DoApplyWetness(),
|
MetaIndex.Wetness => ("Force Wetness", design.DesignData.IsWet(), design.DoApplyWetness(),
|
||||||
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
|
b => manager.ChangeMeta(design, index, b), b => manager.ChangeApplyMeta(design, index, b)),
|
||||||
_ => throw new Exception("Unsupported meta index."),
|
_ => throw new Exception("Unsupported meta index."),
|
||||||
};
|
};
|
||||||
|
|
@ -73,19 +73,19 @@ public ref struct ToggleDrawData
|
||||||
SetValue = v => manager.ChangeCrest(state, slot, v, StateChanged.Source.Manual),
|
SetValue = v => manager.ChangeCrest(state, slot, v, StateChanged.Source.Manual),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static ToggleDrawData FromState(ActorState.MetaIndex index, StateManager manager, ActorState state)
|
public static ToggleDrawData FromState(MetaIndex index, StateManager manager, ActorState state)
|
||||||
{
|
{
|
||||||
var (label, tooltip, value, setValue) = index switch
|
var (label, tooltip, value, setValue) = index switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.HatState => ("Hat Visible", "Hide or show the characters head gear.", state.ModelData.IsHatVisible(),
|
MetaIndex.HatState => ("Hat Visible", "Hide or show the characters head gear.", state.ModelData.IsHatVisible(),
|
||||||
(Action<bool>)(b => manager.ChangeHatState(state, b, StateChanged.Source.Manual))),
|
(Action<bool>)(b => manager.ChangeHatState(state, b, StateChanged.Source.Manual))),
|
||||||
ActorState.MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear.",
|
MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear.",
|
||||||
state.ModelData.IsVisorToggled(),
|
state.ModelData.IsVisorToggled(),
|
||||||
b => manager.ChangeVisorState(state, b, StateChanged.Source.Manual)),
|
b => manager.ChangeVisorState(state, b, StateChanged.Source.Manual)),
|
||||||
ActorState.MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn.",
|
MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn.",
|
||||||
state.ModelData.IsWeaponVisible(),
|
state.ModelData.IsWeaponVisible(),
|
||||||
b => manager.ChangeWeaponState(state, b, StateChanged.Source.Manual)),
|
b => manager.ChangeWeaponState(state, b, StateChanged.Source.Manual)),
|
||||||
ActorState.MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not.", state.ModelData.IsWet(),
|
MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not.", state.ModelData.IsWet(),
|
||||||
b => manager.ChangeWetness(state, b, StateChanged.Source.Manual)),
|
b => manager.ChangeWetness(state, b, StateChanged.Source.Manual)),
|
||||||
_ => throw new Exception("Unsupported meta index."),
|
_ => throw new Exception("Unsupported meta index."),
|
||||||
};
|
};
|
||||||
|
|
@ -100,14 +100,14 @@ public ref struct ToggleDrawData
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ToggleDrawData FromValue(ActorState.MetaIndex index, bool value)
|
public static ToggleDrawData FromValue(MetaIndex index, bool value)
|
||||||
{
|
{
|
||||||
var (label, tooltip) = index switch
|
var (label, tooltip) = index switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.HatState => ("Hat Visible", "Hide or show the characters head gear."),
|
MetaIndex.HatState => ("Hat Visible", "Hide or show the characters head gear."),
|
||||||
ActorState.MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear."),
|
MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear."),
|
||||||
ActorState.MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn."),
|
MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn."),
|
||||||
ActorState.MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not."),
|
MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not."),
|
||||||
_ => throw new Exception("Unsupported meta index."),
|
_ => throw new Exception("Unsupported meta index."),
|
||||||
};
|
};
|
||||||
return new ToggleDrawData
|
return new ToggleDrawData
|
||||||
|
|
|
||||||
|
|
@ -14,24 +14,17 @@ public sealed class CharaFile
|
||||||
public CustomizeFlag ApplyCustomize;
|
public CustomizeFlag ApplyCustomize;
|
||||||
public EquipFlag ApplyEquip;
|
public EquipFlag ApplyEquip;
|
||||||
|
|
||||||
public static CharaFile? ParseData(ItemManager items, string data, string? name = null)
|
public static CharaFile ParseData(ItemManager items, string data, string? name = null)
|
||||||
{
|
{
|
||||||
try
|
var jObj = JObject.Parse(data);
|
||||||
{
|
SanityCheck(jObj);
|
||||||
var jObj = JObject.Parse(data);
|
var ret = new CharaFile();
|
||||||
SanityCheck(jObj);
|
ret.Data.SetDefaultEquipment(items);
|
||||||
var ret = new CharaFile();
|
ret.Data.ModelId = ParseModelId(jObj);
|
||||||
ret.Data.SetDefaultEquipment(items);
|
ret.Name = jObj["Nickname"]?.ToObject<string>() ?? name ?? "New Design";
|
||||||
ret.Data.ModelId = ParseModelId(jObj);
|
ret.ApplyCustomize = ParseCustomize(jObj, ref ret.Data.Customize);
|
||||||
ret.Name = jObj["Nickname"]?.ToObject<string>() ?? name ?? "New Design";
|
ret.ApplyEquip = ParseEquipment(items, jObj, ref ret.Data);
|
||||||
ret.ApplyCustomize = ParseCustomize(jObj, ref ret.Data.Customize);
|
return ret;
|
||||||
ret.ApplyEquip = ParseEquipment(items, jObj, ref ret.Data);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EquipFlag ParseEquipment(ItemManager items, JObject jObj, ref DesignData data)
|
private static EquipFlag ParseEquipment(ItemManager items, JObject jObj, ref DesignData data)
|
||||||
|
|
@ -282,9 +275,6 @@ public sealed class CharaFile
|
||||||
|
|
||||||
private static void SanityCheck(JObject jObj)
|
private static void SanityCheck(JObject jObj)
|
||||||
{
|
{
|
||||||
if (jObj["TypeName"]?.ToObject<string>() is not "Anamnesis Character File")
|
|
||||||
throw new Exception("Wrong TypeName property set.");
|
|
||||||
|
|
||||||
var type = jObj["ObjectKind"]?.ToObject<string>();
|
var type = jObj["ObjectKind"]?.ToObject<string>();
|
||||||
if (type is not "Player")
|
if (type is not "Player")
|
||||||
throw new Exception($"ObjectKind {type} != Player is not supported.");
|
throw new Exception($"ObjectKind {type} != Player is not supported.");
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,6 @@ public class ImportService(CustomizeService _customizations, IDragDropManager _d
|
||||||
{
|
{
|
||||||
var text = File.ReadAllText(path);
|
var text = File.ReadAllText(path);
|
||||||
var file = CharaFile.CharaFile.ParseData(_items, text, Path.GetFileNameWithoutExtension(path));
|
var file = CharaFile.CharaFile.ParseData(_items, text, Path.GetFileNameWithoutExtension(path));
|
||||||
if (file == null)
|
|
||||||
throw new Exception();
|
|
||||||
|
|
||||||
name = file.Name;
|
name = file.Name;
|
||||||
design = new DesignBase(_customizations, file.Data, file.ApplyEquip, file.ApplyCustomize);
|
design = new DesignBase(_customizations, file.Data, file.ApplyEquip, file.ApplyCustomize);
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ public class PaletteImport(DalamudPluginInterface pluginInterface, DesignManager
|
||||||
design.ApplyCustomize = 0;
|
design.ApplyCustomize = 0;
|
||||||
design.ApplyEquip = 0;
|
design.ApplyEquip = 0;
|
||||||
design.ApplyCrest = 0;
|
design.ApplyCrest = 0;
|
||||||
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.VisorState, false);
|
designManager.ChangeApplyMeta(design, MetaIndex.VisorState, false);
|
||||||
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.HatState, false);
|
designManager.ChangeApplyMeta(design, MetaIndex.HatState, false);
|
||||||
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.WeaponState, false);
|
designManager.ChangeApplyMeta(design, MetaIndex.WeaponState, false);
|
||||||
foreach (var flag in flags.Iterate())
|
foreach (var flag in flags.Iterate())
|
||||||
{
|
{
|
||||||
designManager.ChangeApplyParameter(design, flag, true);
|
designManager.ChangeApplyParameter(design, flag, true);
|
||||||
|
|
|
||||||
|
|
@ -255,26 +255,26 @@ public class CommandService : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
--designIdx;
|
--designIdx;
|
||||||
AutoDesign.Type applicationFlags = 0;
|
ApplicationType applicationFlags = 0;
|
||||||
if (split2.Length == 2)
|
if (split2.Length == 2)
|
||||||
foreach (var character in split2[1])
|
foreach (var character in split2[1])
|
||||||
{
|
{
|
||||||
switch (char.ToLowerInvariant(character))
|
switch (char.ToLowerInvariant(character))
|
||||||
{
|
{
|
||||||
case 'c':
|
case 'c':
|
||||||
applicationFlags |= AutoDesign.Type.Customizations;
|
applicationFlags |= ApplicationType.Customizations;
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
applicationFlags |= AutoDesign.Type.Armor;
|
applicationFlags |= ApplicationType.Armor;
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
applicationFlags |= AutoDesign.Type.Accessories;
|
applicationFlags |= ApplicationType.Accessories;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
applicationFlags |= AutoDesign.Type.GearCustomization;
|
applicationFlags |= ApplicationType.GearCustomization;
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
applicationFlags |= AutoDesign.Type.Weapons;
|
applicationFlags |= ApplicationType.Weapons;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_chat.Print(new SeStringBuilder().AddText("The value ").AddPurple(split2[1], true)
|
_chat.Print(new SeStringBuilder().AddText("The value ").AddPurple(split2[1], true)
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,6 @@ namespace Glamourer.State;
|
||||||
|
|
||||||
public class ActorState
|
public class ActorState
|
||||||
{
|
{
|
||||||
public enum MetaIndex
|
|
||||||
{
|
|
||||||
Wetness = EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices,
|
|
||||||
HatState,
|
|
||||||
VisorState,
|
|
||||||
WeaponState,
|
|
||||||
ModelId,
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly ActorIdentifier Identifier;
|
public readonly ActorIdentifier Identifier;
|
||||||
|
|
||||||
public bool AllowsRedraw(ICondition condition)
|
public bool AllowsRedraw(ICondition condition)
|
||||||
|
|
@ -77,47 +68,14 @@ 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>
|
||||||
private readonly StateChanged.Source[] _sources = Enumerable
|
public readonly StateSource Source = new();
|
||||||
.Repeat(StateChanged.Source.Game,
|
|
||||||
EquipFlagExtensions.NumEquipFlags
|
|
||||||
+ CustomizationExtensions.NumIndices
|
|
||||||
+ 5
|
|
||||||
+ CrestExtensions.AllRelevantSet.Count
|
|
||||||
+ CustomizeParameterExtensions.AllFlags.Count).ToArray();
|
|
||||||
|
|
||||||
internal ActorState(ActorIdentifier identifier)
|
internal ActorState(ActorIdentifier identifier)
|
||||||
=> Identifier = identifier.CreatePermanent();
|
=> Identifier = identifier.CreatePermanent();
|
||||||
|
|
||||||
public ref StateChanged.Source this[EquipSlot slot, bool stain]
|
|
||||||
=> ref _sources[slot.ToIndex() + (stain ? EquipFlagExtensions.NumEquipFlags / 2 : 0)];
|
|
||||||
|
|
||||||
public ref StateChanged.Source this[CrestFlag slot]
|
|
||||||
=> ref _sources[EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5 + slot.ToInternalIndex()];
|
|
||||||
|
|
||||||
public ref StateChanged.Source this[CustomizeIndex type]
|
|
||||||
=> ref _sources[EquipFlagExtensions.NumEquipFlags + (int)type];
|
|
||||||
|
|
||||||
public ref StateChanged.Source this[MetaIndex index]
|
|
||||||
=> ref _sources[(int)index];
|
|
||||||
|
|
||||||
public ref StateChanged.Source this[CustomizeParameterFlag flag]
|
|
||||||
=> ref _sources[
|
|
||||||
EquipFlagExtensions.NumEquipFlags
|
|
||||||
+ CustomizationExtensions.NumIndices + 5
|
|
||||||
+ CrestExtensions.AllRelevantSet.Count
|
|
||||||
+ flag.ToInternalIndex()];
|
|
||||||
|
|
||||||
public void RemoveFixedDesignSources()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < _sources.Length; ++i)
|
|
||||||
{
|
|
||||||
if (_sources[i] is StateChanged.Source.Fixed)
|
|
||||||
_sources[i] = StateChanged.Source.Manual;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomizeParameterFlag OnlyChangedParameters()
|
public CustomizeParameterFlag OnlyChangedParameters()
|
||||||
=> CustomizeParameterExtensions.AllFlags.Where(f => this[f] is not StateChanged.Source.Game).Aggregate((CustomizeParameterFlag) 0, (a, b) => a | b);
|
=> CustomizeParameterExtensions.AllFlags.Where(f => Source[f] is not StateChanged.Source.Game)
|
||||||
|
.Aggregate((CustomizeParameterFlag)0, (a, b) => a | b);
|
||||||
|
|
||||||
public bool UpdateTerritory(ushort territory)
|
public bool UpdateTerritory(ushort territory)
|
||||||
{
|
{
|
||||||
|
|
@ -127,4 +85,4 @@ public class ActorState
|
||||||
LastTerritory = territory;
|
LastTerritory = territory;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ public class StateApplier(
|
||||||
// If the source is not IPC we do not want to apply restrictions.
|
// If the source is not IPC we do not want to apply restrictions.
|
||||||
var data = GetData(state);
|
var data = GetData(state);
|
||||||
if (apply)
|
if (apply)
|
||||||
ChangeArmor(data, slot, state.ModelData.Armor(slot), state[slot, false] is not StateChanged.Source.Ipc,
|
ChangeArmor(data, slot, state.ModelData.Armor(slot), state.Source[slot, false] is not StateChanged.Source.Ipc,
|
||||||
state.ModelData.IsHatVisible());
|
state.ModelData.IsHatVisible());
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
|
||||||
|
|
@ -61,21 +61,21 @@ public class StateEditor
|
||||||
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[ActorState.MetaIndex.ModelId] = source;
|
state.Source[MetaIndex.ModelId] = source;
|
||||||
state[ActorState.MetaIndex.HatState] = source;
|
state.Source[MetaIndex.HatState] = source;
|
||||||
state[ActorState.MetaIndex.WeaponState] = source;
|
state.Source[MetaIndex.WeaponState] = source;
|
||||||
state[ActorState.MetaIndex.VisorState] = source;
|
state.Source[MetaIndex.VisorState] = source;
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
{
|
{
|
||||||
state[slot, true] = source;
|
state.Source[slot, true] = source;
|
||||||
state[slot, false] = source;
|
state.Source[slot, false] = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
state[CustomizeIndex.Clan] = source;
|
state.Source[CustomizeIndex.Clan] = source;
|
||||||
state[CustomizeIndex.Gender] = source;
|
state.Source[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[index] = source;
|
state.Source[index] = source;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +83,7 @@ public class StateEditor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.ModelData.LoadNonHuman(modelId, customize, equipData);
|
state.ModelData.LoadNonHuman(modelId, customize, equipData);
|
||||||
state[ActorState.MetaIndex.ModelId] = source;
|
state.Source[MetaIndex.ModelId] = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -98,7 +98,7 @@ public class StateEditor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.ModelData.Customize[idx] = value;
|
state.ModelData.Customize[idx] = value;
|
||||||
state[idx] = source;
|
state.Source[idx] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ public class StateEditor
|
||||||
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[type] = source;
|
state.Source[type] = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -144,12 +144,12 @@ public class StateEditor
|
||||||
_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[slot, false], out _, key);
|
ChangeItem(state, slot, old, state.Source[slot, false], out _, key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
state.ModelData.SetItem(slot, item);
|
state.ModelData.SetItem(slot, item);
|
||||||
state[slot, false] = source;
|
state.Source[slot, false] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,14 +174,14 @@ public class StateEditor
|
||||||
_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[slot, false], out _, out _, key);
|
ChangeEquip(state, slot, old, oldS, state.Source[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[slot, false] = source;
|
state.Source[slot, false] = source;
|
||||||
state[slot, true] = source;
|
state.Source[slot, true] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +193,7 @@ public class StateEditor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.ModelData.SetStain(slot, stain);
|
state.ModelData.SetStain(slot, stain);
|
||||||
state[slot, true] = source;
|
state.Source[slot, true] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,7 +205,7 @@ public class StateEditor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.ModelData.SetCrest(slot, crest);
|
state.ModelData.SetCrest(slot, crest);
|
||||||
state[slot] = source;
|
state.Source[slot] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,20 +218,20 @@ public class StateEditor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.ModelData.Parameters.Set(flag, value);
|
state.ModelData.Parameters.Set(flag, value);
|
||||||
state[flag] = source;
|
state.Source[flag] = source;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ChangeMetaState(ActorState state, ActorState.MetaIndex index, bool value, StateChanged.Source source, out bool oldValue,
|
public bool ChangeMetaState(ActorState state, MetaIndex index, bool value, StateChanged.Source source, out bool oldValue,
|
||||||
uint key = 0)
|
uint key = 0)
|
||||||
{
|
{
|
||||||
(var setter, oldValue) = index switch
|
(var setter, oldValue) = index switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.Wetness => ((Func<bool, bool>)(v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
|
MetaIndex.Wetness => ((Func<bool, bool>)(v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
|
||||||
ActorState.MetaIndex.HatState => ((Func<bool, bool>)(v => state.ModelData.SetHatVisible(v)), state.ModelData.IsHatVisible()),
|
MetaIndex.HatState => ((Func<bool, bool>)(v => state.ModelData.SetHatVisible(v)), state.ModelData.IsHatVisible()),
|
||||||
ActorState.MetaIndex.VisorState => ((Func<bool, bool>)(v => state.ModelData.SetVisor(v)), state.ModelData.IsVisorToggled()),
|
MetaIndex.VisorState => ((Func<bool, bool>)(v => state.ModelData.SetVisor(v)), state.ModelData.IsVisorToggled()),
|
||||||
ActorState.MetaIndex.WeaponState => ((Func<bool, bool>)(v => state.ModelData.SetWeaponVisible(v)),
|
MetaIndex.WeaponState => ((Func<bool, bool>)(v => state.ModelData.SetWeaponVisible(v)),
|
||||||
state.ModelData.IsWeaponVisible()),
|
state.ModelData.IsWeaponVisible()),
|
||||||
_ => throw new Exception("Invalid MetaIndex."),
|
_ => throw new Exception("Invalid MetaIndex."),
|
||||||
};
|
};
|
||||||
|
|
@ -240,7 +240,7 @@ public class StateEditor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
setter(value);
|
setter(value);
|
||||||
state[index] = source;
|
state.Source[index] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ public class StateListener : IDisposable
|
||||||
var set = _customizations.Manager.GetSet(model.Clan, model.Gender);
|
var set = _customizations.Manager.GetSet(model.Clan, model.Gender);
|
||||||
foreach (var index in CustomizationExtensions.AllBasic)
|
foreach (var index in CustomizationExtensions.AllBasic)
|
||||||
{
|
{
|
||||||
if (state[index] is not StateChanged.Source.Fixed)
|
if (state.Source[index] is not StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
var newValue = customize[index];
|
var newValue = customize[index];
|
||||||
var oldValue = model[index];
|
var oldValue = model[index];
|
||||||
|
|
@ -213,7 +213,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[slot, false] is StateChanged.Source.Ipc;
|
locked = state.Source[slot, false] is StateChanged.Source.Ipc;
|
||||||
}
|
}
|
||||||
|
|
||||||
_funModule.ApplyFunToSlot(actor, ref armor, slot);
|
_funModule.ApplyFunToSlot(actor, ref armor, slot);
|
||||||
|
|
@ -240,7 +240,7 @@ public class StateListener : IDisposable
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var changed = changedItem.Weapon(stain);
|
var changed = changedItem.Weapon(stain);
|
||||||
if (current.Value == changed.Value && state[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (current.Value == changed.Value && state.Source[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
{
|
{
|
||||||
_manager.ChangeItem(state, slot, currentItem, StateChanged.Source.Game);
|
_manager.ChangeItem(state, slot, currentItem, StateChanged.Source.Game);
|
||||||
_manager.ChangeStain(state, slot, current.Stain, StateChanged.Source.Game);
|
_manager.ChangeStain(state, slot, current.Stain, StateChanged.Source.Game);
|
||||||
|
|
@ -251,7 +251,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[slot, false] is not StateChanged.Source.Ipc,
|
_applier.ChangeArmor(objects, slot, current.ToArmor(), state.Source[slot, false] is not StateChanged.Source.Ipc,
|
||||||
state.ModelData.IsHatVisible());
|
state.ModelData.IsHatVisible());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -285,12 +285,12 @@ 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[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state.Source[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
if (state[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state.Source[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
@ -384,12 +384,12 @@ public class StateListener : IDisposable
|
||||||
// Update model state if not on fixed design.
|
// Update model state if not on fixed design.
|
||||||
case UpdateState.Change:
|
case UpdateState.Change:
|
||||||
var apply = false;
|
var apply = false;
|
||||||
if (state[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state.Source[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
if (state[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state.Source[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
@ -418,7 +418,7 @@ public class StateListener : IDisposable
|
||||||
switch (UpdateBaseCrest(actor, state, slot, value))
|
switch (UpdateBaseCrest(actor, state, slot, value))
|
||||||
{
|
{
|
||||||
case UpdateState.Change:
|
case UpdateState.Change:
|
||||||
if (state[slot] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state.Source[slot] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game);
|
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
value = state.ModelData.Crest(slot);
|
value = state.ModelData.Crest(slot);
|
||||||
|
|
@ -564,7 +564,7 @@ public class StateListener : IDisposable
|
||||||
{
|
{
|
||||||
// if base state changed, either overwrite the actual value if we have fixed values,
|
// if base state changed, either overwrite the actual value if we have fixed values,
|
||||||
// or overwrite the stored model state with the new one.
|
// or overwrite the stored model state with the new one.
|
||||||
if (state[ActorState.MetaIndex.VisorState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
|
if (state.Source[MetaIndex.VisorState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
|
||||||
value = state.ModelData.IsVisorToggled();
|
value = state.ModelData.IsVisorToggled();
|
||||||
else
|
else
|
||||||
_manager.ChangeVisorState(state, value, StateChanged.Source.Game);
|
_manager.ChangeVisorState(state, value, StateChanged.Source.Game);
|
||||||
|
|
@ -597,7 +597,7 @@ public class StateListener : IDisposable
|
||||||
{
|
{
|
||||||
// if base state changed, either overwrite the actual value if we have fixed values,
|
// if base state changed, either overwrite the actual value if we have fixed values,
|
||||||
// or overwrite the stored model state with the new one.
|
// or overwrite the stored model state with the new one.
|
||||||
if (state[ActorState.MetaIndex.HatState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
|
if (state.Source[MetaIndex.HatState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
|
||||||
value = state.ModelData.IsHatVisible();
|
value = state.ModelData.IsHatVisible();
|
||||||
else
|
else
|
||||||
_manager.ChangeHatState(state, value, StateChanged.Source.Game);
|
_manager.ChangeHatState(state, value, StateChanged.Source.Game);
|
||||||
|
|
@ -630,7 +630,7 @@ public class StateListener : IDisposable
|
||||||
{
|
{
|
||||||
// if base state changed, either overwrite the actual value if we have fixed values,
|
// if base state changed, either overwrite the actual value if we have fixed values,
|
||||||
// or overwrite the stored model state with the new one.
|
// or overwrite the stored model state with the new one.
|
||||||
if (state[ActorState.MetaIndex.WeaponState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
|
if (state.Source[MetaIndex.WeaponState] is StateChanged.Source.Fixed or StateChanged.Source.Ipc)
|
||||||
value = state.ModelData.IsWeaponVisible();
|
value = state.ModelData.IsWeaponVisible();
|
||||||
else
|
else
|
||||||
_manager.ChangeWeaponState(state, value, StateChanged.Source.Game);
|
_manager.ChangeWeaponState(state, value, StateChanged.Source.Game);
|
||||||
|
|
@ -732,7 +732,7 @@ 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[flag])
|
switch (state.Source[flag])
|
||||||
{
|
{
|
||||||
case StateChanged.Source.Game:
|
case StateChanged.Source.Game:
|
||||||
if (state.BaseData.Parameters.Set(flag, newValue))
|
if (state.BaseData.Parameters.Set(flag, newValue))
|
||||||
|
|
@ -755,7 +755,7 @@ public class StateListener : IDisposable
|
||||||
break;
|
break;
|
||||||
case StateChanged.Source.Pending:
|
case StateChanged.Source.Pending:
|
||||||
state.BaseData.Parameters.Set(flag, newValue);
|
state.BaseData.Parameters.Set(flag, newValue);
|
||||||
state[flag] = StateChanged.Source.Manual;
|
state.Source[flag] = StateChanged.Source.Manual;
|
||||||
if (_config.UseAdvancedParameters)
|
if (_config.UseAdvancedParameters)
|
||||||
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
|
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -329,49 +329,49 @@ public class StateManager(
|
||||||
/// <summary> Change hat visibility. </summary>
|
/// <summary> Change hat visibility. </summary>
|
||||||
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.HatState, value, source, out var old, key))
|
if (!_editor.ChangeMetaState(state, MetaIndex.HatState, 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.ChangeHatState(state, source is StateChanged.Source.Manual or StateChanged.Source.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, ActorState.MetaIndex.HatState));
|
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.HatState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change weapon visibility. </summary>
|
/// <summary> Change weapon visibility. </summary>
|
||||||
public void ChangeWeaponState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
public void ChangeWeaponState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.WeaponState, value, source, out var old, key))
|
if (!_editor.ChangeMetaState(state, MetaIndex.WeaponState, value, source, out var old, key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var actors = _applier.ChangeWeaponState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
var actors = _applier.ChangeWeaponState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Weapon Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
$"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, ActorState.MetaIndex.WeaponState));
|
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.WeaponState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change visor state. </summary>
|
/// <summary> Change visor state. </summary>
|
||||||
public void ChangeVisorState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
public void ChangeVisorState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.VisorState, value, source, out var old, key))
|
if (!_editor.ChangeMetaState(state, MetaIndex.VisorState, value, source, out var old, key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var actors = _applier.ChangeVisor(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
var actors = _applier.ChangeVisor(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Visor State in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
$"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, ActorState.MetaIndex.VisorState));
|
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.VisorState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Set GPose Wetness. </summary>
|
/// <summary> Set GPose Wetness. </summary>
|
||||||
public void ChangeWetness(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
public void ChangeWetness(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.Wetness, value, source, out var old, key))
|
if (!_editor.ChangeMetaState(state, MetaIndex.Wetness, value, source, out var old, key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var actors = _applier.ChangeWetness(state, true);
|
var actors = _applier.ChangeWetness(state, true);
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Wetness in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
$"Set Wetness in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaIndex.Wetness], state, actors, (old, value, ActorState.MetaIndex.Wetness));
|
_event.Invoke(StateChanged.Type.Other, state.Source[MetaIndex.Wetness], state, actors, (old, value, MetaIndex.Wetness));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -385,16 +385,16 @@ public class StateManager(
|
||||||
|
|
||||||
var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman;
|
var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman;
|
||||||
if (design.DoApplyWetness())
|
if (design.DoApplyWetness())
|
||||||
_editor.ChangeMetaState(state, ActorState.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.DoApplyHatVisible())
|
||||||
_editor.ChangeMetaState(state, ActorState.MetaIndex.HatState, design.DesignData.IsHatVisible(), source, out _, key);
|
_editor.ChangeMetaState(state, MetaIndex.HatState, design.DesignData.IsHatVisible(), source, out _, key);
|
||||||
if (design.DoApplyWeaponVisible())
|
if (design.DoApplyWeaponVisible())
|
||||||
_editor.ChangeMetaState(state, ActorState.MetaIndex.WeaponState, design.DesignData.IsWeaponVisible(), source, out _, key);
|
_editor.ChangeMetaState(state, MetaIndex.WeaponState, design.DesignData.IsWeaponVisible(), source, out _, key);
|
||||||
if (design.DoApplyVisorToggle())
|
if (design.DoApplyVisorToggle())
|
||||||
_editor.ChangeMetaState(state, ActorState.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)
|
||||||
? design.ApplyCustomize
|
? design.ApplyCustomize
|
||||||
|
|
@ -419,13 +419,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[CustomizeParameterFlag.HairDiffuse], out _, key);
|
state.Source[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[ActorState.MetaIndex.Wetness], state, actors, design);
|
_event.Invoke(StateChanged.Type.Design, state.Source[MetaIndex.Wetness], state, actors, design);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
|
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
|
||||||
|
|
@ -455,7 +455,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[slot, false] is not StateChanged.Source.Ipc,
|
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Source[slot, false] is not StateChanged.Source.Ipc,
|
||||||
state.ModelData.IsHatVisible());
|
state.ModelData.IsHatVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -489,22 +489,22 @@ 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[index] = StateChanged.Source.Game;
|
state.Source[index] = StateChanged.Source.Game;
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
{
|
{
|
||||||
state[slot, true] = StateChanged.Source.Game;
|
state.Source[slot, true] = StateChanged.Source.Game;
|
||||||
state[slot, false] = StateChanged.Source.Game;
|
state.Source[slot, false] = StateChanged.Source.Game;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var type in Enum.GetValues<ActorState.MetaIndex>())
|
foreach (var type in Enum.GetValues<MetaIndex>())
|
||||||
state[type] = StateChanged.Source.Game;
|
state.Source[type] = StateChanged.Source.Game;
|
||||||
|
|
||||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||||
state[slot] = StateChanged.Source.Game;
|
state.Source[slot] = StateChanged.Source.Game;
|
||||||
|
|
||||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||||
state[flag] = StateChanged.Source.Game;
|
state.Source[flag] = StateChanged.Source.Game;
|
||||||
|
|
||||||
var actors = ActorData.Invalid;
|
var actors = ActorData.Invalid;
|
||||||
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||||
|
|
@ -523,7 +523,7 @@ 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[flag] = StateChanged.Source.Game;
|
state.Source[flag] = StateChanged.Source.Game;
|
||||||
|
|
||||||
var actors = ActorData.Invalid;
|
var actors = ActorData.Invalid;
|
||||||
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||||
|
|
@ -538,69 +538,69 @@ public class StateManager(
|
||||||
if (!state.Unlock(key))
|
if (!state.Unlock(key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(i => state[i] is StateChanged.Source.Fixed))
|
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(i => state.Source[i] is StateChanged.Source.Fixed))
|
||||||
{
|
{
|
||||||
state[index] = StateChanged.Source.Game;
|
state.Source[index] = StateChanged.Source.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[slot, true] is StateChanged.Source.Fixed)
|
if (state.Source[slot, true] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[slot, true] = StateChanged.Source.Game;
|
state.Source[slot, true] = StateChanged.Source.Game;
|
||||||
state.ModelData.SetStain(slot, state.BaseData.Stain(slot));
|
state.ModelData.SetStain(slot, state.BaseData.Stain(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[slot, false] is StateChanged.Source.Fixed)
|
if (state.Source[slot, false] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[slot, false] = StateChanged.Source.Game;
|
state.Source[slot, false] = StateChanged.Source.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[slot] is StateChanged.Source.Fixed)
|
if (state.Source[slot] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[slot] = StateChanged.Source.Game;
|
state.Source[slot] = StateChanged.Source.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[flag])
|
switch (state.Source[flag])
|
||||||
{
|
{
|
||||||
case StateChanged.Source.Fixed:
|
case StateChanged.Source.Fixed:
|
||||||
case StateChanged.Source.Manual when !respectManualPalettes:
|
case StateChanged.Source.Manual when !respectManualPalettes:
|
||||||
state[flag] = StateChanged.Source.Game;
|
state.Source[flag] = StateChanged.Source.Game;
|
||||||
state.ModelData.Parameters[flag] = state.BaseData.Parameters[flag];
|
state.ModelData.Parameters[flag] = state.BaseData.Parameters[flag];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[ActorState.MetaIndex.HatState] is StateChanged.Source.Fixed)
|
if (state.Source[MetaIndex.HatState] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[ActorState.MetaIndex.HatState] = StateChanged.Source.Game;
|
state.Source[MetaIndex.HatState] = StateChanged.Source.Game;
|
||||||
state.ModelData.SetHatVisible(state.BaseData.IsHatVisible());
|
state.ModelData.SetHatVisible(state.BaseData.IsHatVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[ActorState.MetaIndex.VisorState] is StateChanged.Source.Fixed)
|
if (state.Source[MetaIndex.VisorState] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[ActorState.MetaIndex.VisorState] = StateChanged.Source.Game;
|
state.Source[MetaIndex.VisorState] = StateChanged.Source.Game;
|
||||||
state.ModelData.SetVisor(state.BaseData.IsVisorToggled());
|
state.ModelData.SetVisor(state.BaseData.IsVisorToggled());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[ActorState.MetaIndex.WeaponState] is StateChanged.Source.Fixed)
|
if (state.Source[MetaIndex.WeaponState] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[ActorState.MetaIndex.WeaponState] = StateChanged.Source.Game;
|
state.Source[MetaIndex.WeaponState] = StateChanged.Source.Game;
|
||||||
state.ModelData.SetWeaponVisible(state.BaseData.IsWeaponVisible());
|
state.ModelData.SetWeaponVisible(state.BaseData.IsWeaponVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[ActorState.MetaIndex.Wetness] is StateChanged.Source.Fixed)
|
if (state.Source[MetaIndex.Wetness] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[ActorState.MetaIndex.Wetness] = StateChanged.Source.Game;
|
state.Source[MetaIndex.Wetness] = StateChanged.Source.Game;
|
||||||
state.ModelData.SetIsWet(state.BaseData.IsWet());
|
state.ModelData.SetIsWet(state.BaseData.IsWet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
58
Glamourer/State/StateSource.cs
Normal file
58
Glamourer/State/StateSource.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
using Glamourer.GameData;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
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
|
||||||
|
+ CustomizationExtensions.NumIndices
|
||||||
|
+ 5
|
||||||
|
+ CrestExtensions.AllRelevantSet.Count
|
||||||
|
+ CustomizeParameterExtensions.AllFlags.Count;
|
||||||
|
|
||||||
|
|
||||||
|
private readonly Source[] _data = Enumerable.Repeat(Source.Game, Size).ToArray();
|
||||||
|
|
||||||
|
public StateSource()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public ref Source this[EquipSlot slot, bool stain]
|
||||||
|
=> ref _data[slot.ToIndex() + (stain ? EquipFlagExtensions.NumEquipFlags / 2 : 0)];
|
||||||
|
|
||||||
|
public ref Source this[CrestFlag slot]
|
||||||
|
=> ref _data[EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5 + slot.ToInternalIndex()];
|
||||||
|
|
||||||
|
public ref Source this[CustomizeIndex type]
|
||||||
|
=> 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()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < _data.Length; ++i)
|
||||||
|
{
|
||||||
|
if (_data[i] is Source.Fixed)
|
||||||
|
_data[i] = Source.Manual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
||||||
Subproject commit d734d5d2b0686db0f5f4270dc379360d31f72e59
|
Subproject commit 04eb0b5ed3930e9cb87ad00dffa9c8be90b58bb3
|
||||||
Loading…
Add table
Add a link
Reference in a new issue