mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-24 05:31:50 +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;
|
||||
|
||||
[Flags]
|
||||
public enum EquipFlag : uint
|
||||
public enum EquipFlag : ulong
|
||||
{
|
||||
Head = 0x00000001,
|
||||
Body = 0x00000002,
|
||||
|
|
@ -30,12 +30,34 @@ public enum EquipFlag : uint
|
|||
LFingerStain = 0x00200000,
|
||||
MainhandStain = 0x00400000,
|
||||
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 const EquipFlag All = (EquipFlag)(((uint)EquipFlag.OffhandStain << 1) - 1);
|
||||
public const int NumEquipFlags = 24;
|
||||
public const EquipFlag All = (EquipFlag)(((ulong)EquipFlag.OffhandCrest << 1) - 1);
|
||||
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)
|
||||
=> slot switch
|
||||
|
|
@ -73,21 +95,39 @@ public static class EquipFlagExtensions
|
|||
_ => 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)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => EquipFlag.Mainhand | EquipFlag.MainhandStain,
|
||||
EquipSlot.OffHand => EquipFlag.Offhand | EquipFlag.OffhandStain,
|
||||
EquipSlot.Head => EquipFlag.Head | EquipFlag.HeadStain,
|
||||
EquipSlot.Body => EquipFlag.Body | EquipFlag.BodyStain,
|
||||
EquipSlot.Hands => EquipFlag.Hands | EquipFlag.HandsStain,
|
||||
EquipSlot.Legs => EquipFlag.Legs | EquipFlag.LegsStain,
|
||||
EquipSlot.Feet => EquipFlag.Feet | EquipFlag.FeetStain,
|
||||
EquipSlot.Ears => EquipFlag.Ears | EquipFlag.EarsStain,
|
||||
EquipSlot.Neck => EquipFlag.Neck | EquipFlag.NeckStain,
|
||||
EquipSlot.Wrists => EquipFlag.Wrist | EquipFlag.WristStain,
|
||||
EquipSlot.RFinger => EquipFlag.RFinger | EquipFlag.RFingerStain,
|
||||
EquipSlot.LFinger => EquipFlag.LFinger | EquipFlag.LFingerStain,
|
||||
EquipSlot.MainHand => EquipFlag.Mainhand | EquipFlag.MainhandStain | EquipFlag.MainhandCrest,
|
||||
EquipSlot.OffHand => EquipFlag.Offhand | EquipFlag.OffhandStain | EquipFlag.OffhandCrest,
|
||||
EquipSlot.Head => EquipFlag.Head | EquipFlag.HeadStain | EquipFlag.HeadCrest,
|
||||
EquipSlot.Body => EquipFlag.Body | EquipFlag.BodyStain | EquipFlag.BodyCrest,
|
||||
EquipSlot.Hands => EquipFlag.Hands | EquipFlag.HandsStain | EquipFlag.HandsCrest,
|
||||
EquipSlot.Legs => EquipFlag.Legs | EquipFlag.LegsStain | EquipFlag.LegsCrest,
|
||||
EquipSlot.Feet => EquipFlag.Feet | EquipFlag.FeetStain | EquipFlag.FeetCrest,
|
||||
EquipSlot.Ears => EquipFlag.Ears | EquipFlag.EarsStain | EquipFlag.EarsCrest,
|
||||
EquipSlot.Neck => EquipFlag.Neck | EquipFlag.NeckStain | EquipFlag.NeckCrest,
|
||||
EquipSlot.Wrists => EquipFlag.Wrist | EquipFlag.WristStain | EquipFlag.WristCrest,
|
||||
EquipSlot.RFinger => EquipFlag.RFinger | EquipFlag.RFingerStain | EquipFlag.RFingerCrest,
|
||||
EquipSlot.LFinger => EquipFlag.LFinger | EquipFlag.LFingerStain | EquipFlag.LFingerCrest,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ public class AutoDesign
|
|||
Weapons = 0x04,
|
||||
Stains = 0x08,
|
||||
Accessories = 0x10,
|
||||
Crests = 0x20,
|
||||
|
||||
All = Armor | Accessories | Customizations | Weapons | Stains,
|
||||
All = Armor | Accessories | Customizations | Weapons | Stains | Crests,
|
||||
}
|
||||
|
||||
public Design? Design;
|
||||
|
|
@ -69,7 +70,8 @@ public class AutoDesign
|
|||
var equipFlags = (ApplicationType.HasFlag(Type.Weapons) ? WeaponFlags : 0)
|
||||
| (ApplicationType.HasFlag(Type.Armor) ? ArmorFlags : 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;
|
||||
|
||||
if (Revert)
|
||||
|
|
@ -99,4 +101,21 @@ public class AutoDesign
|
|||
| EquipFlag.WristStain
|
||||
| EquipFlag.RFingerStain
|
||||
| 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);
|
||||
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);
|
||||
totalEquipFlags |= flag;
|
||||
}
|
||||
|
|
@ -342,17 +342,25 @@ public class AutoDesignApplier : IDisposable
|
|||
var stainFlag = slot.ToStainFlag();
|
||||
if (equipFlags.HasFlag(stainFlag))
|
||||
{
|
||||
if (!respectManual || state[slot, true] is not StateChanged.Source.Manual)
|
||||
if (!respectManual || state[slot, ActorState.EquipField.Stain] is not StateChanged.Source.Manual)
|
||||
_state.ChangeStain(state, slot, design.Stain(slot), source);
|
||||
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))
|
||||
{
|
||||
var item = design.Item(EquipSlot.MainHand);
|
||||
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
||||
var checkState = !respectManual || state[EquipSlot.MainHand, false] is not StateChanged.Source.Manual;
|
||||
var checkState = !respectManual || state[EquipSlot.MainHand, ActorState.EquipField.Item] is not StateChanged.Source.Manual;
|
||||
if (checkUnlock && checkState)
|
||||
{
|
||||
if (fromJobChange)
|
||||
|
|
@ -372,7 +380,7 @@ public class AutoDesignApplier : IDisposable
|
|||
{
|
||||
var item = design.Item(EquipSlot.OffHand);
|
||||
var checkUnlock = !_config.UnlockedItemMode || _itemUnlocks.IsUnlocked(item.Id, out _);
|
||||
var checkState = !respectManual || state[EquipSlot.OffHand, false] is not StateChanged.Source.Manual;
|
||||
var checkState = !respectManual || state[EquipSlot.OffHand, ActorState.EquipField.Item] is not StateChanged.Source.Manual;
|
||||
if (checkUnlock && checkState)
|
||||
{
|
||||
if (fromJobChange)
|
||||
|
|
@ -390,17 +398,31 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
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);
|
||||
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 (!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);
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public class DesignBase
|
|||
internal CustomizeFlag ApplyCustomizeRaw
|
||||
=> _applyCustomize;
|
||||
|
||||
internal EquipFlag ApplyEquip = EquipFlagExtensions.All;
|
||||
internal EquipFlag ApplyEquip = EquipFlagExtensions.AllRelevant;
|
||||
private DesignFlags _designFlags = DesignFlags.ApplyHatVisible | DesignFlags.ApplyVisorState | DesignFlags.ApplyWeaponVisible;
|
||||
|
||||
public bool SetCustomize(CustomizationService customizationService, Customize customize)
|
||||
|
|
@ -166,6 +166,9 @@ public class DesignBase
|
|||
public bool DoApplyStain(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToStainFlag());
|
||||
|
||||
public bool DoApplyCrest(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToCrestFlag());
|
||||
|
||||
public bool DoApplyCustomize(CustomizeIndex idx)
|
||||
=> ApplyCustomize.HasFlag(idx.ToFlag());
|
||||
|
||||
|
|
@ -189,6 +192,16 @@ public class DesignBase
|
|||
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)
|
||||
{
|
||||
var newValue = value ? _applyCustomize | idx.ToFlag() : _applyCustomize & ~idx.ToFlag();
|
||||
|
|
@ -246,13 +259,15 @@ public class DesignBase
|
|||
|
||||
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()
|
||||
{
|
||||
["ItemId"] = id.Id,
|
||||
["Stain"] = stain.Id,
|
||||
["Crest"] = crest,
|
||||
["Apply"] = apply,
|
||||
["ApplyStain"] = applyStain,
|
||||
["ApplyCrest"] = applyCrest,
|
||||
};
|
||||
|
||||
var ret = new JObject();
|
||||
|
|
@ -262,7 +277,8 @@ public class DesignBase
|
|||
{
|
||||
var item = _designData.Item(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");
|
||||
|
|
@ -345,13 +361,15 @@ public class DesignBase
|
|||
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 stain = (StainId)(item?["Stain"]?.ToObject<byte>() ?? 0);
|
||||
var crest = item?["Crest"]?.ToObject<bool>() ?? false;
|
||||
var apply = item?["Apply"]?.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)
|
||||
|
|
@ -362,21 +380,23 @@ public class DesignBase
|
|||
|
||||
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.ValidateStain(stain, out stain, allowUnknown));
|
||||
design._designData.SetItem(slot, item);
|
||||
design._designData.SetStain(slot, stain);
|
||||
design._designData.SetCrest(slot, crest);
|
||||
design.SetApplyEquip(slot, apply);
|
||||
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))
|
||||
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))
|
||||
id = ItemManager.NothingId(FullEquipType.Shield);
|
||||
|
||||
|
|
@ -387,10 +407,14 @@ public class DesignBase
|
|||
design._designData.SetItem(EquipSlot.OffHand, off);
|
||||
design._designData.SetStain(EquipSlot.MainHand, stain);
|
||||
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.OffHand, applyOff);
|
||||
design.SetApplyStain(EquipSlot.MainHand, applyStain);
|
||||
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);
|
||||
design.SetApplyHatVisible(metaValue.Enabled);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public unsafe struct DesignData
|
|||
private FullEquipType _typeMainhand;
|
||||
private FullEquipType _typeOffhand;
|
||||
private byte _states;
|
||||
public ushort CrestVisibility;
|
||||
public bool IsHuman = true;
|
||||
|
||||
public DesignData()
|
||||
|
|
@ -59,6 +60,15 @@ public unsafe struct DesignData
|
|||
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
|
||||
=> _typeMainhand;
|
||||
|
||||
|
|
@ -173,6 +183,16 @@ public unsafe struct DesignData
|
|||
_ => 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()
|
||||
=> (_states & 0x01) == 0x01;
|
||||
|
||||
|
|
@ -228,12 +248,15 @@ public unsafe struct DesignData
|
|||
{
|
||||
SetItem(slot, ItemManager.NothingItem(slot));
|
||||
SetStain(slot, 0);
|
||||
SetCrest(slot, false);
|
||||
}
|
||||
|
||||
SetItem(EquipSlot.MainHand, items.DefaultSword);
|
||||
SetStain(EquipSlot.MainHand, 0);
|
||||
SetCrest(EquipSlot.MainHand, false);
|
||||
SetItem(EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield));
|
||||
SetStain(EquipSlot.OffHand, 0);
|
||||
SetCrest(EquipSlot.OffHand, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -446,6 +446,31 @@ public class DesignManager
|
|||
_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>
|
||||
public void ChangeMeta(Design design, ActorState.MetaIndex metaIndex, bool value)
|
||||
{
|
||||
|
|
@ -514,6 +539,9 @@ public class DesignManager
|
|||
|
||||
if (other.DoApplyStain(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))
|
||||
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)
|
||||
|
|
|
|||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
Design,
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Glamourer.Structs;
|
|||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Data;
|
||||
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,
|
||||
out bool rApply, out bool rApplyStain, bool locked)
|
||||
=> DrawEquip(slot, designData.Item(slot), out rArmor, designData.Stain(slot), out rStain, cApply, out rApply, out rApplyStain, locked,
|
||||
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, out bool rApplyCrest, bool 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);
|
||||
|
||||
public DataChange DrawEquip(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, EquipFlag? cApply,
|
||||
out bool rApply, out bool rApplyStain, bool locked, Gender gender = Gender.Unknown, Race race = Race.Unknown)
|
||||
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, out bool rApplyCrest, bool locked, Gender gender = Gender.Unknown, Race race = Race.Unknown)
|
||||
{
|
||||
if (_config.HideApplyCheckmarks)
|
||||
cApply = null;
|
||||
|
|
@ -102,24 +103,26 @@ public class EquipmentDrawer
|
|||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
|
||||
|
||||
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)
|
||||
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,
|
||||
out StainId rOffhandStain, EquipFlag? cApply, bool allWeapons, out bool rApplyMainhand, out bool rApplyMainhandStain,
|
||||
out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked)
|
||||
out StainId rOffhandStain, out bool rMainhandCrest, 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)
|
||||
=> 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,
|
||||
allWeapons, out rApplyMainhand, out rApplyMainhandStain, out rApplyOffhand, out rApplyOffhandStain, locked);
|
||||
designData.Stain(EquipSlot.MainHand), out rMainhandStain, designData.Stain(EquipSlot.OffHand), out rOffhandStain,
|
||||
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,
|
||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain, EquipFlag? cApply,
|
||||
bool allWeapons, out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain,
|
||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||
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)
|
||||
{
|
||||
if (cMainhand.ModelId.Id == 0)
|
||||
|
|
@ -128,10 +131,14 @@ public class EquipmentDrawer
|
|||
rMainhand = cMainhand;
|
||||
rMainhandStain = cMainhandStain;
|
||||
rOffhandStain = cOffhandStain;
|
||||
rMainhandCrest = cMainhandCrest;
|
||||
rOffhandCrest = cOffhandCrest;
|
||||
rApplyMainhand = false;
|
||||
rApplyMainhandStain = false;
|
||||
rApplyMainhandCrest = false;
|
||||
rApplyOffhand = false;
|
||||
rApplyOffhandStain = false;
|
||||
rApplyOffhandCrest = false;
|
||||
return DataChange.None;
|
||||
}
|
||||
|
||||
|
|
@ -144,15 +151,18 @@ public class EquipmentDrawer
|
|||
|
||||
if (_config.SmallEquip)
|
||||
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);
|
||||
|
||||
if (!locked && _codes.EnabledArtisan)
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -331,6 +341,49 @@ public class EquipmentDrawer
|
|||
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>
|
||||
private bool DrawArmorArtisan(EquipSlot slot, EquipItem current, out EquipItem armor)
|
||||
{
|
||||
|
|
@ -383,8 +436,8 @@ public class EquipmentDrawer
|
|||
return false;
|
||||
}
|
||||
|
||||
private DataChange DrawEquipArtisan(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
||||
EquipFlag? cApply, out bool rApply, out bool rApplyStain)
|
||||
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, out bool rApplyCrest)
|
||||
{
|
||||
var changes = DataChange.None;
|
||||
if (DrawStainArtisan(slot, cStain, out rStain))
|
||||
|
|
@ -392,6 +445,16 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
if (DrawArmorArtisan(slot, cArmor, out rArmor))
|
||||
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)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
|
|
@ -410,8 +473,8 @@ public class EquipmentDrawer
|
|||
return changes;
|
||||
}
|
||||
|
||||
private DataChange DrawEquipSmall(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
||||
EquipFlag? cApply, out bool rApply, out bool rApplyStain, bool locked, Gender gender, Race race)
|
||||
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, out bool rApplyCrest, bool locked, Gender gender, Race race)
|
||||
{
|
||||
var changes = DataChange.None;
|
||||
if (DrawStain(slot, cStain, out rStain, locked, true))
|
||||
|
|
@ -419,6 +482,16 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
if (DrawItem(slot, cArmor, out rArmor, out var label, locked, true, false, false))
|
||||
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)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
|
|
@ -443,8 +516,8 @@ public class EquipmentDrawer
|
|||
return changes;
|
||||
}
|
||||
|
||||
private DataChange DrawEquipNormal(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
||||
EquipFlag? cApply, out bool rApply, out bool rApplyStain, bool locked, Gender gender, Race race)
|
||||
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, out bool rApplyCrest, bool locked, Gender gender, Race race)
|
||||
{
|
||||
var changes = DataChange.None;
|
||||
cArmor.DrawIcon(_textures, _iconSize, slot);
|
||||
|
|
@ -479,6 +552,16 @@ public class EquipmentDrawer
|
|||
{
|
||||
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))
|
||||
{
|
||||
|
|
@ -506,8 +589,9 @@ public class EquipmentDrawer
|
|||
}
|
||||
|
||||
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,
|
||||
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked,
|
||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
|
|
@ -548,8 +642,10 @@ public class EquipmentDrawer
|
|||
if (rOffhand.Type is FullEquipType.Unknown)
|
||||
{
|
||||
rOffhandStain = cOffhandStain;
|
||||
rOffhandCrest = cOffhandCrest;
|
||||
rApplyOffhand = false;
|
||||
rApplyOffhandStain = false;
|
||||
rApplyOffhandCrest = false;
|
||||
return changes;
|
||||
}
|
||||
|
||||
|
|
@ -559,6 +655,17 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
if (DrawOffhand(rMainhand, rOffhand, out rOffhand, out var offhandLabel, locked, true, false, false))
|
||||
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)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
|
|
@ -580,8 +687,9 @@ public class EquipmentDrawer
|
|||
}
|
||||
|
||||
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,
|
||||
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked,
|
||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||
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)
|
||||
{
|
||||
var changes = DataChange.None;
|
||||
|
|
@ -630,13 +738,26 @@ public class EquipmentDrawer
|
|||
{
|
||||
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)
|
||||
{
|
||||
rOffhandStain = cOffhandStain;
|
||||
rOffhandCrest = cOffhandCrest;
|
||||
rApplyOffhand = false;
|
||||
rApplyOffhandStain = false;
|
||||
rApplyOffhandCrest = false;
|
||||
return changes;
|
||||
}
|
||||
|
||||
|
|
@ -673,19 +794,31 @@ public class EquipmentDrawer
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain)
|
||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain,
|
||||
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);
|
||||
rApplyMainhandStain = (cApply ?? 0).HasFlag(EquipFlag.MainhandStain);
|
||||
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)
|
||||
{
|
||||
|
|
@ -741,15 +874,35 @@ public class EquipmentDrawer
|
|||
ImGui.SameLine();
|
||||
if (DrawWeapon(cMainhand, out rMainhand))
|
||||
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))
|
||||
{
|
||||
if (DrawStainArtisan(EquipSlot.OffHand, cOffhandStain, out rOffhandStain))
|
||||
ret |= DataChange.Stain;
|
||||
ret |= DataChange.Stain2;
|
||||
ImGui.SameLine();
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ public class ActorPanel
|
|||
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _state!.IsLocked);
|
||||
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);
|
||||
if (usedAllStain)
|
||||
{
|
||||
|
|
@ -192,14 +192,20 @@ public class ActorPanel
|
|||
case DataChange.Stain:
|
||||
_stateManager.ChangeStain(_state, slot, newStain, StateChanged.Source.Manual);
|
||||
break;
|
||||
case DataChange.Crest:
|
||||
_stateManager.ChangeCrest(_state, slot, newCrest, StateChanged.Source.Manual);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
weaponChanges |= DataChange.Stain | DataChange.Stain2;
|
||||
|
|
@ -208,20 +214,34 @@ public class ActorPanel
|
|||
}
|
||||
|
||||
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
|
||||
_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.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
|
||||
_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));
|
||||
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
|
||||
{
|
||||
(true, true) => (9, 14),
|
||||
(true, false) => (7, 10),
|
||||
(true, true) => (10, 15),
|
||||
(true, false) => (8, 11),
|
||||
(false, true) => (4, 4),
|
||||
(false, false) => (2, 0),
|
||||
};
|
||||
|
|
@ -173,7 +173,7 @@ public class SetPanel
|
|||
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthFixed, 220 * ImGuiHelpers.GlobalScale);
|
||||
if (_config.ShowAllAutomatedApplicationRules)
|
||||
ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed,
|
||||
6 * ImGui.GetFrameHeight() + 10 * ImGuiHelpers.GlobalScale);
|
||||
7 * ImGui.GetFrameHeight() + 11 * ImGuiHelpers.GlobalScale);
|
||||
else
|
||||
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);
|
||||
if (_config.ShowAllAutomatedApplicationRules)
|
||||
ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed,
|
||||
3 * ImGui.GetFrameHeight() + 4 * ImGuiHelpers.GlobalScale);
|
||||
4 * ImGui.GetFrameHeight() + 5 * ImGuiHelpers.GlobalScale);
|
||||
else
|
||||
ImGui.TableSetupColumn("Use", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Use").X);
|
||||
}
|
||||
|
|
@ -383,14 +383,16 @@ public class SetPanel
|
|||
Box(0);
|
||||
ImGui.SameLine();
|
||||
Box(1);
|
||||
ImGui.SameLine();
|
||||
Box(2);
|
||||
if (singleLine)
|
||||
ImGui.SameLine();
|
||||
|
||||
Box(2);
|
||||
ImGui.SameLine();
|
||||
Box(3);
|
||||
ImGui.SameLine();
|
||||
Box(4);
|
||||
ImGui.SameLine();
|
||||
Box(5);
|
||||
}
|
||||
|
||||
_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.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.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."),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -335,14 +335,15 @@ public unsafe class DebugTab : ITab
|
|||
return;
|
||||
|
||||
if (ImGui.SmallButton("Hide"))
|
||||
_updateSlotService.UpdateSlot(model, EquipSlot.Head, CharacterArmor.Empty);
|
||||
_updateSlotService.UpdateSlot(model, EquipSlot.Head, CharacterArmor.Empty, null);
|
||||
ImGui.SameLine();
|
||||
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();
|
||||
if (ImGui.SmallButton("Toggle"))
|
||||
_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)
|
||||
|
|
@ -423,8 +424,11 @@ public unsafe class DebugTab : ITab
|
|||
if (ImGui.SmallButton("Change Stain"))
|
||||
_updateSlotService.UpdateStain(model, slot, 5);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("Toggle Crest"))
|
||||
_updateSlotService.UpdateCrest(model, slot, !model.IsFreeCompanyCrestVisibleOnSlot((byte)slot.ToIndex()));
|
||||
ImGui.SameLine();
|
||||
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)
|
||||
{
|
||||
using var table = ImRaii.Table("##state", 7, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
|
||||
using var table = ImRaii.Table("##state", 9, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
|
|
@ -1189,10 +1193,12 @@ public unsafe class DebugTab : ITab
|
|||
ImGui.TableNextRow();
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
|
||||
{
|
||||
PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state[slot, false]);
|
||||
PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state[slot, ActorState.EquipField.Item]);
|
||||
ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString());
|
||||
ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString());
|
||||
ImGuiUtil.DrawTableColumn(state[slot, true].ToString());
|
||||
ImGuiUtil.DrawTableColumn(state[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>())
|
||||
|
|
@ -1307,12 +1313,16 @@ public unsafe class DebugTab : ITab
|
|||
var apply = design.DoApplyEquip(slot);
|
||||
var stain = design.DesignData.Stain(slot);
|
||||
var applyStain = design.DoApplyStain(slot);
|
||||
var crest = design.DesignData.Crest(slot);
|
||||
var applyCrest = design.DoApplyCrest(slot);
|
||||
ImGuiUtil.DrawTableColumn(slot.ToName());
|
||||
ImGuiUtil.DrawTableColumn(item.Name);
|
||||
ImGuiUtil.DrawTableColumn(item.ItemId.ToString());
|
||||
ImGuiUtil.DrawTableColumn(apply ? "Apply" : "Keep");
|
||||
ImGuiUtil.DrawTableColumn(stain.ToString());
|
||||
ImGuiUtil.DrawTableColumn(applyStain ? "Apply" : "Keep");
|
||||
ImGuiUtil.DrawTableColumn(crest.ToString());
|
||||
ImGuiUtil.DrawTableColumn(applyCrest ? "Apply" : "Keep");
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Hat Visible");
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
|
|||
case DesignChanged.Type.ApplyCustomize:
|
||||
case DesignChanged.Type.ApplyEquip:
|
||||
case DesignChanged.Type.ApplyStain:
|
||||
case DesignChanged.Type.ApplyCrest:
|
||||
case DesignChanged.Type.Customize:
|
||||
case DesignChanged.Type.Equip:
|
||||
case DesignChanged.Type.ChangedColor:
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
ObjectManager _objects, StateManager _state, EquipmentDrawer _equipmentDrawer, ModAssociationsTab _modAssociations,
|
||||
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 HeaderDrawer.Button LockButton()
|
||||
|
|
@ -95,23 +97,28 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
var usedAllStain = _equipmentDrawer.DrawAllStain(out var newAllStain, _selector.Selected!.WriteProtected());
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var changes = _equipmentDrawer.DrawEquip(slot, _selector.Selected!.DesignData, out var newArmor, out var newStain,
|
||||
_selector.Selected.ApplyEquip, out var newApply, out var newApplyStain, _selector.Selected!.WriteProtected());
|
||||
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, out var newApplyCrest, _selector.Selected!.WriteProtected());
|
||||
if (changes.HasFlag(DataChange.Item))
|
||||
_manager.ChangeEquip(_selector.Selected, slot, newArmor);
|
||||
if (changes.HasFlag(DataChange.Stain))
|
||||
_manager.ChangeStain(_selector.Selected, slot, newStain);
|
||||
else if (usedAllStain)
|
||||
_manager.ChangeStain(_selector.Selected, slot, newAllStain);
|
||||
if (changes.HasFlag(DataChange.Crest))
|
||||
_manager.ChangeCrest(_selector.Selected, slot, newCrest);
|
||||
if (changes.HasFlag(DataChange.ApplyItem))
|
||||
_manager.ChangeApplyEquip(_selector.Selected, slot, newApply);
|
||||
if (changes.HasFlag(DataChange.ApplyStain))
|
||||
_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,
|
||||
out var newMainhandStain, out var newOffhandStain, _selector.Selected.ApplyEquip, true, out var applyMain, out var applyMainStain,
|
||||
out var applyOff, out var applyOffStain, _selector.Selected!.WriteProtected());
|
||||
out var newMainhandStain, out var newOffhandStain, out var newMainhandCrest, out var newOffhandCrest,
|
||||
_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))
|
||||
_manager.ChangeWeapon(_selector.Selected, EquipSlot.MainHand, newMainhand);
|
||||
|
|
@ -119,20 +126,28 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newMainhandStain);
|
||||
else if (usedAllStain)
|
||||
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newAllStain);
|
||||
if (weaponChanges.HasFlag(DataChange.Crest))
|
||||
_manager.ChangeCrest(_selector.Selected, EquipSlot.MainHand, newMainhandCrest);
|
||||
if (weaponChanges.HasFlag(DataChange.ApplyItem))
|
||||
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.MainHand, applyMain);
|
||||
if (weaponChanges.HasFlag(DataChange.ApplyStain))
|
||||
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.MainHand, applyMainStain);
|
||||
if (weaponChanges.HasFlag(DataChange.ApplyCrest))
|
||||
_manager.ChangeApplyCrest(_selector.Selected, EquipSlot.MainHand, applyMainCrest);
|
||||
if (weaponChanges.HasFlag(DataChange.Item2))
|
||||
_manager.ChangeWeapon(_selector.Selected, EquipSlot.OffHand, newOffhand);
|
||||
if (weaponChanges.HasFlag(DataChange.Stain2))
|
||||
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newOffhandStain);
|
||||
else if (usedAllStain)
|
||||
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newAllStain);
|
||||
if (weaponChanges.HasFlag(DataChange.Crest2))
|
||||
_manager.ChangeCrest(_selector.Selected, EquipSlot.OffHand, newOffhandCrest);
|
||||
if (weaponChanges.HasFlag(DataChange.ApplyItem2))
|
||||
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.OffHand, applyOff);
|
||||
if (weaponChanges.HasFlag(DataChange.ApplyStain2))
|
||||
_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));
|
||||
DrawEquipmentMetaToggles();
|
||||
|
|
@ -223,43 +238,59 @@ public class DesignPanel(DesignFileSystemSelector _selector, CustomizationDrawer
|
|||
ImGui.SameLine(ImGui.GetContentRegionAvail().X / 2);
|
||||
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);
|
||||
if (stain)
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToStainFlag()) : _selector.Selected!.DoApplyStain(slot);
|
||||
if (ImGui.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange)
|
||||
_manager.ChangeApplyStain(_selector.Selected!, slot, apply);
|
||||
}
|
||||
else
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var apply = bigChange ? ((EquipFlag)flags).HasFlag(slot.ToFlag()) : _selector.Selected!.DoApplyEquip(slot);
|
||||
if (ImGui.Checkbox($"Apply {slot.ToName()}", ref apply) || bigChange)
|
||||
_manager.ChangeApplyEquip(_selector.Selected!, slot, apply);
|
||||
}
|
||||
var bigChange = ImGui.CheckboxFlags($"Apply All {label}", ref flags, (uint)((ulong)allFlags >> shift));
|
||||
|
||||
var adjustedFlags = (EquipFlag)((ulong)flags << shift);
|
||||
switch (equipField)
|
||||
{
|
||||
case ActorState.EquipField.Stain:
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var apply = bigChange ? adjustedFlags.HasFlag(slot.ToStainFlag()) : _selector.Selected!.DoApplyStain(slot);
|
||||
if (ImGui.Checkbox($"Apply {slot.ToName()} Dye", ref apply) || bigChange)
|
||||
_manager.ChangeApplyStain(_selector.Selected!, slot, apply);
|
||||
}
|
||||
break;
|
||||
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[]
|
||||
{
|
||||
EquipSlot.MainHand,
|
||||
EquipSlot.OffHand,
|
||||
});
|
||||
ApplyEquip("Weapons", AutoDesign.WeaponFlags, ActorState.EquipField.Item, EquipSlotExtensions.WeaponSlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
ApplyEquip("Armor", AutoDesign.ArmorFlags, false, EquipSlotExtensions.EquipmentSlots);
|
||||
ApplyEquip("Armor", AutoDesign.ArmorFlags, ActorState.EquipField.Item, EquipSlotExtensions.EquipmentSlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
ApplyEquip("Accessories", AutoDesign.AccessoryFlags, false, EquipSlotExtensions.AccessorySlots);
|
||||
ApplyEquip("Accessories", AutoDesign.AccessoryFlags, ActorState.EquipField.Item, EquipSlotExtensions.AccessorySlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
ApplyEquip("Dyes", AutoDesign.StainFlags, true,
|
||||
ApplyEquip("Dyes", AutoDesign.StainFlags, ActorState.EquipField.Stain,
|
||||
EquipSlotExtensions.FullSlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
ApplyEquip("Crest Visibilities", AutoDesign.RelevantCrestFlags, ActorState.EquipField.Crest, CrestSlots);
|
||||
|
||||
ImGui.NewLine();
|
||||
const uint all = 0x0Fu;
|
||||
var flags = (_selector.Selected!.DoApplyHatVisible() ? 0x01u : 0x00)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ using Penumbra.GameData.Structs;
|
|||
namespace Glamourer.Gui;
|
||||
|
||||
[Flags]
|
||||
public enum DataChange : byte
|
||||
public enum DataChange : ushort
|
||||
{
|
||||
None = 0x00,
|
||||
Item = 0x01,
|
||||
|
|
@ -28,6 +28,10 @@ public enum DataChange : byte
|
|||
Stain2 = 0x20,
|
||||
ApplyItem2 = 0x40,
|
||||
ApplyStain2 = 0x80,
|
||||
Crest = 0x100,
|
||||
ApplyCrest = 0x200,
|
||||
Crest2 = 0x400,
|
||||
ApplyCrest2 = 0x800,
|
||||
}
|
||||
|
||||
public static class UiHelpers
|
||||
|
|
|
|||
|
|
@ -119,14 +119,14 @@ public class ContextMenuService : IDisposable
|
|||
return;
|
||||
|
||||
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.ModelId.Id is > 1600 and < 1651
|
||||
&& _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))
|
||||
_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;
|
||||
|
||||
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.ModelId.Id is > 1600 and < 1651
|
||||
&& _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))
|
||||
_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)
|
||||
=> ((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()
|
||||
=> new(AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).ModelId.Value);
|
||||
|
||||
|
|
@ -115,6 +130,10 @@ public readonly unsafe struct Actor : IEquatable<Actor>
|
|||
public Customize GetCustomize()
|
||||
=> *(Customize*)&AsCharacter->DrawData.CustomizeData;
|
||||
|
||||
// TODO remove this when available in ClientStructs
|
||||
public byte GetFreeCompanyCrestBitfield()
|
||||
=> ((byte*)Address)[0x1BBB];
|
||||
|
||||
public override string ToString()
|
||||
=> $"0x{Address:X}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ public readonly unsafe struct Model : IEquatable<Model>
|
|||
public CharacterArmor GetArmor(EquipSlot slot)
|
||||
=> ((CharacterArmor*)&AsHuman->Head)[slot.ToIndex()];
|
||||
|
||||
public bool GetCrest(EquipSlot slot)
|
||||
=> IsFreeCompanyCrestVisibleOnSlot((byte)slot.ToIndex());
|
||||
|
||||
public Customize GetCustomize()
|
||||
=> *(Customize*)&AsHuman->Customize;
|
||||
|
||||
|
|
@ -195,6 +198,27 @@ public readonly unsafe struct Model : IEquatable<Model>
|
|||
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()
|
||||
=> $"0x{Address:X}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility.Signatures;
|
||||
|
|
@ -6,47 +8,78 @@ using Glamourer.Events;
|
|||
using Glamourer.Interop.Structs;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using PenumbraSigs = Penumbra.GameData.Sigs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
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);
|
||||
_humanSetFreeCompanyCrestVisibleOnSlot = interop.HookFromAddress<SetCrestDelegateIntern>(_humanVTable[96], HumanSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||
_weaponSetFreeCompanyCrestVisibleOnSlot = interop.HookFromAddress<SetCrestDelegateIntern>(_weaponVTable[96], WeaponSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||
_flagSlotForUpdateHook.Enable();
|
||||
_humanSetFreeCompanyCrestVisibleOnSlot.Enable();
|
||||
_weaponSetFreeCompanyCrestVisibleOnSlot.Enable();
|
||||
}
|
||||
|
||||
public void 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)
|
||||
return;
|
||||
|
||||
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)
|
||||
=> UpdateSlot(drawObject, slot, armor.With(stain));
|
||||
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor, StainId stain, bool? crest)
|
||||
=> UpdateSlot(drawObject, slot, armor.With(stain), crest);
|
||||
|
||||
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)
|
||||
=> 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 void SetCrestDelegateIntern(nint drawObject, byte slot, byte visible);
|
||||
|
||||
[Signature(Sigs.FlagSlotForUpdate, DetourName = nameof(FlagSlotForUpdateDetour))]
|
||||
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)
|
||||
{
|
||||
var slot = slotIdx.ToEquipSlot();
|
||||
|
|
@ -56,6 +89,52 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
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)
|
||||
=> _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
|
||||
{
|
||||
private readonly WeaponLoading _event;
|
||||
private readonly UpdateSlotService _updateSlotService;
|
||||
private readonly ThreadLocal<bool> _inUpdate = new(() => false);
|
||||
|
||||
|
||||
|
|
@ -20,9 +21,10 @@ public unsafe class WeaponService : IDisposable
|
|||
_original;
|
||||
|
||||
|
||||
public WeaponService(WeaponLoading @event, IGameInteropProvider interop)
|
||||
public WeaponService(WeaponLoading @event, UpdateSlotService updateSlotService, IGameInteropProvider interop)
|
||||
{
|
||||
_event = @event;
|
||||
_updateSlotService = updateSlotService;
|
||||
_loadWeaponHook =
|
||||
interop.HookFromAddress<LoadWeaponDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadWeapon, LoadWeaponDetour);
|
||||
_original =
|
||||
|
|
@ -83,24 +85,39 @@ public unsafe class WeaponService : IDisposable
|
|||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
_inUpdate.Value = true;
|
||||
_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;
|
||||
return;
|
||||
case EquipSlot.OffHand:
|
||||
_inUpdate.Value = true;
|
||||
_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;
|
||||
return;
|
||||
case EquipSlot.BothHand:
|
||||
_inUpdate.Value = true;
|
||||
_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);
|
||||
if (crest.HasValue)
|
||||
{
|
||||
using var _ = _updateSlotService.EnterCrestVisibilityUpdate();
|
||||
character.Model.GetMainhand().Address.SetFreeCompanyCrestVisibleOnSlot(0, crest.Value);
|
||||
}
|
||||
_inUpdate.Value = false;
|
||||
return;
|
||||
}
|
||||
|
|
@ -112,6 +129,21 @@ public unsafe class WeaponService : IDisposable
|
|||
var (_, _, mh, oh) = mdl.GetWeapons(character);
|
||||
var value = slot == EquipSlot.OffHand ? oh : mh;
|
||||
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("Equipment, ")
|
||||
.AddInitialPurple("Accessories, ")
|
||||
.AddInitialPurple("Dyes and ")
|
||||
.AddInitialPurple("Weapons, where ").AddPurple("CEADW")
|
||||
.AddInitialPurple("Dyes, ")
|
||||
.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.")
|
||||
.BuiltString);
|
||||
return false;
|
||||
|
|
@ -270,6 +271,9 @@ public class CommandService : IDisposable
|
|||
case 'd':
|
||||
applicationFlags |= AutoDesign.Type.Stains;
|
||||
break;
|
||||
case 'f':
|
||||
applicationFlags |= AutoDesign.Type.Crests;
|
||||
break;
|
||||
case 'w':
|
||||
applicationFlags |= AutoDesign.Type.Weapons;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ public static class ServiceManager
|
|||
private static IServiceCollection AddEvents(this IServiceCollection services)
|
||||
=> services.AddSingleton<VisorStateChanged>()
|
||||
.AddSingleton<SlotUpdating>()
|
||||
.AddSingleton<CrestVisibilityUpdating>()
|
||||
.AddSingleton<DesignChanged>()
|
||||
.AddSingleton<AutomationChanged>()
|
||||
.AddSingleton<StateChanged>()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@ public class ActorState
|
|||
ModelId,
|
||||
}
|
||||
|
||||
public enum EquipField
|
||||
{
|
||||
Item = 0,
|
||||
Stain = 12,
|
||||
Crest = 24,
|
||||
}
|
||||
|
||||
public readonly ActorIdentifier Identifier;
|
||||
|
||||
public bool AllowsRedraw(ICondition condition)
|
||||
|
|
@ -85,8 +92,8 @@ public class ActorState
|
|||
internal ActorState(ActorIdentifier identifier)
|
||||
=> Identifier = identifier.CreatePermanent();
|
||||
|
||||
public ref StateChanged.Source this[EquipSlot slot, bool stain]
|
||||
=> ref _sources[slot.ToIndex() + (stain ? EquipFlagExtensions.NumEquipFlags / 2 : 0)];
|
||||
public ref StateChanged.Source this[EquipSlot slot, EquipField field]
|
||||
=> ref _sources[slot.ToIndex() + (int)field];
|
||||
|
||||
public ref StateChanged.Source this[CustomizeIndex type]
|
||||
=> ref _sources[EquipFlagExtensions.NumEquipFlags + (int)type];
|
||||
|
|
|
|||
|
|
@ -99,11 +99,11 @@ public class StateApplier
|
|||
}
|
||||
|
||||
/// <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 never requires redrawing.
|
||||
/// </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)
|
||||
return;
|
||||
|
|
@ -118,11 +118,11 @@ public class StateApplier
|
|||
{
|
||||
var customize = mdl.GetCustomize();
|
||||
var (_, resolvedItem) = _items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender);
|
||||
_updateSlot.UpdateSlot(actor.Model, slot, resolvedItem);
|
||||
_updateSlot.UpdateSlot(actor.Model, slot, resolvedItem, crest);
|
||||
}
|
||||
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.
|
||||
var data = GetData(state);
|
||||
if (apply)
|
||||
ChangeArmor(data, slot, state.ModelData.Armor(slot), state[slot, false] is not StateChanged.Source.Ipc,
|
||||
ChangeArmor(data, slot, state.ModelData.Armor(slot), state.ModelData.Crest(slot), state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Ipc,
|
||||
state.ModelData.IsHatVisible());
|
||||
|
||||
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>
|
||||
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)
|
||||
ChangeMainhand(data, item, stain);
|
||||
ChangeMainhand(data, item, stain, crest);
|
||||
else
|
||||
ChangeOffhand(data, item, stain);
|
||||
ChangeOffhand(data, item, stain, crest);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ChangeWeapon(ActorData,EquipSlot,EquipItem,StainId)"/>
|
||||
|
|
@ -192,7 +226,7 @@ public class StateApplier
|
|||
data = data.OnlyGPose();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -200,19 +234,19 @@ public class StateApplier
|
|||
/// <summary>
|
||||
/// Apply a weapon to the mainhand. If the weapon type has no associated offhand type, apply both.
|
||||
/// </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;
|
||||
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>
|
||||
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;
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -67,8 +67,9 @@ public class StateEditor
|
|||
state[ActorState.MetaIndex.VisorState] = source;
|
||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||
{
|
||||
state[slot, true] = source;
|
||||
state[slot, false] = source;
|
||||
state[slot, ActorState.EquipField.Stain] = source;
|
||||
state[slot, ActorState.EquipField.Item] = source;
|
||||
state[slot, ActorState.EquipField.Crest] = source;
|
||||
}
|
||||
|
||||
state[CustomizeIndex.Clan] = source;
|
||||
|
|
@ -126,7 +127,7 @@ public class StateEditor
|
|||
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)
|
||||
{
|
||||
oldItem = state.ModelData.Item(slot);
|
||||
|
|
@ -144,44 +145,58 @@ public class StateEditor
|
|||
_gPose.AddActionOnLeave(() =>
|
||||
{
|
||||
if (old.Type == state.BaseData.Item(slot).Type)
|
||||
ChangeItem(state, slot, old, state[slot, false], out _, key);
|
||||
ChangeItem(state, slot, old, state[slot, ActorState.EquipField.Item], out _, key);
|
||||
});
|
||||
}
|
||||
|
||||
state.ModelData.SetItem(slot, item);
|
||||
state[slot, false] = source;
|
||||
state[slot, ActorState.EquipField.Item] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
||||
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, out EquipItem oldItem,
|
||||
out StainId oldStain, uint key = 0)
|
||||
/// <summary> Change a single piece of equipment including stain and crest visibility. </summary>
|
||||
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem? item, StainId? stain, bool? crest, StateChanged.Source source, out EquipItem oldItem,
|
||||
out StainId oldStain, out bool oldCrest, uint key = 0)
|
||||
{
|
||||
oldItem = state.ModelData.Item(slot);
|
||||
oldStain = state.ModelData.Stain(slot);
|
||||
oldCrest = state.ModelData.Crest(slot);
|
||||
|
||||
if (!state.CanUnlock(key))
|
||||
return false;
|
||||
|
||||
// Can not change weapon type from expected type in state.
|
||||
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
||||
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
|
||||
if (item.HasValue && (slot is EquipSlot.MainHand && item.Value.Type != state.BaseData.MainhandType
|
||||
|| slot is EquipSlot.OffHand && item.Value.Type != state.BaseData.OffhandType))
|
||||
{
|
||||
if (!_gPose.InGPose)
|
||||
return false;
|
||||
|
||||
var old = oldItem;
|
||||
var oldS = oldStain;
|
||||
var oldC = oldCrest;
|
||||
_gPose.AddActionOnLeave(() =>
|
||||
{
|
||||
if (old.Type == state.BaseData.Item(slot).Type)
|
||||
ChangeEquip(state, slot, old, oldS, state[slot, false], out _, out _, key);
|
||||
ChangeEquip(state, slot, old, oldS, oldC, state[slot, ActorState.EquipField.Item], out _, out _, out _, key);
|
||||
});
|
||||
}
|
||||
|
||||
state.ModelData.SetItem(slot, item);
|
||||
state.ModelData.SetStain(slot, stain);
|
||||
state[slot, false] = source;
|
||||
state[slot, true] = source;
|
||||
if (item.HasValue)
|
||||
{
|
||||
state.ModelData.SetItem(slot, item.Value);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +208,19 @@ public class StateEditor
|
|||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public class StateListener : IDisposable
|
|||
private readonly CustomizationService _customizations;
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly SlotUpdating _slotUpdating;
|
||||
private readonly CrestVisibilityUpdating _crestVisibilityUpdating;
|
||||
private readonly WeaponLoading _weaponLoading;
|
||||
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
||||
private readonly VisorStateChanged _visorState;
|
||||
|
|
@ -51,29 +52,30 @@ public class StateListener : IDisposable
|
|||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
||||
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||
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)
|
||||
{
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
_config = config;
|
||||
_slotUpdating = slotUpdating;
|
||||
_weaponLoading = weaponLoading;
|
||||
_visorState = visorState;
|
||||
_weaponVisibility = weaponVisibility;
|
||||
_headGearVisibility = headGearVisibility;
|
||||
_autoDesignApplier = autoDesignApplier;
|
||||
_funModule = funModule;
|
||||
_humans = humans;
|
||||
_applier = applier;
|
||||
_movedEquipment = movedEquipment;
|
||||
_objects = objects;
|
||||
_gPose = gPose;
|
||||
_changeCustomizeService = changeCustomizeService;
|
||||
_customizations = customizations;
|
||||
_condition = condition;
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
_config = config;
|
||||
_slotUpdating = slotUpdating;
|
||||
_crestVisibilityUpdating = crestVisibilityUpdating;
|
||||
_weaponLoading = weaponLoading;
|
||||
_visorState = visorState;
|
||||
_weaponVisibility = weaponVisibility;
|
||||
_headGearVisibility = headGearVisibility;
|
||||
_autoDesignApplier = autoDesignApplier;
|
||||
_funModule = funModule;
|
||||
_humans = humans;
|
||||
_applier = applier;
|
||||
_movedEquipment = movedEquipment;
|
||||
_objects = objects;
|
||||
_gPose = gPose;
|
||||
_changeCustomizeService = changeCustomizeService;
|
||||
_customizations = customizations;
|
||||
_condition = condition;
|
||||
Subscribe();
|
||||
}
|
||||
|
||||
|
|
@ -212,7 +214,7 @@ public class StateListener : IDisposable
|
|||
&& _manager.TryGetValue(identifier, out var state))
|
||||
{
|
||||
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);
|
||||
|
|
@ -223,6 +225,34 @@ public class StateListener : IDisposable
|
|||
(_, 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)
|
||||
{
|
||||
_objects.Update();
|
||||
|
|
@ -235,11 +265,12 @@ public class StateListener : IDisposable
|
|||
var currentItem = state.BaseData.Item(slot);
|
||||
var model = state.ModelData.Weapon(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))
|
||||
continue;
|
||||
|
||||
var changed = changedItem.Weapon(stain);
|
||||
if (current.Value == changed.Value && state[slot, false] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||
if (current.Value == changed.Value && state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
|
||||
{
|
||||
_manager.ChangeItem(state, slot, currentItem, StateChanged.Source.Game);
|
||||
_manager.ChangeStain(state, slot, current.Stain, StateChanged.Source.Game);
|
||||
|
|
@ -247,10 +278,10 @@ public class StateListener : IDisposable
|
|||
{
|
||||
case EquipSlot.MainHand:
|
||||
case EquipSlot.OffHand:
|
||||
_applier.ChangeWeapon(objects, slot, currentItem, stain);
|
||||
_applier.ChangeWeapon(objects, slot, currentItem, stain, crest);
|
||||
break;
|
||||
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());
|
||||
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.
|
||||
case UpdateState.Transformed: break;
|
||||
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);
|
||||
else
|
||||
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);
|
||||
else
|
||||
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;
|
||||
case UpdateState.NoChange:
|
||||
apply = true;
|
||||
|
|
@ -346,6 +382,7 @@ public class StateListener : IDisposable
|
|||
var item = _items.Identify(slot, actorArmor.Set, actorArmor.Variant);
|
||||
state.BaseData.SetItem(EquipSlot.Head, item);
|
||||
state.BaseData.SetStain(EquipSlot.Head, actorArmor.Stain);
|
||||
state.BaseData.SetCrest(EquipSlot.Head, actor.GetCrest(slot));
|
||||
return UpdateState.Change;
|
||||
}
|
||||
|
||||
|
|
@ -374,6 +411,20 @@ public class StateListener : IDisposable
|
|||
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>
|
||||
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.
|
||||
case UpdateState.Change:
|
||||
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);
|
||||
else
|
||||
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);
|
||||
else
|
||||
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>
|
||||
private unsafe UpdateState UpdateBaseData(Actor actor, ActorState state, EquipSlot slot, CharacterWeapon weapon)
|
||||
{
|
||||
|
|
@ -415,6 +484,7 @@ public class StateListener : IDisposable
|
|||
return UpdateState.Transformed;
|
||||
}
|
||||
|
||||
var crest = actor.GetCrest(slot);
|
||||
var baseData = state.BaseData.Weapon(slot);
|
||||
var change = UpdateState.NoChange;
|
||||
|
||||
|
|
@ -610,6 +680,7 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase += OnCreatingCharacterBase;
|
||||
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
|
||||
_slotUpdating.Subscribe(OnSlotUpdating, SlotUpdating.Priority.StateListener);
|
||||
_crestVisibilityUpdating.Subscribe(OnCrestVisibilityUpdating, CrestVisibilityUpdating.Priority.StateListener);
|
||||
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||
|
|
@ -623,6 +694,7 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase -= OnCreatingCharacterBase;
|
||||
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
|
||||
_slotUpdating.Unsubscribe(OnSlotUpdating);
|
||||
_crestVisibilityUpdating.Unsubscribe(OnCrestVisibilityUpdating);
|
||||
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||
_visorState.Unsubscribe(OnVisorChange);
|
||||
|
|
|
|||
|
|
@ -143,10 +143,12 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
ret.Customize = model.GetCustomize();
|
||||
|
||||
// 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 headItem = _items.Identify(EquipSlot.Head, head.Set, head.Variant);
|
||||
var head = ret.IsHatVisible() || ignoreHatState ? model.GetArmor(EquipSlot.Head) : actor.GetArmor(EquipSlot.Head);
|
||||
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.SetStain(EquipSlot.Head, head.Stain);
|
||||
ret.SetCrest(EquipSlot.Head, headCrest);
|
||||
|
||||
// The other slots can be used from the draw object.
|
||||
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);
|
||||
ret.SetItem(slot, item);
|
||||
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.
|
||||
|
|
@ -174,6 +177,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
||||
ret.SetItem(slot, item);
|
||||
ret.SetStain(slot, armor.Stain);
|
||||
ret.SetCrest(slot, actor.GetCrest(slot));
|
||||
}
|
||||
|
||||
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);
|
||||
ret.SetItem(EquipSlot.MainHand, mainItem);
|
||||
ret.SetStain(EquipSlot.MainHand, main.Stain);
|
||||
ret.SetCrest(EquipSlot.MainHand, actor.GetCrest(EquipSlot.MainHand));
|
||||
ret.SetItem(EquipSlot.OffHand, offItem);
|
||||
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.
|
||||
// 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));
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
||||
public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, uint key = 0)
|
||||
/// <summary> Change a single piece of equipment including stain and crest visibility. </summary>
|
||||
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;
|
||||
|
||||
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||
var actors = type is StateChanged.Type.Equip
|
||||
? _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,
|
||||
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(
|
||||
$"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")}.]");
|
||||
_event.Invoke(type, source, state, actors, (old, item, slot));
|
||||
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (oldStain, stain, slot));
|
||||
$"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")}.]");
|
||||
if (item.HasValue)
|
||||
_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>
|
||||
|
|
@ -304,6 +314,19 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
_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>
|
||||
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)
|
||||
{
|
||||
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,
|
||||
(true, 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),
|
||||
(true, true) => _editor.ChangeEquip(state, slot, design.DesignData.Item(slot), design.DesignData.Stain(slot), source, out _,
|
||||
out _, key),
|
||||
(false, false, false) => false,
|
||||
(true, false, false) => _editor.ChangeItem(state, slot, design.DesignData.Item(slot), source, out _, key),
|
||||
(false, true, false) => _editor.ChangeStain(state, slot, design.DesignData.Stain(slot), source, out _, key),
|
||||
(false, false, true) => _editor.ChangeCrest(state, slot, design.DesignData.Crest(slot), source, 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();
|
||||
|
||||
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);
|
||||
|
|
@ -415,14 +439,14 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state[slot, false] is not StateChanged.Source.Ipc,
|
||||
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.ModelData.Crest(slot), state[slot, ActorState.EquipField.Item] is not StateChanged.Source.Ipc,
|
||||
state.ModelData.IsHatVisible());
|
||||
}
|
||||
|
||||
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;
|
||||
_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)
|
||||
|
|
@ -450,8 +474,9 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
|
||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||
{
|
||||
state[slot, true] = StateChanged.Source.Game;
|
||||
state[slot, false] = StateChanged.Source.Game;
|
||||
state[slot, ActorState.EquipField.Stain] = 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>())
|
||||
|
|
@ -478,15 +503,21 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue