Add support for Global EQP Changes.

This commit is contained in:
Ottermandias 2024-05-26 13:30:35 +02:00
parent f9527970cb
commit ed083f2a4c
15 changed files with 471 additions and 133 deletions

View file

@ -0,0 +1,80 @@
using OtterGui.Services;
using Penumbra.GameData.Structs;
namespace Penumbra.Meta.Manipulations;
public struct GlobalEqpCache : IService
{
private readonly HashSet<PrimaryId> _doNotHideEarrings = [];
private readonly HashSet<PrimaryId> _doNotHideNecklace = [];
private readonly HashSet<PrimaryId> _doNotHideBracelets = [];
private readonly HashSet<PrimaryId> _doNotHideRingL = [];
private readonly HashSet<PrimaryId> _doNotHideRingR = [];
private bool _doNotHideVieraHats;
private bool _doNotHideHrothgarHats;
public GlobalEqpCache()
{ }
public void Clear()
{
_doNotHideEarrings.Clear();
_doNotHideNecklace.Clear();
_doNotHideBracelets.Clear();
_doNotHideRingL.Clear();
_doNotHideRingR.Clear();
_doNotHideHrothgarHats = false;
_doNotHideVieraHats = false;
}
public unsafe EqpEntry Apply(EqpEntry original, CharacterArmor* armor)
{
if (_doNotHideVieraHats)
original |= EqpEntry.HeadShowVieraHat;
if (_doNotHideHrothgarHats)
original |= EqpEntry.HeadShowHrothgarHat;
if (_doNotHideEarrings.Contains(armor[5].Set))
original |= EqpEntry.HeadShowEarrings | EqpEntry.HeadShowEarringsAura | EqpEntry.HeadShowEarringsHuman;
if (_doNotHideNecklace.Contains(armor[6].Set))
original |= EqpEntry.BodyShowNecklace | EqpEntry.HeadShowNecklace;
if (_doNotHideBracelets.Contains(armor[7].Set))
original |= EqpEntry.BodyShowBracelet | EqpEntry.HandShowBracelet;
if (_doNotHideBracelets.Contains(armor[8].Set))
original |= EqpEntry.HandShowRingR;
if (_doNotHideBracelets.Contains(armor[9].Set))
original |= EqpEntry.HandShowRingL;
return original;
}
public bool Add(GlobalEqpManipulation manipulation)
=> manipulation.Type switch
{
GlobalEqpType.DoNotHideEarrings => _doNotHideEarrings.Add(manipulation.Condition),
GlobalEqpType.DoNotHideNecklace => _doNotHideNecklace.Add(manipulation.Condition),
GlobalEqpType.DoNotHideBracelets => _doNotHideBracelets.Add(manipulation.Condition),
GlobalEqpType.DoNotHideRingR => _doNotHideRingR.Add(manipulation.Condition),
GlobalEqpType.DoNotHideRingL => _doNotHideRingL.Add(manipulation.Condition),
GlobalEqpType.DoNotHideHrothgarHats => !_doNotHideHrothgarHats && (_doNotHideHrothgarHats = true),
GlobalEqpType.DoNotHideVieraHats => !_doNotHideVieraHats && (_doNotHideVieraHats = true),
_ => false,
};
public bool Remove(GlobalEqpManipulation manipulation)
=> manipulation.Type switch
{
GlobalEqpType.DoNotHideEarrings => _doNotHideEarrings.Remove(manipulation.Condition),
GlobalEqpType.DoNotHideNecklace => _doNotHideNecklace.Remove(manipulation.Condition),
GlobalEqpType.DoNotHideBracelets => _doNotHideBracelets.Remove(manipulation.Condition),
GlobalEqpType.DoNotHideRingR => _doNotHideRingR.Remove(manipulation.Condition),
GlobalEqpType.DoNotHideRingL => _doNotHideRingL.Remove(manipulation.Condition),
GlobalEqpType.DoNotHideHrothgarHats => _doNotHideHrothgarHats && !(_doNotHideHrothgarHats = false),
GlobalEqpType.DoNotHideVieraHats => _doNotHideVieraHats && !(_doNotHideVieraHats = false),
_ => false,
};
}

View file

@ -0,0 +1,50 @@
using Penumbra.GameData.Structs;
using Penumbra.Interop.Structs;
namespace Penumbra.Meta.Manipulations;
public readonly struct GlobalEqpManipulation : IMetaManipulation<GlobalEqpManipulation>
{
public GlobalEqpType Type { get; init; }
public PrimaryId Condition { get; init; }
public bool Validate()
{
if (!Enum.IsDefined(Type))
return false;
if (Type is GlobalEqpType.DoNotHideVieraHats or GlobalEqpType.DoNotHideHrothgarHats)
return Condition == 0;
return Condition != 0;
}
public bool Equals(GlobalEqpManipulation other)
=> Type == other.Type
&& Condition.Equals(other.Condition);
public int CompareTo(GlobalEqpManipulation other)
{
var typeComp = Type.CompareTo(other);
return typeComp != 0 ? typeComp : Condition.Id.CompareTo(other.Condition.Id);
}
public override bool Equals(object? obj)
=> obj is GlobalEqpManipulation other && Equals(other);
public override int GetHashCode()
=> HashCode.Combine((int)Type, Condition);
public static bool operator ==(GlobalEqpManipulation left, GlobalEqpManipulation right)
=> left.Equals(right);
public static bool operator !=(GlobalEqpManipulation left, GlobalEqpManipulation right)
=> !left.Equals(right);
public override string ToString()
=> $"Global EQP - {Type}{(Condition != 0 ? $" - {Condition.Id}" : string.Empty)}";
public MetaIndex FileIndex()
=> (MetaIndex)(-1);
}

View file

@ -0,0 +1,61 @@
namespace Penumbra.Meta.Manipulations;
public enum GlobalEqpType
{
DoNotHideEarrings,
DoNotHideNecklace,
DoNotHideBracelets,
DoNotHideRingR,
DoNotHideRingL,
DoNotHideHrothgarHats,
DoNotHideVieraHats,
}
public static class GlobalEqpExtensions
{
public static bool HasCondition(this GlobalEqpType type)
=> type switch
{
GlobalEqpType.DoNotHideEarrings => true,
GlobalEqpType.DoNotHideNecklace => true,
GlobalEqpType.DoNotHideBracelets => true,
GlobalEqpType.DoNotHideRingR => true,
GlobalEqpType.DoNotHideRingL => true,
GlobalEqpType.DoNotHideHrothgarHats => false,
GlobalEqpType.DoNotHideVieraHats => false,
_ => false,
};
public static ReadOnlySpan<byte> ToName(this GlobalEqpType type)
=> type switch
{
GlobalEqpType.DoNotHideEarrings => "Do Not Hide Earrings"u8,
GlobalEqpType.DoNotHideNecklace => "Do Not Hide Necklaces"u8,
GlobalEqpType.DoNotHideBracelets => "Do Not Hide Bracelets"u8,
GlobalEqpType.DoNotHideRingR => "Do Not Hide Rings (Right Finger)"u8,
GlobalEqpType.DoNotHideRingL => "Do Not Hide Rings (Left Finger)"u8,
GlobalEqpType.DoNotHideHrothgarHats => "Do Not Hide Hats for Hrothgar"u8,
GlobalEqpType.DoNotHideVieraHats => "Do Not Hide Hats for Viera"u8,
_ => "\0"u8,
};
public static ReadOnlySpan<byte> ToDescription(this GlobalEqpType type)
=> type switch
{
GlobalEqpType.DoNotHideEarrings => "Prevents the game from hiding earrings through other models when a specific earring is worn."u8,
GlobalEqpType.DoNotHideNecklace =>
"Prevents the game from hiding necklaces through other models when a specific necklace is worn."u8,
GlobalEqpType.DoNotHideBracelets =>
"Prevents the game from hiding bracelets through other models when a specific bracelet is worn."u8,
GlobalEqpType.DoNotHideRingR =>
"Prevents the game from hiding rings worn on the right finger through other models when a specific ring is worn on the right finger."u8,
GlobalEqpType.DoNotHideRingL =>
"Prevents the game from hiding rings worn on the left finger through other models when a specific ring is worn on the left finger."u8,
GlobalEqpType.DoNotHideHrothgarHats =>
"Prevents the game from hiding any hats for Hrothgar that are normally flagged to not display on them."u8,
GlobalEqpType.DoNotHideVieraHats =>
"Prevents the game from hiding any hats for Viera that are normally flagged to not display on them."u8,
_ => "\0"u8,
};
}

View file

@ -21,13 +21,14 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
public enum Type : byte
{
Unknown = 0,
Imc = 1,
Eqdp = 2,
Eqp = 3,
Est = 4,
Gmp = 5,
Rsp = 6,
Unknown = 0,
Imc = 1,
Eqdp = 2,
Eqp = 3,
Est = 4,
Gmp = 5,
Rsp = 6,
GlobalEqp = 7,
}
[FieldOffset(0)]
@ -54,6 +55,10 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
[JsonIgnore]
public readonly ImcManipulation Imc = default;
[FieldOffset(0)]
[JsonIgnore]
public readonly GlobalEqpManipulation GlobalEqp = default;
[FieldOffset(15)]
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty("Type")]
@ -63,14 +68,15 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
{
get => ManipulationType switch
{
Type.Unknown => null,
Type.Imc => Imc,
Type.Eqdp => Eqdp,
Type.Eqp => Eqp,
Type.Est => Est,
Type.Gmp => Gmp,
Type.Rsp => Rsp,
_ => null,
Type.Unknown => null,
Type.Imc => Imc,
Type.Eqdp => Eqdp,
Type.Eqp => Eqp,
Type.Est => Est,
Type.Gmp => Gmp,
Type.Rsp => Rsp,
Type.GlobalEqp => GlobalEqp,
_ => null,
};
init
{
@ -100,6 +106,10 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
Imc = m;
ManipulationType = m.Validate(true) ? Type.Imc : Type.Unknown;
return;
case GlobalEqpManipulation m:
GlobalEqp = m;
ManipulationType = m.Validate() ? Type.GlobalEqp : Type.Unknown;
return;
}
}
}
@ -108,13 +118,14 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
{
return ManipulationType switch
{
Type.Imc => Imc.Validate(true),
Type.Eqdp => Eqdp.Validate(),
Type.Eqp => Eqp.Validate(),
Type.Est => Est.Validate(),
Type.Gmp => Gmp.Validate(),
Type.Rsp => Rsp.Validate(),
_ => false,
Type.Imc => Imc.Validate(true),
Type.Eqdp => Eqdp.Validate(),
Type.Eqp => Eqp.Validate(),
Type.Est => Est.Validate(),
Type.Gmp => Gmp.Validate(),
Type.Rsp => Rsp.Validate(),
Type.GlobalEqp => GlobalEqp.Validate(),
_ => false,
};
}
@ -154,6 +165,12 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
ManipulationType = Type.Imc;
}
public MetaManipulation(GlobalEqpManipulation eqp)
{
GlobalEqp = eqp;
ManipulationType = Type.GlobalEqp;
}
public static implicit operator MetaManipulation(EqpManipulation eqp)
=> new(eqp);
@ -172,6 +189,9 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
public static implicit operator MetaManipulation(ImcManipulation imc)
=> new(imc);
public static implicit operator MetaManipulation(GlobalEqpManipulation eqp)
=> new(eqp);
public bool EntryEquals(MetaManipulation other)
{
if (ManipulationType != other.ManipulationType)
@ -179,13 +199,14 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
return ManipulationType switch
{
Type.Eqp => Eqp.Entry.Equals(other.Eqp.Entry),
Type.Gmp => Gmp.Entry.Equals(other.Gmp.Entry),
Type.Eqdp => Eqdp.Entry.Equals(other.Eqdp.Entry),
Type.Est => Est.Entry.Equals(other.Est.Entry),
Type.Rsp => Rsp.Entry.Equals(other.Rsp.Entry),
Type.Imc => Imc.Entry.Equals(other.Imc.Entry),
_ => throw new ArgumentOutOfRangeException(),
Type.Eqp => Eqp.Entry.Equals(other.Eqp.Entry),
Type.Gmp => Gmp.Entry.Equals(other.Gmp.Entry),
Type.Eqdp => Eqdp.Entry.Equals(other.Eqdp.Entry),
Type.Est => Est.Entry.Equals(other.Est.Entry),
Type.Rsp => Rsp.Entry.Equals(other.Rsp.Entry),
Type.Imc => Imc.Entry.Equals(other.Imc.Entry),
Type.GlobalEqp => true,
_ => throw new ArgumentOutOfRangeException(),
};
}
@ -196,13 +217,14 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
return ManipulationType switch
{
Type.Eqp => Eqp.Equals(other.Eqp),
Type.Gmp => Gmp.Equals(other.Gmp),
Type.Eqdp => Eqdp.Equals(other.Eqdp),
Type.Est => Est.Equals(other.Est),
Type.Rsp => Rsp.Equals(other.Rsp),
Type.Imc => Imc.Equals(other.Imc),
_ => false,
Type.Eqp => Eqp.Equals(other.Eqp),
Type.Gmp => Gmp.Equals(other.Gmp),
Type.Eqdp => Eqdp.Equals(other.Eqdp),
Type.Est => Est.Equals(other.Est),
Type.Rsp => Rsp.Equals(other.Rsp),
Type.Imc => Imc.Equals(other.Imc),
Type.GlobalEqp => GlobalEqp.Equals(other.GlobalEqp),
_ => false,
};
}
@ -213,13 +235,14 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
return ManipulationType switch
{
Type.Eqp => Eqp.Copy(other.Eqp.Entry),
Type.Gmp => Gmp.Copy(other.Gmp.Entry),
Type.Eqdp => Eqdp.Copy(other.Eqdp),
Type.Est => Est.Copy(other.Est.Entry),
Type.Rsp => Rsp.Copy(other.Rsp.Entry),
Type.Imc => Imc.Copy(other.Imc.Entry),
_ => throw new ArgumentOutOfRangeException(),
Type.Eqp => Eqp.Copy(other.Eqp.Entry),
Type.Gmp => Gmp.Copy(other.Gmp.Entry),
Type.Eqdp => Eqdp.Copy(other.Eqdp),
Type.Est => Est.Copy(other.Est.Entry),
Type.Rsp => Rsp.Copy(other.Rsp.Entry),
Type.Imc => Imc.Copy(other.Imc.Entry),
Type.GlobalEqp => GlobalEqp,
_ => throw new ArgumentOutOfRangeException(),
};
}
@ -229,13 +252,14 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
public override int GetHashCode()
=> ManipulationType switch
{
Type.Eqp => Eqp.GetHashCode(),
Type.Gmp => Gmp.GetHashCode(),
Type.Eqdp => Eqdp.GetHashCode(),
Type.Est => Est.GetHashCode(),
Type.Rsp => Rsp.GetHashCode(),
Type.Imc => Imc.GetHashCode(),
_ => 0,
Type.Eqp => Eqp.GetHashCode(),
Type.Gmp => Gmp.GetHashCode(),
Type.Eqdp => Eqdp.GetHashCode(),
Type.Est => Est.GetHashCode(),
Type.Rsp => Rsp.GetHashCode(),
Type.Imc => Imc.GetHashCode(),
Type.GlobalEqp => GlobalEqp.GetHashCode(),
_ => 0,
};
public unsafe int CompareTo(MetaManipulation other)
@ -249,13 +273,14 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
public override string ToString()
=> ManipulationType switch
{
Type.Eqp => Eqp.ToString(),
Type.Gmp => Gmp.ToString(),
Type.Eqdp => Eqdp.ToString(),
Type.Est => Est.ToString(),
Type.Rsp => Rsp.ToString(),
Type.Imc => Imc.ToString(),
_ => "Invalid",
Type.Eqp => Eqp.ToString(),
Type.Gmp => Gmp.ToString(),
Type.Eqdp => Eqdp.ToString(),
Type.Est => Est.ToString(),
Type.Rsp => Rsp.ToString(),
Type.Imc => Imc.ToString(),
Type.GlobalEqp => GlobalEqp.ToString(),
_ => "Invalid",
};
public string EntryToString()
@ -263,14 +288,15 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
{
Type.Imc =>
$"{Imc.Entry.DecalId}-{Imc.Entry.MaterialId}-{Imc.Entry.VfxId}-{Imc.Entry.SoundId}-{Imc.Entry.MaterialAnimationId}-{Imc.Entry.AttributeMask}",
Type.Eqdp => $"{(ushort)Eqdp.Entry:X}",
Type.Eqp => $"{(ulong)Eqp.Entry:X}",
Type.Est => $"{Est.Entry}",
Type.Gmp => $"{Gmp.Entry.Value}",
Type.Rsp => $"{Rsp.Entry}",
_ => string.Empty,
};
Type.Eqdp => $"{(ushort)Eqdp.Entry:X}",
Type.Eqp => $"{(ulong)Eqp.Entry:X}",
Type.Est => $"{Est.Entry}",
Type.Gmp => $"{Gmp.Entry.Value}",
Type.Rsp => $"{Rsp.Entry}",
Type.GlobalEqp => string.Empty,
_ => string.Empty,
};
public static bool operator ==(MetaManipulation left, MetaManipulation right)
=> left.Equals(right);