acdream/docs/superpowers/specs/2026-08-26-mosstank-autocombat-design.md

3.9 KiB

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

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.