diff --git a/Penumbra/Collections/Cache/ShpCache.cs b/Penumbra/Collections/Cache/ShpCache.cs index eaf949d9..ee6a4e65 100644 --- a/Penumbra/Collections/Cache/ShpCache.cs +++ b/Penumbra/Collections/Cache/ShpCache.cs @@ -8,27 +8,24 @@ namespace Penumbra.Collections.Cache; public sealed class ShpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase(manager, collection) { public bool ShouldBeEnabled(in ShapeString shape, HumanSlot slot, PrimaryId id) - => _shpData.TryGetValue(shape, out var value) && value.Contains(slot, id); + => EnabledCount > 0 && _shpData.TryGetValue(shape, out var value) && value.Contains(slot, id); - internal IReadOnlyDictionary State - => _shpData; - - internal IEnumerable<(ShapeString, IReadOnlyDictionary)> ConditionState - => _conditionalSet.Select(kvp => (kvp.Key, (IReadOnlyDictionary)kvp.Value)); - - public bool CheckConditionState(ShapeString condition, [NotNullWhen(true)] out IReadOnlyDictionary? dict) - { - if (_conditionalSet.TryGetValue(condition, out var d)) + internal IReadOnlyDictionary State(ShapeConnectorCondition connector) + => connector switch { - dict = d; - return true; - } + ShapeConnectorCondition.None => _shpData, + ShapeConnectorCondition.Wrists => _wristConnectors, + ShapeConnectorCondition.Waist => _waistConnectors, + ShapeConnectorCondition.Ankles => _ankleConnectors, + _ => [], + }; - dict = null; - return false; - } + public int EnabledCount { get; private set; } + public bool ShouldBeEnabled(ShapeConnectorCondition connector, in ShapeString shape, HumanSlot slot, PrimaryId id) + => State(connector).TryGetValue(shape, out var value) && value.Contains(slot, id); + public sealed class ShpHashSet : HashSet<(HumanSlot Slot, PrimaryId Id)> { private readonly BitArray _allIds = new(ShapeManager.ModelSlotSize); @@ -92,14 +89,18 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection) => !_allIds.HasAnySet() && Count is 0; } - private readonly Dictionary _shpData = []; - private readonly Dictionary> _conditionalSet = []; + private readonly Dictionary _shpData = []; + private readonly Dictionary _wristConnectors = []; + private readonly Dictionary _waistConnectors = []; + private readonly Dictionary _ankleConnectors = []; public void Reset() { Clear(); _shpData.Clear(); - _conditionalSet.Clear(); + _wristConnectors.Clear(); + _waistConnectors.Clear(); + _ankleConnectors.Clear(); } protected override void Dispose(bool _) @@ -107,24 +108,16 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection) protected override void ApplyModInternal(ShpIdentifier identifier, ShpEntry entry) { - if (identifier.ShapeCondition.Length > 0) + switch (identifier.ConnectorCondition) { - if (!_conditionalSet.TryGetValue(identifier.ShapeCondition, out var shapes)) - { - if (!entry.Value) - return; - - shapes = new Dictionary(); - _conditionalSet.Add(identifier.ShapeCondition, shapes); - } - - Func(shapes); - } - else - { - Func(_shpData); + case ShapeConnectorCondition.None: Func(_shpData); break; + case ShapeConnectorCondition.Wrists: Func(_wristConnectors); break; + case ShapeConnectorCondition.Waist: Func(_waistConnectors); break; + case ShapeConnectorCondition.Ankles: Func(_ankleConnectors); break; } + return; + void Func(Dictionary dict) { if (!dict.TryGetValue(identifier.Shape, out var value)) @@ -136,22 +129,19 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection) dict.Add(identifier.Shape, value); } - value.TrySet(identifier.Slot, identifier.Id, entry); + if (value.TrySet(identifier.Slot, identifier.Id, entry)) + ++EnabledCount; } } protected override void RevertModInternal(ShpIdentifier identifier) { - if (identifier.ShapeCondition.Length > 0) + switch (identifier.ConnectorCondition) { - if (!_conditionalSet.TryGetValue(identifier.ShapeCondition, out var shapes)) - return; - - Func(shapes); - } - else - { - Func(_shpData); + case ShapeConnectorCondition.None: Func(_shpData); break; + case ShapeConnectorCondition.Wrists: Func(_wristConnectors); break; + case ShapeConnectorCondition.Waist: Func(_waistConnectors); break; + case ShapeConnectorCondition.Ankles: Func(_ankleConnectors); break; } return; @@ -162,7 +152,10 @@ public sealed class ShpCache(MetaFileManager manager, ModCollection collection) return; if (value.TrySet(identifier.Slot, identifier.Id, ShpEntry.False) && value.IsEmpty) + { + --EnabledCount; _shpData.Remove(identifier.Shape); + } } } } diff --git a/Penumbra/Meta/Manipulations/ShpIdentifier.cs b/Penumbra/Meta/Manipulations/ShpIdentifier.cs index 777be512..b3fdb0cb 100644 --- a/Penumbra/Meta/Manipulations/ShpIdentifier.cs +++ b/Penumbra/Meta/Manipulations/ShpIdentifier.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; @@ -7,7 +8,16 @@ using Penumbra.Interop.Structs; namespace Penumbra.Meta.Manipulations; -public readonly record struct ShpIdentifier(HumanSlot Slot, PrimaryId? Id, ShapeString Shape, ShapeString ShapeCondition) +[JsonConverter(typeof(StringEnumConverter))] +public enum ShapeConnectorCondition : byte +{ + None = 0, + Wrists = 1, + Waist = 2, + Ankles = 3, +} + +public readonly record struct ShpIdentifier(HumanSlot Slot, PrimaryId? Id, ShapeString Shape, ShapeConnectorCondition ConnectorCondition) : IComparable, IMetaIdentifier { public int CompareTo(ShpIdentifier other) @@ -34,11 +44,11 @@ public readonly record struct ShpIdentifier(HumanSlot Slot, PrimaryId? Id, Shape return 1; } - var shapeComparison = Shape.CompareTo(other.Shape); - if (shapeComparison is not 0) - return shapeComparison; + var conditionComparison = ConnectorCondition.CompareTo(other.ConnectorCondition); + if (conditionComparison is not 0) + return conditionComparison; - return ShapeCondition.CompareTo(other.ShapeCondition); + return Shape.CompareTo(other.Shape); } @@ -62,9 +72,13 @@ public readonly record struct ShpIdentifier(HumanSlot Slot, PrimaryId? Id, Shape sb.Append("All IDs"); } - if (ShapeCondition.Length > 0) - sb.Append(" - ") - .Append(ShapeCondition); + switch (ConnectorCondition) + { + case ShapeConnectorCondition.Wrists: sb.Append(" - Wrist Connector"); break; + case ShapeConnectorCondition.Waist: sb.Append(" - Waist Connector"); break; + case ShapeConnectorCondition.Ankles: sb.Append(" - Ankle Connector"); break; + } + return sb.ToString(); } @@ -87,20 +101,16 @@ public readonly record struct ShpIdentifier(HumanSlot Slot, PrimaryId? Id, Shape if (!ValidateCustomShapeString(Shape)) return false; - if (ShapeCondition.Length is 0) - return true; - - if (!ValidateCustomShapeString(ShapeCondition)) + if (!Enum.IsDefined(ConnectorCondition)) return false; - return Slot switch + return ConnectorCondition switch { - HumanSlot.Hands when ShapeCondition.IsWrist() => true, - HumanSlot.Body when ShapeCondition.IsWrist() || ShapeCondition.IsWaist() => true, - HumanSlot.Legs when ShapeCondition.IsWaist() || ShapeCondition.IsAnkle() => true, - HumanSlot.Feet when ShapeCondition.IsAnkle() => true, - HumanSlot.Unknown when ShapeCondition.IsWrist() || ShapeCondition.IsWaist() || ShapeCondition.IsAnkle() => true, - _ => false, + ShapeConnectorCondition.None => true, + ShapeConnectorCondition.Wrists => Slot is HumanSlot.Body or HumanSlot.Hands or HumanSlot.Unknown, + ShapeConnectorCondition.Waist => Slot is HumanSlot.Body or HumanSlot.Legs or HumanSlot.Unknown, + ShapeConnectorCondition.Ankles => Slot is HumanSlot.Legs or HumanSlot.Feet or HumanSlot.Unknown, + _ => false, }; } @@ -145,8 +155,8 @@ public readonly record struct ShpIdentifier(HumanSlot Slot, PrimaryId? Id, Shape if (Id.HasValue) jObj["Id"] = Id.Value.Id.ToString(); jObj["Shape"] = Shape.ToString(); - if (ShapeCondition.Length > 0) - jObj["ShapeCondition"] = ShapeCondition.ToString(); + if (ConnectorCondition is not ShapeConnectorCondition.None) + jObj["ConnectorCondition"] = ConnectorCondition.ToString(); return jObj; } @@ -156,11 +166,10 @@ public readonly record struct ShpIdentifier(HumanSlot Slot, PrimaryId? Id, Shape if (shape is null || !ShapeString.TryRead(shape, out var shapeString)) return null; - var slot = jObj["Slot"]?.ToObject() ?? HumanSlot.Unknown; - var id = jObj["Id"]?.ToObject(); - var shapeCondition = jObj["ShapeCondition"]?.ToObject(); - var shapeConditionString = shapeCondition is null || !ShapeString.TryRead(shapeCondition, out var s) ? ShapeString.Empty : s; - var identifier = new ShpIdentifier(slot, id, shapeString, shapeConditionString); + var slot = jObj["Slot"]?.ToObject() ?? HumanSlot.Unknown; + var id = jObj["Id"]?.ToObject(); + var connectorCondition = jObj["ConnectorCondition"]?.ToObject() ?? ShapeConnectorCondition.None; + var identifier = new ShpIdentifier(slot, id, shapeString, connectorCondition); return identifier.Validate() ? identifier : null; } diff --git a/Penumbra/Meta/ShapeManager.cs b/Penumbra/Meta/ShapeManager.cs index 57f6f23f..7431b1c2 100644 --- a/Penumbra/Meta/ShapeManager.cs +++ b/Penumbra/Meta/ShapeManager.cs @@ -1,4 +1,3 @@ -using System.Reflection.Metadata.Ecma335; using OtterGui.Services; using Penumbra.Collections; using Penumbra.Collections.Cache; @@ -80,8 +79,7 @@ public class ShapeManager : IRequiredService, IDisposable { _temporaryIndices[i].TryAdd(shapeString, index); _temporaryMasks[i] |= (ushort)(1 << index); - if (cache.State.Count > 0 - && cache.ShouldBeEnabled(shapeString, modelIndex, _ids[(int)modelIndex])) + if (cache.ShouldBeEnabled(shapeString, modelIndex, _ids[(int)modelIndex])) _temporaryValues[i] |= (ushort)(1 << index); } else @@ -102,14 +100,14 @@ public class ShapeManager : IRequiredService, IDisposable { _temporaryValues[1] |= 1u << topIndex; _temporaryValues[2] |= 1u << handIndex; - CheckCondition(shape, HumanSlot.Body, HumanSlot.Hands, 1, 2); + CheckCondition(cache.State(ShapeConnectorCondition.Wrists), HumanSlot.Body, HumanSlot.Hands, 1, 2); } if (shape.IsWaist() && _temporaryIndices[3].TryGetValue(shape, out var legIndex)) { _temporaryValues[1] |= 1u << topIndex; _temporaryValues[3] |= 1u << legIndex; - CheckCondition(shape, HumanSlot.Body, HumanSlot.Legs, 1, 3); + CheckCondition(cache.State(ShapeConnectorCondition.Waist), HumanSlot.Body, HumanSlot.Legs, 1, 3); } } @@ -119,25 +117,23 @@ public class ShapeManager : IRequiredService, IDisposable { _temporaryValues[3] |= 1u << bottomIndex; _temporaryValues[4] |= 1u << footIndex; - CheckCondition(shape, HumanSlot.Legs, HumanSlot.Feet, 3, 4); + CheckCondition(cache.State(ShapeConnectorCondition.Ankles), HumanSlot.Legs, HumanSlot.Feet, 3, 4); } } return; - void CheckCondition(in ShapeString shape, HumanSlot slot1, HumanSlot slot2, int idx1, int idx2) + void CheckCondition(IReadOnlyDictionary dict, HumanSlot slot1, HumanSlot slot2, int idx1, int idx2) { - if (!cache.CheckConditionState(shape, out var dict)) + if (dict.Count is 0) return; - foreach (var (subShape, set) in dict) + foreach (var (shape, set) in dict) { - if (set.Contains(slot1, _ids[idx1])) - if (_temporaryIndices[idx1].TryGetValue(subShape, out var subIndex)) - _temporaryValues[idx1] |= 1u << subIndex; - if (set.Contains(slot2, _ids[idx2])) - if (_temporaryIndices[idx2].TryGetValue(subShape, out var subIndex)) - _temporaryValues[idx2] |= 1u << subIndex; + if (set.Contains(slot1, _ids[idx1]) && _temporaryIndices[idx1].TryGetValue(shape, out var index1)) + _temporaryValues[idx1] |= 1u << index1; + if (set.Contains(slot2, _ids[idx2]) && _temporaryIndices[idx2].TryGetValue(shape, out var index2)) + _temporaryValues[idx2] |= 1u << index2; } } } diff --git a/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs b/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs index fe7e743c..c40726f8 100644 --- a/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs +++ b/Penumbra/UI/AdvancedWindow/Meta/ShpMetaDrawer.cs @@ -19,10 +19,8 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile public override ReadOnlySpan Label => "Shape Keys (SHP)###SHP"u8; - private ShapeString _buffer = ShapeString.TryRead("shpx_"u8, out var s) ? s : ShapeString.Empty; - private ShapeString _conditionBuffer = ShapeString.Empty; + private ShapeString _buffer = ShapeString.TryRead("shpx_"u8, out var s) ? s : ShapeString.Empty; private bool _identifierValid; - private bool _conditionValid = true; public override int NumColumns => 7; @@ -32,7 +30,7 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile protected override void Initialize() { - Identifier = new ShpIdentifier(HumanSlot.Unknown, null, ShapeString.Empty, ShapeString.Empty); + Identifier = new ShpIdentifier(HumanSlot.Unknown, null, ShapeString.Empty, ShapeConnectorCondition.None); } protected override void DrawNew() @@ -42,7 +40,7 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile new Lazy(() => MetaDictionary.SerializeTo([], Editor.Shp))); ImGui.TableNextColumn(); - var canAdd = !Editor.Contains(Identifier) && _identifierValid && _conditionValid; + var canAdd = !Editor.Contains(Identifier) && _identifierValid; var tt = canAdd ? "Stage this edit."u8 : _identifierValid @@ -69,7 +67,7 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile .OrderBy(kvp => kvp.Key.Shape) .ThenBy(kvp => kvp.Key.Slot) .ThenBy(kvp => kvp.Key.Id) - .ThenBy(kvp => kvp.Key.ShapeCondition) + .ThenBy(kvp => kvp.Key.ConnectorCondition) .Select(kvp => (kvp.Key, kvp.Value)); protected override int Count @@ -87,7 +85,7 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile changes |= DrawShapeKeyInput(ref identifier, ref _buffer, ref _identifierValid); ImGui.TableNextColumn(); - changes |= DrawShapeConditionInput(ref identifier, ref _conditionBuffer, ref _conditionValid); + changes |= DrawConnectorConditionInput(ref identifier); return changes; } @@ -109,9 +107,9 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile ImUtf8.TextFramed(identifier.Shape.AsSpan, FrameColor); ImGui.TableNextColumn(); - if (identifier.ShapeCondition.Length > 0) + if (identifier.ConnectorCondition is not ShapeConnectorCondition.None) { - ImUtf8.TextFramed(identifier.ShapeCondition.AsSpan, FrameColor); + ImUtf8.TextFramed($"{identifier.ConnectorCondition}", FrameColor); ImUtf8.HoverTooltip("Connector condition for this shape to be activated."); } } @@ -190,27 +188,18 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile } else { - if (_conditionBuffer.Length > 0 - && (_conditionBuffer.IsAnkle() && slot is not HumanSlot.Feet and not HumanSlot.Legs - || _conditionBuffer.IsWrist() && slot is not HumanSlot.Hands and not HumanSlot.Body - || _conditionBuffer.IsWaist() && slot is not HumanSlot.Body and not HumanSlot.Legs)) + identifier = identifier with { - identifier = identifier with + Slot = slot, + ConnectorCondition = Identifier.ConnectorCondition switch { - Slot = slot, - ShapeCondition = ShapeString.Empty, - }; - _conditionValid = false; - } - else - { - identifier = identifier with - { - Slot = slot, - ShapeCondition = _conditionBuffer, - }; - _conditionValid = true; - } + ShapeConnectorCondition.Wrists when slot is HumanSlot.Body or HumanSlot.Hands => ShapeConnectorCondition.Wrists, + ShapeConnectorCondition.Waist when slot is HumanSlot.Body or HumanSlot.Legs => ShapeConnectorCondition.Waist, + ShapeConnectorCondition.Ankles when slot is HumanSlot.Legs or HumanSlot.Feet => ShapeConnectorCondition.Ankles, + _ => ShapeConnectorCondition.None, + }, + }; + ret = true; } } } @@ -241,30 +230,40 @@ public sealed class ShpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFile return ret; } - public static unsafe bool DrawShapeConditionInput(ref ShpIdentifier identifier, ref ShapeString buffer, ref bool valid, - float unscaledWidth = 150) + public static unsafe bool DrawConnectorConditionInput(ref ShpIdentifier identifier, float unscaledWidth = 150) { - var ret = false; - var ptr = Unsafe.AsPointer(ref buffer); - var span = new Span(ptr, ShapeString.MaxLength + 1); - using (new ImRaii.ColorStyle().Push(ImGuiCol.Border, Colors.RegexWarningBorder, !valid).Push(ImGuiStyleVar.FrameBorderSize, 1f, !valid)) + var ret = false; + ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale); + var (showWrists, showWaist, showAnkles, disable) = identifier.Slot switch { - ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale); - if (ImUtf8.InputText("##shpCondition"u8, span, out int newLength, "Shape Condition..."u8)) + HumanSlot.Unknown => (true, true, true, false), + HumanSlot.Body => (true, true, false, false), + HumanSlot.Legs => (false, true, true, false), + HumanSlot.Hands => (true, false, false, false), + HumanSlot.Feet => (false, false, true, false), + _ => (false, false, false, true), + }; + using var disabled = ImRaii.Disabled(disable); + using (var combo = ImUtf8.Combo("##shpCondition"u8, $"{identifier.ConnectorCondition}")) + { + if (combo) { - buffer.ForceLength((byte)newLength); - valid = ShpIdentifier.ValidateCustomShapeString(buffer) - && (buffer.IsAnkle() && identifier.Slot is HumanSlot.Unknown or HumanSlot.Feet or HumanSlot.Legs - || buffer.IsWaist() && identifier.Slot is HumanSlot.Unknown or HumanSlot.Body or HumanSlot.Legs - || buffer.IsWrist() && identifier.Slot is HumanSlot.Unknown or HumanSlot.Body or HumanSlot.Hands); - if (valid) - identifier = identifier with { ShapeCondition = buffer }; - ret = true; + if (ImUtf8.Selectable("None"u8, identifier.ConnectorCondition is ShapeConnectorCondition.None)) + identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.None }; + + if (showWrists && ImUtf8.Selectable("Wrists"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Wrists)) + identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Wrists }; + + if (showWaist && ImUtf8.Selectable("Waist"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Waist)) + identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Waist }; + + if (showAnkles && ImUtf8.Selectable("Ankles"u8, identifier.ConnectorCondition is ShapeConnectorCondition.Ankles)) + identifier = identifier with { ConnectorCondition = ShapeConnectorCondition.Ankles }; } } ImUtf8.HoverTooltip( - "Supported conditional shape keys need to have the format `shpx_an_*` (Legs or Feet), `shpx_wr_*` (Body or Hands), or `shpx_wa_*` (Body or Legs) and a maximum length of 30 characters."u8); + "Only activate this shape key if any custom connector shape keys (shpx_[wr|wa|an]_*) are also enabled through matching attributes."u8); return ret; } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index 0b9fcde9..e148167b 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -36,41 +36,6 @@ using MdlMaterialEditor = Penumbra.Mods.Editor.MdlMaterialEditor; namespace Penumbra.UI.AdvancedWindow; -public sealed class OptionSelectCombo(ModEditor editor, ModEditWindow window) - : FilterComboCache<(string FullName, (int Group, int Data) Index)>( - () => window.Mod!.AllDataContainers.Select(c => (c.GetFullName(), c.GetDataIndices())).ToList(), MouseWheelType.Control, Penumbra.Log) -{ - private ImRaii.ColorStyle _border; - - protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight, - ImGuiComboFlags flags) - { - _border = ImRaii.PushFrameBorder(ImUtf8.GlobalScale, ColorId.FolderLine.Value()); - base.DrawCombo(label, preview, tooltip, currentSelected, previewWidth, itemHeight, flags); - _border.Dispose(); - } - - protected override void DrawFilter(int currentSelected, float width) - { - _border.Dispose(); - base.DrawFilter(currentSelected, width); - } - - public bool Draw(float width) - { - var flags = window.Mod!.AllDataContainers.Count() switch - { - 0 => ImGuiComboFlags.NoArrowButton, - > 8 => ImGuiComboFlags.HeightLargest, - _ => ImGuiComboFlags.None, - }; - return Draw("##optionSelector", editor.Option!.GetFullName(), string.Empty, width, ImGui.GetTextLineHeight(), flags); - } - - protected override bool DrawSelectable(int globalIdx, bool selected) - => ImUtf8.Selectable(Items[globalIdx].FullName, selected); -} - public partial class ModEditWindow : Window, IDisposable, IUiService { private const string WindowBaseLabel = "###SubModEdit"; diff --git a/Penumbra/UI/Tabs/Debug/ShapeInspector.cs b/Penumbra/UI/Tabs/Debug/ShapeInspector.cs index 8439587c..109cb5c4 100644 --- a/Penumbra/UI/Tabs/Debug/ShapeInspector.cs +++ b/Penumbra/UI/Tabs/Debug/ShapeInspector.cs @@ -8,6 +8,7 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.Interop.PathResolving; using Penumbra.Meta; +using Penumbra.Meta.Manipulations; namespace Penumbra.UI.Tabs.Debug; @@ -52,17 +53,11 @@ public class ShapeInspector(ObjectManager objects, CollectionResolver resolver) ImUtf8.TableSetupColumn("Enabled"u8, ImGuiTableColumnFlags.WidthStretch); ImGui.TableHeadersRow(); - foreach (var (shape, set) in data.ModCollection.MetaCache!.Shp.State) + foreach (var condition in Enum.GetValues()) { - ImGui.TableNextColumn(); - DrawShape(shape, set); - } - - foreach (var (condition, dict) in data.ModCollection.MetaCache!.Shp.ConditionState) - { - foreach (var (shape, set) in dict) + foreach (var (shape, set) in data.ModCollection.MetaCache!.Shp.State(condition)) { - ImUtf8.DrawTableColumn(condition.AsSpan); + ImUtf8.DrawTableColumn(condition.ToString()); DrawShape(shape, set); } } diff --git a/schemas/structs/meta_enums.json b/schemas/structs/meta_enums.json index 2fc65a0d..bad184e0 100644 --- a/schemas/structs/meta_enums.json +++ b/schemas/structs/meta_enums.json @@ -29,6 +29,10 @@ "$anchor": "SubRace", "enum": [ "Unknown", "Midlander", "Highlander", "Wildwood", "Duskwight", "Plainsfolk", "Dunesfolk", "SeekerOfTheSun", "KeeperOfTheMoon", "Seawolf", "Hellsguard", "Raen", "Xaela", "Helion", "Lost", "Rava", "Veena" ] }, + "ShapeConnectorCondition": { + "$anchor": "ShapeConnectorCondition", + "enum": [ "None", "Wrists", "Waist", "Ankles" ] + }, "U8": { "$anchor": "U8", "oneOf": [ diff --git a/schemas/structs/meta_shp.json b/schemas/structs/meta_shp.json index 4f868a0a..851842a4 100644 --- a/schemas/structs/meta_shp.json +++ b/schemas/structs/meta_shp.json @@ -17,11 +17,8 @@ "maxLength": 30, "pattern": "^shpx_" }, - "ShapeCondition": { - "type": "string", - "minLength": 8, - "maxLength": 30, - "pattern": "^shpx_(wa|an|wr)_" + "ConnectorCondition": { + "$ref": "meta_enums.json#ShapeConnectorCondition" } }, "required": [