Repository navigation
Slice 1: Mount on a sampled slope through a Coverage window module #76
Description
What to build
F6 mounts Skate on gentle sloped ground where the player stands, from sampled surfaces (GLOSSARY.md), instead of refusing anything that isn't flat. The sampling and triangle logic is built as its own Coverage window module, so every ground case is a seconds-fast test against an authored world. Decision record: docs/adr/0004-live-host-queries-supply-riding-surfaces.md (proposed).
Decisions already made
- Coverage window module. It takes a ground probe and the rider's position, and returns either triangles or a coverage gap with its reason. It makes no native calls itself:
mount()passes a probe that wrapsGET_GROUND_Z_FOR_3D_COORD, and tests pass an authored world (flat, slope, curb, wall, curved ground, unloaded). Check how the game loadscombine_skate.jsbefore choosing the packaging; if it can't load modules, keep the module in the same file behind a seam that only the tests export. - Sampling. Keep the existing 5×5 grid at 2 m spacing (±4 m). Query from rider z + 2, so the floor under the rider is the one sampled; rooftops and the road under a bridge need no stacked-floor support.
- Triangles. 2 per grid cell (32 in total), uploaded through the existing
COMBINE_SKATE_SURFACE_BEGIN/SURFACE_VERTEX→COMBINE_SKATE_MOUNT_SURFACE(layer 1). This replacesCOMBINE_SKATE_VERTEX/COMBINE_SKATE_MOUNTon the no-SURFACESroute. - Coverage gaps at mount. Mount is refused with a stated reason for: a non-finite sample, a near-zero (unloaded) sample, or a height jump between neighbouring samples above the wall threshold. The threshold is a named constant starting at 0.3 m.
- Window edge. The window doesn't move yet (Slice 2 adds that). Riding out of it is a coverage gap: dismount with the reason "window edge". The riding tick must not hand a sampled window to
refreshSurface, which only draws from authoredSURFACES. - The flat patch is removed from the JS. The Rust flat path stays until Slice 2. The authored static
SURFACESroute is untouched.
Known risk to test first
While riding, the script requires GTA ground to be within 0.05 m of the Skate floor (combine_skate.js riding tick, about line 356). Triangles built from 2 m samples can differ from real ground by more than that between samples, which would dismount the rider every frame. Add an authored world whose ground bends between samples and report the maximum error. If it exceeds 0.05 m, stop and bring the options to the owner (denser sampling vs a different check) rather than loosening the 0.05 m value.
Acceptance criteria
- Coverage window module tests with authored worlds: flat, slope and curb-below-threshold give triangles; wall-above-threshold, non-finite and near-zero give a gap with its reason; curved ground reports its interpolation error against 0.05 m.
-
tests/script.cjs: F6 on a slope reachesCOMBINE_SKATE_MOUNT_SURFACEwith 32 triangles; each refusal notifies its reason and GTA control stays with the player. - Riding out of the window dismounts with the reason "window edge" and restores GTA control.
- The JS flat-patch branch is gone; the existing authored-
SURFACEStests pass unchanged. -
python3 scripts/verify.pypasses. - Owner runtime check, one session built from
main:- at one slope spot: mount → push → steer both ways → dismount → walk;
- at one wall spot: mount is refused with its reason.
- Log raw samples privately (outside Git) so they can be replayed as fixtures.
- ADR 0004 changes to accepted if the runtime ride passes.
Out of scope
Moving the window, triangle-only Rust cleanup (Slice 2), stacked-floor transitions, walls from line-of-sight, vehicles as floors, choosing between the in-process and worker Session backends.
Activity
Jpatching commentedon Oct 18, 2026
Known risk resolved by owner decision, in PR #78:
Still open on this issue: the owner's in-game check (slope ride, wall refusal), then ADR 0004 → accepted.
Mount on a sampled slope through a Coverage window module (#76)
Ride on floor agreement at the wall threshold (#76)
Jpatching commentedon Oct 22, 2026
Closed to reset the tracker to a fresh state.