diff --git a/docs/research/vtank-kb/08-ui-views.md b/docs/research/vtank-kb/08-ui-views.md new file mode 100644 index 00000000..41962188 --- /dev/null +++ b/docs/research/vtank-kb/08-ui-views.md @@ -0,0 +1,494 @@ +# Campaign VT knowledge base — 08: UI views + +Research-only. No code changes. Sources cited by path; `file:line` given for +every code claim. Decompiled VTank/VVS code is **never quoted verbatim** +below — every fact is paraphrased or given as a name/number/attribute list. + +Primary sources: + +- `refs/vtank/uTank2.ViewXML.mainView.xml` — the main window: 856×210, + 9 `` tabs, **156 controls, 38 list columns** (counted directly from + the file; matches the brief's "156 controls"). +- `refs/vtank/uTank2.ViewXML.AdvancedOptionsView.xml`, + `SelfBuffChoiceView.xml`, `FriendlyLootEditorView.xml` — secondary popup + views. +- `refs/vtank/uTank2.Resources.defaultsettings.usd` — VTank's serialized + default-settings catalog (setting name / type / default / description / + category-bitmask). This is the authoritative cross-reference for what + each Options/Vitals control actually persists. +- `refs/vvs/decompiled/VirindiViewService.XMLParsers/Decal3XMLParser.cs` — + the XML→control mapping table (progid string → concrete `Hud*` class + + constructor arguments). This is the ground truth for "what does this XML + attribute actually do at parse time." +- `refs/vvs/decompiled/VirindiViewService.Controls/*.cs` — control behavior + (`HudList`, `HudCombo`, `HudTabView`, `HudHSlider`/`LinearPositionControl`, + `HudTextBox`, `HudButton`, `HudCheckBox`, `HudPictureBox`, `HudControl`). +- `refs/vvs/decompiled/VirindiViewService/HudView.cs`, + `ViewProperties.cs`, `ACImage.cs` — window chrome, icon-id resolution, + `StoredViewInfo` persistence. +- `refs/vvs/decompiled/VirindiViewService.Themes/Float_Theme.cs` — default + theme pixel constants (`TabHeight`, `SliderNubWidth`, etc.). +- `refs/vtank/decompiled/uTank2/PluginCore.cs` — the class that loads + `mainView.xml`, casts every named control to a typed wrapper interface, + and wires `Click`/`Change`/`Change_Old` handlers. **Heavily obfuscated** + (single/double-letter field and method names) — see §6. +- `refs/vtank/decompiled/MetaViewWrappers.VirindiViewServiceHudControls/List.cs` + — the `IList` wrapper VTank actually programs against (row/cell + accessors, `Click(row,col)`). +- Our side: `src/AcDream.App/UI/MarkupDocument.cs`, `docs/plugin-ui-markup.md`, + `src/AcDream.Plugins.MossTank/mosstank.xml`. + +--- + +## 0. Window-level facts (item 4) + +| Fact | Value | Source | +|---|---|---| +| Declared size | `width="856" height="210"` | `mainView.xml:2` | +| `properties.Width`/`Height` | XML value **minus** `HudViewDrawStyle.Theme_OldDecal`'s border `Size` (border chrome is subtracted from the declared size before it becomes the client area) | `Decal3XMLParser.cs:33-37` | +| Icon attribute | `icon="10667"` | `mainView.xml:2` | +| Icon resolution | `ViewProperties.Icon` is typed `ACImage`; the parser assigns it the **int** returned by `a(XmlAttribute)` (the `icon` attribute parsed as a plain integer), which converts via `ACImage`'s `implicit operator ACImage(int p)`. Every `ACImage` int-constructor path ORs the input with `0x6000000` before resolving it as a portal.dat id (`portalfile \| 0x6000000`) | `Decal3XMLParser.cs:32`, `ACImage.cs:618` (`op_Implicit`), `ACImage.cs:282-410` (the `\| 0x6000000` pattern repeated on every constructor) | +| → confirmed formula | icon **10667** (`0x29AB`) resolves to portal.dat id **`0x0600` `29AB`** = `0x060029AB` — i.e. exactly the task's guessed `0x06000000 + 10667` formula, confirmed structurally (VTank's own icon id space is bare portal.dat indices, same convention our `PluginIcons.Normalize` documents for `did` bindings) | `ACImage.cs:618`, cross-ref `docs/plugin-ui-markup.md`'s icon-id grammar section | +| Title | Set at runtime, not in XML: `"Virindi Tank v." + ` (` (SIMULATION MODE)` appended when simulating) | `refs/vtank/decompiled/uTank2/PluginCore.cs:1608-1613` | +| Window icon (taskbar/system) | Overridden separately after load via `this.k.SetIcon(101, GetModuleHandle("utank2-i"))` — a **Win32 resource icon** from the plugin DLL itself, unrelated to the `icon="10667"` DAT-portal icon (that one is VVS's own window-manager/hud-bar icon) | `PluginCore.cs:1614` | +| Resizability | `UserResizeable = true`; `MinimumClientArea = new Size(792, )`; `MaximumClientArea = new Size(, 9999)` — width is effectively pinned near its authored value (a little horizontal shrink to 792 allowed, no growth past the authored width), height can grow unbounded (to 9999) but not shrink below the authored height. This matches a list-heavy layout where the user wants to see more rows, not more columns | `PluginCore.cs:1859-1862` | +| `HudView.UserResizeable` effect on border | Resizeable views use a **different, thicker** themed border (`ViewBorder*_Size_Resizeable` vs `ViewBorder*_Size`) — border thickness is data-driven per resizability state, not a fixed constant | `HudView.cs:561-607` | +| Persistence store | SQLite file at the registry path `HKLM\SOFTWARE\Decal\Services\{DBAC9286-...}\StoredInfoFile`, table **`StoredViewInfo`**: `ViewKey TEXT UNIQUE PK, LocX INT, LocY INT, LocSticky INT, UserW INT, UserH INT, Ghost INT, Theme TEXT, Enabled INT, ClickThrough INT, IsCustomTheme INT, ThemeID INT, ThemeID2 TEXT` | `HudView.cs:1868-1883` (`CREATE TABLE`), `:1916-2018` (`LoadUserSettings`/`ad()` — the `select` matching this schema), `:2020-2072` (`ac()`/the `insert or replace` on save) | +| `ViewKey` (`a9` field) | `":"` — VTank passes no explicit window key to its main `HudView`, so persistence keys off the assembly name + an empty/default key unless a `pWindowKey` constructor overload is used | `HudView.cs:1711-1726` (`a(...)` ctor helper, `ba`/`a9` fields) | +| What's persisted | Location (X/Y), a 4-bit "sticky edge" mask (`LocSticky`, ghost-docking to a screen edge), user-resized W/H, `Ghosted` (hudified/pinned mode), visibility, click-through, and the active theme (by name or numeric index, plus a custom-theme flag) | same range as above | +| Save trigger | Debounced onto the thread pool (`ThreadPool.QueueUserWorkItem`) any time position/size/ghost/click-through/theme changes, not on a timer | `HudView.cs:2020-2040` | +| Right-click / context menu | Exists **only at the window-chrome level** (`HudView.l` = `ContextMenu`), with fixed entries `close`/`alphaup`/`alphadown`/`ghost`/`clickthrough`/`nexttheme`/`resettheme` plus one per user-added titlebar button (`"user~N"`) and one per theme (`"themeset"`). There is **no** per-control or per-list-row right-click anywhere in the base control classes (`HudControl`/`HudList` only ever wire left-button `MouseDown`/`MouseUp`/`MouseMove`/`MouseWheel`) | `HudView.cs:1583-1643` (menu dispatch), confirmed absent by a repo-wide grep for right/second-button handling in `refs/vvs/decompiled` (`RightMouseDown`/`MouseButton.Right`/etc. — zero matches) | +| Double-click | Not implemented anywhere in the base control classes. `HudControl.Hit`/`MouseUp` fires once per press-release pair inside the same control; there is no timestamp-based double-click detector in `HudControl`, `HudList`, or `HudCombo` | same grep + direct read of `HudControl.cs:429-474` | + +--- + +## 1. Per-tab control tables + +All geometry, names, and text below is transcribed directly from +`mainView.xml` (100% exact — every `left`/`top`/`width`/`height`/`text` is a +literal attribute value). "Setting/action" columns marked **[confirmed]** +are cross-referenced against `defaultsettings.usd` and/or a direct +`PluginCore.cs` line; those without a bracket are a naming-convention +inference (VTank's own control names are self-describing in most cases) and +are called out as such. + +### Tab: Options (37 controls: 36 leaf + 1 `FixedLayout` container, 0 list columns) + +| Control | Type | L,T,W,H | Text | Setting / action | Notes | +|---|---|---|---|---|---| +| Label12 | StaticText | 8,8,80,16 | "Monster Range:" | — | label for txtRange | +| txtRange | Edit | 8,24,40,16 | "" | `AttackDistance` **[confirmed]** — `defaultsettings.usd:283-289` (a fractional "distance" unit, default `0.0208333333333333`) | `imageportalsrc="4726"` — **dead attribute**, see §2 | +| Label22 | StaticText | 8,40,80,16 | "Ring Range:" | — | | +| txtRingRange | Edit | 8,56,40,16 | "" | `RingDistance` **[confirmed]** — `defaultsettings.usd:307-313` | | +| Label23 | StaticText | 8,72,80,16 | "Approach Range:" | — | | +| txtApproachRange | Edit | 8,88,40,16 | "" | `ApproachDistance` **[confirmed]** — `defaultsettings.usd:299-305` | | +| Label24 | StaticText | 8,104,80,16 | "Follow/Nav Min Distance:" | — | | +| txtWPRange | Edit | 8,120,40,16 | "" | `NavCloseStopRange` (inferred by name/purpose match; not directly grepped to this field) — `defaultsettings.usd:331-337` | | +| bForceBuff | PushButton | 90,8,112,25 | "Force Buff" | action (no persisted setting) | wired `IButton.Click` in `PluginCore.cs:1629,1752` (`this.z`) | +| bCancelForceBuff | PushButton | 90,38,112,25 | "Cancel Force Buff" | action | `PluginCore.cs:1630,1772` (`this.aa`) | +| cEnableBuffing | Checkbox | 90,68,120,20 | "Enable Buffing" | `EnableBuffing` **[confirmed]** — `defaultsettings.usd:163-169` | | +| cEnableCombat | Checkbox | 90,88,120,20 | "Enable Combat" | `EnableCombat` **[confirmed]** — `defaultsettings.usd:171-177` | | +| c1EnableNav | Checkbox | 90,108,120,20 | "Enable Navigation" | `EnableNav` **[confirmed]** — `defaultsettings.usd:155-161` | | +| c1EnableLooting | Checkbox | 90,128,120,20 | "Enable Looting" | `EnableLooting` **[confirmed]** — `defaultsettings.usd:147-153` | | +| c1EnableMeta | Checkbox | 90,148,120,20 | "Enable Meta" | `EnableMeta` **[confirmed]** — `defaultsettings.usd:1027-1033` | | +| cEnableAutoStack | Checkbox | 220,8,160,20 | "Enable Autostack" | `AutoStack` **[confirmed]** — `defaultsettings.usd:419-425` | | +| cEnableAutoCram | Checkbox | 220,28,160,20 | "Enable Autocram" | `AutoCram` **[confirmed]** — `defaultsettings.usd:411-417` | | +| cGems | Checkbox | 220,48,120,20 | "Use Dispel Items" | `UseDispelItems` **[confirmed]** — `defaultsettings.usd:403-409` | XML name ("Gems") doesn't match the text/setting — a VTank-internal inconsistency | +| cDispel | Checkbox | 220,68,120,20 | "Cast Dispel Self" | `CastDispelSelf` **[confirmed]** — `defaultsettings.usd:395-401` | | +| cAutoFellow | Checkbox | 220,128,120,20 | "Auto Fellow Mgmt." | `AutoFellowManagement` **[confirmed]** — `defaultsettings.usd:811-817` | | +| cMChargesWhenOff | Checkbox | 220,148,160,20 | "M. Charges When Off" | `ManaChargesWhenOff` **[confirmed]** — `defaultsettings.usd:803-809` | | +| cNavPriorityBoost | Checkbox | 386,8,160,20 | "Boost Nav. Priority" | `NavPriorityBoost` **[confirmed]** — `defaultsettings.usd:851-857` | | +| cLootPriorityBoost | Checkbox | 386,28,160,20 | "Boost Loot Priority" | `LootPriorityBoost` **[confirmed]** — `defaultsettings.usd:659-665` | | +| cLootOnlyRareCorpses | Checkbox | 386,48,160,20 | "Loot Only Rare Corpses" | `LootOnlyRareCorpses` **[confirmed]** — `defaultsettings.usd:907-913` | | +| cIdlePeace | Checkbox | 386,68,160,20 | "Peace Mode When Idle" | `IdlePeaceMode` **[confirmed]** — `defaultsettings.usd:515-521` | | +| cTopoffBuffs | Checkbox | 386,88,160,20 | "Rebuff When Idle" | `IdleBuffTopoff` **[confirmed]** — `defaultsettings.usd:571-577` | | +| cFastCast | Checkbox | 386,108,160,20 | "Fastcast Buffs" | `FastCastBuffs` **[confirmed]** — `defaultsettings.usd:1139-1145` | | +| cCollisionChecks | Checkbox | 386,128,160,20 | "Don't Shoot at Walls" | `UseProjectileAwareness` **[confirmed]** — `defaultsettings.usd:1155-1161` | inverse-named XML control name vs. the actual boolean sense (checked = awareness ON) | +| cDebuffFallback | Checkbox | 386,148,160,20 | "Fallback Debuffs if Blocked" | `AllowDebuffFallback` **[confirmed]** — `defaultsettings.usd:1227-1233` | | +| cSummonPets | Checkbox | 552,8,160,20 | "Summon Pets" | `SummonPets` **[confirmed]** — `defaultsettings.usd:1075-1081` | | +| cCustomPetRange | Checkbox | 552,28,120,20 | "Custom Pet Range:" | `PetRangeMode` (enum 0=`AttackDistance`,1=`PetCustomRange`, per `SettingsEnumInfo` table) **[confirmed]** — `defaultsettings.usd:1083-1089,2260-2271` | | +| txtCustomPetRange | Edit | 672,28,40,16 | "" | `PetCustomRange` **[confirmed]** — `defaultsettings.usd:1091-1097` | | +| Label1_PetDensity | StaticText | 552,48,125,16 | "Pet Min. Monsters:" | — | | +| txtPetDensity | Edit | 672,48,40,16 | "" | `PetMonsterDensity` **[confirmed]** — `defaultsettings.usd:1123-1129` | | +| cShowAdvanced | Checkbox | 611,128,233,20 | "Advanced Options" | UI-only (not in `defaultsettings.usd`) — opens/closes the `AdvancedOptionsView` popup (`refs/vtank/decompiled/db.cs:79`, icon 9660 "gear" per `AdvancedOptionsView.xml:2`) | not a persisted macro setting, a window-visibility toggle | +| cOn | Checkbox | 611,148,233,20 | "Run Macro" | runtime master enable, not in `defaultsettings.usd` | | + +### Tab: Profiles (18 controls, 0 list columns) + +Every row follows the same **enable-checkbox + profile-name combo + +CopyTo/New button (+ optional Reset/Show-Editor)** pattern, one per macro +sub-system: + +| Control | Type | L,T,W,H | Text | Binds to | +|---|---|---|---|---| +| Label123 | StaticText | 8,8,142,16 | "Macro Settings" | — | +| cmbSettingsSet | Choice | 140,8,140,20 | (populated at runtime) | named macro-settings profile list | +| bSettingsSaveAs | PushButton | 280,8,40,20 | "CopyTo" | copy current profile to a new name | +| cSettingsShowAll | Checkbox | 440,8,80,20 | "Mine only" | filter profile list to the current character | +| bSettingsReset | PushButton | 330,8,100,20 | "Clear profile!" | reset current profile to defaults | +| cNav | Checkbox | 8,32,142,20 | "Enable Auto-Navigation" | `EnableNav` **[confirmed]** (same setting the Options-tab `c1EnableNav` binds — two controls, one setting) | +| cmbNavSet | Choice | 140,32,140,20 | — | named nav-profile list | +| bNavSaveAs | PushButton | 280,32,40,20 | "CopyTo" | | +| bNavReset | PushButton | 330,32,100,20 | "Clear nav profile!" | | +| cLoot | Checkbox | 8,56,142,20 | "Enable Looting" | `EnableLooting` **[confirmed]** (same setting as `c1EnableLooting`) | +| cmbLootSet | Choice | 140,56,140,20 | — | named loot-profile list | +| bLootSaveAs | PushButton | 280,56,40,20 | "New" | (label differs from the Nav/Settings "CopyTo" — loot profiles are created fresh, not copied) | +| cShowLootEditor | Checkbox | 440,56,80,20 | "Show Editor" | opens/closes the `FriendlyLootEditorView` popup **[confirmed]** — `PluginCore.cs:1667,1764,6270-6289` (`bb.Change_Old` handler calls `dz.ah.f()`/`dz.ah.b()` to open/close) | +| cMeta | Checkbox | 8,80,142,20 | "Enable Meta Actions" | `EnableMeta` **[confirmed]** (same setting as `c1EnableMeta`) | +| cmbMetaSet | Choice | 140,80,140,20 | — | named meta-profile list | +| bMetaSaveAs | PushButton | 280,80,40,20 | "CopyTo" | | +| bMetaClearViews | PushButton | 330,80,100,20 | "Del. Meta Windows" | closes any floating meta-debug windows | + +### Tab: Vitals (28 controls, 0 list columns) + +Three groups of 3 sliders + 3 percent-readout labels, each slider bound to +one of the nine `Recharge--` settings — **fully confirmed** +by both the initializer and every change handler: + +| Control | Type | L,T,W,H | Label | Setting **[confirmed]** | Evidence | +|---|---|---|---|---|---| +| slMyHP | Slider | 96,0,144,16 | "Heal at:" | `Recharge-Norm-HitP` | `PluginCore.cs:4417-4427` (handler `aj`), `:4566` (init) | +| slMyStam | Slider | 96,16,144,16 | "Restam at:" | `Recharge-Norm-Stam` | `:4404-4415` (`ak`), `:4567` | +| slMyMana | Slider | 96,32,144,16 | "Get Mana at:" | `Recharge-Norm-Mana` | `:4391-4402` (`al`), `:4568` | +| slTopOffHP | Slider | 96,56,144,16 | "Top-off HP:" | `Recharge-NoTarg-HitP` | `:4339-4349` (`ap`), `:4569` | +| slTopOffStam | Slider | 96,72,144,16 | "Top-off Stam:" | `Recharge-NoTarg-Stam` | `:4570` (init; handler by symmetry with `ap`) | +| slTopOffMana | Slider | 96,88,144,16 | "Top-off Mana:" | `Recharge-NoTarg-Mana` | `:4571` | +| slOtherHP | Slider | 96,112,144,16 | "Heal others at:" | `Recharge-Helper-HitP` | `:4378-4389` (`am`), `:4572` | +| slOtherStam | Slider | 96,128,144,16 | "Restam others at:" | `Recharge-Helper-Stam` | `:4573` | +| slOtherMana | Slider | 96,144,144,16 | "Infuse others at:" | `Recharge-Helper-Mana` | `:4574` | + +Each slider has a matching `min="0" max="100"` (`mainView.xml:81-83` etc.) +percent readout label (`lblVitals_H_Self`, `_S_Self`, `_M_Self`, `_H_Idle`, +`_S_Idle`, `_M_Idle`, `_H_Help`, `_S_Help`, `_M_Help` — 9 `StaticText` +controls at 248,0 / 248,16 / … / 248,144, each `80×16`, default text `"0%"`) +updated from the slider's `.Position` on every `Change` (e.g. +`cx.Text = co.Position + "%";` — `PluginCore.cs:4560-4561`). Plus the 9 +"Heal at:"/"Restam at:"/… `Label1`/`Label2`/`Label3`/`Label11`/`Label21`/ +`Label31`/`Label111`/`Label211`/`Label311` captions (8×16 each). + +### Tab: Monsters (26 controls: 25 leaf + 1 container, **23 list columns**) + +The single `lstMonsters` list (0,16,856,116) has **23 declared columns**, +14 checkbox + 7 text + 2 icon — matching (with two anomalies) the +**21-field `MyMonsters` settings table** (`defaultsettings.usd:48-91`: +`MonsterName, AttackPriority, DamageType, WeaponToUse, Imperil, Vuln, +Yield, GravityW, Attack, Ring, Broadside, Fester, WeakeningCurse, +FesteringCurse, Corruption, DestructiveCurse, Corrosion, Streak, +SecondaryVuln, SecondaryEquip, PetDamageType`): + +| # | Column `name` | VVS type | fixedwidth | Data-field match | Header (separate StaticText above the list) | +|---|---|---|---|---|---| +| 1 | clFester | CheckColumn | 16 | `Fester` | "F" (`Label_Mon_F`) | +| 2 | clBroadside | CheckColumn | 16 | `Broadside` | "B" | +| 3 | clGravity | CheckColumn | 16 | `GravityW` | "G" | +| 4 | clImp | CheckColumn | 16 | `Imperil` | "I" | +| 5 | clYield | CheckColumn | 16 | `Yield` | "Y" | +| 6 | clVuln | CheckColumn | 16 | `Vuln` | "V" | +| 7 | clWar | CheckColumn | 16 | `Attack` | "A" | +| 8 | clRings | CheckColumn | 16 | `Ring` | "R" | +| 9 | clRings *(duplicate name — bug in VTank's own XML, see §6)* | CheckColumn | 16 | `Streak`? (unclear — see §6) | "S" | +| 10 | clWeakeningCurse | CheckColumn | 16 | `WeakeningCurse` | "WC" | +| 11 | clFesteringCurse | CheckColumn | 16 | `FesteringCurse` | "FC" | +| 12 | clCorruption | CheckColumn | 16 | `Corruption` | "Cp" | +| 13 | clDestructiveCurse | CheckColumn | 16 | `DestructiveCurse` | "DC" | +| 14 | clCorrosion | CheckColumn | 16 | `Corrosion` | "Cs" | +| 15 | clMonName | TextColumn | 120 | `MonsterName` | "Name" | +| 16 | clPriority | TextColumn | 20 | `AttackPriority` | "P" | +| 17 | clDamageType | TextColumn | 56 | `DamageType` | "Dmg type" | +| 18 | clExVulnType | TextColumn | 56 | `SecondaryVuln` (inferred) | "Ex. Vuln" | +| 19 | clWeaponToUse | TextColumn | 80 | `WeaponToUse` | "Weapon" | +| 20 | clOffHand | TextColumn | 80 | `SecondaryEquip` (inferred) | "Offhand" | +| 21 | clPetDamageType | TextColumn | 56 | `PetDamageType` | "PetDmg" | +| 22 | clMoveUp | IconColumn | 16 | (UI-only reorder arrow, no data field) | — | +| 23 | clMoveDown | IconColumn | 16 | (UI-only reorder arrow, no data field) | — | + +Below the list: `txtNewMonster` (Edit, 8,136,388,16), `cmdNewMonster` +(PushButton "Add", 404,136,50,16), `cmdNewMonsterFromCur` (PushButton +"Add Sel", 462,136,50,16). Nine tooltip strings are assigned to the header +labels at runtime (`bm.TooltipText = "Fester"` … `b0.TooltipText = +"Priority"`, `PluginCore.cs:1693-1707`) — the single-letter header glyphs +are deliberately terse and rely on a hover tooltip to spell out the full +curse/spell-family name. + +### Tab: Items (6 controls, 2 list columns) + +| Control | Type | L,T,W,H | Text | Columns | +|---|---|---|---|---| +| Label4a | StaticText | 0,0,320,16 | "Weapons / Wands / Shields / Pets" | — | +| Label4b | StaticText | 320,0,80,16 | "Hands" | — | +| lstBuffItems | List | 0,16,420,116 | — | `clWeaponName` (TextColumn, 320), `clHandedness` (TextColumn, 80) | +| cmdNewWeapon | PushButton | 10,136,140,16 | "Add" | — | +| cmdNewWeapon_NoBuffs | PushButton | 170,136,140,16 | "Add (no buffs)" | — | + +Underlying data table (inferred by column-name match): `BuffedItems` +(`Object`, `Spell` fields — `defaultsettings.usd:14-20`) for the weapon +list; handedness likely a derived/wrapper property, not itself a +`defaultsettings.usd` field. + +### Tab: Consumables (8 controls, 3 list columns) + +| Control | Type | L,T,W,H | Text | Columns | +|---|---|---|---|---| +| Label7a | StaticText | 0,0,400,16 | "Gems / Food / Kits / Potions / Charges / Grenades / Lockpicks" | — | +| lstExtraBuffItems | List | 0,16,344,116 | — | `clExtraBuffItemsC1` (TextColumn, 320) | +| cmdNewBuffItem | PushButton | 0,136,260,16 | "Add" | — | +| cmdAddAllPeas | PushButton | 264,136,80,16 | "Add All Peas" | — | +| Label7b | StaticText | 448,0,344,16 | "Excluded Scarab Types:" | — | +| lstExcludedComponents | List | 448,16,344,116 | — | `1` (IconColumn, 16 — unnamed/numeric column name, another minor authoring wart), `2` (TextColumn, no fixedwidth → auto-sized) | +| cmdNewExcludedComponent | PushButton | 448,136,344,16 | "Add Selected" | — | + +Underlying data (inferred): `GemFoodItems` (`Name`, `Spell` — +`defaultsettings.usd:26-31`) for the left list; `BlacklistedSpellComps` +(a bare string-list setting — `defaultsettings.usd:1035-1041`) for the +right list. + +### Tab: Buffs (7 controls, 2 list columns) + +| Control | Type | L,T,W,H | Text | Columns | +|---|---|---|---|---| +| LabelBPa | StaticText | 0,0,320,16 | "Extra Buff Spells (best similar will be used)" | — | +| lstBuffSpells | List | 0,16,320,116 | — | `clBuff` (TextColumn, 320) | +| cmdNewBuffSpell | PushButton | 10,136,140,16 | "Add..." | — | +| LabelBPb | StaticText | 472,0,320,16 | "Blacklisted Buff Families" | — | +| lstAntiBuffSpells | List | 472,16,320,116 | — | `clBuff` (TextColumn, 320 — **same column name as the left list**, harmless since each `HudList` instance owns its own column namespace) | +| cmdNewAntiBuffSpell | PushButton | 472,136,140,16 | "Add..." | — | + +Data tables (confirmed by name): `ExtraBuffSpells` and +`AntiExtraBuffSpells`, both single-field `ExemplarId` tables +(`defaultsettings.usd:1-6,21-25`). + +### Tab: Route (20 controls, 2 list columns) + +| Control | Type | L,T,W,H | Text | Notes | +|---|---|---|---|---| +| Label5 | StaticText | 0,0,370,16 | "Navigation Waypoints" | | +| lstWaypoints | List | 0,16,370,116 | — | columns `clWP` (TextColumn, 330), `clWPc` (TextColumn, 30) | +| layoutRouteBottomControls | FixedLayout | 0,136,370,16 | — | nested layout, XML comment: "A layout containing stuff below the points list to make resizing easier" | +| cmbNavType | Choice | 0,0,80,16 (nested) | selected=0 | options: Circular / Linear / Follow / Once | +| cmbNavInsertMode | Choice | 198,0,120,16 (nested) | selected=0 | options: Add to End / Insert Above / Insert Below | +| btnNavDown | Button (image) | 322,0,16,16 (nested) | — | image button, `Background=100673789` **[confirmed]** — `PluginCore.cs:1820`; tooltip "Advance Current Point" | +| btnNavUp | Button (image) | 338,0,16,16 (nested) | — | `Background=100673788` **[confirmed]** — `PluginCore.cs:1822`; tooltip "Regress Current Point" | +| btnNavResetPoint | Button (image) | 354,0,16,16 (nested) | — | `Background=100667895` **[confirmed]** — `PluginCore.cs:1824`; tooltip "Select Nearest Point" | +| cmdNewWaypoint | PushButton | 376,16,88,16 | "Add" | | +| cmdNavOpenVendor | PushButton | 470,16,88,16 | "Open Vendor" | | +| cmdNewWaypointPortal | PushButton | 376,38,88,16 | "Add Portal/NPC" | | +| cmdNavUseNPC | PushButton | 470,38,88,16 | "Add NPC Talk" | | +| cmdNewRecall | PushButton | 376,60,88,16 | "Add Recall" | | +| cmbRecallType | Choice | 470,60,120,16 | selected=0 | 27 options: Primary/Secondary/LS/LS Sending/Portal/Aphus/Sanctuary/Caul/GW/Aerlinthe/Mt. Lethe/Ulgrim's/Bur/PtOIA/Graveyard/Colosseum/Fac. Hub/Gear K. Camp/Neftet/Candeth/FacHub/Rynthid/VR Rocks/VR Tree/Soc. CH/Soc. RB/Soc. EW (note: "Fac. Hub" and "FacHub" both present — another authoring duplicate) | +| cmdNewWaypointPause | PushButton | 376,82,88,16 | "Add Pause" | | +| txtPauseWaypointTime | Edit | 470,82,68,16 | "5" | only Edit control in the whole file with a non-empty default `text` | +| Label52 | StaticText | 543,82,50,16 | "seconds" | | +| cmdNewWaypointChat | PushButton | 376,104,88,16 | "Add Chat" | | +| txtChatWaypoint | Edit | 470,104,120,16 | "/ls" | | + +The `100673789`/`100673788`/`100667895` background image ids assigned to +the three nav image-buttons decode (subtracting VVS's `0x6000000` DAT +block prefix) to portal.dat indices `0x0A47FD`, `0x0A47FC`, +`0x0A2BF7` — retail DAT icon glyphs, not synthesized art. + +### Tab: Meta (5 controls, 6 list columns) + +| Control | Type | L,T,W,H | Text | Columns | +|---|---|---|---|---| +| lstMetaRules | List | 0,16,856,116 | — | `clDelete` (IconColumn,16), `clMoveUp` (IconColumn,16), `clMoveDown` (IconColumn,16), `clState` (TextColumn,150), `clCondition` (TextColumn, no fixedwidth → auto-sized), `clAction` (TextColumn, no fixedwidth → auto-sized) | +| cmdMetaCreate | PushButton | 462,136,50,16 | "Create" | | +| lblmetacurrentstate | StaticText | 4,136,136,16 | "Current State:" | bold, left-justified | +| cmbMetaCurrentState | Choice | 140,136,120,16 | selected=0 | one placeholder option `"---"` — populated at runtime from the profile's actual state list | + +### Secondary view: AdvancedOptionsView (392×300, icon 9660) + +| Control | Type | L,T,W,H | Columns | +|---|---|---|---| +| lOptionList | List | 4,4,260,160 | `clOpt` (TextColumn,180), `clVal` (TextColumn,62) | +| txtInfo | StaticText | 4,188,384,80 | — (description readout for the selected option) | +| txtEditbox | Edit | 4,168,260,16 | — | +| lFilterList | List | 268,4,120,180 | `clEn` (CheckColumn, no fixedwidth), `clSetting` (TextColumn, no fixedwidth) | + +This is the raw editor over the full `Settings` table +(`defaultsettings.usd:135-144`: `Setting, Value, Description, +SettingType`) — **137 rows** per the file's own row count +(`defaultsettings.usd:145`), matching MossTank's `mosstank.xml:107` +tooltip text "Select one of Virindi Tank's 137 advanced options." +`lFilterList` is a checklist of `SettingsCategories` bitmask groups +(`defaultsettings.usd:1513-1518`) used to filter `lOptionList`. + +### Secondary view: SelfBuffChoiceView (268×236, icon 8241) + +| Control | Type | L,T,W,H | Text | Columns | +|---|---|---|---|---| +| lExemplarList | List | 4,4,260,160 | — | `clVal` (TextColumn, no fixedwidth) | +| lblSearch | StaticText | 4,168,384,16 | "Search:" | — | +| txtSearch | Edit | 4,188,260,16 | "" | — | + +A simple filtered single-column exemplar picker, opened from the Buffs +tab's "Add..." buttons (`cmdNewBuffSpell`/`cmdNewAntiBuffSpell`) — the +`txtSearch` live-filters `lExemplarList` (loaded twice under two different +obfuscated class names, `e7.cs:108` and `hz.cs:108`, once per "Add..." +button, each with its own filter predicate). + +### Secondary view: FriendlyLootEditorView (268×300, icon 8241) + +Two-tab `Notebook1`: + +| Page | Control | Type | L,T,W,H | Text | Columns | +|---|---|---|---|---|---| +| Rules | LabelA1 | StaticText | 4,2,80,16 | "Loot Rule:" | — | +| Rules | cmbRuleList | Choice | 4,18,240,20 | — | — | +| Rules | LabelA2 | StaticText | 100,140,150,16 | "This page not yet completed." | — | +| Names | LabelB1 | StaticText | 4,2,80,16 | "Name match:" | — | +| Names | LabelB2 | StaticText | 230,2,30,16 | "Exact" | — | +| Names | lNames | List | 4,18,260,180 | — | `clName` (TextColumn,228), `clExact` (CheckColumn,32) | +| Names | LabelB3 | StaticText | 4,200,80,16 | "New name:" | — | +| Names | txtNewName | Edit | 4,212,196,16 | "" | — | +| Names | cExact | Checkbox | 204,212,40,16 | "Ex." checked=true | — | +| Names | bAddName | PushButton | 234,212,30,16 | "Add" | — | + +**The "Rules" page is a stub in retail VTank itself** — its own XML +literally reads "This page not yet completed." (`FriendlyLootEditorView.xml:10`). +Only the "Names" (name-match) editor is functional. This is the one place +where our `mosstank.xml`'s loot editor (§5) is **more complete than VTank's +own shipped UI**, since it implements a full rule list with action/priority +editing that VTank never finished. + +--- + +## 2. VVS control semantics to reproduce for parity + +| VVS control | Key semantics | Source | +|---|---|---| +| `HudList` | Vertically scrolling grid; every row has the same ordered column set (`cColInfo`: `ControlType`/`Width`/`Name`), each cell is a live child control (`HudStaticText`/`HudCheckBox`/`HudPictureBox`/`HudButton`/`HudTextBox`/nested `HudList`/`HudFixedLayout`/`HudTabView`). **No built-in header row** — the header glyphs seen in `mainView.xml` (F/B/G/I/…) are ordinary `StaticText` controls placed manually above the list at `top=0`, with the list itself starting at `top=16`. Row height defaults to `ControlHeight=16px`; `Padding=1px` (vertical), `WPadding=7px` (horizontal gap between columns), `WPaddingOuter=3px` (left/right list margin). A `0`-width text/button/edit/list/fixedlayout/notebook column auto-sizes: all such columns in one list share the remaining width equally after fixed-width and icon (fixed at declared width) and check (fixed 13px) columns are subtracted. Selection is not built in as a highlighted-row concept — `Click` is a **per-cell** event `delClickedControl(sender, row, col)`, fired from `MouseUp` when the press and release land in the same cell; callers implement "selected row" state themselves. Scrolling is a `HudVScrollBar` child, width from the theme's `VScrollBarButtonSize` (16px in `Float_Theme`), driven by mouse-wheel or drag. | `HudList.cs:14-609` (whole file); `Float_Theme.cs:58` | +| `HudList` column mapping (from progid) | `IconColumn`→`HudPictureBox` (default 16px), `TextColumn`→`HudStaticText`, `CheckColumn`→`HudCheckBox` (fixed 13px regardless of declared width), plus (less commonly used) `FixedLayout`→`HudFixedLayout`, `Notebook`→`HudTabView`, `PushButton`→`HudButton`, `Checkbox`→`HudCheckBox`, `Edit`→`HudTextBox`, `StaticText`→`HudStaticText`, `List`→`HudList` (nested) | `Decal3XMLParser.cs:257-424` | +| `HudCombo` (`Choice`) | A dropdown: closed state shows the selected item's control (usually a `HudStaticText`) in a `ComboBackground_Unselected` box with a themed up/down arrow (16px square, `arrowrect`). Opening spawns a **separate top-level popup surface** (`DxHud`, its own device/texture, `+1` z-priority above the owning view) sized to `min(item count, 10) * 16px` rows (`m_j=10` fixed max-visible, `dropdownamt`), with its own `HudVScrollBar` if the item count exceeds 10. Selecting an item raises `Change`; closing happens on any click outside the drop rect, or via `FireLostFocus`. `AddItem`/`InsertItem` accept either a raw string (wrapped in a `HudStaticText`) or an arbitrary `HudControl` + opaque tag object — so combo items are not restricted to plain text. | `HudCombo.cs:15-744` | +| `HudTabView` (`Notebook`) | Tab strip height is themed (`TabHeight=16px` in `Float_Theme`). Each tab's width auto-sizes to its caption's rendered text width (capped at 280px) plus themed left/right border sizes (`TabLeftBorder_Size`/`TabRightBorder_Size`, both `0` in `Float_Theme`) plus a themed horizontal padding (`TabTextHPadding=6px`). Selected vs. deselected tabs use **separate font options** (`TabTextFontName/Size/Weight/Color/Shadow*_Selected` vs `_Deselected`). Mouse wheel over the tab strip cycles tabs; over the body it's forwarded to the active child. Only one tab's content control is ever `CanDraw=true` at a time — switching tabs disposes no state, it just flips visibility/draw flags on the existing child controls (so per-tab state survives switching, which matters for MossTank/acdream parity: switching tabs must not reset in-progress edits). | `HudTabView.cs:12-455`; `Float_Theme.cs:91-93` | +| `HudHSlider` (`Slider`) | Backed by `LinearPositionControl` (`Min`/`Max`/`Position`, `Changed(min,max,pos)` event, `Percentage` helper = `100*Position/(Max-Min)`). Draws as a 3px-tall two-tone bar (`SliderBarOuter`/`SliderBarInner`) plus a themed nub (`SliderNubWidth=7px`, `SliderNubHeight=12px` in `Float_Theme`) whose X position is `GetPositionProportionalPixels`. Drag-to-set, click-to-jump-to-position, and mouse-wheel (±1 unit per notch, accumulator-smoothed via `v.a`) are all supported; `UserChangeable=false` disables all three without hiding the control. | `HudHSlider.cs:9-131`, `LinearPositionControl.cs:6-186`, `Float_Theme.cs:54-55` | +| `HudTextBox` (`Edit`) | Single-line text input with a blinking caret (500ms toggle via an internal timer `r`), left/right/Home/End/Backspace/Delete/Ctrl+V (paste from the Windows clipboard) key handling, and an optional numeric-only input mode (`eTextBoxInputType.Int_Numeric`/`Float_Numeric`) that rejects any edit that doesn't `TryParse`. Background is themed (`TextBoxBackground`/`_MouseOver`) with a 1px `ButtonHighlight`/`ButtonShadow` bevel. **The `imageportalsrc="4726"` attribute present on every `Edit` control in `mainView.xml` is never read by `Decal3XMLParser.cs`'s `"decalcontrols.edit"` case** — only `text` is consumed; the parser never sets `HudPictureBox.Image` for an Edit control, and a repo-wide grep of VTank's own source found no other code path assigning it either. This is a **dead/vestigial XML attribute**, likely copy-pasted from an older Decal 3.0 view-XML dialect; acdream's markup does not need to reproduce it. | `HudTextBox.cs:11-417` (esp. `RawKeyAction`/`KeyDown` `260-350`, `DrawNow` `371-416`); parser evidence `Decal3XMLParser.cs:205-212`; dead-attribute confirmed by grep across `refs/vtank/decompiled/` (only hits are the literal XML strings and one string-builder-generated XML fragment in `h8.cs:104`) | +| `HudButton` (`PushButton`) vs. `HudImageButton` (`Button`) | **These are two different progids mapping to two different classes.** `PushButton` → `HudButton`: text-only push button, themed `ButtonBackground`/`_MouseOver`/`_Down`/`_Down_MouseOver` fills with a 1px bevel, optional `ImagePressed`/`OverlayImage` override. `Button` → `HudImageButton`: an image-only button taking `iconlibrary` (a portal-dat "icon library" grouping id), `background`/`icon`/`pressedicon` (each resolved via `ACImage.FromIconLibrary` unless the numeric value already looks like a raw image id, per `global::b.d`), and `matte` (a background color override, with `Color.Cyan` specifically mapped to fully transparent). Neither VTank's `mainView.xml` uses `Button` directly (only the three Route-tab nav arrows use it, via VTank's own C# code assigning `.Background` post-construction rather than XML `icon=`/`background=` attributes — see the Route-tab table above) nor does `AdvancedOptionsView.xml`/`FriendlyLootEditorView.xml`. | `HudButton.cs:9-226`; parser `Decal3XMLParser.cs:157-195` (`PushButton`), `:165-195` (`Button`) | +| `HudCheckBox` (`Checkbox`) | Fixed **13×13px** glyph box regardless of the declared control width/height (only the optional trailing label text uses the extra width). Three visual states: unchecked/checked (each with a `_MouseOver` variant) plus a transient "partial" press-feedback state while the mouse is down inside the box. `UserChangeable=false` disables toggling without hiding it. Label text (when present) starts at a fixed `+17px` offset from the left edge. | `HudCheckBox.cs:10-267` | +| `HudControl` (base) | Only left-button `MouseDown`/`MouseUp`/`MouseMove`/`MouseWheel` are wired; there is no right-click or double-click concept anywhere in the base class or in any of the concrete controls surveyed. A `Hit` event fires once per press-then-release-inside-the-same-control. Focus is a single static `HudView.FocusControl` — only one control across the *entire* VVS runtime (all views, all plugins) can have keyboard focus at a time. | `HudControl.cs:13-712` | + +--- + +## 3. Mapping to acdream markup + +### 1:1 today + +| VVS control | acdream element | Notes | +|---|---|---| +| `PushButton`/`Button` (text) | `