Files
sacred_k/docs/wasd.md
T
khallaandClaude Opus 5.5 d296e2850b WASD: resume the walk right after a weapon set switch (untested in-game)
Switching between weapon classes resets the hero's animation state inside
cUI_Taskbar2::SelectWeaponSet, which dropped the walk for a tick. The hook
sends the WASD walk again at once. Checkpoint before the movement rework.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 01:41:13 +02:00

4.7 KiB

WASD movement (src/mods/wasd.cpp)

How the game moves the hero

Movement is a creature order: cEvent_creature (vtable 0x88e95c, 0x44 bytes), command 4 = walk, sent with cEngine::SendCreatureOrder (0x6170d0, (creature, event)), which snaps a ground target to a walkable point and passes the order to creature->vf18 (cCreature::ReceiveOrder, 0x52e400).

Offset Field
+4 command (4 walk, 0x105 ...)
+0x10 target object id (attacks)
+0x14 2: x, y are a ground point; 0: no target
+0x18 / +0x1c ground point (world units, same as creature +0x1c / +0x20)
+0x20 1: re-aim while a mouse button is held; 2: skip target snapping

The controlled creature's object id is at cEngine+0x13c; objects come from cObjectManager::GetObject (0x5fe060, manager *0xad3bc0), cast with __RTDynamicCast to cCreature. Hero state: +0xfc action mode (2 while following a held mouse button), +0x150 movement (1 walking, 2 arriving, 0 standing).

Mouse, measured with an order log:

  • click (cEngine::HandleMouseButton 0x617360): one walk order to the ground point, then engine flag +0x54 |= 0x10 (button held);
  • while held: every mouse event re-sends a plain walk order (+0x20 = 0) to the point under the cursor, dozens per tick; the hero stays in mode 2 and walks smoothly. cEngine::Tick (0x60f1d0, 10 Hz) additionally sends a re-aim order (+0x20 = 1) per tick after 0.4 s of walking;
  • release after > 0.5 s: a walk order without target (+0x14 = 0), which stops the hero in place.

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.

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.

Hotkeys

Keys reach the game as cEvent_key (category 4; key at +6/+8, modifiers at +0xa) through the kernel queue to cEngine::ReceiveEvent (0x618130), which offers them to cUI_Manager::ReceiveEvent (0x757120) first. Both compare toupper(key) (0x84aa86). Presses come from WM_CHAR (the character), releases from WM_KEYUP (the virtual key), so keys without a character (F-keys) never reach the hotkeys. The key bindings (src/mods/input.cpp) handle the raw WM_KEYDOWN/WM_KEYUP instead: W/A/S/D and the digit row are kept from the game, bound keys post the original hotkey's key event (Kernel_Instance 0x808490, cKernel::PostEvent 0x808930); the temporary bindings for the old W/A/S actions are listed in docs/todo.md.

Hotkeys found: engine A (pick up nearby), P (pause), Q/W/E/R/B/Space (cEngine::UseActionSlot), ^ (console), Esc, Ctrl+B/F/X/Z (Z quicksave); UI manager C F H I L M N O S T (S save game window). Arrow keys in cEngine::ScrollInput (0x610650) walk object #2 or scroll the camera; not reachable in normal play.

Text fields (2026-10-18)

Request: in character creation and the save window, bound keys were ignored or typed something else (K -> s: K posts the game's S hotkey).

Measured (key-press log): the UI manager's dialog slot (+0x144) held a hidden cUI_BusyDlg in both cases, so the old "dialog shown + focused edit control" test never found the field. The focused control (+0x148) is the right cUI_EditControl, but neither the focus nor the control's flags (0x8005) change when its window closes, and the control has no parent at +0x50. The save window draws two edit controls per frame.

Now (src/mods/input.cpp, FocusedTextField): keys pass to the game while the focused control is a writable edit control that cUI_EditControl::Render (0x737ae0, thiscall (renderer), ret 4, returns at once while hidden) drew in the last 250 ms. The render hook only notes the focused control.

Weapon set switch while walking (2026-10-18)

Request: the hero stops for a moment when switching weapon sets (F-keys) while walking with WASD.

Measured (state per tick + orders): cUI_Taskbar2::SelectWeaponSet (0x6e1440, ret 8) sends order 0x114 (set, items) and 0x107; between melee 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.