mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Make attributes and shapes completely toggleable.
This commit is contained in:
parent
75f4e66dbf
commit
b48c4f440a
6 changed files with 245 additions and 156 deletions
|
|
@ -8,10 +8,12 @@ namespace Penumbra.Collections.Cache;
|
|||
public sealed class AtrCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<AtrIdentifier, AtrEntry>(manager, collection)
|
||||
{
|
||||
public bool ShouldBeDisabled(in ShapeAttributeString attribute, HumanSlot slot, PrimaryId id, GenderRace genderRace)
|
||||
=> DisabledCount > 0 && _atrData.TryGetValue(attribute, out var value) && value.Contains(slot, id, genderRace);
|
||||
=> DisabledCount > 0 && _atrData.TryGetValue(attribute, out var value) && value.CheckEntry(slot, id, genderRace) is false;
|
||||
|
||||
public int EnabledCount { get; private set; }
|
||||
public int DisabledCount { get; private set; }
|
||||
|
||||
|
||||
internal IReadOnlyDictionary<ShapeAttributeString, ShapeAttributeHashSet> Data
|
||||
=> _atrData;
|
||||
|
||||
|
|
@ -21,24 +23,28 @@ public sealed class AtrCache(MetaFileManager manager, ModCollection collection)
|
|||
{
|
||||
Clear();
|
||||
_atrData.Clear();
|
||||
DisabledCount = 0;
|
||||
EnabledCount = 0;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool _)
|
||||
=> Clear();
|
||||
=> Reset();
|
||||
|
||||
protected override void ApplyModInternal(AtrIdentifier identifier, AtrEntry entry)
|
||||
{
|
||||
if (!_atrData.TryGetValue(identifier.Attribute, out var value))
|
||||
{
|
||||
if (entry.Value)
|
||||
return;
|
||||
|
||||
value = [];
|
||||
_atrData.Add(identifier.Attribute, value);
|
||||
}
|
||||
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, !entry.Value))
|
||||
++DisabledCount;
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, entry.Value, out _))
|
||||
{
|
||||
if (entry.Value)
|
||||
++EnabledCount;
|
||||
else
|
||||
++DisabledCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RevertModInternal(AtrIdentifier identifier)
|
||||
|
|
@ -46,9 +52,12 @@ public sealed class AtrCache(MetaFileManager manager, ModCollection collection)
|
|||
if (!_atrData.TryGetValue(identifier.Attribute, out var value))
|
||||
return;
|
||||
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, false))
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, null, out var which))
|
||||
{
|
||||
--DisabledCount;
|
||||
if (which)
|
||||
--EnabledCount;
|
||||
else
|
||||
--DisabledCount;
|
||||
if (value.IsEmpty)
|
||||
_atrData.Remove(identifier.Attribute);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,93 +19,126 @@ public sealed class ShapeAttributeHashSet : Dictionary<(HumanSlot Slot, PrimaryI
|
|||
public static readonly FrozenDictionary<GenderRace, int> GenderRaceIndices =
|
||||
GenderRaceValues.WithIndex().ToFrozenDictionary(p => p.Value, p => p.Index);
|
||||
|
||||
private readonly BitArray _allIds = new((ShapeAttributeManager.ModelSlotSize + 1) * GenderRaceValues.Count);
|
||||
private readonly BitArray _allIds = new(2 * (ShapeAttributeManager.ModelSlotSize + 1) * GenderRaceValues.Count);
|
||||
|
||||
public bool? this[HumanSlot slot]
|
||||
=> AllCheck(ToIndex(slot, 0));
|
||||
|
||||
public bool? this[GenderRace genderRace]
|
||||
=> ToIndex(HumanSlot.Unknown, genderRace, out var index) ? AllCheck(index) : null;
|
||||
|
||||
public bool? this[HumanSlot slot, GenderRace genderRace]
|
||||
=> ToIndex(slot, genderRace, out var index) ? AllCheck(index) : null;
|
||||
|
||||
public bool? All
|
||||
=> Convert(_allIds[2 * AllIndex], _allIds[2 * AllIndex + 1]);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private bool CheckGroups(HumanSlot slot, GenderRace genderRace)
|
||||
{
|
||||
if (All || this[slot])
|
||||
return true;
|
||||
|
||||
if (!GenderRaceIndices.TryGetValue(genderRace, out var index))
|
||||
return false;
|
||||
|
||||
if (_allIds[ToIndex(HumanSlot.Unknown, index)])
|
||||
return true;
|
||||
|
||||
return _allIds[ToIndex(slot, index)];
|
||||
}
|
||||
|
||||
public bool this[HumanSlot slot]
|
||||
=> _allIds[ToIndex(slot, 0)];
|
||||
|
||||
public bool this[GenderRace genderRace]
|
||||
=> ToIndex(HumanSlot.Unknown, genderRace, out var index) && _allIds[index];
|
||||
|
||||
public bool this[HumanSlot slot, GenderRace genderRace]
|
||||
=> ToIndex(slot, genderRace, out var index) && _allIds[index];
|
||||
|
||||
public bool All
|
||||
=> _allIds[AllIndex];
|
||||
private bool? AllCheck(int idx)
|
||||
=> Convert(_allIds[idx], _allIds[idx + 1]);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int ToIndex(HumanSlot slot, int genderRaceIndex)
|
||||
=> slot is HumanSlot.Unknown ? genderRaceIndex + AllIndex : genderRaceIndex + (int)slot * GenderRaceValues.Count;
|
||||
=> 2 * (slot is HumanSlot.Unknown ? genderRaceIndex + AllIndex : genderRaceIndex + (int)slot * GenderRaceValues.Count);
|
||||
|
||||
public bool Contains(HumanSlot slot, PrimaryId id, GenderRace genderRace)
|
||||
=> CheckGroups(slot, genderRace) || ContainsEntry(slot, id, genderRace);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private bool ContainsEntry(HumanSlot slot, PrimaryId id, GenderRace genderRace)
|
||||
=> GenderRaceIndices.TryGetValue(genderRace, out var index)
|
||||
&& TryGetValue((slot, id), out var flags)
|
||||
&& ((flags & 1ul) is not 0 || (flags & (1ul << index)) is not 0);
|
||||
|
||||
public bool TrySet(HumanSlot slot, PrimaryId? id, GenderRace genderRace, bool value)
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public bool? CheckEntry(HumanSlot slot, PrimaryId id, GenderRace genderRace)
|
||||
{
|
||||
if (!GenderRaceIndices.TryGetValue(genderRace, out var index))
|
||||
return null;
|
||||
|
||||
// Check for specific ID.
|
||||
if (TryGetValue((slot, id), out var flags))
|
||||
{
|
||||
// Check completely specified entry.
|
||||
if (Convert(flags, 2 * index) is { } specified)
|
||||
return specified;
|
||||
|
||||
// Check any gender / race.
|
||||
if (Convert(flags, 0) is { } anyGr)
|
||||
return anyGr;
|
||||
}
|
||||
|
||||
// Check for specified gender / race and slot, but no ID.
|
||||
if (AllCheck(ToIndex(slot, index)) is { } noIdButGr)
|
||||
return noIdButGr;
|
||||
|
||||
// Check for specified gender / race but no slot or ID.
|
||||
if (AllCheck(ToIndex(HumanSlot.Unknown, index)) is { } noSlotButGr)
|
||||
return noSlotButGr;
|
||||
|
||||
// Check for specified slot but no gender / race or ID.
|
||||
if (AllCheck(ToIndex(slot, 0)) is { } noGrButSlot)
|
||||
return noGrButSlot;
|
||||
|
||||
return All;
|
||||
}
|
||||
|
||||
public bool TrySet(HumanSlot slot, PrimaryId? id, GenderRace genderRace, bool? value, out bool which)
|
||||
{
|
||||
which = false;
|
||||
if (!GenderRaceIndices.TryGetValue(genderRace, out var index))
|
||||
return false;
|
||||
|
||||
if (!id.HasValue)
|
||||
{
|
||||
var slotIndex = ToIndex(slot, index);
|
||||
var old = _allIds[slotIndex];
|
||||
_allIds[slotIndex] = value;
|
||||
return old != value;
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (TryGetValue((slot, id.Value), out var flags))
|
||||
var ret = false;
|
||||
if (value is true)
|
||||
{
|
||||
var newFlags = flags | (1ul << index);
|
||||
if (newFlags == flags)
|
||||
return false;
|
||||
if (!_allIds[slotIndex])
|
||||
ret = true;
|
||||
_allIds[slotIndex] = true;
|
||||
_allIds[slotIndex + 1] = false;
|
||||
}
|
||||
else if (value is false)
|
||||
{
|
||||
if (!_allIds[slotIndex + 1])
|
||||
ret = true;
|
||||
_allIds[slotIndex] = false;
|
||||
_allIds[slotIndex + 1] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_allIds[slotIndex])
|
||||
{
|
||||
which = true;
|
||||
ret = true;
|
||||
}
|
||||
else if (_allIds[slotIndex + 1])
|
||||
{
|
||||
which = false;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
this[(slot, id.Value)] = newFlags;
|
||||
return true;
|
||||
_allIds[slotIndex] = false;
|
||||
_allIds[slotIndex + 1] = false;
|
||||
}
|
||||
|
||||
this[(slot, id.Value)] = 1ul << index;
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
else if (TryGetValue((slot, id.Value), out var flags))
|
||||
|
||||
if (TryGetValue((slot, id.Value), out var flags))
|
||||
{
|
||||
var newFlags = flags & ~(1ul << index);
|
||||
var newFlags = value switch
|
||||
{
|
||||
true => (flags | (1ul << index)) & ~(1ul << (index + 1)),
|
||||
false => (flags & ~(1ul << index)) | (1ul << (index + 1)),
|
||||
_ => flags & ~(1ul << index) & ~(1ul << (index + 1)),
|
||||
};
|
||||
if (newFlags == flags)
|
||||
return false;
|
||||
|
||||
if (newFlags is 0)
|
||||
{
|
||||
Remove((slot, id.Value));
|
||||
return true;
|
||||
}
|
||||
|
||||
this[(slot, id.Value)] = newFlags;
|
||||
which = (flags & (1ul << index)) is not 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (value is null)
|
||||
return false;
|
||||
|
||||
this[(slot, id.Value)] = 1ul << (index + (value.Value ? 0 : 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
public new void Clear()
|
||||
|
|
@ -128,4 +161,20 @@ public sealed class ShapeAttributeHashSet : Dictionary<(HumanSlot Slot, PrimaryI
|
|||
index = ToIndex(slot, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static bool? Convert(bool trueValue, bool falseValue)
|
||||
=> trueValue ? true : falseValue ? false : null;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static bool? Convert(ulong mask, int idx)
|
||||
{
|
||||
mask >>= idx;
|
||||
return (mask & 3) switch
|
||||
{
|
||||
1 => true,
|
||||
2 => false,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ namespace Penumbra.Collections.Cache;
|
|||
public sealed class ShpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase<ShpIdentifier, ShpEntry>(manager, collection)
|
||||
{
|
||||
public bool ShouldBeEnabled(in ShapeAttributeString shape, HumanSlot slot, PrimaryId id, GenderRace genderRace)
|
||||
=> EnabledCount > 0 && _shpData.TryGetValue(shape, out var value) && value.Contains(slot, id, genderRace);
|
||||
=> EnabledCount > 0 && _shpData.TryGetValue(shape, out var value) && value.CheckEntry(slot, id, genderRace) is true;
|
||||
|
||||
public bool ShouldBeDisabled(in ShapeAttributeString shape, HumanSlot slot, PrimaryId id, GenderRace genderRace)
|
||||
=> DisabledCount > 0 && _shpData.TryGetValue(shape, out var value) && value.CheckEntry(slot, id, genderRace) is false;
|
||||
|
||||
internal IReadOnlyDictionary<ShapeAttributeString, ShapeAttributeHashSet> State(ShapeConnectorCondition connector)
|
||||
=> connector switch
|
||||
|
|
@ -20,7 +23,8 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection)
|
|||
_ => [],
|
||||
};
|
||||
|
||||
public int EnabledCount { get; private set; }
|
||||
public int EnabledCount { get; private set; }
|
||||
public int DisabledCount { get; private set; }
|
||||
|
||||
private readonly Dictionary<ShapeAttributeString, ShapeAttributeHashSet> _shpData = [];
|
||||
private readonly Dictionary<ShapeAttributeString, ShapeAttributeHashSet> _wristConnectors = [];
|
||||
|
|
@ -34,10 +38,12 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection)
|
|||
_wristConnectors.Clear();
|
||||
_waistConnectors.Clear();
|
||||
_ankleConnectors.Clear();
|
||||
EnabledCount = 0;
|
||||
DisabledCount = 0;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool _)
|
||||
=> Clear();
|
||||
=> Reset();
|
||||
|
||||
protected override void ApplyModInternal(ShpIdentifier identifier, ShpEntry entry)
|
||||
{
|
||||
|
|
@ -55,15 +61,17 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection)
|
|||
{
|
||||
if (!dict.TryGetValue(identifier.Shape, out var value))
|
||||
{
|
||||
if (!entry.Value)
|
||||
return;
|
||||
|
||||
value = [];
|
||||
dict.Add(identifier.Shape, value);
|
||||
}
|
||||
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, entry.Value))
|
||||
++EnabledCount;
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, entry.Value, out _))
|
||||
{
|
||||
if (entry.Value)
|
||||
++EnabledCount;
|
||||
else
|
||||
++DisabledCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,9 +92,12 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection)
|
|||
if (!dict.TryGetValue(identifier.Shape, out var value))
|
||||
return;
|
||||
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, false))
|
||||
if (value.TrySet(identifier.Slot, identifier.Id, identifier.GenderRaceCondition, null, out var which))
|
||||
{
|
||||
--EnabledCount;
|
||||
if (which)
|
||||
--EnabledCount;
|
||||
else
|
||||
--DisabledCount;
|
||||
if (value.IsEmpty)
|
||||
dict.Remove(identifier.Shape);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,46 +106,59 @@ public unsafe class ShapeAttributeManager : IRequiredService, IDisposable
|
|||
|
||||
private void UpdateDefaultMasks(Model human, ShpCache cache)
|
||||
{
|
||||
var genderRace = (GenderRace)human.AsHuman->RaceSexId;
|
||||
foreach (var (shape, topIndex) in _temporaryShapes[1])
|
||||
{
|
||||
if (shape.IsWrist() && _temporaryShapes[2].TryGetValue(shape, out var handIndex))
|
||||
if (shape.IsWrist()
|
||||
&& _temporaryShapes[2].TryGetValue(shape, out var handIndex)
|
||||
&& !cache.ShouldBeDisabled(shape, HumanSlot.Body, _ids[1], genderRace)
|
||||
&& !cache.ShouldBeDisabled(shape, HumanSlot.Hands, _ids[2], genderRace)
|
||||
&& human.AsHuman->Models[1] is not null
|
||||
&& human.AsHuman->Models[2] is not null)
|
||||
{
|
||||
human.AsHuman->Models[1]->EnabledShapeKeyIndexMask |= 1u << topIndex;
|
||||
human.AsHuman->Models[2]->EnabledShapeKeyIndexMask |= 1u << handIndex;
|
||||
CheckCondition(cache.State(ShapeConnectorCondition.Wrists), HumanSlot.Body, HumanSlot.Hands, 1, 2);
|
||||
CheckCondition(cache.State(ShapeConnectorCondition.Wrists), genderRace, HumanSlot.Body, HumanSlot.Hands, 1, 2);
|
||||
}
|
||||
|
||||
if (shape.IsWaist() && _temporaryShapes[3].TryGetValue(shape, out var legIndex))
|
||||
if (shape.IsWaist()
|
||||
&& _temporaryShapes[3].TryGetValue(shape, out var legIndex)
|
||||
&& !cache.ShouldBeDisabled(shape, HumanSlot.Body, _ids[1], genderRace)
|
||||
&& !cache.ShouldBeDisabled(shape, HumanSlot.Legs, _ids[3], genderRace)
|
||||
&& human.AsHuman->Models[1] is not null
|
||||
&& human.AsHuman->Models[3] is not null)
|
||||
{
|
||||
human.AsHuman->Models[1]->EnabledShapeKeyIndexMask |= 1u << topIndex;
|
||||
human.AsHuman->Models[3]->EnabledShapeKeyIndexMask |= 1u << legIndex;
|
||||
CheckCondition(cache.State(ShapeConnectorCondition.Waist), HumanSlot.Body, HumanSlot.Legs, 1, 3);
|
||||
CheckCondition(cache.State(ShapeConnectorCondition.Waist), genderRace, HumanSlot.Body, HumanSlot.Legs, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (shape, bottomIndex) in _temporaryShapes[3])
|
||||
{
|
||||
if (shape.IsAnkle() && _temporaryShapes[4].TryGetValue(shape, out var footIndex))
|
||||
if (shape.IsAnkle()
|
||||
&& _temporaryShapes[4].TryGetValue(shape, out var footIndex)
|
||||
&& !cache.ShouldBeDisabled(shape, HumanSlot.Legs, _ids[3], genderRace)
|
||||
&& !cache.ShouldBeDisabled(shape, HumanSlot.Feet, _ids[4], genderRace)
|
||||
&& human.AsHuman->Models[3] is not null
|
||||
&& human.AsHuman->Models[4] is not null)
|
||||
{
|
||||
human.AsHuman->Models[3]->EnabledShapeKeyIndexMask |= 1u << bottomIndex;
|
||||
human.AsHuman->Models[4]->EnabledShapeKeyIndexMask |= 1u << footIndex;
|
||||
CheckCondition(cache.State(ShapeConnectorCondition.Ankles), HumanSlot.Legs, HumanSlot.Feet, 3, 4);
|
||||
CheckCondition(cache.State(ShapeConnectorCondition.Ankles), genderRace, HumanSlot.Legs, HumanSlot.Feet, 3, 4);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void CheckCondition(IReadOnlyDictionary<ShapeAttributeString, ShapeAttributeHashSet> dict, HumanSlot slot1,
|
||||
void CheckCondition(IReadOnlyDictionary<ShapeAttributeString, ShapeAttributeHashSet> dict, GenderRace genderRace, HumanSlot slot1,
|
||||
HumanSlot slot2, int idx1, int idx2)
|
||||
{
|
||||
if (dict.Count is 0)
|
||||
return;
|
||||
|
||||
foreach (var (shape, set) in dict)
|
||||
{
|
||||
if (set.Contains(slot1, _ids[idx1], GenderRace.Unknown) && _temporaryShapes[idx1].TryGetValue(shape, out var index1))
|
||||
if (set.CheckEntry(slot1, _ids[idx1], genderRace) is true && _temporaryShapes[idx1].TryGetValue(shape, out var index1))
|
||||
human.AsHuman->Models[idx1]->EnabledShapeKeyIndexMask |= 1u << index1;
|
||||
if (set.Contains(slot2, _ids[idx2], GenderRace.Unknown) && _temporaryShapes[idx2].TryGetValue(shape, out var index2))
|
||||
if (set.CheckEntry(slot2, _ids[idx2], genderRace) is true && _temporaryShapes[idx2].TryGetValue(shape, out var index2))
|
||||
human.AsHuman->Models[idx2]->EnabledShapeKeyIndexMask |= 1u << index2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ namespace Penumbra.UI;
|
|||
public sealed class ConfigWindow : Window, IUiService
|
||||
{
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
private readonly Configuration _config;
|
||||
private readonly PerformanceTracker _tracker;
|
||||
private readonly ValidityChecker _validityChecker;
|
||||
private Penumbra? _penumbra;
|
||||
private ConfigTabBar _configTabs = null!;
|
||||
private string? _lastException;
|
||||
private readonly Configuration _config;
|
||||
private readonly PerformanceTracker _tracker;
|
||||
private readonly ValidityChecker _validityChecker;
|
||||
private Penumbra? _penumbra;
|
||||
private ConfigTabBar _configTabs = null!;
|
||||
private string? _lastException;
|
||||
|
||||
public ConfigWindow(PerformanceTracker tracker, IDalamudPluginInterface pi, Configuration config, ValidityChecker checker,
|
||||
TutorialService tutorial)
|
||||
|
|
|
|||
|
|
@ -52,11 +52,14 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
|
|||
return;
|
||||
|
||||
ImUtf8.TableSetupColumn("Attribute"u8, ImGuiTableColumnFlags.WidthFixed, 150 * ImUtf8.GlobalScale);
|
||||
ImUtf8.TableSetupColumn("Disabled"u8, ImGuiTableColumnFlags.WidthStretch);
|
||||
ImUtf8.TableSetupColumn("State"u8, ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
foreach (var (attribute, set) in data.ModCollection.MetaCache!.Atr.Data)
|
||||
DrawShapeAttribute(attribute, set);
|
||||
foreach (var (attribute, set) in data.ModCollection.MetaCache!.Atr.Data.OrderBy(a => a.Key))
|
||||
{
|
||||
ImUtf8.DrawTableColumn(attribute.AsSpan);
|
||||
DrawValues(attribute, set);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void DrawCollectionShapeCache(Actor actor)
|
||||
|
|
@ -72,83 +75,87 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver)
|
|||
|
||||
ImUtf8.TableSetupColumn("Condition"u8, ImGuiTableColumnFlags.WidthFixed, 150 * ImUtf8.GlobalScale);
|
||||
ImUtf8.TableSetupColumn("Shape"u8, ImGuiTableColumnFlags.WidthFixed, 150 * ImUtf8.GlobalScale);
|
||||
ImUtf8.TableSetupColumn("Enabled"u8, ImGuiTableColumnFlags.WidthStretch);
|
||||
ImUtf8.TableSetupColumn("State"u8, ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
foreach (var condition in Enum.GetValues<ShapeConnectorCondition>())
|
||||
{
|
||||
foreach (var (shape, set) in data.ModCollection.MetaCache!.Shp.State(condition))
|
||||
foreach (var (shape, set) in data.ModCollection.MetaCache!.Shp.State(condition).OrderBy(shp => shp.Key))
|
||||
{
|
||||
ImUtf8.DrawTableColumn(condition.ToString());
|
||||
DrawShapeAttribute(shape, set);
|
||||
ImUtf8.DrawTableColumn(shape.AsSpan);
|
||||
DrawValues(shape, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawShapeAttribute(in ShapeAttributeString shapeAttribute, ShapeAttributeHashSet set)
|
||||
private static void DrawValues(in ShapeAttributeString shapeAttribute, ShapeAttributeHashSet set)
|
||||
{
|
||||
ImUtf8.DrawTableColumn(shapeAttribute.AsSpan);
|
||||
if (set.All)
|
||||
{
|
||||
ImUtf8.DrawTableColumn("All"u8);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
foreach (var slot in ShapeAttributeManager.UsedModels)
|
||||
{
|
||||
if (!set[slot])
|
||||
continue;
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
ImUtf8.Text($"All {slot.ToName()}, ");
|
||||
if (set.All is { } value)
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value);
|
||||
ImUtf8.Text("All"u8);
|
||||
ImGui.SameLine(0, 0);
|
||||
}
|
||||
|
||||
foreach (var slot in ShapeAttributeManager.UsedModels)
|
||||
{
|
||||
if (set[slot] is not { } value2)
|
||||
continue;
|
||||
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value2);
|
||||
ImUtf8.Text($"All {slot.ToName()}, ");
|
||||
ImGui.SameLine(0, 0);
|
||||
}
|
||||
|
||||
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
|
||||
{
|
||||
if (set[gr] is { } value3)
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value3);
|
||||
ImUtf8.Text($"All {gr.ToName()}, ");
|
||||
ImGui.SameLine(0, 0);
|
||||
}
|
||||
|
||||
foreach (var gr in ShapeAttributeHashSet.GenderRaceValues.Skip(1))
|
||||
else
|
||||
{
|
||||
if (set[gr])
|
||||
foreach (var slot in ShapeAttributeManager.UsedModels)
|
||||
{
|
||||
ImUtf8.Text($"All {gr.ToName()}, ");
|
||||
ImGui.SameLine(0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var slot in ShapeAttributeManager.UsedModels)
|
||||
{
|
||||
if (!set[slot, gr])
|
||||
continue;
|
||||
|
||||
ImUtf8.Text($"All {gr.ToName()} {slot.ToName()}, ");
|
||||
ImGui.SameLine(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var ((slot, id), flags) in set)
|
||||
{
|
||||
if ((flags & 1ul) is not 0)
|
||||
{
|
||||
if (set[slot])
|
||||
if (set[slot, gr] is not { } value4)
|
||||
continue;
|
||||
|
||||
ImUtf8.Text($"{slot.ToName()} {id.Id:D4}, ");
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value4);
|
||||
ImUtf8.Text($"All {gr.ToName()} {slot.ToName()}, ");
|
||||
ImGui.SameLine(0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentFlags = flags >> 1;
|
||||
var currentIndex = BitOperations.TrailingZeroCount(currentFlags);
|
||||
while (currentIndex < ShapeAttributeHashSet.GenderRaceValues.Count)
|
||||
{
|
||||
var gr = ShapeAttributeHashSet.GenderRaceValues[currentIndex];
|
||||
if (set[slot, gr])
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ImUtf8.Text($"{gr.ToName()} {slot.ToName()} {id.Id:D4}, ");
|
||||
currentFlags >>= currentIndex;
|
||||
currentIndex = BitOperations.TrailingZeroCount(currentFlags);
|
||||
foreach (var ((slot, id), flags) in set)
|
||||
{
|
||||
if ((flags & 3) is not 0)
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), (flags & 2) is not 0);
|
||||
ImUtf8.Text($"{slot.ToName()} {id.Id:D4}, ");
|
||||
ImGui.SameLine(0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentFlags = flags >> 2;
|
||||
var currentIndex = BitOperations.TrailingZeroCount(currentFlags) / 2;
|
||||
while (currentIndex < ShapeAttributeHashSet.GenderRaceValues.Count)
|
||||
{
|
||||
var value5 = (currentFlags & 1) is 1;
|
||||
var gr = ShapeAttributeHashSet.GenderRaceValues[currentIndex];
|
||||
if (set[slot, gr] != value5)
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !value5);
|
||||
ImUtf8.Text($"{gr.ToName()} {slot.ToName()} #{id.Id:D4}, ");
|
||||
}
|
||||
|
||||
currentFlags >>= currentIndex * 2;
|
||||
currentIndex = BitOperations.TrailingZeroCount(currentFlags) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue