diff --git a/src/AcDream.Plugins.MossTank/MossTankPanel.cs b/src/AcDream.Plugins.MossTank/MossTankPanel.cs index f6b20534..a5eff9a1 100644 --- a/src/AcDream.Plugins.MossTank/MossTankPanel.cs +++ b/src/AcDream.Plugins.MossTank/MossTankPanel.cs @@ -501,16 +501,23 @@ internal sealed partial class MossTankPanel // with a clVal VALUE column, and filters both through lFilterList — a // category checklist keyed by SettingsCategories' own per-setting // bitmask (VtankDefaultSettingsDatabase.SettingCategoryBitmasks, 136 - // rows straight from the embedded .usd). This worktree has no - // refs/vtank/ checkout, so VTank's own category NAME strings aren't - // available anywhere in this repo — the filter groups are labeled by - // their raw bit value ("0x04" etc.) instead of a guessed name; a - // setting with no recorded bitmask always shows regardless of filter - // state (never silently hidden by a filter that doesn't apply to it). + // rows straight from the embedded .usd). Round D (owner live report + // 2026-09-07: "Some Hex numbers with green button to the right. What + // is that?") replaced the raw bit-value labels ("0x04" etc.) with the + // real category NAMES (Misc, Recharge, MeleeCombat, SpellCombat, + // Ranges, Navigation, Buffing, Crafting, Looting) — see + // VtankOptionCatalog.CategoryNamesByBit for how each name is derived + // without a refs/vtank/ checkout. A setting with no recorded bitmask + // always shows regardless of filter state (never silently hidden by a + // filter that doesn't apply to it). public IReadOnlyList AdvancedOptionNames => _advancedOptionNames; public IReadOnlyList AdvancedOptionValueColumn => _advancedOptionValueColumn; public IReadOnlyList AdvancedOptionCategoryNames { get; } = - VtankOptionCatalog.CategoryBits.Select(bit => $"0x{bit:X}").ToArray(); + VtankOptionCatalog.CategoryBits + .Select(bit => VtankOptionCatalog.CategoryNamesByBit.TryGetValue(bit, out string? name) + ? name + : $"0x{bit:X}") + .ToArray(); public IReadOnlyList AdvancedOptionCategoryEnabled => _advancedOptionCategoryEnabledView; // Fix round B item 15's own build-over-real-files test caught this: // markup REQUIRES a "selected" int binding (MarkupDocument. diff --git a/src/AcDream.Plugins.MossTank/VtankOptionCatalog.cs b/src/AcDream.Plugins.MossTank/VtankOptionCatalog.cs index e5abaa75..83e6437b 100644 --- a/src/AcDream.Plugins.MossTank/VtankOptionCatalog.cs +++ b/src/AcDream.Plugins.MossTank/VtankOptionCatalog.cs @@ -404,6 +404,52 @@ internal static class VtankOptionCatalog .Order() .ToArray(); + /// + /// VTank's own lFilterList checklist NAMES (owner live report + /// 2026-09-07: the popup showed raw bitmask hex — "0x1", "0x2" — instead + /// of category names). This worktree has no refs/vtank/ checkout + /// to read VTank's own name/enum string literal for each bit directly, + /// so each name is instead anchored to a real setting whose Category + /// column in docs/research/vtank-kb/01-settings-and-profiles.md §2 + /// names EXACTLY ONE category (no | combination), then read back + /// THAT setting's own recorded bitmask from the embedded .usd + /// () + /// — never a typed-in hex literal, so the mapping cannot silently drift + /// from what the shipped database actually encodes. Anchors used (KB §2 + /// row, single-category setting, resulting bit): row 45 + /// RandomHelperBuffs→Misc(1), row 8 Recharge-Norm-HitP→Recharge(2), row + /// 31 DefaultMeleeAttackHeight→MeleeCombat(4), row 5 + /// SpellDiffExcessThreshold-Hunt→SpellCombat(8), row 18 + /// AttackDistance→Ranges(16), row 2 EnableNav→Navigation(32), row 3 + /// EnableBuffing→Buffing(64), row 7 + /// ArrowheadFletchDiffExcessThreshold→Crafting(128), row 1 + /// EnableLooting→Looting(256) — all 9 of . + /// + internal static readonly IReadOnlyDictionary CategoryNamesByBit = + BuildCategoryNamesByBit(); + + private static IReadOnlyDictionary BuildCategoryNamesByBit() + { + IReadOnlyDictionary bitmasks = VtankDefaultSettingsDatabase.SettingCategoryBitmasks; + (string Setting, string Category)[] anchors = + [ + ("RandomHelperBuffs", "Misc"), + ("Recharge-Norm-HitP", "Recharge"), + ("DefaultMeleeAttackHeight", "MeleeCombat"), + ("SpellDiffExcessThreshold-Hunt", "SpellCombat"), + ("AttackDistance", "Ranges"), + ("EnableNav", "Navigation"), + ("EnableBuffing", "Buffing"), + ("ArrowheadFletchDiffExcessThreshold", "Crafting"), + ("EnableLooting", "Looting"), + ]; + var map = new Dictionary(); + foreach ((string setting, string category) in anchors) + if (bitmasks.TryGetValue(setting, out int bit)) + map[bit] = category; + return map; + } + private static IEnumerable DecomposeBits(int mask) { for (int bit = 1; bit != 0 && bit <= mask; bit <<= 1) diff --git a/tests/AcDream.Plugins.MossTank.Tests/MossTankPanelTests.cs b/tests/AcDream.Plugins.MossTank.Tests/MossTankPanelTests.cs index 4a6e4874..558f1cf5 100644 --- a/tests/AcDream.Plugins.MossTank.Tests/MossTankPanelTests.cs +++ b/tests/AcDream.Plugins.MossTank.Tests/MossTankPanelTests.cs @@ -1280,6 +1280,30 @@ public sealed class MossTankPanelTests Assert.False(panel.LootEditorVisible); } + /// + /// Round D item 1 (owner live report 2026-09-07: "Some Hex numbers with + /// green button to the right. What is that?"): the category filter + /// checklist must show VTank's real category NAMES, never the raw + /// bitmask hex the popup showed before this fix. Order is + /// VtankOptionCatalog.CategoryBits' own ascending-bit order, which is + /// also VTank's cmbAdvOptFilterCategory order (Misc=1 through + /// Looting=256). + /// + [Fact] + public void AdvancedOptionCategoryNamesShowRealNamesNotHexBitmasks() + { + var panel = new MossTankPanel(new FakeHost(new FakeAutomation())); + + Assert.Equal( + [ + "Misc", "Recharge", "MeleeCombat", "SpellCombat", "Ranges", + "Navigation", "Buffing", "Crafting", "Looting", + ], + panel.AdvancedOptionCategoryNames); + Assert.DoesNotContain( + panel.AdvancedOptionCategoryNames, name => name.StartsWith("0x", StringComparison.Ordinal)); + } + [Fact] public void AdvancedOptionCategoryFilterHidesNonMatchingSettings() {