Log inGet started
·
scene · drop-in viewer
asset⌬ scenesceneprimary: entrypoint.luau·originates fromworld b29bf57b-3…

main

No description provided.

byclaude-code @ aura·posted 3mo ago
What it does

No description provided yet.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

Lamplighter's Round — scene bootstrap. OWNED BY THE PLANNER. Build agents: do not edit this file or scene.json. Everything you need is reachable through `game.core`; if you think you need a scene change, you almost certainly need a Core interface instead. This file does exactly three things: 1. loads the three slice modules, 2. calls build / start / update / stop on each at the right moment, 3. isolates each slice behind a pcall, so one agent's half-written module cannot take the other two down while they are all working live. The requires are static and must stay static: the engine refuses a computed require in a scene entrypoint, because it cannot be pinned when the world is imported somewhere else.

guard(name: string, mod: ?, hook: string, ...: ?) → void

Call `fn` on a slice; log and quarantine it if it throws, so a broken slice degrades to "does nothing" rather than killing the frame for everyone else.

argtypedescription
namestring
mod?
hookstring
...?

each(hook: string, ...: ?) → void

argtypedescription
hookstring
...?

rebuild( ) → void

Rebuild the world from scratch. Hot-reloading a slice re-runs this, so every slice's `build` must stay idempotent.

onEditLoad( ) → void

Edit mode: load the slices, but build NOTHING. Every entity in this game is created by code on load, so anything standing in the world during edit is an unbaked live edit — and `zm commit` refuses to run while a scene has those. Auto-building here would block all three build agents' commits all session. To look at the district while authoring, flip to play, or call `require("game.district").build()` by hand and despawn the `District` root again before you commit.

onLoad( ) → void

Play mode: build (idempotent), then start the round.

update(dt: number) → void

argtypedescription
dtnumber

onUnload( ) → void

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

Showing up to 3 levels deep. Open a part to see what it holds further down.

20+items
▣
module · born here
❒asset
# rules The round loop of *Lamplighter's Round*: Dread, oil, the hold-to-light interaction, the Ember economy, the prelude, and the win/lose resolution. `require("game.rules")`. It reads `game.core` and writes only the state fields Core names it the owner of. ## The shape of the round You carry a lantern. It is not light enough to see by — it is light enough to keep your nerve for a little while. **Dread** rises whenever you are away from a burning lamp, slowly at first and faster the longer you stay out, and falls hard the moment you step into gaslight. The lantern buys you most of a minute; walking burns it, running burns it faster, and when it is dry the dark has you at the full rate. Lighting a lamp is the one thing that helps and the one thing that costs. You hold the taper, and **while you hold it you cannot move at all** — the rules write the body's walk speed to zero. Letting go does not throw the attempt away: the flame gutters, draining back at 1.5× the fill rate, so a flinch costs you and a change of mind is final. An attempt that got past 15% and then died emits `lampFailed`. The Pale takes you **in the dark**. Under a burning lamp it can reach you and it costs 38 Dread, but it cannot have you — the gaslight is the sanctuary the whole round is walked for. That branch lives here because the round's ending is the rules' to own. ## Measured behaviour Every number below was read out of a live round, not derived: | | | |---|---| | Standing still in the dark, full lantern | dead in **32.7 s** (`cause = "dark"`) | | A walked 11 m crossing | +8 Dread, −8 oil | | Holding the taper at the far end | peaks at **~18** Dread | | The lamp taking | Dread **0** again inside 1.2 s, oil full | | Lantern dry | the Dread rate roughly doubles; ending `cause = "dry"` | | A full nine-lamp round | won at **0:18** clear, **0:48** with the Pale hunting | ## Upgrades One Ember per lamp, so a round pays out nine. Rank *n* costs *n* Embers and each track stops at rank 3, so a maxed track costs 6 of the 9 — you can max one and open a second, never all three. Buying is refused unless you are standing in lamplight. | Track | Rank 0 → 3 | |---|---| | `reach` | interact range 3.0 → 6.6 m | | `stride` | walk speed 2.5 → 3.7 m/s (written to the body's CharacterController) | | `wick` | light time 2.6 → 0.9 s | A full Dread bar also adds 30% to the light time — shaking hands — which is the reason to rest before crossing rather than after. ## What the HUD calls ```luau local Rules = require("game.rules") Rules.buyUpgrade(name) -- -> ok, reason spend an Ember Rules.canBuy(name) -- -> ok, reason why a button is dimmed Rules.upgradeCost(name) -- -> cost | nil nil once the track is maxed Rules.upgradeRank(name) -- -> rank, maxRank Rules.upgradeEffect(name) -- -> "4.2 m" the number it changes, now Rules.UPGRADES -- ordered { name, label, blurb } Rules.prompt() -- -> the line to print near the reticle Rules.stats() -- -> everything the meters and end cards want Rules.targetLamp() -- -> lamp, index, distance, inReach Rules.setLightHeld(down) -- drive hold-to-light from a screen button Rules.restart() -- begin a fresh round from a win/lose card ``` `stats()` returns `phase cause time lampsLit lampTotal dread dreadFrac oil oilFrac embers lightProgress lighting inLamplight lampLight darkTime sinceLastLamp reach lightSeconds targetDistance targetInReach held`. ## Input Hold-to-light is the engine's own **`interact`** control on `@builtin::inputMaps.default` — `KeyE` on a keyboard, the west face button on a pad, an on-screen button on a phone. The module activates that map and subscribes to the control rather than polling a key code, so all three devices work with no extra plumbing. `setLightHeld` ORs a HUD button on top of it for a bespoke on-screen control. ## Notes for the other slices * Lamp positions are read from `Core.lamps` every frame — move the posts wherever the street wants them; nothing here assumes a layout. * `Core.state.paleDist` feeds a Dread multiplier that rises to 2.2× as the Pale closes inside 14 m. While that field is left unwritten the multiplier sits at 1 and the pressure never engages. * Subscriptions are made in `build()`, never at module scope: the scene entrypoint clears the event bus on every rebuild, so a handler registered once at load is silently dropped the first time any slice hot-reloads.
▲ 0↑ born
▣
module · born here
❒asset
# game.dread — the Pale, the HUD, and how fear feels Slice C of *Lamplighter's Round*. Three things live here. ## 1. The Pale A three-metre hooded figure built from primitives — a hem pooling on the cobbles, a heavy skirt, a hunched shoulder line, a narrow hood, arms too long for the body — with a cold light where a face should be. It carries its own point light, because the fog swallows anything that does not. It hunts on the vendored `vendor.stalker` brain, driven by the game's central rule and nothing else: > **Lamplight makes you safe but visible. The dark hides you from the Pale and > feeds your Dread.** So `Core.lightAt(player)` is fed straight in as the stalker's `visible`, `hidden`, `exposure` and `flashOn` senses. Standing under a burning lamp is what lets it see you across the district; standing in the dark is what makes it lose you — and what fills the meter that kills you. The moment it takes an interest is deliberately unmistakable: the face flares and its light swells, the screen stings red, and the HUD says IT HAS SEEN YOU. This slice writes `Core.state.paleState` and `Core.state.paleDist` and nothing else, and emits `paleCaught` when the Pale reaches the player. ## 2. The feel — the `dreadveil` post pass One fullscreen effect, registered at priority 240 on the `scene` layer so it runs after the district's fog, bloom and tonemap and never dims the HUD. Three properties, pushed every frame: | Property | Driven by | What it does | |---|---|---| | `dread` | `Core.state.dread` | drains the colour, cools what is left, and closes the vignette from a wide frame to a keyhole | | `beat` | a double-thump heartbeat envelope, 52 bpm calm to 138 bpm at full dread | squeezes the vignette and warms the rim on every thump | | `sting` | the Pale noticing you, decaying at 1.55/s | bruises the rim red and flares the frame cold | The bruise is keyed to the screen radius rather than to the vignette, so a closed keyhole reddens its own rim instead of washing the whole frame red. ## 3. The HUD driver `ui.hud` owns every pixel; this module owns the snapshot. It reads `Core.state` for the meters and the rules slice's own published surface for everything the player is offered: - `Rules.UPGRADES` / `upgradeRank` / `upgradeCost` / `canBuy` / `upgradeEffect` → the three upgrade rows, including the exact reason a row is dim and the number that rank currently buys - `Rules.prompt()` → the line under the reticle - `Rules.buyUpgrade(name)` → what a click on a row does - `Rules.restart()` → what WALK IT AGAIN does The slow half of the HUD refreshes at 7 Hz and the focus screen at 18 Hz, rising to every frame while a taper is held or a sting is fading. ## Inspecting it `require("game.dread").debug()` returns the live hunt reading, the sting and heartbeat envelopes, the veil's registration and whether the HUD is mounted.
▲ 0↑ born
▣
module · born here
❒asset
# district <!-- zero:scaffolded-readme --> <!-- Replace the line below with what this module provides and who consumes it, then delete both comment lines. --> TODO: describe this module.
▲ 0↑ born
▣
module · born here
❒asset
# core <!-- zero:scaffolded-readme --> <!-- Replace the line below with what this module provides and who consumes it, then delete both comment lines. --> TODO: describe this module.
▲ 0↑ born
backing path · scenes/main.scene

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.