Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d9c847c557 | |||
| 1bf5b526be | |||
| eb122d52a9 | |||
| e819eeb35b | |||
| 8776a6b362 | |||
| c3094c9413 |
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<!-- OPENWC_CLAIM:M02-GMP-INPUT-001:sindo-main-codex:2026-07-16 -->
|
<!-- OPENWC_CLAIM:M02-GMP-INPUT-001:sindo-main-codex:2026-07-16 -->
|
||||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-INPUT-001:6bd2e84 -->
|
<!-- OPENWC_HANDOFF:READY:M02-GMP-INPUT-001:6bd2e84 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-INPUT-001:14dead1 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# M02-GMP-INPUT-PROFILE-335-001 — WoW 3.3.5a input profile
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M02-GMP-INPUT-PROFILE-335-001:sindo-main-codex:2026-07-17 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M02
|
||||||
|
- Program: GMP/QAR
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m02-input-profile-335`
|
||||||
|
- Lease expires UTC: 2026-07-17
|
||||||
|
- Integrator: M02 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Add a profile-aware, remappable `Blizzlike335` input path whose default movement
|
||||||
|
bindings match the extracted build-12340 binding table while preserving the
|
||||||
|
existing `RenderSandbox` controls. Add deterministic turn intent and keyboard
|
||||||
|
turn consumption at the server-contract default turn rate.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Implement jump/fall/swim, network prediction or server reconciliation.
|
||||||
|
- Claim complete movement or camera parity from binding and turn-rate evidence.
|
||||||
|
- Persist user keybindings or build keybinding UI.
|
||||||
|
- Commit proprietary extracted client files.
|
||||||
|
- Mark M02 complete or edit its checklist/Evidence.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/tests/fixtures/player_input_profile_335.json`, this claim
|
||||||
|
- Shared/hotspots: `project.godot`, `src/domain/input/move_intent.gd`,
|
||||||
|
`src/gameplay/input/player_input_actions.gd`,
|
||||||
|
`src/gameplay/input/player_input_source.gd`,
|
||||||
|
`src/gameplay/movement/local_player_movement_controller.gd`,
|
||||||
|
`src/scenes/player/third_person_wow_controller.gd`,
|
||||||
|
`src/scenes/player/third_person_camera_rig.gd`, player/movement verifiers
|
||||||
|
and regression scene, `docs/modules/player-input.md`,
|
||||||
|
`docs/modules/local-player-movement.md`, `docs/modules/third-person-camera.md`
|
||||||
|
- Generated/ignored: local `.godot`, native DLL and proprietary extracted corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- `MoveIntent` gains an immutable, clamped signed turn axis.
|
||||||
|
- `PlayerInputSource` selects explicit sandbox or compatibility action sets at
|
||||||
|
construction and routes compatibility A/D to strafe while camera rotation is held.
|
||||||
|
- `LocalPlayerMovementController` converts turn intent into a deterministic yaw
|
||||||
|
delta without reading engine input or owning a scene transform.
|
||||||
|
- A sanitized JSON fixture records selected binding facts, source hashes and the
|
||||||
|
pinned TrinityCore 3.3.5 server-contract commit.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: merged sandbox capability package on master `c3094c9`.
|
||||||
|
- Blocks: final M02 integration audit and milestone closeout.
|
||||||
|
- External state: local extracted build-12340 binding files are evidence inputs
|
||||||
|
only; committed tests are reproducible without them.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Fixture schema/provenance and selected default-binding regression.
|
||||||
|
- Pure profile composition, A/D turn, Q/E strafe and camera-held A/D strafe cases.
|
||||||
|
- Deterministic turn-rate and controller integration regression.
|
||||||
|
- Existing M02 and repository gates remain required.
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Updated input/movement public API, profile matrix, data flow, sequence,
|
||||||
|
failure/recovery, fidelity provenance and source maps.
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names: `turn_axis`, `BLIZZLIKE_STRAFE_LEFT`, `TURN_LEFT`.
|
||||||
|
- Simplest approach: one profile selection at composition plus one scalar intent.
|
||||||
|
- Rejected complexity: input-context framework, command bus and persisted binding store.
|
||||||
|
- Unavoidable complexity: compatibility A/D behavior depends on camera-rotate state.
|
||||||
|
- Measured optimization evidence: not applicable.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: ready-for-review
|
||||||
|
- Done: added distinct remappable profiles, signed turn intent/controller path,
|
||||||
|
camera-yaw synchronization, sanitized provenance fixture, regression coverage
|
||||||
|
and updated module specifications
|
||||||
|
- Next: milestone integrator reviews and merges `8776a6b`
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M02-GMP-INPUT-PROFILE-335-001:8776a6b -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-INPUT-PROFILE-335-001:eb122d5 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Branch/commit: `work/sindo-main-codex/m02-input-profile-335` at `8776a6b`.
|
||||||
|
- Outcome: `RenderSandbox` retains A/D strafe and debug E/Q/Space/Shift behavior;
|
||||||
|
`Blizzlike335` now samples W/S and Up/Down forward/back, Q/E strafe, A/D and
|
||||||
|
Left/Right turn, with A/D routed to strafe while right-mouse rotation is active.
|
||||||
|
- Public contracts: `MoveIntent.turn_axis`; profile-selecting
|
||||||
|
`PlayerInputSource.new(profile_id)`; pure compatibility composition;
|
||||||
|
`LocalPlayerMovementController.calculate_yaw_delta_radians(...)`; camera yaw
|
||||||
|
synchronization after keyboard turn. Unknown input profiles fail closed to
|
||||||
|
`Blizzlike335`.
|
||||||
|
- Project configuration: four additive compatibility actions; existing action
|
||||||
|
names and sandbox mappings remain valid. W/S also gain the original Up/Down
|
||||||
|
secondary defaults.
|
||||||
|
- Verification: player input `actions=16 intent_cases=9 profiles=3 fixture=1
|
||||||
|
controller=1 regression_scene=3`; movement `cases=15 state_transitions=2`;
|
||||||
|
capabilities `profiles=3 sprint=2 flight=2 scenes=2`; terrain
|
||||||
|
`contract=4 interpolation=1 cache=1 failures=2`; camera
|
||||||
|
`state_cases=12 policy_cases=3`; presentation `appearance_cases=9
|
||||||
|
animation_cases=10`; coordinate boundaries `files=101`; StreamingFocus
|
||||||
|
`runtime_scenes=2 capture_tools=3`.
|
||||||
|
- Renderer: unified dry-run passed project/material/dedupe/cache/manifest/
|
||||||
|
calibration/coordinate/server-spawn gates and planned all seven checkpoints;
|
||||||
|
coordinate gate reported `files=103`. Missing proprietary GLB/DBC/ADT inputs
|
||||||
|
produced the expected diagnostics only.
|
||||||
|
- Repository gates: documentation `module_specs=8`; coordination
|
||||||
|
`fallback_claims=31`, with the same five unrelated expired M00 warnings.
|
||||||
|
- Fidelity: local build-12340 `DefaultBindings.wtf` SHA-256
|
||||||
|
`35FFA0E4E2356A13A0633A848F9E57ABE7DF9D8A16C2245E15D33013AD2F56A5`
|
||||||
|
and `Bindings.xml` SHA-256
|
||||||
|
`2E01276AFB7462F1417710F13B4BF1A456341F7F28CC8AE0D6D9C7442D7F6793`
|
||||||
|
were rechecked and selected facts sanitized into the committed fixture.
|
||||||
|
TrinityCore revision `2853a621d6af91a803787a2b8a509f4ce3e0300d` supplies the
|
||||||
|
3.141594 rad/s player server-contract turn rate. WoWee revision
|
||||||
|
`626243e937fb93965fa583a6507ed5a1aa7dda4b` supports right-mouse A/D routing.
|
||||||
|
- Fidelity limits: server turn rate and public reference routing are not direct
|
||||||
|
original-client timing/behavior proof. Jump, autorun and left-button
|
||||||
|
camera/select defaults are recorded but not implemented. This package does
|
||||||
|
not claim complete input, movement or camera parity.
|
||||||
|
- Migration/cache: no persisted settings or cache schema. Future saved bindings
|
||||||
|
must migrate stable action names; current project users receive additive actions.
|
||||||
|
- Documentation: updated input, movement and camera APIs, inputs/outputs, data
|
||||||
|
flow, sequence, ownership, failure/recovery, configuration, fidelity and source maps.
|
||||||
|
- Recommended merge order: merge after sandbox capabilities (already on master),
|
||||||
|
then run the final M02 integration/evidence audit without marking the target
|
||||||
|
done solely from this package.
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<!-- OPENWC_CLAIM:M02-GMP-MOVEMENT-001:sindo-main-codex:2026-07-16 -->
|
<!-- OPENWC_CLAIM:M02-GMP-MOVEMENT-001:sindo-main-codex:2026-07-16 -->
|
||||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-MOVEMENT-001:435e1c9 -->
|
<!-- OPENWC_HANDOFF:READY:M02-GMP-MOVEMENT-001:435e1c9 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-MOVEMENT-001:5206c42 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ explicit `RenderSandbox` profile and are rejected by `Blizzlike335`.
|
|||||||
- Blocked by:
|
- Blocked by:
|
||||||
|
|
||||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-SANDBOX-CAPABILITIES-001:742c415 -->
|
<!-- OPENWC_HANDOFF:READY:M02-GMP-SANDBOX-CAPABILITIES-001:742c415 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-SANDBOX-CAPABILITIES-001:c3094c9 -->
|
||||||
|
|
||||||
## Handoff
|
## Handoff
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<!-- OPENWC_CLAIM:M02-GMP-TERRAIN-QUERY-001:sindo-main-codex:2026-07-16 -->
|
<!-- OPENWC_CLAIM:M02-GMP-TERRAIN-QUERY-001:sindo-main-codex:2026-07-16 -->
|
||||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-TERRAIN-QUERY-001:a45d521 -->
|
<!-- OPENWC_HANDOFF:READY:M02-GMP-TERRAIN-QUERY-001:a45d521 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-TERRAIN-QUERY-001:f5bb64e -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# M02-QAR-INTEGRATOR-CLOSEOUT-001 — M02 milestone closeout
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M02-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex:2026-07-17 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M02
|
||||||
|
- Program: QAR
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m02-integrator-closeout`
|
||||||
|
- Lease expires UTC: 2026-07-17
|
||||||
|
- Integrator: sindo-main-codex, continuing the user-directed milestone workflow on 2026-07-15
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Audit every merged M02 package on current master, record final structural and
|
||||||
|
fidelity evidence, close M02 and activate M03 only when all target and repository
|
||||||
|
gates pass.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Change player, movement, terrain, camera, presentation or renderer behavior.
|
||||||
|
- Claim complete WoW 3.3.5a locomotion/camera parity.
|
||||||
|
- Implement recorded jump/fall/swim/fly, collision or timing gaps.
|
||||||
|
- Commit proprietary assets, generated UIDs, caches or user scene changes.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: M02/M03 target status, M02 final Evidence, all M02 claim acceptance markers
|
||||||
|
- Shared/hotspots: `targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, this claim
|
||||||
|
- Generated/ignored: local Godot imports, native DLL, renderer corpus and `user://` reports
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Runtime APIs, Input Map actions, fixture schemas and cache formats: unchanged
|
||||||
|
- Consumers: milestone sequence and future M03 renderer-facade packages
|
||||||
|
- Fidelity classification distinguishes direct build-12340 binding evidence,
|
||||||
|
server/reference compatibility evidence and explicitly unverified gaps
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: all seven M02 READY packages integrated through master `eb122d5`
|
||||||
|
- Blocks: M03 activation
|
||||||
|
- External state: private original-client files are hash/provenance inputs only
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: all M02 input/movement/capability/terrain/camera/presentation tests,
|
||||||
|
coordinate/streaming boundaries, unified renderer dry-run, documentation,
|
||||||
|
coordination and diff hygiene
|
||||||
|
- Fidelity evidence: build-12340 default binding hashes and selected facts;
|
||||||
|
pinned TrinityCore turn rate; public reference routing; explicit remaining gaps
|
||||||
|
- Performance: no runtime change; renderer baseline remains the performance guard
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- M02 final target Evidence/checklist and limitation statement
|
||||||
|
- M02 claim integration-acceptance markers
|
||||||
|
- Target index and roadmap transition to M03
|
||||||
|
- Existing API/module specifications and diagrams audited without behavioral edits
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: none beyond the closeout work-package ID
|
||||||
|
- Simplest approach: evidence/status-only integrator patch
|
||||||
|
- Rejected complexity: behavioral fixes or a new closeout framework
|
||||||
|
- Unavoidable complexity: explicit evidence classification prevents false 1:1 claims
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: ready-for-review
|
||||||
|
- Done: accepted seven M02 packages, reran post-merge gates, recorded complete
|
||||||
|
target Evidence, closed M02 and activated M03
|
||||||
|
- Next: merge closeout commit `1bf5b52` before starting an M03 implementation claim
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M02-QAR-INTEGRATOR-CLOSEOUT-001:1bf5b52 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `1bf5b52`.
|
||||||
|
- Branch: `work/sindo-main-codex/m02-integrator-closeout`.
|
||||||
|
- Outcome: M02 is `DONE` with all eight structural checklist items and complete
|
||||||
|
classified Evidence; M03 is the only `ACTIVE` target and the target index/
|
||||||
|
roadmap point to renderer-facade extraction.
|
||||||
|
- Integration audit: all seven READY implementation commits are ancestors of
|
||||||
|
merged master `eb122d5`; their claims now carry the corresponding
|
||||||
|
`OPENWC_INTEGRATION:ACCEPTED` merge markers.
|
||||||
|
- Verification: input `16/9/3/1/3`, movement `15` cases and `2` transitions,
|
||||||
|
capability `3` profiles and `2` scenes, terrain `4/1/1/2`, camera `12/3`,
|
||||||
|
presentation `9/10/3`, coordinate boundaries `103` files and StreamingFocus
|
||||||
|
`2` runtime scenes/`3` capture tools all passed. Unified renderer dry-run
|
||||||
|
passed project/material/dedupe/shutdown gates, manifest `7/7/7`, calibration
|
||||||
|
maximum error `0.000015`, server-spawn renderer and seven checkpoint plans.
|
||||||
|
- Repository gates: documentation passed with `8` module specs and `7` required
|
||||||
|
files. Coordination passed with one active target; `13` expired M00 claim
|
||||||
|
warnings are historical and unrelated to M02.
|
||||||
|
- Fidelity: direct build-12340 binding hashes/facts are preserved in the
|
||||||
|
sanitized fixture. TrinityCore turn rate and WoWee mouse routing remain
|
||||||
|
explicitly reference-only. Translation speeds/camera values are sandbox
|
||||||
|
regressions, and jump/fall/swim/fly parity remains unimplemented/unverified.
|
||||||
|
Debug sprint/free flight is negatively proven absent from `Blizzlike335`.
|
||||||
|
- Remaining risks: target Evidence carries exact follow-up scope for M09/M10/M12:
|
||||||
|
movement/camera timing, jump/fall/swim/fly, autorun/LMB semantics, active
|
||||||
|
collision, terrain/liquid policy, persisted bindings, server authority and
|
||||||
|
prediction/reconciliation. No complete `1:1` claim is made.
|
||||||
|
- Runtime/cache/migration: no behavioral change, dependency, cache schema or
|
||||||
|
persisted settings migration in closeout.
|
||||||
|
- Documentation: M02 target Evidence/checklist, seven acceptance markers,
|
||||||
|
target index and roadmap state updated. Existing input, movement, terrain,
|
||||||
|
camera and presentation API/module specs and diagrams passed the documentation gate.
|
||||||
@@ -90,6 +90,7 @@ no-collision sandbox baseline.
|
|||||||
- Blocked by:
|
- Blocked by:
|
||||||
|
|
||||||
<!-- OPENWC_HANDOFF:READY:M02-RND-CAMERA-001:8c1cf9b -->
|
<!-- OPENWC_HANDOFF:READY:M02-RND-CAMERA-001:8c1cf9b -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-RND-CAMERA-001:68e6f60 -->
|
||||||
|
|
||||||
## Handoff
|
## Handoff
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ components without changing the current sandbox presentation.
|
|||||||
- Blocked by:
|
- Blocked by:
|
||||||
|
|
||||||
<!-- OPENWC_HANDOFF:READY:M02-RND-CHARACTER-PRESENTATION-001:97bb53f -->
|
<!-- OPENWC_HANDOFF:READY:M02-RND-CHARACTER-PRESENTATION-001:97bb53f -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-RND-CHARACTER-PRESENTATION-001:8c71e46 -->
|
||||||
|
|
||||||
## Handoff
|
## Handoff
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,18 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Status | Implemented |
|
| Status | Implemented |
|
||||||
| Target/work package | M02 / M02-GMP-MOVEMENT-001, M02-GMP-SANDBOX-CAPABILITIES-001 and presentation consumers |
|
| 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 |
|
| Owners | Gameplay local movement state; scene composition owns the instance |
|
||||||
| Last verified | `742c415`, 2026-07-14 |
|
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
|
||||||
| Profiles/capabilities | `RenderSandbox` debug movement; `Blizzlike335` rejects debug sprint/flight |
|
| Profiles/capabilities | `RenderSandbox` debug movement; `Blizzlike335` selected bindings and keyboard turn with debug sprint/flight rejected |
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
Own current local velocity, acceleration and sandbox flight state independently
|
Own current local velocity, acceleration and sandbox flight state independently
|
||||||
of the player scene. Convert an immutable `MoveIntent` plus an explicit
|
of the player scene. Convert an immutable `MoveIntent` plus an explicit
|
||||||
Godot-world movement basis into deterministic per-tick displacement without
|
Godot-world movement basis into deterministic per-tick displacement and convert
|
||||||
querying terrain, cameras, input devices or visual assets.
|
signed turn intent into a yaw delta without querying terrain, cameras, input
|
||||||
|
devices or visual assets.
|
||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ flowchart LR
|
|||||||
Profile[Scene profile ID] --> Capabilities[PlayerMovementCapabilities]
|
Profile[Scene profile ID] --> Capabilities[PlayerMovementCapabilities]
|
||||||
Capabilities --> Movement
|
Capabilities --> Movement
|
||||||
Movement --> Displacement[Godot-world displacement]
|
Movement --> Displacement[Godot-world displacement]
|
||||||
|
Movement --> Yaw[Godot yaw delta radians]
|
||||||
Movement --> Velocity[Read-only velocity state]
|
Movement --> Velocity[Read-only velocity state]
|
||||||
Scene[ThirdPersonWowController] --> Basis
|
Scene[ThirdPersonWowController] --> Basis
|
||||||
Displacement --> Scene
|
Displacement --> Scene
|
||||||
@@ -68,6 +70,7 @@ Forbidden dependencies:
|
|||||||
| `movement_profile_id` | Read-only state | Effective capability profile ID | Controller lifetime | 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 |
|
| `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 |
|
| `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:
|
Constructor units:
|
||||||
|
|
||||||
@@ -79,6 +82,7 @@ Constructor units:
|
|||||||
| `flight_vertical_speed_units_per_second` | Sandbox vertical 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 |
|
| `acceleration_units_per_second_squared` | Velocity change per second |
|
||||||
| `sandbox_sprint_multiplier` | Dimensionless debug multiplier |
|
| `sandbox_sprint_multiplier` | Dimensionless debug multiplier |
|
||||||
|
| `keyboard_turn_speed_radians_per_second` | Positive keyboard yaw speed; defaults to PI rad/s |
|
||||||
|
|
||||||
## Inputs and outputs
|
## Inputs and outputs
|
||||||
|
|
||||||
@@ -90,6 +94,7 @@ Constructor units:
|
|||||||
| Input | Delta seconds | Godot physics loop | Movement controller | Scalar value | 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 |
|
| 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 | 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 | 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 |
|
| Output | Flight state | Movement controller | Basis/terrain scene adapter | Read-only boolean | Until next toggle |
|
||||||
|
|
||||||
@@ -104,6 +109,8 @@ Side effects:
|
|||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
Intent[MoveIntent axes] --> Target[Calculate target velocity]
|
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]
|
Basis[Player or camera-pivot Basis] --> Directions[Forward/right directions]
|
||||||
Directions --> Target
|
Directions --> Target
|
||||||
Config[Speeds and sprint multiplier] --> Target
|
Config[Speeds and sprint multiplier] --> Target
|
||||||
@@ -144,6 +151,9 @@ sequenceDiagram
|
|||||||
Input-->>Scene: MoveIntent
|
Input-->>Scene: MoveIntent
|
||||||
Scene->>Cap: map configured profile once
|
Scene->>Cap: map configured profile once
|
||||||
Scene->>Move: construct with immutable capabilities
|
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)
|
Scene->>Move: advance(intent, selected basis, delta)
|
||||||
Move-->>Scene: displacement
|
Move-->>Scene: displacement
|
||||||
Scene->>Scene: apply global position and ground snap
|
Scene->>Scene: apply global position and ground snap
|
||||||
@@ -190,6 +200,7 @@ controller resets velocity and flight state deterministically.
|
|||||||
| Debug sprint permission | Enabled | `RenderSandbox` only | Immutable | Allows sprint request multiplier |
|
| Debug sprint permission | Enabled | `RenderSandbox` only | Immutable | Allows sprint request multiplier |
|
||||||
| Debug free-flight permission | Enabled | `RenderSandbox` only | Immutable | Allows toggle and vertical movement |
|
| Debug free-flight permission | Enabled | `RenderSandbox` only | Immutable | Allows toggle and vertical movement |
|
||||||
| Compatibility-safe debug permissions | Disabled | `Blizzlike335` | Immutable | Ignores sprint/toggle requests |
|
| 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
|
## Persistence, cache and migration
|
||||||
|
|
||||||
@@ -208,15 +219,17 @@ separate versioned movement snapshot contract.
|
|||||||
|
|
||||||
- Unit/contract: `src/tools/verify_local_player_movement.gd` covers forward,
|
- Unit/contract: `src/tools/verify_local_player_movement.gd` covers forward,
|
||||||
backward, strafe, rotated basis, acceleration, deceleration, sprint, flight,
|
backward, strafe, rotated basis, acceleration, deceleration, sprint, flight,
|
||||||
pitched camera basis, state reset and invalid delta.
|
pitched camera basis, state reset, signed keyboard turn and invalid delta.
|
||||||
- Capability: `verify_player_movement_capabilities.gd` covers three profile-ID
|
- Capability: `verify_player_movement_capabilities.gd` covers three profile-ID
|
||||||
cases, safe defaults, sprint/flight allow/reject behavior and two real scene compositions.
|
cases, safe defaults, sprint/flight allow/reject behavior and two real scene compositions.
|
||||||
- Boundary: verifier rejects Node/Input/Camera/ADT dependencies and movement
|
- Boundary: verifier rejects Node/Input/Camera/ADT dependencies and movement
|
||||||
integration/state remaining in the player scene.
|
integration/state remaining in the player scene.
|
||||||
- Scene integration: `verify_player_input.gd` drives the asset-free player
|
- Scene integration: `verify_player_input.gd` drives the asset-free player
|
||||||
regression scene through forward motion and immediate flight toggle.
|
regression scene through forward motion and immediate flight toggle.
|
||||||
- Fidelity evidence: default values and formulas are regression-locked against
|
- Fidelity evidence: translation defaults remain regression-locked against the
|
||||||
the pre-extraction sandbox. No original-client comparison is claimed.
|
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;
|
- Performance budget: constant-time vector math, no per-tick I/O or scene lookup;
|
||||||
`MoveIntent` allocation remains owned by the input package.
|
`MoveIntent` allocation remains owned by the input package.
|
||||||
|
|
||||||
@@ -234,6 +247,7 @@ separate versioned movement snapshot contract.
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Scene-free local velocity state | Implemented | Pure controller and source boundary regression | Integrate target checklist after review |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 height query | Implemented | Typed `TerrainQuery` and injected player regression | Ground-snap policy remains scene-owned |
|
||||||
@@ -243,7 +257,7 @@ separate versioned movement snapshot contract.
|
|||||||
|
|
||||||
## Known gaps and risks
|
## Known gaps and risks
|
||||||
|
|
||||||
- Numeric defaults preserve the existing sandbox but are not original-client evidence.
|
- Translation/acceleration defaults preserve the existing sandbox and are not original-client evidence.
|
||||||
- Configuration is captured at scene `_ready`; runtime mutation of exported speed
|
- Configuration is captured at scene `_ready`; runtime mutation of exported speed
|
||||||
properties does not reconfigure an existing controller.
|
properties does not reconfigure an existing controller.
|
||||||
- Ground snap remains in the player scene, but its ADT data access is now behind
|
- Ground snap remains in the player scene, but its ADT data access is now behind
|
||||||
@@ -269,6 +283,7 @@ separate versioned movement snapshot contract.
|
|||||||
| `src/tools/verify_player_movement_capabilities.gd` | Profile factory, allow/reject and real-scene 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/tests/scenes/player_input_regression.tscn` | Asset-free integrated player fixture |
|
||||||
| `src/tools/verify_player_input.gd` | Input-to-movement scene regression |
|
| `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 |
|
||||||
|
|
||||||
## Related decisions and references
|
## Related decisions and references
|
||||||
|
|
||||||
|
|||||||
@@ -5,24 +5,25 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Status | Partial |
|
| Status | Partial |
|
||||||
| Target/work package | M02 / input, movement, camera and sandbox-capability consumer updates |
|
| Target/work package | M02 / M02-GMP-INPUT-001, M02-GMP-SANDBOX-CAPABILITIES-001, M02-GMP-INPUT-PROFILE-335-001 |
|
||||||
| Owners | Gameplay input boundary; `sindo-main-codex` for current package |
|
| Owners | Gameplay input boundary; `sindo-main-codex` for current package |
|
||||||
| Last verified | `742c415`, 2026-07-14 |
|
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
|
||||||
| Profiles/capabilities | Debug requests sampled in all profiles; execution allowed only by `RenderSandbox` movement capabilities |
|
| Profiles/capabilities | Distinct RenderSandbox and Blizzlike335 action layouts; debug execution remains RenderSandbox-only |
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
Convert remappable Godot Input Map actions into an immutable, scene-free
|
Convert remappable Godot Input Map actions into an immutable, scene-free
|
||||||
`MoveIntent` that can be consumed by the current player controller and a later
|
`MoveIntent` that can be consumed by the independently testable local movement
|
||||||
independently testable local movement controller.
|
controller. Select either the preserved renderer-sandbox layout or the verified
|
||||||
|
build-12340 movement bindings at the scene composition boundary.
|
||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
||||||
- Define authoritative server movement, prediction or reconciliation.
|
- Define authoritative server movement, prediction or reconciliation.
|
||||||
- Own character transform, terrain queries, camera state or animation state.
|
- Own character transform, terrain queries, camera state or animation state.
|
||||||
- Claim that the current W/S/A/D sandbox mapping exactly matches build 12340.
|
- Implement jump, autorun, left-button select/move or persisted binding settings.
|
||||||
- Suppress debug action sampling by physical device or UI context; execution is
|
- Remove process-wide debug actions; Blizzlike335 sampling ignores them and the
|
||||||
rejected at the typed movement capability boundary.
|
movement capability boundary also rejects directly constructed debug requests.
|
||||||
|
|
||||||
## Context and boundaries
|
## Context and boundaries
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ independently testable local movement controller.
|
|||||||
flowchart LR
|
flowchart LR
|
||||||
Devices[Keyboard and mouse] --> InputMap[Godot Input Map]
|
Devices[Keyboard and mouse] --> InputMap[Godot Input Map]
|
||||||
InputMap --> Source[PlayerInputSource]
|
InputMap --> Source[PlayerInputSource]
|
||||||
|
Profile[RenderSandbox or Blizzlike335] --> Source
|
||||||
Source --> Intent[MoveIntent]
|
Source --> Intent[MoveIntent]
|
||||||
Intent --> Movement[LocalPlayerMovementController]
|
Intent --> Movement[LocalPlayerMovementController]
|
||||||
Profile[PlayerMovementCapabilities] --> Movement
|
Profile[PlayerMovementCapabilities] --> Movement
|
||||||
@@ -53,11 +55,13 @@ Forbidden dependencies:
|
|||||||
|
|
||||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `MoveIntent` | Immutable value | Carries normalized forward, strafe and vertical axes plus the sandbox sprint request | Created and consumed on the main physics tick; caller retains reference | Constructor clamps axes; no I/O failure |
|
| `MoveIntent` | Immutable value | Carries normalized forward, strafe, vertical and turn axes plus the sandbox sprint request | Created and consumed on the main physics tick; caller retains reference | Constructor clamps axes; no I/O failure |
|
||||||
| `MoveIntent.has_translation()` | Query | Reports whether any translation axis is non-zero | Any thread while value remains immutable | None |
|
| `MoveIntent.has_translation()` | Query | Reports whether any translation axis is non-zero | Any thread while value remains immutable | None |
|
||||||
| `PlayerInputActions` | Constants | Provides stable action names shared by configuration and adapters | Process lifetime | Missing project actions are detected by verification |
|
| `PlayerInputActions` | Constants | Provides stable action names shared by configuration and adapters | Process lifetime | Missing project actions are detected by verification |
|
||||||
| `PlayerInputSource.sample_move_intent()` | Adapter method | Samples configured actions for one physics tick | Main thread; returned intent is independent of adapter lifetime | Missing actions resolve to zero strength and are a configuration defect |
|
| `PlayerInputSource.new(profile_id)` | Constructor | Selects sandbox or compatibility action layout once | Scene lifetime | Unknown IDs fail closed to `Blizzlike335` |
|
||||||
|
| `PlayerInputSource.sample_move_intent()` | Adapter method | Samples the selected action layout for one physics tick | Main thread; returned intent is independent of adapter lifetime | Missing actions resolve to zero strength and are a configuration defect |
|
||||||
| `PlayerInputSource.compose_move_intent(...)` | Pure factory | Clamps strengths, cancels opposites and normalizes diagonal planar input | Any thread with scalar inputs | None |
|
| `PlayerInputSource.compose_move_intent(...)` | Pure factory | Clamps strengths, cancels opposites and normalizes diagonal planar input | Any thread with scalar inputs | None |
|
||||||
|
| `PlayerInputSource.compose_blizzlike_335_move_intent(...)` | Pure factory | Composes Q/E strafe, A/D turn and right-mouse A/D strafe semantics | Any thread with scalar inputs | None |
|
||||||
|
|
||||||
## Inputs and outputs
|
## Inputs and outputs
|
||||||
|
|
||||||
@@ -67,6 +71,7 @@ Forbidden dependencies:
|
|||||||
| Input | Mouse action events | Godot `_unhandled_input` dispatch | `ThirdPersonCameraRig` through player dispatch | Engine-owned event | Current input dispatch only |
|
| Input | Mouse action events | Godot `_unhandled_input` dispatch | `ThirdPersonCameraRig` through player dispatch | Engine-owned event | Current input dispatch only |
|
||||||
| Output | `MoveIntent` | `PlayerInputSource` | `LocalPlayerMovementController` through scene composition | Immutable caller-held value | One physics tick |
|
| Output | `MoveIntent` | `PlayerInputSource` | `LocalPlayerMovementController` through scene composition | Immutable caller-held value | One physics tick |
|
||||||
| Output | Debug sprint request/vertical axes | `PlayerInputSource` | Capability-gated movement controller | Immutable intent fields | One physics tick |
|
| Output | Debug sprint request/vertical axes | `PlayerInputSource` | Capability-gated movement controller | Immutable intent fields | One physics tick |
|
||||||
|
| Output | Signed turn axis | `PlayerInputSource` | `LocalPlayerMovementController` | Immutable scalar | One physics tick |
|
||||||
| Output | Camera action names | `PlayerInputActions` | `ThirdPersonCameraRig` | Static constants | Process lifetime |
|
| Output | Camera action names | `PlayerInputActions` | `ThirdPersonCameraRig` | Static constants | Process lifetime |
|
||||||
|
|
||||||
Side effects:
|
Side effects:
|
||||||
@@ -83,6 +88,7 @@ flowchart LR
|
|||||||
Project[project.godot action defaults] --> Map[InputMap]
|
Project[project.godot action defaults] --> Map[InputMap]
|
||||||
Remap[User or editor remapping] --> Map
|
Remap[User or editor remapping] --> Map
|
||||||
Map -->|movement strengths| Source[PlayerInputSource]
|
Map -->|movement strengths| Source[PlayerInputSource]
|
||||||
|
Profile[Selected input profile] --> Source
|
||||||
Map -->|camera events| Camera[ThirdPersonCameraRig]
|
Map -->|camera events| Camera[ThirdPersonCameraRig]
|
||||||
Source -->|clamp cancel normalize| Intent[MoveIntent]
|
Source -->|clamp cancel normalize| Intent[MoveIntent]
|
||||||
Intent --> Consumer[LocalPlayerMovementController]
|
Intent --> Consumer[LocalPlayerMovementController]
|
||||||
@@ -95,9 +101,9 @@ flowchart LR
|
|||||||
The adapter is stateless. The scene composition root creates one
|
The adapter is stateless. The scene composition root creates one
|
||||||
`PlayerInputSource` and `LocalPlayerMovementController` in `_ready`; each physics
|
`PlayerInputSource` and `LocalPlayerMovementController` in `_ready`; each physics
|
||||||
tick produces a new immutable intent. Flight enabled/disabled state is owned by
|
tick produces a new immutable intent. Flight enabled/disabled state is owned by
|
||||||
the movement controller and is not hidden in the input adapter. Debug requests
|
the movement controller and is not hidden in the input adapter. RenderSandbox
|
||||||
remain data in the intent; the immutable movement capability value decides
|
retains debug requests in its intent; Blizzlike335 does not sample them, and the
|
||||||
whether they execute.
|
immutable movement capability also rejects directly constructed requests.
|
||||||
|
|
||||||
## Main sequence
|
## Main sequence
|
||||||
|
|
||||||
@@ -111,7 +117,7 @@ sequenceDiagram
|
|||||||
Engine->>Source: movement action strengths
|
Engine->>Source: movement action strengths
|
||||||
Source->>Intent: compose normalized axes and requests
|
Source->>Intent: compose normalized axes and requests
|
||||||
Source-->>Player: sample_move_intent()
|
Source-->>Player: sample_move_intent()
|
||||||
Player->>Move: advance(intent, selected basis, delta)
|
Player->>Move: calculate yaw delta, then advance(intent, selected basis, delta)
|
||||||
Move-->>Player: displacement and velocity state
|
Move-->>Player: displacement and velocity state
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -132,7 +138,7 @@ sequenceDiagram
|
|||||||
| Conflicting opposite actions | Pure composition | Strengths cancel deterministically | Covered by contract test | Release/remap one action |
|
| Conflicting opposite actions | Pure composition | Strengths cancel deterministically | Covered by contract test | Release/remap one action |
|
||||||
| Strength outside range | Pure composition | Value clamps to `[0, 1]` before axis calculation | Covered by contract test | Input adapter may be corrected independently |
|
| Strength outside range | Pure composition | Value clamps to `[0, 1]` before axis calculation | Covered by contract test | Input adapter may be corrected independently |
|
||||||
| Controller disabled/unloaded | Scene lifecycle | Sampling stops with physics processing | Existing scene lifecycle | Re-enable or recreate scene |
|
| Controller disabled/unloaded | Scene lifecycle | Sampling stops with physics processing | Existing scene lifecycle | Re-enable or recreate scene |
|
||||||
| Debug request in `Blizzlike335` | Movement capability check | Request is sampled but has no movement effect | Capability regression | Use `RenderSandbox` only when debug movement is intended |
|
| Debug request in `Blizzlike335` | Input profile and movement capability checks | Input adapter omits it; directly constructed request has no effect | Profile/capability regressions | Use `RenderSandbox` only when debug movement is intended |
|
||||||
|
|
||||||
There is no asynchronous operation to cancel and no persisted state to roll back.
|
There is no asynchronous operation to cancel and no persisted state to roll back.
|
||||||
|
|
||||||
@@ -140,8 +146,11 @@ There is no asynchronous operation to cancel and no persisted state to roll back
|
|||||||
|
|
||||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Forward/backward | Physical W/S | Current render sandbox | Yes, through Input Map | Produces signed forward axis |
|
| Forward/backward | Physical W/S and Up/Down | Both | Yes, through Input Map | Produces signed forward axis |
|
||||||
| Strafe left/right | Physical A/D | Current render sandbox | Yes | Produces signed right axis |
|
| Strafe left/right | Physical A/D | `RenderSandbox` | Yes | Preserves renderer baseline |
|
||||||
|
| Strafe left/right | Physical Q/E | `Blizzlike335` | Yes | Matches selected build-12340 defaults |
|
||||||
|
| Keyboard turn | Physical A/D and Left/Right | `Blizzlike335` | Yes | Produces signed turn axis |
|
||||||
|
| Camera-held A/D | Physical A/D while right mouse is held | `Blizzlike335` | Yes | Produces strafe and suppresses keyboard turn |
|
||||||
| Camera rotate | Right mouse button | Current render sandbox | Yes | Captures/releases mouse for orbit |
|
| Camera rotate | Right mouse button | Current render sandbox | Yes | Captures/releases mouse for orbit |
|
||||||
| Camera zoom | Mouse wheel | Current render sandbox | Yes | Adjusts current camera distance |
|
| Camera zoom | Mouse wheel | Current render sandbox | Yes | Adjusts current camera distance |
|
||||||
| Debug sprint | Shift | Executable only in `RenderSandbox` | Yes | Requests multiplier; `Blizzlike335` ignores it |
|
| Debug sprint | Shift | Executable only in `RenderSandbox` | Yes | Requests multiplier; `Blizzlike335` ignores it |
|
||||||
@@ -163,15 +172,20 @@ provide a settings migration once user settings are persisted.
|
|||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
- Unit/contract tests: `src/tools/verify_player_input.gd` validates all actions,
|
- Unit/contract tests: `src/tools/verify_player_input.gd` validates all actions,
|
||||||
default bindings, axis cancellation/clamping/normalization and debug request flags.
|
both profile defaults, axis cancellation/clamping/normalization, turn routing,
|
||||||
|
safe profile fallback, fixture provenance and debug request flags.
|
||||||
- Integration/E2E: `src/tests/scenes/player_input_regression.tscn` is loaded by
|
- Integration/E2E: `src/tests/scenes/player_input_regression.tscn` is loaded by
|
||||||
the verifier, which drives forward, camera-zoom and immediate sandbox-flight
|
the verifier, which drives sandbox forward/camera/flight plus compatibility
|
||||||
actions through the real scene controller and also rejects direct
|
keyboard turn and right-mouse strafe through real scene controllers.
|
||||||
physical-key polling.
|
|
||||||
- Profile integration: `verify_player_movement_capabilities.gd` drives sprint
|
- Profile integration: `verify_player_movement_capabilities.gd` drives sprint
|
||||||
and flight actions through real `RenderSandbox` and `Blizzlike335` scene instances.
|
and flight actions through real `RenderSandbox` and `Blizzlike335` scene instances.
|
||||||
- Fidelity evidence: current sandbox defaults retain W/S/A/D, E/Q, Shift, Space,
|
- Fidelity evidence: sanitized fixture `player_input_profile_335.json` pins the
|
||||||
right mouse and wheel bindings. This is observable-regression coverage only.
|
private build-12340 `DefaultBindings.wtf`/`Bindings.xml` hashes and selected
|
||||||
|
movement/camera commands. TrinityCore 3.3.5 revision
|
||||||
|
`2853a621d6af91a803787a2b8a509f4ce3e0300d` pins the server-contract player
|
||||||
|
turn rate. WoWee revision `626243e937fb93965fa583a6507ed5a1aa7dda4b`
|
||||||
|
supports right-mouse A/D strafe routing. Both are compatibility references,
|
||||||
|
not direct original-client timing/conditional-behavior proof.
|
||||||
- Performance budgets: no I/O, jobs or scene lookup per sample; one immutable
|
- Performance budgets: no I/O, jobs or scene lookup per sample; one immutable
|
||||||
intent allocation per physics tick.
|
intent allocation per physics tick.
|
||||||
- Manual diagnostics: run the Eastern Kingdoms scene and remap one movement
|
- Manual diagnostics: run the Eastern Kingdoms scene and remap one movement
|
||||||
@@ -192,13 +206,13 @@ provide a settings migration once user settings are persisted.
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Remappable current sandbox controls | Implemented | `verify_player_input.gd` action/default checks | Add persisted user binding settings later |
|
| Remappable current sandbox controls | Implemented | `verify_player_input.gd` action/default checks | Add persisted user binding settings later |
|
||||||
| `PlayerInputSource → MoveIntent` seam | Implemented | Pure composition and controller boundary checks | Local movement consumer is now extracted |
|
| `PlayerInputSource → MoveIntent` seam | Implemented | Pure composition and controller boundary checks | Local movement consumer is now extracted |
|
||||||
| Exact 3.3.5a default semantics | Unknown | No original-client binding fixture in this package | Capture build-12340 defaults and mouse-turn/strafe policy |
|
| Selected 3.3.5a movement defaults | Implemented | Pinned private-source hashes, sanitized selected bindings and profile regressions | Jump, autorun and left-button behavior remain planned |
|
||||||
| Compatibility-profile exclusion of sprint/flight | Implemented | Typed capability and two real-scene regressions | Application shell selection remains future work |
|
| Compatibility-profile exclusion of sprint/flight | Implemented | Typed capability and two real-scene regressions | Application shell selection remains future work |
|
||||||
| Jump/fall/swim/fly gameplay semantics | Planned | M02 target and gameplay roadmap | Implement after terrain/movement state extraction |
|
| Jump/fall/swim/fly gameplay semantics | Planned | M02 target and gameplay roadmap | Implement after terrain/movement state extraction |
|
||||||
|
|
||||||
## Known gaps and risks
|
## Known gaps and risks
|
||||||
|
|
||||||
- The defaults preserve the pre-M02 sandbox behavior, not verified original-client semantics.
|
- Only selected movement/camera binding facts are implemented; the fixture explicitly records unimplemented defaults.
|
||||||
- Input Map still exposes debug bindings process-wide; the movement consumer now
|
- Input Map still exposes debug bindings process-wide; the movement consumer now
|
||||||
rejects their effects outside `RenderSandbox`.
|
rejects their effects outside `RenderSandbox`.
|
||||||
- Camera state is extracted; active collision remains an explicit planned policy.
|
- Camera state is extracted; active collision remains an explicit planned policy.
|
||||||
@@ -213,10 +227,11 @@ provide a settings migration once user settings are persisted.
|
|||||||
| `src/gameplay/input/player_input_actions.gd` | Stable project action names |
|
| `src/gameplay/input/player_input_actions.gd` | Stable project action names |
|
||||||
| `src/gameplay/input/player_input_source.gd` | Engine Input Map adapter and pure composition |
|
| `src/gameplay/input/player_input_source.gd` | Engine Input Map adapter and pure composition |
|
||||||
| `src/gameplay/movement/local_player_movement_controller.gd` | Scene-free movement consumer |
|
| `src/gameplay/movement/local_player_movement_controller.gd` | Scene-free movement consumer |
|
||||||
| `src/gameplay/movement/player_movement_capabilities.gd` | Execution gate for sampled debug requests |
|
| `src/gameplay/movement/player_movement_capabilities.gd` | Defense-in-depth execution gate for debug requests |
|
||||||
| `src/scenes/player/third_person_camera_rig.gd` | Camera-action consumer and orbit/zoom state |
|
| `src/scenes/player/third_person_camera_rig.gd` | Camera-action consumer and orbit/zoom state |
|
||||||
| `src/scenes/player/third_person_wow_controller.gd` | Composition and input-event dispatcher |
|
| `src/scenes/player/third_person_wow_controller.gd` | Composition and input-event dispatcher |
|
||||||
| `src/tests/scenes/player_input_regression.tscn` | Asset-free controller movement/camera regression fixture |
|
| `src/tests/scenes/player_input_regression.tscn` | Asset-free controller movement/camera regression fixture |
|
||||||
|
| `src/tests/fixtures/player_input_profile_335.json` | Sanitized build-12340 binding and server turn-rate provenance |
|
||||||
| `src/tools/verify_player_input.gd` | Headless contract/integration regression |
|
| `src/tools/verify_player_input.gd` | Headless contract/integration regression |
|
||||||
| `src/tools/verify_player_movement_capabilities.gd` | Sandbox versus compatibility-profile integration regression |
|
| `src/tools/verify_player_movement_capabilities.gd` | Sandbox versus compatibility-profile integration regression |
|
||||||
|
|
||||||
@@ -224,4 +239,5 @@ provide a settings migration once user settings are persisted.
|
|||||||
|
|
||||||
- ADR: none; this implements the existing architecture and M02 seam.
|
- ADR: none; this implements the existing architecture and M02 seam.
|
||||||
- Upstream/reference: `targets/02-player-decomposition.md`,
|
- Upstream/reference: `targets/02-player-decomposition.md`,
|
||||||
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`.
|
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`, and the
|
||||||
|
pinned sources recorded in `player_input_profile_335.json`.
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Status | Implemented |
|
| Status | Implemented |
|
||||||
| Target/work package | M02 / M02-RND-CAMERA-001 |
|
| Target/work package | M02 / M02-RND-CAMERA-001 and M02-GMP-INPUT-PROFILE-335-001 consumer update |
|
||||||
| Owners | Camera presentation state and CameraPivot/Camera3D lifecycle |
|
| Owners | Camera presentation state and CameraPivot/Camera3D lifecycle |
|
||||||
| Last verified | `8c1cf9b`, 2026-07-14 |
|
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
|
||||||
| Profiles/capabilities | Current sandbox orbit/zoom; identity collision policy |
|
| Profiles/capabilities | Current sandbox orbit/zoom; identity collision policy |
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
@@ -60,6 +60,7 @@ Forbidden dependencies:
|
|||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `ThirdPersonCameraRig` | `Node3D` component | Owns orbit/zoom state on CameraPivot | Main thread; scene-owned | Reports missing Camera3D |
|
| `ThirdPersonCameraRig` | `Node3D` component | Owns orbit/zoom state on CameraPivot | Main thread; scene-owned | Reports missing Camera3D |
|
||||||
| `initialize_for_character(character_body)` | Composition method | Attaches yaw target and captures initial character yaw | Main thread after scene ready | Null leaves yaw target absent |
|
| `initialize_for_character(character_body)` | Composition method | Attaches yaw target and captures initial character yaw | Main thread after scene ready | Null leaves yaw target absent |
|
||||||
|
| `synchronize_yaw_from_character()` | Composition method | Refreshes orbit yaw after keyboard movement turns the character | Main physics thread | Missing character leaves yaw unchanged |
|
||||||
| `handle_camera_input(event)` | Input adapter | Applies capture/release/zoom/orbit actions | Main input thread | Returns whether camera consumed state |
|
| `handle_camera_input(event)` | Input adapter | Applies capture/release/zoom/orbit actions | Main input thread | Returns whether camera consumed state |
|
||||||
| `set_collision_policy(policy)` | Composition method | Replaces distance resolution policy | Main thread | Null rejected; old policy retained |
|
| `set_collision_policy(policy)` | Composition method | Replaces distance resolution policy | Main thread | Null rejected; old policy retained |
|
||||||
| `refresh_camera_transform()` | Presentation update | Applies pivot pose and resolved camera distance | Main/physics thread | Missing camera leaves state but no Camera3D mutation |
|
| `refresh_camera_transform()` | Presentation update | Applies pivot pose and resolved camera distance | Main/physics thread | Missing camera leaves state but no Camera3D mutation |
|
||||||
@@ -77,6 +78,7 @@ Read-only rig state: `yaw_radians`, `pitch_radians`,
|
|||||||
| Input | Camera action `InputEvent` | Godot/player dispatch | Camera rig | Engine-owned event | One dispatch |
|
| Input | Camera action `InputEvent` | Godot/player dispatch | Camera rig | Engine-owned event | One dispatch |
|
||||||
| Input | Mouse relative pixels | Godot input | Camera rig | Engine-owned event | One dispatch |
|
| Input | Mouse relative pixels | Godot input | Camera rig | Engine-owned event | One dispatch |
|
||||||
| Input | Character `Node3D` | Player composition | Camera rig | Scene-owned reference | Rig scene lifetime |
|
| Input | Character `Node3D` | Player composition | Camera rig | Scene-owned reference | Rig scene lifetime |
|
||||||
|
| Input | Keyboard-turn synchronization | Player scene after yaw application | Camera rig | Synchronous notification | Current physics tick |
|
||||||
| Input | Collision policy | Composition/test | Camera rig | Rig-held RefCounted | Until replacement |
|
| Input | Collision policy | Composition/test | Camera rig | Rig-held RefCounted | Until replacement |
|
||||||
| Output | Character yaw | Camera rig | Player transform | Scene mutation | Orbit event |
|
| Output | Character yaw | Camera rig | Player transform | Scene mutation | Orbit event |
|
||||||
| Output | Pivot pitch/height | Camera rig | CameraPivot | Scene mutation | Refresh |
|
| Output | Pivot pitch/height | Camera rig | CameraPivot | Scene mutation | Refresh |
|
||||||
@@ -194,6 +196,8 @@ tools require no path migration.
|
|||||||
|
|
||||||
- State regression: initial yaw/pitch/distance/height/far plane, capture/release,
|
- State regression: initial yaw/pitch/distance/height/far plane, capture/release,
|
||||||
mouse orbit, character yaw, pitch clamps and zoom clamps.
|
mouse orbit, character yaw, pitch clamps and zoom clamps.
|
||||||
|
- Input-profile integration verifies keyboard yaw synchronization, preventing a
|
||||||
|
later right-mouse orbit from restoring stale character yaw.
|
||||||
- Policy regression: fixed-distance test policy changes Camera3D Z; identity
|
- Policy regression: fixed-distance test policy changes Camera3D Z; identity
|
||||||
policy restores requested distance; baseline policy contains no physics query.
|
policy restores requested distance; baseline policy contains no physics query.
|
||||||
- Scene integration: Eastern Kingdoms, Kalimdor and asset-free regression scenes
|
- Scene integration: Eastern Kingdoms, Kalimdor and asset-free regression scenes
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ enabled=PackedStringArray("res://addons/mpq_extractor/plugin.cfg")
|
|||||||
openwc_player_move_forward={
|
openwc_player_move_forward={
|
||||||
"deadzone": 0.5,
|
"deadzone": 0.5,
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
openwc_player_move_backward={
|
openwc_player_move_backward={
|
||||||
"deadzone": 0.5,
|
"deadzone": 0.5,
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
openwc_player_strafe_left={
|
openwc_player_strafe_left={
|
||||||
@@ -41,6 +43,28 @@ openwc_player_strafe_right={
|
|||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
openwc_player_blizzlike_strafe_left={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
openwc_player_blizzlike_strafe_right={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
openwc_player_turn_left={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
openwc_player_turn_right={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
openwc_player_debug_fly_up={
|
openwc_player_debug_fly_up={
|
||||||
"deadzone": 0.5,
|
"deadzone": 0.5,
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||||
|
|||||||
@@ -21,10 +21,16 @@ var is_sprint_requested: bool:
|
|||||||
get:
|
get:
|
||||||
return _is_sprint_requested
|
return _is_sprint_requested
|
||||||
|
|
||||||
|
## Signed keyboard turn request: left is negative and right is positive.
|
||||||
|
var turn_axis: float:
|
||||||
|
get:
|
||||||
|
return _turn_axis
|
||||||
|
|
||||||
var _forward_axis: float
|
var _forward_axis: float
|
||||||
var _strafe_axis: float
|
var _strafe_axis: float
|
||||||
var _vertical_axis: float
|
var _vertical_axis: float
|
||||||
var _is_sprint_requested: bool
|
var _is_sprint_requested: bool
|
||||||
|
var _turn_axis: float
|
||||||
|
|
||||||
|
|
||||||
## Creates one frame's movement request without retaining mutable input state.
|
## Creates one frame's movement request without retaining mutable input state.
|
||||||
@@ -32,12 +38,14 @@ func _init(
|
|||||||
forward_axis_value: float = 0.0,
|
forward_axis_value: float = 0.0,
|
||||||
strafe_axis_value: float = 0.0,
|
strafe_axis_value: float = 0.0,
|
||||||
vertical_axis_value: float = 0.0,
|
vertical_axis_value: float = 0.0,
|
||||||
sprint_requested: bool = false
|
sprint_requested: bool = false,
|
||||||
|
turn_axis_value: float = 0.0
|
||||||
) -> void:
|
) -> void:
|
||||||
_forward_axis = clampf(forward_axis_value, -1.0, 1.0)
|
_forward_axis = clampf(forward_axis_value, -1.0, 1.0)
|
||||||
_strafe_axis = clampf(strafe_axis_value, -1.0, 1.0)
|
_strafe_axis = clampf(strafe_axis_value, -1.0, 1.0)
|
||||||
_vertical_axis = clampf(vertical_axis_value, -1.0, 1.0)
|
_vertical_axis = clampf(vertical_axis_value, -1.0, 1.0)
|
||||||
_is_sprint_requested = sprint_requested
|
_is_sprint_requested = sprint_requested
|
||||||
|
_turn_axis = clampf(turn_axis_value, -1.0, 1.0)
|
||||||
|
|
||||||
|
|
||||||
## Returns true when the intent requests planar or vertical translation.
|
## Returns true when the intent requests planar or vertical translation.
|
||||||
|
|||||||
@@ -2,13 +2,17 @@ class_name PlayerInputActions
|
|||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
## Stable Input Map action names consumed by the runtime player input adapter.
|
## Stable Input Map action names consumed by the runtime player input adapter.
|
||||||
## Defaults preserve the current render-sandbox controls; users may remap every
|
## Separate translation actions preserve the renderer sandbox while allowing
|
||||||
## action through Godot's Input Map without changing gameplay code.
|
## Blizzlike335 defaults to match build-12340 movement bindings.
|
||||||
|
|
||||||
const MOVE_FORWARD := &"openwc_player_move_forward"
|
const MOVE_FORWARD := &"openwc_player_move_forward"
|
||||||
const MOVE_BACKWARD := &"openwc_player_move_backward"
|
const MOVE_BACKWARD := &"openwc_player_move_backward"
|
||||||
const STRAFE_LEFT := &"openwc_player_strafe_left"
|
const STRAFE_LEFT := &"openwc_player_strafe_left"
|
||||||
const STRAFE_RIGHT := &"openwc_player_strafe_right"
|
const STRAFE_RIGHT := &"openwc_player_strafe_right"
|
||||||
|
const BLIZZLIKE_STRAFE_LEFT := &"openwc_player_blizzlike_strafe_left"
|
||||||
|
const BLIZZLIKE_STRAFE_RIGHT := &"openwc_player_blizzlike_strafe_right"
|
||||||
|
const TURN_LEFT := &"openwc_player_turn_left"
|
||||||
|
const TURN_RIGHT := &"openwc_player_turn_right"
|
||||||
const DEBUG_FLY_UP := &"openwc_player_debug_fly_up"
|
const DEBUG_FLY_UP := &"openwc_player_debug_fly_up"
|
||||||
const DEBUG_FLY_DOWN := &"openwc_player_debug_fly_down"
|
const DEBUG_FLY_DOWN := &"openwc_player_debug_fly_down"
|
||||||
const DEBUG_SPRINT := &"openwc_player_debug_sprint"
|
const DEBUG_SPRINT := &"openwc_player_debug_sprint"
|
||||||
@@ -23,6 +27,10 @@ const REQUIRED_ACTIONS: Array[StringName] = [
|
|||||||
MOVE_BACKWARD,
|
MOVE_BACKWARD,
|
||||||
STRAFE_LEFT,
|
STRAFE_LEFT,
|
||||||
STRAFE_RIGHT,
|
STRAFE_RIGHT,
|
||||||
|
BLIZZLIKE_STRAFE_LEFT,
|
||||||
|
BLIZZLIKE_STRAFE_RIGHT,
|
||||||
|
TURN_LEFT,
|
||||||
|
TURN_RIGHT,
|
||||||
DEBUG_FLY_UP,
|
DEBUG_FLY_UP,
|
||||||
DEBUG_FLY_DOWN,
|
DEBUG_FLY_DOWN,
|
||||||
DEBUG_SPRINT,
|
DEBUG_SPRINT,
|
||||||
|
|||||||
@@ -5,21 +5,86 @@ extends RefCounted
|
|||||||
## This adapter owns no movement state and may be replaced independently of the
|
## This adapter owns no movement state and may be replaced independently of the
|
||||||
## local movement controller.
|
## local movement controller.
|
||||||
|
|
||||||
|
const RENDER_SANDBOX_PROFILE_ID := &"RenderSandbox"
|
||||||
|
const BLIZZLIKE_335_PROFILE_ID := &"Blizzlike335"
|
||||||
|
|
||||||
## Samples the current Input singleton state for one physics tick.
|
var input_profile_id: StringName:
|
||||||
func sample_move_intent() -> MoveIntent:
|
get:
|
||||||
return compose_move_intent(
|
return _input_profile_id
|
||||||
Input.get_action_strength(PlayerInputActions.MOVE_FORWARD),
|
|
||||||
Input.get_action_strength(PlayerInputActions.MOVE_BACKWARD),
|
var _input_profile_id: StringName
|
||||||
Input.get_action_strength(PlayerInputActions.STRAFE_LEFT),
|
|
||||||
Input.get_action_strength(PlayerInputActions.STRAFE_RIGHT),
|
|
||||||
Input.get_action_strength(PlayerInputActions.DEBUG_FLY_UP),
|
## Selects one supported action layout. Unknown profiles fail closed to Blizzlike335.
|
||||||
Input.get_action_strength(PlayerInputActions.DEBUG_FLY_DOWN),
|
func _init(requested_profile_id: StringName = BLIZZLIKE_335_PROFILE_ID) -> void:
|
||||||
Input.is_action_pressed(PlayerInputActions.DEBUG_SPRINT)
|
_input_profile_id = (
|
||||||
|
RENDER_SANDBOX_PROFILE_ID
|
||||||
|
if requested_profile_id == RENDER_SANDBOX_PROFILE_ID
|
||||||
|
else BLIZZLIKE_335_PROFILE_ID
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
## Builds a normalized intent from action strengths for deterministic tests and
|
## Samples the current Input singleton state for one physics tick.
|
||||||
|
func sample_move_intent() -> MoveIntent:
|
||||||
|
if _input_profile_id == RENDER_SANDBOX_PROFILE_ID:
|
||||||
|
return compose_move_intent(
|
||||||
|
Input.get_action_strength(PlayerInputActions.MOVE_FORWARD),
|
||||||
|
Input.get_action_strength(PlayerInputActions.MOVE_BACKWARD),
|
||||||
|
Input.get_action_strength(PlayerInputActions.STRAFE_LEFT),
|
||||||
|
Input.get_action_strength(PlayerInputActions.STRAFE_RIGHT),
|
||||||
|
Input.get_action_strength(PlayerInputActions.DEBUG_FLY_UP),
|
||||||
|
Input.get_action_strength(PlayerInputActions.DEBUG_FLY_DOWN),
|
||||||
|
Input.is_action_pressed(PlayerInputActions.DEBUG_SPRINT)
|
||||||
|
)
|
||||||
|
return compose_blizzlike_335_move_intent(
|
||||||
|
Input.get_action_strength(PlayerInputActions.MOVE_FORWARD),
|
||||||
|
Input.get_action_strength(PlayerInputActions.MOVE_BACKWARD),
|
||||||
|
Input.get_action_strength(PlayerInputActions.BLIZZLIKE_STRAFE_LEFT),
|
||||||
|
Input.get_action_strength(PlayerInputActions.BLIZZLIKE_STRAFE_RIGHT),
|
||||||
|
Input.get_action_strength(PlayerInputActions.TURN_LEFT),
|
||||||
|
Input.get_action_strength(PlayerInputActions.TURN_RIGHT),
|
||||||
|
Input.is_action_pressed(PlayerInputActions.CAMERA_ROTATE)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
## Builds the compatibility layout: Q/E always strafe; A/D turn unless
|
||||||
|
## right-mouse camera rotation is active, when A/D also strafe. Default bindings
|
||||||
|
## come from build 12340; conditional routing is reference-implementation evidence.
|
||||||
|
static func compose_blizzlike_335_move_intent(
|
||||||
|
forward_strength: float,
|
||||||
|
backward_strength: float,
|
||||||
|
strafe_left_strength: float,
|
||||||
|
strafe_right_strength: float,
|
||||||
|
turn_left_strength: float,
|
||||||
|
turn_right_strength: float,
|
||||||
|
camera_rotate_active: bool
|
||||||
|
) -> MoveIntent:
|
||||||
|
var effective_strafe_left := strafe_left_strength
|
||||||
|
var effective_strafe_right := strafe_right_strength
|
||||||
|
var turn_axis := clampf(turn_right_strength, 0.0, 1.0) - clampf(turn_left_strength, 0.0, 1.0)
|
||||||
|
if camera_rotate_active:
|
||||||
|
effective_strafe_left += turn_left_strength
|
||||||
|
effective_strafe_right += turn_right_strength
|
||||||
|
turn_axis = 0.0
|
||||||
|
var move_intent := compose_move_intent(
|
||||||
|
forward_strength,
|
||||||
|
backward_strength,
|
||||||
|
effective_strafe_left,
|
||||||
|
effective_strafe_right,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
return MoveIntent.new(
|
||||||
|
move_intent.forward_axis,
|
||||||
|
move_intent.strafe_axis,
|
||||||
|
0.0,
|
||||||
|
false,
|
||||||
|
turn_axis
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
## Builds a normalized sandbox intent from action strengths for deterministic tests and
|
||||||
## alternative input adapters. Strengths outside `[0, 1]` are clamped.
|
## alternative input adapters. Strengths outside `[0, 1]` are clamped.
|
||||||
static func compose_move_intent(
|
static func compose_move_intent(
|
||||||
forward_strength: float,
|
forward_strength: float,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ var _strafe_speed_units_per_second: float
|
|||||||
var _flight_vertical_speed_units_per_second: float
|
var _flight_vertical_speed_units_per_second: float
|
||||||
var _acceleration_units_per_second_squared: float
|
var _acceleration_units_per_second_squared: float
|
||||||
var _sandbox_sprint_multiplier: float
|
var _sandbox_sprint_multiplier: float
|
||||||
|
var _keyboard_turn_speed_radians_per_second: float
|
||||||
var _movement_capabilities: PlayerMovementCapabilities
|
var _movement_capabilities: PlayerMovementCapabilities
|
||||||
var _godot_world_velocity_units_per_second := Vector3.ZERO
|
var _godot_world_velocity_units_per_second := Vector3.ZERO
|
||||||
var _is_flight_enabled := false
|
var _is_flight_enabled := false
|
||||||
@@ -38,7 +39,8 @@ func _init(
|
|||||||
flight_vertical_speed_units_per_second: float,
|
flight_vertical_speed_units_per_second: float,
|
||||||
acceleration_units_per_second_squared: float,
|
acceleration_units_per_second_squared: float,
|
||||||
sandbox_sprint_multiplier: float,
|
sandbox_sprint_multiplier: float,
|
||||||
movement_capabilities: PlayerMovementCapabilities = null
|
movement_capabilities: PlayerMovementCapabilities = null,
|
||||||
|
keyboard_turn_speed_radians_per_second: float = PI
|
||||||
) -> void:
|
) -> void:
|
||||||
_run_speed_units_per_second = run_speed_units_per_second
|
_run_speed_units_per_second = run_speed_units_per_second
|
||||||
_backward_speed_units_per_second = backward_speed_units_per_second
|
_backward_speed_units_per_second = backward_speed_units_per_second
|
||||||
@@ -46,6 +48,10 @@ func _init(
|
|||||||
_flight_vertical_speed_units_per_second = flight_vertical_speed_units_per_second
|
_flight_vertical_speed_units_per_second = flight_vertical_speed_units_per_second
|
||||||
_acceleration_units_per_second_squared = acceleration_units_per_second_squared
|
_acceleration_units_per_second_squared = acceleration_units_per_second_squared
|
||||||
_sandbox_sprint_multiplier = sandbox_sprint_multiplier
|
_sandbox_sprint_multiplier = sandbox_sprint_multiplier
|
||||||
|
_keyboard_turn_speed_radians_per_second = maxf(
|
||||||
|
keyboard_turn_speed_radians_per_second,
|
||||||
|
0.0
|
||||||
|
)
|
||||||
_movement_capabilities = (
|
_movement_capabilities = (
|
||||||
movement_capabilities
|
movement_capabilities
|
||||||
if movement_capabilities != null
|
if movement_capabilities != null
|
||||||
@@ -53,6 +59,16 @@ func _init(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
## Returns the Godot yaw delta for one signed keyboard-turn request.
|
||||||
|
## Positive intent turns right, which is negative rotation around Godot +Y.
|
||||||
|
func calculate_yaw_delta_radians(move_intent: MoveIntent, delta_seconds: float) -> float:
|
||||||
|
return (
|
||||||
|
-move_intent.turn_axis
|
||||||
|
* _keyboard_turn_speed_radians_per_second
|
||||||
|
* maxf(delta_seconds, 0.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
## Toggles sandbox free flight and clears velocity when the profile permits it.
|
## Toggles sandbox free flight and clears velocity when the profile permits it.
|
||||||
## Returns the resulting state; rejected compatibility-profile requests return false.
|
## Returns the resulting state; rejected compatibility-profile requests return false.
|
||||||
func toggle_sandbox_flight() -> bool:
|
func toggle_sandbox_flight() -> bool:
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ func initialize_for_character(character_body: Node3D) -> void:
|
|||||||
refresh_camera_transform()
|
refresh_camera_transform()
|
||||||
|
|
||||||
|
|
||||||
|
## Synchronizes orbit yaw after keyboard turning changes the character transform.
|
||||||
|
func synchronize_yaw_from_character() -> void:
|
||||||
|
if _character_body != null:
|
||||||
|
_yaw_radians = _character_body.rotation.y
|
||||||
|
|
||||||
|
|
||||||
## Replaces camera-distance collision behavior and immediately refreshes the camera.
|
## Replaces camera-distance collision behavior and immediately refreshes the camera.
|
||||||
## Passing null is rejected and preserves the current policy.
|
## Passing null is rejected and preserves the current policy.
|
||||||
func set_collision_policy(collision_policy: CameraCollisionPolicy) -> void:
|
func set_collision_policy(collision_policy: CameraCollisionPolicy) -> void:
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ const ADT_TERRAIN_QUERY_SCRIPT := preload("res://src/gameplay/terrain/adt_terrai
|
|||||||
@export var sprint_multiplier: float = 6.0
|
@export var sprint_multiplier: float = 6.0
|
||||||
@export var flight_vertical_speed: float = 7.0
|
@export var flight_vertical_speed: float = 7.0
|
||||||
@export var acceleration: float = 28.0
|
@export var acceleration: float = 28.0
|
||||||
|
## TrinityCore 3.3.5 player MOVE_TURN_RATE default, in radians per second.
|
||||||
|
@export var keyboard_turn_speed_radians_per_second: float = 3.141594
|
||||||
## Local composition profile. RenderSandbox preserves debug sprint/free flight;
|
## Local composition profile. RenderSandbox preserves debug sprint/free flight;
|
||||||
## Blizzlike335 rejects them but does not yet claim complete movement parity.
|
## Blizzlike335 rejects them but does not yet claim complete movement parity.
|
||||||
@export_enum("RenderSandbox", "Blizzlike335") var movement_profile_id: String = "RenderSandbox"
|
@export_enum("RenderSandbox", "Blizzlike335") var movement_profile_id: String = "RenderSandbox"
|
||||||
@@ -50,7 +52,7 @@ func set_terrain_query(terrain_query: TerrainQuery) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_player_input_source = PLAYER_INPUT_SOURCE_SCRIPT.new()
|
_player_input_source = PLAYER_INPUT_SOURCE_SCRIPT.new(StringName(movement_profile_id))
|
||||||
if _terrain_query == null:
|
if _terrain_query == null:
|
||||||
_terrain_query = ADT_TERRAIN_QUERY_SCRIPT.new(extracted_dir, map_name)
|
_terrain_query = ADT_TERRAIN_QUERY_SCRIPT.new(extracted_dir, map_name)
|
||||||
_local_movement_controller = LOCAL_PLAYER_MOVEMENT_CONTROLLER_SCRIPT.new(
|
_local_movement_controller = LOCAL_PLAYER_MOVEMENT_CONTROLLER_SCRIPT.new(
|
||||||
@@ -60,7 +62,8 @@ func _ready() -> void:
|
|||||||
flight_vertical_speed,
|
flight_vertical_speed,
|
||||||
acceleration,
|
acceleration,
|
||||||
sprint_multiplier,
|
sprint_multiplier,
|
||||||
PLAYER_MOVEMENT_CAPABILITIES_SCRIPT.for_profile_id(StringName(movement_profile_id))
|
PLAYER_MOVEMENT_CAPABILITIES_SCRIPT.for_profile_id(StringName(movement_profile_id)),
|
||||||
|
keyboard_turn_speed_radians_per_second
|
||||||
)
|
)
|
||||||
_camera_rig = get_node_or_null(camera_pivot_path) as ThirdPersonCameraRig
|
_camera_rig = get_node_or_null(camera_pivot_path) as ThirdPersonCameraRig
|
||||||
_appearance_presenter = get_node_or_null(visual_path) as CharacterAppearancePresenter
|
_appearance_presenter = get_node_or_null(visual_path) as CharacterAppearancePresenter
|
||||||
@@ -96,6 +99,14 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
var move_intent := _player_input_source.sample_move_intent()
|
var move_intent := _player_input_source.sample_move_intent()
|
||||||
|
var yaw_delta_radians := _local_movement_controller.calculate_yaw_delta_radians(
|
||||||
|
move_intent,
|
||||||
|
delta
|
||||||
|
)
|
||||||
|
if not is_zero_approx(yaw_delta_radians):
|
||||||
|
rotation.y += yaw_delta_radians
|
||||||
|
if _camera_rig != null:
|
||||||
|
_camera_rig.synchronize_yaw_from_character()
|
||||||
var godot_world_movement_basis := global_basis
|
var godot_world_movement_basis := global_basis
|
||||||
if _local_movement_controller.is_flight_enabled and _camera_rig != null:
|
if _local_movement_controller.is_flight_enabled and _camera_rig != null:
|
||||||
godot_world_movement_basis = _camera_rig.godot_world_flight_movement_basis()
|
godot_world_movement_basis = _camera_rig.godot_world_flight_movement_basis()
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"profile_id": "Blizzlike335",
|
||||||
|
"sources": {
|
||||||
|
"original_client_default_bindings": {
|
||||||
|
"status": "verified_private_metadata",
|
||||||
|
"build": 12340,
|
||||||
|
"path": "WTF/DefaultBindings.wtf",
|
||||||
|
"sha256": "35FFA0E4E2356A13A0633A848F9E57ABE7DF9D8A16C2245E15D33013AD2F56A5",
|
||||||
|
"distribution": "Selected binding facts only; proprietary file remains outside Git."
|
||||||
|
},
|
||||||
|
"original_client_binding_commands": {
|
||||||
|
"status": "verified_private_metadata",
|
||||||
|
"build": 12340,
|
||||||
|
"path": "Interface/FrameXML/Bindings.xml",
|
||||||
|
"sha256": "2E01276AFB7462F1417710F13B4BF1A456341F7F28CC8AE0D6D9C7442D7F6793",
|
||||||
|
"distribution": "Hash and selected command names only; proprietary file remains outside Git."
|
||||||
|
},
|
||||||
|
"trinitycore_player_turn_rate": {
|
||||||
|
"status": "verified_public_source",
|
||||||
|
"repository": "TrinityCore/TrinityCore",
|
||||||
|
"revision": "2853a621d6af91a803787a2b8a509f4ce3e0300d",
|
||||||
|
"path": "src/server/game/Entities/Unit/Unit.cpp",
|
||||||
|
"url": "https://raw.githubusercontent.com/TrinityCore/TrinityCore/2853a621d6af91a803787a2b8a509f4ce3e0300d/src/server/game/Entities/Unit/Unit.cpp",
|
||||||
|
"symbol": "playerBaseMoveSpeed[MOVE_TURN_RATE]",
|
||||||
|
"radians_per_second": 3.141594
|
||||||
|
},
|
||||||
|
"wowee_mouse_strafe_reference": {
|
||||||
|
"status": "verified_public_reference_only",
|
||||||
|
"repository": "Kelsidavis/WoWee",
|
||||||
|
"revision": "626243e937fb93965fa583a6507ed5a1aa7dda4b",
|
||||||
|
"path": "src/rendering/camera_controller.cpp",
|
||||||
|
"sha256": "54A41EFDA77629B2EAB432734A94D0F6F58223F3E4752338FF4C321E7835001B",
|
||||||
|
"observation": "A/D turn while right mouse is released and strafe while it is held; Q/E always strafe.",
|
||||||
|
"fidelity_limit": "Reference implementation behavior, not original-client proof."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selected_default_bindings": [
|
||||||
|
{"command": "MOVEFORWARD", "keys": ["W", "UP"], "implemented_action": "openwc_player_move_forward"},
|
||||||
|
{"command": "MOVEBACKWARD", "keys": ["S", "DOWN"], "implemented_action": "openwc_player_move_backward"},
|
||||||
|
{"command": "TURNLEFT", "keys": ["A", "LEFT"], "implemented_action": "openwc_player_turn_left"},
|
||||||
|
{"command": "TURNRIGHT", "keys": ["D", "RIGHT"], "implemented_action": "openwc_player_turn_right"},
|
||||||
|
{"command": "STRAFELEFT", "keys": ["Q"], "implemented_action": "openwc_player_blizzlike_strafe_left"},
|
||||||
|
{"command": "STRAFERIGHT", "keys": ["E"], "implemented_action": "openwc_player_blizzlike_strafe_right"},
|
||||||
|
{"command": "JUMP", "keys": ["SPACE", "NUMPAD0"], "implemented_action": null},
|
||||||
|
{"command": "TOGGLEAUTORUN", "keys": ["NUMLOCK", "BUTTON4"], "implemented_action": null},
|
||||||
|
{"command": "CAMERAZOOMIN", "keys": ["MOUSEWHEELUP"], "implemented_action": "openwc_player_camera_zoom_in"},
|
||||||
|
{"command": "CAMERAZOOMOUT", "keys": ["MOUSEWHEELDOWN"], "implemented_action": "openwc_player_camera_zoom_out"},
|
||||||
|
{"command": "TURNORACTION", "keys": ["BUTTON2"], "implemented_action": "openwc_player_camera_rotate"},
|
||||||
|
{"command": "CAMERAORSELECTORMOVE", "keys": ["BUTTON1"], "implemented_action": null}
|
||||||
|
],
|
||||||
|
"known_gaps": [
|
||||||
|
"Jump, autorun and left-button camera/select behavior are recorded but not implemented by this package.",
|
||||||
|
"The server-core turn-rate default is compatibility evidence, not direct original-client timing proof.",
|
||||||
|
"Right-mouse A/D strafe routing is supported by a public reference implementation, not direct original-client capture."
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ func _initialize() -> void:
|
|||||||
_verify_acceleration_and_deceleration(failures)
|
_verify_acceleration_and_deceleration(failures)
|
||||||
_verify_sandbox_sprint(failures)
|
_verify_sandbox_sprint(failures)
|
||||||
_verify_flight_state_and_basis(failures)
|
_verify_flight_state_and_basis(failures)
|
||||||
|
_verify_keyboard_turn(failures)
|
||||||
_verify_invalid_delta(failures)
|
_verify_invalid_delta(failures)
|
||||||
_verify_scene_boundary(failures)
|
_verify_scene_boundary(failures)
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ func _initialize() -> void:
|
|||||||
quit(1)
|
quit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
print("LOCAL_PLAYER_MOVEMENT PASS cases=12 state_transitions=2 scene_boundary=1")
|
print("LOCAL_PLAYER_MOVEMENT PASS cases=15 state_transitions=2 scene_boundary=1")
|
||||||
quit(0)
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
@@ -88,6 +89,13 @@ func _verify_invalid_delta(failures: Array[String]) -> void:
|
|||||||
_expect_vector_near(controller.godot_world_velocity_units_per_second, Vector3.ZERO, "negative delta state", failures)
|
_expect_vector_near(controller.godot_world_velocity_units_per_second, Vector3.ZERO, "negative delta state", failures)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_keyboard_turn(failures: Array[String]) -> void:
|
||||||
|
var controller := _new_controller()
|
||||||
|
_expect_near(controller.calculate_yaw_delta_radians(_intent(0.0, 0.0, 0.0, false, 1.0), 0.5), -PI * 0.5, "right turn yaw", failures)
|
||||||
|
_expect_near(controller.calculate_yaw_delta_radians(_intent(0.0, 0.0, 0.0, false, -1.0), 0.5), PI * 0.5, "left turn yaw", failures)
|
||||||
|
_expect_near(controller.calculate_yaw_delta_radians(_intent(0.0, 0.0, 0.0, false, 1.0), -1.0), 0.0, "negative turn delta", failures)
|
||||||
|
|
||||||
|
|
||||||
func _verify_scene_boundary(failures: Array[String]) -> void:
|
func _verify_scene_boundary(failures: Array[String]) -> void:
|
||||||
var movement_source := _read_text(MOVEMENT_CONTROLLER_PATH, failures)
|
var movement_source := _read_text(MOVEMENT_CONTROLLER_PATH, failures)
|
||||||
for forbidden_text in ["extends Node", "extends Resource", "Input.", "Camera3D", "ADTLoader", "global_position"]:
|
for forbidden_text in ["extends Node", "extends Resource", "Input.", "Camera3D", "ADTLoader", "global_position"]:
|
||||||
@@ -117,9 +125,10 @@ func _intent(
|
|||||||
forward_axis: float,
|
forward_axis: float,
|
||||||
strafe_axis: float,
|
strafe_axis: float,
|
||||||
vertical_axis: float = 0.0,
|
vertical_axis: float = 0.0,
|
||||||
sprint_requested: bool = false
|
sprint_requested: bool = false,
|
||||||
|
turn_axis: float = 0.0
|
||||||
) -> MoveIntent:
|
) -> MoveIntent:
|
||||||
return MoveIntent.new(forward_axis, strafe_axis, vertical_axis, sprint_requested)
|
return MoveIntent.new(forward_axis, strafe_axis, vertical_axis, sprint_requested, turn_axis)
|
||||||
|
|
||||||
|
|
||||||
func _read_text(path: String, failures: Array[String]) -> String:
|
func _read_text(path: String, failures: Array[String]) -> String:
|
||||||
@@ -140,6 +149,11 @@ func _expect_vector_near(
|
|||||||
failures.append("%s expected %s, got %s" % [label, expected_value, actual_value])
|
failures.append("%s expected %s, got %s" % [label, expected_value, actual_value])
|
||||||
|
|
||||||
|
|
||||||
|
func _expect_near(actual_value: float, expected_value: float, label: String, failures: Array[String]) -> void:
|
||||||
|
if absf(actual_value - expected_value) > 0.000001:
|
||||||
|
failures.append("%s expected %.6f, got %.6f" % [label, expected_value, actual_value])
|
||||||
|
|
||||||
|
|
||||||
func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
|
func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
|
||||||
if not actual_value:
|
if not actual_value:
|
||||||
failures.append("%s expected true" % label)
|
failures.append("%s expected true" % label)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const MOVE_INTENT_PATH := "res://src/domain/input/move_intent.gd"
|
|||||||
const PLAYER_INPUT_SOURCE_PATH := "res://src/gameplay/input/player_input_source.gd"
|
const PLAYER_INPUT_SOURCE_PATH := "res://src/gameplay/input/player_input_source.gd"
|
||||||
const PLAYER_CONTROLLER_PATH := "res://src/scenes/player/third_person_wow_controller.gd"
|
const PLAYER_CONTROLLER_PATH := "res://src/scenes/player/third_person_wow_controller.gd"
|
||||||
const REGRESSION_SCENE_PATH := "res://src/tests/scenes/player_input_regression.tscn"
|
const REGRESSION_SCENE_PATH := "res://src/tests/scenes/player_input_regression.tscn"
|
||||||
|
const PROFILE_FIXTURE_PATH := "res://src/tests/fixtures/player_input_profile_335.json"
|
||||||
|
|
||||||
|
|
||||||
func _initialize() -> void:
|
func _initialize() -> void:
|
||||||
@@ -15,6 +16,7 @@ func _initialize() -> void:
|
|||||||
func _run_verification() -> void:
|
func _run_verification() -> void:
|
||||||
var failures: Array[String] = []
|
var failures: Array[String] = []
|
||||||
_verify_default_input_actions(failures)
|
_verify_default_input_actions(failures)
|
||||||
|
_verify_profile_fixture(failures)
|
||||||
_verify_move_intent_composition(failures)
|
_verify_move_intent_composition(failures)
|
||||||
_verify_controller_boundary(failures)
|
_verify_controller_boundary(failures)
|
||||||
_verify_regression_scene(failures)
|
_verify_regression_scene(failures)
|
||||||
@@ -25,7 +27,7 @@ func _run_verification() -> void:
|
|||||||
quit(1)
|
quit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
print("PLAYER_INPUT PASS actions=%d intent_cases=6 controller=1 regression_scene=1" % PlayerInputActions.REQUIRED_ACTIONS.size())
|
print("PLAYER_INPUT PASS actions=%d intent_cases=9 profiles=3 fixture=1 controller=1 regression_scene=3" % PlayerInputActions.REQUIRED_ACTIONS.size())
|
||||||
quit(0)
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
@@ -34,9 +36,17 @@ func _verify_default_input_actions(failures: Array[String]) -> void:
|
|||||||
_expect_true(InputMap.has_action(action_name), "Input Map contains %s" % action_name, failures)
|
_expect_true(InputMap.has_action(action_name), "Input Map contains %s" % action_name, failures)
|
||||||
|
|
||||||
_expect_key_binding(PlayerInputActions.MOVE_FORWARD, KEY_W, true, failures)
|
_expect_key_binding(PlayerInputActions.MOVE_FORWARD, KEY_W, true, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.MOVE_FORWARD, KEY_UP, false, failures)
|
||||||
_expect_key_binding(PlayerInputActions.MOVE_BACKWARD, KEY_S, true, failures)
|
_expect_key_binding(PlayerInputActions.MOVE_BACKWARD, KEY_S, true, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.MOVE_BACKWARD, KEY_DOWN, false, failures)
|
||||||
_expect_key_binding(PlayerInputActions.STRAFE_LEFT, KEY_A, true, failures)
|
_expect_key_binding(PlayerInputActions.STRAFE_LEFT, KEY_A, true, failures)
|
||||||
_expect_key_binding(PlayerInputActions.STRAFE_RIGHT, KEY_D, true, failures)
|
_expect_key_binding(PlayerInputActions.STRAFE_RIGHT, KEY_D, true, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.BLIZZLIKE_STRAFE_LEFT, KEY_Q, true, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.BLIZZLIKE_STRAFE_RIGHT, KEY_E, true, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.TURN_LEFT, KEY_A, true, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.TURN_LEFT, KEY_LEFT, false, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.TURN_RIGHT, KEY_D, true, failures)
|
||||||
|
_expect_key_binding(PlayerInputActions.TURN_RIGHT, KEY_RIGHT, false, failures)
|
||||||
_expect_key_binding(PlayerInputActions.DEBUG_FLY_UP, KEY_E, true, failures)
|
_expect_key_binding(PlayerInputActions.DEBUG_FLY_UP, KEY_E, true, failures)
|
||||||
_expect_key_binding(PlayerInputActions.DEBUG_FLY_DOWN, KEY_Q, true, failures)
|
_expect_key_binding(PlayerInputActions.DEBUG_FLY_DOWN, KEY_Q, true, failures)
|
||||||
_expect_key_binding(PlayerInputActions.DEBUG_SPRINT, KEY_SHIFT, false, failures)
|
_expect_key_binding(PlayerInputActions.DEBUG_SPRINT, KEY_SHIFT, false, failures)
|
||||||
@@ -47,6 +57,40 @@ func _verify_default_input_actions(failures: Array[String]) -> void:
|
|||||||
_expect_mouse_binding(PlayerInputActions.CAMERA_ZOOM_OUT, MOUSE_BUTTON_WHEEL_DOWN, failures)
|
_expect_mouse_binding(PlayerInputActions.CAMERA_ZOOM_OUT, MOUSE_BUTTON_WHEEL_DOWN, failures)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_profile_fixture(failures: Array[String]) -> void:
|
||||||
|
var fixture_text := _read_text(PROFILE_FIXTURE_PATH, failures)
|
||||||
|
var parsed_fixture: Variant = JSON.parse_string(fixture_text)
|
||||||
|
_expect_true(parsed_fixture is Dictionary, "profile fixture parses", failures)
|
||||||
|
if not parsed_fixture is Dictionary:
|
||||||
|
return
|
||||||
|
var fixture := parsed_fixture as Dictionary
|
||||||
|
_expect_true(fixture.get("schema_version") == 1, "profile fixture schema", failures)
|
||||||
|
_expect_true(fixture.get("profile_id") == "Blizzlike335", "profile fixture identity", failures)
|
||||||
|
var sources := fixture.get("sources", {}) as Dictionary
|
||||||
|
var bindings_source := sources.get("original_client_default_bindings", {}) as Dictionary
|
||||||
|
_expect_true(bindings_source.get("build") == 12340, "fixture client build", failures)
|
||||||
|
_expect_true(
|
||||||
|
bindings_source.get("sha256") == "35FFA0E4E2356A13A0633A848F9E57ABE7DF9D8A16C2245E15D33013AD2F56A5",
|
||||||
|
"fixture binding source hash",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
var commands_source := sources.get("original_client_binding_commands", {}) as Dictionary
|
||||||
|
_expect_true(
|
||||||
|
commands_source.get("sha256") == "2E01276AFB7462F1417710F13B4BF1A456341F7F28CC8AE0D6D9C7442D7F6793",
|
||||||
|
"fixture command source hash",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
var turn_source := sources.get("trinitycore_player_turn_rate", {}) as Dictionary
|
||||||
|
_expect_near(float(turn_source.get("radians_per_second", 0.0)), 3.141594, "fixture turn rate", failures)
|
||||||
|
var mouse_strafe_source := sources.get("wowee_mouse_strafe_reference", {}) as Dictionary
|
||||||
|
_expect_true(
|
||||||
|
mouse_strafe_source.get("revision") == "626243e937fb93965fa583a6507ed5a1aa7dda4b",
|
||||||
|
"fixture mouse-strafe reference revision",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
_expect_true((fixture.get("selected_default_bindings", []) as Array).size() == 12, "fixture selected binding count", failures)
|
||||||
|
|
||||||
|
|
||||||
func _verify_move_intent_composition(failures: Array[String]) -> void:
|
func _verify_move_intent_composition(failures: Array[String]) -> void:
|
||||||
var neutral := PlayerInputSource.compose_move_intent(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)
|
var neutral := PlayerInputSource.compose_move_intent(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)
|
||||||
_expect_near(neutral.forward_axis, 0.0, "neutral forward", failures)
|
_expect_near(neutral.forward_axis, 0.0, "neutral forward", failures)
|
||||||
@@ -67,6 +111,18 @@ func _verify_move_intent_composition(failures: Array[String]) -> void:
|
|||||||
var debug_requests := PlayerInputSource.compose_move_intent(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, true)
|
var debug_requests := PlayerInputSource.compose_move_intent(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, true)
|
||||||
_expect_true(debug_requests.is_sprint_requested, "sprint request retained", failures)
|
_expect_true(debug_requests.is_sprint_requested, "sprint request retained", failures)
|
||||||
|
|
||||||
|
var blizzlike_strafe := PlayerInputSource.compose_blizzlike_335_move_intent(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, false)
|
||||||
|
_expect_near(blizzlike_strafe.strafe_axis, -1.0, "blizzlike Q strafe", failures)
|
||||||
|
var blizzlike_turn := PlayerInputSource.compose_blizzlike_335_move_intent(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, false)
|
||||||
|
_expect_near(blizzlike_turn.turn_axis, 1.0, "blizzlike D turn", failures)
|
||||||
|
var mouse_strafe := PlayerInputSource.compose_blizzlike_335_move_intent(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, true)
|
||||||
|
_expect_near(mouse_strafe.strafe_axis, 1.0, "camera-held D strafe", failures)
|
||||||
|
_expect_near(mouse_strafe.turn_axis, 0.0, "camera-held D suppresses turn", failures)
|
||||||
|
|
||||||
|
_expect_true(PlayerInputSource.new(&"RenderSandbox").input_profile_id == &"RenderSandbox", "sandbox profile selected", failures)
|
||||||
|
_expect_true(PlayerInputSource.new(&"Blizzlike335").input_profile_id == &"Blizzlike335", "blizzlike profile selected", failures)
|
||||||
|
_expect_true(PlayerInputSource.new(&"Unknown").input_profile_id == &"Blizzlike335", "unknown profile fails closed", failures)
|
||||||
|
|
||||||
|
|
||||||
func _verify_controller_boundary(failures: Array[String]) -> void:
|
func _verify_controller_boundary(failures: Array[String]) -> void:
|
||||||
var controller_source := _read_text(PLAYER_CONTROLLER_PATH, failures)
|
var controller_source := _read_text(PLAYER_CONTROLLER_PATH, failures)
|
||||||
@@ -118,6 +174,31 @@ func _verify_regression_scene(failures: Array[String]) -> void:
|
|||||||
_expect_true(movement_controller.is_flight_enabled, "flight action preserves immediate sandbox toggle", failures)
|
_expect_true(movement_controller.is_flight_enabled, "flight action preserves immediate sandbox toggle", failures)
|
||||||
player.free()
|
player.free()
|
||||||
|
|
||||||
|
var blizzlike_player := packed_scene.instantiate() as CharacterBody3D
|
||||||
|
blizzlike_player.set("movement_profile_id", "Blizzlike335")
|
||||||
|
root.add_child(blizzlike_player)
|
||||||
|
blizzlike_player.set_physics_process(false)
|
||||||
|
Input.action_press(PlayerInputActions.TURN_RIGHT)
|
||||||
|
blizzlike_player.call("_physics_process", 0.5)
|
||||||
|
Input.action_release(PlayerInputActions.TURN_RIGHT)
|
||||||
|
_expect_near(blizzlike_player.rotation.y, -3.141594 * 0.5, "blizzlike D turns right", failures)
|
||||||
|
var blizzlike_camera := blizzlike_player.get_node("CameraPivot") as ThirdPersonCameraRig
|
||||||
|
_expect_near(blizzlike_camera.yaw_radians, blizzlike_player.rotation.y, "keyboard turn synchronizes camera yaw", failures)
|
||||||
|
blizzlike_player.free()
|
||||||
|
|
||||||
|
var mouse_strafe_player := packed_scene.instantiate() as CharacterBody3D
|
||||||
|
mouse_strafe_player.set("movement_profile_id", "Blizzlike335")
|
||||||
|
root.add_child(mouse_strafe_player)
|
||||||
|
mouse_strafe_player.set_physics_process(false)
|
||||||
|
Input.action_press(PlayerInputActions.CAMERA_ROTATE)
|
||||||
|
Input.action_press(PlayerInputActions.TURN_RIGHT)
|
||||||
|
mouse_strafe_player.call("_physics_process", 0.5)
|
||||||
|
Input.action_release(PlayerInputActions.TURN_RIGHT)
|
||||||
|
Input.action_release(PlayerInputActions.CAMERA_ROTATE)
|
||||||
|
_expect_near(mouse_strafe_player.rotation.y, 0.0, "camera-held D does not turn", failures)
|
||||||
|
_expect_near(mouse_strafe_player.position.x, 2.25, "camera-held D strafes right", failures)
|
||||||
|
mouse_strafe_player.free()
|
||||||
|
|
||||||
|
|
||||||
func _expect_key_binding(
|
func _expect_key_binding(
|
||||||
action_name: StringName,
|
action_name: StringName,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M02 — Player Decomposition
|
# M02 — Player Decomposition
|
||||||
|
|
||||||
<!-- OPENWC_TARGET:ACTIVE -->
|
<!-- OPENWC_TARGET:DONE -->
|
||||||
|
<!-- OPENWC_TARGET_DONE:M02:work/sindo-main-codex/m02-integrator-closeout:2026-07-15 -->
|
||||||
|
|
||||||
## Outcome
|
## Outcome
|
||||||
|
|
||||||
@@ -8,14 +9,14 @@
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
- [ ] Заменить hardcoded keys на remappable Input Map actions с профилем 3.3.5a.
|
- [x] Заменить hardcoded keys на remappable Input Map actions с профилем 3.3.5a.
|
||||||
- [ ] Выделить `PlayerInputSource → MoveIntent`.
|
- [x] Выделить `PlayerInputSource → MoveIntent`.
|
||||||
- [ ] Выделить local movement state/controller.
|
- [x] Выделить local movement state/controller.
|
||||||
- [ ] Выделить `TerrainQuery` и убрать прямой ADT parsing из player.
|
- [x] Выделить `TerrainQuery` и убрать прямой ADT parsing из player.
|
||||||
- [ ] Выделить third-person camera rig и camera collision policy.
|
- [x] Выделить third-person camera rig и camera collision policy.
|
||||||
- [ ] Выделить character appearance и animation presenters.
|
- [x] Выделить character appearance и animation presenters.
|
||||||
- [ ] Оставить sprint/flight debug-функции только в sandbox profile.
|
- [x] Оставить sprint/flight debug-функции только в sandbox profile.
|
||||||
- [ ] Добавить movement/camera state tests и regression scene.
|
- [x] Добавить movement/camera state tests и regression scene.
|
||||||
|
|
||||||
## Fidelity evidence
|
## Fidelity evidence
|
||||||
|
|
||||||
@@ -27,10 +28,47 @@
|
|||||||
|
|
||||||
## Evidence
|
## Evidence
|
||||||
|
|
||||||
- Date:
|
- Date: 2026-07-15
|
||||||
- Revision/worktree:
|
- Revision/worktree: merged master `eb122d5`; closeout
|
||||||
- Commands:
|
`work/sindo-main-codex/m02-integrator-closeout`.
|
||||||
- Results:
|
- Commands: `verify_player_input.gd`; `verify_local_player_movement.gd`;
|
||||||
- Fidelity comparison:
|
`verify_player_movement_capabilities.gd`; `verify_terrain_query.gd`;
|
||||||
- Changed files:
|
`verify_third_person_camera_rig.gd`; `verify_character_presentation.gd`;
|
||||||
- Remaining risks:
|
`verify_coordinate_conversion_boundaries.gd`; `verify_streaming_focus.gd`;
|
||||||
|
`tools/run_render_baseline.ps1 -DryRun`; documentation, coordination and
|
||||||
|
diff-hygiene gates.
|
||||||
|
- Results: input PASS (`16` actions, `9` intent cases, `3` profiles, one
|
||||||
|
provenance fixture, `3` real-scene cases); movement PASS (`15` cases, `2`
|
||||||
|
state transitions); profile capabilities PASS (`3` profiles, sprint/flight
|
||||||
|
allow/reject and `2` scenes); terrain PASS (`4` contract, interpolation,
|
||||||
|
cache and `2` failure cases); camera PASS (`12` state and `3` policy cases);
|
||||||
|
presentation PASS (`9` appearance and `10` animation cases across `3`
|
||||||
|
scenes); coordinate boundary PASS (`103` files, `5` consumers); StreamingFocus
|
||||||
|
PASS (`2` runtime scenes, `3` capture tools). Renderer dry-run passed project,
|
||||||
|
material, dedupe and shutdown gates, manifest `7/7/7`, calibration maximum
|
||||||
|
error `0.000015`, server-spawn renderer and all seven planned checkpoints.
|
||||||
|
- Fidelity comparison: selected build-12340 default bindings are direct private
|
||||||
|
observations pinned by `DefaultBindings.wtf` SHA-256
|
||||||
|
`35FFA0E4E2356A13A0633A848F9E57ABE7DF9D8A16C2245E15D33013AD2F56A5`
|
||||||
|
and `Bindings.xml` SHA-256
|
||||||
|
`2E01276AFB7462F1417710F13B4BF1A456341F7F28CC8AE0D6D9C7442D7F6793`;
|
||||||
|
only sanitized selected facts are committed. `3.141594` rad/s keyboard turn
|
||||||
|
is pinned to TrinityCore 3.3.5 revision
|
||||||
|
`2853a621d6af91a803787a2b8a509f4ce3e0300d`; right-mouse A/D strafe routing
|
||||||
|
is supported by WoWee revision `626243e937fb93965fa583a6507ed5a1aa7dda4b`.
|
||||||
|
These two sources are compatibility references, not direct client timing/
|
||||||
|
conditional-behavior proof. Translation speeds and camera values remain
|
||||||
|
sandbox regression baselines rather than original-client parity evidence;
|
||||||
|
jump/fall/swim/fly gameplay is not implemented by this decomposition target.
|
||||||
|
Debug sprint/free flight is proven unable to execute in `Blizzlike335`.
|
||||||
|
- Changed files: typed input intent/actions/source, local movement/capabilities,
|
||||||
|
terrain query, camera rig/collision policy, character appearance/animation
|
||||||
|
presenters, player composition, Input Map configuration, asset-free regression
|
||||||
|
scenes, sanitized input fixture, M02 verifiers, module specifications and M02
|
||||||
|
coordination claims.
|
||||||
|
- Remaining risks: direct build-12340 timing evidence for movement/camera,
|
||||||
|
jump/fall/swim/fly, autorun, left-button camera/select behavior, active camera
|
||||||
|
collision, terrain slopes/holes/liquids, persisted keybindings, server authority,
|
||||||
|
prediction and reconciliation remain for M09/M10/M12. Runtime renderer scenes
|
||||||
|
continue to default to explicit `RenderSandbox` until an application shell
|
||||||
|
selects `Blizzlike335`. M02 makes no complete `1:1` compatibility claim.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# M03 — Renderer Facade and Safe Extraction
|
# M03 — Renderer Facade and Safe Extraction
|
||||||
|
|
||||||
<!-- OPENWC_TARGET:OPEN -->
|
<!-- OPENWC_TARGET:ACTIVE -->
|
||||||
|
|
||||||
## Outcome
|
## Outcome
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ OpenWC должен предоставить четыре согласованн
|
|||||||
|
|
||||||
- MPQ/BLP/ADT/WDT/M2/WMO native parsing и renderer vertical slice уже существуют.
|
- MPQ/BLP/ADT/WDT/M2/WMO native parsing и renderer vertical slice уже существуют.
|
||||||
- Runtime streaming, terrain quality, MultiMesh M2, WMO caches, liquids, sky и character experiments находятся в рабочем состоянии, но сосредоточены в крупных orchestration scripts.
|
- Runtime streaming, terrain quality, MultiMesh M2, WMO caches, liquids, sky и character experiments находятся в рабочем состоянии, но сосредоточены в крупных orchestration scripts.
|
||||||
- Renderer baseline M00 завершён: paired comparison с оригинальным клиентом записывает измеренные gaps без заявления parity, а обязательные diagnostic gaps закрыты или явно классифицированы. M01 завершил coordinate, identity и streaming-focus seams; текущая работа M02 декомпозирует player input, movement, terrain query, camera и presentation без observable regression.
|
- Renderer baseline M00 завершён: paired comparison с оригинальным клиентом записывает измеренные gaps без заявления parity, а обязательные diagnostic gaps закрыты или явно классифицированы. M01 завершил coordinate, identity и streaming-focus seams; M02 декомпозировал player input, movement, terrain query, camera и presentation без observable regression. Текущая работа M03 закрывает renderer стабильным facade и извлекает planner/scheduler без rewrite.
|
||||||
- Gameplay domain, network protocol, production UI/Lua, audio orchestration и server adapters в основном предстоит реализовать.
|
- Gameplay domain, network protocol, production UI/Lua, audio orchestration и server adapters в основном предстоит реализовать.
|
||||||
- Editor plugin пока решает extraction/preview задачи, но не является полноценной authoring platform.
|
- Editor plugin пока решает extraction/preview задачи, но не является полноценной authoring platform.
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
## Current target
|
## Current target
|
||||||
|
|
||||||
`M02` — [02-player-decomposition.md](02-player-decomposition.md)
|
`M03` — [03-renderer-facade.md](03-renderer-facade.md)
|
||||||
|
|
||||||
Одновременно `ACTIVE` может быть только одна цель. Следующая цель становится `ACTIVE` после появления валидной `OPENWC_TARGET_DONE` метки у предыдущей.
|
Одновременно `ACTIVE` может быть только одна цель. Следующая цель становится `ACTIVE` после появления валидной `OPENWC_TARGET_DONE` метки у предыдущей.
|
||||||
|
|
||||||
@@ -35,8 +35,8 @@
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| M00 | [Renderer baseline](00-render-baseline.md) | — | DONE |
|
| M00 | [Renderer baseline](00-render-baseline.md) | — | DONE |
|
||||||
| M01 | [Coordinates and architecture seams](01-coordinates-and-seams.md) | M00 | DONE |
|
| M01 | [Coordinates and architecture seams](01-coordinates-and-seams.md) | M00 | DONE |
|
||||||
| M02 | [Player decomposition](02-player-decomposition.md) | M01 | ACTIVE |
|
| M02 | [Player decomposition](02-player-decomposition.md) | M01 | DONE |
|
||||||
| M03 | [Renderer facade and extraction](03-renderer-facade.md) | M02 | OPEN |
|
| M03 | [Renderer facade and extraction](03-renderer-facade.md) | M02 | ACTIVE |
|
||||||
| M04 | [Godot Editor shell](04-editor-shell.md) | M03 | OPEN |
|
| M04 | [Godot Editor shell](04-editor-shell.md) | M03 | OPEN |
|
||||||
| M05 | [Content Project](05-content-project.md) | M04 | OPEN |
|
| M05 | [Content Project](05-content-project.md) | M04 | OPEN |
|
||||||
| M06 | [Server database adapters](06-server-adapters.md) | M05 | OPEN |
|
| M06 | [Server database adapters](06-server-adapters.md) | M05 | OPEN |
|
||||||
|
|||||||
Reference in New Issue
Block a user