Warcraft III Group Attack And Chase Gaps
This document tracks the remaining gaps against the group Attack, chase, and
combat-movement specification. It describes the implementation in
skills/s_attack.c, skills/s_move.c, and the order queue as it stands after
the propulsion-window, cooldown-deadline, and active-target identity fixes. It
does not claim that
unverified behavior is a retail mismatch; items marked as coverage gaps need a
representative simulation test before changing production code.
Implemented Baseline
Direct Attack is fanned out to controllable selected units through
attack_menu_selecttarget(). Each unit owns an independent Attack order and
resolves the current compatible Attack 1/Attack 2 profile at use sites. The
explicit goalentity remains the target while legal; automatic target search
is a separate path. Attack-Move and Follow retain their parent destination
while opportunistic combat runs, and the order queue stores entity targets as
edict number plus spawn_time.
UnitData propWin is authored in degrees and converted once to radians when
spawning. SetUnitPropWindow/GetUnitPropWindow use the native radian value.
Movement applies the window in the shared steering step; zero prevents
translation while turning. Move, Follow, and Build leave the Walk/Stand
transition to that shared steering decision so blocked turns do not restart
the Stand animation each tick. The selected
weapon's rangeBuffer is used only while its saved simulation-time cooldown
deadline is active; the damage point still rechecks true range. See
Attack Damage for weapon timing, legality, and projectile
contracts, Pathfinding for shared route behavior, and
Shift Order Queue for queued-target identity.
The active direct target also retains its edict spawn generation through
save/load, and zero PropWindow blocks translation while the unit turns in
place.
Resolved Fixes And Remaining Gaps
Attack facing gate and turn animation
The shared movement step now switches to a stable Stand pose when propulsion is
blocked by the facing window. The separate attack-facing tolerance is still a
gap: melee and ranged windup callbacks advance without an attack-facing gate.
Do not reuse propWin for that rule; establish the authoritative attack-facing
tolerance before adding the gate and cover both melee and ranged transitions.
Target distance uses collision geometry
attack_target_distance() is shared by maximum- and minimum-range checks. It
measures from the attacker's collision edge to a unit/destructable collision
edge, or to the target's authored pathing footprint when present. Regressions
cover unit collision radii for both maximum and minimum range; the existing
building-footprint test continues to cover structures. A destructable-specific
range regression is still useful.
Active direct target incarnation is retained
Active direct Attack stores attack_target_spawn_time with its goal pointer,
checks the target incarnation before combat callbacks, and persists the field
through save/load. Regression coverage verifies slot reuse rejection and a live
Attack target round trip.
Long-route moving-target refresh is bounded by relative displacement
Direct steering and the final approach read the target's current coordinates.
For a longer route, M_RefreshHeatmapForMover() reuses a cached target field
until the target moves more than 10% of the current mover-to-target distance
and the field is at least 400 ms old. When no mover is supplied, it retains the
64-unit bound. Tests for reversals, obstacle crossings, and small displacement
at short remaining distance are still needed.
Acceptance Coverage Still Needed
These scenarios have partial implementation support, but the current automated tests do not establish the full specification contract:
- Group surrounds: movement has per-unit collision and attack stops each unit based on its own range, with no global surround-slot allocator. Add 4/12/24/50 melee attackers around a moving unit and a large building. Assert no overlap or target-center deadlock, legal pathing, individual range stops, and continued approach by blocked units.
- Moving target chase: test live direct-target following through an obstacle, target reversal, target crossing behind the attacker, and target death. Check that the explicit order never switches to a nearer enemy.
- Profile transform:
attack_profile()resolves compatibility from the current target when used, but add a ground-to-air transform test proving an incompatible swing is stopped and a compatible second profile is selected. - Save during actual chase: current save coverage verifies direct target and cooldown fields, but should save after a real canceled windup while the target is moving, then verify target identity, cooldown expiry, and route regeneration after load.
- Large battle cost: run 100 attackers against 20 moving enemies and record bounded route requests and test duration. The current design has no central surround solver; that alone does not prove the full movement workload meets the spec's performance requirement.
- Deterministic attack rolls:
attack-damage.mdalready records that rolls use processrand(). Replace it with an authoritative simulation RNG before claiming multiplayer/save-load deterministic combat.
Group-membership fan-out, direct-vs-Attack-Move target policy, Shift queue identity, replacement orders, Stop, and cooldown deadline persistence already have dedicated implementation paths or regressions. They should remain in the regression suite while closing the gaps above.
Verification Pointers
Relevant code and tests:
games/warcraft-3/game/skills/s_attack.cgames/warcraft-3/game/skills/s_move.cgames/warcraft-3/game/g_local.h(edict_s.movement, active target pointers, andunitOrder_t)games/warcraft-3/game/g_save.c(edict_fieldsand order queue persistence)games/warcraft-3/game/tests/t_combat.cgames/warcraft-3/game/tests/t_movement.cgames/warcraft-3/game/tests/t_order_lifecycle.c
Run focused Warcraft III tests in both data variants with:
make test-wc3-engine WC3_PATTERN='wc3_combat.*'
make test-wc3-engine WC3_PATTERN='wc3_movement.*'
make test-wc3-engine WC3_PATTERN='wc3_order_lifecycle.*'
For completed implementation work, run make test as required by
CONTRIBUTING.md.