Sound
Everything *First Light: Crowfeet* is heard through. One component, on the scene's `sound` entity; nothing else in the game plays a sound.
game.audio.Sound
Everything First Light: Crowfeet is heard through. One component, on the
scene's sound entity; nothing else in the game plays a sound.
The mix
| Channel | What is on it | Level |
|---|---|---|
ambient | the two beds — the sea below, the roost above | 0.62 |
music | the two score decks | 0.50 |
sfx | every one-shot | 1.00 |
voice | the Old Crow's nine lines | 1.00 |
Master 0.9. The bed sits under everything: it is the room, not an event.
The climb is audible with your eyes shut
Two ambience beds run at once and cross with the crow's height. amb_sea is
loudest on the strand and thins to about a quarter of itself at the roost;
amb_roost — air over the summit and a few birds that have not settled — comes
up to meet it. So the last ledge sounds nothing like the first, and the light
takes a little more of the sea away as it comes.
The score turns over with the light
music_night and music_dawn are the same sixteen-second drone on the same E,
one with the minor third and one with the major. Both decks run in parallel and
are crossfaded by CF.state.dawn between 0.15 and 0.85 — at dawn 0.5 each sits
at exactly half. There is no track change: what the player hears is a third
turning over as the sky does.
One line, spoken and printed
CF.say(speaker, text, key) fires line. The presentation slice prints the
words; this component plays game/audio/voice/<key>.soundClip if one exists.
The two are independent on purpose: a key with no clip is a line that is quiet,
never a line that is lost. While a line is speaking the beds and the score duck
to 0.40 and come back.
The nine keys are open_1, hunger_low, eat_1, crumble_1, first_light,
won_1, lost_sea, lost_starved and lost_late.
What it answers
hop · land (by hardness — soft under 0.5, hard over) · grab · pullup ·
drop · peck · eat (by food.kind: grub / limpet / crowfoot) · crumble ·
fall · tooWeak · hungerLow · firstLight · phase (the flock lifting on
a win; the sea, the collapse or the murder leaving without you on each loss) ·
reset · pause · line · ui.
CF.emit("ui", which) takes move / confirm / back / pause and their
aliases (nav, select, start, restart, cancel, resume, menu, …);
anything else answers with the quiet tick, so a new screen is never silent.
What another slice can ask of it
local sound = entity.find("sound").component.get("game.audio.Sound")
sound.speaking() --> the line key being spoken, or nil
sound.play("caw", 0.6) --> any recipe by name, for a noise with no event
Notes
Every source is non-spatial: the listener rides the camera and this is a mix,
not a room. The beds loop with the Audio component's own looping field and
are baked codec = "pcm" so they join without a click. Clips come from
game/audio/recipes via game/audio/bake.luau; the voice lines were generated
once and cached as assets — nothing is synthesised or generated at play time.
The blackboard is a module, and a module that reloads under a running session
either becomes a new table or re-runs into the same one with an empty listener
list. Either way a component that registered against the old bus hears nothing
and says nothing about it — the whole soundtrack goes quiet with every reading
still green. So this component does not trust its subscription: every couple of
seconds it emits a private event through the module require hands back now,
and if its own listener did not hear it back, it moves onto the live bus.
Owned by the sound slice. See /zero/source/design/spec.md.
Scoped to this part · feeds back into the world's score.