fix(app+core): Phase B.3 — streaming follows player, AC jump physics

Three fixes for user-reported movement bugs:

1. Character disappears far from spawn: streaming observer now computed
   from _playerController.Position when player mode is active, instead
   of _lastLivePlayerLandblockId which only updates from server echoes
   (never for autonomous moves). The 5x5 streaming window now follows
   the player as they walk.

2. Jump physics from ACE: JumpImpulse=5.0 and GravityAccel=9.8
   matching AC's formula: velocity_z = sqrt(height * 19.6) where
   height = BurdenMod * (JumpSkill / (JumpSkill + 1300) * 22.2 + 0.05)
   For a new char (skill=100, burden=50%): height≈1.31, vz≈5.07.

3. Gravity reduced from 20 to 9.8 (AC's F_GRAVITY constant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-12 21:06:42 +02:00
parent 192e066182
commit 6f05c298cf
2 changed files with 21 additions and 4 deletions

View file

@ -64,8 +64,14 @@ public sealed class PlayerMovementController
public float VerticalVelocity { get; private set; } public float VerticalVelocity { get; private set; }
public bool IsAirborne { get; private set; } public bool IsAirborne { get; private set; }
public float JumpImpulse { get; set; } = 3.5f; // placeholder; retail scales by Jump skill /// <summary>
public float GravityAccel { get; set; } = 20f; /// Jump velocity Z. AC formula: sqrt(height * 19.6) where
/// height = BurdenMod * (JumpSkill / (JumpSkill + 1300) * 22.2 + 0.05) * power.
/// For a typical new char (skill=100, burden=50%, power=1.0) ≈ 5.07.
/// </summary>
public float JumpImpulse { get; set; } = 5.0f;
/// <summary>AC's gravity constant (F_GRAVITY = 9.8 m/s²).</summary>
public float GravityAccel { get; set; } = 9.8f;
public float AirControlFactor { get; set; } = 0.2f; public float AirControlFactor { get; set; } = 0.2f;
/// <summary> /// <summary>

View file

@ -1562,11 +1562,22 @@ public sealed class GameWindow : IDisposable
int observerCx = _liveCenterX; int observerCx = _liveCenterX;
int observerCy = _liveCenterY; int observerCy = _liveCenterY;
if (_liveSession is not null if (_playerMode && _playerController is not null)
{
// Player mode: follow the physics-resolved player position.
// The player walks via the local physics engine; the server
// doesn't echo back our autonomous moves, so _lastLivePlayer*
// stays at the login position. Compute the landblock from the
// controller's current world-space position instead.
var pp = _playerController.Position;
observerCx = _liveCenterX + (int)System.Math.Floor(pp.X / 192f);
observerCy = _liveCenterY + (int)System.Math.Floor(pp.Y / 192f);
}
else if (_liveSession is not null
&& _liveSession.CurrentState == AcDream.Core.Net.WorldSession.State.InWorld && _liveSession.CurrentState == AcDream.Core.Net.WorldSession.State.InWorld
&& _lastLivePlayerLandblockId is { } lid) && _lastLivePlayerLandblockId is { } lid)
{ {
// Live mode: follow the server's last-known player position. // Live mode (fly camera): follow the server's last-known player position.
observerCx = (int)((lid >> 24) & 0xFFu); observerCx = (int)((lid >> 24) & 0xFFu);
observerCy = (int)((lid >> 16) & 0xFFu); observerCy = (int)((lid >> 16) & 0xFFu);
} }