PURPOSE
Reward-cinematic module that paints a left-to-right rainbow shooting star across the reward card row during the slot_intro HUD state. As the bead crosses each card’s midpoint, the card is revealed and a short-lived burst of glittery sparkle particles is spawned at the card’s center. The trail behind the bead is a tapered rainbow comet painted additively in the overlay pass. Registered for cinematic family 'shooting_star'.
OWNS
- Module-scoped streak state: captured choices array, per-card revealed/sparked flags, sparkle particle pool, intro elapsed timer, last-sampled bead position (
_starX,_starY), cached per-card center coordinates. - The
Sparkleparticle record (position, velocity, life, max life, hue, base size, spin rate, current rotation). - Tunable constants for the streak fraction, bob amplitude, trail length and segment count, sparkles per card, sparkle cap, sparkle lifetime, and the rainbow hue stops.
- Card-center geometry computation that mirrors the HUD’s resting card layout math.
- Rendering helpers for the rainbow comet trail, the bright white bead with warm halo, and a single 4-point sparkle.
- The exported
shootingStarModuleimplementing theCinematicModulecontract (id,onStart,onUpdate,drawCardOverride,drawOverlay,onEnd).
READS FROM
./registryfor theCinematicModuleandCinematicContexttypes../audioforplayCinematicTone, used to trigger the per-card sparkle chime.../hudforsetSlotIntroDuration, used to shorten the slot intro to match the streak’s running time (or to a near-zero value when accessibility skip is on).../../core/config/_accessibilityforisSkipRewardAnimations, the accessibility gate that bypasses the cinematic entirely.- The
CinematicContextfields supplied each frame: viewportWandH,cardW,cardH,state,stateTimer,dt,choices, and the canvas 2D context passed into overlay/card-override callbacks.
PUSHES TO
- The provided
CanvasRenderingContext2Dduring the overlay pass, painting the rainbow trail, the bright white core stroke, the bead’s radial gradient, and each live sparkle. Composite mode is set tolighter(additive) and restored viasave/restore. - The HUD’s slot-intro duration, written through
setSlotIntroDurationon cinematic start (full streak duration, or a tiny value when reward animations are skipped). - The cinematic audio channel via
playCinematicTone, emitting a'sparkle'tone whose frequency rises by card index when each card reveals. - The HUD’s card draw pipeline indirectly, by returning
truefromdrawCardOverridewhile a card is still hidden behind the bead (suppressing the card) andfalseonce the bead has passed it (handing rendering back).
DOES NOT
- Does not advance, schedule, or transition cinematic state — state and timers come from the HUD via
CinematicContext. - Does not select reward choices or mutate
ctx.choices; it snapshots them once for stable layout and ignores later changes to the same array. - Does not render the reward cards themselves, draw their text, glyphs, frames, or selection highlight — that remains the HUD’s responsibility once a card is unsuppressed.
- Does not exit or short-circuit
slot_intro; it shapes the duration on start and otherwise lets the HUD finish the state. - Does not allocate beyond the configured sparkle cap; once
SPARKLE_CAPis reached, further sparkles in a burst are dropped. - Does not handle input, hit-testing, or selection.
- Does not write to module state from the render callbacks; all state mutation lives in
onUpdateandonStart/onEnd.
Signals
- Cinematic start (
onStart): full state reset; on accessibility skip, slot intro is set to a near-instant duration and the function returns; otherwise slot intro is set to a duration that matches the streak’s run time. - Cinematic update (
onUpdate): on the first framectx.choicesis populated, the choice list is snapshotted, per-card flags are sized, and card centers are computed; if the viewport changed mid-cinematic the centers are recomputed. The bead position is sampled from the current state — parked off-screen left duringannounce, eased left-to-right across the screen with a sinusoidal Y bob duringslot_intro, and clamped past the right edge forslot_lock/shine/showing. For each unrevealed card whose midpoint the bead has crossed, the card is marked revealed, sparkles are spawned at the card center, and the card-specific sparkle chime is played. Particles are then stepped byctx.dt. - Card override (
drawCardOverride): returnsfalsewhen reward animations are skipped, when the card has been revealed, or when the state is notannounce/slot_intro; otherwise returnstrueto suppress the card while the bead has not yet arrived. - Overlay draw (
drawOverlay): returns immediately when reward animations are skipped, and again when the streak is not active and no sparkles are alive. Otherwise saves composite state, switches to additive'lighter'blending, paints the trail and bead at the cached bead position with intensity decayed acrossslot_lockandshine, paints every live sparkle, then restores composite state. - Cinematic end (
onEnd): full state reset.
Entry points
shootingStarModule— default export object implementingCinematicModulewith the fixedid'shooting-star'. Registered for the'shooting_star'family by the cinematic registry. Public surface isonStart,onUpdate,drawCardOverride,drawOverlay, andonEnd; all other helpers in this file are module-private.
Pattern notes
- State machine model: this module is a passive consumer of the HUD’s reward-flow states (
announce,slot_intro,slot_lock,shine,showing). It maps each state to a stage of the streak (charge, run, lock decay, shine fade-out, idle) without owning timers. - Reveal contract: the cinematic owns card visibility during
announce/slot_introviadrawCardOverride; once a card is marked revealed it stays revealed across all subsequent states, so the HUD reclaims rendering smoothly. - Snapshot-on-first-frame:
ctx.choicesis copied once and the copy drives layout for the rest of the cinematic, which keeps reveal geometry stable even if upstream rebuilds the choices array. Cached card centers are also recomputed defensively when their length stops matching the snapshot (orientation flip mid-reveal). - Easing: bead travel uses a smooth in-out curve so cards “pop” rather than fly; the bob is a low-amplitude sinusoid on Y for life.
- Trail: 14 tapered horizontal segments, alpha and thickness weighted toward the head, plus a bright white core pass over the front quarter for a hot-bead feel. Drawn with
lineCap = 'round'and additive composite. - Hue palette: a fixed array of seven rainbow stops drives both the trail gradient walk and the sparkle hue assignment, which keeps the streak and the per-card bursts visually consistent.
- Particle pool: sparkles are an
Array<Sparkle>updated in place; dead entries are swap-popped from the tail to avoid mid-array splices. TheSPARKLE_CAPis a hard upper bound on concurrent particles for mobile cost control. - Sparkle draw safety:
k = min(1, life / maxLife)clamps the lifetime ratio so that variable spawn lifetimes can’t drive a negative radius intoarc(). - Accessibility:
isSkipRewardAnimations()is gated inonStart,onUpdate,drawCardOverride, anddrawOverlay; when on, the cinematic short-circuits, the slot intro is compressed to near-instant, all cards are pre-marked revealed, and no draw work is done. - Cost discipline: all per-frame allocation is constrained to fixed-size arrays and per-particle scratch math; per-frame heap allocations from helpers are limited to a single radial gradient for the bead and per-segment HSL strings for the trail.
- Composite hygiene: overlay rendering wraps every additive pass in
ctx2d.save()/ctx2d.restore()and resetsglobalAlphabetween intensity-scaled passes, so the caller’s composite state is preserved.