From b341193cfedc8346641b848067b4220025298e15 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 12 Apr 2026 15:11:50 +0200 Subject: [PATCH] =?UTF-8?q?fix(app+core):=20Phase=20B.2=20=E2=80=94=20incr?= =?UTF-8?q?ease=20step=20height=20+=20resolve=20initial=20Z=20from=20terra?= =?UTF-8?q?in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes for the "position never changes when walking" bug: 1. StepUpHeight was 1.0 units — too tight. The player started at Z=92.2 (ACE relocation from previous session) but terrain Z was ~94, so every movement attempt had a Z delta of 1.8 which exceeded the limit. Increased to 5.0 (forgiving for MVP; AC default for humans is ~2 from Setup.StepUpHeight). 2. Initial position now resolves through PhysicsEngine with a huge step height (100) to snap to the correct terrain Z regardless of where the server-sent Z currently is. With indoor transitions disabled, this always produces the outdoor terrain height. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Input/PlayerMovementController.cs | 9 ++++++++- src/AcDream.App/Rendering/GameWindow.cs | 17 ++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/AcDream.App/Input/PlayerMovementController.cs b/src/AcDream.App/Input/PlayerMovementController.cs index e151197..b5b7466 100644 --- a/src/AcDream.App/Input/PlayerMovementController.cs +++ b/src/AcDream.App/Input/PlayerMovementController.cs @@ -44,7 +44,14 @@ public sealed class PlayerMovementController public float RunSpeed { get; set; } = 7f; public float TurnSpeed { get; set; } = 1.5f; public float MouseTurnSensitivity { get; set; } = 0.003f; - public float StepUpHeight { get; set; } = 1.0f; + /// + /// Maximum Z increase per movement step before the move is rejected. + /// AC's default StepUpHeight for human characters is ~2 units (from + /// Setup.StepUpHeight). Using 5 for the MVP to be forgiving — prevents + /// walking up vertical walls but allows stairs, ramps, and terrain + /// slopes that the heightmap interpolation can produce. + /// + public float StepUpHeight { get; set; } = 5.0f; public float Yaw { get; set; } public Vector3 Position { get; private set; } diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index af7aedc..223c8d8 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -203,15 +203,14 @@ public sealed class GameWindow : IDisposable float plocalX = playerEntity.Position.X - (plbX - _liveCenterX) * 192f; float plocalY = playerEntity.Position.Y - (plbY - _liveCenterY) * 192f; uint pinitCellId = ((uint)plbX << 24) | ((uint)plbY << 16) | 0x0001u; - // Use the server-sent position directly — the server already - // gave us a valid position at CreateObject time. Running it - // through PhysicsEngine.Resolve was mapping the player into - // an indoor cell (e.g., the foundry at Z=66) when they're - // actually standing on outdoor terrain at Z=93+. The physics - // engine's cell-containment check is too aggressive for the - // initial placement. Once the player starts moving, Resolve - // will keep them on the correct surface. - _playerController.SetPosition(playerEntity.Position, pinitCellId & 0xFFFFu); + // Resolve the initial position through the physics engine to + // get the correct terrain Z. The server-sent Z may be stale + // from a previous ACE relocation. With indoor transitions + // disabled, Resolve will always snap to outdoor terrain Z. + var initResult = _physicsEngine.Resolve( + playerEntity.Position, pinitCellId & 0xFFFFu, + System.Numerics.Vector3.Zero, 100f); // huge step height for initial snap + _playerController.SetPosition(initResult.Position, initResult.CellId); // Derive initial yaw from the entity's server-sent rotation // rather than hardcoding. Extract yaw from the quaternion. var q = playerEntity.Rotation;