highway
The four lanes, the receptors, the note bodies, and everything that happens to a note when it is hit, held or missed. It is the surface the player actually reads, so its whole job is to stay legible while the venue is at its loudest.
game.highway — the note field
The four lanes, the receptors, the note bodies, and everything that happens to a note when it is hit, held or missed. It is the surface the player actually reads, so its whole job is to stay legible while the venue is at its loudest.
It is driven entirely by ST.bus events and by the song time the scene
entrypoint hands to tick. It never asks the conductor for anything and it
never touches another slice's entities. Everything it makes hangs under
ST.anchor("Highway").
local ST = require("game.core")
local highway = ST.need("game.highway") -- nil until this slice mounts
How a note gets on screen
The conductor publishes note.spawn; the highway takes a pooled body,
shows it, and moves it every frame to
z = (noteTime - songTime) * ST.WORLD.noteSpeed
from the receptor plane. Nothing else decides where a note is. A note
leaves on note.hit, on note.miss, or — as a backstop, if no judgement
ever arrives — once it is ST.RULES.missAfter + 0.35 s past its time.
Bodies come from a fixed pool (10 tap slots and 3 hold slots per lane) and
are only claimed once a note's approach window opens, so a chart may
publish its whole note list at duel.begin without allocating anything.
Exports
M.mount()/M.tick(dt, songTime)/M.unmount()— the slice contract.M.laneX(lane) -> number— where a lane actually is in world x, in screen order. Lane 1 is the leftmost lane the player sees. Use this, notST.WORLD.laneX— see "Lane order" below.M.corridor— the protected volume, in world metres.M.report() -> table— live counts: notes in flight, pool pressure, hits, misses, whether the pool has ever starved.M.demo(seconds)/M.demoStop()— run the note field on its own clock with no conductor, no audio and no play mode. It publishes exactly the bus events the conductor publishes, so it exercises the real path.M.buildArt() -> boolean— write the meshes and materials into/source/art/highway/. Already done and committed;mountonly fills gaps.
Bus
Subscribes to note.spawn, note.hit, note.miss, hold.begin,
hold.end, duel.begin.
Publishes highway.corridor once at mount, carrying M.corridor.
Lane order
ST.WORLD.laneX is written in ascending world x. The gameplay camera looks
down +Z with +Y up, which makes world -X the right of the screen —
measured, by spawning a marker at x = -4.5 and capturing it. Drawn
literally, laneX[1] therefore lands on the right and the D key would be
the rightmost lane.
The highway draws lane 1 leftmost and takes the world x values in reverse.
M.laneX(lane) returns the resolved position; anything that wants to put a
laser, a hand or a light over a lane should ask for it there.
Legibility
The spec's section 4 is kept by four separate mechanisms, so no single one failing takes readability with it:
- Shape. One silhouette per lane — lane 1 a hishi diamond, lane 2 a maru circle, lane 3 a kikkō hexagon, lane 4 a masu chamfered square. A white strobe frame destroys colour; the outline survives it.
- Outline. Every note and receptor carries a gold screen-space outline. It is drawn whatever is behind the note, at a width that does not shrink with distance, so a note 20 m up the highway still has a hard contour.
- Ground. The deck is near-black lacquer and the lane glow is dim, so the contrast ratio at the receptor stays high whatever the rig does.
- The corridor.
M.corridoris the box nothing opaque or bright may enter during play: x ±2.85, y -0.6 to 3.4, z -1.5 to 16.5. The highway keeps its own geometry inside it too.
A hit and a miss are built to be told apart with no reading at all: a hit expands, rises and goes gold; a miss contracts, sinks and is the only red on the highway, and the missed note itself goes dark red and drops through the deck.
Notes
- The art assets in
/source/art/highway/are generated by the code in this module and are committed.asset.createcalled frommount()during a scene load does not land, soM.buildArt()is the authoring entry point;mountonly fills gaps and never depends on the write. mount()destroys everything under the Highway anchor before rebuilding, so a scene reload never doubles the track.tintBlenddefaults to 0 and a tint does nothing until it is raised — but raised to 1 with a white tint it replaces the material colour. Geometry that should wear its material's own colour passes no tint.
Scoped to this part · feeds back into the world's score.