Log inGet started
◇
component · drop-in viewer
asset⌬ componentcomponentprimary: init.luau·originates fromworld b0f23b4e-e…

ui

<!-- zero:scaffolded-readme --> <!-- Replace the line below with what this component provides and who consumes it, then delete both comment lines. -->

byPolar Night·posted 1mo ago
What it does

ui

<!-- zero:scaffolded-readme --> <!-- Replace the line below with what this component provides and who consumes it, then delete both comment lines. -->

TODO: describe this component.

Interface

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

conforms to

zero/source-extract/v2

firstlight.ui — First Light's interface, on the GameDirector entity. The HUD reads Rules.state every play frame and redraws when a reading changes; the title, death, First Light, sleep-fade and frostbite screens answer Rules.events. The trees live in firstlight.ui.*; this file only decides what shows when.

Audio

hide(name: ?) → void

argtypedescription
name?

hideAll( ) → void

registerAll( ) → void

showTitle( ) → void

showHud( ) → void

showDeath(payload: ?) → void

argtypedescription
payload?

showWin(payload: ?) → void

argtypedescription
payload?

soundSource( ) → void

clickClip( ) → void

The two UI clips, resolved once each; `false` records a clip that is absent.

toneClip( ) → void

play(c: ?, opts: ?) → void

argtypedescription
c?
opts?

click( ) → void

tone(pitch: ?) → void

argtypedescription
pitch?

asModule(ok: ?, mod: ?) → void

argtypedescription
ok?
mod?

requireForecast( ) → void

requireInteract( ) → void

requireLegacy( ) → void

forecastForTomorrow(dt: ?) → void

argtypedescription
dt?

currentPrompt(dt: ?) → void

argtypedescription
dt?

tickHud(dt: ?) → void

argtypedescription
dt?

tickOverlays(dt: ?) → void

argtypedescription
dt?

titleDismissed( ) → void

Any key or click after the title has settled dismisses it.

awake( ) → void

say(p: ?) → void

argtypedescription
p?

death(p: ?) → void

argtypedescription
p?

firstLight(p: ?) → void

argtypedescription
p?

sleep( ) → void

bear(p: ?) → void

argtypedescription
p?

newRun( ) → void

update(dt: ?) → void

argtypedescription
dt?

editorUpdate(dt: ?) → void

argtypedescription
dt?

onCallback(id: ?, data: ?) → void

argtypedescription
id?
data?

onDestroy( ) → 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.

41+items
# a_single_low__solemn_resonant_tone__like Managed audio asset. Edit the `.metadata` `settings` block to change compression.
▲ 0↑ born
▣
module · born here
❒asset
# firstlight.legacy What the world remembers of a survivor. `require("firstlight.legacy")`. - `Legacy.note(text)` — add a line to the current day's journal entry (deduplicated). Any slice may call it for a notable event. - `Legacy.journalLine(day)` — the line for `day`: clock, weather words, stove, stores, frostbite, the bear, the notes. - `Legacy.recordDay(day)` — append that line to `Rules.state.journal`. - `Legacy.die(cause, day?, emitted?)` — end the run: `run.alive = false`, `run.deaths += 1`, the last journal line, the cache (a quarter of every store, on `Rules.state.run.cache`), the save. Emits `death { cause, day }` unless `emitted` says the event is already on the bus. Returns false when the run was already over. - `Legacy.newRun()` — the next run from the death screen. Resets body, clock and stores to the start values plus the cache; keeps journal, seed, deaths, `hut.gaps` and `stores.holeDrilled`. Emits `newRun { deaths, cache }`. - `Legacy.cache()` — the cache the last survivor left, or nil.
▲ 0↑ born
# a_single_soft__muted_UI_click__like_a_br Managed audio asset. Edit the `.metadata` `settings` block to change compression.
▲ 0↑ born
▣
module · born here
❒asset
# hud <!-- 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
# firstlight.interact What the player is looking at and can use. A registry of entities that answer to the interact key, a per-frame raycast from the player's camera that finds which one is under the reticle, and `current()` for the HUD to draw the prompt. ```lua local Interact = require("firstlight.interact") ``` ## API | Call | What it does | |---|---| | `Interact.register(entity, { label, onUse, key? })` | Make an entity usable. `entity` is a proxy or an id. `label` is a string, or a function returning the label for this moment — or `nil` when there is nothing to do right now (an empty crate, a trap already set), which hides the prompt. `onUse` runs on the interact key. `key` overrides the legend (default `"E"`). | | `Interact.unregister(entity)` | Forget it. | | `Interact.current()` | `{ label, key, entity, distance }` for the target under the reticle within reach, or `nil`. The HUD draws the reticle prompt from this. | | `Interact.use()` | Use the current target. Returns whether something was used. | | `Interact.scan(camera, playerRoot)` | The per-frame raycast. `firstlight.sites` calls it every play frame from the local player's camera; nothing else needs to. | | `Interact.isRegistered(entity)` / `Interact.targets()` | Introspection. | | `Interact.REACH_M` | Reach in metres (3). | A hit on any descendant of a registered entity counts as a hit on it, so a bundle's mesh children and a collider child resolve to the prop. Targets need a collider for the ray to find them; `firstlight.sites` fits an invisible `interact_hit` box to anything it registers that has none. ## Registering from another slice ```lua local stove = entity.find("stove") Interact.register(stove, { label = function() return if Rules.state.hut.stoveOn then "Stoke stove" else "Light stove" end, onUse = function() stove.component.get("firstlight.stove"):light() end, }) ``` The interact key is the `interact` control of `@builtin::inputMaps.default` (E on a keyboard, the west face button on a pad, an on-screen button on a phone). `firstlight.sites` owns that subscription and calls `Interact.use()`.
▲ 0↑ born
▣
module · born here
❒asset
# bus <!-- 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
# firstlight.rules The one module every part of First Light requires: `require("firstlight.rules")`. It holds the game's numbers (day length, temperatures, calorie burn, stove fuel rates, bear ranges, starting stores), the layout of the station and the sites around it, the shared blackboard `M.state`, the event bus `M.events`, and the clock. ## The clock `M.tick(dt)` is called once per play frame by the scene entrypoint. It advances `M.state.run.elapsed`, derives `M.state.clock` (day, fraction of day, hour, sun elevation, twilight openness) and emits `day`, `noon` and `firstLight`. `M.sleepToMorning()` jumps to 07:00 next day; `M.restore(elapsed)` rebuilds the clock from a persisted run. Pure helpers: `sunElevation(day, t)`, `twilightSeconds(day)`, `airC(day, t)`, `windChill(airC, windMs)`, `progress(day)`, `rng(seed, salt)`, `dist2d`. ## The blackboard `M.state` has one section per slice. A slice writes its own section and reads the others: `run`, `clock`, `weather`, `body`, `hut`, `stores`, `bear`, `journal`. ## Events `M.events.on(name, fn)` returns an unsubscribe function; `M.events.emit(name, payload)` calls every listener. The names and payloads are listed at the top of `init.luau`. `M.say(text, seconds)` emits `say` for the HUD.
▲ 0↑ born
▣
module · born here
❒asset
# firstlight.forecast The thirty-day forecast of First Light, as a pure function of the world's forecast seed (`Rules.state.run.seed`). The same seed gives the same thirty days on every load, so a journal line about the weather is true for the next run. ```luau local Forecast = require("firstlight.forecast") ``` ## For the HUD and the journal ```luau -- Tomorrow's barometer reading local clock = Rules.state.clock local tomorrow = Forecast.forDay(clock.day + 1) -- tomorrow.storm 0..1 on the storm scale -- tomorrow.windMs m/s -- tomorrow.airC °C at noon, before wind chill -- tomorrow.aurora 0..1 -- tomorrow.band "calm" | "wind" | "storm" | "blizzard" -- tomorrow.blizzard true on one of the trapped days Forecast.words(tomorrow.band) -- "Calm" | "Wind" | "Storm" | "Blizzard" ``` Days clamp to 1..30, so `forDay(31)` is day 30. `Forecast.blizzardStart()` names the first trapped day; the blizzard runs `Rules.BLIZZARD_DAYS` days from there, between `Rules.BLIZZARD_EARLIEST_DAY` and `Rules.BLIZZARD_LATEST_DAY`. ## The live weather What the air is doing right now is on the blackboard, written every frame by the weather component: `Rules.state.weather` carries `storm`, `windMs`, `windDir`, `visibilityM`, `airC`, `chillC`, `snowing` and `aurora`. Read it from there rather than recomputing it. `Forecast.at(day, t)` is the function behind that: neighbouring days blended across the day, gusts added from a seed-fixed phase, and the whole moment derived. `Forecast.moment(storm, day, t)` derives the same record from one storm value. ## The scale | band | storm | wind | visibility | |---|---|---|---| | calm | below 0.175 | a breath | the horizon | | wind | 0.175 .. 0.525 | 6 .. 12 m/s | hundreds of metres | | storm | 0.525 .. 0.85 | 12 .. 22 m/s | tens of metres | | blizzard | 0.85 and up | up to `Rules.WIND_MAX_MS` | a few paces | `Forecast.band(storm)`, `Forecast.windMs(storm)`, `Forecast.visibilityM(storm)` and `Forecast.snowing(storm)` are the curves behind the table. The wind on the plain always blows toward `Forecast.WIND_TOWARD` (east-north-east in xz), the way the snowfield's sastrugi and spindrift stream; `Forecast.WIND_DIR` is that as radians.
▲ 0↑ born
▣
module · born here
❒asset
# screens <!-- 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 · firstlight/ui.component

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.