game.course — SURF VOID's course: layout AND collision, one source of truth.
The course is a STAGE TABLE expanded by a rule into convex solids (surf
ramps = sheared triangular prisms, pads = yawed boxes). The controller
collides against `M.solids` analytically (no physics engine involved), and
the visuals build their meshes from the very same vertices — so what you
see is exactly what you surf on. Change a number in `STAGES`, and both
follow (re-run the scene build to rebake the visual meshes:
`layers.active.asset:build()`).
World frame: metres, +Y up, the course runs toward -Z and descends.
local Course = require("game.course")
Course.solids -- { Solid }, see below
Course.checkpoints -- { Checkpoint } index 1..N (pads after each stage)
Course.start / Course.finish -- the start pad / the finish platform
Course.collideSphere(c, r) -- push a sphere out of every solid
Course.zoneAt(p) -- "start" | "checkpoint", i | "finish" | nil
Course.killY(checkpointIndex) -- below this y the player has fallen
v(x: ?, y: ?, z: ?) → Vec3 return
| arg | type | description |
|---|
| x | ? | |
| y | ? | |
| z | ? | |
basis(yaw: number) → void
heading → forward / right unit vectors (yaw 0 → forward -Z, right +X)
| arg | type | description |
|---|
| yaw | number | |
finalize(solid: ?) → void
Solid = {
name, kind = "ramp" | "pad" | "finish", stage (1-based; pads: the stage they begin, 0 start),
verts = { Vec3 }, faces = { { vertex indices, CCW seen from outside } },
planes = { { n = Vec3, d = number } }, -- outward, dot(n,p) - d > 0 is outside
aabb = { min = Vec3, max = Vec3 },
surf = bool -- true for ramps (their slopes are surf surfaces)
}
ramp(name: ?, stage: ?, origin: ?, yaw: ?, length: ?, halfWidth: ?, height: ?, drop: ?) → void
A sheared triangular prism: base at `origin`, ridge `height` above, running
`length` along `yaw`, the whole thing descending `drop` over its length.
| arg | type | description |
|---|
| name | ? | |
| stage | ? | |
| origin | ? | |
| yaw | ? | |
| length | ? | |
| halfWidth | ? | |
| height | ? | |
| drop | ? | |
P(lat: ?, up: ?, along: ?) → void
| arg | type | description |
|---|
| lat | ? | |
| up | ? | |
| along | ? | |
box(name: ?, kind: ?, stage: ?, top: ?, yaw: ?, halfX: ?, halfZ: ?, thickness: ?) → void
A yawed box whose TOP face centre is `top`.
| arg | type | description |
|---|
| name | ? | |
| kind | ? | |
| stage | ? | |
| top | ? | |
| yaw | ? | |
| halfX | ? | |
| halfZ | ? | |
| thickness | ? | |
P(sx: ?, sy: ?, sz: ?) → void
| arg | type | description |
|---|
| sx | ? | |
| sy | ? | |
| sz | ? | |
checkpointPad(i: number) → void
The pad the player respawns on for checkpoint `i` (0 = the start).
| arg | type | description |
|---|
| i | number | |
spawnFor(i: number) → void
Spawn point (feet) + facing yaw for checkpoint `i`.
| arg | type | description |
|---|
| i | number | |
killY(i: number) → number
Fallen below the stage after checkpoint `i`?
| arg | type | description |
|---|
| i | number | |
insideBoxZone(p: ?, b: ?) → void
zoneAt(p: Vec3) → void
Which zone the feet point `p` stands in: "start" | "checkpoint", index | "finish" | nil
collideSphere(c: Vec3, r: number) → void
Push a sphere (centre `c`, radius `r`) out of every solid it overlaps.
Returns the corrected centre and the list of contact normals (outward
surface normals, unit length) — the controller clips its velocity
against each (Source ClipVelocity) and reads n.y for ground vs surf.
Convex SAT against the face planes: the deepest-overlap axis is the
face with the largest signed distance, which is the push-out direction.
| arg | type | description |
|---|
| c | Vec3 | |
| r | number | |