fix(vtank): slice 7 round D item 2 — VTank's Advanced Options click model
Owner live report 2026-09-07, screenshots 2/3 (relayed VTank click
model — this worktree has no refs/vtank/ checkout to read
db.cs:118-165 directly): clicking a VALUE cell used to always just
select the row, no matter the setting's type. VTank's own model
dispatches by declared type: a tBool value flips in place, a tEnum
value cycles to the next label, and only int/double/single/string
select the row and load the current value into the edit field below
for typing. Apply/Back buttons are gone entirely (retail's own
AdvancedOptionsView has neither; Enter on the field already applies
via its existing onsubmit) — the popup closes from its own title bar
or the Options tab's toggle, both already independent of this content.
Changes:
- VtankDefaultSettingsDatabase.SettingEnumValues parses VTank's own
SettingsEnumInfo table (3 columns, 33 rows: Setting/Value/EnumValue)
from the embedded .usd — the real per-setting enum code->label table
(UseArcs: 1=No, 2=At Range, 3=Yes), not a hand-typed guess.
- MossTankPanel.DisplayAdvancedOptionValue shows the enum LABEL for
Enum-typed settings in the value column instead of the raw stored
integer.
- MossTankPanel.ClickAdvancedOptionValue dispatches to
FlipAdvancedOptionBool / CycleAdvancedOptionEnum (wraps past the
last entry) / SelectAdvancedOption by VtankOptionCatalog.DeclaredType.
- mosstank-advanced.xml: the value column's onclick is now
{ClickAdvancedOptionValue}; Apply/Back buttons removed; the notice
label and the whole "MossTank Extras" section below it moved up 26px
to reclaim the space; panel height 476->450.
- Removed the now-unreferenced ApplyAdvancedOption wrapper property
(ApplyAdvancedOptionCore is still used by the field's own onsubmit).
Mutations shown to fail: (1) reducing ClickAdvancedOptionValue to a
bare SelectAdvancedOption(index) call failed both the bool-flip test
(expected False, got True — the click never flipped it) and the
enum-cycle test (expected "At Range", got "No" — the click never
advanced); the numeric-select test correctly stayed green since
select-only is still its own expected behavior. Restored and
confirmed green.
Verified: dotnet build AcDream.slnx -c Release green; MossTank suite
682/682 (679 -> 682, three new interaction tests); App markup/plugin
filter 242/242 (unchanged, mosstank-advanced.xml's new footprint
392x450 updated in ExpectedPopupBounds).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
ebe670adf5
commit
435ced86f7
5 changed files with 288 additions and 41 deletions
|
|
@ -578,7 +578,7 @@ internal sealed partial class MossTankPanel
|
|||
: _advancedOptionNames[
|
||||
Math.Clamp(_selectedAdvancedOption, 0, _advancedOptionNames.Count - 1)];
|
||||
_advancedOptionValueColumn = _advancedOptionNames
|
||||
.Select(name => GetMetaOption(name).ToDisplayString())
|
||||
.Select(DisplayAdvancedOptionValue)
|
||||
.ToArray();
|
||||
_advancedOptionSelectedDescription =
|
||||
VtankDefaultSettingsDatabase.SettingDescriptions.TryGetValue(
|
||||
|
|
@ -587,6 +587,29 @@ internal sealed partial class MossTankPanel
|
|||
: _advancedOptionSelectedName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round D item 2 (owner's VTank interaction model, screenshots 2/3):
|
||||
/// an enum-typed setting's value column shows the LABEL
|
||||
/// (VtankDefaultSettingsDatabase.SettingEnumValues, VTank's own
|
||||
/// SettingsEnumInfo table — e.g. "No"/"At Range"/"Yes" for UseArcs),
|
||||
/// never the raw stored integer. Every other type keeps its plain
|
||||
/// ToDisplayString() text.
|
||||
/// </summary>
|
||||
private string DisplayAdvancedOptionValue(string name)
|
||||
{
|
||||
ExpressionValue value = GetMetaOption(name);
|
||||
if (VtankOptionCatalog.DeclaredType(name) == VtankSettingValueType.Enum
|
||||
&& VtankDefaultSettingsDatabase.SettingEnumValues.TryGetValue(
|
||||
name, out IReadOnlyList<VtankEnumValue>? entries))
|
||||
{
|
||||
int current = value.AsInt32();
|
||||
foreach (VtankEnumValue entry in entries)
|
||||
if (entry.Value == current)
|
||||
return entry.Label;
|
||||
}
|
||||
return value.ToDisplayString();
|
||||
}
|
||||
|
||||
public Action ShowAdvancedOptions => () =>
|
||||
{
|
||||
_advancedOptionsVisible = true;
|
||||
|
|
@ -613,6 +636,36 @@ internal sealed partial class MossTankPanel
|
|||
RefreshAdvancedOptions();
|
||||
LoadAdvancedOptionDraft();
|
||||
};
|
||||
/// <summary>
|
||||
/// Round D item 2: VTank's own Advanced Options click model
|
||||
/// (owner-relayed <c>db.cs:118-165</c> behavior, screenshots 2/3 — this
|
||||
/// worktree has no refs/vtank/ checkout to read the decompile directly).
|
||||
/// Clicking the VALUE cell dispatches by the setting's declared type:
|
||||
/// a <c>tBool</c> flips in place, a <c>tEnum</c> cycles to the next
|
||||
/// label, and everything else (int/double/single/string/the one
|
||||
/// Custom row) just selects the row and loads its current value into
|
||||
/// the edit field at the bottom — the same behavior every click used
|
||||
/// to have. Only numbers (and strings) are actually typed/submitted
|
||||
/// through that field; booleans and enums never need it.
|
||||
/// </summary>
|
||||
public Action<int> ClickAdvancedOptionValue => index =>
|
||||
{
|
||||
if ((uint)index >= (uint)_advancedOptionNames.Count)
|
||||
return;
|
||||
string name = _advancedOptionNames[index];
|
||||
switch (VtankOptionCatalog.DeclaredType(name))
|
||||
{
|
||||
case VtankSettingValueType.Bool:
|
||||
FlipAdvancedOptionBool(index, name);
|
||||
break;
|
||||
case VtankSettingValueType.Enum:
|
||||
CycleAdvancedOptionEnum(index, name);
|
||||
break;
|
||||
default:
|
||||
SelectAdvancedOption(index);
|
||||
break;
|
||||
}
|
||||
};
|
||||
public Action<string> SetAdvancedOptionValueDraft => value =>
|
||||
_advancedOptionValueDraft = value;
|
||||
public Action<string> SubmitAdvancedOption => value =>
|
||||
|
|
@ -620,7 +673,6 @@ internal sealed partial class MossTankPanel
|
|||
_advancedOptionValueDraft = value;
|
||||
ApplyAdvancedOptionCore();
|
||||
};
|
||||
public Action ApplyAdvancedOption => ApplyAdvancedOptionCore;
|
||||
|
||||
/// <summary>The button is Force Buff; while a pass runs it cancels.</summary>
|
||||
public string BuffButtonText => _running ? "Stop buffing" : "Force buff";
|
||||
|
|
@ -2622,6 +2674,57 @@ internal sealed partial class MossTankPanel
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round D item 2: clicking a <c>tBool</c> row's VALUE cell flips it in
|
||||
/// place — no edit field, no Apply click, matching the owner's relayed
|
||||
/// VTank click model.
|
||||
/// </summary>
|
||||
private void FlipAdvancedOptionBool(int index, string name)
|
||||
{
|
||||
_selectedAdvancedOption = index;
|
||||
ExpressionValue next = ExpressionValue.Boolean(!GetMetaOption(name).IsTruthy);
|
||||
_advancedOptionNotice = SetMetaOption(name, next)
|
||||
? $"Set {name} = {next.ToDisplayString()}."
|
||||
: $"{name} is unavailable.";
|
||||
RefreshAdvancedOptions();
|
||||
LoadAdvancedOptionDraft();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Round D item 2: clicking a <c>tEnum</c> row's VALUE cell advances to
|
||||
/// the NEXT row of VTank's own <c>SettingsEnumInfo</c> table for that
|
||||
/// setting (wrapping past the last entry back to the first). A current
|
||||
/// value with no matching entry (should not happen against the real
|
||||
/// data) starts the cycle at the first entry rather than throwing.
|
||||
/// </summary>
|
||||
private void CycleAdvancedOptionEnum(int index, string name)
|
||||
{
|
||||
_selectedAdvancedOption = index;
|
||||
if (!VtankDefaultSettingsDatabase.SettingEnumValues.TryGetValue(
|
||||
name, out IReadOnlyList<VtankEnumValue>? entries)
|
||||
|| entries.Count == 0)
|
||||
{
|
||||
SelectAdvancedOption(index);
|
||||
return;
|
||||
}
|
||||
int current = GetMetaOption(name).AsInt32();
|
||||
int currentPosition = -1;
|
||||
for (int i = 0; i < entries.Count; i++)
|
||||
{
|
||||
if (entries[i].Value == current)
|
||||
{
|
||||
currentPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
VtankEnumValue nextEntry = entries[(currentPosition + 1) % entries.Count];
|
||||
_advancedOptionNotice = SetMetaOption(name, ExpressionValue.Number(nextEntry.Value))
|
||||
? $"Set {name} = {nextEntry.Label}."
|
||||
: $"{name} is unavailable.";
|
||||
RefreshAdvancedOptions();
|
||||
LoadAdvancedOptionDraft();
|
||||
}
|
||||
|
||||
/// <summary>VTank: row 0 is always DEFAULT and can never be empty.</summary>
|
||||
private void EnsureDefaultMonsterRule()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ internal static class VtankDefaultSettingsDatabase
|
|||
new(LoadCategoryBitmasks);
|
||||
private static readonly Lazy<IReadOnlyDictionary<string, string>> DescriptionsByName =
|
||||
new(LoadDescriptions);
|
||||
private static readonly Lazy<IReadOnlyDictionary<string, IReadOnlyList<VtankEnumValue>>>
|
||||
EnumValuesByName = new(LoadEnumValues);
|
||||
|
||||
/// <summary>A fresh, independently mutable parse of the embedded document — every call gets its own object graph.</summary>
|
||||
public static VtankDatabase Parse() => VtankDatabase.Parse(RawText.Value);
|
||||
|
|
@ -54,6 +56,54 @@ internal static class VtankDefaultSettingsDatabase
|
|||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, string> SettingDescriptions => DescriptionsByName.Value;
|
||||
|
||||
/// <summary>
|
||||
/// VTank's own <c>SettingsEnumInfo</c> table (3 columns, 33 rows:
|
||||
/// <c>Setting</c> name, integer <c>Value</c>, <c>EnumValue</c> label —
|
||||
/// e.g. <c>UseArcs</c> = 1 "No", 2 "At Range", 3 "Yes"). Owner-driven
|
||||
/// Advanced Options interaction model (2026-09-07, VTank's own
|
||||
/// <c>db.cs:118-165</c> click behavior): clicking a <c>tEnum</c> row's
|
||||
/// VALUE cell cycles to the NEXT row here (wrapping), and the value
|
||||
/// column shows the label, not the raw stored integer. Rows are
|
||||
/// returned in the file's own order, which is already ascending by
|
||||
/// <c>Value</c> within each setting's group.
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, IReadOnlyList<VtankEnumValue>> SettingEnumValues =>
|
||||
EnumValuesByName.Value;
|
||||
|
||||
private static IReadOnlyDictionary<string, IReadOnlyList<VtankEnumValue>> LoadEnumValues()
|
||||
{
|
||||
VtankDatabase database = VtankDatabase.Parse(RawText.Value);
|
||||
VtankTable? enumInfo = database.Find("SettingsEnumInfo");
|
||||
var map = new Dictionary<string, List<VtankEnumValue>>(StringComparer.OrdinalIgnoreCase);
|
||||
if (enumInfo is null)
|
||||
return map.ToDictionary(
|
||||
static pair => pair.Key,
|
||||
static pair => (IReadOnlyList<VtankEnumValue>)pair.Value,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
int nameColumn = enumInfo.ColumnIndex("Setting");
|
||||
int valueColumn = enumInfo.ColumnIndex("Value");
|
||||
int labelColumn = enumInfo.ColumnIndex("EnumValue");
|
||||
if (nameColumn < 0 || valueColumn < 0 || labelColumn < 0)
|
||||
return map.ToDictionary(
|
||||
static pair => pair.Key,
|
||||
static pair => (IReadOnlyList<VtankEnumValue>)pair.Value,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
foreach (VtankRow row in enumInfo.Rows)
|
||||
{
|
||||
string name = row.Cells[nameColumn].AsString();
|
||||
var entry = new VtankEnumValue(
|
||||
row.Cells[valueColumn].AsInt(),
|
||||
row.Cells[labelColumn].AsString());
|
||||
if (!map.TryGetValue(name, out List<VtankEnumValue>? list))
|
||||
map[name] = list = [];
|
||||
list.Add(entry);
|
||||
}
|
||||
return map.ToDictionary(
|
||||
static pair => pair.Key,
|
||||
static pair => (IReadOnlyList<VtankEnumValue>)pair.Value,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, string> LoadDescriptions()
|
||||
{
|
||||
VtankDatabase database = VtankDatabase.Parse(RawText.Value);
|
||||
|
|
@ -126,3 +176,6 @@ internal static class VtankDefaultSettingsDatabase
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>One row of VTank's <c>SettingsEnumInfo</c> table: an enum-typed setting's integer code and its display label.</summary>
|
||||
internal readonly record struct VtankEnumValue(int Value, string Label);
|
||||
|
|
|
|||
|
|
@ -55,31 +55,56 @@
|
|||
beside lFilterList (268,4,120,180), a real category checklist
|
||||
(check + text columns) that filters lOptionList by VTank's own
|
||||
SettingsCategories bitmask (VtankDefaultSettingsDatabase — 136 rows
|
||||
straight from the embedded .usd, 9 distinct bits). This worktree has NO
|
||||
refs/vtank/ checkout, so VTank's own category NAME strings for those 9
|
||||
bits aren't available anywhere in this repo; the checklist labels each
|
||||
by its raw bit value ("0x04" etc.) rather than a guessed name — a
|
||||
documented, honest gap, not silently dropped. The description readout
|
||||
(AdvancedOptionDescription) fills VTank's exact 384x80 txtInfo and now
|
||||
surfaces VTank's own real per-setting help text (Settings.Description,
|
||||
93 of 137 rows non-empty, e.g. DoHelp's own retail wording) prefixed
|
||||
with the option's name — replacing the old bare name-only label, which
|
||||
is dropped as redundant. Apply/Back move beside each other under the
|
||||
two lists (VTank's own popup has neither; acdream keeps them for the
|
||||
draft-then-submit editing flow the plain key-value grammar needs).
|
||||
Panel height grows 392->476 for the taller two-list row plus the
|
||||
description block; the "MossTank Extras" section (item 8) shifts down
|
||||
to keep sitting below everything the retail portion now needs. Bold
|
||||
text isn't representable in the plain retail UI font (0x40000000 has no
|
||||
bold face) — the section header uses the same bright caption color
|
||||
other headers use instead. -->
|
||||
<panel x="253" y="405" w="392" h="476" title="MossTank Advanced Options"
|
||||
straight from the embedded .usd, 9 distinct bits). Round D item 1
|
||||
replaced lFilterList's raw bit-value labels ("0x04" etc.) with VTank's
|
||||
own category names (Misc, Recharge, MeleeCombat, SpellCombat, Ranges,
|
||||
Navigation, Buffing, Crafting, Looting — VtankOptionCatalog.
|
||||
CategoryNamesByBit). The description readout (AdvancedOptionDescription)
|
||||
fills VTank's exact 384x80 txtInfo and surfaces VTank's own real
|
||||
per-setting help text (Settings.Description, 93 of 137 rows non-empty,
|
||||
e.g. DoHelp's own retail wording) prefixed with the option's name.
|
||||
|
||||
Round D item 2 (owner live report 2026-09-07, screenshots 2/3 —
|
||||
relayed VTank click model, this worktree has no refs/vtank/ checkout to
|
||||
read db.cs:118-165 directly): clicking a VALUE cell now dispatches by
|
||||
the setting's declared type instead of always just selecting the row.
|
||||
A tBool value flips in place; a tEnum value cycles to the next label
|
||||
(VtankDefaultSettingsDatabase.SettingEnumValues, VTank's own
|
||||
SettingsEnumInfo table); everything else (int/double/single/string)
|
||||
selects the row and loads its current value into the field below for
|
||||
typing — the SAME behavior every click used to have, now narrowed to
|
||||
only the types that actually need an edit box. Apply/Back are REMOVED
|
||||
entirely: retail's own AdvancedOptionsView has neither (VTank edits are
|
||||
destructive-live), and acdream's own draft-then-submit flow no longer
|
||||
needs a button once Enter (the field's own onsubmit) is the only path
|
||||
a number/string value ever takes — the popup closes from its own
|
||||
title bar or the Options tab's "Advanced Options" toggle, both already
|
||||
wired independently of this content (see the top-of-file note on
|
||||
AdvancedOptionsVisible/PluginWindowVisibilityController). Removing the
|
||||
23px button row plus its 3px gap to the notice label reclaims 26px:
|
||||
the notice label and every "MossTank Extras" control below it moved up
|
||||
by that amount, and the panel's own height shrinks 476->450 to match
|
||||
(438px of real content + 12px bottom padding, the same margin the
|
||||
file used before this round).
|
||||
|
||||
Fix round B item 8's own "MossTank Extras" section (controls with no
|
||||
VTank-tab counterpart, moved here per the owner's silhouette rule)
|
||||
is otherwise unchanged: Checkpoint/Jump (AddRouteCheckpoint/
|
||||
AddRouteJump), Remove (RemoveRouteWaypoint), Set Follow Target + its
|
||||
status label, Follow Corners/Open Doors (navigation toggles with no
|
||||
Route-tab VTank equivalent), Nav Priority (a real second copy of
|
||||
Options' own "Boost Nav. Priority"), and the Follow/Nav Min Distance
|
||||
+/- stepper (a real duplicate of Options' own editable
|
||||
FollowNavMinimumValueText field). Bold text isn't representable in the
|
||||
plain retail UI font (0x40000000 has no bold face) — the section
|
||||
header uses the same bright caption color other headers use instead. -->
|
||||
<panel x="253" y="405" w="392" h="450" title="MossTank Advanced Options"
|
||||
visible="{AdvancedOptionsVisible}" resize="none">
|
||||
<list x="4" y="4" w="256" h="160" rowheight="17"
|
||||
selected="{SelectedAdvancedOptionIndex}" onchange="{SelectAdvancedOption}"
|
||||
tooltip="Select one of Virindi Tank's 137 advanced options.">
|
||||
<column type="text" width="187" items="{AdvancedOptionNames}" onclick="{SelectAdvancedOption}" />
|
||||
<column type="text" width="69" items="{AdvancedOptionValueColumn}" onclick="{SelectAdvancedOption}" />
|
||||
<column type="text" width="69" items="{AdvancedOptionValueColumn}" onclick="{ClickAdvancedOptionValue}" />
|
||||
</list>
|
||||
<!-- Fix round B item 15's own build-over-real-files test caught a real
|
||||
bug here: <list> markup requires a "selected" int binding even for a
|
||||
|
|
@ -94,29 +119,25 @@
|
|||
<field x="4" y="168" w="260" h="16" text="{AdvancedOptionValueDraft}"
|
||||
onchange="{SetAdvancedOptionValueDraft}" onsubmit="{SubmitAdvancedOption}"
|
||||
maxlength="160" clearonsubmit="false" background="#E6000000"
|
||||
tooltip="Edit the selected advanced-option value and press Enter or Apply." />
|
||||
tooltip="Edit the selected advanced-option value and press Enter to apply." />
|
||||
<label x="4" y="188" w="384" h="80" text="{AdvancedOptionDescription}" color="#FFE8DEC3" />
|
||||
<button x="4" y="272" w="116" h="23" text="Apply"
|
||||
onclick="{ApplyAdvancedOption}" />
|
||||
<button x="128" y="272" w="116" h="23" text="Back"
|
||||
onclick="{HideAdvancedOptions}" />
|
||||
<label x="4" y="298" w="384" h="16" text="{AdvancedOptionNotice}" color="#FFC7B98F" />
|
||||
<label x="4" y="272" w="384" h="16" text="{AdvancedOptionNotice}" color="#FFC7B98F" />
|
||||
|
||||
<label x="4" y="320" w="200" h="16" text="MossTank Extras" color="#FFE8DEC3" />
|
||||
<button x="4" y="338" w="90" h="18" text="Checkpoint" onclick="{AddRouteCheckpoint}" />
|
||||
<button x="100" y="338" w="90" h="18" text="Jump" onclick="{AddRouteJump}" />
|
||||
<button x="4" y="360" w="90" h="18" text="Remove" onclick="{RemoveRouteWaypoint}" />
|
||||
<button x="100" y="360" w="140" h="18" text="Set Follow Target" onclick="{SetFollowTarget}" />
|
||||
<label x="4" y="382" w="376" h="16" text="{RouteFollowTargetText}" color="#FFC7B98F" />
|
||||
<toggle x="4" y="402" w="140" h="18" text="Follow Corners"
|
||||
<label x="4" y="294" w="200" h="16" text="MossTank Extras" color="#FFE8DEC3" />
|
||||
<button x="4" y="312" w="90" h="18" text="Checkpoint" onclick="{AddRouteCheckpoint}" />
|
||||
<button x="100" y="312" w="90" h="18" text="Jump" onclick="{AddRouteJump}" />
|
||||
<button x="4" y="334" w="90" h="18" text="Remove" onclick="{RemoveRouteWaypoint}" />
|
||||
<button x="100" y="334" w="140" h="18" text="Set Follow Target" onclick="{SetFollowTarget}" />
|
||||
<label x="4" y="356" w="376" h="16" text="{RouteFollowTargetText}" color="#FFC7B98F" />
|
||||
<toggle x="4" y="376" w="140" h="18" text="Follow Corners"
|
||||
checked="{FollowAroundCornersEnabled}" onclick="{ToggleFollowAroundCorners}" />
|
||||
<toggle x="200" y="402" w="140" h="18" text="Open Doors"
|
||||
<toggle x="200" y="376" w="140" h="18" text="Open Doors"
|
||||
checked="{OpenDoorsEnabled}" onclick="{ToggleOpenDoors}" />
|
||||
<toggle x="4" y="424" w="140" h="18" text="Nav Priority"
|
||||
<toggle x="4" y="398" w="140" h="18" text="Nav Priority"
|
||||
checked="{NavigationPriorityEnabled}" onclick="{ToggleNavigationPriority}" />
|
||||
<label x="4" y="446" w="180" h="16" text="{RouteMinimumDistanceText}" color="#FFC7B98F" />
|
||||
<button x="190" y="446" w="24" h="18" text="-"
|
||||
<label x="4" y="420" w="180" h="16" text="{RouteMinimumDistanceText}" color="#FFC7B98F" />
|
||||
<button x="190" y="420" w="24" h="18" text="-"
|
||||
onclick="{RouteMinimumDistanceDown}" tooltip="Decrease the route arrival distance." />
|
||||
<button x="218" y="446" w="24" h="18" text="+"
|
||||
<button x="218" y="420" w="24" h="18" text="+"
|
||||
onclick="{RouteMinimumDistanceUp}" tooltip="Increase the route arrival distance." />
|
||||
</panel>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue