Shaft
The authored course of WICK, as data: where the granite is, where it is open, what is on it, and which way it leans.
Shaft
The authored course of WICK, as data: where the granite is, where it is open, what is on it, and which way it leans.
game.Climb simulates against these records and the lighthouse dressing is
built from the same ones, so what punishes the player and what the player can
see are the same thing by construction. Nothing here mutates the blackboard.
local Shaft = require("game.Shaft")
local course = Shaft.segments()
What segments() returns
| Field | What it holds |
|---|---|
version | The course revision. Bump it when the shape changes. |
walls | Solid spans: { id, side, y0, y1, x0, x1 }. x0 / x1 are the inner-face distance from the shaft axis at each end, so a leaning span is built leaning. |
windows | Openings: { id, side, y0, y1 }. No granite here — a kick into one catches nothing. |
hazards | { id, kind, side, y0, y1, y, amplitude?, period?, duty?, phase?, reach? }. |
leans | { side, y0, y1, delta } — where the face is not plumb. |
lamp | { y } — the lamp-room floor. |
bounds | { y0, y1 } — the lowest and highest height worth dressing. |
side is -1 for the left wall and 1 for the right. A chain carries
side = 0: it hangs in the middle of the shaft.
Hazard kinds
kind | What it does |
|---|---|
spike | Rusted iron. Always live. |
wire | Sparking wire, live on a duty cycle — read Shaft.hazardLive(h) every frame and drive the spark from it. |
plaster | Holds a landing for a moment, then comes away once. Shaft.isConsumed(h.id) afterwards; that patch is bare for the rest of the run. |
chain | Swings across the shaft at one height. Shaft.hazardX(h) is where it is right now. |
Queries
| Call | Answers |
|---|---|
Shaft.faceX(side, y) | The inner face of that wall at that height, in metres from the axis. |
Shaft.clingX(side, y) | Where the cat's centre sits clinging there. |
Shaft.hasWall(side, y) | Is there granite to catch? |
Shaft.windowAt(side, y) | The opening at that height, if any. |
Shaft.hazardAt(side, y) | The wall hazard standing there, live or not. |
Shaft.hazardsBetween(y0, y1) | Everything overlapping a height range — what a dresser asks for when it only builds what the camera can see. |
Shaft.byId(id) | One hazard by its id. |
Shaft.hazardLive(h, t?) | Is it dangerous at this instant? |
Shaft.hazardX(h, t?) | Where it is, in metres from the axis. |
Shaft.chainHitting(x, y, t?) | The chain whose sweep covers that point right now. |
Shaft.isConsumed(id) / Shaft.consume(id) / Shaft.reset() | The once-only state. game.Climb resets it at the top of a run. |
t defaults to Core.state.elapsed, which is the clock the rules run on — so
a dresser that leaves it out is animating on exactly the clock the simulation
is using, and the chain on screen is the chain that hits.
The curve
| Band | What is in it |
|---|---|
| 4 – 25 m | Clean granite both sides. Nothing but the rhythm and the sea. |
| 25 – 45 m | The first window slits, the first spikes. |
| 45 – 70 m | Sparking wire and crumbling plaster; the windows widen. |
| 70 – 95 m | The first swinging chain, the wall leaning inward so the rhythm breaks, wire and spikes together. |
| 95 – 120 m | Everything at once, two more chains, the long windows, one stretch where the wall bulges out — then 117 – 120 clean, so the lamp is reached and not stolen. |
Scoped to this part · feeds back into the world's score.