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,21 +12,9 @@ public class AutoDesign
|
|||
{
|
||||
public const string RevertName = "Revert";
|
||||
|
||||
[Flags]
|
||||
public enum Type : byte
|
||||
{
|
||||
Armor = 0x01,
|
||||
Customizations = 0x02,
|
||||
Weapons = 0x04,
|
||||
GearCustomization = 0x08,
|
||||
Accessories = 0x10,
|
||||
|
||||
All = Armor | Accessories | Customizations | Weapons | GearCustomization,
|
||||
}
|
||||
|
||||
public Design? Design;
|
||||
public JobGroup Jobs;
|
||||
public Type ApplicationType;
|
||||
public ApplicationType Type;
|
||||
public short GearsetIndex = -1;
|
||||
|
||||
public string Name(bool incognito)
|
||||
|
|
@ -42,7 +30,7 @@ public class AutoDesign
|
|||
=> new()
|
||||
{
|
||||
Design = Design,
|
||||
ApplicationType = ApplicationType,
|
||||
Type = Type,
|
||||
Jobs = Jobs,
|
||||
GearsetIndex = GearsetIndex,
|
||||
};
|
||||
|
|
@ -65,7 +53,7 @@ public class AutoDesign
|
|||
=> new()
|
||||
{
|
||||
["Design"] = Design?.Identifier.ToString(),
|
||||
["ApplicationType"] = (uint)ApplicationType,
|
||||
["Type"] = (uint)Type,
|
||||
["Conditions"] = CreateConditionObject(),
|
||||
};
|
||||
|
||||
|
|
@ -82,42 +70,5 @@ public class AutoDesign
|
|||
|
||||
public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, bool ApplyHat, bool ApplyVisor, bool
|
||||
ApplyWeapon, bool ApplyWet) ApplyWhat()
|
||||
{
|
||||
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;
|
||||
=> Type.ApplyWhat(Design);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop;
|
||||
|
|
@ -15,7 +16,7 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Automation;
|
||||
|
||||
public class AutoDesignApplier : IDisposable
|
||||
public sealed class AutoDesignApplier : IDisposable
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly AutoDesignManager _manager;
|
||||
|
|
@ -30,6 +31,7 @@ public class AutoDesignApplier : IDisposable
|
|||
private readonly ObjectManager _objects;
|
||||
private readonly WeaponLoading _weapons;
|
||||
private readonly HumanModelList _humans;
|
||||
private readonly DesignMerger _designMerger;
|
||||
private readonly IClientState _clientState;
|
||||
|
||||
private ActorState? _jobChangeState;
|
||||
|
|
@ -182,7 +184,7 @@ public class AutoDesignApplier : IDisposable
|
|||
}
|
||||
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)
|
||||
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))
|
||||
state.RemoveFixedDesignSources();
|
||||
state.Source.RemoveFixedDesignSources();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -276,7 +278,7 @@ public class AutoDesignApplier : IDisposable
|
|||
if (set.BaseState == AutoDesignSet.Base.Game)
|
||||
_state.ResetStateFixed(state, respectManual);
|
||||
else if (!respectManual)
|
||||
state.RemoveFixedDesignSources();
|
||||
state.Source.RemoveFixedDesignSources();
|
||||
|
||||
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
||||
return;
|
||||
|
|
@ -286,7 +288,7 @@ public class AutoDesignApplier : IDisposable
|
|||
if (!design.IsActive(actor))
|
||||
continue;
|
||||
|
||||
if (design.ApplicationType is 0)
|
||||
if (design.Type is 0)
|
||||
continue;
|
||||
|
||||
ref readonly var data = ref design.GetDesignData(state);
|
||||
|
|
@ -342,7 +344,7 @@ public class AutoDesignApplier : IDisposable
|
|||
if (!crestFlags.HasFlag(slot))
|
||||
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);
|
||||
totalCrestFlags |= slot;
|
||||
}
|
||||
|
|
@ -360,7 +362,7 @@ public class AutoDesignApplier : IDisposable
|
|||
if (!parameterFlags.HasFlag(flag))
|
||||
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);
|
||||
totalParameterFlags |= flag;
|
||||
}
|
||||
|
|
@ -381,7 +383,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var item = design.Item(slot);
|
||||
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);
|
||||
totalEquipFlags |= flag;
|
||||
}
|
||||
|
|
@ -390,7 +392,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var stainFlag = slot.ToStainFlag();
|
||||
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);
|
||||
totalEquipFlags |= stainFlag;
|
||||
}
|
||||
|
|
@ -400,7 +402,7 @@ public class AutoDesignApplier : IDisposable
|
|||
{
|
||||
var item = design.Item(EquipSlot.MainHand);
|
||||
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 (fromJobChange)
|
||||
|
|
@ -420,7 +422,7 @@ public class AutoDesignApplier : IDisposable
|
|||
{
|
||||
var item = design.Item(EquipSlot.OffHand);
|
||||
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 (fromJobChange)
|
||||
|
|
@ -438,14 +440,14 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
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);
|
||||
totalEquipFlags |= EquipFlag.MainhandStain;
|
||||
}
|
||||
|
||||
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);
|
||||
totalEquipFlags |= EquipFlag.OffhandStain;
|
||||
}
|
||||
|
|
@ -467,7 +469,7 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
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);
|
||||
customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race);
|
||||
totalCustomizeFlags |= CustomizeFlag.Clan | CustomizeFlag.Race;
|
||||
|
|
@ -475,7 +477,7 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
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);
|
||||
customizeFlags &= ~CustomizeFlag.Gender;
|
||||
totalCustomizeFlags |= CustomizeFlag.Gender;
|
||||
|
|
@ -486,7 +488,7 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
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);
|
||||
customizeFlags &= ~CustomizeFlag.Face;
|
||||
totalCustomizeFlags |= CustomizeFlag.Face;
|
||||
|
|
@ -506,7 +508,7 @@ public class AutoDesignApplier : IDisposable
|
|||
if (data.HasValue && _config.UnlockedItemMode && !_customizeUnlocks.IsUnlocked(data.Value, out _))
|
||||
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);
|
||||
totalCustomizeFlags |= flag;
|
||||
}
|
||||
|
|
@ -518,28 +520,28 @@ public class AutoDesignApplier : IDisposable
|
|||
{
|
||||
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);
|
||||
totalMetaFlags |= 0x01;
|
||||
}
|
||||
|
||||
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);
|
||||
totalMetaFlags |= 0x02;
|
||||
}
|
||||
|
||||
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);
|
||||
totalMetaFlags |= 0x04;
|
||||
}
|
||||
|
||||
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);
|
||||
totalMetaFlags |= 0x08;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
var newDesign = new AutoDesign()
|
||||
{
|
||||
Design = design,
|
||||
ApplicationType = AutoDesign.Type.All,
|
||||
Type = ApplicationType.All,
|
||||
Jobs = _jobs.JobGroups[1],
|
||||
};
|
||||
set.Designs.Add(newDesign);
|
||||
|
|
@ -328,21 +328,21 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
_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)
|
||||
return;
|
||||
|
||||
type &= AutoDesign.Type.All;
|
||||
applicationType &= ApplicationType.All;
|
||||
var design = set.Designs[which];
|
||||
if (design.ApplicationType == type)
|
||||
if (design.Type == applicationType)
|
||||
return;
|
||||
|
||||
var old = design.ApplicationType;
|
||||
design.ApplicationType = type;
|
||||
var old = design.Type;
|
||||
design.Type = applicationType;
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed application type from {old} to {type} for associated design {which + 1} in design set.");
|
||||
_event.Invoke(AutomationChanged.Type.ChangedType, set, (which, old, type));
|
||||
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, applicationType));
|
||||
}
|
||||
|
||||
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,
|
||||
ApplicationType = applicationType & AutoDesign.Type.All,
|
||||
Type = applicationType & ApplicationType.All,
|
||||
};
|
||||
return ParseConditions(setName, jObj, ret) ? ret : null;
|
||||
}
|
||||
|
|
@ -550,7 +551,24 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
private ActorIdentifier[] GetGroup(ActorIdentifier identifier)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
@ -566,23 +584,6 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
|
|||
identifier.Kind,
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ using Penumbra.GameData.Actors;
|
|||
|
||||
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 ActorIdentifier[] Identifiers;
|
||||
public string Name = name;
|
||||
public ActorIdentifier[] Identifiers = identifiers;
|
||||
public bool Enabled;
|
||||
public Base BaseState = Base.Current;
|
||||
|
||||
|
|
@ -32,13 +32,6 @@ public class AutoDesignSet
|
|||
: 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
|
||||
{
|
||||
Current,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public class FixedDesignMigrator(JobService jobs)
|
|||
|
||||
autoManager.AddDesign(set, leaf.Value);
|
||||
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 Glamourer.Automation;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -35,10 +37,11 @@ public sealed class Design : DesignBase, ISavable
|
|||
public DateTimeOffset LastEdit { get; internal set; }
|
||||
public LowerString Name { get; internal set; } = LowerString.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 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
|
||||
=> Identifier.ToString()[..8];
|
||||
|
|
@ -64,6 +67,7 @@ public sealed class Design : DesignBase, ISavable
|
|||
["Customize"] = SerializeCustomize(),
|
||||
["Parameters"] = SerializeParameters(),
|
||||
["Mods"] = SerializeMods(),
|
||||
["Links"] = Links.Serialize(),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -95,24 +99,18 @@ public sealed class Design : DesignBase, ISavable
|
|||
|
||||
#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;
|
||||
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."),
|
||||
};
|
||||
}
|
||||
|
||||
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 design = new Design(customizations, items)
|
||||
|
|
@ -131,8 +129,15 @@ public sealed class Design : DesignBase, ISavable
|
|||
LoadEquip(items, json["Equipment"], design, design.Name, true);
|
||||
LoadMods(json["Mods"], design);
|
||||
LoadParameters(json["Parameters"], design, design.Name);
|
||||
LoadLinks(linkLoader, json["Links"], design);
|
||||
design.Color = json["Color"]?.ToObject<string>() ?? string.Empty;
|
||||
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)
|
||||
|
|
@ -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
|
||||
|
||||
#region ISavable
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Glamourer.GameData;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Utility;
|
||||
|
|
@ -10,7 +11,7 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
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;
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
case (byte)'{':
|
||||
var jObj1 = JObject.Parse(Encoding.UTF8.GetString(bytes));
|
||||
ret = jObj1["Identifier"] != null
|
||||
? Design.LoadDesign(_customize, _items, jObj1)
|
||||
? Design.LoadDesign(_customize, _items, _linkLoader, jObj1)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj1);
|
||||
break;
|
||||
case 1:
|
||||
|
|
@ -90,7 +91,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
var jObj2 = JObject.Parse(decompressed);
|
||||
Debug.Assert(version == 3);
|
||||
ret = jObj2["Identifier"] != null
|
||||
? Design.LoadDesign(_customize, _items, jObj2)
|
||||
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||
break;
|
||||
}
|
||||
|
|
@ -101,7 +102,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
var jObj2 = JObject.Parse(decompressed);
|
||||
Debug.Assert(version == 5);
|
||||
ret = jObj2["Identifier"] != null
|
||||
? Design.LoadDesign(_customize, _items, jObj2)
|
||||
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||
break;
|
||||
}
|
||||
|
|
@ -111,7 +112,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
var jObj2 = JObject.Parse(decompressed);
|
||||
Debug.Assert(version == 6);
|
||||
ret = jObj2["Identifier"] != null
|
||||
? Design.LoadDesign(_customize, _items, jObj2)
|
||||
? Design.LoadDesign(_customize, _items, _linkLoader, jObj2)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObj2);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Utility;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
|
|
@ -20,30 +21,33 @@ public class DesignManager
|
|||
private readonly HumanModelList _humans;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly DesignChanged _event;
|
||||
private readonly List<Design> _designs = [];
|
||||
private readonly DesignStorage _designs;
|
||||
private readonly Dictionary<Guid, DesignData> _undoStore = [];
|
||||
|
||||
public IReadOnlyList<Design> Designs
|
||||
=> _designs;
|
||||
|
||||
public DesignManager(SaveService saveService, ItemManager items, CustomizeService customizations,
|
||||
DesignChanged @event, HumanModelList humans)
|
||||
DesignChanged @event, HumanModelList humans, DesignStorage storage, DesignLinkLoader designLinkLoader)
|
||||
{
|
||||
_designs = storage;
|
||||
_saveService = saveService;
|
||||
_items = items;
|
||||
_customizations = customizations;
|
||||
_event = @event;
|
||||
_humans = humans;
|
||||
|
||||
LoadDesigns(designLinkLoader);
|
||||
CreateDesignFolder(saveService);
|
||||
LoadDesigns();
|
||||
MigrateOldDesigns();
|
||||
designLinkLoader.SetAllObjects();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear currently loaded designs and load all designs anew from file.
|
||||
/// Invalid data is fixed, but changes are not saved until manual changes.
|
||||
/// </summary>
|
||||
public void LoadDesigns()
|
||||
public void LoadDesigns(DesignLinkLoader linkLoader)
|
||||
{
|
||||
_humans.Awaiter.Wait();
|
||||
_customizations.Awaiter.Wait();
|
||||
|
|
@ -59,7 +63,7 @@ public class DesignManager
|
|||
{
|
||||
var text = File.ReadAllText(f.FullName);
|
||||
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));
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -497,14 +501,14 @@ public class DesignManager
|
|||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
ActorState.MetaIndex.Wetness => design.GetDesignDataRef().SetIsWet(value),
|
||||
ActorState.MetaIndex.HatState => design.GetDesignDataRef().SetHatVisible(value),
|
||||
ActorState.MetaIndex.VisorState => design.GetDesignDataRef().SetVisor(value),
|
||||
ActorState.MetaIndex.WeaponState => design.GetDesignDataRef().SetWeaponVisible(value),
|
||||
MetaIndex.Wetness => design.GetDesignDataRef().SetIsWet(value),
|
||||
MetaIndex.HatState => design.GetDesignDataRef().SetHatVisible(value),
|
||||
MetaIndex.VisorState => design.GetDesignDataRef().SetVisor(value),
|
||||
MetaIndex.WeaponState => design.GetDesignDataRef().SetWeaponVisible(value),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(metaIndex), metaIndex, null),
|
||||
};
|
||||
if (!change)
|
||||
|
|
@ -517,14 +521,14 @@ public class DesignManager
|
|||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
ActorState.MetaIndex.Wetness => design.SetApplyWetness(value),
|
||||
ActorState.MetaIndex.HatState => design.SetApplyHatVisible(value),
|
||||
ActorState.MetaIndex.VisorState => design.SetApplyVisorToggle(value),
|
||||
ActorState.MetaIndex.WeaponState => design.SetApplyWeaponVisible(value),
|
||||
MetaIndex.Wetness => design.SetApplyWetness(value),
|
||||
MetaIndex.HatState => design.SetApplyHatVisible(value),
|
||||
MetaIndex.VisorState => design.SetApplyVisorToggle(value),
|
||||
MetaIndex.WeaponState => design.SetApplyWeaponVisible(value),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(metaIndex), metaIndex, null),
|
||||
};
|
||||
if (!change)
|
||||
|
|
|
|||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
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>
|
||||
Customize,
|
||||
|
||||
|
|
@ -92,6 +95,9 @@ public sealed class DesignChanged()
|
|||
|
||||
public enum Priority
|
||||
{
|
||||
/// <seealso cref="Designs.Links.DesignLinkManager.OnDesignChange"/>
|
||||
DesignLinkManager = 1,
|
||||
|
||||
/// <seealso cref="Automation.AutoDesignManager.OnDesignChange"/>
|
||||
AutoDesignManager = 1,
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ public class ActorPanel(
|
|||
if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked, _lockedRedraw))
|
||||
_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));
|
||||
}
|
||||
|
||||
|
|
@ -187,21 +187,21 @@ public class ActorPanel(
|
|||
{
|
||||
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!));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
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!));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
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!));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,13 +369,13 @@ public class SetPanel(
|
|||
private void DrawApplicationTypeBoxes(AutoDesignSet set, AutoDesign design, int autoDesignIndex, bool singleLine)
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(2 * ImGuiHelpers.GlobalScale));
|
||||
var newType = design.ApplicationType;
|
||||
var newType = design.Type;
|
||||
var newTypeInt = (uint)newType;
|
||||
style.Push(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale);
|
||||
using (_ = ImRaii.PushColor(ImGuiCol.Border, ColorId.FolderLine.Value()))
|
||||
{
|
||||
if (ImGui.CheckboxFlags("##all", ref newTypeInt, (uint)AutoDesign.Type.All))
|
||||
newType = (AutoDesign.Type)newTypeInt;
|
||||
if (ImGui.CheckboxFlags("##all", ref newTypeInt, (uint)ApplicationType.All))
|
||||
newType = (ApplicationType)newTypeInt;
|
||||
}
|
||||
|
||||
style.Pop();
|
||||
|
|
@ -385,7 +385,7 @@ public class SetPanel(
|
|||
void Box(int 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))
|
||||
newType = value ? newType | type : newType & ~type;
|
||||
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."),
|
||||
(AutoDesign.Type.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."),
|
||||
(AutoDesign.Type.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.Armor, "Apply all armor piece 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."),
|
||||
(ApplicationType.GearCustomization, "Apply all dye and crest changes that are enabled in this design."),
|
||||
(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>
|
||||
|
|
|
|||
|
|
@ -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})";
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
|
||||
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();
|
||||
PrintRow("Visor Toggled", state.BaseData.IsVisorToggled(), state.ModelData.IsVisorToggled(),
|
||||
state[ActorState.MetaIndex.VisorState]);
|
||||
state.Source[MetaIndex.VisorState]);
|
||||
ImGui.TableNextRow();
|
||||
PrintRow("Weapon Visible", state.BaseData.IsWeaponVisible(), state.ModelData.IsWeaponVisible(),
|
||||
state[ActorState.MetaIndex.WeaponState]);
|
||||
state.Source[MetaIndex.WeaponState]);
|
||||
ImGui.TableNextRow();
|
||||
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.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>())
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class AutoDesignPanel(AutoDesignManager _autoDesignManager) : IGameDataDr
|
|||
foreach (var (design, designIdx) in set.Designs.WithIndex())
|
||||
{
|
||||
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,
|
||||
ImportService _importService,
|
||||
MultiDesignPanel _multiDesignPanel,
|
||||
CustomizeParameterDrawer _parameterDrawer)
|
||||
CustomizeParameterDrawer _parameterDrawer,
|
||||
DesignLinkDrawer _designLinkDrawer)
|
||||
{
|
||||
private readonly FileDialogManager _fileDialog = new();
|
||||
|
||||
|
|
@ -119,21 +120,21 @@ public class DesignPanel(
|
|||
{
|
||||
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!));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
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!));
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
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!));
|
||||
}
|
||||
}
|
||||
|
|
@ -158,7 +159,7 @@ public class DesignPanel(
|
|||
_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));
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +167,7 @@ public class DesignPanel(
|
|||
{
|
||||
if (!_config.UseAdvancedParameters)
|
||||
return;
|
||||
|
||||
using var h = ImRaii.CollapsingHeader("Advanced Customizations");
|
||||
if (!h)
|
||||
return;
|
||||
|
|
@ -259,20 +261,20 @@ public class DesignPanel(
|
|||
}
|
||||
}
|
||||
|
||||
ApplyEquip("Weapons", AutoDesign.WeaponFlags, false, new[]
|
||||
ApplyEquip("Weapons", ApplicationTypeExtensions.WeaponFlags, false, new[]
|
||||
{
|
||||
EquipSlot.MainHand,
|
||||
EquipSlot.OffHand,
|
||||
});
|
||||
|
||||
ImGui.NewLine();
|
||||
ApplyEquip("Armor", AutoDesign.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots);
|
||||
ApplyEquip("Armor", ApplicationTypeExtensions.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
ApplyEquip("Accessories", AutoDesign.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots);
|
||||
ApplyEquip("Accessories", ApplicationTypeExtensions.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
ApplyEquip("Dyes", AutoDesign.StainFlags, true,
|
||||
ApplyEquip("Dyes", ApplicationTypeExtensions.StainFlags, true,
|
||||
EquipSlotExtensions.FullSlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
|
|
@ -294,19 +296,19 @@ public class DesignPanel(
|
|||
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
|
||||
var apply = bigChange ? (flags & 0x01) == 0x01 : _selector.Selected!.DoApplyHatVisible();
|
||||
if (ImGui.Checkbox("Apply Hat Visibility", ref apply) || bigChange)
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, ActorState.MetaIndex.HatState, apply);
|
||||
_manager.ChangeApplyMeta(_selector.Selected!, MetaIndex.HatState, apply);
|
||||
|
||||
apply = bigChange ? (flags & 0x02) == 0x02 : _selector.Selected!.DoApplyVisorToggle();
|
||||
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();
|
||||
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();
|
||||
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()
|
||||
|
|
@ -370,6 +372,7 @@ public class DesignPanel(
|
|||
_designDetails.Draw();
|
||||
DrawApplicationRules();
|
||||
_modAssociations.Draw();
|
||||
_designLinkDrawer.Draw();
|
||||
}
|
||||
|
||||
private void DrawButtonRow()
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ public class NpcPanel(
|
|||
_equipDrawer.DrawWeapons(mainhandData, offhandData, false);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,17 +22,17 @@ public ref struct 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
|
||||
{
|
||||
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))),
|
||||
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)),
|
||||
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)),
|
||||
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)),
|
||||
_ => throw new Exception("Unsupported meta index."),
|
||||
};
|
||||
|
|
@ -73,19 +73,19 @@ public ref struct ToggleDrawData
|
|||
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
|
||||
{
|
||||
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))),
|
||||
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(),
|
||||
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(),
|
||||
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)),
|
||||
_ => 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
|
||||
{
|
||||
ActorState.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."),
|
||||
ActorState.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.HatState => ("Hat Visible", "Hide or show the characters head gear."),
|
||||
MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear."),
|
||||
MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn."),
|
||||
MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not."),
|
||||
_ => throw new Exception("Unsupported meta index."),
|
||||
};
|
||||
return new ToggleDrawData
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ public sealed class CharaFile
|
|||
public CustomizeFlag ApplyCustomize;
|
||||
public EquipFlag ApplyEquip;
|
||||
|
||||
public static CharaFile? ParseData(ItemManager items, string data, string? name = null)
|
||||
{
|
||||
try
|
||||
public static CharaFile ParseData(ItemManager items, string data, string? name = null)
|
||||
{
|
||||
var jObj = JObject.Parse(data);
|
||||
SanityCheck(jObj);
|
||||
|
|
@ -28,11 +26,6 @@ public sealed class CharaFile
|
|||
ret.ApplyEquip = ParseEquipment(items, jObj, ref ret.Data);
|
||||
return ret;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (jObj["TypeName"]?.ToObject<string>() is not "Anamnesis Character File")
|
||||
throw new Exception("Wrong TypeName property set.");
|
||||
|
||||
var type = jObj["ObjectKind"]?.ToObject<string>();
|
||||
if (type is not "Player")
|
||||
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 file = CharaFile.CharaFile.ParseData(_items, text, Path.GetFileNameWithoutExtension(path));
|
||||
if (file == null)
|
||||
throw new Exception();
|
||||
|
||||
name = file.Name;
|
||||
design = new DesignBase(_customizations, file.Data, file.ApplyEquip, file.ApplyCustomize);
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ public class PaletteImport(DalamudPluginInterface pluginInterface, DesignManager
|
|||
design.ApplyCustomize = 0;
|
||||
design.ApplyEquip = 0;
|
||||
design.ApplyCrest = 0;
|
||||
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.VisorState, false);
|
||||
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.HatState, false);
|
||||
designManager.ChangeApplyMeta(design, ActorState.MetaIndex.WeaponState, false);
|
||||
designManager.ChangeApplyMeta(design, MetaIndex.VisorState, false);
|
||||
designManager.ChangeApplyMeta(design, MetaIndex.HatState, false);
|
||||
designManager.ChangeApplyMeta(design, MetaIndex.WeaponState, false);
|
||||
foreach (var flag in flags.Iterate())
|
||||
{
|
||||
designManager.ChangeApplyParameter(design, flag, true);
|
||||
|
|
|
|||
|
|
@ -255,26 +255,26 @@ public class CommandService : IDisposable
|
|||
}
|
||||
|
||||
--designIdx;
|
||||
AutoDesign.Type applicationFlags = 0;
|
||||
ApplicationType applicationFlags = 0;
|
||||
if (split2.Length == 2)
|
||||
foreach (var character in split2[1])
|
||||
{
|
||||
switch (char.ToLowerInvariant(character))
|
||||
{
|
||||
case 'c':
|
||||
applicationFlags |= AutoDesign.Type.Customizations;
|
||||
applicationFlags |= ApplicationType.Customizations;
|
||||
break;
|
||||
case 'e':
|
||||
applicationFlags |= AutoDesign.Type.Armor;
|
||||
applicationFlags |= ApplicationType.Armor;
|
||||
break;
|
||||
case 'a':
|
||||
applicationFlags |= AutoDesign.Type.Accessories;
|
||||
applicationFlags |= ApplicationType.Accessories;
|
||||
break;
|
||||
case 'd':
|
||||
applicationFlags |= AutoDesign.Type.GearCustomization;
|
||||
applicationFlags |= ApplicationType.GearCustomization;
|
||||
break;
|
||||
case 'w':
|
||||
applicationFlags |= AutoDesign.Type.Weapons;
|
||||
applicationFlags |= ApplicationType.Weapons;
|
||||
break;
|
||||
default:
|
||||
_chat.Print(new SeStringBuilder().AddText("The value ").AddPurple(split2[1], true)
|
||||
|
|
|
|||
|
|
@ -11,15 +11,6 @@ namespace Glamourer.State;
|
|||
|
||||
public class ActorState
|
||||
{
|
||||
public enum MetaIndex
|
||||
{
|
||||
Wetness = EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices,
|
||||
HatState,
|
||||
VisorState,
|
||||
WeaponState,
|
||||
ModelId,
|
||||
}
|
||||
|
||||
public readonly ActorIdentifier Identifier;
|
||||
|
||||
public bool AllowsRedraw(ICondition condition)
|
||||
|
|
@ -77,47 +68,14 @@ public class ActorState
|
|||
=> 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>
|
||||
private readonly StateChanged.Source[] _sources = Enumerable
|
||||
.Repeat(StateChanged.Source.Game,
|
||||
EquipFlagExtensions.NumEquipFlags
|
||||
+ CustomizationExtensions.NumIndices
|
||||
+ 5
|
||||
+ CrestExtensions.AllRelevantSet.Count
|
||||
+ CustomizeParameterExtensions.AllFlags.Count).ToArray();
|
||||
public readonly StateSource Source = new();
|
||||
|
||||
internal ActorState(ActorIdentifier identifier)
|
||||
=> 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()
|
||||
=> 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ public class StateApplier(
|
|||
// If the source is not IPC we do not want to apply restrictions.
|
||||
var data = GetData(state);
|
||||
if (apply)
|
||||
ChangeArmor(data, slot, state.ModelData.Armor(slot), state[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());
|
||||
|
||||
return data;
|
||||
|
|
|
|||
|
|
@ -61,21 +61,21 @@ public class StateEditor
|
|||
state.ModelData.SetHatVisible(true);
|
||||
state.ModelData.SetWeaponVisible(true);
|
||||
state.ModelData.SetVisor(false);
|
||||
state[ActorState.MetaIndex.ModelId] = source;
|
||||
state[ActorState.MetaIndex.HatState] = source;
|
||||
state[ActorState.MetaIndex.WeaponState] = source;
|
||||
state[ActorState.MetaIndex.VisorState] = source;
|
||||
state.Source[MetaIndex.ModelId] = source;
|
||||
state.Source[MetaIndex.HatState] = source;
|
||||
state.Source[MetaIndex.WeaponState] = source;
|
||||
state.Source[MetaIndex.VisorState] = source;
|
||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||
{
|
||||
state[slot, true] = source;
|
||||
state[slot, false] = source;
|
||||
state.Source[slot, true] = source;
|
||||
state.Source[slot, false] = source;
|
||||
}
|
||||
|
||||
state[CustomizeIndex.Clan] = source;
|
||||
state[CustomizeIndex.Gender] = source;
|
||||
state.Source[CustomizeIndex.Clan] = source;
|
||||
state.Source[CustomizeIndex.Gender] = source;
|
||||
var set = _customizations.Manager.GetSet(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
|
||||
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(set.IsAvailable))
|
||||
state[index] = source;
|
||||
state.Source[index] = source;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -83,7 +83,7 @@ public class StateEditor
|
|||
return false;
|
||||
|
||||
state.ModelData.LoadNonHuman(modelId, customize, equipData);
|
||||
state[ActorState.MetaIndex.ModelId] = source;
|
||||
state.Source[MetaIndex.ModelId] = source;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -98,7 +98,7 @@ public class StateEditor
|
|||
return false;
|
||||
|
||||
state.ModelData.Customize[idx] = value;
|
||||
state[idx] = source;
|
||||
state.Source[idx] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ public class StateEditor
|
|||
foreach (var type in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
if (applied.HasFlag(type.ToFlag()))
|
||||
state[type] = source;
|
||||
state.Source[type] = source;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -144,12 +144,12 @@ public class StateEditor
|
|||
_gPose.AddActionOnLeave(() =>
|
||||
{
|
||||
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[slot, false] = source;
|
||||
state.Source[slot, false] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -174,14 +174,14 @@ public class StateEditor
|
|||
_gPose.AddActionOnLeave(() =>
|
||||
{
|
||||
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.SetStain(slot, stain);
|
||||
state[slot, false] = source;
|
||||
state[slot, true] = source;
|
||||
state.Source[slot, false] = source;
|
||||
state.Source[slot, true] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ public class StateEditor
|
|||
return false;
|
||||
|
||||
state.ModelData.SetStain(slot, stain);
|
||||
state[slot, true] = source;
|
||||
state.Source[slot, true] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ public class StateEditor
|
|||
return false;
|
||||
|
||||
state.ModelData.SetCrest(slot, crest);
|
||||
state[slot] = source;
|
||||
state.Source[slot] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -218,20 +218,20 @@ public class StateEditor
|
|||
return false;
|
||||
|
||||
state.ModelData.Parameters.Set(flag, value);
|
||||
state[flag] = source;
|
||||
state.Source[flag] = source;
|
||||
|
||||
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)
|
||||
{
|
||||
(var setter, oldValue) = index switch
|
||||
{
|
||||
ActorState.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()),
|
||||
ActorState.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.Wetness => ((Func<bool, bool>)(v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
|
||||
MetaIndex.HatState => ((Func<bool, bool>)(v => state.ModelData.SetHatVisible(v)), state.ModelData.IsHatVisible()),
|
||||
MetaIndex.VisorState => ((Func<bool, bool>)(v => state.ModelData.SetVisor(v)), state.ModelData.IsVisorToggled()),
|
||||
MetaIndex.WeaponState => ((Func<bool, bool>)(v => state.ModelData.SetWeaponVisible(v)),
|
||||
state.ModelData.IsWeaponVisible()),
|
||||
_ => throw new Exception("Invalid MetaIndex."),
|
||||
};
|
||||
|
|
@ -240,7 +240,7 @@ public class StateEditor
|
|||
return false;
|
||||
|
||||
setter(value);
|
||||
state[index] = source;
|
||||
state.Source[index] = source;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ public class StateListener : IDisposable
|
|||
var set = _customizations.Manager.GetSet(model.Clan, model.Gender);
|
||||
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 oldValue = model[index];
|
||||
|
|
@ -213,7 +213,7 @@ public class StateListener : IDisposable
|
|||
&& _manager.TryGetValue(identifier, out var state))
|
||||
{
|
||||
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);
|
||||
|
|
@ -240,7 +240,7 @@ public class StateListener : IDisposable
|
|||
continue;
|
||||
|
||||
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.ChangeStain(state, slot, current.Stain, StateChanged.Source.Game);
|
||||
|
|
@ -251,7 +251,7 @@ public class StateListener : IDisposable
|
|||
_applier.ChangeWeapon(objects, slot, currentItem, stain);
|
||||
break;
|
||||
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());
|
||||
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.
|
||||
case UpdateState.Transformed: break;
|
||||
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);
|
||||
else
|
||||
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);
|
||||
else
|
||||
apply = true;
|
||||
|
|
@ -384,12 +384,12 @@ public class StateListener : IDisposable
|
|||
// Update model state if not on fixed design.
|
||||
case UpdateState.Change:
|
||||
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);
|
||||
else
|
||||
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);
|
||||
else
|
||||
apply = true;
|
||||
|
|
@ -418,7 +418,7 @@ public class StateListener : IDisposable
|
|||
switch (UpdateBaseCrest(actor, state, slot, value))
|
||||
{
|
||||
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);
|
||||
else
|
||||
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,
|
||||
// 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();
|
||||
else
|
||||
_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,
|
||||
// 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();
|
||||
else
|
||||
_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,
|
||||
// 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();
|
||||
else
|
||||
_manager.ChangeWeaponState(state, value, StateChanged.Source.Game);
|
||||
|
|
@ -732,7 +732,7 @@ public class StateListener : IDisposable
|
|||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
var newValue = data[flag];
|
||||
switch (state[flag])
|
||||
switch (state.Source[flag])
|
||||
{
|
||||
case StateChanged.Source.Game:
|
||||
if (state.BaseData.Parameters.Set(flag, newValue))
|
||||
|
|
@ -755,7 +755,7 @@ public class StateListener : IDisposable
|
|||
break;
|
||||
case StateChanged.Source.Pending:
|
||||
state.BaseData.Parameters.Set(flag, newValue);
|
||||
state[flag] = StateChanged.Source.Manual;
|
||||
state.Source[flag] = StateChanged.Source.Manual;
|
||||
if (_config.UseAdvancedParameters)
|
||||
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -329,49 +329,49 @@ public class StateManager(
|
|||
/// <summary> Change hat visibility. </summary>
|
||||
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;
|
||||
|
||||
var actors = _applier.ChangeHatState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"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>
|
||||
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;
|
||||
|
||||
var actors = _applier.ChangeWeaponState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Weapon Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.WeaponState));
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.WeaponState));
|
||||
}
|
||||
|
||||
/// <summary> Change visor state. </summary>
|
||||
public void ChangeVisorState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||
{
|
||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.VisorState, value, source, out var old, key))
|
||||
if (!_editor.ChangeMetaState(state, MetaIndex.VisorState, value, source, out var old, key))
|
||||
return;
|
||||
|
||||
var actors = _applier.ChangeVisor(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Visor State in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.VisorState));
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, MetaIndex.VisorState));
|
||||
}
|
||||
|
||||
/// <summary> Set GPose Wetness. </summary>
|
||||
public void ChangeWetness(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||
{
|
||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.Wetness, value, source, out var old, key))
|
||||
if (!_editor.ChangeMetaState(state, MetaIndex.Wetness, value, source, out var old, key))
|
||||
return;
|
||||
|
||||
var actors = _applier.ChangeWetness(state, true);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Wetness in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, state[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
|
||||
|
|
@ -385,16 +385,16 @@ public class StateManager(
|
|||
|
||||
var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman;
|
||||
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 (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())
|
||||
_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())
|
||||
_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)
|
||||
? design.ApplyCustomize
|
||||
|
|
@ -419,13 +419,13 @@ public class StateManager(
|
|||
if (!state.ModelData.Customize.Highlights)
|
||||
_editor.ChangeParameter(state, CustomizeParameterFlag.HairHighlight,
|
||||
state.ModelData.Parameters[CustomizeParameterFlag.HairDiffuse],
|
||||
state[CustomizeParameterFlag.HairDiffuse], out _, key);
|
||||
state.Source[CustomizeParameterFlag.HairDiffuse], out _, key);
|
||||
}
|
||||
|
||||
var actors = ApplyAll(state, redraw, false);
|
||||
Glamourer.Log.Verbose(
|
||||
$"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;
|
||||
|
||||
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
|
||||
|
|
@ -455,7 +455,7 @@ public class StateManager(
|
|||
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
@ -489,22 +489,22 @@ public class StateManager(
|
|||
state.ModelData = state.BaseData;
|
||||
state.ModelData.SetIsWet(false);
|
||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||
state[index] = StateChanged.Source.Game;
|
||||
state.Source[index] = StateChanged.Source.Game;
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||
{
|
||||
state[slot, true] = StateChanged.Source.Game;
|
||||
state[slot, false] = StateChanged.Source.Game;
|
||||
state.Source[slot, true] = StateChanged.Source.Game;
|
||||
state.Source[slot, false] = StateChanged.Source.Game;
|
||||
}
|
||||
|
||||
foreach (var type in Enum.GetValues<ActorState.MetaIndex>())
|
||||
state[type] = StateChanged.Source.Game;
|
||||
foreach (var type in Enum.GetValues<MetaIndex>())
|
||||
state.Source[type] = StateChanged.Source.Game;
|
||||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
state[slot] = StateChanged.Source.Game;
|
||||
state.Source[slot] = StateChanged.Source.Game;
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
state[flag] = StateChanged.Source.Game;
|
||||
state.Source[flag] = StateChanged.Source.Game;
|
||||
|
||||
var actors = ActorData.Invalid;
|
||||
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||
|
|
@ -523,7 +523,7 @@ public class StateManager(
|
|||
state.ModelData.Parameters = state.BaseData.Parameters;
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
state[flag] = StateChanged.Source.Game;
|
||||
state.Source[flag] = StateChanged.Source.Game;
|
||||
|
||||
var actors = ActorData.Invalid;
|
||||
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||
|
|
@ -538,69 +538,69 @@ public class StateManager(
|
|||
if (!state.Unlock(key))
|
||||
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];
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
switch (state[flag])
|
||||
switch (state.Source[flag])
|
||||
{
|
||||
case StateChanged.Source.Fixed:
|
||||
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];
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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