Files
open-wc/docs/modules/local-player-movement.md
T
sindoring 8776a6b362 gmp(M02): add 3.3.5 input profile
Work-Package: M02-GMP-INPUT-PROFILE-335-001
Agent: sindo-main-codex
Tests: player input/movement/capability/terrain/camera/presentation, coordinate/streaming, docs/coordination and renderer dry-run passed
Fidelity: build-12340 binding hashes and selected defaults pinned; TrinityCore turn rate and WoWee mouse routing are compatibility references with explicit limits
2026-07-15 00:17:34 +04:00

17 KiB
Raw Blame History

Local Player Movement

Metadata

Field Value
Status Implemented
Target/work package M02 / M02-GMP-MOVEMENT-001, M02-GMP-SANDBOX-CAPABILITIES-001, M02-GMP-INPUT-PROFILE-335-001 and presentation consumers
Owners Gameplay local movement state; scene composition owns the instance
Last verified M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15
Profiles/capabilities RenderSandbox debug movement; Blizzlike335 selected bindings and keyboard turn with debug sprint/flight rejected

Purpose

Own current local velocity, acceleration and sandbox flight state independently of the player scene. Convert an immutable MoveIntent plus an explicit Godot-world movement basis into deterministic per-tick displacement and convert signed turn intent into a yaw delta without querying terrain, cameras, input devices or visual assets.

Non-goals

  • Apply world transforms or own a CharacterBody3D.
  • Query terrain height, collision, water or slopes.
  • Implement jump, fall, swim, server prediction or reconciliation.
  • Select the player basis versus camera-pivot basis; the scene adapter owns that decision.
  • Claim build-12340 movement fidelity from sandbox regression alone.

Context and boundaries

flowchart LR
    Input[MoveIntent] --> Movement[LocalPlayerMovementController]
    Basis[Explicit local movement Basis] --> Movement
    Delta[Physics delta seconds] --> Movement
    Profile[Scene profile ID] --> Capabilities[PlayerMovementCapabilities]
    Capabilities --> Movement
    Movement --> Displacement[Godot-world displacement]
    Movement --> Yaw[Godot yaw delta radians]
    Movement --> Velocity[Read-only velocity state]
    Scene[ThirdPersonWowController] --> Basis
    Displacement --> Scene
    Scene --> Transform[Player world transform]
    Scene --> Terrain[TerrainQuery and ground snap adapter]
    Velocity --> Presentation[Visual facing and CharacterAnimationPresenter adapter]

Allowed dependencies:

  • MoveIntent from the gameplay input boundary.
  • Godot value types Basis and Vector3 for renderer world-space direction math.
  • Main-thread scene adapter for basis selection and displacement application.

Forbidden dependencies:

  • Node, Resource, Input, Camera3D, ADTLoader or scene lookup.
  • World-coordinate conversion or manual ADT/tile formulas.
  • Character assets, animation players, materials, packets or server state.

Public API

Symbol Kind Purpose Thread/lifetime Errors
PlayerMovementCapabilities.render_sandbox() Factory Creates the only debug sprint/free-flight capability set Immutable caller value None
PlayerMovementCapabilities.blizzlike_335() Factory Creates the compatibility-safe set with debug movement disabled Immutable caller value Name is intent, not a parity claim
PlayerMovementCapabilities.for_profile_id(...) Factory Maps composition configuration once; unknown IDs fail closed Immutable caller value Unknown maps to Blizzlike335
LocalPlayerMovementController.new(...) Constructor Sets explicit speeds, acceleration, sprint multiplier and movement capabilities Created by composition root; retained for player scene lifetime Null capability defaults to Blizzlike335
godot_world_velocity_units_per_second Read-only state Current Godot-world velocity after the last update Stable until next mutation on owning thread None
is_flight_enabled Read-only state Current sandbox free-flight mode Stable until toggle None
movement_profile_id Read-only state Effective capability profile ID Controller lifetime None
toggle_sandbox_flight() Command Toggles flight only when explicitly permitted and clears velocity Owning physics/input thread Rejected request returns false with state unchanged
advance(move_intent, godot_world_movement_basis, delta_seconds) Stateful update Accelerates toward requested velocity and returns displacement for the tick Owning physics thread; one call per tick Negative delta is treated as zero
calculate_yaw_delta_radians(move_intent, delta_seconds) Pure query Converts signed turn input to Godot yaw delta Owning physics thread Negative delta is treated as zero

Constructor units:

Parameter Unit/meaning
run_speed_units_per_second Forward Godot units per second
backward_speed_units_per_second Backward Godot units per second
strafe_speed_units_per_second Lateral Godot units per second
flight_vertical_speed_units_per_second Sandbox vertical Godot units per second
acceleration_units_per_second_squared Velocity change per second
sandbox_sprint_multiplier Dimensionless debug multiplier
keyboard_turn_speed_radians_per_second Positive keyboard yaw speed; defaults to PI rad/s

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input MoveIntent PlayerInputSource Movement controller Immutable caller-held value One physics tick
Input PlayerMovementCapabilities Application/player scene composition Movement controller Immutable caller-held value Controller lifetime
Input Godot-world Basis Player or ThirdPersonCameraRig adapter Movement controller Value copy One physics tick
Input Delta seconds Godot physics loop Movement controller Scalar value One physics tick
Input Flight toggle command Remappable sandbox action via scene adapter Movement controller Synchronous command Input event dispatch
Output Displacement Vector3 Movement controller Player scene adapter Value copy Applied in same tick
Output Yaw delta radians Movement controller Player scene adapter Scalar value Applied before movement basis selection
Output Velocity Vector3 Movement controller Facing and CharacterAnimationPresenter scene adapter Read-only value copy Until next update
Output Flight state Movement controller Basis/terrain scene adapter Read-only boolean Until next toggle

Side effects:

  • Mutates only controller-owned velocity and flight state.
  • Does not mutate scene tree, transforms, resources, filesystem, network or cache.
  • The scene adapter remains responsible for transform mutation and ground snap.

Data flow

flowchart LR
    Intent[MoveIntent axes] --> Target[Calculate target velocity]
    Intent --> Turn[Calculate signed yaw delta]
    Turn --> SceneYaw[Scene applies character yaw]
    Basis[Player or camera-pivot Basis] --> Directions[Forward/right directions]
    Directions --> Target
    Config[Speeds and sprint multiplier] --> Target
    Profile[RenderSandbox or Blizzlike335] --> Capability[Immutable debug capabilities]
    Capability -->|allow or reject sprint| Target
    Capability -->|allow or reject flight toggle| Current
    Previous[Owned velocity] --> Integrate[move_toward by acceleration × delta]
    Target --> Integrate
    Integrate --> Current[Updated velocity]
    Current --> Step[velocity × delta]
    Step --> Displacement[Scene-applied displacement]

Lifecycle/state

stateDiagram-v2
    [*] --> GroundedSandbox
    GroundedSandbox --> FlyingSandbox: toggle [debug flight allowed] / clear velocity
    GroundedSandbox --> GroundedSandbox: toggle [debug flight rejected]
    FlyingSandbox --> GroundedSandbox: toggle / clear velocity
    GroundedSandbox --> GroundedSandbox: advance
    FlyingSandbox --> FlyingSandbox: advance

The names describe the existing sandbox mode only. They are not authoritative gameplay movement states and do not imply terrain contact or server permission.

Main sequence

sequenceDiagram
    participant Input as PlayerInputSource
    participant Scene as ThirdPersonWowController
    participant Move as LocalPlayerMovementController
    participant Cap as PlayerMovementCapabilities
    participant Present as CharacterAnimationPresenter adapter
    Input-->>Scene: MoveIntent
    Scene->>Cap: map configured profile once
    Scene->>Move: construct with immutable capabilities
    Scene->>Move: calculate_yaw_delta_radians(intent, delta)
    Move-->>Scene: yaw delta
    Scene->>Scene: apply yaw and synchronize camera orbit yaw
    Scene->>Move: advance(intent, selected basis, delta)
    Move-->>Scene: displacement
    Scene->>Scene: apply global position and ground snap
    Scene->>Move: godot_world_velocity_units_per_second
    Scene->>Present: horizontal motion

Ownership, threading and resources

  • The player scene creates and exclusively owns one movement controller.
  • The composition root creates one immutable capability value; the controller retains it and exposes only its effective profile ID.
  • Velocity and flight state have the same lifetime as that controller.
  • All mutations currently occur on the main input/physics thread.
  • Read-only value returns are copies; consumers cannot mutate owned state.
  • No Nodes, Resources, RIDs, files, jobs or worker threads are created.
  • The supplied Basis is a Godot-world direction basis, not a typed world-position contract or coordinate conversion.

Errors, cancellation and recovery

Failure Detection Behavior Diagnostic Recovery
Negative physics delta advance guard Returns zero displacement; velocity unchanged Unit regression Resume with valid non-negative delta
Missing scene basis owner Scene composition defect Controller cannot be called correctly Scene/parser error or boundary test Restore explicit scene dependency
Flight toggle during motion Command path Velocity clears before next step State-transition regression Continue from zero velocity
Flight/sprint request in Blizzlike335 Capability check Request has no effect Profile regression Select explicit RenderSandbox only for tooling/debug scenes
Unknown/null profile capability Factory/constructor safe default Uses Blizzlike335; debug movement disabled Capability regression Correct composition profile ID
Missing terrain data TerrainGroundSample.is_available == false Scene skips existing ground snap Stable terrain failure code Replace/recover query backend

There is no asynchronous cancellation, retry or rollback. Recreating the controller resets velocity and flight state deterministically.

Configuration and capabilities

Setting/capability Default Profile Runtime mutable Effect
Forward speed 7.0 units/s Current sandbox Fixed for controller lifetime Target forward velocity
Backward speed 4.5 units/s Current sandbox Fixed for controller lifetime Target backward velocity
Strafe speed 4.5 units/s Current sandbox Fixed for controller lifetime Target lateral velocity
Acceleration 28.0 units/s² Current sandbox Fixed for controller lifetime Acceleration and deceleration
Flight vertical speed 7.0 units/s Sandbox debug Fixed for controller lifetime Vertical free-flight target
Sprint multiplier 6.0 Sandbox debug Fixed for controller lifetime Multiplies planar and vertical targets
Camera-relative basis Selected only while flight is enabled Sandbox debug Per tick Preserves existing pitched flight direction
Debug sprint permission Enabled RenderSandbox only Immutable Allows sprint request multiplier
Debug free-flight permission Enabled RenderSandbox only Immutable Allows toggle and vertical movement
Compatibility-safe debug permissions Disabled Blizzlike335 Immutable Ignores sprint/toggle requests
Keyboard turn rate 3.141594 rad/s Blizzlike335 composition Fixed for controller lifetime Server-contract default applied to signed turn intent

Persistence, cache and migration

Movement state is transient and not serialized. No schema, cache, migration or saved settings change is introduced. Future reconnect/replay state requires a separate versioned movement snapshot contract.

Diagnostics and observability

  • Logs: verifier emits LOCAL_PLAYER_MOVEMENT PASS or named invariant failures.
  • Metrics: none yet; update is constant-time vector math.
  • Debug views: current character facing and animation remain observable scene outputs.
  • Correlation IDs: not applicable to synchronous local sandbox state.

Verification

  • Unit/contract: src/tools/verify_local_player_movement.gd covers forward, backward, strafe, rotated basis, acceleration, deceleration, sprint, flight, pitched camera basis, state reset, signed keyboard turn and invalid delta.
  • Capability: verify_player_movement_capabilities.gd covers three profile-ID cases, safe defaults, sprint/flight allow/reject behavior and two real scene compositions.
  • Boundary: verifier rejects Node/Input/Camera/ADT dependencies and movement integration/state remaining in the player scene.
  • Scene integration: verify_player_input.gd drives the asset-free player regression scene through forward motion and immediate flight toggle.
  • Fidelity evidence: translation defaults remain regression-locked against the pre-extraction sandbox. The compatibility keyboard turn default is pinned to TrinityCore 3.3.5 playerBaseMoveSpeed[MOVE_TURN_RATE]; that server contract does not alone prove original-client timing parity.
  • Performance budget: constant-time vector math, no per-tick I/O or scene lookup; MoveIntent allocation remains owned by the input package.

Extension points

  • Terrain/collision policy can consume displacement without changing input or velocity ownership.
  • A future server-aware predictor may replace this controller behind the scene composition boundary.
  • A typed movement snapshot may expose deterministic replay state when M08/M09 require it.
  • A future application profile can map to this narrow capability value without coupling the movement controller to the application shell.

Capability status

Capability Status Evidence Gap/next step
Scene-free local velocity state Implemented Pure controller and source boundary regression Integrate target checklist after review
Existing planar acceleration/speeds Implemented Exact numeric unit cases and real scene regression Compare with build 12340 before fidelity claim
Compatibility keyboard turn Implemented Pinned server-core value, signed pure cases and real-scene profile regression Direct original-client timing comparison remains required
Existing camera-relative free flight Implemented in RenderSandbox Pitched-basis, profile and toggle/reset cases Original-client flight semantics remain out of scope
Typed sprint/free-flight exclusion Implemented Pure and real-scene profile regressions Application shell must select profile explicitly later
Terrain height query Implemented Typed TerrainQuery and injected player regression Ground-snap policy remains scene-owned
Terrain collision movement policy Planned Height-only query does not model collision Add slopes/holes/collision later
Jump/fall/swim Planned M02/M09 roadmap Requires terrain/liquid and server contracts
Prediction/reconciliation Planned M08/M09 roadmap Requires movement snapshot/network contract

Known gaps and risks

  • Translation/acceleration defaults preserve the existing sandbox and are not original-client evidence.
  • Configuration is captured at scene _ready; runtime mutation of exported speed properties does not reconfigure an existing controller.
  • Ground snap remains in the player scene, but its ADT data access is now behind a replaceable TerrainQuery.
  • Runtime renderer scenes intentionally default to RenderSandbox; a future game-client composition must explicitly select Blizzlike335.
  • Blizzlike335 currently means only debug movement exclusion and must not be interpreted as evidence that remaining movement semantics are 1:1.
  • The module uses Godot math value types; a future engine-independent gameplay predictor may require scalar/domain movement contracts.

Source map

Path Responsibility
src/gameplay/movement/local_player_movement_controller.gd Owned velocity/flight state and deterministic integration
src/gameplay/movement/player_movement_capabilities.gd Immutable profile-to-debug-capability contract and safe defaults
src/gameplay/terrain/terrain_query.gd Replaceable ground-height input boundary
src/scenes/player/third_person_camera_rig.gd Camera-relative sandbox flight basis producer
src/scenes/character/character_animation_presenter.gd Locomotion presentation consumer behind the scene adapter
src/scenes/player/third_person_wow_controller.gd Composition, basis selection, displacement/terrain and presentation-state adapters
src/tools/verify_local_player_movement.gd Pure numeric, state-transition and dependency regression
src/tools/verify_player_movement_capabilities.gd Profile factory, allow/reject and real-scene regression
src/tests/scenes/player_input_regression.tscn Asset-free integrated player fixture
src/tools/verify_player_input.gd Input-to-movement scene regression
src/tests/fixtures/player_input_profile_335.json Turn-rate and selected binding provenance
  • ADR: none; this follows the existing M02 decomposition boundary.
  • Upstream/reference: targets/02-player-decomposition.md, targets/roadmap/04-gameplay-systems.md, docs/ARCHITECTURE.md.