From ebe670adf5e802a83fad59133fb7ee8aecdf83c9 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 7 Sep 2026 16:21:57 +0200 Subject: [PATCH] =?UTF-8?q?fix(vtank):=20slice=207=20round=20D=20item=201?= =?UTF-8?q?=20=E2=80=94=20Advanced=20Options=20category=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner live report 2026-09-07: "Some Hex numbers with green button to the right. What is that?" — the Advanced Options popup's category filter checklist showed raw bitmask hex ("0x1", "0x2", ...) instead of VTank's real category names (Misc, Recharge, MeleeCombat, SpellCombat, Ranges, Navigation, Buffing, Crafting, Looting). This worktree has no refs/vtank/ checkout, so VTank's own name/enum string literal for each bit isn't directly readable. Derived the mapping instead from real data already in the embedded .usd: for each category name, docs/research/vtank-kb/01-settings-and-profiles.md §2 was searched for a setting whose Category column names EXACTLY that one category (no `|` combination) — e.g. row 45 RandomHelperBuffs is pure "Misc", row 18 AttackDistance is pure "Ranges" — then that setting's own recorded bitmask was read back from VtankDefaultSettingsDatabase.SettingCategoryBitmasks (never a typed-in hex literal), so the mapping tracks the shipped database instead of silently drifting from it. All 9 of VtankOptionCatalog.CategoryBits resolve to a real name this way (VtankOptionCatalog. CategoryNamesByBit), in VTank's own ascending-bit order. Mutation shown to fail: reverting AdvancedOptionCategoryNames to the old `$"0x{bit:X}"` projection failed the new AdvancedOptionCategoryNamesShowRealNamesNotHexBitmasks test with ["0x1", "0x2", ...] instead of ["Misc", "Recharge", ...]; restored and confirmed green. Verified: dotnet build AcDream.slnx -c Release green; MossTank suite 679/679 (678 -> 679); App markup/plugin filter 242/242 (unchanged). Co-Authored-By: Claude Fable 5.1 --- src/AcDream.Plugins.MossTank/MossTankPanel.cs | 21 ++++++--- .../VtankOptionCatalog.cs | 46 +++++++++++++++++++ .../MossTankPanelTests.cs | 24 ++++++++++ 3 files changed, 84 insertions(+), 7 deletions(-) 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() {