mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Current state.
This commit is contained in:
parent
7a602d6ec5
commit
81059411e5
42 changed files with 913 additions and 320 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 4aaece34289d64363bc32aaa8fe52c8e7d3dce32
|
||||
Subproject commit ca003395306791b9e595683c47824b4718385311
|
||||
|
|
@ -35,7 +35,8 @@ public class ApiHelpers(ObjectManager objects, StateManager stateManager, ActorM
|
|||
state = null;
|
||||
return GlamourerApiEc.ActorNotFound;
|
||||
}
|
||||
stateManager.TryGetValue(identifier, out state);
|
||||
|
||||
stateManager.TryGetValue(identifier, out state);
|
||||
return GlamourerApiEc.Success;
|
||||
}
|
||||
|
||||
|
|
@ -54,12 +55,10 @@ public class ApiHelpers(ObjectManager objects, StateManager stateManager, ActorM
|
|||
internal static DesignBase.FlagRestrictionResetter Restrict(DesignBase design, ApplyFlag flags)
|
||||
=> (flags & (ApplyFlag.Equipment | ApplyFlag.Customization)) switch
|
||||
{
|
||||
ApplyFlag.Equipment => design.TemporarilyRestrictApplication(EquipFlagExtensions.All, 0, CrestExtensions.All, 0),
|
||||
ApplyFlag.Customization => design.TemporarilyRestrictApplication(0, CustomizeFlagExtensions.All, 0,
|
||||
CustomizeParameterExtensions.All),
|
||||
ApplyFlag.Equipment | ApplyFlag.Customization => design.TemporarilyRestrictApplication(EquipFlagExtensions.All,
|
||||
CustomizeFlagExtensions.All, CrestExtensions.All, CustomizeParameterExtensions.All),
|
||||
_ => design.TemporarilyRestrictApplication(0, 0, 0, 0),
|
||||
ApplyFlag.Equipment => design.TemporarilyRestrictApplication(ApplicationCollection.Equipment),
|
||||
ApplyFlag.Customization => design.TemporarilyRestrictApplication(ApplicationCollection.Customizations),
|
||||
ApplyFlag.Equipment | ApplyFlag.Customization => design.TemporarilyRestrictApplication(ApplicationCollection.All),
|
||||
_ => design.TemporarilyRestrictApplication(ApplicationCollection.None),
|
||||
};
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ public static class ApplicationTypeExtensions
|
|||
(ApplicationType.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
|
||||
];
|
||||
|
||||
public static (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, MetaFlag Meta) ApplyWhat(
|
||||
this ApplicationType type, IDesignStandIn designStandIn)
|
||||
public static ApplicationCollection Collection(this ApplicationType type)
|
||||
{
|
||||
var equipFlags = (type.HasFlag(ApplicationType.Weapons) ? WeaponFlags : 0)
|
||||
| (type.HasFlag(ApplicationType.Armor) ? ArmorFlags : 0)
|
||||
|
|
@ -37,18 +36,18 @@ public static class ApplicationTypeExtensions
|
|||
| (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;
|
||||
var metaFlag = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState : 0)
|
||||
var crestFlags = type.HasFlag(ApplicationType.GearCustomization) ? CrestExtensions.AllRelevant : 0;
|
||||
var metaFlags = (type.HasFlag(ApplicationType.Armor) ? MetaFlag.HatState | MetaFlag.VisorState : 0)
|
||||
| (type.HasFlag(ApplicationType.Weapons) ? MetaFlag.WeaponState : 0)
|
||||
| (type.HasFlag(ApplicationType.Customizations) ? MetaFlag.Wetness : 0);
|
||||
var bonusFlags = type.HasFlag(ApplicationType.Armor) ? BonusExtensions.All : 0;
|
||||
|
||||
if (designStandIn is not DesignBase design)
|
||||
return (equipFlags, customizeFlags, crestFlag, parameterFlags, metaFlag);
|
||||
|
||||
return (equipFlags & design!.ApplyEquip, customizeFlags & design.ApplyCustomize, crestFlag & design.ApplyCrest,
|
||||
parameterFlags & design.ApplyParameters, metaFlag & design.ApplyMeta);
|
||||
return new ApplicationCollection(equipFlags, bonusFlags, customizeFlags, crestFlags, parameterFlags, metaFlags);
|
||||
}
|
||||
|
||||
public static ApplicationCollection ApplyWhat(this ApplicationType type, IDesignStandIn designStandIn)
|
||||
=> designStandIn is not DesignBase design ? type.Collection() : type.Collection().Restrict(design.Application);
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,6 @@ public class AutoDesign
|
|||
return ret;
|
||||
}
|
||||
|
||||
public (EquipFlag Equip, CustomizeFlag Customize, CrestFlag Crest, CustomizeParameterFlag Parameters, MetaFlag Meta) ApplyWhat()
|
||||
public ApplicationCollection ApplyWhat()
|
||||
=> Type.ApplyWhat(Design);
|
||||
}
|
||||
|
|
|
|||
61
Glamourer/Designs/ApplicationCollection.cs
Normal file
61
Glamourer/Designs/ApplicationCollection.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using Glamourer.GameData;
|
||||
using ImGuiNET;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public record struct ApplicationCollection(
|
||||
EquipFlag Equip,
|
||||
BonusItemFlag BonusItem,
|
||||
CustomizeFlag Customize,
|
||||
CrestFlag Crest,
|
||||
CustomizeParameterFlag Parameters,
|
||||
MetaFlag Meta)
|
||||
{
|
||||
public static readonly ApplicationCollection All = new(EquipFlagExtensions.All, BonusExtensions.All,
|
||||
CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, CustomizeParameterExtensions.All, MetaExtensions.All);
|
||||
|
||||
public static readonly ApplicationCollection None = new(0, 0, 0, 0, 0, 0);
|
||||
|
||||
public static readonly ApplicationCollection Equipment = new(EquipFlagExtensions.All, BonusExtensions.All,
|
||||
0, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState);
|
||||
|
||||
public static readonly ApplicationCollection Customizations = new(0, 0, CustomizeFlagExtensions.AllRelevant, 0,
|
||||
CustomizeParameterExtensions.All, MetaFlag.Wetness);
|
||||
|
||||
public static readonly ApplicationCollection Default = new(EquipFlagExtensions.All, BonusExtensions.All,
|
||||
CustomizeFlagExtensions.AllRelevant, CrestExtensions.AllRelevant, 0, MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState);
|
||||
|
||||
public static ApplicationCollection FromKeys()
|
||||
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
|
||||
{
|
||||
(false, false) => All,
|
||||
(true, true) => All,
|
||||
(true, false) => Equipment,
|
||||
(false, true) => Customizations,
|
||||
};
|
||||
|
||||
public void RemoveEquip()
|
||||
{
|
||||
Equip = 0;
|
||||
BonusItem = 0;
|
||||
Crest = 0;
|
||||
Meta &= ~(MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState);
|
||||
}
|
||||
|
||||
public void RemoveCustomize()
|
||||
{
|
||||
Customize = 0;
|
||||
Parameters = 0;
|
||||
Meta &= MetaFlag.Wetness;
|
||||
}
|
||||
|
||||
public ApplicationCollection Restrict(ApplicationCollection old)
|
||||
=> new(old.Equip & Equip, old.BonusItem & BonusItem, old.Customize & Customize, old.Crest & Crest,
|
||||
old.Parameters & Parameters, old.Meta & Meta);
|
||||
|
||||
public ApplicationCollection CloneSecure()
|
||||
=> new(Equip & EquipFlagExtensions.All, BonusItem & BonusExtensions.All,
|
||||
(Customize & CustomizeFlagExtensions.AllRelevant) | CustomizeFlag.BodyType, Crest & CrestExtensions.AllRelevant,
|
||||
Parameters & CustomizeParameterExtensions.All, Meta & MetaExtensions.All);
|
||||
}
|
||||
|
|
@ -5,16 +5,9 @@ using Penumbra.GameData.Enums;
|
|||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public readonly struct ApplicationRules(
|
||||
EquipFlag equip,
|
||||
CustomizeFlag customize,
|
||||
CrestFlag crest,
|
||||
CustomizeParameterFlag parameters,
|
||||
MetaFlag meta,
|
||||
bool materials)
|
||||
public readonly struct ApplicationRules(ApplicationCollection application, bool materials)
|
||||
{
|
||||
public static readonly ApplicationRules All = new(EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant,
|
||||
CrestExtensions.AllRelevant, CustomizeParameterExtensions.All, MetaExtensions.All, true);
|
||||
public static readonly ApplicationRules All = new(ApplicationCollection.All, true);
|
||||
|
||||
public static ApplicationRules FromModifiers(ActorState state)
|
||||
=> FromModifiers(state, ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift);
|
||||
|
|
@ -23,54 +16,43 @@ public readonly struct ApplicationRules(
|
|||
=> NpcFromModifiers(ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift);
|
||||
|
||||
public static ApplicationRules AllButParameters(ActorState state)
|
||||
=> new(All.Equip, All.Customize, All.Crest, ComputeParameters(state.ModelData, state.BaseData, All.Parameters), All.Meta, true);
|
||||
=> new(ApplicationCollection.All with { Parameters = ComputeParameters(state.ModelData, state.BaseData, All.Parameters) }, true);
|
||||
|
||||
public static ApplicationRules AllWithConfig(Configuration config)
|
||||
=> new(All.Equip, All.Customize, All.Crest, config.UseAdvancedParameters ? All.Parameters : 0, All.Meta, config.UseAdvancedDyes);
|
||||
=> new(ApplicationCollection.All with { Parameters = config.UseAdvancedParameters ? All.Parameters : 0 }, config.UseAdvancedDyes);
|
||||
|
||||
public static ApplicationRules NpcFromModifiers(bool ctrl, bool shift)
|
||||
=> new(ctrl || !shift ? EquipFlagExtensions.All : 0,
|
||||
!ctrl || shift ? CustomizeFlagExtensions.AllRelevant : 0,
|
||||
0,
|
||||
0,
|
||||
ctrl || !shift ? MetaFlag.VisorState : 0, false);
|
||||
{
|
||||
var equip = ctrl || !shift ? EquipFlagExtensions.All : 0;
|
||||
var customize = !ctrl || shift ? CustomizeFlagExtensions.AllRelevant : 0;
|
||||
var visor = equip != 0 ? MetaFlag.VisorState : 0;
|
||||
return new ApplicationRules(new ApplicationCollection(equip, 0, customize, 0, 0, visor), false);
|
||||
}
|
||||
|
||||
public static ApplicationRules FromModifiers(ActorState state, bool ctrl, bool shift)
|
||||
{
|
||||
var equip = ctrl || !shift ? EquipFlagExtensions.All : 0;
|
||||
var customize = !ctrl || shift ? CustomizeFlagExtensions.AllRelevant : 0;
|
||||
var bonus = equip == 0 ? 0 : BonusExtensions.All;
|
||||
var crest = equip == 0 ? 0 : CrestExtensions.AllRelevant;
|
||||
var parameters = customize == 0 ? 0 : CustomizeParameterExtensions.All;
|
||||
var meta = state.ModelData.IsWet() ? MetaFlag.Wetness : 0;
|
||||
if (equip != 0)
|
||||
meta |= MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState;
|
||||
|
||||
return new ApplicationRules(equip, customize, crest, ComputeParameters(state.ModelData, state.BaseData, parameters), meta, equip != 0);
|
||||
var collection = new ApplicationCollection(equip, bonus, customize, crest,
|
||||
ComputeParameters(state.ModelData, state.BaseData, parameters), meta);
|
||||
return new ApplicationRules(collection, equip != 0);
|
||||
}
|
||||
|
||||
public void Apply(DesignBase design)
|
||||
{
|
||||
design.ApplyEquip = Equip;
|
||||
design.ApplyCustomize = Customize;
|
||||
design.ApplyCrest = Crest;
|
||||
design.ApplyParameters = Parameters;
|
||||
design.ApplyMeta = Meta;
|
||||
}
|
||||
=> design.Application = application;
|
||||
|
||||
public EquipFlag Equip
|
||||
=> equip & EquipFlagExtensions.All;
|
||||
|
||||
public CustomizeFlag Customize
|
||||
=> customize & CustomizeFlagExtensions.AllRelevant;
|
||||
|
||||
public CrestFlag Crest
|
||||
=> crest & CrestExtensions.AllRelevant;
|
||||
=> application.Equip & EquipFlagExtensions.All;
|
||||
|
||||
public CustomizeParameterFlag Parameters
|
||||
=> parameters & CustomizeParameterExtensions.All;
|
||||
|
||||
public MetaFlag Meta
|
||||
=> meta & MetaExtensions.All;
|
||||
=> application.Parameters & CustomizeParameterExtensions.All;
|
||||
|
||||
public bool Materials
|
||||
=> materials;
|
||||
|
|
|
|||
|
|
@ -42,23 +42,19 @@ public class DesignBase
|
|||
/// <summary> Used when importing .cma or .chara files. </summary>
|
||||
internal DesignBase(CustomizeService customize, in DesignData designData, EquipFlag equipFlags, CustomizeFlag customizeFlags)
|
||||
{
|
||||
_designData = designData;
|
||||
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
|
||||
ApplyEquip = equipFlags & EquipFlagExtensions.All;
|
||||
ApplyMeta = 0;
|
||||
CustomizeSet = SetCustomizationSet(customize);
|
||||
_designData = designData;
|
||||
ApplyCustomize = customizeFlags & CustomizeFlagExtensions.AllRelevant;
|
||||
Application.Equip = equipFlags & EquipFlagExtensions.All;
|
||||
Application.Meta = 0;
|
||||
CustomizeSet = SetCustomizationSet(customize);
|
||||
}
|
||||
|
||||
internal DesignBase(DesignBase clone)
|
||||
{
|
||||
_designData = clone._designData;
|
||||
_materials = clone._materials.Clone();
|
||||
CustomizeSet = clone.CustomizeSet;
|
||||
ApplyCustomize = clone.ApplyCustomizeRaw;
|
||||
ApplyEquip = clone.ApplyEquip & EquipFlagExtensions.All;
|
||||
ApplyParameters = clone.ApplyParameters & CustomizeParameterExtensions.All;
|
||||
ApplyCrest = clone.ApplyCrest & CrestExtensions.All;
|
||||
ApplyMeta = clone.ApplyMeta & MetaExtensions.All;
|
||||
_designData = clone._designData;
|
||||
_materials = clone._materials.Clone();
|
||||
CustomizeSet = clone.CustomizeSet;
|
||||
Application = clone.Application.CloneSecure();
|
||||
}
|
||||
|
||||
/// <summary> Ensure that the customization set is updated when the design data changes. </summary>
|
||||
|
|
@ -70,27 +66,20 @@ public class DesignBase
|
|||
|
||||
#region Application Data
|
||||
|
||||
private CustomizeFlag _applyCustomize = CustomizeFlagExtensions.AllRelevant;
|
||||
public CustomizeSet CustomizeSet { get; private set; }
|
||||
public CustomizeSet CustomizeSet { get; private set; }
|
||||
|
||||
public CustomizeParameterFlag ApplyParameters { get; internal set; }
|
||||
public ApplicationCollection Application = ApplicationCollection.Default;
|
||||
|
||||
internal CustomizeFlag ApplyCustomize
|
||||
{
|
||||
get => _applyCustomize.FixApplication(CustomizeSet);
|
||||
set => _applyCustomize = (value & CustomizeFlagExtensions.AllRelevant) | CustomizeFlag.BodyType;
|
||||
get => Application.Customize.FixApplication(CustomizeSet);
|
||||
set => Application.Customize = (value & CustomizeFlagExtensions.AllRelevant) | CustomizeFlag.BodyType;
|
||||
}
|
||||
|
||||
internal CustomizeFlag ApplyCustomizeExcludingBodyType
|
||||
=> _applyCustomize.FixApplication(CustomizeSet) & ~CustomizeFlag.BodyType;
|
||||
=> Application.Customize.FixApplication(CustomizeSet) & ~CustomizeFlag.BodyType;
|
||||
|
||||
internal CustomizeFlag ApplyCustomizeRaw
|
||||
=> _applyCustomize;
|
||||
|
||||
internal EquipFlag ApplyEquip = EquipFlagExtensions.All;
|
||||
internal CrestFlag ApplyCrest = CrestExtensions.AllRelevant;
|
||||
internal MetaFlag ApplyMeta = MetaFlag.HatState | MetaFlag.VisorState | MetaFlag.WeaponState;
|
||||
private bool _writeProtected;
|
||||
private bool _writeProtected;
|
||||
|
||||
public bool SetCustomize(CustomizeService customizeService, CustomizeArray customize)
|
||||
{
|
||||
|
|
@ -103,18 +92,18 @@ public class DesignBase
|
|||
}
|
||||
|
||||
public bool DoApplyMeta(MetaIndex index)
|
||||
=> ApplyMeta.HasFlag(index.ToFlag());
|
||||
=> Application.Meta.HasFlag(index.ToFlag());
|
||||
|
||||
public bool WriteProtected()
|
||||
=> _writeProtected;
|
||||
|
||||
public bool SetApplyMeta(MetaIndex index, bool value)
|
||||
{
|
||||
var newFlag = value ? ApplyMeta | index.ToFlag() : ApplyMeta & ~index.ToFlag();
|
||||
if (newFlag == ApplyMeta)
|
||||
var newFlag = value ? Application.Meta | index.ToFlag() : Application.Meta & ~index.ToFlag();
|
||||
if (newFlag == Application.Meta)
|
||||
return false;
|
||||
|
||||
ApplyMeta = newFlag;
|
||||
Application.Meta = newFlag;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -128,103 +117,100 @@ public class DesignBase
|
|||
}
|
||||
|
||||
public bool DoApplyEquip(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToFlag());
|
||||
=> Application.Equip.HasFlag(slot.ToFlag());
|
||||
|
||||
public bool DoApplyStain(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToStainFlag());
|
||||
=> Application.Equip.HasFlag(slot.ToStainFlag());
|
||||
|
||||
public bool DoApplyCustomize(CustomizeIndex idx)
|
||||
=> ApplyCustomize.HasFlag(idx.ToFlag());
|
||||
=> Application.Customize.HasFlag(idx.ToFlag());
|
||||
|
||||
public bool DoApplyCrest(CrestFlag slot)
|
||||
=> ApplyCrest.HasFlag(slot);
|
||||
=> Application.Crest.HasFlag(slot);
|
||||
|
||||
public bool DoApplyParameter(CustomizeParameterFlag flag)
|
||||
=> ApplyParameters.HasFlag(flag);
|
||||
=> Application.Parameters.HasFlag(flag);
|
||||
|
||||
public bool DoApplyBonusItem(BonusItemFlag slot)
|
||||
=> Application.BonusItem.HasFlag(slot);
|
||||
|
||||
internal bool SetApplyEquip(EquipSlot slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyEquip | slot.ToFlag() : ApplyEquip & ~slot.ToFlag();
|
||||
if (newValue == ApplyEquip)
|
||||
var newValue = value ? Application.Equip | slot.ToFlag() : Application.Equip & ~slot.ToFlag();
|
||||
if (newValue == Application.Equip)
|
||||
return false;
|
||||
|
||||
ApplyEquip = newValue;
|
||||
Application.Equip = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyBonusItem(BonusItemFlag slot, bool value)
|
||||
{
|
||||
var newValue = value ? Application.BonusItem | slot : Application.BonusItem & ~slot;
|
||||
if (newValue == Application.BonusItem)
|
||||
return false;
|
||||
|
||||
Application.BonusItem = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyStain(EquipSlot slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyEquip | slot.ToStainFlag() : ApplyEquip & ~slot.ToStainFlag();
|
||||
if (newValue == ApplyEquip)
|
||||
var newValue = value ? Application.Equip | slot.ToStainFlag() : Application.Equip & ~slot.ToStainFlag();
|
||||
if (newValue == Application.Equip)
|
||||
return false;
|
||||
|
||||
ApplyEquip = newValue;
|
||||
Application.Equip = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyCustomize(CustomizeIndex idx, bool value)
|
||||
{
|
||||
var newValue = value ? _applyCustomize | idx.ToFlag() : _applyCustomize & ~idx.ToFlag();
|
||||
if (newValue == _applyCustomize)
|
||||
var newValue = value ? Application.Customize | idx.ToFlag() : Application.Customize & ~idx.ToFlag();
|
||||
if (newValue == Application.Customize)
|
||||
return false;
|
||||
|
||||
_applyCustomize = newValue;
|
||||
Application.Customize = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyCrest(CrestFlag slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyCrest | slot : ApplyCrest & ~slot;
|
||||
if (newValue == ApplyCrest)
|
||||
var newValue = value ? Application.Crest | slot : Application.Crest & ~slot;
|
||||
if (newValue == Application.Crest)
|
||||
return false;
|
||||
|
||||
ApplyCrest = newValue;
|
||||
Application.Crest = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyParameter(CustomizeParameterFlag flag, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyParameters | flag : ApplyParameters & ~flag;
|
||||
if (newValue == ApplyParameters)
|
||||
var newValue = value ? Application.Parameters | flag : Application.Parameters & ~flag;
|
||||
if (newValue == Application.Parameters)
|
||||
return false;
|
||||
|
||||
ApplyParameters = newValue;
|
||||
Application.Parameters = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal FlagRestrictionResetter TemporarilyRestrictApplication(EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags,
|
||||
CustomizeParameterFlag parameterFlags)
|
||||
=> new(this, equipFlags, customizeFlags, crestFlags, parameterFlags);
|
||||
internal FlagRestrictionResetter TemporarilyRestrictApplication(ApplicationCollection restrictions)
|
||||
=> new(this, restrictions);
|
||||
|
||||
internal readonly struct FlagRestrictionResetter : IDisposable
|
||||
{
|
||||
private readonly DesignBase _design;
|
||||
private readonly EquipFlag _oldEquipFlags;
|
||||
private readonly CustomizeFlag _oldCustomizeFlags;
|
||||
private readonly CrestFlag _oldCrestFlags;
|
||||
private readonly CustomizeParameterFlag _oldParameterFlags;
|
||||
private readonly DesignBase _design;
|
||||
private readonly ApplicationCollection _oldFlags;
|
||||
|
||||
public FlagRestrictionResetter(DesignBase d, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags,
|
||||
CustomizeParameterFlag parameterFlags)
|
||||
public FlagRestrictionResetter(DesignBase d, ApplicationCollection restrictions)
|
||||
{
|
||||
_design = d;
|
||||
_oldEquipFlags = d.ApplyEquip;
|
||||
_oldCustomizeFlags = d.ApplyCustomizeRaw;
|
||||
_oldCrestFlags = d.ApplyCrest;
|
||||
_oldParameterFlags = d.ApplyParameters;
|
||||
d.ApplyEquip &= equipFlags;
|
||||
d.ApplyCustomize &= customizeFlags;
|
||||
d.ApplyCrest &= crestFlags;
|
||||
d.ApplyParameters &= parameterFlags;
|
||||
_design = d;
|
||||
_oldFlags = d.Application;
|
||||
_design.Application = restrictions.Restrict(_oldFlags);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_design.ApplyEquip = _oldEquipFlags;
|
||||
_design.ApplyCustomize = _oldCustomizeFlags;
|
||||
_design.ApplyCrest = _oldCrestFlags;
|
||||
_design.ApplyParameters = _oldParameterFlags;
|
||||
}
|
||||
=> _design.Application = _oldFlags;
|
||||
}
|
||||
|
||||
private CustomizeSet SetCustomizationSet(CustomizeService customize)
|
||||
|
|
@ -285,6 +271,22 @@ public class DesignBase
|
|||
});
|
||||
}
|
||||
|
||||
protected JObject SerializeBonusItems()
|
||||
{
|
||||
var ret = new JObject();
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var item = _designData.BonusItem(slot);
|
||||
ret[slot.ToString()] = new JObject()
|
||||
{
|
||||
["BonusId"] = item.ModelId.Id,
|
||||
["Apply"] = DoApplyBonusItem(slot),
|
||||
};
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected JObject SerializeCustomize()
|
||||
{
|
||||
var ret = new JObject()
|
||||
|
|
@ -299,7 +301,7 @@ public class DesignBase
|
|||
ret[idx.ToString()] = new JObject()
|
||||
{
|
||||
["Value"] = customize[idx].Value,
|
||||
["Apply"] = ApplyCustomizeRaw.HasFlag(idx.ToFlag()),
|
||||
["Apply"] = Application.Customize.HasFlag(idx.ToFlag()),
|
||||
};
|
||||
}
|
||||
else
|
||||
|
|
@ -382,7 +384,7 @@ public class DesignBase
|
|||
{
|
||||
var k = uint.Parse(key.Name, NumberStyles.HexNumber);
|
||||
var v = value.ToObject<MaterialValueDesign>();
|
||||
if (!MaterialValueIndex.FromKey(k, out var idx))
|
||||
if (!MaterialValueIndex.FromKey(k, out _))
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage($"Invalid material value key {k} for design {name}, skipped.",
|
||||
NotificationType.Warning);
|
||||
|
|
@ -429,7 +431,7 @@ public class DesignBase
|
|||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
design.ApplyParameters = 0;
|
||||
design.Application.Parameters = 0;
|
||||
design.GetDesignDataRef().Parameters = default;
|
||||
return;
|
||||
}
|
||||
|
|
@ -490,7 +492,7 @@ public class DesignBase
|
|||
return true;
|
||||
}
|
||||
|
||||
design.ApplyParameters &= ~flag;
|
||||
design.Application.Parameters &= ~flag;
|
||||
design.GetDesignDataRef().Parameters[flag] = CustomizeParameterValue.Zero;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -669,11 +671,12 @@ public class DesignBase
|
|||
{
|
||||
_designData = DesignBase64Migration.MigrateBase64(items, humans, base64, out var equipFlags, out var customizeFlags,
|
||||
out var writeProtected, out var applyMeta);
|
||||
ApplyEquip = equipFlags;
|
||||
ApplyCustomize = customizeFlags;
|
||||
ApplyParameters = 0;
|
||||
ApplyCrest = 0;
|
||||
ApplyMeta = applyMeta;
|
||||
Application.Equip = equipFlags;
|
||||
ApplyCustomize = customizeFlags;
|
||||
Application.Parameters = 0;
|
||||
Application.Crest = 0;
|
||||
Application.Meta = applyMeta;
|
||||
Application.BonusItem = 0;
|
||||
SetWriteProtected(writeProtected);
|
||||
CustomizeSet = SetCustomizationSet(customize);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ public class DesignColors : ISavable, IReadOnlyDictionary<string, uint>
|
|||
public static uint AutoColor(DesignBase design)
|
||||
{
|
||||
var customize = design.ApplyCustomizeExcludingBodyType == 0;
|
||||
var equip = design.ApplyEquip == 0;
|
||||
var equip = design.Application.Equip == 0;
|
||||
return (customize, equip) switch
|
||||
{
|
||||
(true, true) => ColorId.StateDesign.Value(),
|
||||
|
|
|
|||
|
|
@ -72,16 +72,11 @@ public class DesignConverter(
|
|||
? Design.LoadDesign(_customize, _items, _linkLoader, jObject)
|
||||
: DesignBase.LoadDesignBase(_customize, _items, jObject);
|
||||
|
||||
ret.SetApplyMeta(MetaIndex.Wetness, customize);
|
||||
if (!customize)
|
||||
ret.ApplyCustomize = 0;
|
||||
ret.Application.RemoveCustomize();
|
||||
|
||||
if (!equip)
|
||||
{
|
||||
ret.ApplyEquip = 0;
|
||||
ret.ApplyCrest = 0;
|
||||
ret.ApplyMeta &= ~(MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState);
|
||||
}
|
||||
ret.Application.RemoveEquip();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -155,16 +150,11 @@ public class DesignConverter(
|
|||
return null;
|
||||
}
|
||||
|
||||
ret.SetApplyMeta(MetaIndex.Wetness, customize);
|
||||
if (!customize)
|
||||
ret.ApplyCustomize = 0;
|
||||
ret.Application.RemoveCustomize();
|
||||
|
||||
if (!equip)
|
||||
{
|
||||
ret.ApplyEquip = 0;
|
||||
ret.ApplyCrest = 0;
|
||||
ret.ApplyMeta &= ~(MetaFlag.HatState | MetaFlag.WeaponState | MetaFlag.VisorState);
|
||||
}
|
||||
ret.Application.RemoveEquip();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,24 +9,31 @@ namespace Glamourer.Designs;
|
|||
|
||||
public unsafe struct DesignData
|
||||
{
|
||||
public const int EquipmentByteSize = 10 * CharacterArmor.Size;
|
||||
public const int NumEquipment = 10;
|
||||
public const int EquipmentByteSize = NumEquipment * CharacterArmor.Size;
|
||||
public const int NumBonusItems = 1;
|
||||
public const int NumWeapons = 2;
|
||||
|
||||
private string _nameHead = string.Empty;
|
||||
private string _nameBody = string.Empty;
|
||||
private string _nameHands = string.Empty;
|
||||
private string _nameLegs = string.Empty;
|
||||
private string _nameFeet = string.Empty;
|
||||
private string _nameEars = string.Empty;
|
||||
private string _nameNeck = string.Empty;
|
||||
private string _nameWrists = string.Empty;
|
||||
private string _nameRFinger = string.Empty;
|
||||
private string _nameLFinger = string.Empty;
|
||||
private string _nameMainhand = string.Empty;
|
||||
private string _nameOffhand = string.Empty;
|
||||
private string _nameFaceWear = string.Empty;
|
||||
private fixed uint _itemIds[12];
|
||||
private fixed uint _iconIds[12];
|
||||
private fixed byte _equipmentBytes[EquipmentByteSize + 16];
|
||||
private string _nameHead = string.Empty;
|
||||
private string _nameBody = string.Empty;
|
||||
private string _nameHands = string.Empty;
|
||||
private string _nameLegs = string.Empty;
|
||||
private string _nameFeet = string.Empty;
|
||||
private string _nameEars = string.Empty;
|
||||
private string _nameNeck = string.Empty;
|
||||
private string _nameWrists = string.Empty;
|
||||
private string _nameRFinger = string.Empty;
|
||||
private string _nameLFinger = string.Empty;
|
||||
private string _nameMainhand = string.Empty;
|
||||
private string _nameOffhand = string.Empty;
|
||||
private string _nameGlasses = string.Empty;
|
||||
|
||||
private fixed uint _itemIds[NumEquipment + NumWeapons];
|
||||
private fixed uint _iconIds[NumEquipment + NumWeapons + NumBonusItems];
|
||||
private fixed byte _equipmentBytes[EquipmentByteSize + NumWeapons * CharacterWeapon.Size];
|
||||
private fixed ushort _bonusIds[NumBonusItems];
|
||||
private fixed ushort _bonusModelIds[NumBonusItems];
|
||||
private fixed byte _bonusVariants[NumBonusItems];
|
||||
public CustomizeParameterData Parameters;
|
||||
public CustomizeArray Customize = CustomizeArray.Default;
|
||||
public uint ModelId;
|
||||
|
|
@ -52,7 +59,7 @@ public unsafe struct DesignData
|
|||
|| name.IsContained(_nameLFinger)
|
||||
|| name.IsContained(_nameMainhand)
|
||||
|| name.IsContained(_nameOffhand)
|
||||
|| name.IsContained(_nameFaceWear);
|
||||
|| name.IsContained(_nameGlasses);
|
||||
|
||||
public readonly StainIds Stain(EquipSlot slot)
|
||||
{
|
||||
|
|
@ -101,6 +108,15 @@ public unsafe struct DesignData
|
|||
}
|
||||
}
|
||||
|
||||
public readonly BonusItem BonusItem(BonusItemFlag slot)
|
||||
=> slot switch
|
||||
{
|
||||
// @formatter:off
|
||||
BonusItemFlag.Glasses => new BonusItem(_nameGlasses, _iconIds[12], _bonusIds[0], _bonusModelIds[0], _bonusVariants[0], BonusItemFlag.Glasses),
|
||||
_ => Penumbra.GameData.Structs.BonusItem.Empty(slot),
|
||||
// @formatter:on
|
||||
};
|
||||
|
||||
public readonly CharacterArmor Armor(EquipSlot slot)
|
||||
{
|
||||
fixed (byte* ptr = _equipmentBytes)
|
||||
|
|
@ -134,7 +150,7 @@ public unsafe struct DesignData
|
|||
public bool SetItem(EquipSlot slot, EquipItem item)
|
||||
{
|
||||
var index = slot.ToIndex();
|
||||
if (index > 11)
|
||||
if (index > NumEquipment + NumWeapons)
|
||||
return false;
|
||||
|
||||
_itemIds[index] = item.ItemId.Id;
|
||||
|
|
@ -173,6 +189,25 @@ public unsafe struct DesignData
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool SetBonusItem(BonusItemFlag slot, BonusItem item)
|
||||
{
|
||||
var index = slot.ToIndex();
|
||||
if (index > NumBonusItems)
|
||||
return false;
|
||||
|
||||
_iconIds[NumEquipment + NumWeapons + index] = item.Icon.Id;
|
||||
_bonusIds[index] = item.Id.Id;
|
||||
_bonusModelIds[index] = item.ModelId.Id;
|
||||
_bonusVariants[index] = item.Variant.Id;
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
_nameGlasses = item.Name;
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetStain(EquipSlot slot, StainIds stains)
|
||||
=> slot.ToIndex() switch
|
||||
{
|
||||
|
|
@ -313,17 +348,17 @@ public unsafe struct DesignData
|
|||
MemoryUtility.MemSet(ptr, 0, 10 * 2);
|
||||
}
|
||||
|
||||
_nameHead = string.Empty;
|
||||
_nameBody = string.Empty;
|
||||
_nameHands = string.Empty;
|
||||
_nameLegs = string.Empty;
|
||||
_nameFeet = string.Empty;
|
||||
_nameEars = string.Empty;
|
||||
_nameNeck = string.Empty;
|
||||
_nameWrists = string.Empty;
|
||||
_nameRFinger = string.Empty;
|
||||
_nameLFinger = string.Empty;
|
||||
_nameFaceWear = string.Empty;
|
||||
_nameHead = string.Empty;
|
||||
_nameBody = string.Empty;
|
||||
_nameHands = string.Empty;
|
||||
_nameLegs = string.Empty;
|
||||
_nameFeet = string.Empty;
|
||||
_nameEars = string.Empty;
|
||||
_nameNeck = string.Empty;
|
||||
_nameWrists = string.Empty;
|
||||
_nameRFinger = string.Empty;
|
||||
_nameLFinger = string.Empty;
|
||||
_nameGlasses = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -165,6 +165,11 @@ public class DesignEditor(
|
|||
}
|
||||
}
|
||||
|
||||
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ChangeStains(object data, EquipSlot slot, StainIds stains, ApplySettings _ = default)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -347,6 +347,18 @@ public sealed class DesignManager : DesignEditor
|
|||
DesignChanged.Invoke(DesignChanged.Type.ApplyEquip, design, slot);
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific equipment piece. </summary>
|
||||
public void ChangeApplyBonusItem(Design design, BonusItemFlag slot, bool value)
|
||||
{
|
||||
if (!design.SetApplyBonusItem(slot, value))
|
||||
return;
|
||||
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set applying of {slot} bonus item to {value}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.ApplyBonus, design, slot);
|
||||
}
|
||||
|
||||
/// <summary> Change whether to apply a specific stain. </summary>
|
||||
public void ChangeApplyStain(Design design, EquipSlot slot, bool value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ public interface IDesignEditor
|
|||
public void ChangeItem(object data, EquipSlot slot, EquipItem item, ApplySettings settings = default)
|
||||
=> ChangeEquip(data, slot, item, null, settings);
|
||||
|
||||
/// <summary> Change a bonus item. </summary>
|
||||
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default);
|
||||
|
||||
/// <summary> Change the stain for any equipment piece. </summary>
|
||||
public void ChangeStains(object data, EquipSlot slot, StainIds stains, ApplySettings settings = default)
|
||||
=> ChangeEquip(data, slot, null, stains, settings);
|
||||
|
|
|
|||
|
|
@ -42,14 +42,15 @@ public class DesignMerger(
|
|||
if (!data.IsHuman)
|
||||
continue;
|
||||
|
||||
var (equipFlags, customizeFlags, crestFlags, parameterFlags, applyMeta) = type.ApplyWhat(design);
|
||||
ReduceMeta(data, applyMeta, ret, source);
|
||||
ReduceCustomize(data, customizeFlags, ref fixFlags, ret, source, respectOwnership, startBodyType);
|
||||
ReduceEquip(data, equipFlags, ret, source, respectOwnership);
|
||||
ReduceMainhands(data, jobs, equipFlags, ret, source, respectOwnership);
|
||||
ReduceOffhands(data, jobs, equipFlags, ret, source, respectOwnership);
|
||||
ReduceCrests(data, crestFlags, ret, source);
|
||||
ReduceParameters(data, parameterFlags, ret, source);
|
||||
var collection = type.ApplyWhat(design);
|
||||
ReduceMeta(data, collection.Meta, ret, source);
|
||||
ReduceCustomize(data, collection.Customize, ref fixFlags, ret, source, respectOwnership, startBodyType);
|
||||
ReduceEquip(data, collection.Equip, ret, source, respectOwnership);
|
||||
ReduceBonusItems(data, collection.BonusItem, ret, source, respectOwnership);
|
||||
ReduceMainhands(data, jobs, collection.Equip, ret, source, respectOwnership);
|
||||
ReduceOffhands(data, jobs, collection.Equip, ret, source, respectOwnership);
|
||||
ReduceCrests(data, collection.Crest, ret, source);
|
||||
ReduceParameters(data, collection.Parameters, ret, source);
|
||||
ReduceMods(design as Design, ret, modAssociations);
|
||||
if (type.HasFlag(ApplicationType.GearCustomization))
|
||||
ReduceMaterials(design, ret);
|
||||
|
|
@ -83,7 +84,7 @@ public class DesignMerger(
|
|||
|
||||
private static void ReduceMeta(in DesignData design, MetaFlag applyMeta, MergedDesign ret, StateSource source)
|
||||
{
|
||||
applyMeta &= ~ret.Design.ApplyMeta;
|
||||
applyMeta &= ~ret.Design.Application.Meta;
|
||||
if (applyMeta == 0)
|
||||
return;
|
||||
|
||||
|
|
@ -100,7 +101,7 @@ public class DesignMerger(
|
|||
|
||||
private static void ReduceCrests(in DesignData design, CrestFlag crestFlags, MergedDesign ret, StateSource source)
|
||||
{
|
||||
crestFlags &= ~ret.Design.ApplyCrest;
|
||||
crestFlags &= ~ret.Design.Application.Crest;
|
||||
if (crestFlags == 0)
|
||||
return;
|
||||
|
||||
|
|
@ -118,7 +119,7 @@ public class DesignMerger(
|
|||
private static void ReduceParameters(in DesignData design, CustomizeParameterFlag parameterFlags, MergedDesign ret,
|
||||
StateSource source)
|
||||
{
|
||||
parameterFlags &= ~ret.Design.ApplyParameters;
|
||||
parameterFlags &= ~ret.Design.Application.Parameters;
|
||||
if (parameterFlags == 0)
|
||||
return;
|
||||
|
||||
|
|
@ -136,7 +137,7 @@ public class DesignMerger(
|
|||
private void ReduceEquip(in DesignData design, EquipFlag equipFlags, MergedDesign ret, StateSource source,
|
||||
bool respectOwnership)
|
||||
{
|
||||
equipFlags &= ~ret.Design.ApplyEquip;
|
||||
equipFlags &= ~ret.Design.Application.Equip;
|
||||
if (equipFlags == 0)
|
||||
return;
|
||||
|
||||
|
|
@ -174,6 +175,22 @@ public class DesignMerger(
|
|||
}
|
||||
}
|
||||
|
||||
private void ReduceBonusItems(in DesignData design, BonusItemFlag bonusItems, MergedDesign ret, StateSource source, bool respectOwnership)
|
||||
{
|
||||
bonusItems &= ~ret.Design.Application.BonusItem;
|
||||
if (bonusItems == 0)
|
||||
return;
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags.Where(b => bonusItems.HasFlag(b)))
|
||||
{
|
||||
var item = design.BonusItem(slot);
|
||||
if (!respectOwnership || true) // TODO: maybe check unlocks
|
||||
ret.Design.GetDesignDataRef().SetBonusItem(slot, item);
|
||||
ret.Design.SetApplyBonusItem(slot, true);
|
||||
ret.Sources[slot] = source;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReduceMainhands(in DesignData design, JobFlag allowedJobs, EquipFlag equipFlags, MergedDesign ret, StateSource source,
|
||||
bool respectOwnership)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,12 +64,8 @@ public sealed class MergedDesign
|
|||
{
|
||||
public MergedDesign(DesignManager designManager)
|
||||
{
|
||||
Design = designManager.CreateTemporary();
|
||||
Design.ApplyEquip = 0;
|
||||
Design.ApplyCustomize = 0;
|
||||
Design.ApplyCrest = 0;
|
||||
Design.ApplyParameters = 0;
|
||||
Design.ApplyMeta = 0;
|
||||
Design = designManager.CreateTemporary();
|
||||
Design.Application = ApplicationCollection.None;
|
||||
}
|
||||
|
||||
public MergedDesign(DesignBase design)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Glamourer.Events;
|
|||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class BonusSlotUpdating()
|
||||
: EventWrapperRef34<Model, BonusEquipFlag, CharacterArmor, ulong, BonusSlotUpdating.Priority>(nameof(BonusSlotUpdating))
|
||||
: EventWrapperRef34<Model, BonusItemFlag, CharacterArmor, ulong, BonusSlotUpdating.Priority>(nameof(BonusSlotUpdating))
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ public sealed class DesignChanged()
|
|||
/// <summary> An existing design changed whether a specific equipment piece is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||
ApplyEquip,
|
||||
|
||||
/// <summary> An existing design changed whether a specific bonus item is applied. Data is the slot of the item [BonusItemFlag]. </summary>
|
||||
ApplyBonus,
|
||||
|
||||
/// <summary> An existing design changed whether a specific stain is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||
ApplyStain,
|
||||
|
||||
|
|
|
|||
|
|
@ -179,8 +179,7 @@ public sealed class DesignQuickBar : Window, IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
using var _ = design!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
|
||||
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks);
|
||||
}
|
||||
|
||||
|
|
|
|||
57
Glamourer/Gui/Equipment/BonusDrawData.cs
Normal file
57
Glamourer/Gui/Equipment/BonusDrawData.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using Glamourer.Designs;
|
||||
using Glamourer.State;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Equipment;
|
||||
|
||||
public struct BonusDrawData(BonusItemFlag slot, in DesignData designData)
|
||||
{
|
||||
private IDesignEditor _editor;
|
||||
private object _object;
|
||||
public readonly BonusItemFlag Slot = slot;
|
||||
public bool Locked;
|
||||
public bool DisplayApplication;
|
||||
public bool AllowRevert;
|
||||
|
||||
public readonly bool IsDesign
|
||||
=> _object is Design;
|
||||
|
||||
public readonly bool IsState
|
||||
=> _object is ActorState;
|
||||
|
||||
public readonly void SetItem(BonusItem item)
|
||||
=> _editor.ChangeBonusItem(_object, Slot, item, ApplySettings.Manual);
|
||||
|
||||
public readonly void SetApplyItem(bool value)
|
||||
{
|
||||
var manager = (DesignManager)_editor;
|
||||
var design = (Design)_object;
|
||||
manager.ChangeApplyBonusItem(design, Slot, value);
|
||||
}
|
||||
|
||||
public BonusItem CurrentItem = designData.BonusItem(slot);
|
||||
public BonusItem GameItem = default;
|
||||
public bool CurrentApply;
|
||||
|
||||
public static BonusDrawData FromDesign(DesignManager manager, Design design, BonusItemFlag slot)
|
||||
=> new(slot, design.DesignData)
|
||||
{
|
||||
_editor = manager,
|
||||
_object = design,
|
||||
CurrentApply = design.DoApplyBonusItem(slot),
|
||||
Locked = design.WriteProtected(),
|
||||
DisplayApplication = true,
|
||||
};
|
||||
|
||||
public static BonusDrawData FromState(StateManager manager, ActorState state, BonusItemFlag slot)
|
||||
=> new(slot, state.ModelData)
|
||||
{
|
||||
_editor = manager,
|
||||
_object = state,
|
||||
Locked = state.IsLocked,
|
||||
DisplayApplication = false,
|
||||
GameItem = state.BaseData.BonusItem(slot),
|
||||
AllowRevert = true,
|
||||
};
|
||||
}
|
||||
123
Glamourer/Gui/Equipment/BonusItemCombo.cs
Normal file
123
Glamourer/Gui/Equipment/BonusItemCombo.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Equipment;
|
||||
|
||||
public sealed class BonusItemCombo : FilterComboCache<BonusItem>
|
||||
{
|
||||
private readonly FavoriteManager _favorites;
|
||||
public readonly string Label;
|
||||
private BonusItemId _currentItem;
|
||||
private float _innerWidth;
|
||||
|
||||
public PrimaryId CustomSetId { get; private set; }
|
||||
public Variant CustomVariant { get; private set; }
|
||||
|
||||
public BonusItemCombo(IDataManager gameData, ItemManager items, BonusItemFlag slot, Logger log, FavoriteManager favorites)
|
||||
: base(() => GetItems(favorites, items, slot), MouseWheelType.Control, log)
|
||||
{
|
||||
_favorites = favorites;
|
||||
Label = GetLabel(gameData, slot);
|
||||
_currentItem = 0;
|
||||
SearchByParts = true;
|
||||
}
|
||||
|
||||
protected override void DrawList(float width, float itemHeight)
|
||||
{
|
||||
base.DrawList(width, itemHeight);
|
||||
if (NewSelection != null && Items.Count > NewSelection.Value)
|
||||
CurrentSelection = Items[NewSelection.Value];
|
||||
}
|
||||
|
||||
protected override int UpdateCurrentSelected(int currentSelected)
|
||||
{
|
||||
if (CurrentSelection.Id == _currentItem)
|
||||
return currentSelected;
|
||||
|
||||
CurrentSelectionIdx = Items.IndexOf(i => i.Id == _currentItem);
|
||||
CurrentSelection = CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : default;
|
||||
return base.UpdateCurrentSelected(CurrentSelectionIdx);
|
||||
}
|
||||
|
||||
public bool Draw(string previewName, BonusItemId previewIdx, float width, float innerWidth)
|
||||
{
|
||||
_innerWidth = innerWidth;
|
||||
_currentItem = previewIdx;
|
||||
CustomVariant = 0;
|
||||
return Draw($"##{Label}", previewName, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
|
||||
}
|
||||
|
||||
protected override float GetFilterWidth()
|
||||
=> _innerWidth - 2 * ImGui.GetStyle().FramePadding.X;
|
||||
|
||||
protected override bool DrawSelectable(int globalIdx, bool selected)
|
||||
{
|
||||
var obj = Items[globalIdx];
|
||||
var name = ToString(obj);
|
||||
if (UiHelpers.DrawFavoriteStar(_favorites, obj) && CurrentSelectionIdx == globalIdx)
|
||||
{
|
||||
CurrentSelectionIdx = -1;
|
||||
_currentItem = obj.Id;
|
||||
CurrentSelection = default;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
var ret = ImGui.Selectable(name, selected);
|
||||
ImGui.SameLine();
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080);
|
||||
ImGuiUtil.RightAlign($"({obj.ModelId.Id}-{obj.Variant.Id})");
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override bool IsVisible(int globalIndex, LowerString filter)
|
||||
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelId.Id.ToString());
|
||||
|
||||
protected override string ToString(BonusItem obj)
|
||||
=> obj.Name;
|
||||
|
||||
private static string GetLabel(IDataManager gameData, BonusItemFlag slot)
|
||||
{
|
||||
var sheet = gameData.GetExcelSheet<Addon>()!;
|
||||
|
||||
return slot switch
|
||||
{
|
||||
BonusItemFlag.Glasses => sheet.GetRow(16050)?.Text.ToString() ?? "Facewear",
|
||||
BonusItemFlag.UnkSlot => sheet.GetRow(16051)?.Text.ToString() ?? "Facewear",
|
||||
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
private static List<BonusItem> GetItems(FavoriteManager favorites, ItemManager items, BonusItemFlag slot)
|
||||
{
|
||||
var nothing = BonusItem.Empty(slot);
|
||||
if (slot is not BonusItemFlag.Glasses)
|
||||
return [nothing];
|
||||
|
||||
return items.DictBonusItems.Values.OrderByDescending(favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing).ToList();
|
||||
}
|
||||
|
||||
protected override void OnClosePopup()
|
||||
{
|
||||
// If holding control while the popup closes, try to parse the input as a full pair of set id and variant, and set a custom item for that.
|
||||
if (!ImGui.GetIO().KeyCtrl)
|
||||
return;
|
||||
|
||||
var split = Filter.Text.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
if (split.Length != 2 || !ushort.TryParse(split[0], out var setId) || !byte.TryParse(split[1], out var variant))
|
||||
return;
|
||||
|
||||
CustomSetId = setId;
|
||||
CustomVariant = variant;
|
||||
}
|
||||
}
|
||||
|
|
@ -72,4 +72,4 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
|
|||
GameStains = state.BaseData.Stain(slot),
|
||||
AllowRevert = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ public class EquipmentDrawer
|
|||
private readonly GlamourerColorCombo _stainCombo;
|
||||
private readonly DictStain _stainData;
|
||||
private readonly ItemCombo[] _itemCombo;
|
||||
private readonly BonusItemCombo[] _bonusItemCombo;
|
||||
private readonly Dictionary<FullEquipType, WeaponCombo> _weaponCombo;
|
||||
private readonly CodeService _codes;
|
||||
private readonly TextureService _textures;
|
||||
|
|
@ -37,16 +38,17 @@ public class EquipmentDrawer
|
|||
public EquipmentDrawer(FavoriteManager favorites, IDataManager gameData, ItemManager items, CodeService codes, TextureService textures,
|
||||
Configuration config, GPoseService gPose, AdvancedDyePopup advancedDyes)
|
||||
{
|
||||
_items = items;
|
||||
_codes = codes;
|
||||
_textures = textures;
|
||||
_config = config;
|
||||
_gPose = gPose;
|
||||
_advancedDyes = advancedDyes;
|
||||
_stainData = items.Stains;
|
||||
_stainCombo = new GlamourerColorCombo(DefaultWidth - 20, _stainData, favorites);
|
||||
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray();
|
||||
_weaponCombo = new Dictionary<FullEquipType, WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2);
|
||||
_items = items;
|
||||
_codes = codes;
|
||||
_textures = textures;
|
||||
_config = config;
|
||||
_gPose = gPose;
|
||||
_advancedDyes = advancedDyes;
|
||||
_stainData = items.Stains;
|
||||
_stainCombo = new GlamourerColorCombo(DefaultWidth - 20, _stainData, favorites);
|
||||
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, Glamourer.Log, favorites)).ToArray();
|
||||
_bonusItemCombo = BonusExtensions.AllFlags.Select(f => new BonusItemCombo(gameData, items, f, Glamourer.Log, favorites)).ToArray();
|
||||
_weaponCombo = new Dictionary<FullEquipType, WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2);
|
||||
foreach (var type in Enum.GetValues<FullEquipType>())
|
||||
{
|
||||
if (type.ToSlot() is EquipSlot.MainHand)
|
||||
|
|
@ -100,6 +102,21 @@ public class EquipmentDrawer
|
|||
DrawEquipNormal(equipDrawData);
|
||||
}
|
||||
|
||||
public void DrawBonusItem(BonusDrawData bonusDrawData)
|
||||
{
|
||||
if (_config.HideApplyCheckmarks)
|
||||
bonusDrawData.DisplayApplication = false;
|
||||
|
||||
using var id = ImRaii.PushId(100 + (int)bonusDrawData.Slot);
|
||||
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
|
||||
|
||||
if (_config.SmallEquip)
|
||||
DrawBonusItemSmall(bonusDrawData);
|
||||
else
|
||||
DrawBonusItemNormal(bonusDrawData);
|
||||
}
|
||||
|
||||
public void DrawWeapons(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons)
|
||||
{
|
||||
if (mainhand.CurrentItem.PrimaryId.Id == 0)
|
||||
|
|
@ -302,6 +319,25 @@ public class EquipmentDrawer
|
|||
ImGui.TextUnformatted(label);
|
||||
}
|
||||
|
||||
private void DrawBonusItemSmall(in BonusDrawData bonusDrawData)
|
||||
{
|
||||
ImGui.Dummy(new Vector2(StainId.NumStains * ImUtf8.FrameHeight + (StainId.NumStains - 1) * ImUtf8.ItemSpacing.X, ImUtf8.FrameHeight));
|
||||
ImGui.SameLine();
|
||||
DrawBonusItem(bonusDrawData, out var label, true, false, false);
|
||||
if (bonusDrawData.DisplayApplication)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
DrawApply(bonusDrawData);
|
||||
}
|
||||
else if (bonusDrawData.IsState)
|
||||
{
|
||||
_advancedDyes.DrawButton(bonusDrawData.Slot);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(label);
|
||||
}
|
||||
|
||||
private void DrawWeaponsSmall(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons)
|
||||
{
|
||||
DrawStain(mainhand, true);
|
||||
|
|
@ -382,6 +418,27 @@ public class EquipmentDrawer
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawBonusItemNormal(in BonusDrawData bonusDrawData)
|
||||
{
|
||||
ImGui.Dummy(_iconSize with { Y = ImUtf8.FrameHeight });
|
||||
var right = ImGui.IsItemClicked(ImGuiMouseButton.Right);
|
||||
var left = ImGui.IsItemClicked(ImGuiMouseButton.Left);
|
||||
ImGui.SameLine();
|
||||
DrawBonusItem(bonusDrawData, out var label, false, right, left);
|
||||
if (bonusDrawData.DisplayApplication)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
DrawApply(bonusDrawData);
|
||||
}
|
||||
else if (bonusDrawData.IsState)
|
||||
{
|
||||
_advancedDyes.DrawButton(bonusDrawData.Slot);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(label);
|
||||
}
|
||||
|
||||
private void DrawWeaponsNormal(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons)
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
||||
|
|
@ -491,6 +548,25 @@ public class EquipmentDrawer
|
|||
data.SetItem(item);
|
||||
}
|
||||
|
||||
private void DrawBonusItem(in BonusDrawData data, out string label, bool small, bool clear, bool open)
|
||||
{
|
||||
var combo = _bonusItemCombo[data.Slot.ToIndex()];
|
||||
label = combo.Label;
|
||||
if (!data.Locked && open)
|
||||
UiHelpers.OpenCombo($"##{combo.Label}");
|
||||
|
||||
using var disabled = ImRaii.Disabled(data.Locked);
|
||||
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
|
||||
_requiredComboWidth);
|
||||
if (change)
|
||||
data.SetItem(combo.CurrentSelection);
|
||||
else if (combo.CustomVariant.Id > 0)
|
||||
data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant));
|
||||
|
||||
if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, BonusItem.Empty(data.Slot), out var item))
|
||||
data.SetItem(item);
|
||||
}
|
||||
|
||||
private static bool ResetOrClear<T>(bool locked, bool clicked, bool allowRevert, bool allowClear,
|
||||
in T currentItem, in T revertItem, in T clearItem, out T? item) where T : IEquatable<T>
|
||||
{
|
||||
|
|
@ -590,6 +666,13 @@ public class EquipmentDrawer
|
|||
data.SetApplyItem(enabled);
|
||||
}
|
||||
|
||||
private static void DrawApply(in BonusDrawData data)
|
||||
{
|
||||
if (UiHelpers.DrawCheckbox($"##apply{data.Slot}", "Apply this bonus item when applying the Design.", data.CurrentApply, out var enabled,
|
||||
data.Locked))
|
||||
data.SetApplyItem(enabled);
|
||||
}
|
||||
|
||||
private static void DrawApplyStain(in EquipDrawData data)
|
||||
{
|
||||
if (UiHelpers.DrawCheckbox($"##applyStain{data.Slot}", "Apply this dye to the item when applying the Design.", data.CurrentApplyStain,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ public sealed unsafe class AdvancedDyePopup(
|
|||
public void DrawButton(EquipSlot slot)
|
||||
=> DrawButton(MaterialValueIndex.FromSlot(slot));
|
||||
|
||||
public void DrawButton(BonusItemFlag slot)
|
||||
=> DrawButton(MaterialValueIndex.FromSlot(slot));
|
||||
|
||||
private void DrawButton(MaterialValueIndex index)
|
||||
{
|
||||
if (!config.UseAdvancedDyes)
|
||||
|
|
|
|||
|
|
@ -215,6 +215,12 @@ public class ActorPanel
|
|||
var offhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.OffHand);
|
||||
_equipmentDrawer.DrawWeapons(mainhand, offhand, GameMain.IsInGPose());
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var data = BonusDrawData.FromState(_stateManager, _state!, slot);
|
||||
_equipmentDrawer.DrawBonusItem(data);
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
DrawEquipmentMetaToggles();
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
|
|
|
|||
|
|
@ -277,13 +277,13 @@ public class SetPanel(
|
|||
var size = new Vector2(ImGui.GetFrameHeight());
|
||||
size.X += ImGuiHelpers.GlobalScale;
|
||||
|
||||
var (equipFlags, customizeFlags, _, _, _) = design.ApplyWhat();
|
||||
var collection = design.ApplyWhat();
|
||||
var sb = new StringBuilder();
|
||||
var designData = design.Design.GetDesignData(default);
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand))
|
||||
{
|
||||
var flag = slot.ToFlag();
|
||||
if (!equipFlags.HasFlag(flag))
|
||||
if (!collection.Equip.HasFlag(flag))
|
||||
continue;
|
||||
|
||||
var item = designData.Item(slot);
|
||||
|
|
@ -308,7 +308,7 @@ public class SetPanel(
|
|||
foreach (var type in CustomizationExtensions.All)
|
||||
{
|
||||
var flag = type.ToFlag();
|
||||
if (!customizeFlags.HasFlag(flag))
|
||||
if (!collection.Customize.HasFlag(flag))
|
||||
continue;
|
||||
|
||||
if (flag.RequiresRedraw())
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class DesignManagerPanel(DesignManager _designManager, DesignFileSystem _
|
|||
continue;
|
||||
|
||||
DrawDesign(design, _designFileSystem);
|
||||
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.ApplyEquip, design.ApplyCustomizeRaw, design.ApplyMeta,
|
||||
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.Application.Equip, design.Application.Customize, design.Application.Meta,
|
||||
design.WriteProtected());
|
||||
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
|
||||
ImGuiUtil.TextWrapped(base64);
|
||||
|
|
|
|||
|
|
@ -112,11 +112,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
|
|||
public DesignBase CreateDesign(in MirageManager.GlamourPlate plate)
|
||||
{
|
||||
var design = _design.CreateTemporary();
|
||||
design.ApplyCustomize = 0;
|
||||
design.ApplyCrest = 0;
|
||||
design.ApplyMeta = 0;
|
||||
design.ApplyParameters = 0;
|
||||
design.ApplyEquip = 0;
|
||||
design.Application = ApplicationCollection.None;
|
||||
foreach (var (slot, index) in EquipSlotExtensions.FullSlots.WithIndex())
|
||||
{
|
||||
var itemId = plate.ItemIds[index];
|
||||
|
|
@ -129,7 +125,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
|
|||
|
||||
design.GetDesignDataRef().SetItem(slot, item);
|
||||
design.GetDesignDataRef().SetStain(slot, StainIds.FromGlamourPlate(plate, index));
|
||||
design.ApplyEquip |= slot.ToBothFlags();
|
||||
design.Application.Equip |= slot.ToBothFlags();
|
||||
}
|
||||
|
||||
return design;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public unsafe class ModelEvaluationPanel(
|
|||
UpdateSlotService _updateSlotService,
|
||||
ChangeCustomizeService _changeCustomizeService,
|
||||
CrestService _crestService,
|
||||
DictGlasses _glasses) : IGameDataDrawer
|
||||
DictBonusItems bonusItems) : IGameDataDrawer
|
||||
{
|
||||
public string Label
|
||||
=> "Model Evaluation";
|
||||
|
|
@ -57,6 +57,16 @@ public unsafe class ModelEvaluationPanel(
|
|||
ImGui.TextUnformatted($"Transformation Id: {actor.AsCharacter->CharacterData.TransformationId}");
|
||||
if (actor.AsCharacter->CharacterData.ModelCharaId_2 != -1)
|
||||
ImGui.TextUnformatted($"ModelChara2 {actor.AsCharacter->CharacterData.ModelCharaId_2}");
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Character Mode");
|
||||
ImGuiUtil.DrawTableColumn($"{actor.AsCharacter->Mode}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Animation");
|
||||
ImGuiUtil.DrawTableColumn($"{((ushort*)&actor.AsCharacter->Timeline)[0x78]}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Mainhand");
|
||||
|
|
@ -226,7 +236,7 @@ public unsafe class ModelEvaluationPanel(
|
|||
_updateSlotService.UpdateEquipSlot(model, slot, actor.GetArmor(slot));
|
||||
}
|
||||
|
||||
foreach (var slot in BonusSlotExtensions.AllFlags)
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
using var id2 = ImRaii.PushId((int)slot.ToModelIndex());
|
||||
ImGuiUtil.DrawTableColumn(slot.ToName());
|
||||
|
|
@ -236,9 +246,9 @@ public unsafe class ModelEvaluationPanel(
|
|||
}
|
||||
else
|
||||
{
|
||||
var glassesId = actor.GetBonusSlot(slot);
|
||||
if (_glasses.TryGetValue(glassesId, out var glasses))
|
||||
ImGuiUtil.DrawTableColumn($"{glasses.Id.Id},{glasses.Variant.Id} ({glassesId})");
|
||||
var glassesId = actor.GetBonusItem(slot);
|
||||
if (bonusItems.TryGetValue(glassesId, out var glasses))
|
||||
ImGuiUtil.DrawTableColumn($"{glasses.ModelId.Id},{glasses.Variant.Id} ({glassesId})");
|
||||
else
|
||||
ImGuiUtil.DrawTableColumn($"{glassesId}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,13 @@ public class DesignPanel
|
|||
var mainhand = EquipDrawData.FromDesign(_manager, _selector.Selected!, EquipSlot.MainHand);
|
||||
var offhand = EquipDrawData.FromDesign(_manager, _selector.Selected!, EquipSlot.OffHand);
|
||||
_equipmentDrawer.DrawWeapons(mainhand, offhand, true);
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var data = BonusDrawData.FromDesign(_manager, _selector.Selected!, slot);
|
||||
_equipmentDrawer.DrawBonusItem(data);
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
DrawEquipmentMetaToggles();
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
|
|
@ -149,7 +156,7 @@ public class DesignPanel
|
|||
if (!h)
|
||||
return;
|
||||
|
||||
if (_customizationDrawer.Draw(_selector.Selected!.DesignData.Customize, _selector.Selected.ApplyCustomizeRaw,
|
||||
if (_customizationDrawer.Draw(_selector.Selected!.DesignData.Customize, _selector.Selected.Application.Customize,
|
||||
_selector.Selected!.WriteProtected(), false))
|
||||
foreach (var idx in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
|
|
@ -224,7 +231,7 @@ public class DesignPanel
|
|||
private void DrawCrestApplication()
|
||||
{
|
||||
using var id = ImRaii.PushId("Crests");
|
||||
var flags = (uint)_selector.Selected!.ApplyCrest;
|
||||
var flags = (uint)_selector.Selected!.Application.Crest;
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Crests", ref flags, (uint)CrestExtensions.AllRelevant);
|
||||
foreach (var flag in CrestExtensions.AllRelevantSet)
|
||||
{
|
||||
|
|
@ -255,7 +262,7 @@ public class DesignPanel
|
|||
{
|
||||
void ApplyEquip(string label, EquipFlag allFlags, bool stain, IEnumerable<EquipSlot> slots)
|
||||
{
|
||||
var flags = (uint)(allFlags & _selector.Selected!.ApplyEquip);
|
||||
var flags = (uint)(allFlags & _selector.Selected!.Application.Equip);
|
||||
using var id = ImRaii.PushId(label);
|
||||
var bigChange = ImGui.CheckboxFlags($"Apply All {label}", ref flags, (uint)allFlags);
|
||||
if (stain)
|
||||
|
|
@ -302,7 +309,7 @@ public class DesignPanel
|
|||
{
|
||||
using var id = ImRaii.PushId("Meta");
|
||||
const uint all = (uint)MetaExtensions.All;
|
||||
var flags = (uint)_selector.Selected!.ApplyMeta;
|
||||
var flags = (uint)_selector.Selected!.Application.Meta;
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Meta Changes", ref flags, all);
|
||||
|
||||
var labels = new[]
|
||||
|
|
@ -324,7 +331,7 @@ public class DesignPanel
|
|||
private void DrawParameterApplication()
|
||||
{
|
||||
using var id = ImRaii.PushId("Parameter");
|
||||
var flags = (uint)_selector.Selected!.ApplyParameters;
|
||||
var flags = (uint)_selector.Selected!.Application.Parameters;
|
||||
var bigChange = ImGui.CheckboxFlags("Apply All Customize Parameters", ref flags, (uint)CustomizeParameterExtensions.All);
|
||||
foreach (var flag in CustomizeParameterExtensions.AllFlags)
|
||||
{
|
||||
|
|
@ -408,8 +415,7 @@ public class DesignPanel
|
|||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
|
||||
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks);
|
||||
}
|
||||
}
|
||||
|
|
@ -427,8 +433,7 @@ public class DesignPanel
|
|||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest, applyParameters) = UiHelpers.ConvertKeysToFlags();
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(applyGear, applyCustomize, applyCrest, applyParameters);
|
||||
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
|
||||
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
|
|
@ -98,15 +97,6 @@ public static class UiHelpers
|
|||
return (currentValue != newValue, currentApply != newApply);
|
||||
}
|
||||
|
||||
public static (EquipFlag, CustomizeFlag, CrestFlag, CustomizeParameterFlag) ConvertKeysToFlags()
|
||||
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
|
||||
{
|
||||
(false, false) => (EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All, CustomizeParameterExtensions.All),
|
||||
(true, true) => (EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant, CrestExtensions.All, CustomizeParameterExtensions.All),
|
||||
(true, false) => (EquipFlagExtensions.All, (CustomizeFlag)0, CrestExtensions.All, 0),
|
||||
(false, true) => ((EquipFlag)0, CustomizeFlagExtensions.AllRelevant, 0, CustomizeParameterExtensions.All),
|
||||
};
|
||||
|
||||
public static (bool, bool) ConvertKeysToBool()
|
||||
=> (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift) switch
|
||||
{
|
||||
|
|
@ -126,16 +116,36 @@ public static class UiHelpers
|
|||
using var c = ImRaii.PushColor(ImGuiCol.Text,
|
||||
hovering ? ColorId.FavoriteStarHovered.Value() : favorite ? ColorId.FavoriteStarOn.Value() : ColorId.FavoriteStarOff.Value());
|
||||
ImGui.TextUnformatted(FontAwesomeIcon.Star.ToIconString());
|
||||
if (ImGui.IsItemClicked())
|
||||
{
|
||||
if (favorite)
|
||||
favorites.Remove(item);
|
||||
else
|
||||
favorites.TryAdd(item);
|
||||
return true;
|
||||
}
|
||||
if (!ImGui.IsItemClicked())
|
||||
return false;
|
||||
|
||||
if (favorite)
|
||||
favorites.Remove(item);
|
||||
else
|
||||
favorites.TryAdd(item);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static bool DrawFavoriteStar(FavoriteManager favorites, BonusItem item)
|
||||
{
|
||||
var favorite = favorites.Contains(item);
|
||||
var hovering = ImGui.IsMouseHoveringRect(ImGui.GetCursorScreenPos(),
|
||||
ImGui.GetCursorScreenPos() + new Vector2(ImGui.GetTextLineHeight()));
|
||||
|
||||
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||
using var c = ImRaii.PushColor(ImGuiCol.Text,
|
||||
hovering ? ColorId.FavoriteStarHovered.Value() : favorite ? ColorId.FavoriteStarOn.Value() : ColorId.FavoriteStarOff.Value());
|
||||
ImGui.TextUnformatted(FontAwesomeIcon.Star.ToIconString());
|
||||
if (!ImGui.IsItemClicked())
|
||||
return false;
|
||||
|
||||
if (favorite)
|
||||
favorites.Remove(item);
|
||||
else
|
||||
favorites.TryAdd(item);
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool DrawFavoriteStar(FavoriteManager favorites, StainId stain)
|
||||
|
|
@ -149,15 +159,14 @@ public static class UiHelpers
|
|||
hovering ? ColorId.FavoriteStarHovered.Value() : favorite ? ColorId.FavoriteStarOn.Value() : ColorId.FavoriteStarOff.Value());
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(FontAwesomeIcon.Star.ToIconString());
|
||||
if (ImGui.IsItemClicked())
|
||||
{
|
||||
if (favorite)
|
||||
favorites.Remove(stain);
|
||||
else
|
||||
favorites.TryAdd(stain);
|
||||
return true;
|
||||
}
|
||||
if (!ImGui.IsItemClicked())
|
||||
return false;
|
||||
|
||||
if (favorite)
|
||||
favorites.Remove(stain);
|
||||
else
|
||||
favorites.TryAdd(stain);
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,12 @@ public readonly record struct MaterialValueIndex(
|
|||
return Invalid;
|
||||
}
|
||||
|
||||
public static MaterialValueIndex FromSlot(BonusItemFlag slot)
|
||||
{
|
||||
var idx = slot.ToIndex();
|
||||
return idx > 2 ? Invalid : new MaterialValueIndex(DrawObjectType.Human, (byte)(idx + 16), 0, 0);
|
||||
}
|
||||
|
||||
public EquipSlot ToEquipSlot()
|
||||
=> DrawObject switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,17 +37,13 @@ public class PaletteImport(IDalamudPluginInterface pluginInterface, DesignManage
|
|||
}
|
||||
|
||||
var design = designManager.CreateEmpty(fullPath, true);
|
||||
design.ApplyCustomize = 0;
|
||||
design.ApplyEquip = 0;
|
||||
design.ApplyCrest = 0;
|
||||
designManager.ChangeApplyMeta(design, MetaIndex.VisorState, false);
|
||||
designManager.ChangeApplyMeta(design, MetaIndex.HatState, false);
|
||||
designManager.ChangeApplyMeta(design, MetaIndex.WeaponState, false);
|
||||
design.Application = ApplicationCollection.None;
|
||||
foreach (var flag in flags.Iterate())
|
||||
{
|
||||
designManager.ChangeApplyParameter(design, flag, true);
|
||||
designManager.ChangeCustomizeParameter(design, flag, palette[flag]);
|
||||
}
|
||||
|
||||
Glamourer.Log.Information($"Added design for palette {name} at {fullPath}.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
{
|
||||
public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
|
||||
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
|
||||
private readonly DictGlasses _glasses;
|
||||
private readonly DictBonusItems _bonusItems;
|
||||
|
||||
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, IGameInteropProvider interop,
|
||||
DictGlasses glasses)
|
||||
DictBonusItems bonusItems)
|
||||
{
|
||||
EquipSlotUpdatingEvent = equipSlotUpdating;
|
||||
BonusSlotUpdatingEvent = bonusSlotUpdating;
|
||||
_glasses = glasses;
|
||||
_bonusItems = bonusItems;
|
||||
interop.InitializeFromAttributes(this);
|
||||
_flagSlotForUpdateHook.Enable();
|
||||
_flagBonusSlotForUpdateHook.Enable();
|
||||
|
|
@ -41,7 +41,7 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
FlagSlotForUpdateInterop(drawObject, slot, data);
|
||||
}
|
||||
|
||||
public void UpdateBonusSlot(Model drawObject, BonusEquipFlag slot, CharacterArmor data)
|
||||
public void UpdateBonusSlot(Model drawObject, BonusItemFlag slot, CharacterArmor data)
|
||||
{
|
||||
if (!drawObject.IsCharacterBase)
|
||||
return;
|
||||
|
|
@ -53,13 +53,13 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
_flagBonusSlotForUpdateHook.Original(drawObject.Address, index, &data);
|
||||
}
|
||||
|
||||
public void UpdateGlasses(Model drawObject, GlassesId id)
|
||||
public void UpdateGlasses(Model drawObject, BonusItemId id)
|
||||
{
|
||||
if (!_glasses.TryGetValue(id, out var glasses))
|
||||
if (!_bonusItems.TryGetValue(id, out var glasses))
|
||||
return;
|
||||
|
||||
var armor = new CharacterArmor(glasses.Id, glasses.Variant, StainIds.None);
|
||||
_flagBonusSlotForUpdateHook.Original(drawObject.Address, BonusEquipFlag.Glasses.ToIndex(), &armor);
|
||||
var armor = new CharacterArmor(glasses.ModelId, glasses.Variant, StainIds.None);
|
||||
_flagBonusSlotForUpdateHook.Original(drawObject.Address, BonusItemFlag.Glasses.ToIndex(), &armor);
|
||||
}
|
||||
|
||||
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor, StainIds stains)
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@ public class ItemManager
|
|||
public readonly ExcelSheet<Lumina.Excel.GeneratedSheets.Item> ItemSheet;
|
||||
public readonly DictStain Stains;
|
||||
public readonly ItemData ItemData;
|
||||
public readonly DictBonusItems DictBonusItems;
|
||||
public readonly RestrictedGear RestrictedGear;
|
||||
|
||||
public readonly EquipItem DefaultSword;
|
||||
|
||||
public ItemManager(Configuration config, IDataManager gameData, ObjectIdentification objectIdentification,
|
||||
ItemData itemData, DictStain stains, RestrictedGear restrictedGear)
|
||||
ItemData itemData, DictStain stains, RestrictedGear restrictedGear, DictBonusItems dictBonusItems)
|
||||
{
|
||||
_config = config;
|
||||
ItemSheet = gameData.GetExcelSheet<Lumina.Excel.GeneratedSheets.Item>()!;
|
||||
|
|
@ -33,6 +34,7 @@ public class ItemManager
|
|||
ItemData = itemData;
|
||||
Stains = stains;
|
||||
RestrictedGear = restrictedGear;
|
||||
DictBonusItems = dictBonusItems;
|
||||
DefaultSword = EquipItem.FromMainhand(ItemSheet.GetRow(1601)!); // Weathered Shortsword
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +126,22 @@ public class ItemManager
|
|||
}
|
||||
}
|
||||
|
||||
public BonusItem Identify(BonusItemFlag slot, PrimaryId id, Variant variant)
|
||||
{
|
||||
var index = slot.ToIndex();
|
||||
if (index == uint.MaxValue)
|
||||
return new BonusItem($"Invalid ({id.Id}-{variant})", 0, 0, id, variant, slot);
|
||||
|
||||
if (id.Id == 0)
|
||||
return BonusItem.Empty(slot);
|
||||
|
||||
return ObjectIdentification.Identify(id, variant, slot)
|
||||
.FirstOrDefault(new BonusItem($"Invalid ({id.Id}-{variant})", 0, 0, id, variant, slot));
|
||||
}
|
||||
|
||||
public BonusItem Resolve(BonusItemFlag slot, BonusItemId id)
|
||||
=> IsBonusItemValid(slot, id, out var item) ? item : new BonusItem($"Invalid ({id.Id})", 0, id, 0, 0, slot);
|
||||
|
||||
/// <summary> Return the default offhand for a given mainhand, that is for both handed weapons, return the correct offhand part, and for everything else Nothing. </summary>
|
||||
public EquipItem GetDefaultOffhand(EquipItem mainhand)
|
||||
{
|
||||
|
|
@ -161,6 +179,18 @@ public class ItemManager
|
|||
return item.Valid;
|
||||
}
|
||||
|
||||
/// <summary> Returns whether a bonus item id represents a valid item for a slot and gives the item. </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public bool IsBonusItemValid(BonusItemFlag slot, BonusItemId itemId, out BonusItem item)
|
||||
{
|
||||
if (itemId.Id != 0)
|
||||
return DictBonusItems.TryGetValue(itemId, out item) && slot == item.Slot;
|
||||
|
||||
item = BonusItem.Empty(slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check whether an item id resolves to an existing item of the correct slot (which should not be weapons.)
|
||||
/// The returned item is either the resolved correct item, or the Nothing item for that slot.
|
||||
|
|
|
|||
|
|
@ -151,6 +151,18 @@ public class InternalStateEditor(
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Change a single bonus item. </summary>
|
||||
public bool ChangeBonusItem(ActorState state, BonusItemFlag slot, BonusItem item, StateSource source, out BonusItem oldItem, uint key = 0)
|
||||
{
|
||||
oldItem = state.ModelData.BonusItem(slot);
|
||||
if (!state.CanUnlock(key))
|
||||
return false;
|
||||
|
||||
state.ModelData.SetBonusItem(slot, item);
|
||||
state.Sources[slot] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
||||
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainIds stains, StateSource source, out EquipItem oldItem,
|
||||
out StainIds oldStains, uint key = 0)
|
||||
|
|
|
|||
|
|
@ -125,6 +125,33 @@ public class StateApplier(
|
|||
return data;
|
||||
}
|
||||
|
||||
public void ChangeBonusItem(ActorData data, BonusItemFlag slot, PrimaryId id, Variant variant)
|
||||
{
|
||||
var item = new CharacterArmor(id, variant, StainIds.None);
|
||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
||||
{
|
||||
var mdl = actor.Model;
|
||||
if (!mdl.IsHuman)
|
||||
continue;
|
||||
|
||||
_updateSlot.UpdateBonusSlot(actor.Model, slot, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ChangeBonusItem(ActorData,BonusItemFlag,PrimaryId,Variant)"/>
|
||||
public ActorData ChangeBonusItem(ActorState state, BonusItemFlag slot, bool apply)
|
||||
{
|
||||
// If the source is not IPC we do not want to apply restrictions.
|
||||
var data = GetData(state);
|
||||
if (apply)
|
||||
{
|
||||
var item = state.ModelData.BonusItem(slot);
|
||||
ChangeBonusItem(data, slot, item.ModelId, item.Variant);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Change the stain of a single piece of armor or weapon.
|
||||
|
|
|
|||
|
|
@ -89,6 +89,18 @@ public class StateEditor(
|
|||
StateChanged.Invoke(type, settings.Source, state, actors, (old, item, slot));
|
||||
}
|
||||
|
||||
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default)
|
||||
{
|
||||
var state = (ActorState)data;
|
||||
if (!Editor.ChangeBonusItem(state, slot, item, settings.Source, out var old, settings.Key))
|
||||
return;
|
||||
|
||||
var actors = Applier.ChangeBonusItem(state, slot, settings.Source.RequiresChange());
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChangeType.BonusItem, settings.Source, state, actors, (old, item, slot));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainIds? stains, ApplySettings settings)
|
||||
{
|
||||
|
|
@ -226,7 +238,7 @@ public class StateEditor(
|
|||
out _, settings.Key);
|
||||
}
|
||||
|
||||
var customizeFlags = mergedDesign.Design.ApplyCustomizeRaw;
|
||||
var customizeFlags = mergedDesign.Design.Application.Customize;
|
||||
if (mergedDesign.Design.DoApplyCustomize(CustomizeIndex.Clan))
|
||||
customizeFlags |= CustomizeFlag.Race;
|
||||
|
||||
|
|
@ -245,7 +257,7 @@ public class StateEditor(
|
|||
state.Sources[parameter] = StateSource.Game;
|
||||
}
|
||||
|
||||
foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
|
||||
foreach (var parameter in mergedDesign.Design.Application.Parameters.Iterate())
|
||||
{
|
||||
if (settings.RespectManual && state.Sources[parameter].IsManual())
|
||||
continue;
|
||||
|
|
@ -273,6 +285,13 @@ public class StateEditor(
|
|||
Source(slot.ToState(true)), out _, settings.Key);
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
if (mergedDesign.Design.DoApplyBonusItem(slot))
|
||||
if (!settings.RespectManual || !state.Sources[slot].IsManual())
|
||||
Editor.ChangeBonusItem(state, slot, mergedDesign.Design.DesignData.BonusItem(slot), Source(slot), out _, settings.Key);
|
||||
}
|
||||
|
||||
foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots)
|
||||
{
|
||||
if (mergedDesign.Design.DoApplyStain(weaponSlot))
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
_ => Invalid,
|
||||
};
|
||||
|
||||
public static implicit operator StateIndex(BonusItemFlag flag)
|
||||
=> flag switch
|
||||
{
|
||||
BonusItemFlag.Glasses => new StateIndex(BonusItemGlasses),
|
||||
_ => Invalid,
|
||||
};
|
||||
|
||||
public static implicit operator StateIndex(CustomizeIndex index)
|
||||
=> index switch
|
||||
{
|
||||
|
|
@ -198,23 +205,13 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
public const int ParamFacePaintUvOffset = ParamFacePaintUvMultiplier + 1;
|
||||
public const int ParamDecalColor = ParamFacePaintUvOffset + 1;
|
||||
|
||||
public const int Size = ParamDecalColor + 1;
|
||||
public const int BonusItemGlasses = ParamDecalColor + 1;
|
||||
|
||||
public const int Size = BonusItemGlasses + 1;
|
||||
|
||||
public static IEnumerable<StateIndex> All
|
||||
=> Enumerable.Range(0, Size - 1).Select(i => new StateIndex(i));
|
||||
|
||||
public bool GetApply(DesignBase data)
|
||||
=> GetFlag() switch
|
||||
{
|
||||
EquipFlag e => data.ApplyEquip.HasFlag(e),
|
||||
CustomizeFlag c => data.ApplyCustomize.HasFlag(c),
|
||||
MetaFlag m => data.ApplyMeta.HasFlag(m),
|
||||
CrestFlag c => data.ApplyCrest.HasFlag(c),
|
||||
CustomizeParameterFlag c => data.ApplyParameters.HasFlag(c),
|
||||
bool v => v,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public string ToName()
|
||||
=> GetFlag() switch
|
||||
{
|
||||
|
|
@ -223,6 +220,7 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
MetaFlag m => m.ToIndex().ToName(),
|
||||
CrestFlag c => c.ToLabel(),
|
||||
CustomizeParameterFlag c => c.ToName(),
|
||||
BonusItemFlag b => b.ToName(),
|
||||
bool v => "Model ID",
|
||||
_ => "Unknown",
|
||||
};
|
||||
|
|
@ -317,6 +315,8 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
ParamFacePaintUvOffset => CustomizeParameterFlag.FacePaintUvOffset,
|
||||
ParamDecalColor => CustomizeParameterFlag.DecalColor,
|
||||
|
||||
BonusItemGlasses => BonusItemFlag.Glasses,
|
||||
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
|
|
@ -411,6 +411,8 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
ParamFacePaintUvOffset => data.Parameters[CustomizeParameterFlag.FacePaintUvOffset],
|
||||
ParamDecalColor => data.Parameters[CustomizeParameterFlag.DecalColor],
|
||||
|
||||
BonusItemGlasses => data.BonusItem(BonusItemFlag.Glasses),
|
||||
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ public class StateListener : IDisposable
|
|||
private readonly ItemManager _items;
|
||||
private readonly CustomizeService _customizations;
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly EquipSlotUpdating _equipSlotUpdating;
|
||||
private readonly EquipSlotUpdating _equipSlotUpdating;
|
||||
private readonly BonusSlotUpdating _bonusSlotUpdating;
|
||||
private readonly WeaponLoading _weaponLoading;
|
||||
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
||||
private readonly VisorStateChanged _visorState;
|
||||
|
|
@ -52,17 +53,18 @@ public class StateListener : IDisposable
|
|||
private CharacterWeapon _lastFistOffhand = CharacterWeapon.Empty;
|
||||
|
||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config,
|
||||
EquipSlotUpdating equipSlotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans,
|
||||
StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose,
|
||||
ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition, CrestService crestService)
|
||||
EquipSlotUpdating equipSlotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState,
|
||||
WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier,
|
||||
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects,
|
||||
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
|
||||
CrestService crestService, BonusSlotUpdating bonusSlotUpdating)
|
||||
{
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
_config = config;
|
||||
_equipSlotUpdating = equipSlotUpdating;
|
||||
_equipSlotUpdating = equipSlotUpdating;
|
||||
_weaponLoading = weaponLoading;
|
||||
_visorState = visorState;
|
||||
_weaponVisibility = weaponVisibility;
|
||||
|
|
@ -78,6 +80,7 @@ public class StateListener : IDisposable
|
|||
_customizations = customizations;
|
||||
_condition = condition;
|
||||
_crestService = crestService;
|
||||
_bonusSlotUpdating = bonusSlotUpdating;
|
||||
Subscribe();
|
||||
}
|
||||
|
||||
|
|
@ -227,6 +230,35 @@ public class StateListener : IDisposable
|
|||
(_, armor) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender);
|
||||
}
|
||||
|
||||
private void OnBonusSlotUpdating(Model model, BonusItemFlag slot, ref CharacterArmor item, ref ulong returnValue)
|
||||
{
|
||||
var actor = _penumbra.GameObjectFromDrawObject(model);
|
||||
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
|
||||
return;
|
||||
|
||||
if (actor.Identifier(_actors, out var identifier)
|
||||
&& _manager.TryGetValue(identifier, out var state))
|
||||
switch (UpdateBaseData(actor, state, slot, item))
|
||||
{
|
||||
// Base data changed equipment while actors were not there.
|
||||
// Update model state if not on fixed design.
|
||||
case UpdateState.Change:
|
||||
var apply = false;
|
||||
if (!state.Sources[slot].IsFixed())
|
||||
_manager.ChangeBonusItem(state, slot, state.BaseData.BonusItem(slot), ApplySettings.Game);
|
||||
else
|
||||
apply = true;
|
||||
if (apply)
|
||||
item = state.ModelData.BonusItem(slot).ToArmor();
|
||||
break;
|
||||
// Use current model data.
|
||||
case UpdateState.NoChange:
|
||||
item = state.ModelData.BonusItem(slot).ToArmor();
|
||||
break;
|
||||
case UpdateState.Transformed: break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMovedEquipment((EquipSlot, uint, StainIds)[] items)
|
||||
{
|
||||
_objects.Update();
|
||||
|
|
@ -403,6 +435,28 @@ public class StateListener : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private UpdateState UpdateBaseData(Actor actor, ActorState state, BonusItemFlag slot, CharacterArmor item)
|
||||
{
|
||||
var actorItemId = actor.GetBonusItem(slot);
|
||||
if (!_items.IsBonusItemValid(slot, actorItemId, out var actorItem))
|
||||
return UpdateState.NoChange;
|
||||
|
||||
// The actor item does not correspond to the model item, thus the actor is transformed.
|
||||
if (actorItem.ModelId != item.Set || actorItem.Variant != item.Variant)
|
||||
return UpdateState.Transformed;
|
||||
|
||||
var baseData = state.BaseData.BonusItem(slot);
|
||||
var change = UpdateState.NoChange;
|
||||
if (baseData.Id != actorItem.Id || baseData.ModelId != item.Set || baseData.Variant != item.Variant)
|
||||
{
|
||||
var identified = _items.Identify(slot, item.Set, item.Variant);
|
||||
state.BaseData.SetBonusItem(slot, identified);
|
||||
change = UpdateState.Change;
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
/// <summary> Handle a full equip slot update for base data and model data. </summary>
|
||||
private void HandleEquipSlot(Actor actor, ActorState state, EquipSlot slot, ref CharacterArmor armor)
|
||||
{
|
||||
|
|
@ -700,6 +754,7 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase += OnCreatingCharacterBase;
|
||||
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
|
||||
_equipSlotUpdating.Subscribe(OnEquipSlotUpdating, EquipSlotUpdating.Priority.StateListener);
|
||||
_bonusSlotUpdating.Subscribe(OnBonusSlotUpdating, BonusSlotUpdating.Priority.StateListener);
|
||||
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||
|
|
@ -716,6 +771,7 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase -= OnCreatingCharacterBase;
|
||||
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
|
||||
_equipSlotUpdating.Unsubscribe(OnEquipSlotUpdating);
|
||||
_bonusSlotUpdating.Unsubscribe(OnBonusSlotUpdating);
|
||||
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||
_visorState.Unsubscribe(OnVisorChange);
|
||||
|
|
|
|||
|
|
@ -160,6 +160,13 @@ public sealed class StateManager(
|
|||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
ret.SetCrest(slot, CrestService.GetModelCrest(actor, slot));
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var data = model.GetBonus(slot);
|
||||
var item = Items.Identify(slot, data.Set, data.Variant);
|
||||
ret.SetBonusItem(slot, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -181,6 +188,13 @@ public sealed class StateManager(
|
|||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
ret.SetCrest(slot, actor.GetCrest(slot));
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var id = actor.GetBonusItem(slot);
|
||||
var item = Items.Resolve(slot, id);
|
||||
ret.SetBonusItem(slot, item);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the weapons regardless of source.
|
||||
|
|
@ -241,6 +255,9 @@ public sealed class StateManager(
|
|||
state.Sources[slot, false] = StateSource.Game;
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
state.Sources[slot] = StateSource.Game;
|
||||
|
||||
foreach (var type in Enum.GetValues<MetaIndex>())
|
||||
state.Sources[type] = StateSource.Game;
|
||||
|
||||
|
|
@ -328,6 +345,12 @@ public sealed class StateManager(
|
|||
state.ModelData.IsHatVisible());
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var item = state.ModelData.BonusItem(slot);
|
||||
Applier.ChangeBonusItem(actors, slot, item.ModelId, item.Variant);
|
||||
}
|
||||
|
||||
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
|
||||
Applier.ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));
|
||||
var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? actors.OnlyGPose() : actors;
|
||||
|
|
@ -364,6 +387,15 @@ public sealed class StateManager(
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
if (state.Sources[slot] is StateSource.Fixed)
|
||||
{
|
||||
state.Sources[slot] = StateSource.Game;
|
||||
state.ModelData.SetBonusItem(slot, state.BaseData.BonusItem(slot));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
{
|
||||
if (state.Sources[slot] is StateSource.Fixed)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public class FavoriteManager : ISavable
|
|||
private readonly HashSet<ItemId> _favorites = [];
|
||||
private readonly HashSet<StainId> _favoriteColors = [];
|
||||
private readonly HashSet<FavoriteHairStyle> _favoriteHairStyles = [];
|
||||
private readonly HashSet<BonusItemId> _favoriteBonusItems = [];
|
||||
|
||||
public FavoriteManager(SaveService saveService)
|
||||
{
|
||||
|
|
@ -62,6 +63,7 @@ public class FavoriteManager : ISavable
|
|||
_favorites.UnionWith(load!.FavoriteItems.Select(i => (ItemId)i));
|
||||
_favoriteColors.UnionWith(load!.FavoriteColors.Select(i => (StainId)i));
|
||||
_favoriteHairStyles.UnionWith(load!.FavoriteHairStyles.Select(t => new FavoriteHairStyle(t)));
|
||||
_favoriteBonusItems.UnionWith(load!.FavoriteBonusItems.Select(b => new BonusItemId(b)));
|
||||
break;
|
||||
|
||||
default: throw new Exception($"Unknown Version {load?.Version ?? 0}");
|
||||
|
|
@ -109,6 +111,11 @@ public class FavoriteManager : ISavable
|
|||
foreach (var hairStyle in _favoriteHairStyles)
|
||||
j.WriteValue(hairStyle.ToValue());
|
||||
j.WriteEndArray();
|
||||
j.WriteStartArray();
|
||||
j.WritePropertyName(nameof(LoadIntermediary.FavoriteBonusItems));
|
||||
foreach (var item in _favoriteBonusItems)
|
||||
j.WriteValue(item.Id);
|
||||
j.WriteEndArray();
|
||||
j.WriteEndObject();
|
||||
}
|
||||
|
||||
|
|
@ -124,9 +131,6 @@ public class FavoriteManager : ISavable
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool TryAdd(Stain stain)
|
||||
=> TryAdd(stain.RowIndex);
|
||||
|
||||
public bool TryAdd(StainId stain)
|
||||
{
|
||||
if (stain.Id == 0 || !_favoriteColors.Add(stain))
|
||||
|
|
@ -136,6 +140,15 @@ public class FavoriteManager : ISavable
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool TryAdd(BonusItem bonusItem)
|
||||
{
|
||||
if (bonusItem.Id == 0 || !_favoriteBonusItems.Add(bonusItem.Id))
|
||||
return false;
|
||||
|
||||
Save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAdd(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value)
|
||||
{
|
||||
if (!TypeAllowed(type) || !_favoriteHairStyles.Add(new FavoriteHairStyle(gender, race, type, value)))
|
||||
|
|
@ -157,9 +170,6 @@ public class FavoriteManager : ISavable
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(Stain stain)
|
||||
=> Remove(stain.RowIndex);
|
||||
|
||||
public bool Remove(StainId stain)
|
||||
{
|
||||
if (!_favoriteColors.Remove(stain))
|
||||
|
|
@ -169,6 +179,15 @@ public class FavoriteManager : ISavable
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(BonusItem bonusItem)
|
||||
{
|
||||
if (!_favoriteBonusItems.Remove(bonusItem.Id))
|
||||
return false;
|
||||
|
||||
Save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value)
|
||||
{
|
||||
if (!_favoriteHairStyles.Remove(new FavoriteHairStyle(gender, race, type, value)))
|
||||
|
|
@ -181,23 +200,21 @@ public class FavoriteManager : ISavable
|
|||
public bool Contains(EquipItem item)
|
||||
=> _favorites.Contains(item.ItemId);
|
||||
|
||||
public bool Contains(Stain stain)
|
||||
=> _favoriteColors.Contains(stain.RowIndex);
|
||||
|
||||
public bool Contains(ItemId item)
|
||||
=> _favorites.Contains(item);
|
||||
|
||||
public bool Contains(StainId stain)
|
||||
=> _favoriteColors.Contains(stain);
|
||||
|
||||
public bool Contains(BonusItem bonusItem)
|
||||
=> _favoriteBonusItems.Contains(bonusItem.Id);
|
||||
|
||||
public bool Contains(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value)
|
||||
=> _favoriteHairStyles.Contains(new FavoriteHairStyle(gender, race, type, value));
|
||||
|
||||
private class LoadIntermediary
|
||||
{
|
||||
public int Version = CurrentVersion;
|
||||
public uint[] FavoriteItems = [];
|
||||
public byte[] FavoriteColors = [];
|
||||
public uint[] FavoriteHairStyles = [];
|
||||
public int Version = CurrentVersion;
|
||||
public uint[] FavoriteItems = [];
|
||||
public byte[] FavoriteColors = [];
|
||||
public uint[] FavoriteHairStyles = [];
|
||||
public ushort[] FavoriteBonusItems = [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit d83303ccc3ec5d7237f5da621e9c2433ad28f9e1
|
||||
Subproject commit 8928015f38f951810a9a6fbb44fb4a0cb9a712dd
|
||||
Loading…
Add table
Add a link
Reference in a new issue