Loot Chest Unlock
- Skia
- Reanimated
- Haptics
A chest that strains, splits along its seam and throws light and embers out of the gap. Four rarities run through the same code: nothing branches on tier, it only reads different numbers from a table.
Purchases are not open yet.
Rarity is a table, not a branch
Each tier carries a tint, an edge colour and a blade count that climbs from ten to twenty-four. The shader, the card frame and the seam all take those values as inputs, so a legendary unlock is grander than a common one without a single conditional in the render path. Adding a fifth tier means adding a row.
export const RARITY_TIERS: readonly RarityTier[] = [ { name: 'COMMON', color: '#9BB0C9', accent: '#5C6E86', rayCount: 10 }, { name: 'RARE', color: '#3FA9FF', accent: '#1E5FB0', rayCount: 14 }, { name: 'EPIC', color: '#B86BFF', accent: '#6A2FB0', rayCount: 18 }, { name: 'LEGENDARY', color: '#FFC93C', accent: '#FF7A1A', rayCount: 24 },];Embers that cool instead of blinking out
A spark holds full opacity for the first half of its life and only then begins to fade, while its scale shrinks steadily by 40% across the whole span. Fading from the start would read as a dimmer switch; shrinking without fading would read as dots vanishing. Doing one late and one throughout is what makes them read as cooling.
const u = s.life > 0 ? t / s.life : 1;const alpha = u >= 1 ? 0 : u < 0.5 ? 1 : 1 - (u - 0.5) / 0.5;const scale = s.scale * (1 - 0.4 * Math.min(1, u));return { x: vx * t, y: vy * t + 0.5 * gravity * t * t, rot: s.rot0 + s.rotV * t,Rays that lean skyward
Light leaves a chest through its lid, so the fan is weighted upward rather than drawn as an even star. The bias is a single term from the ray's angle, remapped to between 0.55 and 1, which dims the downward blades without deleting them. The blades themselves are a cosine raised to the sixth power, with a finer counter-rotating set at a third of the strength.
float ang = atan(d.y, d.x);float rot = u_time * 0.25;// …float upBias = 0.55 + 0.45 * (-sin(ang)); // sin(ang) is +down in screen spaceupBias = clamp(upBias, 0.0, 1.0);