feat(mosstank): add VTank-style automation PoC
This commit is contained in:
parent
f6fe0f2a4f
commit
4e6e9bc9d9
212 changed files with 49462 additions and 416 deletions
|
|
@ -24,6 +24,79 @@ What does NOT go here:
|
|||
- Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending.
|
||||
- Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed.
|
||||
|
||||
## #451 — GLFW can dereference another acdream process's private window pointer after cross-process activation
|
||||
|
||||
**Status:** IN-PROGRESS — exact root fixed; 100-switch/30-minute dual-client
|
||||
stress passed 2026-08-27. Graceful exit of both sessions remains before closure.
|
||||
**Component:** graphical host / GLFW Win32 event pump / multi-process stability.
|
||||
**Severity:** HIGH for multi-account play; one of the two sessions is lost without
|
||||
an orderly disconnect.
|
||||
|
||||
Running two copies of the exact isolated `app-release23` graphical artifact
|
||||
against local ACE reproduced the same access violation three times. The
|
||||
faulting process varied: secondary PID 13688 at 15:01:39, primary PID 15300 at
|
||||
15:03:35, and fresh primary PID 31412 at 15:08:58. The last occurrence fired
|
||||
while the fresh primary was still on character selection, before EnterWorld;
|
||||
the already-in-world secondary survived. Windows Application Error reports
|
||||
all three as `coreclr.dll` exception `0xC0000005`, fault offset `0x356d4f`.
|
||||
The managed terminal stack is only:
|
||||
|
||||
```text
|
||||
Silk.NET.Windowing.WindowExtensions...Run
|
||||
Silk.NET.Windowing.Internals.ViewImplementationBase.Run
|
||||
Silk.NET.Windowing.Glfw.GlfwWindow.Run
|
||||
AcDream.App.Rendering.GameWindow.Run
|
||||
```
|
||||
|
||||
This is not #422's rare `0xC0000374` heap corruption during graceful process
|
||||
exit: #451 happens while both graphical clients are active and reproduces
|
||||
quickly. It is also not a MossTank/plugin-API, CoreCLR, Vulkan, PAK, or world-
|
||||
cache failure.
|
||||
|
||||
**Exact root (first-chance cdb proof):** the access violation is in packaged
|
||||
GLFW's Win32 event pump at `glfw3+0x10681`, not in CoreCLR. During its modifier-
|
||||
key repair pass `_glfwPollEventsWin32` calls `GetActiveWindow`, then
|
||||
`GetPropW(hwnd, L"GLFW")`, and dereferences the returned value as this
|
||||
process's `_GLFWwindow*`. Windows UI automation temporarily joins input queues,
|
||||
so the primary process can receive the secondary process's HWND. Because every
|
||||
GLFW process uses the same `GLFW` property name, `GetPropW` succeeds but returns
|
||||
the secondary process's private pointer. The crashed primary had
|
||||
`rbx=00000202a7180ab0`; a debugger breakpoint in the surviving secondary
|
||||
reported its own valid `ACTIVE_GLFW_WINDOW=00000202a7180ab0` — exact pointer
|
||||
identity across the process boundary.
|
||||
|
||||
**Fix:** `Win32GlfwActiveWindowGuard` patches only `glfw3.dll`'s import-address-
|
||||
table slot for `USER32!GetActiveWindow`, after the GLFW library is loaded and
|
||||
before `glfwInit`/window creation. The replacement returns the real HWND only
|
||||
when `GetWindowThreadProcessId` says it belongs to the current process;
|
||||
otherwise it returns zero, GLFW's existing safe "nothing to repair" branch.
|
||||
There is no system-wide hook and no other module is changed. Four focused
|
||||
tests cover local, foreign, null and unowned HWNDs.
|
||||
|
||||
The isolated `app-release24` live gate launched two graphical clients, both
|
||||
logged `GLFW foreign-active-window guard installed (#451)`, entered the world,
|
||||
and remained responsive through 100 rapid forced cross-process activation
|
||||
switches — the exact prior trigger — plus a 30-minute combined in-world soak.
|
||||
The local peer API then passed in both directions: the secondary evaluated the
|
||||
primary heartbeat and returned `+Acdream`. A two-member fellowship gate also
|
||||
passed with both canonical rosters populated and the secondary returning `2`
|
||||
from `getfellowshipcount[]`; both processes remained alive and responsive.
|
||||
|
||||
Evidence:
|
||||
|
||||
- `artifacts/live-gates/mosstank-final23-secondary/`
|
||||
- `artifacts/live-gates/mosstank-final23-secondary2/`
|
||||
- `artifacts/live-gates/mosstank-final23-primary5/`
|
||||
- `artifacts/live-gates/i451-cdb-primary-attach/cdb.log`
|
||||
- `artifacts/live-gates/i451-cdb-secondary/cdb.log`
|
||||
- `artifacts/live-gates/i451-guard-primary2/`
|
||||
- `artifacts/live-gates/i451-guard-secondary/`
|
||||
- Windows Application Error events at 2026-08-27 15:01:39, 15:03:35 and
|
||||
15:08:58 (same module, exception and offset).
|
||||
|
||||
**Next:** close only after both `app-release24` sessions exit gracefully; the
|
||||
activation and sustained in-world portions of the regression gate have passed.
|
||||
|
||||
## #450 — Fast character re-entry after logout can remain in portal space at `lb 0/0`
|
||||
|
||||
**Status:** DONE — OWNER-ACCEPTED 2026-08-26 in the combined client-parity gate.
|
||||
|
|
|
|||
|
|
@ -137,6 +137,68 @@ loads none). The headless adapter projects entity snapshots on demand from the
|
|||
canonical Runtime view, subscribes to Runtime's ordered events, and borrows the
|
||||
exact Runtime selection owner; it does not mirror gameplay state.
|
||||
|
||||
Graphical plugin panels are first-class retained windows. A plugin calls
|
||||
`IUiRegistry.AddPanel` with a BCL-only `PluginPanelDescriptor`; Core's scoped
|
||||
host authenticates the owner from the loaded manifest, and App derives the
|
||||
stable identity `plugin:{pluginId}:{windowId}`. The host, not the plugin, owns
|
||||
window geometry, z-order, persisted visibility, minimize/restore chrome, and
|
||||
the shared right-edge plugin shelf. Minimizing only hides the presentation:
|
||||
the plugin session, event subscriptions, automation policy, and binding object
|
||||
remain live. Legacy `AddMarkupPanel` registrations are enriched into the same
|
||||
first-class path, so API-v1 plugins keep working without a second lifecycle.
|
||||
The markup vocabulary includes nested groups plus retained tab, toggle,
|
||||
slider, editable-field, and retail-menu controls. Fields bind live
|
||||
`Action<string>` change/submit callbacks and menus bind an
|
||||
`IEnumerable<string>` plus selection callback, so plugin-owned profile/rule
|
||||
editors stay behind the BCL contract instead of importing App widgets. These
|
||||
are presentation bindings only and never become parallel gameplay owners.
|
||||
|
||||
Durable plugin data uses the BCL-only `IPluginHost.Storage` contract. Core's
|
||||
manifest-authenticated scoped host prefixes every logical key with the loaded
|
||||
plugin id; graphical composition writes atomically beneath the per-user config
|
||||
root (`plugins/{pluginId}/...`). Plugins receive neither another plugin's
|
||||
namespace nor a machine-specific path. Hosts without durable storage expose
|
||||
`NoOpPluginStorage` and report the capability unavailable.
|
||||
The additive `List(prefix)` operation enumerates only keys inside that same
|
||||
authenticated namespace, allowing plugins to discover explicit import/export
|
||||
files without receiving a filesystem path or crossing plugin ownership.
|
||||
|
||||
`IPluginHost.Automation` is the additive gameplay-automation projection. Its
|
||||
character, spell, magic, chat, combat, equipment, item, loot, fellowship,
|
||||
enchantment-observation, and navigation
|
||||
groups contain BCL-only immutable snapshots plus attempt-style commands; the
|
||||
graphical implementation borrows the exact `GameRuntime`
|
||||
character/action/entity/object/vendor/fellowship owners. Item projections also
|
||||
carry Virindi's stable ObjectClass plus ordered ObjDesc subpalette samples;
|
||||
the graphical host resolves each representative RGB directly from portal DAT
|
||||
using VTank's sample-index formula. MossTank owns all
|
||||
macro policy (buff planning, target rules, selection scoring, corpse policy,
|
||||
loot-rule ordering and action timing). In particular, `ICombatAutomation`
|
||||
does not create a plugin combat model: each hostile capture is a detached
|
||||
point-in-time projection of `RuntimeHostileTargetQuery`, and physical commands
|
||||
enter the canonical `RuntimeCombatModeState` / `RuntimeCombatAttackState`
|
||||
press-charge-release state machine. Item and loot commands similarly enter
|
||||
App's one `ItemInteractionController`: appraisal, use/apply, pickup,
|
||||
move/split/merge/drop/give, retail 0x027D salvage, and current-vendor sale
|
||||
reuse the same readiness checks, reservations, wire sends, and authoritative
|
||||
completion/object-table signals as retained retail UI. Plugins never hold an
|
||||
optimistic inventory or vendor shadow. Navigation similarly projects live and
|
||||
server-accepted position, portal/object state, and semantic movement levels;
|
||||
the App host applies those levels through Runtime's one command interpreter.
|
||||
Route sequencing, steering cones, follow breadcrumbs, checkpoint policy,
|
||||
door/lockpick decisions and portal retry behavior remain plugin-owned. The
|
||||
shared enchantment-observation group is deliberately a confirmed-cast timer
|
||||
ledger rather than another authoritative spellbook: the host records successful
|
||||
local duration casts and cooperating plugins can report their own confirmed
|
||||
casts, matching VTank's `LogSpellCast` contract. It resets at session detach;
|
||||
dispel/debuff policy remains plugin-owned. The
|
||||
inert default remains
|
||||
`NoOpAutomationSurface`, preserving one plugin code path on hosts without a
|
||||
live gameplay session.
|
||||
`ICharacterInfo.Name` projects the canonical local `ClientObject.Name` (empty
|
||||
when unavailable) solely for per-character plugin profile scoping; it does not
|
||||
introduce a second identity owner.
|
||||
|
||||
Core `SelectionState` is the sole selected-object owner for world,
|
||||
radar, inventory, paperdoll, toolbar, use/examine consumers, and plugins;
|
||||
`IPluginHost.Selection` exposes that same state and retail-style old/new callback.
|
||||
|
|
@ -420,6 +482,10 @@ src/
|
|||
IGameState.cs -> done
|
||||
IEvents.cs -> done
|
||||
ISelectionService.cs -> done
|
||||
IPluginStorage.cs -> manifest-scoped durable text profiles
|
||||
Automation.cs -> character/spell/magic/chat automation groups
|
||||
CombatAutomation.cs -> hostile snapshots + retail combat attempts
|
||||
EnchantmentAutomation.cs -> shared confirmed duration-cast timer ledger
|
||||
|
||||
AcDream.App/ Layer 1 + Layer 4 wiring
|
||||
Platform/
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Release
|
|||
| `ACDREAM_NEAR_RADIUS` | `=<int>` | Overrides preset's `NearRadius` (inner streaming ring radius) | Changes the streaming/render window size — CLAUDE.md: leave unset for measurement/gate runs | preset's `NearRadius` (Low=2, Medium=3, High=4, Ultra=5) | `QualitySettings.WithEnvOverrides` (`QualityPreset.cs:46`) |
|
||||
| `ACDREAM_NO_AUDIO` | `=1` | Suppresses OpenAL device init entirely (headless / driver-broken machines). | Fail-open design: even without this flag, a missing/broken OpenAL driver already makes `IsAvailable=false` and all `Play*` calls no-ops — this flag is the explicit, deliberate version of the same fallback. When set, `LiveSessionWorldRuntime.WorldAudio` is `null` and logout-audio reset/resume steps no-op. | `false` | `RuntimeOptions.NoAudio` → `GameWindow.cs:1430` → `ContentEffectsAudioCompositionPhase` → `OpenAlAudioEngine.cs` (fail-open doc), `LiveSessionRuntimeFactory.cs:71` (`WorldAudio` gate) |
|
||||
| `ACDREAM_PAK_PATH` | `=<path>` | Overrides the path to the prepared machine-local asset bake (`acdream.pak`) that production world-mesh streaming reads through `IPreparedAssetSource`. | none beyond the obvious | unset → `<datDir>/acdream.pak` | `RuntimeOptions.PreparedAssetPath` → `ContentEffectsAudioComposition.cs:53,372,379`, `GameWindow.cs:1420` |
|
||||
| `ACDREAM_PLUGIN_TAGS` | comma-separated tags (maximum 128 tags, 128 characters each) | Advertises machine-local role/group tags through the plugin peer-discovery API, for UtilityBelt-compatible expressions such as client selection by tag. Values are trimmed and deduplicated case-insensitively. | Writes the tags into the bounded local peer heartbeat document while a character is in world; no network traffic leaves the machine. | unset → no tags | `RuntimeOptions.PluginTags` → `AppAutomationSurface` / `LocalPluginPeerRegistry` |
|
||||
| `ACDREAM_RESIDENCY_ALPHA_SCRATCH_MIB` | `=<int MiB>` (`>0`, else default; overflow-checked) | Byte ceiling for the retail alpha (translucency) draw queue's scratch buffer | Shrinking below production working set changes translucency-queue eviction/reflow behavior — not comparable to a default-budget perf run | 16 MiB | `ResidencyBudgetOptions.Parse` (`ResidencyBudgetOptions.cs:88-89`), flows through `RuntimeOptions.ResidencyBudgets`→`AlphaScratchBudgetProfile.Create`→`RetailAlphaQueue` ctor (`GameWindow.cs:721-725`) |
|
||||
| `ACDREAM_RESIDENCY_ANIMATION_ENTRIES` | `=<int>` (`>0`, else default) | Entry-count ceiling for the retained animation-data cache | Changes cache eviction cadence for animation data — a perf/memory measurement under a non-default value is not representative | 512 | `ResidencyBudgetOptions.Parse` (`:82-84`) |
|
||||
| `ACDREAM_RESIDENCY_ANIMATION_MIB` | `=<int MiB>` (`>0`, else default) | Byte ceiling for the retained animation-data cache | Same cache-behavior-change caveat as the entries variant | 64 MiB | `ResidencyBudgetOptions.Parse` (`:79-81`) |
|
||||
|
|
|
|||
|
|
@ -184,17 +184,28 @@ panel through `IPanelRenderer`.
|
|||
|
||||
## Plugin UI API
|
||||
|
||||
The shipped plugin-facing gameplay UI contract is
|
||||
`AcDream.Plugin.Abstractions.IUiRegistry.AddMarkupPanel`: a plugin provides
|
||||
KSML-style markup and a binding object; the host builds it into the retained
|
||||
`UiRoot` tree. `IPanel`/`IPanelRenderer` remains a first-party developer-panel
|
||||
contract and is intentionally not referenced by `Plugin.Abstractions`.
|
||||
The shipped plugin-facing gameplay UI contract is the additive BCL-only
|
||||
`AcDream.Plugin.Abstractions.IUiRegistry.AddPanel`: a plugin provides a stable
|
||||
window id/title/icon descriptor, KSML-style markup, and a binding object; the
|
||||
host builds it into the retained `UiRoot` tree. The API-v1
|
||||
`AddMarkupPanel` member remains source/binary compatible and is enriched into
|
||||
the same first-class window route by the scoped host. `IPanel`/
|
||||
`IPanelRenderer` remains a historical first-party developer-panel contract and
|
||||
is intentionally not referenced by `Plugin.Abstractions`.
|
||||
|
||||
This makes plugin gameplay panels independent of ImGui while allowing them to
|
||||
share the retained input, window, and DAT-sprite runtime. Registrations made
|
||||
before the GL host exists are buffered. In builds where retail UI is disabled,
|
||||
they remain registered but have no gameplay surface; the long-term release
|
||||
configuration enables retained gameplay UI.
|
||||
This makes plugin gameplay panels presentation-assembly independent while
|
||||
allowing them to share the retained input, window, and DAT-sprite runtime.
|
||||
Registrations made before the graphical host exists are buffered. The host
|
||||
assigns `plugin:{pluginId}:{windowId}`, registers every panel with the common
|
||||
window manager, persists its geometry/visibility, and exposes it through the
|
||||
shared plugin sidepanel. Hiding/minimizing a panel does not dispose or pause the
|
||||
plugin. No-window hosts retain the plugin session but expose the no-op UI
|
||||
capability.
|
||||
|
||||
The retained markup vocabulary includes panels, nested groups, labels,
|
||||
buttons, meters, tabs, lamp-style toggles, and scalar sliders. Controls bind to
|
||||
BCL-visible properties/actions on the plugin binding object; visible controls
|
||||
must correspond to real behavior, never placeholders that report success.
|
||||
|
||||
The following was the original pre-D.2b proposal and remains historical
|
||||
context, not the shipped plugin contract:
|
||||
|
|
@ -255,7 +266,8 @@ walk around / take damage / regen.
|
|||
### Sprint 3 — Plugin API hardening (superseded shape)
|
||||
|
||||
- Document the `IPanel` contract.
|
||||
- The shipped route is `IUiRegistry.AddMarkupPanel`, not plugin-owned
|
||||
- The shipped route is `IUiRegistry.AddPanel` (with `AddMarkupPanel` as the
|
||||
compatible legacy entry), not plugin-owned
|
||||
`IPanel` implementations.
|
||||
- Confirm plugins can subscribe to game events and expose retained markup
|
||||
bindings without referencing App or ImGui assemblies.
|
||||
|
|
|
|||
483
docs/plans/2026-08-26-mosstank-parity-campaign.md
Normal file
483
docs/plans/2026-08-26-mosstank-parity-campaign.md
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
# MossTank — VTank parity campaign
|
||||
|
||||
Date: 2026-08-26
|
||||
Status: ACTIVE — MT1 USER-PASSED; MTUI–MT9 functional/API scope complete; connected shelf/shell, accessibility, reconnect, bidirectional peer-expression and two-member fellowship gates passed; #451 root fixed and 30-minute dual-client activation soak passed; hostile/collision gates remain
|
||||
|
||||
Research baseline:
|
||||
`docs/research/2026-08-26-mosstank-vtank-utilitybelt-research.md`
|
||||
|
||||
## Product definition
|
||||
|
||||
MossTank will provide the complete automation capability associated with
|
||||
Virindi Tank, implemented as a first-class acdream plugin over a stable,
|
||||
BCL-only plugin API. UtilityBelt's typed expression dialect is the scripting
|
||||
baseline. Native file formats may differ; behavior and extensibility may not.
|
||||
The finished surface is a visually verbatim VTank reproduction: every VTank
|
||||
tab and function is present and every enabled control invokes real behavior.
|
||||
|
||||
## Non-negotiable boundaries
|
||||
|
||||
- modern code, behavior matched to documented VTank/retail behavior;
|
||||
- one Runtime owner for every state/action; plugin API is a borrowed projection;
|
||||
- policy engines remain in MossTank, not App or Runtime;
|
||||
- plugin UI only through `IUiRegistry`;
|
||||
- every API addition works in graphical and no-window hosts, with explicit
|
||||
unavailable behavior until the host can genuinely supply it;
|
||||
- no fake success and no silent expression-function omission.
|
||||
|
||||
## Slice ledger
|
||||
|
||||
### MT0 — research and campaign design
|
||||
|
||||
- [x] Reconcile existing VTank audit with current Runtime ownership.
|
||||
- [x] Audit current UtilityBelt grammar and all 260 expression declarations.
|
||||
- [x] Define complete capability ledger and staged architecture.
|
||||
|
||||
### MT1 — autocombat foundation (current stop gate)
|
||||
|
||||
- [x] Add target/combat views and attempt commands to the plugin API.
|
||||
- [x] Project canonical hostile, selection, mode, power and spell state.
|
||||
- [x] Implement target lock and range/angle/hybrid selection.
|
||||
- [x] Implement melee/missile charge-release and direct offensive magic.
|
||||
- [x] Deliver the polished combat dashboard and settings.
|
||||
- [x] Focused, App/Runtime and complete solution gates.
|
||||
- [x] Connected user gate: user confirmed autocombat works in the plugin.
|
||||
|
||||
MT1 intentionally does not pretend later features exist. It is “autocombat
|
||||
ported,” not “all combat policy ported.”
|
||||
|
||||
### MTUI — generic plugin-window and VTank shell foundation
|
||||
|
||||
- [x] Add manifest-authenticated, stable plugin panel descriptors without
|
||||
breaking API-v1 hosts/plugins.
|
||||
- [x] Register plugin panels with the common retained window manager so
|
||||
geometry and visibility persist.
|
||||
- [x] Add the shared right-edge plugin shelf and window minimize/restore;
|
||||
hidden panels leave the plugin session and automation running.
|
||||
- [x] Add reusable nested groups, tabs, lamp toggles and sliders to retained
|
||||
plugin markup.
|
||||
- [x] Replace MossTank's dashboard/settings pair with one VTank-shaped shell
|
||||
using the exact Options, Profiles, Vitals, Monsters, Items, Consumables,
|
||||
Buffs, Route, Meta tab order.
|
||||
- [x] Bind all currently enabled controls to real MT1/buff behavior and leave
|
||||
unimplemented tabs visibly disabled.
|
||||
- [x] Enable the Items/Consumables pages against durable, manifest-scoped
|
||||
exact-name profiles; selection and Add/Add-no-buffs/Add-All-Peas controls
|
||||
all mutate the policy consumed by combat.
|
||||
- [x] Connected visual gate: shelf placement, minimize/restore persistence,
|
||||
and first VTank-shell comparison in the live client.
|
||||
|
||||
### MT2 — complete monster/weapon/debuff combat policy
|
||||
|
||||
- [x] ordered `DEFAULT` + first-match monster rules;
|
||||
- [x] priorities -1..4 and complete action-flag matrix;
|
||||
- [x] damage/weapon/offhand selection, swap state machine and auto power,
|
||||
including the official GameInfoDB exact-name overrides, ordered creature-
|
||||
species preferences, and VTank's final elemental fallback;
|
||||
- [x] debuff groups, skill/level choice, receipt-gated reapply and explicit
|
||||
wand switching policy;
|
||||
- [x] ring/arc/bolt density/range logic, streaks, Void, harm/martyr, grenades,
|
||||
lenses, cast-on-strike and pets;
|
||||
(carried phials are complete; crafting a missing phial belongs to MT4's
|
||||
generalized craft transaction);
|
||||
- [x] blacklist and both ghost-monster detectors, including canonical App
|
||||
entity teardown for a detected client ghost.
|
||||
|
||||
MT2 checkpoint 2026-08-27: the BCL API now projects complete learned-combat
|
||||
spell metadata, server cast and physical-attack receipts, health-update
|
||||
revision/age, canonical equipment snapshots/commands, and exact-incarnation
|
||||
ghost deletion. MossTank owns the complete Monsters expression/action model,
|
||||
debuff tracker, elemental/shape spell catalog, range/density selection,
|
||||
weapon/offhand policy, temporary blacklist, and both VTank ghost algorithms.
|
||||
Focused evidence at this checkpoint: 104 MossTank tests, 20 Runtime action/
|
||||
target tests, and an isolated Release App build all pass with zero failures or
|
||||
warnings. MT2 remains open for automatic physical power and the four item-
|
||||
backed combat families.
|
||||
|
||||
MT2 checkpoint 2 (2026-08-27): the official VTank assembly and live GameInfoDB
|
||||
feed were inspected directly. Item appraisal SpellBooks are now retained;
|
||||
plugins receive ordered combat chat and exact item UseDone receipts; the
|
||||
source planner implements `dz.b.CompareTo` for SpellLevel/Skill preference;
|
||||
the 72 official phials, lenses, cast-on-strike weapons and pets are executable
|
||||
and profile-gated; proc success waits for the actual `You cast ... on ...`
|
||||
line. `hi.cs` automatic attack power, including Recklessness clamping, is
|
||||
ported verbatim. Items/Consumables profiles are atomically persisted through a
|
||||
new per-manifest plugin-storage contract. Focused evidence: 131 MossTank tests,
|
||||
the storage/chat/App tests, and an isolated Release App build pass. MT2 remains
|
||||
open only for target-database `Auto` damage selection and the connected gate;
|
||||
missing-grenade crafting is deliberately MT4 transaction scope.
|
||||
|
||||
MT2 automated closeout (2026-08-27): `Auto` now consumes the official 59-name
|
||||
override and 103-species preference tables. The ordered element decision
|
||||
outranks spell shape/tier, drives profiled physical weapon selection, and feeds
|
||||
automatic attack power and vulnerability policy. Unknown targets preserve
|
||||
VTank's final Pierce→Bludgeon→Slash→Acid→Lightning→Cold→Fire fallback. Focused
|
||||
evidence after the closeout and named-profile foundation: 146 MossTank tests;
|
||||
isolated Release App build 0 warnings / 0 errors. The connected MT2 combat
|
||||
matrix remains part of the later combined user gate.
|
||||
|
||||
### MT3 — buff, heal and resource parity
|
||||
|
||||
- [x] named macro profiles, buff exclusions/item buffs/top-off foundation;
|
||||
- [x] all three vital threshold tiers and canonical fellowship vitals;
|
||||
- [x] profiled kits/consumables and worn-item mana recharge;
|
||||
- [x] VTank ManaStone/ManaTank acquisition and exact-receipt fill behavior;
|
||||
- [x] conversions, self/item/fellow dispel response, and critical/normal/idle
|
||||
component plus six-category consumable upkeep.
|
||||
|
||||
### MT4 — inventory, craft and transactions
|
||||
|
||||
- [x] AutoStack/AutoCram and the official 757-row VTank craft database;
|
||||
- [x] generalized use/apply/give/move/split/stack/drop transaction API with
|
||||
receipts and busy arbitration;
|
||||
- [x] retail 0x027D salvage and authoritative current-vendor sale paths;
|
||||
- [x] same-input authoritative split crafting, all three split priorities and
|
||||
exact VTank door/lockpick policy.
|
||||
|
||||
### MT5 — looting and extensible rule engine
|
||||
|
||||
- [x] corpse lifecycle/ID waits, exact 30-attempt/200-second open blacklist,
|
||||
60-minute cache, 100-second public ownership, fellow Share Loot and rare-only
|
||||
policy;
|
||||
- [x] ordered first-match raw/projected-property expressions plus Keep,
|
||||
KeepUpTo, Read, Salvage, Sell, ManaStone, ManaTank and User1–User5;
|
||||
- [x] canonical appraisal/pickup/salvage/vendor seams, unknown-scroll fallback,
|
||||
and exact VTank salvage workmanship bands with 40-attempt abandonment;
|
||||
- [x] independent By-char and named native loot profile documents;
|
||||
- [x] exact VTClassic `.utl` v0/v1 importer/exporter, every structured
|
||||
requirement, forward-compatible length blocks, and profile-owned salvage
|
||||
ranges/value modes;
|
||||
- [x] external loot-classifier plugin capability.
|
||||
|
||||
MT5 functional closeout (2026-08-27): the graphical host now exposes corpse
|
||||
discovery, raw item properties, canonical appraisal/pickup, learned-spell
|
||||
membership, fellowship Share Loot, retail salvage (0x027D), and current-vendor
|
||||
sale through additive BCL-only interfaces. MossTank owns all policy and waits
|
||||
for authoritative receipts/object removal; no action reports success at
|
||||
dispatch. The official VTank corpse timers, rare/fellow ownership branches,
|
||||
unknown-scroll difficulty check, mana-stone pairing, salvage-bag workmanship
|
||||
bands, and bugged-bag retry ceiling were ported from the official decompiled
|
||||
source. Focused evidence: 184 MossTank tests, 21 inventory-wire/session tests,
|
||||
134 App automation/item/UI tests, and an isolated Release App build with zero
|
||||
warnings/errors. File interoperability remains an MT9 compatibility tail, not
|
||||
a reason to hold Route/Navigation.
|
||||
|
||||
### MT6 — navigation
|
||||
|
||||
- [x] canonical move/follow/turn/charged-jump/checkpoint host primitives;
|
||||
- [x] circular, linear, once and Target/follow routes, including VTank's
|
||||
endpoint reversal, destructive Once traversal and follow-around-corners;
|
||||
- [x] every decoded nav node (0..9), closed-door/lockpick policy, vendor and
|
||||
repeated NPC use, portal re-entry protection and combat/nav priority;
|
||||
- [x] independent By-char and named native route profiles;
|
||||
- [x] exact `uTank2 NAV 1.2` importer/exporter.
|
||||
|
||||
MT6 functional closeout (2026-08-27): the additive navigation API projects
|
||||
VTank coordinates, live and server-accepted player position, object
|
||||
reacquisition, door state, portal state, and typed movement levels through the
|
||||
one Runtime command interpreter. MossTank owns the exact four route modes and
|
||||
ten node types. Steering ports `fd.cs`'s 4° turn threshold, far 45° and near
|
||||
15° forward cones; checkpoints use `gr.cs`'s accepted-position gate and
|
||||
15-second nudge; Target mode ports `gl.cs` breadcrumb pruning; doors port
|
||||
`b7.cs`'s defaults (disabled, 20 m ID, 4 m open, −50 lockpick threshold).
|
||||
Portal2/UseNPC reacquire exact-name objects near the saved point, NPC use waits
|
||||
for tell/give chat, jumps align to their stored heading before charge/release,
|
||||
and Once removes completed rows exactly like VTank. Evidence: 204 MossTank
|
||||
tests, focused App navigation projection tests, and isolated Release App build
|
||||
with zero warnings/errors. Legacy file interop remains an MT9 compatibility
|
||||
tail and does not hold the expression engine.
|
||||
|
||||
### MT7 — expressions
|
||||
|
||||
- [x] immutable AST, typed values, budgets and diagnostics;
|
||||
- [x] UtilityBelt grammar semantics including lists/dicts/slices;
|
||||
- [x] implement/alias/explicitly disposition the 260-function audit ledger;
|
||||
- [x] VTank option/expression command diagnostics;
|
||||
- [x] parser, evaluator, persistence and capability-security gates.
|
||||
|
||||
### MT8 — meta engine and runtime views
|
||||
|
||||
- [x] complete condition/action vocabulary, nested composition, once-per-entry,
|
||||
call/return and watchdog;
|
||||
- [x] chat capture variables and option access;
|
||||
- [x] plugin-authored runtime views over the retained markup contract;
|
||||
- [x] native meta profile;
|
||||
- [x] exact VTank CondAct `.met` importer/exporter, including recursive rules,
|
||||
embedded NAV and the historical CreateView record quirk.
|
||||
|
||||
### MT9 — fellowship, profiles, commands and polish
|
||||
|
||||
- [x] tell-driven recruitment, waiting-list, status/location commands, and
|
||||
two-minute kick/ban/giveleader/setopen voting over canonical fellowship
|
||||
commands;
|
||||
- [x] helper healing, fellowship corpse permissions and shared target views;
|
||||
- [x] macro-profile foundation: true per-character `By char` documents, named
|
||||
create/copy/clear/select, mine-only filtering, hot loading, atomic manifest-
|
||||
scoped storage, and complete current combat/buff/vitals/monster/item state;
|
||||
- [x] independent navigation/loot/meta profile documents remain with MT5/MT6/MT8;
|
||||
- [x] exact 137-name typed VTank option catalog/defaults and durable
|
||||
`/vt opt setinall` across every indexed named/character macro profile;
|
||||
- [x] all documented `/vt` command names are locally registered and handled;
|
||||
- [x] exact `.nav`, `.met`, and `.utl` dumps/import-export;
|
||||
- [x] privileged debug-operation semantics (`clearlocks`, `clearbusy`,
|
||||
`fakeimp`) use canonical owners and authoritative lifetime cleanup;
|
||||
- [x] first-run guidance, native/VTank profile migration and corrupt-profile
|
||||
recovery with append-only raw-data preservation;
|
||||
- [x] accessibility and scaling polish;
|
||||
- [ ] performance soak, reconnect/lifecycle and multi-client gates.
|
||||
|
||||
## MT1 execution order
|
||||
|
||||
1. Add BCL-only combat records/interfaces with inert defaults.
|
||||
2. Extend Runtime hostile query with exact position/heading snapshots.
|
||||
3. Bind App's automation surface to the canonical action/spell owners.
|
||||
4. Implement/test MossTank's deterministic combat controller.
|
||||
5. Replace the small panel with dashboard/settings markup and generic markup
|
||||
affordances needed by the design.
|
||||
6. Run narrow tests, Release build, broad tests; record exact evidence here.
|
||||
|
||||
## Closeout evidence
|
||||
|
||||
MT1 code-complete 2026-08-26 and user-passed 2026-08-27. The additive BCL-only contract is
|
||||
`CombatAutomation.cs`; older API-v1 implementations retain inert default
|
||||
members. `AppAutomationSurface` borrows the canonical Runtime owners and
|
||||
projects hostile captures, combat state, physical press/release attempts,
|
||||
targeted casting and learned direct offensive spells. MossTank's
|
||||
`CombatController` owns priority, target lock, range/angle/both selection,
|
||||
mode entry, power-bar timing and magic choice. The dashboard/settings markup
|
||||
uses the retained plugin registry; generic markup now supports bound child
|
||||
visibility/enabled state and button colors.
|
||||
|
||||
Automated evidence:
|
||||
|
||||
- focused MossTank: 54 passed / 0 failed;
|
||||
- complete Runtime: 1,854 passed / 0 failed;
|
||||
- repository-owned hermetic Release gate: **15,775 passed / 0 skipped /
|
||||
0 failed across 14 assemblies**;
|
||||
- Release build: 0 warnings / 0 errors;
|
||||
- the original MossTank XML documents parsed successfully before the gate.
|
||||
|
||||
MTUI code-complete 2026-08-27. `PluginPanelDescriptor` and authenticated
|
||||
`PluginUiOwner` carry presentation metadata through Core's transactional
|
||||
plugin lifetime; App mounts the stable panel as a `RetailWindowHandle` and the
|
||||
generic `PluginSidePanel` owns only hide/restore UI. The one-window MossTank
|
||||
shell uses real retained tabs/toggles/sliders. Focused evidence: 18 App/plugin
|
||||
tests and 56 MossTank tests passed; isolated Release App build passed with
|
||||
0 warnings / 0 errors. Broader hermetic evidence: Core 4,720/4,720 and Runtime
|
||||
1,854/1,854 passed; App passed 6,441/6,442 with the sole failure in the
|
||||
unrelated pre-existing landblock recenter assertion
|
||||
`OriginRecenter_RetryPreservesLiveIdentityAndDoesNotRescueReusedGuid`. Its
|
||||
connected visual gate remains open.
|
||||
|
||||
MT3/MT4 resource closeout 2026-08-27: crafting now runs through VTank's three
|
||||
ordered tiers: critical component/consumable recovery, normal component and
|
||||
general profile crafting, then no-target idle component and six-category
|
||||
kit/food stock targets. Same-input recipes wait for both the authoritative
|
||||
split receipt and publication of two distinct stacks before applying. The
|
||||
official `IdleCraftCount_*` underscore names, 4/20/20 component defaults, and
|
||||
2/2/2 kit plus 15/15/15 food targets persist in named/By-char profiles.
|
||||
Self-cast and item dispels port `c8.cs`/`cx.cs`; fellowship Awakener selection
|
||||
ports `af.cs`, including exact training, Arcane Lore, 5 m, spell-3179 and
|
||||
summed-vulnerability-quality gates. The additive shared duration-spell ledger
|
||||
matches VTank's confirmed local/external `LogSpellCast` model and clears on
|
||||
session detach. Evidence: 277/277 MossTank tests, 12/12 focused App automation
|
||||
tests, and isolated Release App build with zero warnings/errors.
|
||||
|
||||
MT7–MT9 checkpoint 2026-08-27: MossTank registers all 260 audited
|
||||
UtilityBelt public expression names over the typed evaluator, and the Meta
|
||||
runtime/editor, dynamic views, embedded routes, command execution and durable
|
||||
variable scopes are integrated. The host now provides an unload-safe generic
|
||||
plugin-command registry; `/vt` follows the same local command route from typed
|
||||
chat, launcher login commands and no-window clients. The exact official
|
||||
four-line command catalog and 137-row typed option database are present;
|
||||
`setinall` rewrites every indexed named/character profile. Run Macro is now a
|
||||
master lifecycle distinct from Enable Combat, and command jumps align before
|
||||
charging. The additive fellowship API projects the canonical retail commands;
|
||||
MossTank owns VTank's tell commands, wait list, spam limit, near-player
|
||||
recruitment, leader transition cleanup and two-minute voting. Evidence at this
|
||||
checkpoint: 261/261 MossTank tests, 18/18 runnable focused App/plugin tests,
|
||||
and isolated Release App build with zero warnings/errors. Four additional
|
||||
GraphicalPluginSession tests could not locate the repository when deliberately
|
||||
run from an isolated OutputPath; this is test-harness path behavior, not a
|
||||
product failure. Connected shelf/UI/fellowship and combined automation gates
|
||||
remain open.
|
||||
|
||||
Legacy-profile checkpoint 2026-08-27: native JSON remains MossTank's durable
|
||||
working format, while every save also emits a genuine VTank compatibility
|
||||
file. `uTank2 NAV 1.2` routes and CondAct `.met` files round-trip exactly;
|
||||
the Meta writer was independently accepted and canonicalized byte-identically
|
||||
by the public `metaf` reference compiler. VTClassic `.utl` v0/v1 now retains
|
||||
length-delimited unknown requirements/blocks, executes all 31 published
|
||||
requirement types (including the DAT-resolved ordered-palette color family),
|
||||
and applies per-material salvage ranges/value modes to the real 0x027D combine
|
||||
planner. Native-only text rules export disabled rather than becoming
|
||||
VTClassic's dangerous empty-requirement match-all. Evidence: 290/290 MossTank
|
||||
tests, 13/13 focused App/plugin tests, and isolated Release App build with zero
|
||||
warnings/errors.
|
||||
|
||||
External-loot checkpoint 2026-08-27: the BCL-only host now owns an unload-safe
|
||||
classifier registry. Classifier ids are namespaced to the registering plugin,
|
||||
all registrations are disposed transactionally with that plugin's session,
|
||||
and exceptions are isolated at the registry boundary. MossTank exposes the
|
||||
available engines in Profiles, persists the selection with the macro profile,
|
||||
and runs Keep/KeepUpTo/Read/Salvage/Sell/User1–User5 decisions through its
|
||||
existing authoritative corpse executor. An unavailable engine never silently
|
||||
changes policy by falling back to VTClassic. Evidence: 2 focused Core registry
|
||||
tests, 55 focused MossTank loot/panel/markup tests, and isolated Release App
|
||||
build with zero warnings/errors.
|
||||
|
||||
Options/debug checkpoint 2026-08-27: the VTank Options page now uses the
|
||||
verbatim four-column control arrangement. Normal automatic rebuff, the
|
||||
separate idle top-off window, Attack→Approach distance navigation, and final
|
||||
Idle Peace fallback were ported from `fz.cs`, `cLogic.cs`, `g8.cs`, `eb.cs`
|
||||
and `cm.cs`; Force Buff and Cancel Force Buff remain distinct actions. The
|
||||
Advanced Options button opens the full ordered 137-setting table. `/vt
|
||||
clearbusy` decrements exactly one Runtime-owned inventory busy reference,
|
||||
`clearlocks` clears only MossTank's transient policy locks, and `fakeimp`
|
||||
records VTank's local 3,000-second Gossamer Flesh debug marker without forging
|
||||
a server cast. External classifiers now receive authoritative `OnLooted` and
|
||||
`OnItemRemoved` lifecycle callbacks after inventory publication. Evidence:
|
||||
298/298 MossTank tests and an isolated Release App build with zero warnings
|
||||
and zero errors.
|
||||
|
||||
Final automated API/options checkpoint 2026-08-27: every one of the 137
|
||||
official advanced-option names has an explicit writable live-policy mapping;
|
||||
the full catalog, official defaults, case-insensitive lookup and durable
|
||||
profile propagation are covered. The Monsters page now exposes the three
|
||||
distinct official cycles for Damage type, Ex. Vuln and PetDmg rather than one
|
||||
shared internal enum. Prismatic remains an ammunition policy while preserving
|
||||
automatic magic-element selection; Fists uses Tusker Fists only while its
|
||||
enchantment is active. `DoJiggle` now ports VTank's PreviousSelection followed
|
||||
by alternating NextPlayer/PreviousPlayer at 131 ms and no longer moves the
|
||||
character. `ShowCollisionDebug` publishes bounded projectile samples through
|
||||
the BCL-only API and renders transient red/green markers in the retained UI.
|
||||
`WhoYouGonnaCall` is intentionally stored but inert, matching the official
|
||||
source's explicit `No Function` disposition.
|
||||
|
||||
The plugin API now projects combat, magic, equipment/items, looting,
|
||||
fellowship, enchantments, navigation, world objects/time, login, network peer
|
||||
state, recovery, projectile diagnostics and selection through canonical
|
||||
Runtime/App owners. Startup peer tags are parsed once by `RuntimeOptions`,
|
||||
portable data paths come from `ApplicationPathSet`, and both graphical and
|
||||
headless plugin hosts load fixtures correctly from isolated output graphs.
|
||||
Latest hermetic evidence: App 6,592 passed / 94 environment-dependent skips;
|
||||
Runtime 1,863/1,863; Core 4,911/4,911; Core.Net 1,042/1,042; Headless
|
||||
171/171; UI abstractions 880/880; MossTank 320/320 — **15,779 passed, zero
|
||||
failed** across the selected automated lanes. The Release App build completed
|
||||
with zero warnings and zero errors. Excluded gates are explicit: manual/live
|
||||
lanes, Linux-only tests on this Windows host, the machine-local stale bake-tool
|
||||
4 PAK test, and one registered pre-existing tower-ascent known failure. The
|
||||
generic shelf, VTank shell, minimization-while-running, reconnect, live combat,
|
||||
multi-client peer expressions, and collision-marker appearance remain owed in
|
||||
the combined connected user gate.
|
||||
|
||||
Connected shelf/shell gate 2026-08-27: the first isolated Release launch found
|
||||
that App's plugin-copy target still assumed each plugin's conventional `bin`
|
||||
directory when a custom `OutputPath` was active. That caused the packaged
|
||||
MossTank DLL/markup to be stale even though the root build outputs were current.
|
||||
Build and publish now resolve both first-party plugin targets through MSBuild's
|
||||
`GetTargetPath`; MossTank markup copies directly from its source. The rebuilt
|
||||
package's MossTank DLL and XML matched their build/source SHA-256 hashes and
|
||||
the boundary regression passed 5/5.
|
||||
|
||||
The next live launch exposed a retained-markup contract mismatch: one field
|
||||
reused an `Action` button binding where `onsubmit` requires `Action<string>`,
|
||||
preventing the complete plugin window from mounting. MossTank now has a typed
|
||||
submit action and its markup contract test validates every interactive binding's
|
||||
delegate shape. A later visual pass also caught three unsupported inline label
|
||||
bindings on Meta; all are now whole-value properties, and the contract rejects
|
||||
future inline interpolation. Focused MossTank evidence is 321/321; isolated
|
||||
Release build `app-release22` is zero-warning/zero-error with exact packaged
|
||||
artifact hashes.
|
||||
|
||||
The connected `app-release22` gate then passed: all nine tabs mounted and were
|
||||
visually inspected; Meta rendered `State: Default`, `N: 0`, and `N2: 0`; the
|
||||
right-edge `MT` shelf button was fully reachable; minimize hid only the window;
|
||||
while hidden the live buff pass advanced from 91/97 to 77/97; restore showed
|
||||
`Stop Macro` and the changed live status; the macro stopped normally. Logs show
|
||||
92 server-confirmed `UseDone err=0` casts and no plugin/UI exception. Shift+Esc
|
||||
completed the full logout presentation and returned to character selection.
|
||||
This supersedes the earlier statement that the shelf, shell, minimization, and
|
||||
basic reconnect/lifecycle presentation were wholly unproven. At that checkpoint,
|
||||
still owed were
|
||||
the accessibility/scale closeout, longer performance/reconnect soak, live
|
||||
hostile combat matrix, two-client peer expressions/fellowship, and collision-
|
||||
marker appearance.
|
||||
|
||||
Accessibility/reconnect/peer checkpoint 2026-08-27: textless and terse controls
|
||||
now carry runtime-bound retained tooltips, and the common window owner clamps
|
||||
plugin panels to the current viewport (including the 800x600 oversize case).
|
||||
Focused evidence is 325/325 MossTank tests, 16/16 retained-UI tooltip/geometry
|
||||
tests, and isolated Release `app-release23` with zero warnings/errors. The live
|
||||
client displayed the Monster Range help text, completed a same-character
|
||||
logout/re-entry, restarted the macro, and completed another 92 server-confirmed
|
||||
casts. Working/private memory stayed approximately 1.59/1.84 GiB across the
|
||||
combined soak rather than climbing with casts or reconnect.
|
||||
|
||||
The local peer API also passed real two-process expressions in both directions:
|
||||
the secondary `+Horan` evaluated
|
||||
`dictgetitem[listgetitem[netclients['mosstank-guard-primary'],0],'Name']` and
|
||||
received `+Acdream`, while the earlier reciprocal gate returned `+Horan` to
|
||||
the primary; both heartbeat documents contained the expected names, tags,
|
||||
vitals and positions.
|
||||
|
||||
That broader gate exposed separate client defect #451. First-chance cdb proof
|
||||
located it in GLFW's Win32 event pump: temporary cross-process input-queue
|
||||
attachment let `GetActiveWindow` return the other acdream process's HWND;
|
||||
GLFW's shared `L"GLFW"` property then returned the other process's private
|
||||
`_GLFWwindow*`, which the caller dereferenced. `app-release24` installs the
|
||||
current-process HWND guard at GLFW's own import slot before `glfwInit`; its four
|
||||
focused tests pass. Two rebuilt graphical clients then entered world, survived
|
||||
100 rapid forced activation switches—the exact old trigger—and remained
|
||||
responsive through a 30-minute combined soak with no native error. Issue #451
|
||||
remains in-progress only until both sessions complete a graceful-exit gate.
|
||||
|
||||
The secondary-owned fellowship gate also passed: `+Acdream` created
|
||||
`mosstankgate`, `+Horan` joined, both canonical rosters contained both members,
|
||||
and the secondary evaluated `getfellowshipcount[]` as `2`.
|
||||
|
||||
Still owed here: the hostile combat matrix and collision-marker appearance.
|
||||
|
||||
Final local validation checkpoint 2026-08-27: the complete Release solution
|
||||
build passed with zero warnings and zero errors. Focused MossTank passed
|
||||
325/325 and the App plugin/API/UI/GLFW set passed 35/35. The conservative
|
||||
Windows hermetic filter passed 15,083 non-network tests; Core.Net then passed
|
||||
1,042/1,042 in its isolated lane, for 16,125 passing selected tests. The first
|
||||
max-parallel combined invocation made Core.Net's timing-sensitive two-percent
|
||||
packet-loss soak exhaust its wall-clock headroom; the same case and complete
|
||||
Core.Net lane passed immediately when isolated. No MossTank, plugin API, plugin
|
||||
UI, Runtime-owner, or #451 guard test failed.
|
||||
|
||||
Live hostile discovery checkpoint 2026-08-27: the first surrounded-monster
|
||||
gate exposed two coupled compatibility defects. Retail's classic `* Lure`
|
||||
vulnerability names were absent from the debuff classifier, so an attack-only
|
||||
profile could misclassify Piercing Lure's "piercing damage" description as a
|
||||
direct attack. The classifier now recognizes all seven classic elemental Lure
|
||||
families (while excluding the distinct Lure Blade item spell), and the attack
|
||||
catalog defensively rejects every host-authored debuff. Target evaluation also
|
||||
now ports official `dz::a`'s previous-target tie-break after priority and manual
|
||||
TargetLock: a valid chosen monster remains selected while the character turns,
|
||||
instead of angle rescans alternating between surrounding monsters. The new
|
||||
Lure/attack and target-stability regressions bring the focused MossTank lane to
|
||||
337/337. Connected re-test remains part of the hostile combat gate.
|
||||
|
||||
## Requirement-level completion audit (2026-08-27)
|
||||
|
||||
Completion is deliberately **not** claimed while live evidence remains missing.
|
||||
The authoritative requirement/evidence map is:
|
||||
|
||||
| Objective requirement | Current evidence | Audit result |
|
||||
| --- | --- | --- |
|
||||
| Functionally complete VTank behavior | MT2–MT9 implementation ledger; 337 MossTank behavior/format/expression tests; connected MT1 autocombat acceptance | Proven for implemented policy and formats; the combined hostile physical/magic matrix remains live-unproven |
|
||||
| Visually verbatim nine-tab VTank surface | `mosstank.xml` contains the exact Options, Profiles, Vitals, Monsters, Items, Consumables, Buffs, Route, Meta order; all nine tabs mounted in `app-release22` | Proven for shell/tab presence and first comparison; projectile debug-marker appearance remains live-unproven |
|
||||
| Every visible control has real behavior | 190 interactive controls expose 202 bindings (191 unique); `MossTankMarkupContractTests` resolves every binding, verifies delegate shape, and rejects handlerless controls; 137/137 advanced options have explicit writable mappings | Proven statically and by focused controller tests. `WhoYouGonnaCall` intentionally stores its value but performs no action because the official VTank source labels it `No Function` |
|
||||
| Generic plugin sidepanel; minimizing must not stop plugins | retained `PluginSidePanel`/window-manager tests plus connected hide/restore gate where the hidden buff pass advanced from 91/97 to 77/97 | Proven |
|
||||
| Modern acdream plugin APIs over canonical owners | additive BCL-only combat, magic, equipment, item, loot, fellowship, enchantment, navigation, object, world-time, login, network, recovery, projectile, selection, storage, command and classifier contracts; 35 focused App/API/UI tests and 16,125 selected Release tests | Proven for the graphical live host; older/no-window implementations explicitly report unavailable and never fabricate success |
|
||||
| UtilityBelt-compatible expression superset | immutable evaluator tests; all 260 audited public names registered; host-action, object, fellowship, time, login/network, UI, persistence, collection and meta tests | Proven by catalog and semantic family tests; bidirectional two-client network expressions passed live |
|
||||
| Lifecycle, reconnect, multi-client stability | same-character reconnect and hidden execution passed; peer expressions and two-member fellowship passed; #451 exact trigger survived 100 focus switches and a 30-minute dual-client soak | Proven through soak; #451 cannot close until both current sessions exit gracefully |
|
||||
|
||||
Open completion gates: (1) hostile physical and offensive-magic behavior against
|
||||
a live target at valid configured range; (2) visible green/red projectile
|
||||
collision markers with `ShowCollisionDebug`; (3) graceful exit of both current
|
||||
soak clients with no native or managed failure. These are evidence gaps, not
|
||||
redefined-away acceptance criteria.
|
||||
447
docs/research/2026-08-26-mosstank-vtank-utilitybelt-research.md
Normal file
447
docs/research/2026-08-26-mosstank-vtank-utilitybelt-research.md
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
# MossTank research: Virindi Tank parity and UtilityBelt expressions
|
||||
|
||||
Date: 2026-08-26
|
||||
|
||||
This report is the requirements baseline for turning MossTank from the small
|
||||
self-buffing sample into acdream's full automation plugin. The product target is
|
||||
deliberately broad: **all Virindi Tank functionality**, with UtilityBelt's more
|
||||
capable expression dialect as the scripting baseline. File compatibility is a
|
||||
separate decision; behavioral capability is not.
|
||||
|
||||
## 1. Evidence and limits
|
||||
|
||||
The following primary VTank pages were read and cross-checked (the live wiki
|
||||
and its indexed historical revisions were both used where a mirror was
|
||||
temporarily unavailable):
|
||||
|
||||
- `http://virindi.net/wiki/index.php/Virindi_Tank`
|
||||
- `http://virindi.net/wiki/index.php/Virindi_Tank_Standard_Options`
|
||||
- `http://virindi.net/wiki/index.php/Virindi_Tank_Advanced_Options`
|
||||
- `http://virindi.net/wiki/index.php/Virindi_Tank_Commands`
|
||||
- `http://virindi.net/wiki/index.php/Virindi_Tank_Meta_System`
|
||||
- `http://virindi.net/wiki/index.php/Meta_Expressions`
|
||||
- `https://utilitybelt.gitlab.io/docs/expressions/`
|
||||
|
||||
The 2026-08-27 MT2 follow-up also verified the exact documented distinctions
|
||||
that drive the combat scheduler:
|
||||
|
||||
- A+R requires `MinimumRingTargets` inside Ring Range; R without A rings with
|
||||
any configured target inside range and falls back to standard war outside;
|
||||
- `UseArcs` prefers an arc over a bolt only at/above `ArcRange`;
|
||||
- `Void Basic`, `Drain Auto`, and `Harm` are distinct Monsters damage choices;
|
||||
- `GhostMonsterSpellAttemptCount` counts spell attempts which never start,
|
||||
while `BlacklistMonsterAttemptCount` counts successful attacks which miss;
|
||||
- the health-tracker ghost detector is independent and applies to melee,
|
||||
missile, and magic.
|
||||
|
||||
Sources: the official `Virindi_Tank_Standard_Options`,
|
||||
`Virindi_Tank_Advanced_Options`, `Virindi_Tank_FAQ`, `Options_List`, and
|
||||
`Virindi_Tank_Changelog` pages listed above.
|
||||
|
||||
For UtilityBelt, documentation was checked against the primary source rather
|
||||
than relying on the generated web page alone. The inspected repository was
|
||||
`https://gitlab.com/utilitybelt/utilitybelt`, commit
|
||||
`5fe9825a82f38047737768fd92c61dd47d88e467` (2026-03-05). The grammar is
|
||||
`UtilityBelt/Lib/Expressions/MetaExpressions.g4`; every method carrying an
|
||||
`ExpressionMethod` attribute was enumerated. The source is MIT licensed.
|
||||
|
||||
This report extends, rather than replaces,
|
||||
`2026-07-29-vtank-plugin-automation-requirements.md`. That earlier report
|
||||
already decoded VTank's `.met`, `.nav`, and `.utl` structures from primary
|
||||
sources and remains the format reference.
|
||||
|
||||
## 2. Complete VTank capability map
|
||||
|
||||
### 2.1 Combat
|
||||
|
||||
VTank is a priority-driven combat controller, not merely an auto-attack loop.
|
||||
Its supported combat family includes:
|
||||
|
||||
- melee, missile, mage, hybrid, two-handed, Void and Summoning characters;
|
||||
- Life harm/martyr attacks, grenades, lenses, cast-on-strike weapons, streaks;
|
||||
- automatic damage/weapon choice and monster-specific weapon, offhand and pet
|
||||
element overrides;
|
||||
- monster rules with `DEFAULT` plus ordered first-match expressions;
|
||||
- per-rule priority from ignore (`-1`) through `4`, attack/debuff flags,
|
||||
damage type, attack height, ring/streak choices, and void curses;
|
||||
- target selection by distance, angular deviation, or the hybrid method using
|
||||
angle inside a configurable cutoff and distance outside it;
|
||||
- target lock, blacklist/retry behavior, and ghost-target retirement after
|
||||
failed casts or missing health updates;
|
||||
- debuff scheduling by one target, priority group, or all targets before
|
||||
attack; spell-level versus skill-based debuff choice and reapply windows;
|
||||
- automatic ring use by nearby-target density and arc/bolt choice by range;
|
||||
- melee high/middle/low attacks, automatic or explicit power, Recklessness;
|
||||
- pet density, element, refill and test behavior.
|
||||
|
||||
VTank's macro scheduler checks multiple action lists in priority order. Combat
|
||||
therefore cannot be implemented as an isolated timer: healing, buffing,
|
||||
navigation, looting, fellowship assistance and combat all need one arbiter.
|
||||
|
||||
### 2.2 Buffing and vitals
|
||||
|
||||
- automatic trained attribute/skill buffs, protections, banes, auras,
|
||||
regeneration and configured extra buffs;
|
||||
- protection/bane profile sets, exclusions, level/tier selection, signed
|
||||
skill-over-difficulty thresholds, force buff and idle top-off;
|
||||
- time-remaining rebuff and persisted item-buff duration knowledge;
|
||||
- combat, idle and fellowship-helper vital thresholds;
|
||||
- kits, vital transfers, post-switch recharge behavior and special healing
|
||||
items;
|
||||
- self-dispel in response to high-level vulnerabilities.
|
||||
|
||||
MossTank's current buff engine already owns the first useful subset: known
|
||||
self buffs, tier/difficulty choice, in-force enchantment timing, force buff,
|
||||
and stamina/mana upkeep. It remains plugin policy over host primitives.
|
||||
|
||||
### 2.3 Inventory and crafting
|
||||
|
||||
- AutoStack and AutoCram;
|
||||
- pea splitting and priority rules;
|
||||
- crafting of kits, foods, arrowheads and special ammunition;
|
||||
- mana-stone acquisition, filling and application to equipped items;
|
||||
- lockpick selection and use;
|
||||
- component, consumable, tool and ammunition upkeep.
|
||||
|
||||
### 2.4 Looting
|
||||
|
||||
- corpse approach/open/retry/timeout/blacklist;
|
||||
- all/fellow/rare loot modes and priority boosts;
|
||||
- appraisal/ID wait, unknown-scroll reading and salvage combining;
|
||||
- a loot-plugin seam, with VTClassic as the canonical ordered, first-match
|
||||
rule engine over raw and computed item properties;
|
||||
- actions including no-loot, keep, keep-up-to, salvage, sell, read and custom
|
||||
user actions.
|
||||
|
||||
The host must expose object property bags, appraisal completion and
|
||||
transaction primitives. Rule ordering and loot-profile policy belong in
|
||||
MossTank.
|
||||
|
||||
### 2.5 Navigation
|
||||
|
||||
- circular, linear, once/runback and follow routes;
|
||||
- points, portals, recalls, pauses, chat, vendor, repeated NPC talk/use,
|
||||
server-confirmed checkpoints and charged/shift/strafe jumps;
|
||||
- closest-entry, reversal, arrival/off-course ranges, door use and
|
||||
follow-around-corners;
|
||||
- combat/nav priority interaction.
|
||||
|
||||
Route storage belongs to the plugin. The host owes move-to, follow, turn,
|
||||
jump, use and authoritative-arrival primitives.
|
||||
|
||||
Direct inspection of the official assembly on 2026-08-27 pinned the route
|
||||
contract more tightly:
|
||||
|
||||
- `eNavType` is Circular, Linear, Target and Once; Once destructively removes
|
||||
its first completed row and Linear deliberately visits each endpoint once
|
||||
while flipping direction;
|
||||
- `eWaypointType` assigns Point/Portal/Recall/Pause/ChatCommand/OpenVendor/
|
||||
Portal2/UseNPC/Checkpoint/Jump to numeric ids 0..9;
|
||||
- `fd.cs` turns outside 4°, moves while turning only within 45° beyond 3 m or
|
||||
15° inside 3 m, and stops at `NavCloseStopRange` (default 2 m);
|
||||
- `gr.cs` compares the checkpoint against the last server position rather
|
||||
than client prediction and nudges forward after 15 seconds without an
|
||||
acknowledgement;
|
||||
- `gl.cs` records the followed player's path by approximately 9.6 cm and
|
||||
drops old breadcrumbs when the follower comes within 2.4 m of a later path
|
||||
segment, preserving follow-around-corners;
|
||||
- `e9.cs` and `fa.cs` reacquire exact-name/class objects within 2.5 m of the
|
||||
stored position. Portal2 retries when portal exit remains within 15 m of
|
||||
its origin; UseNPC repeats until the named NPC tells or gives to the player;
|
||||
- `b7.cs` is a rule independent of the route node list. `OpenDoors` defaults
|
||||
false; it IDs doors at 20 m, opens at 4 m, and accepts a lock when Lockpick
|
||||
is at least `difficulty - 50` using an owned lockpick.
|
||||
|
||||
These are plugin policies over additive canonical projections, not a second
|
||||
movement model. The host applies semantic movement intent through Runtime's
|
||||
existing command interpreter and supplies the accepted server position needed
|
||||
only by Checkpoint.
|
||||
|
||||
### 2.6 Fellowship and social automation
|
||||
|
||||
- tell-driven recruitment and waiting lists;
|
||||
- fellowship leader/member/state queries and leader replacement voting;
|
||||
- fellowship healing, corpse permissions and coordinated target/debuff policy;
|
||||
- multi-client composition through chat rather than a privileged macro API.
|
||||
|
||||
### External loot-classifier seam
|
||||
|
||||
VTank loads one `LootPluginBase`, asks `DoesPotentialItemNeedID`, and then
|
||||
calls `GetLootDecision(GameItemInfo)`. Its public result vocabulary is
|
||||
NoLoot, Keep, Salvage, Sell, Read, User1–User5 and KeepUpTo with `Data1` as the
|
||||
limit. MossTank modernizes discovery into a host-owned classifier registry:
|
||||
plugins register a namespaced classifier for their own lifetime, while
|
||||
MossTank remains the corpse/appraisal/pickup/action executor. The selected
|
||||
engine is durable policy; if it unloads, MossTank returns no classifier match
|
||||
instead of silently applying the built-in profile.
|
||||
|
||||
Direct inspection of the official `hv.cs` also shows that a custom loot
|
||||
plugin's per-item action is retained only after the item enters owned
|
||||
inventory and is removed when the item leaves. The modern registry therefore
|
||||
has matching `OnLooted` and `OnItemRemoved` callbacks. MossTank invokes them
|
||||
only from authoritative inventory publication/removal, never when pickup is
|
||||
merely dispatched.
|
||||
|
||||
### Options-page scheduler findings
|
||||
|
||||
The official Options controls are not merely presentation aliases:
|
||||
|
||||
- `fz.cs` runs the ordinary `RebuffTimeRemainingSeconds` rule before combat;
|
||||
- `cLogic.cs` runs a second `IdleBuffTopoffTimeSeconds` pass only behind
|
||||
`IdleBuffTopoff`, after attack/loot work has gone idle;
|
||||
- the PRETARGETAPPROACH `g8` rule navigates only between `AttackDistance` and
|
||||
`ApproachDistance`, and requires both combat and navigation to be enabled;
|
||||
- `cm.cs` changes to Peace only as the final no-target/no-work fallback.
|
||||
|
||||
The UI displays AC-distance settings multiplied by 240. MossTank stores metres
|
||||
in its typed controllers and converts only at the VTank option boundary.
|
||||
|
||||
### 2.7 Meta state machine
|
||||
|
||||
- named states beginning at `Default`;
|
||||
- state-local rules, each firing once per state entry;
|
||||
- nested conditions (`All`, `Any`, `Not`) and conditions for chat regex,
|
||||
inventory, timers, nav state, death, vendors, monsters, buffs, coordinates,
|
||||
portals, burden, route distance, expressions and captured chat groups;
|
||||
- actions for state transition, chat, grouped actions, embedded navigation,
|
||||
call/return stack, expression execution, expression-derived chat, watchdogs,
|
||||
option read/write and runtime-created views;
|
||||
- a roughly 293 ms decision cadence plus evaluation when the macro asks for
|
||||
its next action.
|
||||
|
||||
### 2.8 Profiles, commands and companion behavior
|
||||
|
||||
- independent settings, navigation, loot and meta profiles; global and
|
||||
per-character variants; hot loading and automatic persistence;
|
||||
- command parity for macro state, options, buffing, meta, item testing,
|
||||
property dumps, monster/spell diagnostics, route editing, attack power and
|
||||
debug output;
|
||||
- extensibility equivalent to VTClassic, VI2, item tools, follower/status HUD,
|
||||
alerts and cross-character inventory. Some belong as separate acdream
|
||||
plugins, but MossTank's API must permit them without privileged host code.
|
||||
|
||||
## 3. Expression language target
|
||||
|
||||
### 3.1 Why UtilityBelt is the baseline
|
||||
|
||||
VTank expressions are enough to power classic metas, but UtilityBelt preserves
|
||||
the familiar syntax while adding typed lists and dictionaries, slicing,
|
||||
higher-order collection functions, broader object queries and more action
|
||||
primitives. MossTank should implement the UtilityBelt-compatible semantic
|
||||
superset and offer a VTank compatibility mode for old expressions.
|
||||
|
||||
### 3.2 Grammar and evaluation semantics
|
||||
|
||||
The audited UtilityBelt grammar supports:
|
||||
|
||||
- multiple `;`-separated statements, returning the final result;
|
||||
- session (`$`), persistent (`@`) and global (`&`) variables;
|
||||
- decimal and hexadecimal numbers, booleans and two string forms;
|
||||
- function calls using `name[...]`;
|
||||
- typed values: number, string, boolean, list, dictionary, coordinate, world
|
||||
object, stopwatch and UI control;
|
||||
- list/string/dictionary indexing, slices and negative indices;
|
||||
- complement, shifts, bitwise operators, exponentiation, arithmetic, regex
|
||||
match (`#`), comparison, short-circuit `&&` and `||`;
|
||||
- registered function metadata, arity/type validation and documented return
|
||||
types;
|
||||
- collection creation/mutation/copying plus map/filter/reduce/sort/range.
|
||||
|
||||
Implementation requirements follow directly: parse into an immutable AST;
|
||||
compile or interpret without ambient reflection; use explicit value kinds;
|
||||
short-circuit logical nodes; attach cancellation and an instruction budget;
|
||||
make all world/action functions capabilities supplied by the MossTank engine;
|
||||
and serialize only persistent/global variable stores.
|
||||
|
||||
### 3.3 Audited UtilityBelt function catalog (260 declarations)
|
||||
|
||||
The declaration count includes aliases/overloads. Grouped by capability, the
|
||||
public names are:
|
||||
|
||||
- **language/conversion/math:** `abs`, `acos`, `asin`, `atan`, `atan2`,
|
||||
`ceiling`, `chr`, `cnumber`, `cos`, `cosh`, `cstr`, `cstrf`, `floor`,
|
||||
`hexstr`, `iif`, `ifthen`, `isfalse`, `istrue`, `lumavg`, `lumtotal`,
|
||||
`ord`, `randint`, `round`, `sin`, `sinh`, `sqrt`, `strlen`, `tan`, `tanh`,
|
||||
`tostring`, `vitae`;
|
||||
- **variables:** `getvar`, `setvar`, `testvar`, `touchvar`, `clearvar`,
|
||||
`clearallvars` and the corresponding `pvar` and `gvar` families;
|
||||
- **execution/chat:** `exec`, `delayexec`, `clearexec`, `echo`, `chatbox`,
|
||||
`chatboxpaste`;
|
||||
- **lists:** `listcreate`, `listadd`, `listinsert`, `listremove`,
|
||||
`listremoveat`, `listgetitem`, `listcontains`, `listindexof`,
|
||||
`listlastindexof`, `listcopy`, `listreverse`, `listpop`, `listcount`,
|
||||
`listclear`, `listfilter`, `listmap`, `listreduce`, `listsort`,
|
||||
`listfromrange`;
|
||||
- **dictionaries:** `dictcreate`, `dictgetitem`, `dictadditem`, `dicthaskey`,
|
||||
`dictremovekey`, `dictkeys`, `dictvalues`, `dictsize`, `dictclear`,
|
||||
`dictcopy`;
|
||||
- **time/location:** `getdatetimelocal`, `getdatetimeutc`, `getunixtime`,
|
||||
`getworldname`, `getplayercoordinates`, `getplayerlandblock`,
|
||||
`getplayerlandcell`, coordinate parse/get/distance/string functions,
|
||||
stopwatch functions, and the eleven `getgame*`/day/night functions;
|
||||
- **character:** raw typed property reads, base/buffed skills, training level,
|
||||
base/current/buffed-max vitals, base/buffed attributes, burden, free slots,
|
||||
cooldown expiration, account hash and character index;
|
||||
- **spells/components:** `getknownspells`, `getisspellknown`,
|
||||
`getcancastspell_buff`, `getcancastspell_hunt`, `getspellexpiration`,
|
||||
`getspellexpirationbyname`, `spelldata`, `spellname`, `componentdata`,
|
||||
`componentname`;
|
||||
- **world objects:** validity/data/ID-time, raw typed properties, identity,
|
||||
health/vitals, spells, coordinates, selection/player/open-container, door
|
||||
state, nearest monster/door/by class/name/template, and `wobjectfindall*`
|
||||
variants over world, landscape, inventory and containers;
|
||||
- **actions:** select, use, apply, give, equip wand, cast, cast-on-target,
|
||||
move, split and drop;
|
||||
- **combat/movement:** combat state get/set, busy state, equipped weapon type,
|
||||
heading/get-heading-to, motion get/set/clear and portal-state query;
|
||||
- **inventory/loot/salvage:** counts by name/regex/type, give-profile,
|
||||
unopened corpse queries, `ustadd`, `ustopen`, `ustsalvage`;
|
||||
- **fellowship/quest/XP:** thirteen fellowship queries, quest state/progress,
|
||||
seven XP-meter operations;
|
||||
- **UI/options/network/login:** status HUD, view/control get/set/visibility,
|
||||
VT option/meta get/set, macro status, UtilityBelt options, regex capture,
|
||||
network clients and next-login control.
|
||||
|
||||
This catalog is a compatibility test ledger. Each name must eventually be
|
||||
implemented, deliberately aliased, or marked unsupported with a documented
|
||||
reason; silent omission is not acceptable.
|
||||
|
||||
## 4. acdream mapping after the 2026-08 campaigns
|
||||
|
||||
The 2026-07 report's architecture remains correct, but its gap table is stale.
|
||||
The Runtime now owns inventory transactions, selection, combat mode and power
|
||||
state, casting, fellowship, allegiance, vendor and secure-trade state. MossTank
|
||||
already consumes a small BCL-only `IAutomationSurface` for vitals, skills,
|
||||
spells, enchantments, casting and local chat.
|
||||
|
||||
The gaps relevant to the first autocombat milestone are narrower:
|
||||
|
||||
| Need | Canonical owner today | Plugin gap |
|
||||
|---|---|---|
|
||||
| hostile query and live position | `RuntimeEntityDirectory` + `ClientObjectTable` | no target snapshot/query |
|
||||
| health and selected target | `RuntimeActionState` | no combat view |
|
||||
| melee/missile charge/release | `RuntimeCombatAttackState` | no command surface |
|
||||
| combat-mode transition | `RuntimeCombatModeState` | no command surface |
|
||||
| known offensive spells | `Spellbook` | only self buffs are enumerated |
|
||||
| target-specific cast | selection + `RuntimeSpellCastState` | possible only by composing two old services |
|
||||
| polished plugin controls | retained `IUiRegistry` markup | markup lacks bound visibility/enabled/style affordances |
|
||||
|
||||
The first implementation therefore does not need a second runtime bridge or a
|
||||
second object model. It needs a narrow additive projection of those exact
|
||||
owners.
|
||||
|
||||
## 5. Decisions for MossTank
|
||||
|
||||
1. MossTank remains an ordinary plugin. It never references App, Runtime,
|
||||
rendering, networking or DAT assemblies.
|
||||
2. The host API exposes snapshots and attempt-style commands; MossTank owns
|
||||
target scoring, rule ordering, spell/attack choice and timing.
|
||||
3. The first combat milestone supports melee, missile and direct offensive
|
||||
magic, target lock, range/angle/hybrid scoring, priority rules, attack
|
||||
height and power. Navigation, weapon swapping, debuffs, vulnerabilities,
|
||||
pets and monster expressions are later combat slices, not hidden stubs.
|
||||
4. Expressions will use UtilityBelt's richer typed semantics. Compatibility
|
||||
is defined by parser/evaluator tests and the audited function ledger, not by
|
||||
copying UtilityBelt implementation code.
|
||||
5. Native MossTank profiles will be versioned JSON. Importers for VTank files
|
||||
can be added later without constraining the internal model.
|
||||
6. The UI uses acdream's retained plugin UI contract. Missing generic controls
|
||||
should improve that contract/markup rather than making MossTank depend on a
|
||||
presentation implementation.
|
||||
|
||||
### 5.1 Follow-up implementation findings (2026-08-27)
|
||||
|
||||
VTank's official `e0.d(name)` first looks in `MonsterDamageOverrides`, then
|
||||
maps the monster to `SpeciesDamages`; `ga.g(...)` walks that ordered preference
|
||||
list and finally tries the unlisted elements 0..6. acdream already projects
|
||||
retail `CreatureType` as `PluginCombatTarget.SpeciesId`, so MossTank can bypass
|
||||
VTank's name-to-species compatibility table while preserving the same ordered
|
||||
damage result. Exact name overrides still win. The imported official feed has
|
||||
59 overrides and 103 species rows.
|
||||
|
||||
### 5.2 Official inventory and loot findings (2026-08-27)
|
||||
|
||||
The official VTank assembly and its GameInfoDB were inspected rather than
|
||||
inferring behavior from the UI labels:
|
||||
|
||||
- `el.cs`/`cf.cs` supply 757 exact craft rows; prerequisites are recursive and
|
||||
share the canonical item-use transaction;
|
||||
- `fo.cs` identifies every corpse before selection, parses `Killed by ...`,
|
||||
admits the player's own corpse immediately, admits a Share Loot fellow
|
||||
immediately, waits 100 seconds for a non-sharing fellow or unrelated public
|
||||
corpse, and never crosses ownership on another player's rare-generating
|
||||
corpse;
|
||||
- the default corpse-open retry contract is 30 attempts, then a 200-second
|
||||
blacklist; completed corpse records expire after 60 minutes;
|
||||
- `hv.cs` applies the ordered loot rule first, then falls back to readable
|
||||
unknown scrolls and automatic mana-stone/tank acquisition;
|
||||
- `dy.cs` proves that ManaTank is a mana-bearing donor target, not a worn-item
|
||||
recharge consumable. A ManaStone is used on that donor when its mana is at
|
||||
least `ManaTankMinimumMana` (default 1000);
|
||||
- `c7.cs` combines only same-material salvage bags in exact workmanship bands
|
||||
`<7`, `7–<9`, `9–<10`, and exactly `10`; one bugged source is abandoned after
|
||||
40 failed combine attempts;
|
||||
- `gmSalvageUI::Salvage` calls
|
||||
`CM_Inventory::Event_CreateTinkeringTool`: game action `0x027D`, tool id,
|
||||
then `PackableList<unsigned long>` (count plus ordered item ids). This same
|
||||
operation handles ordinary source salvage and salvage-bag combination.
|
||||
|
||||
The native implementation keeps settings, loot, route, and meta documents
|
||||
independent, matching VTank's profile model while using versioned JSON as the
|
||||
working format. It also emits and imports exact compatibility files: `uTank2
|
||||
NAV 1.2`, CondAct `.met`, and VTClassic `UTL 1` (plus legacy UTL v0 reads).
|
||||
The UTL port preserves unknown length-delimited requirement and extra-block
|
||||
payloads, executes the complete 31-type requirement vocabulary, and carries
|
||||
the `SalvageCombine` material ranges/value modes into the live combine planner.
|
||||
VTClassic's color rules use the original ordered ObjDesc subpalettes and the
|
||||
original sample index `length*16 + offset*32 + 8`, resolved from portal DAT
|
||||
palette colors rather than approximated from icon pixels.
|
||||
|
||||
Profiles are implemented over manifest-scoped JSON with exact VTank files as
|
||||
an interchange/export layer: `By char` hashes the canonical character name into a distinct
|
||||
document, named profiles are explicit shared snapshots, and the index records
|
||||
owner plus per-character active selection. Create/copy/clear/select all hot-
|
||||
load the same mutable policy owners already borrowed by the controllers. The
|
||||
generic retained markup contract gained editable fields and retail dropdown
|
||||
menus for this editor; later Monsters, Loot, Route, and Meta editors reuse the
|
||||
same controls.
|
||||
|
||||
## 6. Acceptance boundary for “autocombat ported”
|
||||
|
||||
The milestone is complete when an in-world MossTank panel can enable/disable
|
||||
combat, periodically capture canonical hostile targets, preserve a valid
|
||||
locked target, choose a target by configured range/angle/hybrid policy and
|
||||
priority, enter the equipped default combat mode, drive retail's physical
|
||||
press/charge/release state machine at configured height/power, or cast the
|
||||
best usable learned direct offensive spell in magic mode. It must stop cleanly
|
||||
on session loss, invalid/dead/out-of-range targets, and user disable; it must
|
||||
not duplicate Runtime state or issue overlapping requests.
|
||||
|
||||
Full VTank parity is the campaign target. This acceptance boundary is only the
|
||||
first executable slice requested for this work session.
|
||||
|
||||
## 7. Official binary combat-item findings (2026-08-27)
|
||||
|
||||
The official `vt.tar.gz` update was decompiled for behavior research and the
|
||||
live GameInfoDB v9 feed was read directly. The decisive implementations are
|
||||
`dz.cs` (debuff source selection), `ga.cs` (item classification), `gs.cs`
|
||||
(caster-item confirmation), `bo.cs` (physical/proc confirmation), and `hi.cs`
|
||||
(attack-power policy).
|
||||
|
||||
- `dz.b.CompareTo` ranks spell quality then source skill/spellcraft for
|
||||
`SpellLevel`, reverses those two for `Skill`, and gives a learned spell the
|
||||
final tie. Spell quality is normally spell difficulty.
|
||||
- Caster items activate on the target. Melee/missile proc weapons are equipped
|
||||
and repeatedly attack at power 0/1 respectively. Neither path counts as
|
||||
applied until color-7 combat chat matches `^You cast (.*) on .*$`.
|
||||
- Grenades are missile-class items with CombatUse 0 and `Phial` in the name;
|
||||
the official database contains exactly 72 names across eight material tiers,
|
||||
with Alchemy requirements 75..400 and spellcraft 100..520.
|
||||
- Normal physical attack power is not a smooth heuristic. `hi.cs` emits the
|
||||
exact 0, .2, .49, .5 or 1 values for slash/pierce hybrid arrangements, then
|
||||
clamps to .11..90 when trained Recklessness is enabled.
|
||||
|
||||
These findings require three host facts VTank formerly obtained through
|
||||
Decal: retained per-item appraisal SpellBooks, ordered transcript capture, and
|
||||
an explicit combat-mode command. They are additive BCL plugin contracts;
|
||||
MossTank retains all source-choice and retry policy.
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
# MossTank autocombat design
|
||||
|
||||
Date: 2026-08-26
|
||||
|
||||
## Outcome
|
||||
|
||||
Ship the first VTank-class MossTank milestone: a polished in-client controller
|
||||
that performs safe automatic melee, missile or direct-spell combat while all
|
||||
policy remains in the plugin and all authoritative state/actions remain in
|
||||
Runtime.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
Runtime canonical owners
|
||||
entity directory + object table + selection + combat + spellbook
|
||||
|
|
||||
v
|
||||
AppAutomationSurface (borrowed projection, no ownership)
|
||||
PluginCombatTarget[] + PluginCombatSnapshot + attempt commands
|
||||
|
|
||||
v
|
||||
MossTank CombatController (policy/state machine)
|
||||
scan -> score/lock -> mode -> charge/cast -> wait -> repeat
|
||||
|
|
||||
v
|
||||
retained plugin panel (bindings only)
|
||||
```
|
||||
|
||||
`AcDream.Plugin.Abstractions` stays BCL-only. New interfaces use records,
|
||||
enums, arrays/lists and primitives only. Existing interfaces gain default
|
||||
members where needed so API v1 plugins remain loadable.
|
||||
|
||||
## API additions
|
||||
|
||||
- `PluginCombatTarget`: id, name, weenie class, distance, signed relative
|
||||
angle, health-known and health fraction.
|
||||
- `PluginCombatSnapshot`: selected id, mode, charge/request state, power and
|
||||
server-pending state.
|
||||
- `ICombatAutomation`: immutable hostile snapshot plus explicit mode,
|
||||
begin/release/abort attempts.
|
||||
- `ISpellCatalog.KnownAttackSpells`: learned, direct offensive spells.
|
||||
- `IAutomationSurface.Combat`: the combat group.
|
||||
|
||||
Attempt results distinguish unavailable, invalid target, wrong mode, busy,
|
||||
transition started and sent/started. This avoids `bool` APIs whose `false`
|
||||
cannot tell a plugin whether to wait, retry, reselect or stop.
|
||||
|
||||
## Target snapshots
|
||||
|
||||
`RuntimeHostileTargetQuery` is extended with a snapshot capture method. It
|
||||
borrows the same entity directory and `ClientObjectTable` used by gameplay,
|
||||
filters with the same `CombatTargetPolicy`, and computes distance and relative
|
||||
heading using retail's `MoveToMath` helpers. Hidden, no-draw, dead and
|
||||
cell-less entities are excluded. The App surface refreshes at bounded cadence
|
||||
and publishes one immutable list reference; retained UI reads do not scan the
|
||||
world or allocate.
|
||||
|
||||
## Combat controller
|
||||
|
||||
States:
|
||||
|
||||
1. `Off`: no automation command may be emitted.
|
||||
2. `Acquire`: keep a valid lock or choose the lowest score.
|
||||
3. `Mode`: request the equipped default combat mode and wait for confirmation.
|
||||
4. `PhysicalCharge`: select, set power, press height, then wait until the
|
||||
canonical meter reaches desired power before release.
|
||||
5. `MagicCast`: select and cast the chosen known offensive spell.
|
||||
6. `Wait`: wait while physical server response, repeat state or magic busy is
|
||||
active, then reacquire/repeat.
|
||||
|
||||
Target scoring first applies ordered rules (initial slice supplies a default
|
||||
priority and an ignore-name list), then applies the configured selection
|
||||
method. Target lock keeps the current target while it remains admissible.
|
||||
|
||||
The controller never fabricates success. Health and disappearance retire a
|
||||
target; timeouts return to `Acquire`; session loss transitions to `Off` and
|
||||
aborts an in-progress physical build.
|
||||
|
||||
## UI
|
||||
|
||||
The main window becomes a dashboard rather than a single force-buff button:
|
||||
macro toggle, current target/mode, state, vitals, combat settings, buff action
|
||||
and settings navigation. Generic markup gains bound child visibility/enabled
|
||||
and color/style attributes so active controls read as active without App types
|
||||
leaking into the plugin.
|
||||
|
||||
## Verification
|
||||
|
||||
- pure controller tests for selection policies, lock, mode transition,
|
||||
charge/release, busy suppression, magic choice, disable and session loss;
|
||||
- Runtime query tests for filter, range, distance, relative angle and health;
|
||||
- App projection tests for caching and command mapping where practical;
|
||||
- markup parser tests for new generic bindings;
|
||||
- MossTank, Runtime, App and complete Release solution gates.
|
||||
Loading…
Add table
Add a link
Reference in a new issue