Reveal
Why you’d want this
Section titled “Why you’d want this”The core chaos loop is suspense: a viewer pays, the stream holds its breath while a reel spins, and then it lands on you. “Pick one of N, build tension, apply” recurs everywhere: a random live monster gets enraged, a random spawn point erupts, in co-op a random marine gets cursed.
Reveal is the platform primitive for that moment. The bridge does the roll in its trusted process, holds for a declared duration while a pack-agnostic overlay animates toward the result, then dispatches the outcome. You get an on-stream reveal without hand-writing the timed hold and result-plumbing in your Adapter, and because the roll is bridge-side, every paid outcome is auditable and defensible under a refund dispute.
Reveal has two sources, depending on where the candidates come from:
authored: the candidates are a wheel of your events, composed by the broadcaster. This is a Composite event.state: the candidates are resolved at redeem time from live game state (e.g. the marines currently in the co-op lobby, the live spawn points). Declared on the event, described below.
Both share the same machinery and the same overlay.
The state source
Section titled “The state source”When the target set only exists at dispatch time, declare a reveal block on the
event pointing at a pack-state key:
[events.curse.reveal]source = "coop_players" # pack-state key to roll overoptions = "marines" # array path within that key's valuelabel = "name" # per-element field used as the reel labelfilter = "non-null" # drop empty slots; roll only over live marineschosen_param = "target" # param to stamp the rolled index intorevealMs = 2500At redeem the dispatcher:
- reads
sourcefrom its live state store and resolves theoptionsarray path; - applies
filter(non-nulldrops empty/absent entries, so a half-full lobby never rolls an empty slot); - rolls uniform over the survivors;
- holds
revealMs, labelling the reel from each element’slabelfield; - re-dispatches the same event with
chosen_paramstamped with the rolled 0-based index.
The contrast with authored: a composite dispatches a different (member)
event; a state reveal dispatches the same event, having filled in one
target parameter. Your Adapter receives curse with target already chosen and
just applies it.
source must reference a declared state key, filter must be non-null, and a
reveal block is rejected on a kind = "composite" event (composites own their
authored reveal). All of source/options/label/filter/chosen_param are
part of the manifest hash.
The target_reveal key
Section titled “The target_reveal key”Whichever source rolled, the dispatcher writes one self-describing, read-only pack-state key the overlay renders:
{ "invocationId": 412, "event": "curse", "options": [{ "id": "0", "label": "RIPNTEAR" }, { "id": "1", "label": "DOOMGUY" }], "chosenIndex": 1, "revealMs": 2500, "status": "revealing"}Because the key carries its own option labels and chosenIndex, one
pack-agnostic overlay renders every reveal: the bridge learns no Pack
semantics. options[].label is optional: the state source fills it (the marine
name); the authored source omits it and the overlay falls back to the id.
status moves "revealing" → "revealed". The overlay never writes back; it is
a pure renderer (see Overlays and the read-only
data plane).
See also
Section titled “See also”- Composite events: the
authoredsource. - Pack state & the data plane: the state a
statereveal rolls over, and wheretarget_reveallives. - Overlays: rendering the reveal on stream.