Files
BT412/context/locomotion.md
T
arcattackandClaude Opus 4.8 f914fc040a context-system: complete migration -> CLAUDE.md is now a 160-line router (zero context lost)
Full migration of the 2236-line monolithic CLAUDE.md into the progressive-context
knowledge graph (per spark-lesson / expert-seed.md), so the deep RE knowledge loads
on-demand instead of every session.

ZERO CONTEXT LOST:
- docs/PROGRESS_LOG.md = the complete old CLAUDE.md, VERBATIM (byte-identical) -- the
  lossless safety net + the "full detail" quick-lookup fallback.
- 18 context/*.md topic files (1343 lines) digest every section (§1-3 -> project-overview,
  §4 -> content-archives, §5 -> asset-formats/bgf-format, §5a -> source-completeness,
  §5b/§8 -> wintesla-port, §7/§10 -> locomotion, §10a -> build-and-run, §10b ->
  reconstruction-method, §10c -> combat-damage + reconstruction-gotchas, §10d -> subsystems,
  render notes -> rendering, gauges -> gauges-hud, MP -> multiplayer, §9 -> open-questions).
- reference/glossary.yaml (53 terms). decomp-reference.md = the offsets/ClassIDs/addresses hub.

CLAUDE.md (160 lines) = router: identity, answer/reason protocols, quick-lookup table,
evidence tiers (T0 engine-truth / T1 decompiled+verified / T2 reconstructed+runtime /
T3 guarded / T4 hypothesis), conventions + DO-NOT (the systemic bug classes), structure.
Retains the load-bearing work directives (build recipe pointer, "keep current" mandate).

Knowledge graph validates CLEAN (scratchpad/checkctx.py -- all [[links]] + quick-lookup +
docs refs resolve; [[name]] -> topic file or glossary term). docs/*.md ledgers stay as the
detailed logs; context/*.md are the curated digests that route into them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 22:19:50 -05:00

5.6 KiB
Raw Blame History

id, title, status, source_sections, related_topics, key_terms, open_questions
id title status source_sections related_topics key_terms open_questions
locomotion Locomotion — gait, the ground model, collision, shadow established PROGRESS_LOG.md §7, §10 (gait/ground/collision/shadow); docs/P3_LOCOMOTION.md
asset-formats
decomp-reference
reconstruction-gotchas
combat-damage
rendering
RootTranslation
ground-model
SequenceController
MechControlsMapper
SLD
joint
Authentic per-mech turn-rate constant (currently bring-up constant)
Body-callback gimp handlers (states 16-19); airborne callbacks

Locomotion

Walking, the gait controller, the ground/collision model, and the shadow. All DEFAULT-ON now (BT_GAIT_CUTOVER, BT_GAIT_SM, BT_COLLISION, BT_REAL_CONTROLS — set =0 to fall back). Full detail: docs/P3_LOCOMOTION.md + docs/PROGRESS_LOG.md §7, §10.

Walk speed is animation-driven (not physics)

Each .ANI has a [RootTranslation] where .z = forward root SPEED (world units/s). The engine AnimationInstance::Animate(dt, move_joints) integrates movement += Δt·rootTrans.z and moves the body that far → feet plant by construction (no stride constant; k=1). Idle anims have z=0. The .MOD physics governed thrust VEHICLES, NOT walk gait. [T1]

The two-channel gait (real controls)

The BT SequenceController (seqctl.cpp, from SelectSequence@004277a8+Advance@0042790c) drives locomotion. Under BT_REAL_CONTROLS two channels run:

  • LEG channel writes the skeleton JOINTS from the live demand (a local sim; its joint writes are overwritten by the body).
  • BODY channel is the DISPLAYED motion — travel comes from the SAME Advance that writes the displayed joints (move_joints=1), so display == travel BY CONSTRUCTION (v4 fix — killed the desync class). [T2] The gait STATE MACHINE (AdvanceBodyAnimation, mech2.cpp) slews bodyCycleSpeed toward the demand through walk/run caps (LoadLocomotionClips, mech3.cpp:326 — measures walkStride/standSpeed via legAnimation.SelectSequence). Transition callbacks (Mech::BodyClipFinished @0x4a6d8c, LegClipFinished @0x4a6928 — PE-parsed jump tables) alternate walk 6↔7 / run 12↔13 + walk→run. [T2] ⚠ Aliasing bug class (namedClip): namedClip[]@0x5e0 == animationClips[]@0x5cc+0x14 (ONE array); gimpBaseClip@0x64c == animationClips[0x20]; bodyAnimationState@0x728 == bodyStateAlarm.level. Declared-separate members don't see each other's writes — alias them.

The AUTHENTIC 1995 ground model (default-on, BT_GROUND_REAL)

Decoded by the 10-agent ground-model-decode workflow (binary FUN_004a9b5c = the master perf). NO GRAVITY anywhere — accelerations zeroed every frame. Vertical position = an ABSOLUTE per-frame ground SNAP:

  1. ctor lifts collisionTemplate->minY by 5% of volume X-width (the probe height + wall-vs-floor separation).
  2. per frame MoveCollisionVolume() re-finds the containing node in the zone's BoundingBoxTree (BOXTREE.cpp — NOT a heightfield).
  3. probe q = origin + (0, lift, 0)FindBoundingBoxUnder(q, &h) (h = distance down to the highest solid top; 1 = miss).
  4. snap gate h > 1e-4: origin.y -= (h lift) ⇒ y = surfaceY exactly. MISS ⇒ NOTHING (y holds, no runaway). Up-slope bounded by the lift window (implicit step allowance); walk-offs drop instantly.
  5. GetCurrentCollisionsProcessCollisionListMech::ProcessCollision (@0x4abb40, vtbl+0x3c): BoxedSolid resolver → StaticBounce → owner classification. Crushable icon (flags&0x8000) = damage 0.00123f sentinel = move stands; damage>0 = FULL FRAME REJECTION (walls block, never slide); impactVel²>40 = crash-anim (SetLegAnimation(0x20)). [T2] KEY: FindSmallestNodeContainingColumn/FindBoundingBoxUnder (FUN_0040e36c/0040e5f0, BOXTREE.CPP) are ALIVE in the 2007 engine — the "heightfield" labels were wrong (caused two reverted attempts). A master mech needs a CollisionAssistant (GetCurrentCollisions iterates it unchecked). BT solids ship ZERO tiles (h never negative). [T2]

Knockdown / crash + the desync/stutter fixes

Wall impacts iv²>40 (~6.3 u/s) bind the bmp stagger clip. Two documented bug fixes: (a) the knockdown must stagger BOTH channels (SetBodyAnimation(0x20) + SetLegAnimation(0x20)) or display

  • travel split permanently (foot-slip); (b) a contact-hysteresis gate (0.4 s gBlockCooldown) stops the knockdown re-firing on sustained/glancing contact. localVelocity is zeroed while crashed (spec-faithful) so a knocked mech can't keep advancing. [T2]

Controls (BT_REAL_CONTROLS, default-on)

MechControlsMapper (mechmppr.cpp @004afbe0; btl4mppr.cpp mappers) interprets input → speedDemand / turnDemand. ⚠ WndProc NEVER receives WM_KEYUP (the engine's per-frame reader GetMessages them out) → poll GetAsyncKeyState per frame, gated on foreground. Virtual controls: W/S sweep a persistent throttle LEVER with a zero detent; A/D a momentary auto-centering stick. [T2]

Shadow (default-on, BT_SHADOW_TILT)

BT 4.10's shadow is the flat *_tshd.bgf proxy drawn as blend-pass geometry, posed by jointshadow (terrain tilt) + jointtshadow (torso twist). Terrain-tilt samples the collision surface gradient; a depth-bias (D3DRS_DEPTHBIAS, decal-style) stops it z-fighting/vanishing on inclines. Polar maps have STUB *_tshd (shadow authored-off on snow). Also the visual-ground conform (btvisgnd.cpp, render-only lift to the visible terrain triangles — presentation only, never touches localOrigin). [T2]

Key Relationships