mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-24 21:51:48 +01:00
FC crest visibility draft
This commit is contained in:
parent
0a7d800706
commit
63f7818481
28 changed files with 980 additions and 223 deletions
|
|
@ -4,7 +4,7 @@ using Penumbra.GameData.Enums;
|
||||||
namespace Glamourer.Structs;
|
namespace Glamourer.Structs;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum EquipFlag : uint
|
public enum EquipFlag : ulong
|
||||||
{
|
{
|
||||||
Head = 0x00000001,
|
Head = 0x00000001,
|
||||||
Body = 0x00000002,
|
Body = 0x00000002,
|
||||||
|
|
@ -30,12 +30,34 @@ public enum EquipFlag : uint
|
||||||
LFingerStain = 0x00200000,
|
LFingerStain = 0x00200000,
|
||||||
MainhandStain = 0x00400000,
|
MainhandStain = 0x00400000,
|
||||||
OffhandStain = 0x00800000,
|
OffhandStain = 0x00800000,
|
||||||
|
HeadCrest = 0x01000000,
|
||||||
|
BodyCrest = 0x02000000,
|
||||||
|
HandsCrest = 0x04000000,
|
||||||
|
LegsCrest = 0x08000000,
|
||||||
|
FeetCrest = 0x10000000,
|
||||||
|
EarsCrest = 0x20000000,
|
||||||
|
NeckCrest = 0x40000000,
|
||||||
|
WristCrest = 0x80000000,
|
||||||
|
RFingerCrest = 0x100000000,
|
||||||
|
LFingerCrest = 0x200000000,
|
||||||
|
MainhandCrest = 0x400000000,
|
||||||
|
OffhandCrest = 0x800000000,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EquipFlagExtensions
|
public static class EquipFlagExtensions
|
||||||
{
|
{
|
||||||
public const EquipFlag All = (EquipFlag)(((uint)EquipFlag.OffhandStain << 1) - 1);
|
public const EquipFlag All = (EquipFlag)(((ulong)EquipFlag.OffhandCrest << 1) - 1);
|
||||||
public const int NumEquipFlags = 24;
|
public const EquipFlag AllRelevant = All
|
||||||
|
& ~EquipFlag.HandsCrest
|
||||||
|
& ~EquipFlag.LegsCrest
|
||||||
|
& ~EquipFlag.FeetCrest
|
||||||
|
& ~EquipFlag.EarsCrest
|
||||||
|
& ~EquipFlag.NeckCrest
|
||||||
|
& ~EquipFlag.WristCrest
|
||||||
|
& ~EquipFlag.RFingerCrest
|
||||||
|
& ~EquipFlag.LFingerCrest
|
||||||
|
& ~EquipFlag.MainhandCrest;
|
||||||
|
public const int NumEquipFlags = 36;
|
||||||
|
|
||||||
public static EquipFlag ToFlag(this EquipSlot slot)
|
public static EquipFlag ToFlag(this EquipSlot slot)
|
||||||
=> slot switch
|
=> slot switch
|
||||||
|
|
@ -73,21 +95,39 @@ public static class EquipFlagExtensions
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static EquipFlag ToCrestFlag(this EquipSlot slot)
|
||||||
|
=> slot switch
|
||||||
|
{
|
||||||
|
EquipSlot.MainHand => EquipFlag.MainhandCrest,
|
||||||
|
EquipSlot.OffHand => EquipFlag.OffhandCrest,
|
||||||
|
EquipSlot.Head => EquipFlag.HeadCrest,
|
||||||
|
EquipSlot.Body => EquipFlag.BodyCrest,
|
||||||
|
EquipSlot.Hands => EquipFlag.HandsCrest,
|
||||||
|
EquipSlot.Legs => EquipFlag.LegsCrest,
|
||||||
|
EquipSlot.Feet => EquipFlag.FeetCrest,
|
||||||
|
EquipSlot.Ears => EquipFlag.EarsCrest,
|
||||||
|
EquipSlot.Neck => EquipFlag.NeckCrest,
|
||||||
|
EquipSlot.Wrists => EquipFlag.WristCrest,
|
||||||
|
EquipSlot.RFinger => EquipFlag.RFingerCrest,
|
||||||
|
EquipSlot.LFinger => EquipFlag.LFingerCrest,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
public static EquipFlag ToBothFlags(this EquipSlot slot)
|
public static EquipFlag ToBothFlags(this EquipSlot slot)
|
||||||
=> slot switch
|
=> slot switch
|
||||||
{
|
{
|
||||||
EquipSlot.MainHand => EquipFlag.Mainhand | EquipFlag.MainhandStain,
|
EquipSlot.MainHand => EquipFlag.Mainhand | EquipFlag.MainhandStain | EquipFlag.MainhandCrest,
|
||||||
EquipSlot.OffHand => EquipFlag.Offhand | EquipFlag.OffhandStain,
|
EquipSlot.OffHand => EquipFlag.Offhand | EquipFlag.OffhandStain | EquipFlag.OffhandCrest,
|
||||||
EquipSlot.Head => EquipFlag.Head | EquipFlag.HeadStain,
|
EquipSlot.Head => EquipFlag.Head | EquipFlag.HeadStain | EquipFlag.HeadCrest,
|
||||||
EquipSlot.Body => EquipFlag.Body | EquipFlag.BodyStain,
|
EquipSlot.Body => EquipFlag.Body | EquipFlag.BodyStain | EquipFlag.BodyCrest,
|
||||||
EquipSlot.Hands => EquipFlag.Hands | EquipFlag.HandsStain,
|
EquipSlot.Hands => EquipFlag.Hands | EquipFlag.HandsStain | EquipFlag.HandsCrest,
|
||||||
EquipSlot.Legs => EquipFlag.Legs | EquipFlag.LegsStain,
|
EquipSlot.Legs => EquipFlag.Legs | EquipFlag.LegsStain | EquipFlag.LegsCrest,
|
||||||
EquipSlot.Feet => EquipFlag.Feet | EquipFlag.FeetStain,
|
EquipSlot.Feet => EquipFlag.Feet | EquipFlag.FeetStain | EquipFlag.FeetCrest,
|
||||||
EquipSlot.Ears => EquipFlag.Ears | EquipFlag.EarsStain,
|
EquipSlot.Ears => EquipFlag.Ears | EquipFlag.EarsStain | EquipFlag.EarsCrest,
|
||||||
EquipSlot.Neck => EquipFlag.Neck | EquipFlag.NeckStain,
|
EquipSlot.Neck => EquipFlag.Neck | EquipFlag.NeckStain | EquipFlag.NeckCrest,
|
||||||
EquipSlot.Wrists => EquipFlag.Wrist | EquipFlag.WristStain,
|
EquipSlot.Wrists => EquipFlag.Wrist | EquipFlag.WristStain | EquipFlag.WristCrest,
|
||||||
EquipSlot.RFinger => EquipFlag.RFinger | EquipFlag.RFingerStain,
|
EquipSlot.RFinger => EquipFlag.RFinger | EquipFlag.RFingerStain | EquipFlag.RFingerCrest,
|
||||||
EquipSlot.LFinger => EquipFlag.LFinger | EquipFlag.LFingerStain,
|
EquipSlot.LFinger => EquipFlag.LFinger | EquipFlag.LFingerStain | EquipFlag.LFingerCrest,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ public class AutoDesign
|
||||||
Weapons = 0x04,
|
Weapons = 0x04,
|
||||||
Stains = 0x08,
|
Stains = 0x08,
|
||||||
Accessories = 0x10,
|
Accessories = 0x10,
|
||||||
|
Crests = 0x20,
|
||||||
|
|
||||||
All = Armor | Accessories | Customizations | Weapons | Stains,
|
All = Armor | Accessories | Customizations | Weapons | Stains | Crests,
|
||||||
}
|
}
|
||||||
|
|
||||||
public Design? Design;
|
public Design? Design;
|
||||||
|
|
@ -69,7 +70,8 @@ public class AutoDesign
|
||||||
var equipFlags = (ApplicationType.HasFlag(Type.Weapons) ? WeaponFlags : 0)
|
var equipFlags = (ApplicationType.HasFlag(Type.Weapons) ? WeaponFlags : 0)
|
||||||
| (ApplicationType.HasFlag(Type.Armor) ? ArmorFlags : 0)
|
| (ApplicationType.HasFlag(Type.Armor) ? ArmorFlags : 0)
|
||||||
| (ApplicationType.HasFlag(Type.Accessories) ? AccessoryFlags : 0)
|
| (ApplicationType.HasFlag(Type.Accessories) ? AccessoryFlags : 0)
|
||||||
| (ApplicationType.HasFlag(Type.Stains) ? StainFlags : 0);
|
| (ApplicationType.HasFlag(Type.Stains) ? StainFlags : 0)
|
||||||
|
| (ApplicationType.HasFlag(Type.Crests) ? CrestFlags : 0);
|
||||||
var customizeFlags = ApplicationType.HasFlag(Type.Customizations) ? CustomizeFlagExtensions.All : 0;
|
var customizeFlags = ApplicationType.HasFlag(Type.Customizations) ? CustomizeFlagExtensions.All : 0;
|
||||||
|
|
||||||
if (Revert)
|
if (Revert)
|
||||||
|
|
@ -99,4 +101,21 @@ public class AutoDesign
|
||||||
| EquipFlag.WristStain
|
| EquipFlag.WristStain
|
||||||
| EquipFlag.RFingerStain
|
| EquipFlag.RFingerStain
|
||||||
| EquipFlag.LFingerStain;
|
| EquipFlag.LFingerStain;
|
||||||
|
|
||||||
|
public const EquipFlag CrestFlags = EquipFlag.MainhandCrest
|
||||||
|
| EquipFlag.OffhandCrest
|
||||||
|
| EquipFlag.HeadCrest
|
||||||
|
| EquipFlag.BodyCrest
|
||||||
|
| EquipFlag.HandsCrest
|
||||||
|
| EquipFlag.LegsCrest
|
||||||
|
| EquipFlag.FeetCrest
|
||||||
|
| EquipFlag.EarsCrest
|
||||||
|
| EquipFlag.NeckCrest
|
||||||
|
| EquipFlag.WristCrest
|
||||||
|
| EquipFlag.RFingerCrest
|
||||||
|
| EquipFlag.LFingerCrest;
|
||||||
|
|
||||||
|
public const EquipFlag RelevantCrestFlags = EquipFlag.OffhandCrest
|
||||||
|
| EquipFlag.HeadCrest
|
||||||
|
| EquipFlag.BodyCrest;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
var item = design.Item(slot);
|
var item = design.Item(slot);
|
||||||
if (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _))
|
if (!_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[slot, false] is not StateChanged.Source.Manual)
|
if (!respectManual || state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeItem(state, slot, item, source);
|
_state.ChangeItem(state, slot, item, source);
|
||||||
totalEquipFlags |= flag;
|
totalEquipFlags |= flag;
|
||||||
}
|
}
|
||||||
|
|
@ -342,17 +342,25 @@ public class AutoDesignApplier : IDisposable
|
||||||
var stainFlag = slot.ToStainFlag();
|
var stainFlag = slot.ToStainFlag();
|
||||||
if (equipFlags.HasFlag(stainFlag))
|
if (equipFlags.HasFlag(stainFlag))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[slot, true] is not StateChanged.Source.Manual)
|
if (!respectManual || state[slot, ActorState.EquipField.Stain] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeStain(state, slot, design.Stain(slot), source);
|
_state.ChangeStain(state, slot, design.Stain(slot), source);
|
||||||
totalEquipFlags |= stainFlag;
|
totalEquipFlags |= stainFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var crestFlag = slot.ToCrestFlag();
|
||||||
|
if (equipFlags.HasFlag(crestFlag))
|
||||||
|
{
|
||||||
|
if (!respectManual || state[slot, ActorState.EquipField.Crest] is not StateChanged.Source.Manual)
|
||||||
|
_state.ChangeCrest(state, slot, design.Crest(slot), source);
|
||||||
|
totalEquipFlags |= crestFlag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.Mainhand))
|
if (equipFlags.HasFlag(EquipFlag.Mainhand))
|
||||||
{
|
{
|
||||||
var item = design.Item(EquipSlot.MainHand);
|
var item = design.Item(EquipSlot.MainHand);
|
||||||
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
||||||
var checkState = !respectManual || state[EquipSlot.MainHand, false] is not StateChanged.Source.Manual;
|
var checkState = !respectManual || state[EquipSlot.MainHand, ActorState.EquipField.Item] is not StateChanged.Source.Manual;
|
||||||
if (checkUnlock && checkState)
|
if (checkUnlock && checkState)
|
||||||
{
|
{
|
||||||
if (fromJobChange)
|
if (fromJobChange)
|
||||||
|
|
@ -372,7 +380,7 @@ public class AutoDesignApplier : IDisposable
|
||||||
{
|
{
|
||||||
var item = design.Item(EquipSlot.OffHand);
|
var item = design.Item(EquipSlot.OffHand);
|
||||||
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
||||||
var checkState = !respectManual || state[EquipSlot.OffHand, false] is not StateChanged.Source.Manual;
|
var checkState = !respectManual || state[EquipSlot.OffHand, ActorState.EquipField.Item] is not StateChanged.Source.Manual;
|
||||||
if (checkUnlock && checkState)
|
if (checkUnlock && checkState)
|
||||||
{
|
{
|
||||||
if (fromJobChange)
|
if (fromJobChange)
|
||||||
|
|
@ -390,17 +398,31 @@ public class AutoDesignApplier : IDisposable
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.MainhandStain))
|
if (equipFlags.HasFlag(EquipFlag.MainhandStain))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[EquipSlot.MainHand, true] is not StateChanged.Source.Manual)
|
if (!respectManual || state[EquipSlot.MainHand, ActorState.EquipField.Stain] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), source);
|
_state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), source);
|
||||||
totalEquipFlags |= EquipFlag.MainhandStain;
|
totalEquipFlags |= EquipFlag.MainhandStain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (equipFlags.HasFlag(EquipFlag.MainhandCrest))
|
||||||
|
{
|
||||||
|
if (!respectManual || state[EquipSlot.MainHand, ActorState.EquipField.Crest] is not StateChanged.Source.Manual)
|
||||||
|
_state.ChangeCrest(state, EquipSlot.MainHand, design.Crest(EquipSlot.MainHand), source);
|
||||||
|
totalEquipFlags |= EquipFlag.MainhandCrest;
|
||||||
|
}
|
||||||
|
|
||||||
if (equipFlags.HasFlag(EquipFlag.OffhandStain))
|
if (equipFlags.HasFlag(EquipFlag.OffhandStain))
|
||||||
{
|
{
|
||||||
if (!respectManual || state[EquipSlot.OffHand, true] is not StateChanged.Source.Manual)
|
if (!respectManual || state[EquipSlot.OffHand, ActorState.EquipField.Stain] is not StateChanged.Source.Manual)
|
||||||
_state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), source);
|
_state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), source);
|
||||||
totalEquipFlags |= EquipFlag.OffhandStain;
|
totalEquipFlags |= EquipFlag.OffhandStain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (equipFlags.HasFlag(EquipFlag.OffhandCrest))
|
||||||
|
{
|
||||||
|
if (!respectManual || state[EquipSlot.OffHand, ActorState.EquipField.Crest] is not StateChanged.Source.Manual)
|
||||||
|
_state.ChangeCrest(state, EquipSlot.OffHand, design.Crest(EquipSlot.OffHand), source);
|
||||||
|
totalEquipFlags |= EquipFlag.OffhandCrest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReduceCustomize(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags,
|
private void ReduceCustomize(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags,
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ public class DesignBase
|
||||||
internal CustomizeFlag ApplyCustomizeRaw
|
internal CustomizeFlag ApplyCustomizeRaw
|
||||||
=> _applyCustomize;
|
=> _applyCustomize;
|
||||||
|
|
||||||
internal EquipFlag ApplyEquip = EquipFlagExtensions.All;
|
internal EquipFlag ApplyEquip = EquipFlagExtensions.AllRelevant;
|
||||||
private DesignFlags _designFlags = DesignFlags.ApplyHatVisible | DesignFlags.ApplyVisorState | DesignFlags.ApplyWeaponVisible;
|
private DesignFlags _designFlags = DesignFlags.ApplyHatVisible | DesignFlags.ApplyVisorState | DesignFlags.ApplyWeaponVisible;
|
||||||
|
|
||||||
public bool SetCustomize(CustomizationService customizationService, Customize customize)
|
public bool SetCustomize(CustomizationService customizationService, Customize customize)
|
||||||
|
|
@ -166,6 +166,9 @@ public class DesignBase
|
||||||
public bool DoApplyStain(EquipSlot slot)
|
public bool DoApplyStain(EquipSlot slot)
|
||||||
=> ApplyEquip.HasFlag(slot.ToStainFlag());
|
=> ApplyEquip.HasFlag(slot.ToStainFlag());
|
||||||
|
|
||||||
|
public bool DoApplyCrest(EquipSlot slot)
|
||||||
|
=> ApplyEquip.HasFlag(slot.ToCrestFlag());
|
||||||
|
|
||||||
public bool DoApplyCustomize(CustomizeIndex idx)
|
public bool DoApplyCustomize(CustomizeIndex idx)
|
||||||
=> ApplyCustomize.HasFlag(idx.ToFlag());
|
=> ApplyCustomize.HasFlag(idx.ToFlag());
|
||||||
|
|
||||||
|
|
@ -189,6 +192,16 @@ public class DesignBase
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool SetApplyCrest(EquipSlot slot, bool value)
|
||||||
|
{
|
||||||
|
var newValue = value ? ApplyEquip | slot.ToCrestFlag() : ApplyEquip & ~slot.ToCrestFlag();
|
||||||
|
if (newValue == ApplyEquip)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ApplyEquip = newValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
internal bool SetApplyCustomize(CustomizeIndex idx, bool value)
|
internal bool SetApplyCustomize(CustomizeIndex idx, bool value)
|
||||||
{
|
{
|
||||||
var newValue = value ? _applyCustomize | idx.ToFlag() : _applyCustomize & ~idx.ToFlag();
|
var newValue = value ? _applyCustomize | idx.ToFlag() : _applyCustomize & ~idx.ToFlag();
|
||||||
|
|
@ -246,13 +259,15 @@ public class DesignBase
|
||||||
|
|
||||||
protected JObject SerializeEquipment()
|
protected JObject SerializeEquipment()
|
||||||
{
|
{
|
||||||
static JObject Serialize(CustomItemId id, StainId stain, bool apply, bool applyStain)
|
static JObject Serialize(CustomItemId id, StainId stain, bool crest, bool apply, bool applyStain, bool applyCrest)
|
||||||
=> new()
|
=> new()
|
||||||
{
|
{
|
||||||
["ItemId"] = id.Id,
|
["ItemId"] = id.Id,
|
||||||
["Stain"] = stain.Id,
|
["Stain"] = stain.Id,
|
||||||
|
["Crest"] = crest,
|
||||||
["Apply"] = apply,
|
["Apply"] = apply,
|
||||||
["ApplyStain"] = applyStain,
|
["ApplyStain"] = applyStain,
|
||||||
|
["ApplyCrest"] = applyCrest,
|
||||||
};
|
};
|
||||||
|
|
||||||
var ret = new JObject();
|
var ret = new JObject();
|
||||||
|
|
@ -262,7 +277,8 @@ public class DesignBase
|
||||||
{
|
{
|
||||||
var item = _designData.Item(slot);
|
var item = _designData.Item(slot);
|
||||||
var stain = _designData.Stain(slot);
|
var stain = _designData.Stain(slot);
|
||||||
ret[slot.ToString()] = Serialize(item.Id, stain, DoApplyEquip(slot), DoApplyStain(slot));
|
var crest = _designData.Crest(slot);
|
||||||
|
ret[slot.ToString()] = Serialize(item.Id, stain, crest, DoApplyEquip(slot), DoApplyStain(slot), DoApplyCrest(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyHatVisible()).ToJObject("Show", "Apply");
|
ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyHatVisible()).ToJObject("Show", "Apply");
|
||||||
|
|
@ -345,13 +361,15 @@ public class DesignBase
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static (CustomItemId, StainId, bool, bool) ParseItem(EquipSlot slot, JToken? item)
|
static (CustomItemId, StainId, bool, bool, bool, bool) ParseItem(EquipSlot slot, JToken? item)
|
||||||
{
|
{
|
||||||
var id = item?["ItemId"]?.ToObject<ulong>() ?? ItemManager.NothingId(slot).Id;
|
var id = item?["ItemId"]?.ToObject<ulong>() ?? ItemManager.NothingId(slot).Id;
|
||||||
var stain = (StainId)(item?["Stain"]?.ToObject<byte>() ?? 0);
|
var stain = (StainId)(item?["Stain"]?.ToObject<byte>() ?? 0);
|
||||||
|
var crest = item?["Crest"]?.ToObject<bool>() ?? false;
|
||||||
var apply = item?["Apply"]?.ToObject<bool>() ?? false;
|
var apply = item?["Apply"]?.ToObject<bool>() ?? false;
|
||||||
var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false;
|
var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false;
|
||||||
return (id, stain, apply, applyStain);
|
var applyCrest = item?["ApplyCrest"]?.ToObject<bool>() ?? false;
|
||||||
|
return (id, stain, crest, apply, applyStain, applyCrest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintWarning(string msg)
|
void PrintWarning(string msg)
|
||||||
|
|
@ -362,21 +380,23 @@ public class DesignBase
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
{
|
{
|
||||||
var (id, stain, apply, applyStain) = ParseItem(slot, equip[slot.ToString()]);
|
var (id, stain, crest, apply, applyStain, applyCrest) = ParseItem(slot, equip[slot.ToString()]);
|
||||||
|
|
||||||
PrintWarning(items.ValidateItem(slot, id, out var item, allowUnknown));
|
PrintWarning(items.ValidateItem(slot, id, out var item, allowUnknown));
|
||||||
PrintWarning(items.ValidateStain(stain, out stain, allowUnknown));
|
PrintWarning(items.ValidateStain(stain, out stain, allowUnknown));
|
||||||
design._designData.SetItem(slot, item);
|
design._designData.SetItem(slot, item);
|
||||||
design._designData.SetStain(slot, stain);
|
design._designData.SetStain(slot, stain);
|
||||||
|
design._designData.SetCrest(slot, crest);
|
||||||
design.SetApplyEquip(slot, apply);
|
design.SetApplyEquip(slot, apply);
|
||||||
design.SetApplyStain(slot, applyStain);
|
design.SetApplyStain(slot, applyStain);
|
||||||
|
design.SetApplyCrest(slot, applyCrest);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var (id, stain, apply, applyStain) = ParseItem(EquipSlot.MainHand, equip[EquipSlot.MainHand.ToString()]);
|
var (id, stain, crest, apply, applyStain, applyCrest) = ParseItem(EquipSlot.MainHand, equip[EquipSlot.MainHand.ToString()]);
|
||||||
if (id == ItemManager.NothingId(EquipSlot.MainHand))
|
if (id == ItemManager.NothingId(EquipSlot.MainHand))
|
||||||
id = items.DefaultSword.ItemId;
|
id = items.DefaultSword.ItemId;
|
||||||
var (idOff, stainOff, applyOff, applyStainOff) = ParseItem(EquipSlot.OffHand, equip[EquipSlot.OffHand.ToString()]);
|
var (idOff, stainOff, crestOff, applyOff, applyStainOff, applyCrestOff) = ParseItem(EquipSlot.OffHand, equip[EquipSlot.OffHand.ToString()]);
|
||||||
if (id == ItemManager.NothingId(EquipSlot.OffHand))
|
if (id == ItemManager.NothingId(EquipSlot.OffHand))
|
||||||
id = ItemManager.NothingId(FullEquipType.Shield);
|
id = ItemManager.NothingId(FullEquipType.Shield);
|
||||||
|
|
||||||
|
|
@ -387,10 +407,14 @@ public class DesignBase
|
||||||
design._designData.SetItem(EquipSlot.OffHand, off);
|
design._designData.SetItem(EquipSlot.OffHand, off);
|
||||||
design._designData.SetStain(EquipSlot.MainHand, stain);
|
design._designData.SetStain(EquipSlot.MainHand, stain);
|
||||||
design._designData.SetStain(EquipSlot.OffHand, stainOff);
|
design._designData.SetStain(EquipSlot.OffHand, stainOff);
|
||||||
|
design._designData.SetCrest(EquipSlot.MainHand, crest);
|
||||||
|
design._designData.SetCrest(EquipSlot.OffHand, crestOff);
|
||||||
design.SetApplyEquip(EquipSlot.MainHand, apply);
|
design.SetApplyEquip(EquipSlot.MainHand, apply);
|
||||||
design.SetApplyEquip(EquipSlot.OffHand, applyOff);
|
design.SetApplyEquip(EquipSlot.OffHand, applyOff);
|
||||||
design.SetApplyStain(EquipSlot.MainHand, applyStain);
|
design.SetApplyStain(EquipSlot.MainHand, applyStain);
|
||||||
design.SetApplyStain(EquipSlot.OffHand, applyStainOff);
|
design.SetApplyStain(EquipSlot.OffHand, applyStainOff);
|
||||||
|
design.SetApplyCrest(EquipSlot.MainHand, applyCrest);
|
||||||
|
design.SetApplyCrest(EquipSlot.OffHand, applyCrestOff);
|
||||||
}
|
}
|
||||||
var metaValue = QuadBool.FromJObject(equip["Hat"], "Show", "Apply", QuadBool.NullFalse);
|
var metaValue = QuadBool.FromJObject(equip["Hat"], "Show", "Apply", QuadBool.NullFalse);
|
||||||
design.SetApplyHatVisible(metaValue.Enabled);
|
design.SetApplyHatVisible(metaValue.Enabled);
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ public unsafe struct DesignData
|
||||||
private FullEquipType _typeMainhand;
|
private FullEquipType _typeMainhand;
|
||||||
private FullEquipType _typeOffhand;
|
private FullEquipType _typeOffhand;
|
||||||
private byte _states;
|
private byte _states;
|
||||||
|
public ushort CrestVisibility;
|
||||||
public bool IsHuman = true;
|
public bool IsHuman = true;
|
||||||
|
|
||||||
public DesignData()
|
public DesignData()
|
||||||
|
|
@ -59,6 +60,15 @@ public unsafe struct DesignData
|
||||||
return index > 11 ? (StainId)0 : _equipmentBytes[4 * index + 3];
|
return index > 11 ? (StainId)0 : _equipmentBytes[4 * index + 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ushort CrestMask(EquipSlot slot)
|
||||||
|
{
|
||||||
|
var index = slot.ToIndex();
|
||||||
|
return index <= 11 ? (ushort)(1u << (int)index) : (ushort)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool Crest(EquipSlot slot)
|
||||||
|
=> (CrestVisibility & CrestMask(slot)) != 0;
|
||||||
|
|
||||||
public FullEquipType MainhandType
|
public FullEquipType MainhandType
|
||||||
=> _typeMainhand;
|
=> _typeMainhand;
|
||||||
|
|
||||||
|
|
@ -173,6 +183,16 @@ public unsafe struct DesignData
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public bool SetCrest(EquipSlot slot, bool visible)
|
||||||
|
{
|
||||||
|
var crestVisibility = CrestVisibility;
|
||||||
|
if (visible)
|
||||||
|
crestVisibility |= CrestMask(slot);
|
||||||
|
else
|
||||||
|
crestVisibility &= (ushort)~CrestMask(slot);
|
||||||
|
return SetIfDifferent(ref CrestVisibility, crestVisibility);
|
||||||
|
}
|
||||||
|
|
||||||
public readonly bool IsWet()
|
public readonly bool IsWet()
|
||||||
=> (_states & 0x01) == 0x01;
|
=> (_states & 0x01) == 0x01;
|
||||||
|
|
||||||
|
|
@ -228,12 +248,15 @@ public unsafe struct DesignData
|
||||||
{
|
{
|
||||||
SetItem(slot, ItemManager.NothingItem(slot));
|
SetItem(slot, ItemManager.NothingItem(slot));
|
||||||
SetStain(slot, 0);
|
SetStain(slot, 0);
|
||||||
|
SetCrest(slot, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetItem(EquipSlot.MainHand, items.DefaultSword);
|
SetItem(EquipSlot.MainHand, items.DefaultSword);
|
||||||
SetStain(EquipSlot.MainHand, 0);
|
SetStain(EquipSlot.MainHand, 0);
|
||||||
|
SetCrest(EquipSlot.MainHand, false);
|
||||||
SetItem(EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield));
|
SetItem(EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield));
|
||||||
SetStain(EquipSlot.OffHand, 0);
|
SetStain(EquipSlot.OffHand, 0);
|
||||||
|
SetCrest(EquipSlot.OffHand, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -446,6 +446,31 @@ public class DesignManager
|
||||||
_event.Invoke(DesignChanged.Type.ApplyStain, design, slot);
|
_event.Invoke(DesignChanged.Type.ApplyStain, design, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Change the crest visibility for any equipment piece. </summary>
|
||||||
|
public void ChangeCrest(Design design, EquipSlot slot, bool crest)
|
||||||
|
{
|
||||||
|
var oldCrest = design.DesignData.Crest(slot);
|
||||||
|
if (!design.GetDesignDataRef().SetCrest(slot, crest))
|
||||||
|
return;
|
||||||
|
|
||||||
|
design.LastEdit = DateTimeOffset.UtcNow;
|
||||||
|
_saveService.QueueSave(design);
|
||||||
|
Glamourer.Log.Debug($"Set crest visibility of {slot} equipment piece to {crest}.");
|
||||||
|
_event.Invoke(DesignChanged.Type.Crest, design, (oldCrest, crest, slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change whether to apply a specific crest visibility. </summary>
|
||||||
|
public void ChangeApplyCrest(Design design, EquipSlot slot, bool value)
|
||||||
|
{
|
||||||
|
if (!design.SetApplyCrest(slot, value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
design.LastEdit = DateTimeOffset.UtcNow;
|
||||||
|
_saveService.QueueSave(design);
|
||||||
|
Glamourer.Log.Debug($"Set applying of crest visibility of {slot} equipment piece to {value}.");
|
||||||
|
_event.Invoke(DesignChanged.Type.ApplyCrest, design, slot);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Change the bool value of one of the meta flags. </summary>
|
/// <summary> Change the bool value of one of the meta flags. </summary>
|
||||||
public void ChangeMeta(Design design, ActorState.MetaIndex metaIndex, bool value)
|
public void ChangeMeta(Design design, ActorState.MetaIndex metaIndex, bool value)
|
||||||
{
|
{
|
||||||
|
|
@ -514,6 +539,9 @@ public class DesignManager
|
||||||
|
|
||||||
if (other.DoApplyStain(slot))
|
if (other.DoApplyStain(slot))
|
||||||
ChangeStain(design, slot, other.DesignData.Stain(slot));
|
ChangeStain(design, slot, other.DesignData.Stain(slot));
|
||||||
|
|
||||||
|
if (other.DoApplyCrest(slot))
|
||||||
|
ChangeCrest(design, slot, other.DesignData.Crest(slot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -528,6 +556,12 @@ public class DesignManager
|
||||||
|
|
||||||
if (other.DoApplyStain(EquipSlot.OffHand))
|
if (other.DoApplyStain(EquipSlot.OffHand))
|
||||||
ChangeStain(design, EquipSlot.OffHand, other.DesignData.Stain(EquipSlot.OffHand));
|
ChangeStain(design, EquipSlot.OffHand, other.DesignData.Stain(EquipSlot.OffHand));
|
||||||
|
|
||||||
|
if (other.DoApplyCrest(EquipSlot.MainHand))
|
||||||
|
ChangeCrest(design, EquipSlot.MainHand, other.DesignData.Crest(EquipSlot.MainHand));
|
||||||
|
|
||||||
|
if (other.DoApplyCrest(EquipSlot.OffHand))
|
||||||
|
ChangeCrest(design, EquipSlot.OffHand, other.DesignData.Crest(EquipSlot.OffHand));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UndoDesignChange(Design design)
|
public void UndoDesignChange(Design design)
|
||||||
|
|
|
||||||
34
Glamourer/Events/CrestVisibilityUpdating.cs
Normal file
34
Glamourer/Events/CrestVisibilityUpdating.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
using Glamourer.Interop.Structs;
|
||||||
|
using OtterGui.Classes;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
|
||||||
|
namespace Glamourer.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggered when the crest visibility is updated on a model.
|
||||||
|
/// <list type="number">
|
||||||
|
/// <item>Parameter is the model with an update. </item>
|
||||||
|
/// <item>Parameter is the equipment slot changed. </item>
|
||||||
|
/// <item>Parameter is the whether the crest will be shown. </item>
|
||||||
|
/// </list>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CrestVisibilityUpdating : EventWrapper<Action<Model, EquipSlot, Ref<bool>>, CrestVisibilityUpdating.Priority>
|
||||||
|
{
|
||||||
|
public enum Priority
|
||||||
|
{
|
||||||
|
/// <seealso cref="State.StateListener.OnCrestVisibilityUpdating"/>
|
||||||
|
StateListener = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
public CrestVisibilityUpdating()
|
||||||
|
: base(nameof(CrestVisibilityUpdating))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public void Invoke(Model model, EquipSlot slot, ref bool visible)
|
||||||
|
{
|
||||||
|
var @return = new Ref<bool>(visible);
|
||||||
|
Invoke(this, model, slot, @return);
|
||||||
|
visible = @return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -62,6 +62,9 @@ public sealed class DesignChanged : EventWrapper<Action<DesignChanged.Type, Desi
|
||||||
/// <summary> An existing design had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
/// <summary> An existing design had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
||||||
Stain,
|
Stain,
|
||||||
|
|
||||||
|
/// <summary> An existing design had a crest visibility changed. Data is the old crest visibility, the new crest visibility and the slot [(bool, bool, EquipSlot)]. </summary>
|
||||||
|
Crest,
|
||||||
|
|
||||||
/// <summary> An existing design changed whether a specific customization is applied. Data is the type of customization [CustomizeIndex]. </summary>
|
/// <summary> An existing design changed whether a specific customization is applied. Data is the type of customization [CustomizeIndex]. </summary>
|
||||||
ApplyCustomize,
|
ApplyCustomize,
|
||||||
|
|
||||||
|
|
@ -71,6 +74,9 @@ public sealed class DesignChanged : EventWrapper<Action<DesignChanged.Type, Desi
|
||||||
/// <summary> An existing design changed whether a specific stain is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
/// <summary> An existing design changed whether a specific stain is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||||
ApplyStain,
|
ApplyStain,
|
||||||
|
|
||||||
|
/// <summary> An existing design changed whether a specific crest visibility is applied. Data is the slot of the equipment [EquipSlot]. </summary>
|
||||||
|
ApplyCrest,
|
||||||
|
|
||||||
/// <summary> An existing design changed its write protection status. Data is the new value [bool]. </summary>
|
/// <summary> An existing design changed its write protection status. Data is the new value [bool]. </summary>
|
||||||
WriteProtection,
|
WriteProtection,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ public sealed class StateChanged : EventWrapper<Action<StateChanged.Type, StateC
|
||||||
/// <summary> A characters saved state had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
/// <summary> A characters saved state had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
||||||
Stain,
|
Stain,
|
||||||
|
|
||||||
|
/// <summary> A characters saved state had a crest visibility changed. Data is the old crest visibility, the new crest visibility and the slot [(bool, bool, EquipSlot)]. </summary>
|
||||||
|
Crest,
|
||||||
|
|
||||||
/// <summary> A characters saved state had a design applied. This means everything may have changed. Data is the applied design. [DesignBase] </summary>
|
/// <summary> A characters saved state had a design applied. This means everything may have changed. Data is the applied design. [DesignBase] </summary>
|
||||||
Design,
|
Design,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using Glamourer.Structs;
|
||||||
using Glamourer.Unlocks;
|
using Glamourer.Unlocks;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
|
using OtterGui.Classes;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
@ -86,13 +87,13 @@ public class EquipmentDrawer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public DataChange DrawEquip(EquipSlot slot, in DesignData designData, out EquipItem rArmor, out StainId rStain, EquipFlag? cApply,
|
public DataChange DrawEquip(EquipSlot slot, in DesignData designData, out EquipItem rArmor, out StainId rStain, out bool rCrest, EquipFlag? cApply,
|
||||||
out bool rApply, out bool rApplyStain, bool locked)
|
out bool rApply, out bool rApplyStain, out bool rApplyCrest, bool locked)
|
||||||
=> DrawEquip(slot, designData.Item(slot), out rArmor, designData.Stain(slot), out rStain, cApply, out rApply, out rApplyStain, locked,
|
=> DrawEquip(slot, designData.Item(slot), out rArmor, designData.Stain(slot), out rStain, designData.Crest(slot), out rCrest, cApply, out rApply, out rApplyStain, out rApplyCrest, locked,
|
||||||
designData.Customize.Gender, designData.Customize.Race);
|
designData.Customize.Gender, designData.Customize.Race);
|
||||||
|
|
||||||
public DataChange DrawEquip(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, EquipFlag? cApply,
|
public DataChange DrawEquip(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, bool cCrest, out bool rCrest, EquipFlag? cApply,
|
||||||
out bool rApply, out bool rApplyStain, bool locked, Gender gender = Gender.Unknown, Race race = Race.Unknown)
|
out bool rApply, out bool rApplyStain, out bool rApplyCrest, bool locked, Gender gender = Gender.Unknown, Race race = Race.Unknown)
|
||||||
{
|
{
|
||||||
if (_config.HideApplyCheckmarks)
|
if (_config.HideApplyCheckmarks)
|
||||||
cApply = null;
|
cApply = null;
|
||||||
|
|
@ -102,24 +103,26 @@ public class EquipmentDrawer
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
|
||||||
|
|
||||||
if (_config.SmallEquip)
|
if (_config.SmallEquip)
|
||||||
return DrawEquipSmall(slot, cArmor, out rArmor, cStain, out rStain, cApply, out rApply, out rApplyStain, locked, gender, race);
|
return DrawEquipSmall(slot, cArmor, out rArmor, cStain, out rStain, cCrest, out rCrest, cApply, out rApply, out rApplyStain, out rApplyCrest, locked, gender, race);
|
||||||
|
|
||||||
if (!locked && _codes.EnabledArtisan)
|
if (!locked && _codes.EnabledArtisan)
|
||||||
return DrawEquipArtisan(slot, cArmor, out rArmor, cStain, out rStain, cApply, out rApply, out rApplyStain);
|
return DrawEquipArtisan(slot, cArmor, out rArmor, cStain, out rStain, cCrest, out rCrest, cApply, out rApply, out rApplyStain, out rApplyCrest);
|
||||||
|
|
||||||
return DrawEquipNormal(slot, cArmor, out rArmor, cStain, out rStain, cApply, out rApply, out rApplyStain, locked, gender, race);
|
return DrawEquipNormal(slot, cArmor, out rArmor, cStain, out rStain, cCrest, out rCrest, cApply, out rApply, out rApplyStain, out rApplyCrest, locked, gender, race);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataChange DrawWeapons(in DesignData designData, out EquipItem rMainhand, out EquipItem rOffhand, out StainId rMainhandStain,
|
public DataChange DrawWeapons(in DesignData designData, out EquipItem rMainhand, out EquipItem rOffhand, out StainId rMainhandStain,
|
||||||
out StainId rOffhandStain, EquipFlag? cApply, bool allWeapons, out bool rApplyMainhand, out bool rApplyMainhandStain,
|
out StainId rOffhandStain, out bool rMainhandCrest, out bool rOffhandCrest, EquipFlag? cApply, bool allWeapons, out bool rApplyMainhand, out bool rApplyMainhandStain,
|
||||||
out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked)
|
out bool rApplyMainhandCrest, out bool rApplyOffhand, out bool rApplyOffhandStain, out bool rApplyOffhandCrest, bool locked)
|
||||||
=> DrawWeapons(designData.Item(EquipSlot.MainHand), out rMainhand, designData.Item(EquipSlot.OffHand), out rOffhand,
|
=> DrawWeapons(designData.Item(EquipSlot.MainHand), out rMainhand, designData.Item(EquipSlot.OffHand), out rOffhand,
|
||||||
designData.Stain(EquipSlot.MainHand), out rMainhandStain, designData.Stain(EquipSlot.OffHand), out rOffhandStain, cApply,
|
designData.Stain(EquipSlot.MainHand), out rMainhandStain, designData.Stain(EquipSlot.OffHand), out rOffhandStain,
|
||||||
allWeapons, out rApplyMainhand, out rApplyMainhandStain, out rApplyOffhand, out rApplyOffhandStain, locked);
|
designData.Crest(EquipSlot.MainHand), out rMainhandCrest, designData.Crest(EquipSlot.OffHand), out rOffhandCrest, cApply,
|
||||||
|
allWeapons, out rApplyMainhand, out rApplyMainhandStain, out rApplyMainhandCrest, out rApplyOffhand, out rApplyOffhandStain, out rApplyOffhandCrest, locked);
|
||||||
|
|
||||||
private DataChange DrawWeapons(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
private DataChange DrawWeapons(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
||||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain, EquipFlag? cApply,
|
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||||
bool allWeapons, out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain,
|
bool cMainhandCrest, out bool rMainhandCrest, bool cOffhandCrest, out bool rOffhandCrest, EquipFlag? cApply,
|
||||||
|
bool allWeapons, out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyMainhandCrest, out bool rApplyOffhand, out bool rApplyOffhandStain, out bool rApplyOffhandCrest,
|
||||||
bool locked)
|
bool locked)
|
||||||
{
|
{
|
||||||
if (cMainhand.ModelId.Id == 0)
|
if (cMainhand.ModelId.Id == 0)
|
||||||
|
|
@ -128,10 +131,14 @@ public class EquipmentDrawer
|
||||||
rMainhand = cMainhand;
|
rMainhand = cMainhand;
|
||||||
rMainhandStain = cMainhandStain;
|
rMainhandStain = cMainhandStain;
|
||||||
rOffhandStain = cOffhandStain;
|
rOffhandStain = cOffhandStain;
|
||||||
|
rMainhandCrest = cMainhandCrest;
|
||||||
|
rOffhandCrest = cOffhandCrest;
|
||||||
rApplyMainhand = false;
|
rApplyMainhand = false;
|
||||||
rApplyMainhandStain = false;
|
rApplyMainhandStain = false;
|
||||||
|
rApplyMainhandCrest = false;
|
||||||
rApplyOffhand = false;
|
rApplyOffhand = false;
|
||||||
rApplyOffhandStain = false;
|
rApplyOffhandStain = false;
|
||||||
|
rApplyOffhandCrest = false;
|
||||||
return DataChange.None;
|
return DataChange.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,15 +151,18 @@ public class EquipmentDrawer
|
||||||
|
|
||||||
if (_config.SmallEquip)
|
if (_config.SmallEquip)
|
||||||
return DrawWeaponsSmall(cMainhand, out rMainhand, cOffhand, out rOffhand, cMainhandStain, out rMainhandStain, cOffhandStain,
|
return DrawWeaponsSmall(cMainhand, out rMainhand, cOffhand, out rOffhand, cMainhandStain, out rMainhandStain, cOffhandStain,
|
||||||
out rOffhandStain, cApply, out rApplyMainhand, out rApplyMainhandStain, out rApplyOffhand, out rApplyOffhandStain, locked,
|
out rOffhandStain, cMainhandCrest, out rMainhandCrest, cOffhandCrest, out rOffhandCrest,
|
||||||
|
cApply, out rApplyMainhand, out rApplyMainhandStain, out rApplyMainhandCrest, out rApplyOffhand, out rApplyOffhandStain, out rApplyOffhandCrest, locked,
|
||||||
allWeapons);
|
allWeapons);
|
||||||
|
|
||||||
if (!locked && _codes.EnabledArtisan)
|
if (!locked && _codes.EnabledArtisan)
|
||||||
return DrawWeaponsArtisan(cMainhand, out rMainhand, cOffhand, out rOffhand, cMainhandStain, out rMainhandStain, cOffhandStain,
|
return DrawWeaponsArtisan(cMainhand, out rMainhand, cOffhand, out rOffhand, cMainhandStain, out rMainhandStain, cOffhandStain,
|
||||||
out rOffhandStain, cApply, out rApplyMainhand, out rApplyMainhandStain, out rApplyOffhand, out rApplyOffhandStain);
|
out rOffhandStain, cMainhandCrest, out rMainhandCrest, cOffhandCrest, out rOffhandCrest,
|
||||||
|
cApply, out rApplyMainhand, out rApplyMainhandStain, out rApplyMainhandCrest, out rApplyOffhand, out rApplyOffhandStain, out rApplyOffhandCrest);
|
||||||
|
|
||||||
return DrawWeaponsNormal(cMainhand, out rMainhand, cOffhand, out rOffhand, cMainhandStain, out rMainhandStain, cOffhandStain,
|
return DrawWeaponsNormal(cMainhand, out rMainhand, cOffhand, out rOffhand, cMainhandStain, out rMainhandStain, cOffhandStain,
|
||||||
out rOffhandStain, cApply, out rApplyMainhand, out rApplyMainhandStain, out rApplyOffhand, out rApplyOffhandStain, locked,
|
out rOffhandStain, cMainhandCrest, out rMainhandCrest, cOffhandCrest, out rOffhandCrest,
|
||||||
|
cApply, out rApplyMainhand, out rApplyMainhandStain, out rApplyMainhandCrest, out rApplyOffhand, out rApplyOffhandStain, out rApplyOffhandCrest, locked,
|
||||||
allWeapons);
|
allWeapons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,6 +341,49 @@ public class EquipmentDrawer
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static bool CanHaveCrest(EquipSlot slot)
|
||||||
|
=> slot is EquipSlot.OffHand or EquipSlot.Head or EquipSlot.Body;
|
||||||
|
|
||||||
|
private DataChange DrawCrest(EquipSlot slot, bool current, EquipFlag? cApply, out bool ret, out bool rApply, bool locked, bool small, bool change2)
|
||||||
|
{
|
||||||
|
var changes = DataChange.None;
|
||||||
|
if (cApply.HasValue)
|
||||||
|
{
|
||||||
|
var apply = cApply.Value.HasFlag(slot.ToCrestFlag());
|
||||||
|
var flags = (sbyte)(apply ? current ? 1 : -1 : 0);
|
||||||
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing);
|
||||||
|
using (var disabled = ImRaii.Disabled(locked))
|
||||||
|
{
|
||||||
|
if (new TristateCheckbox(ColorId.TriStateCross.Value(), ColorId.TriStateCheck.Value(), ColorId.TriStateNeutral.Value()).Draw($"##crest{slot}", flags, out flags))
|
||||||
|
{
|
||||||
|
(ret, rApply) = flags switch
|
||||||
|
{
|
||||||
|
-1 => (false, true),
|
||||||
|
0 => (current, false),
|
||||||
|
_ => (true, true),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = current;
|
||||||
|
rApply = apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGuiUtil.HoverTooltip($"Display your Free Company crest on this equipment.\n\nThis attribute will be {(apply ? current ? "enabled." : "disabled." : "kept as is.")}");
|
||||||
|
if (ret != current)
|
||||||
|
changes |= change2 ? DataChange.Crest2 : DataChange.Crest;
|
||||||
|
if (rApply != apply)
|
||||||
|
changes |= change2 ? DataChange.ApplyCrest2 : DataChange.ApplyCrest;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rApply = false;
|
||||||
|
if (UiHelpers.DrawCheckbox($"##crest{slot}", "Display your Free Company crest on this equipment.", current, out ret, locked))
|
||||||
|
changes |= change2 ? DataChange.Crest2 : DataChange.Crest;
|
||||||
|
}
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Draw an input for armor that can set arbitrary values instead of choosing items. </summary>
|
/// <summary> Draw an input for armor that can set arbitrary values instead of choosing items. </summary>
|
||||||
private bool DrawArmorArtisan(EquipSlot slot, EquipItem current, out EquipItem armor)
|
private bool DrawArmorArtisan(EquipSlot slot, EquipItem current, out EquipItem armor)
|
||||||
{
|
{
|
||||||
|
|
@ -383,8 +436,8 @@ public class EquipmentDrawer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataChange DrawEquipArtisan(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
private DataChange DrawEquipArtisan(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, bool cCrest, out bool rCrest,
|
||||||
EquipFlag? cApply, out bool rApply, out bool rApplyStain)
|
EquipFlag? cApply, out bool rApply, out bool rApplyStain, out bool rApplyCrest)
|
||||||
{
|
{
|
||||||
var changes = DataChange.None;
|
var changes = DataChange.None;
|
||||||
if (DrawStainArtisan(slot, cStain, out rStain))
|
if (DrawStainArtisan(slot, cStain, out rStain))
|
||||||
|
|
@ -392,6 +445,16 @@ public class EquipmentDrawer
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawArmorArtisan(slot, cArmor, out rArmor))
|
if (DrawArmorArtisan(slot, cArmor, out rArmor))
|
||||||
changes |= DataChange.Item;
|
changes |= DataChange.Item;
|
||||||
|
if (CanHaveCrest(slot))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
changes |= DrawCrest(slot, cCrest, cApply, out rCrest, out rApplyCrest, false, true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rCrest = cCrest;
|
||||||
|
rApplyCrest = false;
|
||||||
|
}
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -410,8 +473,8 @@ public class EquipmentDrawer
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataChange DrawEquipSmall(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
private DataChange DrawEquipSmall(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, bool cCrest, out bool rCrest,
|
||||||
EquipFlag? cApply, out bool rApply, out bool rApplyStain, bool locked, Gender gender, Race race)
|
EquipFlag? cApply, out bool rApply, out bool rApplyStain, out bool rApplyCrest, bool locked, Gender gender, Race race)
|
||||||
{
|
{
|
||||||
var changes = DataChange.None;
|
var changes = DataChange.None;
|
||||||
if (DrawStain(slot, cStain, out rStain, locked, true))
|
if (DrawStain(slot, cStain, out rStain, locked, true))
|
||||||
|
|
@ -419,6 +482,16 @@ public class EquipmentDrawer
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawItem(slot, cArmor, out rArmor, out var label, locked, true, false, false))
|
if (DrawItem(slot, cArmor, out rArmor, out var label, locked, true, false, false))
|
||||||
changes |= DataChange.Item;
|
changes |= DataChange.Item;
|
||||||
|
if (CanHaveCrest(slot))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
changes |= DrawCrest(slot, cCrest, cApply, out rCrest, out rApplyCrest, locked, true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rCrest = cCrest;
|
||||||
|
rApplyCrest = false;
|
||||||
|
}
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -443,8 +516,8 @@ public class EquipmentDrawer
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataChange DrawEquipNormal(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
private DataChange DrawEquipNormal(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, bool cCrest, out bool rCrest,
|
||||||
EquipFlag? cApply, out bool rApply, out bool rApplyStain, bool locked, Gender gender, Race race)
|
EquipFlag? cApply, out bool rApply, out bool rApplyStain, out bool rApplyCrest, bool locked, Gender gender, Race race)
|
||||||
{
|
{
|
||||||
var changes = DataChange.None;
|
var changes = DataChange.None;
|
||||||
cArmor.DrawIcon(_textures, _iconSize, slot);
|
cArmor.DrawIcon(_textures, _iconSize, slot);
|
||||||
|
|
@ -479,6 +552,16 @@ public class EquipmentDrawer
|
||||||
{
|
{
|
||||||
rApplyStain = true;
|
rApplyStain = true;
|
||||||
}
|
}
|
||||||
|
if (CanHaveCrest(slot))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
changes |= DrawCrest(slot, cCrest, cApply, out rCrest, out rApplyCrest, locked, false, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rCrest = cCrest;
|
||||||
|
rApplyCrest = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (VerifyRestrictedGear(slot, rArmor, gender, race))
|
if (VerifyRestrictedGear(slot, rArmor, gender, race))
|
||||||
{
|
{
|
||||||
|
|
@ -506,8 +589,9 @@ public class EquipmentDrawer
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataChange DrawWeaponsSmall(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
private DataChange DrawWeaponsSmall(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
||||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain, EquipFlag? cApply,
|
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||||
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked,
|
bool cMainhandCrest, out bool rMainhandCrest, bool cOffhandCrest, out bool rOffhandCrest, EquipFlag? cApply,
|
||||||
|
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyMainhandCrest, out bool rApplyOffhand, out bool rApplyOffhandStain, out bool rApplyOffhandCrest, bool locked,
|
||||||
bool allWeapons)
|
bool allWeapons)
|
||||||
{
|
{
|
||||||
var changes = DataChange.None;
|
var changes = DataChange.None;
|
||||||
|
|
@ -526,6 +610,16 @@ public class EquipmentDrawer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CanHaveCrest(EquipSlot.MainHand))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
changes |= DrawCrest(EquipSlot.MainHand, cMainhandCrest, cApply, out rMainhandCrest, out rApplyMainhandCrest, locked, false, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rMainhandCrest = cMainhandCrest;
|
||||||
|
rApplyMainhandCrest = true;
|
||||||
|
}
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -548,8 +642,10 @@ public class EquipmentDrawer
|
||||||
if (rOffhand.Type is FullEquipType.Unknown)
|
if (rOffhand.Type is FullEquipType.Unknown)
|
||||||
{
|
{
|
||||||
rOffhandStain = cOffhandStain;
|
rOffhandStain = cOffhandStain;
|
||||||
|
rOffhandCrest = cOffhandCrest;
|
||||||
rApplyOffhand = false;
|
rApplyOffhand = false;
|
||||||
rApplyOffhandStain = false;
|
rApplyOffhandStain = false;
|
||||||
|
rApplyOffhandCrest = false;
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -559,6 +655,17 @@ public class EquipmentDrawer
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawOffhand(rMainhand, rOffhand, out rOffhand, out var offhandLabel, locked, true, false, false))
|
if (DrawOffhand(rMainhand, rOffhand, out rOffhand, out var offhandLabel, locked, true, false, false))
|
||||||
changes |= DataChange.Item2;
|
changes |= DataChange.Item2;
|
||||||
|
|
||||||
|
if (CanHaveCrest(EquipSlot.OffHand))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
changes |= DrawCrest(EquipSlot.OffHand, cOffhandCrest, cApply, out rOffhandCrest, out rApplyOffhandCrest, locked, false, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rOffhandCrest = cOffhandCrest;
|
||||||
|
rApplyOffhandCrest = true;
|
||||||
|
}
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
@ -580,8 +687,9 @@ public class EquipmentDrawer
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataChange DrawWeaponsNormal(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
private DataChange DrawWeaponsNormal(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
||||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain, EquipFlag? cApply,
|
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||||
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked,
|
bool cMainhandCrest, out bool rMainhandCrest, bool cOffhandCrest, out bool rOffhandCrest, EquipFlag? cApply,
|
||||||
|
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyMainhandCrest, out bool rApplyOffhand, out bool rApplyOffhandStain, out bool rApplyOffhandCrest, bool locked,
|
||||||
bool allWeapons)
|
bool allWeapons)
|
||||||
{
|
{
|
||||||
var changes = DataChange.None;
|
var changes = DataChange.None;
|
||||||
|
|
@ -630,13 +738,26 @@ public class EquipmentDrawer
|
||||||
{
|
{
|
||||||
rApplyMainhandStain = true;
|
rApplyMainhandStain = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CanHaveCrest(EquipSlot.MainHand))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
changes |= DrawCrest(EquipSlot.MainHand, cMainhandCrest, cApply, out rMainhandCrest, out rApplyMainhandCrest, locked, false, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rMainhandCrest = cMainhandCrest;
|
||||||
|
rApplyMainhandCrest = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rOffhand.Type is FullEquipType.Unknown)
|
if (rOffhand.Type is FullEquipType.Unknown)
|
||||||
{
|
{
|
||||||
rOffhandStain = cOffhandStain;
|
rOffhandStain = cOffhandStain;
|
||||||
|
rOffhandCrest = cOffhandCrest;
|
||||||
rApplyOffhand = false;
|
rApplyOffhand = false;
|
||||||
rApplyOffhandStain = false;
|
rApplyOffhandStain = false;
|
||||||
|
rApplyOffhandCrest = false;
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -673,19 +794,31 @@ public class EquipmentDrawer
|
||||||
{
|
{
|
||||||
rApplyOffhandStain = true;
|
rApplyOffhandStain = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CanHaveCrest(EquipSlot.OffHand))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
changes |= DrawCrest(EquipSlot.OffHand, cOffhandCrest, cApply, out rOffhandCrest, out rApplyOffhandCrest, locked, false, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rOffhandCrest = cOffhandCrest;
|
||||||
|
rApplyOffhandCrest = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataChange DrawWeaponsArtisan(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
private DataChange DrawWeaponsArtisan(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
||||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain, EquipFlag? cApply,
|
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||||
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain)
|
bool cMainhandCrest, out bool rMainhandCrest, bool cOffhandCrest, out bool rOffhandCrest, EquipFlag? cApply,
|
||||||
|
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyMainhandCrest, out bool rApplyOffhand, out bool rApplyOffhandStain, out bool rApplyOffhandCrest)
|
||||||
{
|
{
|
||||||
rApplyMainhand = (cApply ?? 0).HasFlag(EquipFlag.Mainhand);
|
rApplyMainhand = (cApply ?? 0).HasFlag(EquipFlag.Mainhand);
|
||||||
rApplyMainhandStain = (cApply ?? 0).HasFlag(EquipFlag.MainhandStain);
|
rApplyMainhandStain = (cApply ?? 0).HasFlag(EquipFlag.MainhandStain);
|
||||||
rApplyOffhand = (cApply ?? 0).HasFlag(EquipFlag.Offhand);
|
rApplyOffhand = (cApply ?? 0).HasFlag(EquipFlag.Offhand);
|
||||||
rApplyOffhandStain = (cApply ?? 0).HasFlag(EquipFlag.MainhandStain);
|
rApplyOffhandStain = (cApply ?? 0).HasFlag(EquipFlag.OffhandStain);
|
||||||
|
|
||||||
bool DrawWeapon(EquipItem current, out EquipItem ret)
|
bool DrawWeapon(EquipItem current, out EquipItem ret)
|
||||||
{
|
{
|
||||||
|
|
@ -741,15 +874,35 @@ public class EquipmentDrawer
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawWeapon(cMainhand, out rMainhand))
|
if (DrawWeapon(cMainhand, out rMainhand))
|
||||||
ret |= DataChange.Item;
|
ret |= DataChange.Item;
|
||||||
|
if (CanHaveCrest(EquipSlot.MainHand))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ret |= DrawCrest(EquipSlot.MainHand, cMainhandCrest, cApply, out rMainhandCrest, out rApplyMainhandCrest, false, true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rMainhandCrest = cMainhandCrest;
|
||||||
|
rApplyMainhandCrest = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var id = ImRaii.PushId(1))
|
using (var id = ImRaii.PushId(1))
|
||||||
{
|
{
|
||||||
if (DrawStainArtisan(EquipSlot.OffHand, cOffhandStain, out rOffhandStain))
|
if (DrawStainArtisan(EquipSlot.OffHand, cOffhandStain, out rOffhandStain))
|
||||||
ret |= DataChange.Stain;
|
ret |= DataChange.Stain2;
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawWeapon(cOffhand, out rOffhand))
|
if (DrawWeapon(cOffhand, out rOffhand))
|
||||||
ret |= DataChange.Item;
|
ret |= DataChange.Item2;
|
||||||
|
if (CanHaveCrest(EquipSlot.OffHand))
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ret |= DrawCrest(EquipSlot.OffHand, cOffhandCrest, cApply, out rOffhandCrest, out rApplyOffhandCrest, false, true, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rOffhandCrest = cOffhandCrest;
|
||||||
|
rApplyOffhandCrest = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ public class ActorPanel
|
||||||
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _state!.IsLocked);
|
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _state!.IsLocked);
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
{
|
{
|
||||||
var changes = _equipmentDrawer.DrawEquip(slot, _state!.ModelData, out var newArmor, out var newStain, null, out _, out _,
|
var changes = _equipmentDrawer.DrawEquip(slot, _state!.ModelData, out var newArmor, out var newStain, out var newCrest, null, out _, out _, out _,
|
||||||
_state.IsLocked);
|
_state.IsLocked);
|
||||||
if (usedAllStain)
|
if (usedAllStain)
|
||||||
{
|
{
|
||||||
|
|
@ -192,14 +192,20 @@ public class ActorPanel
|
||||||
case DataChange.Stain:
|
case DataChange.Stain:
|
||||||
_stateManager.ChangeStain(_state, slot, newStain, StateChanged.Source.Manual);
|
_stateManager.ChangeStain(_state, slot, newStain, StateChanged.Source.Manual);
|
||||||
break;
|
break;
|
||||||
|
case DataChange.Crest:
|
||||||
|
_stateManager.ChangeCrest(_state, slot, newCrest, StateChanged.Source.Manual);
|
||||||
|
break;
|
||||||
case DataChange.Item | DataChange.Stain:
|
case DataChange.Item | DataChange.Stain:
|
||||||
_stateManager.ChangeEquip(_state, slot, newArmor, newStain, StateChanged.Source.Manual);
|
case DataChange.Item | DataChange.Crest:
|
||||||
|
case DataChange.Stain | DataChange.Crest:
|
||||||
|
case DataChange.Item | DataChange.Stain | DataChange.Crest:
|
||||||
|
_stateManager.ChangeEquip(_state, slot, changes.HasFlag(DataChange.Item) ? newArmor : null, changes.HasFlag(DataChange.Stain) ? newStain : null, changes.HasFlag(DataChange.Crest) ? newCrest : null, StateChanged.Source.Manual);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var weaponChanges = _equipmentDrawer.DrawWeapons(_state!.ModelData, out var newMainhand, out var newOffhand, out var newMainhandStain,
|
var weaponChanges = _equipmentDrawer.DrawWeapons(_state!.ModelData, out var newMainhand, out var newOffhand, out var newMainhandStain,
|
||||||
out var newOffhandStain, null, GameMain.IsInGPose(), out _, out _, out _, out _, _state.IsLocked);
|
out var newOffhandStain, out var newMainhandCrest, out var newOffhandCrest, null, GameMain.IsInGPose(), out _, out _, out _, out _, out _, out _, _state.IsLocked);
|
||||||
if (usedAllStain)
|
if (usedAllStain)
|
||||||
{
|
{
|
||||||
weaponChanges |= DataChange.Stain | DataChange.Stain2;
|
weaponChanges |= DataChange.Stain | DataChange.Stain2;
|
||||||
|
|
@ -208,20 +214,34 @@ public class ActorPanel
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weaponChanges.HasFlag(DataChange.Item))
|
if (weaponChanges.HasFlag(DataChange.Item))
|
||||||
if (weaponChanges.HasFlag(DataChange.Stain))
|
{
|
||||||
_stateManager.ChangeEquip(_state, EquipSlot.MainHand, newMainhand, newMainhandStain, StateChanged.Source.Manual);
|
if (weaponChanges.HasFlag(DataChange.Stain) || weaponChanges.HasFlag(DataChange.Crest))
|
||||||
|
_stateManager.ChangeEquip(_state, EquipSlot.MainHand, newMainhand, weaponChanges.HasFlag(DataChange.Stain) ? newMainhandStain : null, weaponChanges.HasFlag(DataChange.Crest) ? newMainhandCrest : null, StateChanged.Source.Manual);
|
||||||
else
|
else
|
||||||
_stateManager.ChangeItem(_state, EquipSlot.MainHand, newMainhand, StateChanged.Source.Manual);
|
_stateManager.ChangeItem(_state, EquipSlot.MainHand, newMainhand, StateChanged.Source.Manual);
|
||||||
else if (weaponChanges.HasFlag(DataChange.Stain))
|
}
|
||||||
_stateManager.ChangeStain(_state, EquipSlot.MainHand, newMainhandStain, StateChanged.Source.Manual);
|
else
|
||||||
|
{
|
||||||
|
if (weaponChanges.HasFlag(DataChange.Stain))
|
||||||
|
_stateManager.ChangeStain(_state, EquipSlot.MainHand, newMainhandStain, StateChanged.Source.Manual);
|
||||||
|
if (weaponChanges.HasFlag(DataChange.Crest))
|
||||||
|
_stateManager.ChangeCrest(_state, EquipSlot.MainHand, newMainhandCrest, StateChanged.Source.Manual);
|
||||||
|
}
|
||||||
|
|
||||||
if (weaponChanges.HasFlag(DataChange.Item2))
|
if (weaponChanges.HasFlag(DataChange.Item2))
|
||||||
if (weaponChanges.HasFlag(DataChange.Stain2))
|
{
|
||||||
_stateManager.ChangeEquip(_state, EquipSlot.OffHand, newOffhand, newOffhandStain, StateChanged.Source.Manual);
|
if (weaponChanges.HasFlag(DataChange.Stain2) || weaponChanges.HasFlag(DataChange.Crest2))
|
||||||
|
_stateManager.ChangeEquip(_state, EquipSlot.OffHand, newOffhand, weaponChanges.HasFlag(DataChange.Stain2) ? newOffhandStain : null, weaponChanges.HasFlag(DataChange.Crest2) ? newOffhandCrest : null, StateChanged.Source.Manual);
|
||||||
else
|
else
|
||||||
_stateManager.ChangeItem(_state, EquipSlot.OffHand, newOffhand, StateChanged.Source.Manual);
|
_stateManager.ChangeItem(_state, EquipSlot.OffHand, newOffhand, StateChanged.Source.Manual);
|
||||||
else if (weaponChanges.HasFlag(DataChange.Stain2))
|
}
|
||||||
_stateManager.ChangeStain(_state, EquipSlot.OffHand, newOffhandStain, StateChanged.Source.Manual);
|
else
|
||||||
|
{
|
||||||
|
if (weaponChanges.HasFlag(DataChange.Stain2))
|
||||||
|
_stateManager.ChangeStain(_state, EquipSlot.OffHand, newOffhandStain, StateChanged.Source.Manual);
|
||||||
|
if (weaponChanges.HasFlag(DataChange.Crest2))
|
||||||
|
_stateManager.ChangeCrest(_state, EquipSlot.OffHand, newOffhandCrest, StateChanged.Source.Manual);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
if (EquipmentDrawer.DrawHatState(_state!.ModelData.IsHatVisible(), out var newHatState, _state!.IsLocked))
|
if (EquipmentDrawer.DrawHatState(_state!.ModelData.IsHatVisible(), out var newHatState, _state!.IsLocked))
|
||||||
|
|
|
||||||
|
|
@ -141,8 +141,8 @@ public class SetPanel
|
||||||
{
|
{
|
||||||
var (numCheckboxes, numSpacing) = (_config.ShowAllAutomatedApplicationRules, _config.ShowUnlockedItemWarnings) switch
|
var (numCheckboxes, numSpacing) = (_config.ShowAllAutomatedApplicationRules, _config.ShowUnlockedItemWarnings) switch
|
||||||
{
|
{
|
||||||
(true, true) => (9, 14),
|
(true, true) => (10, 15),
|
||||||
(true, false) => (7, 10),
|
(true, false) => (8, 11),
|
||||||
(false, true) => (4, 4),
|
(false, true) => (4, 4),
|
||||||
(false, false) => (2, 0),
|
(false, false) => (2, 0),
|
||||||
};
|
};
|
||||||
|
|
@ -173,7 +173,7 @@ public class SetPanel
|
||||||
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthFixed, 220 * ImGuiHelpers.GlobalScale);
|
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthFixed, 220 * ImGuiHelpers.GlobalScale);
|
||||||
if (_config.ShowAllAutomatedApplicationRules)
|
if (_config.ShowAllAutomatedApplicationRules)
|
||||||
ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed,
|
ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed,
|
||||||
6 * ImGui.GetFrameHeight() + 10 * ImGuiHelpers.GlobalScale);
|
7 * ImGui.GetFrameHeight() + 11 * ImGuiHelpers.GlobalScale);
|
||||||
else
|
else
|
||||||
ImGui.TableSetupColumn("Use", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X);
|
ImGui.TableSetupColumn("Use", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X);
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,7 @@ public class SetPanel
|
||||||
ImGui.TableSetupColumn("Design / Job Restrictions", ImGuiTableColumnFlags.WidthFixed, 250 * ImGuiHelpers.GlobalScale);
|
ImGui.TableSetupColumn("Design / Job Restrictions", ImGuiTableColumnFlags.WidthFixed, 250 * ImGuiHelpers.GlobalScale);
|
||||||
if (_config.ShowAllAutomatedApplicationRules)
|
if (_config.ShowAllAutomatedApplicationRules)
|
||||||
ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed,
|
ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed,
|
||||||
3 * ImGui.GetFrameHeight() + 4 * ImGuiHelpers.GlobalScale);
|
4 * ImGui.GetFrameHeight() + 5 * ImGuiHelpers.GlobalScale);
|
||||||
else
|
else
|
||||||
ImGui.TableSetupColumn("Use", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X);
|
ImGui.TableSetupColumn("Use", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X);
|
||||||
}
|
}
|
||||||
|
|
@ -383,14 +383,16 @@ public class SetPanel
|
||||||
Box(0);
|
Box(0);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
Box(1);
|
Box(1);
|
||||||
|
ImGui.SameLine();
|
||||||
|
Box(2);
|
||||||
if (singleLine)
|
if (singleLine)
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
Box(2);
|
|
||||||
ImGui.SameLine();
|
|
||||||
Box(3);
|
Box(3);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
Box(4);
|
Box(4);
|
||||||
|
ImGui.SameLine();
|
||||||
|
Box(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
_manager.ChangeApplicationType(set, autoDesignIndex, newType);
|
_manager.ChangeApplicationType(set, autoDesignIndex, newType);
|
||||||
|
|
@ -424,6 +426,7 @@ public class SetPanel
|
||||||
(AutoDesign.Type.Armor, "Apply all armor piece changes that are enabled in this design and that are valid in a fixed design."),
|
(AutoDesign.Type.Armor, "Apply all armor piece changes that are enabled in this design and that are valid in a fixed design."),
|
||||||
(AutoDesign.Type.Accessories, "Apply all accessory changes that are enabled in this design and that are valid in a fixed design."),
|
(AutoDesign.Type.Accessories, "Apply all accessory changes that are enabled in this design and that are valid in a fixed design."),
|
||||||
(AutoDesign.Type.Stains, "Apply all dye changes that are enabled in this design."),
|
(AutoDesign.Type.Stains, "Apply all dye changes that are enabled in this design."),
|
||||||
|
(AutoDesign.Type.Crests, "Apply all crest visibility changes that are enabled in this design."),
|
||||||
(AutoDesign.Type.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
|
(AutoDesign.Type.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -335,14 +335,15 @@ public unsafe class DebugTab : ITab
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ImGui.SmallButton("Hide"))
|
if (ImGui.SmallButton("Hide"))
|
||||||
_updateSlotService.UpdateSlot(model, EquipSlot.Head, CharacterArmor.Empty);
|
_updateSlotService.UpdateSlot(model, EquipSlot.Head, CharacterArmor.Empty, null);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGui.SmallButton("Show"))
|
if (ImGui.SmallButton("Show"))
|
||||||
_updateSlotService.UpdateSlot(model, EquipSlot.Head, actor.GetArmor(EquipSlot.Head));
|
_updateSlotService.UpdateSlot(model, EquipSlot.Head, actor.GetArmor(EquipSlot.Head), actor.GetCrest(EquipSlot.Head));
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGui.SmallButton("Toggle"))
|
if (ImGui.SmallButton("Toggle"))
|
||||||
_updateSlotService.UpdateSlot(model, EquipSlot.Head,
|
_updateSlotService.UpdateSlot(model, EquipSlot.Head,
|
||||||
model.AsHuman->Head.Value == 0 ? actor.GetArmor(EquipSlot.Head) : CharacterArmor.Empty);
|
model.AsHuman->Head.Value == 0 ? actor.GetArmor(EquipSlot.Head) : CharacterArmor.Empty,
|
||||||
|
model.AsHuman->Head.Value == 0 ? actor.GetCrest(EquipSlot.Head) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawWeaponState(Actor actor, Model model)
|
private void DrawWeaponState(Actor actor, Model model)
|
||||||
|
|
@ -423,8 +424,11 @@ public unsafe class DebugTab : ITab
|
||||||
if (ImGui.SmallButton("Change Stain"))
|
if (ImGui.SmallButton("Change Stain"))
|
||||||
_updateSlotService.UpdateStain(model, slot, 5);
|
_updateSlotService.UpdateStain(model, slot, 5);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
if (ImGui.SmallButton("Toggle Crest"))
|
||||||
|
_updateSlotService.UpdateCrest(model, slot, !model.IsFreeCompanyCrestVisibleOnSlot((byte)slot.ToIndex()));
|
||||||
|
ImGui.SameLine();
|
||||||
if (ImGui.SmallButton("Reset"))
|
if (ImGui.SmallButton("Reset"))
|
||||||
_updateSlotService.UpdateSlot(model, slot, actor.GetArmor(slot));
|
_updateSlotService.UpdateSlot(model, slot, actor.GetArmor(slot), actor.GetCrest(slot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1146,7 +1150,7 @@ public unsafe class DebugTab : ITab
|
||||||
|
|
||||||
public void DrawState(ActorData data, ActorState state)
|
public void DrawState(ActorData data, ActorState state)
|
||||||
{
|
{
|
||||||
using var table = ImRaii.Table("##state", 7, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
|
using var table = ImRaii.Table("##state", 9, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
|
||||||
if (!table)
|
if (!table)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -1189,10 +1193,12 @@ public unsafe class DebugTab : ITab
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
|
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
|
||||||
{
|
{
|
||||||
PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state[slot, false]);
|
PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state[slot, ActorState.EquipField.Item]);
|
||||||
ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString());
|
ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString());
|
ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(state[slot, true].ToString());
|
ImGuiUtil.DrawTableColumn(state[slot, ActorState.EquipField.Stain].ToString());
|
||||||
|
ImGuiUtil.DrawTableColumn(state.BaseData.Crest(slot).ToString());
|
||||||
|
ImGuiUtil.DrawTableColumn(state[slot, ActorState.EquipField.Crest].ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var type in Enum.GetValues<CustomizeIndex>())
|
foreach (var type in Enum.GetValues<CustomizeIndex>())
|
||||||
|
|
@ -1307,12 +1313,16 @@ public unsafe class DebugTab : ITab
|
||||||
var apply = design.DoApplyEquip(slot);
|
var apply = design.DoApplyEquip(slot);
|
||||||
var stain = design.DesignData.Stain(slot);
|
var stain = design.DesignData.Stain(slot);
|
||||||
var applyStain = design.DoApplyStain(slot);
|
var applyStain = design.DoApplyStain(slot);
|
||||||
|
var crest = design.DesignData.Crest(slot);
|
||||||
|
var applyCrest = design.DoApplyCrest(slot);
|
||||||
ImGuiUtil.DrawTableColumn(slot.ToName());
|
ImGuiUtil.DrawTableColumn(slot.ToName());
|
||||||
ImGuiUtil.DrawTableColumn(item.Name);
|
ImGuiUtil.DrawTableColumn(item.Name);
|
||||||
ImGuiUtil.DrawTableColumn(item.ItemId.ToString());
|
ImGuiUtil.DrawTableColumn(item.ItemId.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(apply ? "Apply" : "Keep");
|
ImGuiUtil.DrawTableColumn(apply ? "Apply" : "Keep");
|
||||||
ImGuiUtil.DrawTableColumn(stain.ToString());
|
ImGuiUtil.DrawTableColumn(stain.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(applyStain ? "Apply" : "Keep");
|
ImGuiUtil.DrawTableColumn(applyStain ? "Apply" : "Keep");
|
||||||
|
ImGuiUtil.DrawTableColumn(crest.ToString());
|
||||||
|
ImGuiUtil.DrawTableColumn(applyCrest ? "Apply" : "Keep");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.DrawTableColumn("Hat Visible");
|
ImGuiUtil.DrawTableColumn("Hat Visible");
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
|
||||||
case DesignChanged.Type.ApplyCustomize:
|
case DesignChanged.Type.ApplyCustomize:
|
||||||
case DesignChanged.Type.ApplyEquip:
|
case DesignChanged.Type.ApplyEquip:
|
||||||
case DesignChanged.Type.ApplyStain:
|
case DesignChanged.Type.ApplyStain:
|
||||||
|
case DesignChanged.Type.ApplyCrest:
|
||||||
case DesignChanged.Type.Customize:
|
case DesignChanged.Type.Customize:
|
||||||
case DesignChanged.Type.Equip:
|
case DesignChanged.Type.Equip:
|
||||||
case DesignChanged.Type.ChangedColor:
|
case DesignChanged.Type.ChangedColor:
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
||||||
ObjectManager _objects, StateManager _state, EquipmentDrawer _equipmentDrawer, ModAssociationsTab _modAssociations,
|
ObjectManager _objects, StateManager _state, EquipmentDrawer _equipmentDrawer, ModAssociationsTab _modAssociations,
|
||||||
DesignDetailTab _designDetails, DesignConverter _converter, ImportService _importService, MultiDesignPanel _multiDesignPanel)
|
DesignDetailTab _designDetails, DesignConverter _converter, ImportService _importService, MultiDesignPanel _multiDesignPanel)
|
||||||
{
|
{
|
||||||
|
private static readonly EquipSlot[] CrestSlots = EquipSlotExtensions.FullSlots.Where(EquipmentDrawer.CanHaveCrest).ToArray();
|
||||||
|
|
||||||
private readonly FileDialogManager _fileDialog = new();
|
private readonly FileDialogManager _fileDialog = new();
|
||||||
|
|
||||||
private HeaderDrawer.Button LockButton()
|
private HeaderDrawer.Button LockButton()
|
||||||
|
|
@ -95,23 +97,28 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
||||||
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _selector.Selected!.WriteProtected());
|
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _selector.Selected!.WriteProtected());
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
{
|
{
|
||||||
var changes = _equipmentDrawer.DrawEquip(slot, _selector.Selected!.DesignData, out var newArmor, out var newStain,
|
var changes = _equipmentDrawer.DrawEquip(slot, _selector.Selected!.DesignData, out var newArmor, out var newStain, out var newCrest,
|
||||||
_selector.Selected.ApplyEquip, out var newApply, out var newApplyStain, _selector.Selected!.WriteProtected());
|
_selector.Selected.ApplyEquip, out var newApply, out var newApplyStain, out var newApplyCrest, _selector.Selected!.WriteProtected());
|
||||||
if (changes.HasFlag(DataChange.Item))
|
if (changes.HasFlag(DataChange.Item))
|
||||||
_manager.ChangeEquip(_selector.Selected, slot, newArmor);
|
_manager.ChangeEquip(_selector.Selected, slot, newArmor);
|
||||||
if (changes.HasFlag(DataChange.Stain))
|
if (changes.HasFlag(DataChange.Stain))
|
||||||
_manager.ChangeStain(_selector.Selected, slot, newStain);
|
_manager.ChangeStain(_selector.Selected, slot, newStain);
|
||||||
else if (usedAllStain)
|
else if (usedAllStain)
|
||||||
_manager.ChangeStain(_selector.Selected, slot, newAllStain);
|
_manager.ChangeStain(_selector.Selected, slot, newAllStain);
|
||||||
|
if (changes.HasFlag(DataChange.Crest))
|
||||||
|
_manager.ChangeCrest(_selector.Selected, slot, newCrest);
|
||||||
if (changes.HasFlag(DataChange.ApplyItem))
|
if (changes.HasFlag(DataChange.ApplyItem))
|
||||||
_manager.ChangeApplyEquip(_selector.Selected, slot, newApply);
|
_manager.ChangeApplyEquip(_selector.Selected, slot, newApply);
|
||||||
if (changes.HasFlag(DataChange.ApplyStain))
|
if (changes.HasFlag(DataChange.ApplyStain))
|
||||||
_manager.ChangeApplyStain(_selector.Selected, slot, newApplyStain);
|
_manager.ChangeApplyStain(_selector.Selected, slot, newApplyStain);
|
||||||
|
if (changes.HasFlag(DataChange.ApplyCrest))
|
||||||
|
_manager.ChangeApplyCrest(_selector.Selected, slot, newApplyCrest);
|
||||||
}
|
}
|
||||||
|
|
||||||
var weaponChanges = _equipmentDrawer.DrawWeapons(_selector.Selected!.DesignData, out var newMainhand, out var newOffhand,
|
var weaponChanges = _equipmentDrawer.DrawWeapons(_selector.Selected!.DesignData, out var newMainhand, out var newOffhand,
|
||||||
out var newMainhandStain, out var newOffhandStain, _selector.Selected.ApplyEquip, true, out var applyMain, out var applyMainStain,
|
out var newMainhandStain, out var newOffhandStain, out var newMainhandCrest, out var newOffhandCrest,
|
||||||
out var applyOff, out var applyOffStain, _selector.Selected!.WriteProtected());
|
_selector.Selected.ApplyEquip, true, out var applyMain, out var applyMainStain, out var applyMainCrest,
|
||||||
|
out var applyOff, out var applyOffStain, out var applyOffCrest, _selector.Selected!.WriteProtected());
|
||||||
|
|
||||||
if (weaponChanges.HasFlag(DataChange.Item))
|
if (weaponChanges.HasFlag(DataChange.Item))
|
||||||
_manager.ChangeWeapon(_selector.Selected, EquipSlot.MainHand, newMainhand);
|
_manager.ChangeWeapon(_selector.Selected, EquipSlot.MainHand, newMainhand);
|
||||||
|
|
@ -119,20 +126,28 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
||||||
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newMainhandStain);
|
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newMainhandStain);
|
||||||
else if (usedAllStain)
|
else if (usedAllStain)
|
||||||
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newAllStain);
|
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newAllStain);
|
||||||
|
if (weaponChanges.HasFlag(DataChange.Crest))
|
||||||
|
_manager.ChangeCrest(_selector.Selected, EquipSlot.MainHand, newMainhandCrest);
|
||||||
if (weaponChanges.HasFlag(DataChange.ApplyItem))
|
if (weaponChanges.HasFlag(DataChange.ApplyItem))
|
||||||
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.MainHand, applyMain);
|
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.MainHand, applyMain);
|
||||||
if (weaponChanges.HasFlag(DataChange.ApplyStain))
|
if (weaponChanges.HasFlag(DataChange.ApplyStain))
|
||||||
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.MainHand, applyMainStain);
|
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.MainHand, applyMainStain);
|
||||||
|
if (weaponChanges.HasFlag(DataChange.ApplyCrest))
|
||||||
|
_manager.ChangeApplyCrest(_selector.Selected, EquipSlot.MainHand, applyMainCrest);
|
||||||
if (weaponChanges.HasFlag(DataChange.Item2))
|
if (weaponChanges.HasFlag(DataChange.Item2))
|
||||||
_manager.ChangeWeapon(_selector.Selected, EquipSlot.OffHand, newOffhand);
|
_manager.ChangeWeapon(_selector.Selected, EquipSlot.OffHand, newOffhand);
|
||||||
if (weaponChanges.HasFlag(DataChange.Stain2))
|
if (weaponChanges.HasFlag(DataChange.Stain2))
|
||||||
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newOffhandStain);
|
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newOffhandStain);
|
||||||
else if (usedAllStain)
|
else if (usedAllStain)
|
||||||
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newAllStain);
|
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newAllStain);
|
||||||
|
if (weaponChanges.HasFlag(DataChange.Crest2))
|
||||||
|
_manager.ChangeCrest(_selector.Selected, EquipSlot.OffHand, newOffhandCrest);
|
||||||
if (weaponChanges.HasFlag(DataChange.ApplyItem2))
|
if (weaponChanges.HasFlag(DataChange.ApplyItem2))
|
||||||
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.OffHand, applyOff);
|
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.OffHand, applyOff);
|
||||||
if (weaponChanges.HasFlag(DataChange.ApplyStain2))
|
if (weaponChanges.HasFlag(DataChange.ApplyStain2))
|
||||||
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.OffHand, applyOffStain);
|
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.OffHand, applyOffStain);
|
||||||
|
if (weaponChanges.HasFlag(DataChange.ApplyCrest2))
|
||||||
|
_manager.ChangeApplyCrest(_selector.Selected, EquipSlot.OffHand, applyOffCrest);
|
||||||
|
|
||||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
DrawEquipmentMetaToggles();
|
DrawEquipmentMetaToggles();
|
||||||
|
|
@ -223,43 +238,59 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
||||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X / 2);
|
ImGui.SameLine(ImGui.GetContentRegionAvail().X / 2);
|
||||||
using (var _ = ImRaii.Group())
|
using (var _ = ImRaii.Group())
|
||||||
{
|
{
|
||||||
void ApplyEquip(string label, EquipFlag allFlags, bool stain, IEnumerable<EquipSlot> slots)
|
void ApplyEquip(string label, EquipFlag allFlags, ActorState.EquipField equipField, IEnumerable<EquipSlot> slots)
|
||||||
{
|
{
|
||||||
var flags = (uint)(allFlags & _selector.Selected!.ApplyEquip);
|
// The flags we may edit with a single "big change" checkbox will always fit in a uint, but the whole bitfield doesn't
|
||||||
|
var shift = (int)equipField;
|
||||||
|
var flags = (uint)((ulong)(allFlags & _selector.Selected!.ApplyEquip) >> shift);
|
||||||
|
|
||||||
var bigChange = ImGui.CheckboxFlags($"Apply All {label}", ref flags, (uint)allFlags);
|
var bigChange = ImGui.CheckboxFlags($"Apply All {label}", ref flags, (uint)((ulong)allFlags >> shift));
|
||||||
if (stain)
|
|
||||||
foreach (var slot in slots)
|
var adjustedFlags = (EquipFlag)((ulong)flags << shift);
|
||||||
{
|
switch (equipField)
|
||||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToStainFlag()) : _selector.Selected!.DoApplyStain(slot);
|
{
|
||||||
if (ImGui.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange)
|
case ActorState.EquipField.Stain:
|
||||||
_manager.ChangeApplyStain(_selector.Selected!, slot, apply);
|
foreach (var slot in slots)
|
||||||
}
|
{
|
||||||
else
|
var apply = bigChange ? adjustedFlags.HasFlag(slot.ToStainFlag()) : _selector.Selected!.DoApplyStain(slot);
|
||||||
foreach (var slot in slots)
|
if (ImGui.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange)
|
||||||
{
|
_manager.ChangeApplyStain(_selector.Selected!, slot, apply);
|
||||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToFlag()) : _selector.Selected!.DoApplyEquip(slot);
|
}
|
||||||
if (ImGui.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange)
|
break;
|
||||||
_manager.ChangeApplyEquip(_selector.Selected!, slot, apply);
|
case ActorState.EquipField.Crest:
|
||||||
}
|
foreach (var slot in slots)
|
||||||
|
{
|
||||||
|
var apply = bigChange ? adjustedFlags.HasFlag(slot.ToCrestFlag()) : _selector.Selected!.DoApplyCrest(slot);
|
||||||
|
if (ImGui.Checkbox($"Apply {slot.ToName()} Crest Visibility", ref apply) || bigChange)
|
||||||
|
_manager.ChangeApplyCrest(_selector.Selected!, slot, apply);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
foreach (var slot in slots)
|
||||||
|
{
|
||||||
|
var apply = bigChange ? adjustedFlags.HasFlag(slot.ToFlag()) : _selector.Selected!.DoApplyEquip(slot);
|
||||||
|
if (ImGui.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange)
|
||||||
|
_manager.ChangeApplyEquip(_selector.Selected!, slot, apply);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyEquip("Weapons", AutoDesign.WeaponFlags, false, new[]
|
ApplyEquip("Weapons", AutoDesign.WeaponFlags, ActorState.EquipField.Item, EquipSlotExtensions.WeaponSlots);
|
||||||
{
|
|
||||||
EquipSlot.MainHand,
|
|
||||||
EquipSlot.OffHand,
|
|
||||||
});
|
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
ApplyEquip("Armor", AutoDesign.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots);
|
ApplyEquip("Armor", AutoDesign.ArmorFlags, ActorState.EquipField.Item, EquipSlotExtensions.EquipmentSlots);
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
ApplyEquip("Accessories", AutoDesign.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots);
|
ApplyEquip("Accessories", AutoDesign.AccessoryFlags, ActorState.EquipField.Item, EquipSlotExtensions.AccessorySlots);
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
ApplyEquip("Dyes", AutoDesign.StainFlags, true,
|
ApplyEquip("Dyes", AutoDesign.StainFlags, ActorState.EquipField.Stain,
|
||||||
EquipSlotExtensions.FullSlots);
|
EquipSlotExtensions.FullSlots);
|
||||||
|
|
||||||
|
ImGui.NewLine();
|
||||||
|
ApplyEquip("Crest Visibilities", AutoDesign.RelevantCrestFlags, ActorState.EquipField.Crest, CrestSlots);
|
||||||
|
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
const uint all = 0x0Fu;
|
const uint all = 0x0Fu;
|
||||||
var flags = (_selector.Selected!.DoApplyHatVisible() ? 0x01u : 0x00)
|
var flags = (_selector.Selected!.DoApplyHatVisible() ? 0x01u : 0x00)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ using Penumbra.GameData.Structs;
|
||||||
namespace Glamourer.Gui;
|
namespace Glamourer.Gui;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum DataChange : byte
|
public enum DataChange : ushort
|
||||||
{
|
{
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
Item = 0x01,
|
Item = 0x01,
|
||||||
|
|
@ -28,6 +28,10 @@ public enum DataChange : byte
|
||||||
Stain2 = 0x20,
|
Stain2 = 0x20,
|
||||||
ApplyItem2 = 0x40,
|
ApplyItem2 = 0x40,
|
||||||
ApplyStain2 = 0x80,
|
ApplyStain2 = 0x80,
|
||||||
|
Crest = 0x100,
|
||||||
|
ApplyCrest = 0x200,
|
||||||
|
Crest2 = 0x400,
|
||||||
|
ApplyCrest2 = 0x800,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UiHelpers
|
public static class UiHelpers
|
||||||
|
|
|
||||||
|
|
@ -119,14 +119,14 @@ public class ContextMenuService : IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var slot = item.Type.ToSlot();
|
var slot = item.Type.ToSlot();
|
||||||
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual);
|
_state.ChangeEquip(state, slot, item, 0, false, StateChanged.Source.Manual);
|
||||||
if (item.Type.ValidOffhand().IsOffhandType())
|
if (item.Type.ValidOffhand().IsOffhandType())
|
||||||
{
|
{
|
||||||
if (item.ModelId.Id is > 1600 and < 1651
|
if (item.ModelId.Id is > 1600 and < 1651
|
||||||
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
||||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual);
|
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, false, StateChanged.Source.Manual);
|
||||||
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
||||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual);
|
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, false, StateChanged.Source.Manual);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -143,14 +143,14 @@ public class ContextMenuService : IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var slot = item.Type.ToSlot();
|
var slot = item.Type.ToSlot();
|
||||||
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual);
|
_state.ChangeEquip(state, slot, item, 0, false, StateChanged.Source.Manual);
|
||||||
if (item.Type.ValidOffhand().IsOffhandType())
|
if (item.Type.ValidOffhand().IsOffhandType())
|
||||||
{
|
{
|
||||||
if (item.ModelId.Id is > 1600 and < 1651
|
if (item.ModelId.Id is > 1600 and < 1651
|
||||||
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
|
||||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual);
|
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, false, StateChanged.Source.Manual);
|
||||||
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
|
||||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual);
|
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, false, StateChanged.Source.Manual);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,21 @@ public readonly unsafe struct Actor : IEquatable<Actor>
|
||||||
public CharacterArmor GetArmor(EquipSlot slot)
|
public CharacterArmor GetArmor(EquipSlot slot)
|
||||||
=> ((CharacterArmor*)&AsCharacter->DrawData.Head)[slot.ToIndex()];
|
=> ((CharacterArmor*)&AsCharacter->DrawData.Head)[slot.ToIndex()];
|
||||||
|
|
||||||
|
public bool GetCrest(EquipSlot slot)
|
||||||
|
=> (GetFreeCompanyCrestBitfield() & CrestMask(slot)) != 0;
|
||||||
|
|
||||||
|
private static byte CrestMask(EquipSlot slot)
|
||||||
|
=> slot switch
|
||||||
|
{
|
||||||
|
EquipSlot.OffHand => 0x01,
|
||||||
|
EquipSlot.Head => 0x02,
|
||||||
|
EquipSlot.Body => 0x04,
|
||||||
|
EquipSlot.Hands => 0x08,
|
||||||
|
EquipSlot.Legs => 0x10,
|
||||||
|
EquipSlot.Feet => 0x20,
|
||||||
|
_ => 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
public CharacterWeapon GetMainhand()
|
public CharacterWeapon GetMainhand()
|
||||||
=> new(AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).ModelId.Value);
|
=> new(AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).ModelId.Value);
|
||||||
|
|
||||||
|
|
@ -115,6 +130,10 @@ public readonly unsafe struct Actor : IEquatable<Actor>
|
||||||
public Customize GetCustomize()
|
public Customize GetCustomize()
|
||||||
=> *(Customize*)&AsCharacter->DrawData.CustomizeData;
|
=> *(Customize*)&AsCharacter->DrawData.CustomizeData;
|
||||||
|
|
||||||
|
// TODO remove this when available in ClientStructs
|
||||||
|
public byte GetFreeCompanyCrestBitfield()
|
||||||
|
=> ((byte*)Address)[0x1BBB];
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> $"0x{Address:X}";
|
=> $"0x{Address:X}";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,9 @@ public readonly unsafe struct Model : IEquatable<Model>
|
||||||
public CharacterArmor GetArmor(EquipSlot slot)
|
public CharacterArmor GetArmor(EquipSlot slot)
|
||||||
=> ((CharacterArmor*)&AsHuman->Head)[slot.ToIndex()];
|
=> ((CharacterArmor*)&AsHuman->Head)[slot.ToIndex()];
|
||||||
|
|
||||||
|
public bool GetCrest(EquipSlot slot)
|
||||||
|
=> IsFreeCompanyCrestVisibleOnSlot((byte)slot.ToIndex());
|
||||||
|
|
||||||
public Customize GetCustomize()
|
public Customize GetCustomize()
|
||||||
=> *(Customize*)&AsHuman->Customize;
|
=> *(Customize*)&AsHuman->Customize;
|
||||||
|
|
||||||
|
|
@ -195,6 +198,27 @@ public readonly unsafe struct Model : IEquatable<Model>
|
||||||
return discriminator1 == 0 && discriminator2 != 0 ? (second, first) : (first, second);
|
return discriminator1 == 0 && discriminator2 != 0 ? (second, first) : (first, second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO remove these when available in ClientStructs
|
||||||
|
public bool IsFreeCompanyCrestVisibleOnSlot(byte slot)
|
||||||
|
{
|
||||||
|
if (!IsCharacterBase)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var characterBase = AsCharacterBase;
|
||||||
|
var getter = (delegate* unmanaged<CharacterBase*, byte, byte>)((nint*)characterBase->VTable)[95];
|
||||||
|
return getter(characterBase, slot) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFreeCompanyCrestVisibleOnSlot(byte slot, bool visible)
|
||||||
|
{
|
||||||
|
if (!IsCharacterBase)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var characterBase = AsCharacterBase;
|
||||||
|
var setter = (delegate* unmanaged<CharacterBase*, byte, byte, void>)((nint*)characterBase->VTable)[96];
|
||||||
|
setter(characterBase, slot, visible ? (byte)1 : (byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> $"0x{Address:X}";
|
=> $"0x{Address:X}";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
|
|
@ -6,47 +8,78 @@ using Glamourer.Events;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
using PenumbraSigs = Penumbra.GameData.Sigs;
|
||||||
|
|
||||||
namespace Glamourer.Interop;
|
namespace Glamourer.Interop;
|
||||||
|
|
||||||
public unsafe class UpdateSlotService : IDisposable
|
public unsafe class UpdateSlotService : IDisposable
|
||||||
{
|
{
|
||||||
public readonly SlotUpdating SlotUpdatingEvent;
|
public readonly SlotUpdating SlotUpdatingEvent;
|
||||||
|
public readonly CrestVisibilityUpdating CrestVisibilityUpdatingEvent;
|
||||||
|
private readonly ThreadLocal<uint> _crestVisibilityUpdate = new(() => 0, false);
|
||||||
|
|
||||||
public UpdateSlotService(SlotUpdating slotUpdating, IGameInteropProvider interop)
|
public UpdateSlotService(SlotUpdating slotUpdating, CrestVisibilityUpdating crestVisibilityUpdating, IGameInteropProvider interop)
|
||||||
{
|
{
|
||||||
SlotUpdatingEvent = slotUpdating;
|
SlotUpdatingEvent = slotUpdating;
|
||||||
|
CrestVisibilityUpdatingEvent = crestVisibilityUpdating;
|
||||||
interop.InitializeFromAttributes(this);
|
interop.InitializeFromAttributes(this);
|
||||||
|
_humanSetFreeCompanyCrestVisibleOnSlot = interop.HookFromAddress<SetCrestDelegateIntern>(_humanVTable[96], HumanSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||||
|
_weaponSetFreeCompanyCrestVisibleOnSlot = interop.HookFromAddress<SetCrestDelegateIntern>(_weaponVTable[96], WeaponSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||||
_flagSlotForUpdateHook.Enable();
|
_flagSlotForUpdateHook.Enable();
|
||||||
|
_humanSetFreeCompanyCrestVisibleOnSlot.Enable();
|
||||||
|
_weaponSetFreeCompanyCrestVisibleOnSlot.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_flagSlotForUpdateHook.Dispose();
|
_flagSlotForUpdateHook.Dispose();
|
||||||
|
_humanSetFreeCompanyCrestVisibleOnSlot.Dispose();
|
||||||
|
_weaponSetFreeCompanyCrestVisibleOnSlot.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateSlot(Model drawObject, EquipSlot slot, CharacterArmor data)
|
public void UpdateSlot(Model drawObject, EquipSlot slot, CharacterArmor data, bool? crest)
|
||||||
{
|
{
|
||||||
if (!drawObject.IsCharacterBase)
|
if (!drawObject.IsCharacterBase)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FlagSlotForUpdateInterop(drawObject, slot, data);
|
FlagSlotForUpdateInterop(drawObject, slot, data);
|
||||||
|
if (crest.HasValue)
|
||||||
|
{
|
||||||
|
using var _ = EnterCrestVisibilityUpdate();
|
||||||
|
drawObject.SetFreeCompanyCrestVisibleOnSlot((byte)slot.ToIndex(), crest.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor, StainId stain)
|
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor, StainId stain, bool? crest)
|
||||||
=> UpdateSlot(drawObject, slot, armor.With(stain));
|
=> UpdateSlot(drawObject, slot, armor.With(stain), crest);
|
||||||
|
|
||||||
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
||||||
=> UpdateArmor(drawObject, slot, armor, drawObject.GetArmor(slot).Stain);
|
=> UpdateArmor(drawObject, slot, armor, drawObject.GetArmor(slot).Stain, null);
|
||||||
|
|
||||||
public void UpdateStain(Model drawObject, EquipSlot slot, StainId stain)
|
public void UpdateStain(Model drawObject, EquipSlot slot, StainId stain)
|
||||||
=> UpdateArmor(drawObject, slot, drawObject.GetArmor(slot), stain);
|
=> UpdateArmor(drawObject, slot, drawObject.GetArmor(slot), stain, null);
|
||||||
|
|
||||||
|
public void UpdateCrest(Model drawObject, EquipSlot slot, bool crest)
|
||||||
|
{
|
||||||
|
using var _ = EnterCrestVisibilityUpdate();
|
||||||
|
drawObject.SetFreeCompanyCrestVisibleOnSlot((byte)slot.ToIndex(), crest);
|
||||||
|
}
|
||||||
|
|
||||||
private delegate ulong FlagSlotForUpdateDelegateIntern(nint drawObject, uint slot, CharacterArmor* data);
|
private delegate ulong FlagSlotForUpdateDelegateIntern(nint drawObject, uint slot, CharacterArmor* data);
|
||||||
|
private delegate void SetCrestDelegateIntern(nint drawObject, byte slot, byte visible);
|
||||||
|
|
||||||
[Signature(Sigs.FlagSlotForUpdate, DetourName = nameof(FlagSlotForUpdateDetour))]
|
[Signature(Sigs.FlagSlotForUpdate, DetourName = nameof(FlagSlotForUpdateDetour))]
|
||||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagSlotForUpdateHook = null!;
|
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagSlotForUpdateHook = null!;
|
||||||
|
|
||||||
|
[Signature(PenumbraSigs.HumanVTable, ScanType = ScanType.StaticAddress)]
|
||||||
|
private readonly nint* _humanVTable = null!;
|
||||||
|
|
||||||
|
[Signature(PenumbraSigs.WeaponVTable, ScanType = ScanType.StaticAddress)]
|
||||||
|
private readonly nint* _weaponVTable = null!;
|
||||||
|
|
||||||
|
private readonly Hook<SetCrestDelegateIntern> _humanSetFreeCompanyCrestVisibleOnSlot;
|
||||||
|
private readonly Hook<SetCrestDelegateIntern> _weaponSetFreeCompanyCrestVisibleOnSlot;
|
||||||
|
|
||||||
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||||
{
|
{
|
||||||
var slot = slotIdx.ToEquipSlot();
|
var slot = slotIdx.ToEquipSlot();
|
||||||
|
|
@ -56,6 +89,52 @@ public unsafe class UpdateSlotService : IDisposable
|
||||||
return returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
return returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HumanSetFreeCompanyCrestVisibleOnSlotDetour(nint drawObject, byte slotIdx, byte visible)
|
||||||
|
{
|
||||||
|
var slot = ((uint)slotIdx).ToEquipSlot();
|
||||||
|
var rVisible = visible != 0;
|
||||||
|
var inUpdate = _crestVisibilityUpdate.IsValueCreated && _crestVisibilityUpdate.Value > 0;
|
||||||
|
if (!inUpdate)
|
||||||
|
CrestVisibilityUpdatingEvent.Invoke(drawObject, slot, ref rVisible);
|
||||||
|
Glamourer.Log.Excessive($"[Human.SetFreeCompanyCrestVisibleOnSlot] Called with 0x{drawObject:X} for slot {slot} with {rVisible} (original: {visible != 0}, in update: {inUpdate}).");
|
||||||
|
_humanSetFreeCompanyCrestVisibleOnSlot.Original(drawObject, slotIdx, rVisible ? (byte)1 : (byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WeaponSetFreeCompanyCrestVisibleOnSlotDetour(nint drawObject, byte slotIdx, byte visible)
|
||||||
|
{
|
||||||
|
var rVisible = visible != 0;
|
||||||
|
var inUpdate = _crestVisibilityUpdate.IsValueCreated && _crestVisibilityUpdate.Value > 0;
|
||||||
|
if (!inUpdate)
|
||||||
|
CrestVisibilityUpdatingEvent.Invoke(drawObject, EquipSlot.BothHand, ref rVisible);
|
||||||
|
Glamourer.Log.Excessive($"[Weapon.SetFreeCompanyCrestVisibleOnSlot] Called with 0x{drawObject:X} with {rVisible} (original: {visible != 0}, in update: {inUpdate}).");
|
||||||
|
_weaponSetFreeCompanyCrestVisibleOnSlot.Original(drawObject, slotIdx, rVisible ? (byte)1 : (byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
||||||
=> _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
=> _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Temporarily disables the crest update hooks on the current thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns> A struct that will undo this operation when disposed. Best used with: <code>using (var _ = updateSlotService.EnterCrestVisibilityUpdate()) { ... }</code> </returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public CrestVisibilityUpdateRaii EnterCrestVisibilityUpdate()
|
||||||
|
=> new(this);
|
||||||
|
|
||||||
|
public readonly ref struct CrestVisibilityUpdateRaii
|
||||||
|
{
|
||||||
|
private readonly ThreadLocal<uint> _crestVisibilityUpdate;
|
||||||
|
|
||||||
|
public CrestVisibilityUpdateRaii(UpdateSlotService parent)
|
||||||
|
{
|
||||||
|
_crestVisibilityUpdate = parent._crestVisibilityUpdate;
|
||||||
|
++_crestVisibilityUpdate.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public readonly void Dispose()
|
||||||
|
{
|
||||||
|
--_crestVisibilityUpdate.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ namespace Glamourer.Interop;
|
||||||
public unsafe class WeaponService : IDisposable
|
public unsafe class WeaponService : IDisposable
|
||||||
{
|
{
|
||||||
private readonly WeaponLoading _event;
|
private readonly WeaponLoading _event;
|
||||||
|
private readonly UpdateSlotService _updateSlotService;
|
||||||
private readonly ThreadLocal<bool> _inUpdate = new(() => false);
|
private readonly ThreadLocal<bool> _inUpdate = new(() => false);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,9 +21,10 @@ public unsafe class WeaponService : IDisposable
|
||||||
_original;
|
_original;
|
||||||
|
|
||||||
|
|
||||||
public WeaponService(WeaponLoading @event, IGameInteropProvider interop)
|
public WeaponService(WeaponLoading @event, UpdateSlotService updateSlotService, IGameInteropProvider interop)
|
||||||
{
|
{
|
||||||
_event = @event;
|
_event = @event;
|
||||||
|
_updateSlotService = updateSlotService;
|
||||||
_loadWeaponHook =
|
_loadWeaponHook =
|
||||||
interop.HookFromAddress<LoadWeaponDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadWeapon, LoadWeaponDetour);
|
interop.HookFromAddress<LoadWeaponDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadWeapon, LoadWeaponDetour);
|
||||||
_original =
|
_original =
|
||||||
|
|
@ -83,24 +85,39 @@ public unsafe class WeaponService : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a specific weapon for a character by its data and slot.
|
// Load a specific weapon for a character by its data and slot.
|
||||||
public void LoadWeapon(Actor character, EquipSlot slot, CharacterWeapon weapon)
|
public void LoadWeapon(Actor character, EquipSlot slot, CharacterWeapon weapon, bool? crest)
|
||||||
{
|
{
|
||||||
switch (slot)
|
switch (slot)
|
||||||
{
|
{
|
||||||
case EquipSlot.MainHand:
|
case EquipSlot.MainHand:
|
||||||
_inUpdate.Value = true;
|
_inUpdate.Value = true;
|
||||||
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0);
|
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0);
|
||||||
|
if (crest.HasValue)
|
||||||
|
{
|
||||||
|
using var _ = _updateSlotService.EnterCrestVisibilityUpdate();
|
||||||
|
character.Model.GetMainhand().Address.SetFreeCompanyCrestVisibleOnSlot(0, crest.Value);
|
||||||
|
}
|
||||||
_inUpdate.Value = false;
|
_inUpdate.Value = false;
|
||||||
return;
|
return;
|
||||||
case EquipSlot.OffHand:
|
case EquipSlot.OffHand:
|
||||||
_inUpdate.Value = true;
|
_inUpdate.Value = true;
|
||||||
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 1, weapon.Value, 1, 0, 1, 0);
|
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 1, weapon.Value, 1, 0, 1, 0);
|
||||||
|
if (crest.HasValue)
|
||||||
|
{
|
||||||
|
using var _ = _updateSlotService.EnterCrestVisibilityUpdate();
|
||||||
|
character.Model.GetOffhand().Address.SetFreeCompanyCrestVisibleOnSlot(0, crest.Value);
|
||||||
|
}
|
||||||
_inUpdate.Value = false;
|
_inUpdate.Value = false;
|
||||||
return;
|
return;
|
||||||
case EquipSlot.BothHand:
|
case EquipSlot.BothHand:
|
||||||
_inUpdate.Value = true;
|
_inUpdate.Value = true;
|
||||||
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0);
|
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 0, weapon.Value, 1, 0, 1, 0);
|
||||||
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 1, CharacterWeapon.Empty.Value, 1, 0, 1, 0);
|
_loadWeaponHook.Original(&character.AsCharacter->DrawData, 1, CharacterWeapon.Empty.Value, 1, 0, 1, 0);
|
||||||
|
if (crest.HasValue)
|
||||||
|
{
|
||||||
|
using var _ = _updateSlotService.EnterCrestVisibilityUpdate();
|
||||||
|
character.Model.GetMainhand().Address.SetFreeCompanyCrestVisibleOnSlot(0, crest.Value);
|
||||||
|
}
|
||||||
_inUpdate.Value = false;
|
_inUpdate.Value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +129,21 @@ public unsafe class WeaponService : IDisposable
|
||||||
var (_, _, mh, oh) = mdl.GetWeapons(character);
|
var (_, _, mh, oh) = mdl.GetWeapons(character);
|
||||||
var value = slot == EquipSlot.OffHand ? oh : mh;
|
var value = slot == EquipSlot.OffHand ? oh : mh;
|
||||||
var weapon = value.With(value.Set.Id == 0 ? 0 : stain);
|
var weapon = value.With(value.Set.Id == 0 ? 0 : stain);
|
||||||
LoadWeapon(character, slot, weapon);
|
LoadWeapon(character, slot, weapon, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateCrest(Actor character, EquipSlot slot, bool visible)
|
||||||
|
{
|
||||||
|
using var _ = _updateSlotService.EnterCrestVisibilityUpdate();
|
||||||
|
switch (slot)
|
||||||
|
{
|
||||||
|
case EquipSlot.MainHand:
|
||||||
|
case EquipSlot.BothHand:
|
||||||
|
character.Model.GetMainhand().Address.SetFreeCompanyCrestVisibleOnSlot(0, visible);
|
||||||
|
break;
|
||||||
|
case EquipSlot.OffHand:
|
||||||
|
character.Model.GetOffhand().Address.SetFreeCompanyCrestVisibleOnSlot(0, visible);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,8 +165,9 @@ public class CommandService : IDisposable
|
||||||
.AddInitialPurple("Customizations, ")
|
.AddInitialPurple("Customizations, ")
|
||||||
.AddInitialPurple("Equipment, ")
|
.AddInitialPurple("Equipment, ")
|
||||||
.AddInitialPurple("Accessories, ")
|
.AddInitialPurple("Accessories, ")
|
||||||
.AddInitialPurple("Dyes and ")
|
.AddInitialPurple("Dyes, ")
|
||||||
.AddInitialPurple("Weapons, where ").AddPurple("CEADW")
|
.AddInitialPurple("Free company crest visibilities and ")
|
||||||
|
.AddInitialPurple("Weapons, where ").AddPurple("CEADFW")
|
||||||
.AddText(" means everything should be toggled on, and no value means nothing should be toggled on.")
|
.AddText(" means everything should be toggled on, and no value means nothing should be toggled on.")
|
||||||
.BuiltString);
|
.BuiltString);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -270,6 +271,9 @@ public class CommandService : IDisposable
|
||||||
case 'd':
|
case 'd':
|
||||||
applicationFlags |= AutoDesign.Type.Stains;
|
applicationFlags |= AutoDesign.Type.Stains;
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
applicationFlags |= AutoDesign.Type.Crests;
|
||||||
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
applicationFlags |= AutoDesign.Type.Weapons;
|
applicationFlags |= AutoDesign.Type.Weapons;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ public static class ServiceManager
|
||||||
private static IServiceCollection AddEvents(this IServiceCollection services)
|
private static IServiceCollection AddEvents(this IServiceCollection services)
|
||||||
=> services.AddSingleton<VisorStateChanged>()
|
=> services.AddSingleton<VisorStateChanged>()
|
||||||
.AddSingleton<SlotUpdating>()
|
.AddSingleton<SlotUpdating>()
|
||||||
|
.AddSingleton<CrestVisibilityUpdating>()
|
||||||
.AddSingleton<DesignChanged>()
|
.AddSingleton<DesignChanged>()
|
||||||
.AddSingleton<AutomationChanged>()
|
.AddSingleton<AutomationChanged>()
|
||||||
.AddSingleton<StateChanged>()
|
.AddSingleton<StateChanged>()
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,13 @@ public class ActorState
|
||||||
ModelId,
|
ModelId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum EquipField
|
||||||
|
{
|
||||||
|
Item = 0,
|
||||||
|
Stain = 12,
|
||||||
|
Crest = 24,
|
||||||
|
}
|
||||||
|
|
||||||
public readonly ActorIdentifier Identifier;
|
public readonly ActorIdentifier Identifier;
|
||||||
|
|
||||||
public bool AllowsRedraw(ICondition condition)
|
public bool AllowsRedraw(ICondition condition)
|
||||||
|
|
@ -85,8 +92,8 @@ public class ActorState
|
||||||
internal ActorState(ActorIdentifier identifier)
|
internal ActorState(ActorIdentifier identifier)
|
||||||
=> Identifier = identifier.CreatePermanent();
|
=> Identifier = identifier.CreatePermanent();
|
||||||
|
|
||||||
public ref StateChanged.Source this[EquipSlot slot, bool stain]
|
public ref StateChanged.Source this[EquipSlot slot, EquipField field]
|
||||||
=> ref _sources[slot.ToIndex() + (stain ? EquipFlagExtensions.NumEquipFlags / 2 : 0)];
|
=> ref _sources[slot.ToIndex() + (int)field];
|
||||||
|
|
||||||
public ref StateChanged.Source this[CustomizeIndex type]
|
public ref StateChanged.Source this[CustomizeIndex type]
|
||||||
=> ref _sources[EquipFlagExtensions.NumEquipFlags + (int)type];
|
=> ref _sources[EquipFlagExtensions.NumEquipFlags + (int)type];
|
||||||
|
|
|
||||||
|
|
@ -99,11 +99,11 @@ public class StateApplier
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change a single piece of armor and/or stain depending on slot.
|
/// Change a single piece of armor and/or stain and/or crest visibility depending on slot.
|
||||||
/// This uses the current customization of the model to potentially prevent restricted gear types from appearing.
|
/// This uses the current customization of the model to potentially prevent restricted gear types from appearing.
|
||||||
/// This never requires redrawing.
|
/// This never requires redrawing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ChangeArmor(ActorData data, EquipSlot slot, CharacterArmor armor, bool checkRestrictions, bool isHatVisible = true)
|
public void ChangeArmor(ActorData data, EquipSlot slot, CharacterArmor armor, bool crest, bool checkRestrictions, bool isHatVisible = true)
|
||||||
{
|
{
|
||||||
if (slot is EquipSlot.Head && !isHatVisible)
|
if (slot is EquipSlot.Head && !isHatVisible)
|
||||||
return;
|
return;
|
||||||
|
|
@ -118,11 +118,11 @@ public class StateApplier
|
||||||
{
|
{
|
||||||
var customize = mdl.GetCustomize();
|
var customize = mdl.GetCustomize();
|
||||||
var (_, resolvedItem) = _items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender);
|
var (_, resolvedItem) = _items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender);
|
||||||
_updateSlot.UpdateSlot(actor.Model, slot, resolvedItem);
|
_updateSlot.UpdateSlot(actor.Model, slot, resolvedItem, crest);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_updateSlot.UpdateSlot(actor.Model, slot, armor);
|
_updateSlot.UpdateSlot(actor.Model, slot, armor, crest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +133,7 @@ public class StateApplier
|
||||||
// If the source is not IPC we do not want to apply restrictions.
|
// If the source is not IPC we do not want to apply restrictions.
|
||||||
var data = GetData(state);
|
var data = GetData(state);
|
||||||
if (apply)
|
if (apply)
|
||||||
ChangeArmor(data, slot, state.ModelData.Armor(slot), state[slot, false] is not StateChanged.Source.Ipc,
|
ChangeArmor(data, slot, state.ModelData.Armor(slot), state.ModelData.Crest(slot), state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Ipc,
|
||||||
state.ModelData.IsHatVisible());
|
state.ModelData.IsHatVisible());
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
@ -175,13 +175,47 @@ public class StateApplier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change the crest visibility of a single piece of armor or weapon.
|
||||||
|
/// </summary>
|
||||||
|
public void ChangeCrest(ActorData data, EquipSlot slot, bool visible)
|
||||||
|
{
|
||||||
|
var idx = slot.ToIndex();
|
||||||
|
switch (idx)
|
||||||
|
{
|
||||||
|
case < 10:
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_updateSlot.UpdateCrest(actor.Model, slot, visible);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_weapon.UpdateCrest(actor, EquipSlot.MainHand, visible);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_weapon.UpdateCrest(actor, EquipSlot.OffHand, visible);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeCrest(ActorData,EquipSlot,bool)"/>
|
||||||
|
public ActorData ChangeCrest(ActorState state, EquipSlot slot, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeCrest(data, slot, state.ModelData.Crest(slot));
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary> Apply a weapon to the appropriate slot. </summary>
|
/// <summary> Apply a weapon to the appropriate slot. </summary>
|
||||||
public void ChangeWeapon(ActorData data, EquipSlot slot, EquipItem item, StainId stain)
|
public void ChangeWeapon(ActorData data, EquipSlot slot, EquipItem item, StainId stain, bool crest)
|
||||||
{
|
{
|
||||||
if (slot is EquipSlot.MainHand)
|
if (slot is EquipSlot.MainHand)
|
||||||
ChangeMainhand(data, item, stain);
|
ChangeMainhand(data, item, stain, crest);
|
||||||
else
|
else
|
||||||
ChangeOffhand(data, item, stain);
|
ChangeOffhand(data, item, stain, crest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ChangeWeapon(ActorData,EquipSlot,EquipItem,StainId)"/>
|
/// <inheritdoc cref="ChangeWeapon(ActorData,EquipSlot,EquipItem,StainId)"/>
|
||||||
|
|
@ -192,7 +226,7 @@ public class StateApplier
|
||||||
data = data.OnlyGPose();
|
data = data.OnlyGPose();
|
||||||
|
|
||||||
if (apply)
|
if (apply)
|
||||||
ChangeWeapon(data, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
ChangeWeapon(data, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot), state.ModelData.Crest(slot));
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
@ -200,19 +234,19 @@ public class StateApplier
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply a weapon to the mainhand. If the weapon type has no associated offhand type, apply both.
|
/// Apply a weapon to the mainhand. If the weapon type has no associated offhand type, apply both.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ChangeMainhand(ActorData data, EquipItem weapon, StainId stain)
|
public void ChangeMainhand(ActorData data, EquipItem weapon, StainId stain, bool crest)
|
||||||
{
|
{
|
||||||
var slot = weapon.Type.ValidOffhand() == FullEquipType.Unknown ? EquipSlot.BothHand : EquipSlot.MainHand;
|
var slot = weapon.Type.ValidOffhand() == FullEquipType.Unknown ? EquipSlot.BothHand : EquipSlot.MainHand;
|
||||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stain));
|
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stain), crest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Apply a weapon to the offhand. </summary>
|
/// <summary> Apply a weapon to the offhand. </summary>
|
||||||
public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain)
|
public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain, bool crest)
|
||||||
{
|
{
|
||||||
stain = weapon.ModelId.Id == 0 ? 0 : stain;
|
stain = weapon.ModelId.Id == 0 ? 0 : stain;
|
||||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain));
|
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain), crest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the visor state of actors only on the draw object. </summary>
|
/// <summary> Change the visor state of actors only on the draw object. </summary>
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,9 @@ public class StateEditor
|
||||||
state[ActorState.MetaIndex.VisorState] = source;
|
state[ActorState.MetaIndex.VisorState] = source;
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
{
|
{
|
||||||
state[slot, true] = source;
|
state[slot, ActorState.EquipField.Stain] = source;
|
||||||
state[slot, false] = source;
|
state[slot, ActorState.EquipField.Item] = source;
|
||||||
|
state[slot, ActorState.EquipField.Crest] = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
state[CustomizeIndex.Clan] = source;
|
state[CustomizeIndex.Clan] = source;
|
||||||
|
|
@ -126,7 +127,7 @@ public class StateEditor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment without stain. </summary>
|
/// <summary> Change a single piece of equipment without stain or crest visibility. </summary>
|
||||||
public bool ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, out EquipItem oldItem, uint key = 0)
|
public bool ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, out EquipItem oldItem, uint key = 0)
|
||||||
{
|
{
|
||||||
oldItem = state.ModelData.Item(slot);
|
oldItem = state.ModelData.Item(slot);
|
||||||
|
|
@ -144,44 +145,58 @@ public class StateEditor
|
||||||
_gPose.AddActionOnLeave(() =>
|
_gPose.AddActionOnLeave(() =>
|
||||||
{
|
{
|
||||||
if (old.Type == state.BaseData.Item(slot).Type)
|
if (old.Type == state.BaseData.Item(slot).Type)
|
||||||
ChangeItem(state, slot, old, state[slot, false], out _, key);
|
ChangeItem(state, slot, old, state[slot, ActorState.EquipField.Item], out _, key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
state.ModelData.SetItem(slot, item);
|
state.ModelData.SetItem(slot, item);
|
||||||
state[slot, false] = source;
|
state[slot, ActorState.EquipField.Item] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
/// <summary> Change a single piece of equipment including stain and crest visibility. </summary>
|
||||||
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, out EquipItem oldItem,
|
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem? item, StainId? stain, bool? crest, StateChanged.Source source, out EquipItem oldItem,
|
||||||
out StainId oldStain, uint key = 0)
|
out StainId oldStain, out bool oldCrest, uint key = 0)
|
||||||
{
|
{
|
||||||
oldItem = state.ModelData.Item(slot);
|
oldItem = state.ModelData.Item(slot);
|
||||||
oldStain = state.ModelData.Stain(slot);
|
oldStain = state.ModelData.Stain(slot);
|
||||||
|
oldCrest = state.ModelData.Crest(slot);
|
||||||
|
|
||||||
if (!state.CanUnlock(key))
|
if (!state.CanUnlock(key))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Can not change weapon type from expected type in state.
|
// Can not change weapon type from expected type in state.
|
||||||
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
if (item.HasValue && (slot is EquipSlot.MainHand && item.Value.Type != state.BaseData.MainhandType
|
||||||
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
|
|| slot is EquipSlot.OffHand && item.Value.Type != state.BaseData.OffhandType))
|
||||||
{
|
{
|
||||||
if (!_gPose.InGPose)
|
if (!_gPose.InGPose)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var old = oldItem;
|
var old = oldItem;
|
||||||
var oldS = oldStain;
|
var oldS = oldStain;
|
||||||
|
var oldC = oldCrest;
|
||||||
_gPose.AddActionOnLeave(() =>
|
_gPose.AddActionOnLeave(() =>
|
||||||
{
|
{
|
||||||
if (old.Type == state.BaseData.Item(slot).Type)
|
if (old.Type == state.BaseData.Item(slot).Type)
|
||||||
ChangeEquip(state, slot, old, oldS, state[slot, false], out _, out _, key);
|
ChangeEquip(state, slot, old, oldS, oldC, state[slot, ActorState.EquipField.Item], out _, out _, out _, key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
state.ModelData.SetItem(slot, item);
|
if (item.HasValue)
|
||||||
state.ModelData.SetStain(slot, stain);
|
{
|
||||||
state[slot, false] = source;
|
state.ModelData.SetItem(slot, item.Value);
|
||||||
state[slot, true] = source;
|
state[slot, ActorState.EquipField.Item] = source;
|
||||||
|
}
|
||||||
|
if (stain.HasValue)
|
||||||
|
{
|
||||||
|
state.ModelData.SetStain(slot, stain.Value);
|
||||||
|
state[slot, ActorState.EquipField.Stain] = source;
|
||||||
|
}
|
||||||
|
if (crest.HasValue)
|
||||||
|
{
|
||||||
|
state.ModelData.SetCrest(slot, crest.Value);
|
||||||
|
state[slot, ActorState.EquipField.Crest] = source;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +208,19 @@ public class StateEditor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.ModelData.SetStain(slot, stain);
|
state.ModelData.SetStain(slot, stain);
|
||||||
state[slot, true] = source;
|
state[slot, ActorState.EquipField.Stain] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change only the crest visibility of an equipment piece. </summary>
|
||||||
|
public bool ChangeCrest(ActorState state, EquipSlot slot, bool crest, StateChanged.Source source, out bool oldCrest, uint key = 0)
|
||||||
|
{
|
||||||
|
oldCrest = state.ModelData.Crest(slot);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.SetCrest(slot, crest);
|
||||||
|
state[slot, ActorState.EquipField.Crest] = source;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ public class StateListener : IDisposable
|
||||||
private readonly CustomizationService _customizations;
|
private readonly CustomizationService _customizations;
|
||||||
private readonly PenumbraService _penumbra;
|
private readonly PenumbraService _penumbra;
|
||||||
private readonly SlotUpdating _slotUpdating;
|
private readonly SlotUpdating _slotUpdating;
|
||||||
|
private readonly CrestVisibilityUpdating _crestVisibilityUpdating;
|
||||||
private readonly WeaponLoading _weaponLoading;
|
private readonly WeaponLoading _weaponLoading;
|
||||||
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
||||||
private readonly VisorStateChanged _visorState;
|
private readonly VisorStateChanged _visorState;
|
||||||
|
|
@ -51,29 +52,30 @@ public class StateListener : IDisposable
|
||||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
||||||
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans,
|
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans,
|
||||||
StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose,
|
CrestVisibilityUpdating crestVisibilityUpdating, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose,
|
||||||
ChangeCustomizeService changeCustomizeService, CustomizationService customizations, ICondition condition)
|
ChangeCustomizeService changeCustomizeService, CustomizationService customizations, ICondition condition)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_items = items;
|
_items = items;
|
||||||
_penumbra = penumbra;
|
_penumbra = penumbra;
|
||||||
_actors = actors;
|
_actors = actors;
|
||||||
_config = config;
|
_config = config;
|
||||||
_slotUpdating = slotUpdating;
|
_slotUpdating = slotUpdating;
|
||||||
_weaponLoading = weaponLoading;
|
_crestVisibilityUpdating = crestVisibilityUpdating;
|
||||||
_visorState = visorState;
|
_weaponLoading = weaponLoading;
|
||||||
_weaponVisibility = weaponVisibility;
|
_visorState = visorState;
|
||||||
_headGearVisibility = headGearVisibility;
|
_weaponVisibility = weaponVisibility;
|
||||||
_autoDesignApplier = autoDesignApplier;
|
_headGearVisibility = headGearVisibility;
|
||||||
_funModule = funModule;
|
_autoDesignApplier = autoDesignApplier;
|
||||||
_humans = humans;
|
_funModule = funModule;
|
||||||
_applier = applier;
|
_humans = humans;
|
||||||
_movedEquipment = movedEquipment;
|
_applier = applier;
|
||||||
_objects = objects;
|
_movedEquipment = movedEquipment;
|
||||||
_gPose = gPose;
|
_objects = objects;
|
||||||
_changeCustomizeService = changeCustomizeService;
|
_gPose = gPose;
|
||||||
_customizations = customizations;
|
_changeCustomizeService = changeCustomizeService;
|
||||||
_condition = condition;
|
_customizations = customizations;
|
||||||
|
_condition = condition;
|
||||||
Subscribe();
|
Subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,7 +214,7 @@ public class StateListener : IDisposable
|
||||||
&& _manager.TryGetValue(identifier, out var state))
|
&& _manager.TryGetValue(identifier, out var state))
|
||||||
{
|
{
|
||||||
HandleEquipSlot(actor, state, slot, ref armor.Value);
|
HandleEquipSlot(actor, state, slot, ref armor.Value);
|
||||||
locked = state[slot, false] is StateChanged.Source.Ipc;
|
locked = state[slot, ActorState.EquipField.Item] is StateChanged.Source.Ipc;
|
||||||
}
|
}
|
||||||
|
|
||||||
_funModule.ApplyFun(actor, ref armor.Value, slot);
|
_funModule.ApplyFun(actor, ref armor.Value, slot);
|
||||||
|
|
@ -223,6 +225,34 @@ public class StateListener : IDisposable
|
||||||
(_, armor.Value) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender);
|
(_, armor.Value) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnCrestVisibilityUpdating(Model model, EquipSlot slot, Ref<bool> visible)
|
||||||
|
{
|
||||||
|
var actor = _penumbra.GameObjectFromDrawObject(model);
|
||||||
|
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (slot == EquipSlot.BothHand)
|
||||||
|
{
|
||||||
|
var rootModel = actor.Model;
|
||||||
|
var (mainHand, offHand, _, _) = rootModel.GetWeapons(actor);
|
||||||
|
if (model == mainHand)
|
||||||
|
slot = EquipSlot.MainHand;
|
||||||
|
else if (model == offHand)
|
||||||
|
slot = EquipSlot.OffHand;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Glamourer.Log.Excessive($"Cannot identify weapon slot: got model {model.Address:X}, main hand is {mainHand.Address:X}, off hand is {offHand.Address:X}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor.Identifier(_actors.AwaitedService, out var identifier)
|
||||||
|
&& _manager.TryGetValue(identifier, out var state))
|
||||||
|
{
|
||||||
|
HandleCrestVisibility(actor, state, slot, ref visible.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnMovedEquipment((EquipSlot, uint, StainId)[] items)
|
private void OnMovedEquipment((EquipSlot, uint, StainId)[] items)
|
||||||
{
|
{
|
||||||
_objects.Update();
|
_objects.Update();
|
||||||
|
|
@ -235,11 +265,12 @@ public class StateListener : IDisposable
|
||||||
var currentItem = state.BaseData.Item(slot);
|
var currentItem = state.BaseData.Item(slot);
|
||||||
var model = state.ModelData.Weapon(slot);
|
var model = state.ModelData.Weapon(slot);
|
||||||
var current = currentItem.Weapon(state.BaseData.Stain(slot));
|
var current = currentItem.Weapon(state.BaseData.Stain(slot));
|
||||||
|
var crest = state.BaseData.Crest(slot);
|
||||||
if (model.Value == current.Value || !_items.ItemService.AwaitedService.TryGetValue(item, EquipSlot.MainHand, out var changedItem))
|
if (model.Value == current.Value || !_items.ItemService.AwaitedService.TryGetValue(item, EquipSlot.MainHand, out var changedItem))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var changed = changedItem.Weapon(stain);
|
var changed = changedItem.Weapon(stain);
|
||||||
if (current.Value == changed.Value && state[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (current.Value == changed.Value && state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
{
|
{
|
||||||
_manager.ChangeItem(state, slot, currentItem, StateChanged.Source.Game);
|
_manager.ChangeItem(state, slot, currentItem, StateChanged.Source.Game);
|
||||||
_manager.ChangeStain(state, slot, current.Stain, StateChanged.Source.Game);
|
_manager.ChangeStain(state, slot, current.Stain, StateChanged.Source.Game);
|
||||||
|
|
@ -247,10 +278,10 @@ public class StateListener : IDisposable
|
||||||
{
|
{
|
||||||
case EquipSlot.MainHand:
|
case EquipSlot.MainHand:
|
||||||
case EquipSlot.OffHand:
|
case EquipSlot.OffHand:
|
||||||
_applier.ChangeWeapon(objects, slot, currentItem, stain);
|
_applier.ChangeWeapon(objects, slot, currentItem, stain, crest);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_applier.ChangeArmor(objects, slot, current.ToArmor(), state[slot, false] is not StateChanged.Source.Ipc,
|
_applier.ChangeArmor(objects, slot, current.ToArmor(), crest, state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Ipc,
|
||||||
state.ModelData.IsHatVisible());
|
state.ModelData.IsHatVisible());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -284,15 +315,20 @@ public class StateListener : IDisposable
|
||||||
// Do nothing. But this usually can not happen because the hooked function also writes to game objects later.
|
// Do nothing. But this usually can not happen because the hooked function also writes to game objects later.
|
||||||
case UpdateState.Transformed: break;
|
case UpdateState.Transformed: break;
|
||||||
case UpdateState.Change:
|
case UpdateState.Change:
|
||||||
if (state[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
if (state[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state[slot, ActorState.EquipField.Stain] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
|
if (state[slot, ActorState.EquipField.Crest] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
|
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game);
|
||||||
|
else
|
||||||
|
apply = true;
|
||||||
break;
|
break;
|
||||||
case UpdateState.NoChange:
|
case UpdateState.NoChange:
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
@ -346,6 +382,7 @@ public class StateListener : IDisposable
|
||||||
var item = _items.Identify(slot, actorArmor.Set, actorArmor.Variant);
|
var item = _items.Identify(slot, actorArmor.Set, actorArmor.Variant);
|
||||||
state.BaseData.SetItem(EquipSlot.Head, item);
|
state.BaseData.SetItem(EquipSlot.Head, item);
|
||||||
state.BaseData.SetStain(EquipSlot.Head, actorArmor.Stain);
|
state.BaseData.SetStain(EquipSlot.Head, actorArmor.Stain);
|
||||||
|
state.BaseData.SetCrest(EquipSlot.Head, actor.GetCrest(slot));
|
||||||
return UpdateState.Change;
|
return UpdateState.Change;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -374,6 +411,20 @@ public class StateListener : IDisposable
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UpdateState UpdateBaseCrest(Actor actor, ActorState state, EquipSlot slot, bool visible)
|
||||||
|
{
|
||||||
|
if (actor.IsTransformed)
|
||||||
|
return UpdateState.Transformed;
|
||||||
|
|
||||||
|
if (state.BaseData.Crest(slot) != visible)
|
||||||
|
{
|
||||||
|
state.BaseData.SetCrest(slot, visible);
|
||||||
|
return UpdateState.Change;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpdateState.NoChange;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Handle a full equip slot update for base data and model data. </summary>
|
/// <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)
|
private void HandleEquipSlot(Actor actor, ActorState state, EquipSlot slot, ref CharacterArmor armor)
|
||||||
{
|
{
|
||||||
|
|
@ -383,12 +434,12 @@ public class StateListener : IDisposable
|
||||||
// Update model state if not on fixed design.
|
// Update model state if not on fixed design.
|
||||||
case UpdateState.Change:
|
case UpdateState.Change:
|
||||||
var apply = false;
|
var apply = false;
|
||||||
if (state[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
||||||
if (state[slot, true] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
if (state[slot, ActorState.EquipField.Stain] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), StateChanged.Source.Game);
|
||||||
else
|
else
|
||||||
apply = true;
|
apply = true;
|
||||||
|
|
@ -405,6 +456,24 @@ public class StateListener : IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleCrestVisibility(Actor actor, ActorState state, EquipSlot slot, ref bool visible)
|
||||||
|
{
|
||||||
|
switch (UpdateBaseCrest(actor, state, slot, visible))
|
||||||
|
{
|
||||||
|
case UpdateState.Change:
|
||||||
|
if (state[slot, ActorState.EquipField.Crest] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||||
|
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game);
|
||||||
|
else
|
||||||
|
visible = state.ModelData.Crest(slot);
|
||||||
|
break;
|
||||||
|
case UpdateState.NoChange:
|
||||||
|
case UpdateState.HatHack:
|
||||||
|
visible = state.ModelData.Crest(slot);
|
||||||
|
break;
|
||||||
|
case UpdateState.Transformed: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Update base data for a single changed weapon slot. </summary>
|
/// <summary> Update base data for a single changed weapon slot. </summary>
|
||||||
private unsafe UpdateState UpdateBaseData(Actor actor, ActorState state, EquipSlot slot, CharacterWeapon weapon)
|
private unsafe UpdateState UpdateBaseData(Actor actor, ActorState state, EquipSlot slot, CharacterWeapon weapon)
|
||||||
{
|
{
|
||||||
|
|
@ -415,6 +484,7 @@ public class StateListener : IDisposable
|
||||||
return UpdateState.Transformed;
|
return UpdateState.Transformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var crest = actor.GetCrest(slot);
|
||||||
var baseData = state.BaseData.Weapon(slot);
|
var baseData = state.BaseData.Weapon(slot);
|
||||||
var change = UpdateState.NoChange;
|
var change = UpdateState.NoChange;
|
||||||
|
|
||||||
|
|
@ -610,6 +680,7 @@ public class StateListener : IDisposable
|
||||||
_penumbra.CreatingCharacterBase += OnCreatingCharacterBase;
|
_penumbra.CreatingCharacterBase += OnCreatingCharacterBase;
|
||||||
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
|
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
|
||||||
_slotUpdating.Subscribe(OnSlotUpdating, SlotUpdating.Priority.StateListener);
|
_slotUpdating.Subscribe(OnSlotUpdating, SlotUpdating.Priority.StateListener);
|
||||||
|
_crestVisibilityUpdating.Subscribe(OnCrestVisibilityUpdating, CrestVisibilityUpdating.Priority.StateListener);
|
||||||
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
||||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||||
|
|
@ -623,6 +694,7 @@ public class StateListener : IDisposable
|
||||||
_penumbra.CreatingCharacterBase -= OnCreatingCharacterBase;
|
_penumbra.CreatingCharacterBase -= OnCreatingCharacterBase;
|
||||||
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
|
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
|
||||||
_slotUpdating.Unsubscribe(OnSlotUpdating);
|
_slotUpdating.Unsubscribe(OnSlotUpdating);
|
||||||
|
_crestVisibilityUpdating.Unsubscribe(OnCrestVisibilityUpdating);
|
||||||
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
||||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||||
_visorState.Unsubscribe(OnVisorChange);
|
_visorState.Unsubscribe(OnVisorChange);
|
||||||
|
|
|
||||||
|
|
@ -143,10 +143,12 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
ret.Customize = model.GetCustomize();
|
ret.Customize = model.GetCustomize();
|
||||||
|
|
||||||
// We can not use the head slot data from the draw object if the hat is hidden.
|
// We can not use the head slot data from the draw object if the hat is hidden.
|
||||||
var head = ret.IsHatVisible() || ignoreHatState ? model.GetArmor(EquipSlot.Head) : actor.GetArmor(EquipSlot.Head);
|
var head = ret.IsHatVisible() || ignoreHatState ? model.GetArmor(EquipSlot.Head) : actor.GetArmor(EquipSlot.Head);
|
||||||
var headItem = _items.Identify(EquipSlot.Head, head.Set, head.Variant);
|
var headCrest = ret.IsHatVisible() || ignoreHatState ? model.GetCrest(EquipSlot.Head) : actor.GetCrest(EquipSlot.Head);
|
||||||
|
var headItem = _items.Identify(EquipSlot.Head, head.Set, head.Variant);
|
||||||
ret.SetItem(EquipSlot.Head, headItem);
|
ret.SetItem(EquipSlot.Head, headItem);
|
||||||
ret.SetStain(EquipSlot.Head, head.Stain);
|
ret.SetStain(EquipSlot.Head, head.Stain);
|
||||||
|
ret.SetCrest(EquipSlot.Head, headCrest);
|
||||||
|
|
||||||
// The other slots can be used from the draw object.
|
// The other slots can be used from the draw object.
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1))
|
foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1))
|
||||||
|
|
@ -155,6 +157,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
||||||
ret.SetItem(slot, item);
|
ret.SetItem(slot, item);
|
||||||
ret.SetStain(slot, armor.Stain);
|
ret.SetStain(slot, armor.Stain);
|
||||||
|
ret.SetCrest(slot, model.GetCrest(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weapons use the draw objects of the weapons, but require the game object either way.
|
// Weapons use the draw objects of the weapons, but require the game object either way.
|
||||||
|
|
@ -174,6 +177,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
||||||
ret.SetItem(slot, item);
|
ret.SetItem(slot, item);
|
||||||
ret.SetStain(slot, armor.Stain);
|
ret.SetStain(slot, armor.Stain);
|
||||||
|
ret.SetCrest(slot, actor.GetCrest(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
main = actor.GetMainhand();
|
main = actor.GetMainhand();
|
||||||
|
|
@ -187,8 +191,10 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
var offItem = _items.Identify(EquipSlot.OffHand, off.Set, off.Type, off.Variant, mainItem.Type);
|
var offItem = _items.Identify(EquipSlot.OffHand, off.Set, off.Type, off.Variant, mainItem.Type);
|
||||||
ret.SetItem(EquipSlot.MainHand, mainItem);
|
ret.SetItem(EquipSlot.MainHand, mainItem);
|
||||||
ret.SetStain(EquipSlot.MainHand, main.Stain);
|
ret.SetStain(EquipSlot.MainHand, main.Stain);
|
||||||
|
ret.SetCrest(EquipSlot.MainHand, actor.GetCrest(EquipSlot.MainHand));
|
||||||
ret.SetItem(EquipSlot.OffHand, offItem);
|
ret.SetItem(EquipSlot.OffHand, offItem);
|
||||||
ret.SetStain(EquipSlot.OffHand, off.Stain);
|
ret.SetStain(EquipSlot.OffHand, off.Stain);
|
||||||
|
ret.SetCrest(EquipSlot.OffHand, actor.GetCrest(EquipSlot.OffHand));
|
||||||
|
|
||||||
// Wetness can technically only be set in GPose or via external tools.
|
// Wetness can technically only be set in GPose or via external tools.
|
||||||
// It is only available in the game object.
|
// It is only available in the game object.
|
||||||
|
|
@ -257,7 +263,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
_event.Invoke(StateChanged.Type.EntireCustomize, source, state, actors, (old, applied));
|
_event.Invoke(StateChanged.Type.EntireCustomize, source, state, actors, (old, applied));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment without stain. </summary>
|
/// <summary> Change a single piece of equipment without stain or crest visibility. </summary>
|
||||||
/// <remarks> Do not use this in the same frame as ChangeStain, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks>
|
/// <remarks> Do not use this in the same frame as ChangeStain, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks>
|
||||||
public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, uint key = 0)
|
public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
|
|
@ -274,21 +280,25 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
_event.Invoke(type, source, state, actors, (old, item, slot));
|
_event.Invoke(type, source, state, actors, (old, item, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
/// <summary> Change a single piece of equipment including stain and crest visibility. </summary>
|
||||||
public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, uint key = 0)
|
public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem? item, StainId? stain, bool? crest, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
if (!_editor.ChangeEquip(state, slot, item, stain, source, out var old, out var oldStain, key))
|
if (!_editor.ChangeEquip(state, slot, item, stain, crest, source, out var old, out var oldStain, out var oldCrest, key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||||
var actors = type is StateChanged.Type.Equip
|
var actors = type is StateChanged.Type.Equip
|
||||||
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||||
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc,
|
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc,
|
||||||
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
|
(item ?? old).Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}) and its stain from {oldStain.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
|
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {(item ?? old).Name} ({(item ?? old).ItemId}), its stain from {oldStain.Id} to {(stain ?? oldStain).Id} and its crest visibility from {oldCrest} to {crest ?? oldCrest}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(type, source, state, actors, (old, item, slot));
|
if (item.HasValue)
|
||||||
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (oldStain, stain, slot));
|
_event.Invoke(type, source, state, actors, (old, item.Value, slot));
|
||||||
|
if (stain.HasValue)
|
||||||
|
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (oldStain, stain.Value, slot));
|
||||||
|
if (crest.HasValue)
|
||||||
|
_event.Invoke(StateChanged.Type.Crest, source, state, actors, (oldCrest, crest.Value, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change only the stain of an equipment piece. </summary>
|
/// <summary> Change only the stain of an equipment piece. </summary>
|
||||||
|
|
@ -304,6 +314,19 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot));
|
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Change only the crest visibility of an equipment piece. </summary>
|
||||||
|
/// <remarks> Do not use this in the same frame as ChangeEquip, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks>
|
||||||
|
public void ChangeCrest(ActorState state, EquipSlot slot, bool crest, StateChanged.Source source, uint key = 0)
|
||||||
|
{
|
||||||
|
if (!_editor.ChangeCrest(state, slot, crest, source, out var old, key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var actors = _applier.ChangeCrest(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
|
Glamourer.Log.Verbose(
|
||||||
|
$"Set {slot.ToName()} crest visibility in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
|
_event.Invoke(StateChanged.Type.Crest, source, state, actors, (old, crest, slot));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Change hat visibility. </summary>
|
/// <summary> Change hat visibility. </summary>
|
||||||
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
|
|
@ -356,15 +379,16 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
|
|
||||||
public void ApplyDesign(DesignBase design, ActorState state, StateChanged.Source source, uint key = 0)
|
public void ApplyDesign(DesignBase design, ActorState state, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
|
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain, bool applyCrest)
|
||||||
{
|
{
|
||||||
var unused = (applyPiece, applyStain) switch
|
var unused = (applyPiece, applyStain, applyCrest) switch
|
||||||
{
|
{
|
||||||
(false, false) => false,
|
(false, false, false) => false,
|
||||||
(true, false) => _editor.ChangeItem(state, slot, design.DesignData.Item(slot), source, out _, key),
|
(true, false, false) => _editor.ChangeItem(state, slot, design.DesignData.Item(slot), source, out _, key),
|
||||||
(false, true) => _editor.ChangeStain(state, slot, design.DesignData.Stain(slot), source, out _, key),
|
(false, true, false) => _editor.ChangeStain(state, slot, design.DesignData.Stain(slot), source, out _, key),
|
||||||
(true, true) => _editor.ChangeEquip(state, slot, design.DesignData.Item(slot), design.DesignData.Stain(slot), source, out _,
|
(false, false, true) => _editor.ChangeCrest(state, slot, design.DesignData.Crest(slot), source, out _, key),
|
||||||
out _, key),
|
_ => _editor.ChangeEquip(state, slot, applyPiece ? design.DesignData.Item(slot) : null, applyStain ? design.DesignData.Stain(slot) : null, applyCrest ? design.DesignData.Crest(slot) : null, source, out _,
|
||||||
|
out _, out _, key),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,7 +416,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
redraw |= applied.RequiresRedraw();
|
redraw |= applied.RequiresRedraw();
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot));
|
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot), design.DoApplyCrest(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
var actors = ApplyAll(state, redraw, false);
|
var actors = ApplyAll(state, redraw, false);
|
||||||
|
|
@ -415,14 +439,14 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
{
|
{
|
||||||
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state[slot, false] is not StateChanged.Source.Ipc,
|
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.ModelData.Crest(slot), state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Ipc,
|
||||||
state.ModelData.IsHatVisible());
|
state.ModelData.IsHatVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
|
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
|
||||||
_applier.ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));
|
_applier.ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand), state.ModelData.Crest(EquipSlot.MainHand));
|
||||||
var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? actors.OnlyGPose() : actors;
|
var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? actors.OnlyGPose() : actors;
|
||||||
_applier.ChangeOffhand(offhandActors, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand));
|
_applier.ChangeOffhand(offhandActors, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand), state.ModelData.Crest(EquipSlot.OffHand));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.ModelData.IsHuman)
|
if (state.ModelData.IsHuman)
|
||||||
|
|
@ -450,8 +474,9 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
{
|
{
|
||||||
state[slot, true] = StateChanged.Source.Game;
|
state[slot, ActorState.EquipField.Stain] = StateChanged.Source.Game;
|
||||||
state[slot, false] = StateChanged.Source.Game;
|
state[slot, ActorState.EquipField.Item] = StateChanged.Source.Game;
|
||||||
|
state[slot, ActorState.EquipField.Crest] = StateChanged.Source.Game;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var type in Enum.GetValues<ActorState.MetaIndex>())
|
foreach (var type in Enum.GetValues<ActorState.MetaIndex>())
|
||||||
|
|
@ -478,15 +503,21 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
{
|
{
|
||||||
if (state[slot, true] is StateChanged.Source.Fixed)
|
if (state[slot, ActorState.EquipField.Crest] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[slot, true] = StateChanged.Source.Game;
|
state[slot, ActorState.EquipField.Crest] = StateChanged.Source.Game;
|
||||||
|
state.ModelData.SetCrest(slot, state.BaseData.Crest(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state[slot, ActorState.EquipField.Stain] is StateChanged.Source.Fixed)
|
||||||
|
{
|
||||||
|
state[slot, ActorState.EquipField.Stain] = StateChanged.Source.Game;
|
||||||
state.ModelData.SetStain(slot, state.BaseData.Stain(slot));
|
state.ModelData.SetStain(slot, state.BaseData.Stain(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state[slot, false] is StateChanged.Source.Fixed)
|
if (state[slot, ActorState.EquipField.Item] is StateChanged.Source.Fixed)
|
||||||
{
|
{
|
||||||
state[slot, false] = StateChanged.Source.Game;
|
state[slot, ActorState.EquipField.Item] = StateChanged.Source.Game;
|
||||||
state.ModelData.SetItem(slot, state.BaseData.Item(slot));
|
state.ModelData.SetItem(slot, state.BaseData.Item(slot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue