Add shape meta manipulations and rework attribute hook.

This commit is contained in:
Ottermandias 2025-05-15 17:46:53 +02:00
parent 0adec35848
commit 6ad0b4299a
23 changed files with 900 additions and 298 deletions

View file

@ -245,6 +245,8 @@ public sealed class CollectionCache : IDisposable
AddManipulation(mod, identifier, entry);
foreach (var (identifier, entry) in files.Manipulations.Atch)
AddManipulation(mod, identifier, entry);
foreach (var (identifier, entry) in files.Manipulations.Shp)
AddManipulation(mod, identifier, entry);
foreach (var identifier in files.Manipulations.GlobalEqp)
AddManipulation(mod, identifier, null!);
}

View file

@ -16,11 +16,12 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
public readonly RspCache Rsp = new(manager, collection);
public readonly ImcCache Imc = new(manager, collection);
public readonly AtchCache Atch = new(manager, collection);
public readonly ShpCache Shp = new(manager, collection);
public readonly GlobalEqpCache GlobalEqp = new();
public bool IsDisposed { get; private set; }
public int Count
=> Eqp.Count + Eqdp.Count + Est.Count + Gmp.Count + Rsp.Count + Imc.Count + Atch.Count + GlobalEqp.Count;
=> Eqp.Count + Eqdp.Count + Est.Count + Gmp.Count + Rsp.Count + Imc.Count + Atch.Count + Shp.Count + GlobalEqp.Count;
public IEnumerable<(IMetaIdentifier, IMod)> IdentifierSources
=> Eqp.Select(kvp => ((IMetaIdentifier)kvp.Key, kvp.Value.Source))
@ -30,6 +31,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
.Concat(Rsp.Select(kvp => ((IMetaIdentifier)kvp.Key, kvp.Value.Source)))
.Concat(Imc.Select(kvp => ((IMetaIdentifier)kvp.Key, kvp.Value.Source)))
.Concat(Atch.Select(kvp => ((IMetaIdentifier)kvp.Key, kvp.Value.Source)))
.Concat(Shp.Select(kvp => ((IMetaIdentifier)kvp.Key, kvp.Value.Source)))
.Concat(GlobalEqp.Select(kvp => ((IMetaIdentifier)kvp.Key, kvp.Value)));
public void Reset()
@ -41,6 +43,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
Rsp.Reset();
Imc.Reset();
Atch.Reset();
Shp.Reset();
GlobalEqp.Clear();
}
@ -57,6 +60,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
Rsp.Dispose();
Imc.Dispose();
Atch.Dispose();
Shp.Dispose();
}
public bool TryGetMod(IMetaIdentifier identifier, [NotNullWhen(true)] out IMod? mod)
@ -71,6 +75,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
ImcIdentifier i => Imc.TryGetValue(i, out var p) && Convert(p, out mod),
RspIdentifier i => Rsp.TryGetValue(i, out var p) && Convert(p, out mod),
AtchIdentifier i => Atch.TryGetValue(i, out var p) && Convert(p, out mod),
ShpIdentifier i => Shp.TryGetValue(i, out var p) && Convert(p, out mod),
GlobalEqpManipulation i => GlobalEqp.TryGetValue(i, out mod),
_ => false,
};
@ -92,6 +97,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
ImcIdentifier i => Imc.RevertMod(i, out mod),
RspIdentifier i => Rsp.RevertMod(i, out mod),
AtchIdentifier i => Atch.RevertMod(i, out mod),
ShpIdentifier i => Shp.RevertMod(i, out mod),
GlobalEqpManipulation i => GlobalEqp.RevertMod(i, out mod),
_ => (mod = null) != null,
};
@ -108,6 +114,7 @@ public class MetaCache(MetaFileManager manager, ModCollection collection)
ImcIdentifier i when entry is ImcEntry e => Imc.ApplyMod(mod, i, e),
RspIdentifier i when entry is RspEntry e => Rsp.ApplyMod(mod, i, e),
AtchIdentifier i when entry is AtchEntry e => Atch.ApplyMod(mod, i, e),
ShpIdentifier i when entry is ShpEntry e => Shp.ApplyMod(mod, i, e),
GlobalEqpManipulation i => GlobalEqp.ApplyMod(mod, i),
_ => false,
};

View file

@ -0,0 +1,109 @@
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections.Cache;
public sealed class ShpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<ShpIdentifier, ShpEntry>(manager, collection)
{
public bool ShouldBeEnabled(in ShapeString shape, HumanSlot slot, PrimaryId id)
=> _shpData.TryGetValue(shape, out var value) && value.Contains(slot, id);
internal IReadOnlyDictionary<ShapeString, ShpHashSet> State
=> _shpData;
internal sealed class ShpHashSet : HashSet<(HumanSlot Slot, PrimaryId Id)>
{
private readonly BitArray _allIds = new(ShapeManager.ModelSlotSize);
public bool All
{
get => _allIds[^1];
set => _allIds[^1] = value;
}
public bool this[HumanSlot slot]
{
get
{
if (slot is HumanSlot.Unknown)
return All;
return _allIds[(int)slot];
}
set
{
if (slot is HumanSlot.Unknown)
_allIds[^1] = value;
else
_allIds[(int)slot] = value;
}
}
public bool Contains(HumanSlot slot, PrimaryId id)
=> All || this[slot] || Contains((slot, id));
public bool TrySet(HumanSlot slot, PrimaryId? id, ShpEntry value)
{
if (slot is HumanSlot.Unknown)
{
var old = All;
All = value.Value;
return old != value.Value;
}
if (!id.HasValue)
{
var old = this[slot];
this[slot] = value.Value;
return old != value.Value;
}
if (value.Value)
return Add((slot, id.Value));
return Remove((slot, id.Value));
}
public new void Clear()
{
base.Clear();
_allIds.SetAll(false);
}
public bool IsEmpty
=> !_allIds.HasAnySet() && Count is 0;
}
private readonly Dictionary<ShapeString, ShpHashSet> _shpData = [];
public void Reset()
{
Clear();
_shpData.Clear();
}
protected override void Dispose(bool _)
=> Clear();
protected override void ApplyModInternal(ShpIdentifier identifier, ShpEntry entry)
{
if (!_shpData.TryGetValue(identifier.Shape, out var value))
{
value = [];
_shpData.Add(identifier.Shape, value);
}
value.TrySet(identifier.Slot, identifier.Id, entry);
}
protected override void RevertModInternal(ShpIdentifier identifier)
{
if (!_shpData.TryGetValue(identifier.Shape, out var value))
return;
if (value.TrySet(identifier.Slot, identifier.Id, ShpEntry.False) && value.IsEmpty)
_shpData.Remove(identifier.Shape);
}
}