Files
healer-man/docs/ENEMY_FEAR_MOVEMENT_2026-10-06.md
2026-10-06 22:25:34 -04:00

142 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Enemy fear movement follow-up — 2026-10-06
This pass integrates source-informed movement for the pinned enemy shout fear
auras into player, party and mobile owned-summon consumers. Voluntary movement,
jumping, attacks and casts remain suppressed while fear controls the actor.
Movement requires reviewed navigation and collision/LOS approval. Missing or
blocked spatial support safely holds the actor. Other controls retain their
existing policy; this does not claim exact historical-server path or RNG parity.
## Historical core behavior
The read-only historical source is AzerothCore commit
`e822d718322289dd036fb2daf8867ba41283bf9a`, specifically
`src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp`,
`src/server/game/Movement/MotionMaster.cpp`,
`src/server/game/Entities/Unit/Unit.cpp`, and
`src/server/game/Spells/Auras/SpellAuraEffects.cpp` under
`../wow-pvt-server-backups-2/azerothcore-wotlk`.
`HandleModFear` passes the live aura caster to `Unit::SetControlled`; that
passes it to `SetFeared`, then `MotionMaster::MoveFleeing`. The generator looks
up the caster GUID when it chooses each new destination. It does not preserve
the caster's cast-time position. If that lookup fails, it picks a random angle
and follows its near-source distance branch with a source distance of zero. A
dead caster can still resolve while its unit remains in-world; after removal,
the source-less fallback applies.
The source's exact movement bounds and retries are:
| Core rule | Source value |
| --- | ---: |
| Quiet-range lower bound | 28 yd |
| Quiet-range upper bound | 43 yd |
| Minimum accepted path | 2 yd |
| Path length limit | 30 yd |
| Retry after destination LOS failure | 200 ms |
| Retry after failed, short, or invalid path | 100 ms |
| Repath wait after spline travel | random 800–1500 ms |
Below 28 yards, the server picks a distance between `0.4` and `1.3` times the
remaining distance to 28 yards and a heading within ±π/8 of the direction away
from the source. Above 43 yards, it picks `0.4`–`1.0` times the 15-yard band
width and heads partly back toward the source, within ±π/4. Inside the quiet
band, it picks `0.6`–`1.2` times the band width in a random direction. A source
distance at or below 0.2 yards uses a random base angle. The core constrains the
candidate with `MovePositionToFirstCollision`, checks LOS to the resulting
point, then asks `PathGenerator` for a path and rejects shortcuts, no-path,
far-from-poly, paths shorter than 2 yards, and paths beyond the 30-yard limit.
After five failed paths it stops looking up the source and uses the
source-less random fallback.
`SetControlled` does not start fear while stun or confusion already owns the
unit. When the higher-priority control ends, it rechecks active fear auras and
resolves their caster GUID. This helper accepts the actor's active stun end time
and holds fear movement until that higher-priority control expires.
## Planner boundary
[`src/game/enemyFearMovement.ts`](../src/game/enemyFearMovement.ts) carries the
source distance bands, steering ranges, LOS/path retries, and path length
limits. Its hash-based choices are simulator decisions: they reproduce the
source's ranges with stable values derived from actor ID, source ID, aura
application time, and decision index. They do not reproduce the core's global
random-number stream. `worldUnitsPerYard` is required input because imported map
coordinates can have a different scale. The estimated time to the next choice
uses approved path length divided by this actor's current effective speed,
then adds a seeded wait from the core's 800–1500 ms range.
Each actor supplies its own fear expiry, stun expiry, base movement speed, and
source speed multiplier. The shout's +25% speed is therefore applied only when
that target has the still-active source speed aura. The current adapters are
limited to tagged source fear. `getSourcePosition` runs once per repath so
the goal uses the live caster location at that decision; the caller should
cache the resulting goal until `nextDecisionAtMs` instead of recomputing it
every frame.
The planner requires three map adapters: a projection that rejects excessive
snaps and floor changes, an LOS test, and a complete actor-safe path-length
query. Missing map support, an unsafe projection, blocked LOS, or an invalid
route yields a hold with the source retry timing. The destination is a route
goal only. The caller must move through actor collision and approved navigation
waypoints; it must never assign the destination directly to an actor position.
`stepEnemyFearTowardDestination` only computes a bounded request and does not
replace those checks.
## Integration seams before this pass
- `PlayerRig` resolves the player's shout control near its frame movement step.
`enemyControlPreventsMovement` currently blocks all fear before movement, and
`Rapier`'s character controller checks the player's capsule against terrain.
For a tagged source fear with no active stun, the frame can use the cached
flee goal to set the autonomous world movement vector, then submit its bounded
delta through the existing character controller. The goal must not be
derived from camera input or change camera yaw.
- `PartyPopulation` similarly blocks fear before companion route planning. It
already has the reviewed navigation graph, `planPartyRoute`, direct-segment
collision checks, and route-segment clearance validation. A fear goal should
use those route helpers and hold when no safe route exists.
- The summon loop in `combatStore.ts` returns early when `controlledUntil` is
active, before movement and actions. `CombatSummon` currently keeps a control
expiry but no control mechanic. A source fear adapter needs to preserve the
per-summon fear and stun state, move only when fear is the active control,
and continue to suppress actions. `CombatSpatialProvider` provides live actor
positions, LOS, ground projection, and the reviewed navigation graph; the
adapter still needs to validate a complete route before advancing a summon.
- The shout control resolver currently returns only mechanic and expiry. The
integration must identify the active tagged `ActiveAura` per actor to retain
its source ID and `appliedAt` for live source lookup and deterministic seeding.
That lookup can use the mob runtime position while the caster exists; when
the source ID no longer resolves, pass `null` and use the source-less branch.
The existing hostile NPC fear path is separate. `MobPopulation` already calls
`stepFearedMobAway` and clamps that direct step to the home leash, using the
current aggro target as the threat. That helper does not perform LOS or route
validation for the flee step. This follow-up leaves it unchanged and does not
claim it matches the historical generator.
## Integrated consumers and verification
`enemyFearMovementRuntime.ts` retains the selected aura's source/application,
caches each goal and implements the five-failure source fallback. Concurrent
tagged stun and generic stun/confuse/root/sleep auras hold movement even when
the actor's longest control timer belongs to fear. Expiry or purge clears the
per-actor goal; a refreshed aura starts a new decision sequence.
PlayerRig follows approved route waypoints through its existing Rapier character
controller, including gravity. Keyboard movement and jumping do not steer fear.
PartyPopulation uses its reviewed graph and capsule route validators, normal
party speed with actor-local source modifiers, and follows forced fear even
under Stop. Fear never enables catch-up speed or stuck-recovery relocation.
Camera look retains its ordinary hold-and-drag behavior.
`enemyFearSummonMovement.ts` requires a loaded graph and validates projection,
LOS, path segments and each bounded step. The actual combat tick consumes it
before summon actions, holds concurrent controls, and preserves the pet's Prowl
movement modifier. Totems and stationary zones remain stationary. Remote actor
authority and hostile-NPC flee policy remain outside this bounded integration.
Focused planner/cache/summon verification passes 20 tests, including actual pet
ticks, blocked/missing navigation and simultaneous stun expiry. Input regressions
pass 33 tests. Final build/live evidence is recorded in the combined follow-up report.