fix(core+app): Phase B.3 — Setup.StepUpHeight + scenery road exclusion

Four targeted fixes for user-reported movement/visual bugs:

1. Player entity disappearing: GpuWorldState now supports persistent
   entities (MarkPersistent/DrainRescued). The player character survives
   landblock unloads and gets re-injected into the streaming window at
   the current center landblock.

2. Feet sinking into terrain: +0.15 Z bias in PlayerMovementController
   keeps the character model above terrain z-fighting edge cases.

3. Camera after portal teleport: ChaseCamera.Update now called
   immediately after teleport snap so the camera recenters on the new
   position instead of lingering at the pre-teleport location.

4. Scenery on roads: SceneryGenerator now checks road status at the
   final displaced position (not just the origin vertex), catching
   objects that drift from non-road vertices onto road cells.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-12 18:56:45 +02:00
parent 9dbb2cbd5c
commit 41013ce3e3
5 changed files with 71 additions and 8 deletions

View file

@ -122,6 +122,17 @@ public static class SceneryGenerator
if (lx < 0 || ly < 0 || lx >= LandblockSize || ly >= LandblockSize)
continue;
// Check if the final displaced position lands on a road vertex.
// The road status is per-vertex (9×9 grid); sample the nearest
// vertex to the displaced position to catch scenery that drifted
// from a non-road vertex onto a road.
{
int nearX = Math.Clamp((int)(lx / CellSize + 0.5f), 0, VerticesPerSide - 1);
int nearY = Math.Clamp((int)(ly / CellSize + 0.5f), 0, VerticesPerSide - 1);
ushort nearRaw = block.Terrain[nearX * VerticesPerSide + nearY];
if (IsRoadVertex(nearRaw)) continue;
}
// Z at the cell corner from the heightmap. Skipping slope-based
// Z placement (ACViewer uses find_terrain_poly which we don't have)
// — accept that some scenery will float or clip.