WASD: direction movement steers the walk path directly

The keys rewrite the hero's path every cCreature::Update: one segment
along the held direction, the facing set to it, sliding along blocked
cells. Starting from idle runs the game's own Think steps at once
(StartPendingWalk, WalkAction) instead of waiting up to 110 ms, so
weapon switches no longer stop the walk. Stops wait 50 ms after the
keys are released, and a stop in progress is taken back when keys are
held again. Names for movement, stats and text-key functions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
khallaandClaude Opus 5.5 committed 2026-10-18 03:18:51 +02:00
1 parent d296e2850b
commit 84c50d8c33
6 files changed
+422 -104

No files matched your search

+87 -11
View File
@@ -34,15 +34,87 @@ Mouse, measured with an order log:
## What the mod does
Every `cEngine::Tick`, while W/A/S/D are held (and no text field has the
focus, no mouse button is held): a plain walk order to hero position +
direction x 300 world units (about a cursor's distance). Screen directions in
world axes: up (-1,-1), down (1,1), left (-1,1), right (1,-1); combinations are
summed and normalised. All keys released: the target-less stop order.
### Direction movement (2026-10-18)
Dead ends, for the record: re-aim orders (+0x20 = 1) on their own stop the
hero after two orders; far targets (2000 units) are rejected or followed
badly; targets near the stepwise-updated position make it arrive and stop.
Request: "lets go all in with directional vector movement". The keys steer
the hero directly instead of sending walk orders 10 times per second, which
stuttered and stopped on weapon switches.
Measured (`Place` hook, watchpoints on the hero's x and on
`g_creatureNewPosition`):
- The only writer of a creature's position is `cObjectManager::Place`
(`0x5fcd10`, thiscall (id, position*), ret 8). For walking it is called from
`cCreature::Update` (`0x594110`, thiscall (u8), ret 4) at `0x59653a`, about
40 times per second on the engine thread.
- `Update` steps the creature along its path:
- waypoints {x, y} at +0x2ed + i * 8, segment index +0x36d, count +0x371,
sub-unit remainder +0x37d / +0x381;
- speed +0x375 (337 for the battlemage) times the elapsed time.
- The step goes into `g_creatureNewPosition` (`0xad2368`, {u16 area, x, y,
u8 level}; written at `0x5953da` and `0x5955f7`). It then passes through
`cCreature::CanStepTo` (`0x593c50`, thiscall (x, y, position*), ret 0xc),
which refuses a step into another cell that `cCreature::IsCellWalkable`
(`0x4f7b20`, thiscall (cell*), ret 4, read-only) rejects. On refusal the new
position is reset to the current one (`0x59631a`). Cells are 53.66563 world
units.
- Walking straight into a fence, 27 of 28 steps were refused: the hero ran on
the spot at 2 units per update.
Now (`src/mods/wasd.cpp`, hook on `cCreature::Update` for the controlled
creature, before the original):
- Held keys give a unit direction in world axes: up (-1,-1), down (1,1),
left (-1,1), right (1,-1); combinations are summed and normalised.
- Walking: the path is replaced by one segment from the hero's position,
1000 units along the direction (index 0, count 2). The game's own step moves
the hero at its own speed and animation, and it never arrives.
- Obstacles: the way 1.25 cells ahead is probed at quarter cells with
`IsCellWalkable`. If it is blocked, the direction turned by 30, 60 and 90
degrees is tried, the side last slid to first, so the hero slides along
walls. If all are blocked: stop order.
- The facing (+0x70 / +0x74, float unit vector in world axes) is set to the
direction each update. The game turns it towards the path gradually; on a
reversal that took about 300 ms and swung through the side, so the hero
walked backwards for a moment (user report: "direction change east/west is
not instant").
- Idle (start, after an attack, cast or weapon switch): a walk order 300
units ahead, and right after it the two steps `cCreature::Think` (vf04,
`0x537430`, thiscall (float), ret 4) would take on its next run:
`cCreature::StartPendingWalk` (`0x4ede70`: pending action +0xfc 2 with the
parameters at +0x100 becomes action 1 with +0x154) and
`cCreature::WalkAction` (`0x4f45b0`, phase 0 → 1: path, walk animation).
Measured: Think runs only about every 110 ms per creature, so the walk
started 16..125 ms after the order. Now it starts in the same update. If a
status effect holds the hero, the order is retried at most every 150 ms.
- Other actions (attack, cast, shot): left alone until they end.
- Rapid key changes (user report: "not 100% smooth when rapidly changing
directions"). Measured: a moment without any key (30 ms in the test)
sent the stop order. The hero then went through the game's stop: walk
phase 0x64, action 2 with the stop animation, idle about 110 ms later,
and only then started again (about 125 ms standing per change). Now:
- the stop order waits until the keys have been released for 50 ms;
- a stop requested but not begun (walk, phase 0x64) goes back to phase 1
when keys are held again;
- a running stop (action 2) restarts like standing idle.
Mouse walking has the same uneven steps (4/6 units per update) and paired
updates, so those are the game's own rhythm.
- Keys released: the target-less walk order (stops in place), unless a mouse
button is held.
Autotest (battlemage, each key 1.5 s): about 250 units per second and almost
no refused steps. The hero faces the key direction and plays the walk
animation.
Superseded: the walk order per `cEngine::Tick` (10 Hz). Dead ends, for the
record:
- re-aim orders (+0x20 = 1) on their own stop the hero after two orders;
- far targets (2000 units) are rejected or followed badly;
- targets near the stepwise-updated position make the hero arrive and stop;
- setting the walk mode +0xfc to 2 (as while a mouse button is held) does
not change the speed.
## Hotkeys
@@ -91,6 +163,10 @@ sets the walk goes on, between a melee and a bow set the hero drops to idle
for one tick: the weapon class changed, and the switch resets the hero's
animation state (`0x54e540(1, 0)`). The next WASD tick restarts the walk.
Now: `SelectWeaponSet` is hooked (`skillbar.cpp`) and calls
`mods::wasd::ResumeWalk`, which sends the walk for the held keys at once.
Not yet confirmed in-game.
Measured again with a watchpoint on the action word: on a weapon class
change `cCreature::ResetToIdle` (`0x54e540`) plays the idle animation and
sets action 0, phase 1, pending action 0. A user report ("still little
stops, weapon to no weapon and back") came after the restart through a walk
order, which waited for the next Think (up to 110 ms). Now direction movement
starts the walk in the same update (see above). The `SelectWeaponSet` hook
with `ResumeWalk` was removed.