NEON-29: remove horizontal step limit; add HorizontalStepEnabled flag
Expanded 45×45 district made the 18 m MaxHorizontalStep too restrictive (floor diagonal ~63 m). Add HorizontalStepEnabled to MovementValidationOptions (default false, matching DistrictBoundsEnabled pattern) so the check is skipped unless explicitly opted in. Add test for disabled case (9 pass). Update README and plan with the decision.pull/41/head
parent
6879d283a9
commit
ac75af9c73
|
|
@ -26,7 +26,7 @@ The default play space is a **45 × 45** unit flat floor (~2 000 sq units, ~5×
|
|||
| `Floor` | `walkable` | 45 × 45 | (0, 0) | Main play surface |
|
||||
| `PrototypeTerminal` | `walkable` | 0.9 × 0.4 | (0, 0) | Interaction target |
|
||||
| `MoveRejectPedestal` | `walkable` | 1.5 × 1.5 | (7.5, −6.5) | NEON-7 vertical reject |
|
||||
| `MoveRejectFarPad` | `walkable` | 2.5 × 2.5 | (9, 9) | NEON-7 range reject |
|
||||
| `MoveRejectFarPad` | `walkable` | 2.5 × 2.5 | (9, 9) | NEON-7 vertical reject pad (horizontal limit removed NEON-29) |
|
||||
| `Obstacle` | `occluder` | 2 × 2 | (6, 5) | Original occlusion / click-through target |
|
||||
| `ObstacleB` | `occluder` | 3 × 3 | (−10, 5) | Second occluder (wider angle from spawn) |
|
||||
| `ObstacleC` | `occluder` | 2 × 4 | (5, −12) | Tall occluder, south quadrant |
|
||||
|
|
|
|||
|
|
@ -102,8 +102,11 @@ No new automated GDScript tests are added for this story — there is no new GDS
|
|||
|
||||
---
|
||||
|
||||
## Decisions
|
||||
|
||||
- **No horizontal step limit (resolved):** The server's `MaxHorizontalStep = 18 m` was too restrictive for the expanded 45 × 45 m floor (diagonal ≈ 63 m). Added `HorizontalStepEnabled` bool to `MovementValidationOptions` (defaults `false`), matching the `DistrictBoundsEnabled` pattern. The horizontal check is now skipped by default; the property remains available to opt back in via config. `MoveRejectFarPad` no longer exercises a horizontal range reject; it retains its walkable surface for vertical-step regression only.
|
||||
|
||||
## Open questions / risks
|
||||
|
||||
- **NavMesh bake time:** A 45×45 floor with several terraces will bake more polygons than the 20×20 baseline. Expected to be negligible for `bake_navigation_mesh(false)` on desktop, but monitor Output for a perceptible pause.
|
||||
- **NEON-7 reject range change:** The server's `MaxDistance` is currently tuned for the old district. Clicks from the far corners of the new floor will be within server range (they're not "too far" in server terms — the server checks xz step size from _current_ position to _target_, not total map size). Confirm no new silent-reject edge cases; document if found.
|
||||
- **Agent climb on terraces:** `agent_max_climb = 0.35` limits single-step height. Ramps must grade gently enough for the bake to include them as traversable, or use stacked step geometry (~0.30 m rise per step). Verify in Godot editor nav mesh debug overlay before shipping.
|
||||
- **Agent climb on terraces:** `agent_max_climb = 0.35` limits single-step height. Each terrace transition is 0.30 m; verify in Godot editor nav mesh debug overlay before shipping.
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ namespace NeonSprawl.Server.Tests.Game.PositionState;
|
|||
|
||||
public class MoveCommandValidationTests
|
||||
{
|
||||
private static MovementValidationOptions Rules(double maxH, double maxV, bool bounds = false) =>
|
||||
private static MovementValidationOptions Rules(
|
||||
double maxH, double maxV, bool bounds = false, bool horizontalEnabled = true) =>
|
||||
new()
|
||||
{
|
||||
HorizontalStepEnabled = horizontalEnabled,
|
||||
MaxHorizontalStep = maxH,
|
||||
MaxVerticalStep = maxV,
|
||||
DistrictBoundsEnabled = bounds,
|
||||
|
|
@ -63,6 +65,15 @@ public class MoveCommandValidationTests
|
|||
Assert.Equal(MoveCommandReasonCodes.HorizontalStepExceeded, code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryValidate_ShouldAllow_WhenHorizontalDisabled()
|
||||
{
|
||||
var from = new PositionSnapshot(0, 0, 0, 0);
|
||||
var to = new PositionVector { X = 1000, Y = 0, Z = 1000 };
|
||||
Assert.True(MoveCommandValidation.TryValidate(from, to, Rules(1, 10, horizontalEnabled: false), out var code));
|
||||
Assert.Equal("", code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryValidate_ShouldIgnoreBounds_WhenDistrictDisabled()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,13 +18,16 @@ public static class MoveCommandValidation
|
|||
{
|
||||
reasonCode = "";
|
||||
|
||||
var dx = to.X - from.X;
|
||||
var dz = to.Z - from.Z;
|
||||
var horizontal = Math.Sqrt(dx * dx + dz * dz);
|
||||
if (horizontal > rules.MaxHorizontalStep)
|
||||
if (rules.HorizontalStepEnabled)
|
||||
{
|
||||
reasonCode = MoveCommandReasonCodes.HorizontalStepExceeded;
|
||||
return false;
|
||||
var dx = to.X - from.X;
|
||||
var dz = to.Z - from.Z;
|
||||
var horizontal = Math.Sqrt(dx * dx + dz * dz);
|
||||
if (horizontal > rules.MaxHorizontalStep)
|
||||
{
|
||||
reasonCode = MoveCommandReasonCodes.HorizontalStepExceeded;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var dy = Math.Abs(to.Y - from.Y);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ namespace NeonSprawl.Server.Game.PositionState;
|
|||
/// <summary>Per-command movement sanity limits (NS-19). Bound from <c>Game:MovementValidation</c>.</summary>
|
||||
public sealed class MovementValidationOptions
|
||||
{
|
||||
/// <summary>Max horizontal displacement on X/Z in one move (meters). Inclusive at the limit.</summary>
|
||||
/// <summary>When true, enforces <see cref="MaxHorizontalStep"/>. False (default) means no horizontal limit.</summary>
|
||||
public bool HorizontalStepEnabled { get; set; }
|
||||
|
||||
/// <summary>Max horizontal displacement on X/Z in one move (meters). Inclusive at the limit.
|
||||
/// Only applied when <see cref="HorizontalStepEnabled"/> is true.</summary>
|
||||
public double MaxHorizontalStep { get; set; } = 18.0;
|
||||
|
||||
/// <summary>Max absolute delta on Y in one move (meters). Inclusive at the limit.</summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue