feat(movement): run/walk gaits — run by default, hold Shift to walk #180
No reviewers
Labels
No labels
alpha:wave-0
alpha:wave-1
alpha:wave-2
alpha:wave-3
area:assets
area:combat
area:ecology
area:infra
area:render
area:scripting
area:ui
area:world
enhancement
epic
migration
post-alpha
roadmap
tech-debt
type:bug
type:chore
type:design
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
marco/IsoMmo!180
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/run-walk"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Splits the single movement speed into two gaits: a faster run (the default) and a slower, deliberate walk (hold Shift). Movement stays server-authoritative — the client sends the gait as an intent and the server picks the per-step cooldown and enforces it as a floor, so a modified client cannot outrun the run cadence. Implements #177 (design + Invariants Check on the issue). Stamina is deliberately not part of this MVP — tracked in #178.
Reviewed with
critical-design-reviewbefore any code; the agreed Invariants Check and Verification plan live on #177.What changed
MoveRequest(Direction, Running);PlayerState/PlayerMovedcarryRunningso observers glide + animate the right gait.ProtocolVersion.Current11 → 12 in the same change.GameOptions:MoveCooldownTicks(walk, now 3 ≈ 300 ms — a touch slower than before) andRunCooldownTicks(1 ≈ 100 ms).MovementSystemkeeps the pending intent's gait and gates the step on the matching cooldown;PlayerMobile.Runningis the entity's current gait (transient, never serialized) and teleport/resurrect resets it.Mobileglides for a gait-dependent duration andPlayerplays the run cycle while running.human/run_0..4(body 400, action group 2) into the pack. To avoid regenerating every asset from whichever client is mounted, this adds a surgical extractor commandhuman-anim <client> <action> <name> [fps] [pack-path]that patches one animation into the existing pack in place;packalso learned therunaction for future full rebuilds. (UO art remains a temporary placeholder, to be redone with original art.)move <dir> [run]/goto <x> <y> [run], with the step pacing following the gait so a timedgotoreflects real walk vs run speed (docs/debug-harness.mdupdated).How it was tested
World) —RunWalkTests: a run step applies in fewer ticks than a walk step; a client spamming the run intent cannot beat the run cooldown; the moved state carries the applied gait; a walk step leaves it false. Client.Core —GaitTimingTests(run glides quicker, run fallback speeds the leg cycle);HumanArtLayoutTestsextended for therun_*names.Screenshots
Mid-stride, walk vs run — the run row is the real UO run cycle (body pitched forward, arms pumping, extended stride), clearly distinct from the upright walk.
Run pose in world (first frame is walking for reference):
Notes / follow-ups
MoveCooldownTicksis also the creature chase cadence, so creatures now chase at the walk speed (3 ticks) and a running player can outrun them. That's an acceptable MVP consequence (and arguably right — you can sprint away), but if you'd rather creatures kept pace they need their own tunable; say the word and I'll split it.Follow-up fix: overlays detaching while running
Reported after the first pass: the hair "chased" the character when running. Root cause was mine —
DriveOverlayonly played a layer's animationif (set.Has(anim)), and the overlay art (hair/beard/equip) had norun_*frames, so while the body played the run cycle each overlay was left neither played nor updated: frozen on its last frame, keeping that frame's own anchor, so it visibly drifted off the head.Fixed properly, two parts:
run_*(action group 2) for all 7 hair styles, 5 beard styles and 3 equip kinds, so each overlay animates its own run cycle in true lockstep with the body (78 run entries in the pack now). Thepackcommand also learned therunaction for equip/hair/beard so future full rebuilds include them.DriveOverlaynow falls back to the walk cycle when a layer lacks the body's action instead of freezing (a frozen overlay keeps a stale anchor and drifts). Covers e.g. the two beard styles whose client art only has 4 of the 5 stored facings.The surgical extractor command was generalised from
human-animtobody-anim <client> <body> <action> <name> <prefix> [fps] [pack]so any body/namespace can be patched into the existing pack.Verified in-game: in the running poses the hair now sits on the head and follows the forward-leaning stride. Build 0 warnings, all tests green, CI green.
Realigned on
mainafter #155 mergedMerged the new
main(biome art + map + marsh move-cost) into this branch and resolved all four expected conflicts:ProtocolVersion— both branches had claimed 12. Marsh feedback keeps 12 (it landed first, onmain); the run/walk gait becomes 13, with both changelog lines preserved.MovementSystem— the substantive one: the two gates are now composed rather than either/or. The gait picks the base cadence and the terrain scales it:MoveCostTicks(running ? runCooldownTicks : walkCooldownTicks, from, into)— so marsh slows a runner and a walker alike, using the costlier of from/into, and the result is still the floor the server enforces.ServerMessageDispatcher— combined:MoveTo(tile, moved.Running)and the marsh step feedback fire on the samePlayerMoved.assets.isoa— rebuilt rather than picked: tookmain's pack (biome land art, dual-grid masks, marsh ripple), re-appliedown-art, then re-extracted the run animations. The merged pack carries 78 run entries (human + 7 hair + 5 beard + 3 equip), 50 land tiles, 16 masks and the ripple.Also added the composition test the owner asked for — marsh × gait: a run step through marsh is slower than one on grass, yet still quicker than walking the marsh (you can sprint the bog, it just costs you).
Whole solution builds 0 warnings; all tests green (7/7 projects, 513 tests, marsh and run/walk suites side by side); CI green; branch mergeable.
Updated to current
main(was 43 commits behind → not mergeable). Conflicts resolved:MoveCostTicks(baseCooldown,...)+CanStep(..., maxStepZ); ctor is nowMovementSystem(map, walkCooldownTicks, runCooldownTicks, maxStepZ).Running, per-gait glide duration).GaitTiming.FallbackFpsScale, which the feature was designed for); re-baking the real run frames into the current pack is a follow-up.Gates green in a worktree off current main: whole solution builds with 0 warnings, 763 tests pass (incl.
RunWalkTests+ the terrain-Z suite), CSharpier clean.mergeableis now true.