# 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.