pub fn entity_run(
sink: &mut dyn FightSink,
lookups: &ContentLookups,
entity: &Entity,
to: Coordinates,
) -> Result<(), Error>Expand description
Native: move entity toward the enemy line in a single multi-cell run
(one StartMove instead of one per cell — every per-cell seam quantizes
to the 100ms game tick and gives the client a chance to stutter).
Movement is fixed-direction (allies always advance +x, enemies −x), so we
only ever advance toward the nearest opponent ahead — never chase one we
have already reached or passed, which would march us away from the fight
forever. We cover the whole gap toward the opponent (one column short)
in one StartMove, so an approach is a single smooth run, not one per cell.
In the two-phase anchor model exactly one side moves at any moment (enemies
charge the stationary hero-anchor; in phase 2 the Ally team charges the
stationary casting ranged enemies), so there is no “meet in the middle” —
the mover simply closes the whole distance. Two same-team runners planning
on the same tick still can’t land on or cross each other: a Run lowers to
an immediate StartMove and the event loop is depth-first LIFO, so the
first planner’s handle_start_move sets its move_target reservation
BEFORE the second plans this tick; the second’s probe routes every cell
through cell_exists_and_free (checks coordinates AND reserved
move_target), so it sidesteps around the reservation.
(The entrance “run onto the screen → run in the fight” seam is a separate,
client-side concern — Unity’s run-in tween must flow into the first
StartMove instead of force-settling to Idle; no server distance can cover
it.)
The walk also stops on the first cell where ability gains a valid target,
and never steps onto a cell occupied or reserved by another entity.
Neither team may cross the other’s front line. A mover advances at most to
the column directly in front of the opposing team’s frontmost LIVING unit and
never onto or past it, so the two sides meet exactly one column apart instead
of stacking on the same column (an enemy standing right under the hero). The
opposing front is taken over both current cells AND reserved move_targets,
so two mutual approachers don’t both land on the same midpoint column on an
even gap: the depth-first / LIFO second planner sees the first’s reservation
and stops one column short. This is the hard guarantee layered on top of the
full-gap max_steps walk and the in-range stop.
Native: emit a run action moving entity to to (no-op if speed is 0 or
the move covers no distance).