Restore obstacle routing via waypoints elsewhere; on bump tops nav often
prefers a downward segment and breaks departure. Detect plateau with
is_on_floor + flat normal + ray down into group ns19_bump; then steer
only toward authoritative goal xz. Slopes keep waypoint pathing.
- Add BoxShape3D to PrototypeTerminal (layer 1, walkable) so the player
cannot pass through; matches terminal mesh size.
- Bake navigation from collision layer 1 only so the player (layer 2) is
not merged into the mesh; reparent Player under NavigationRegion3D so
NavigationAgent3D resolves a valid map.
- Restore nav waypoint following for long horizontal legs; keep horizontal
steering toward next/goal so bump departure behavior stays sane.
Ground pick: step the ray through steep walkable hits (0.09 < dot UP < 0.64)
so a view that grazes bump sides still resolves to flat floor beyond; walls
(dot ~0) stop the chain.
Player: drop NavigationAgent3D waypoint following; steer only toward the
server goal in xz so plateau paths are not dominated by vertical nav
segments. move_and_slide handles slopes and obstacles.
MIN_WALKABLE_NORMAL_DOT_UP 0.82 is stricter than NS-19 frustum slopes
(~45° / dot 0.71 on Bump A, ~39° / 0.78 on B). Clicks off the plateau
often hit the slope first; the pick was dropped with no MoveCommand.
Lower to 0.64 so ramps register; vertical walls remain ~0.
VERT_ARRIVE_EPS (0.16) was larger than the bump rise (~0.12–0.15), so a
floor click beside the plateau often satisfied arrival (horiz + vert) on
the first frame and cleared the walk goal before any motion.
Tighten vertical arrival to 0.055. Add a steer fallback when the target
shares xz but is lower (plateau vs floor underfoot) so we still pick a
horizontal escape toward the rim.
Path waypoints from the plateau often sit on the slope almost under the
player (same xz, lower y). 3D steer + y clamp gave almost no horizontal
velocity, so standing on top then clicking off looked stuck. Steer in xz
at MOVE_SPEED and let move_and_slide follow floor/slope; keep 3D only for
vertical climb when xz is aligned.
Replace top slab + four ramp wedges with one ConvexPolygonShape3D frustum
per bump so plateau and slopes are one manifold (no collider seams at
table edges when walking off).
Add CharacterBody3D safe_margin to reduce edge contact with physics.
Full bump box shared vertical faces with convex ramp wedges, doubling
collision and snagging CharacterBody3D on and off. Replace with thin
top BoxShape3D; ramps own all side contact.
Raise Player floor_max_angle to 50° and floor_snap_length slightly for
cleaner ramp floor snapping.
- Add four invisible ConvexPolygonShape3D ramp wedges per bump (walkable) so
move_and_slide can climb/descend without test_move ledge hacks.
- Remove _try_autostep / _maybe_autostep_after_slide and LEDGE_DROP_STEPS;
they mis-detected geometry and fought CharacterBody3D slide.
- Fix duplicate subresource unique_id on BumpA RampWest and BumpB RampSouth.
- Run .NET build/test only when the solution, server/, or the workflow changes.
- Add gdlint and gdformat --check for client/ on relevant path changes.
- Align client GDScript with gdtoolkit (formatting, enum order, line length).
Implement E1.M1 intent → authority: POST /game/players/{id}/move with v1 JSON
MoveCommandRequest (snap + sequence), extend in-memory store and
PositionStateApi. Godot client splits ground_pick and position_authority_client
with thin main.gd; snap_to_server on player.
Tests live under NeonSprawl.Server.Tests/Game/PositionState/ with AAA layout
and MethodName_ShouldExpectedOutcome_WhenScenario naming. Debug builds use
UseAppHost=false so Linux IDEs resolve .NET 10 from a user-local SDK.
Add NS-16 implementation plan, code review doc, godot-client-script-organization
rule, csharp test layout/naming rules, and refresh E1.M1 / alignment docs.
Server and client README updates include move API, prerequisites, and Rider
notes.
Made-with: Cursor
- State GDScript as the locked choice for Godot client code
- Summary tables and client README note standard (non-.NET) Godot build
- Server/client contract via Protobuf or JSON spike, not shared C# on client
Made-with: Cursor