Runner
The madcap. Mounted by the scene entrypoint on the local player's body — the entity that carries the avatar's movement kit — so this component and the `CharacterController` it drives are siblings on one entity.
Runner
The madcap. Mounted by the scene entrypoint on the local player's body —
the entity that carries the avatar's movement kit — so this component and
the CharacterController it drives are siblings on one entity.
Controls
game.loop.run (the .inputMap beside this folder) is pointed at the
CharacterController on the same entity, which is what makes this game's
scheme the live one: move and jump are read by the controller itself,
camera-relative, and dash and shove are read here. Every one of the
four carries keyboard, gamepad and touch, so the on-screen phone layout
is those bindings — nothing in this slice draws a button.
The map's group is run, which is what uimenu / uipause stand down
while a card is up. It in turn suppresses player, the engine's default
scheme, whose look / zoom / crouch / interact / pointer-lock controls this
game has no use for and whose touch buttons would otherwise crowd the four
that matter.
The controller holds the map whether or not the race is running, so the
gate on "can the runner move" is not the map — it is moveSpeed and
jumpForce, both taken to zero in every phase but play.
The madcap meter
Recklessness banked as speed, and the meter is the speed: at full it
adds 45% to MC.RUNNER.runSpeed. A dash pays madcapPerDash in, a dash
taken off the ground pays madcapPerJumpDash, a rival passed pays
madcapPerShove, and it bleeds at madcapBleedPerSecond the whole time —
so a cautious run arrives with an empty meter. A dash overrides the meter
outright with dashSpeed for dashSeconds.
The censer
MC.state.flame (0..1) and MC.state.flameLit. Dashing feeds it wind and
it guts at flameGutterPerDashSecond; a rival's shove puts it out
outright; standing within relightRange of any brazier_<tier>_<a|b>
brings it back and emits flameLit. An unlit censer at the altar is not a
win — the altar arrival is reported either way, and Game refuses it.
The climb
MC.state.height is the runner's world y and MC.state.tier the highest
terrace stood on, read against MC.tierTopY. Each new terrace emits
tierReached. The altar is MC.altar() within altarRange of ground
distance, which emits loop:altarReached once per approach.
Coming off the temple
Every terrace is a ledge and every flight's foot stands three to four metres clear of the terrace below it, so a madcap falls. A fall that leaves the world — below the plain, or more than 2.2 s in the air, which is longer than any jump or drop between terraces — puts the runner back on the terrace they had reached, two metres in from the face, with the meter emptied. A race that can end with the runner falling for ever is not a race.
Fields
| field | default | meaning |
|---|---|---|
relightRange | 2.5 | metres from a brazier at which the censer catches |
altarRange | 2.2 | ground metres from MC.altar() that count as arriving |
dashCooldown | 0.8 | seconds between one dash and the next |
dashing / tier | — | live readouts |
Methods
:status()— position, grounded, move speed, which map the controller reads, how many braziers it found, and the whole ofMC.state's runner half.:toStart()— back toMC.startPosition(), as a restart does.:teleport(x, y, z)— put the runner anywhere; every rule above still applies from where it lands, which is how a test climbs the temple without playing it in real time.
Scoped to this part · feeds back into the world's score.