Rarity and drops
A drop is a scene that starts out hidden: an achievement the year earned, or an easter egg handed out by luck. Each has a rarity, common, rare, epic or legendary, and the rarity decides its colour everywhere it shows up. This page covers the data a drop is made of and how one turns into a scene.
The four rarities in the current palette, then the sample year's drops.
The four rarities
| Rarity | Label | Colour variable |
|---|---|---|
common | Common | --wr-common |
rare | Rare | --wr-rare |
epic | Epic | --wr-epic |
legendary | Legendary | --wr-legendary |
RARITY_LABELS maps each rarity to its word. The colours are not fixed: every palette brings four that sit well on it, in palette.rarity, and the custom palette cuts them from your accent. On a stage, data-rarity on an element sets --wr-rarity for everything inside it, which is how a chart lights its point in the colour of the scene it is in.
import type { CSSProperties } from "react";
import { PIXEL_ART, RARITY_LABELS, type Rarity, resolvePalette, type WrappedConfig } from "@react-wrapped/core";
import { type HiddenDrop, PixelGlyph } from "@react-wrapped/scan";
const GRID: CSSProperties = { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 10 };
const TILE: CSSProperties = { display: "grid", gap: 12, padding: 16, borderRadius: 18 };
const NAME: CSSProperties = { display: "block", fontWeight: 650, fontSize: 15 };
const SMALL: CSSProperties = { display: "block", fontSize: 13, fontWeight: 600 };
// Easiest to hardest. The colours are the palette's own, so they change with the theme.
const RARITIES: readonly Rarity[] = ["common", "rare", "epic", "legendary"];
export function Rarities({ drops, config }: { drops: readonly HiddenDrop[]; config: WrappedConfig }) {
const palette = resolvePalette(config);
const tile = { ...TILE, background: palette.card, color: palette.fg };
return (
<div style={GRID}>
{RARITIES.map((rarity) => (
<div key={rarity} style={tile}>
<PixelGlyph art={PIXEL_ART.trophy} colour={palette.rarity[rarity]} cell={8} />
<span>
<b style={NAME}>{RARITY_LABELS[rarity]}</b>
<small style={{ ...SMALL, color: palette.rarity[rarity] }}>{palette.rarity[rarity]}</small>
</span>
</div>
))}
{drops.map((drop) => (
<div key={drop.id} style={tile}>
<PixelGlyph art={drop.art} colour={palette.rarity[drop.rarity]} cell={8} />
<span>
<b style={NAME}>{drop.name}</b>
<small style={{ ...SMALL, color: palette.rarity[drop.rarity] }}>
{`${drop.kind}, ${RARITY_LABELS[drop.rarity]}`}
</small>
</span>
</div>
))}
</div>
);
}A hidden drop
HiddenDrop is what the scan knows about a drop: enough to draw its card. The scene itself is yours and stays out of the video until the drop is found.
| Key | Type | What it is |
|---|---|---|
id | string | Stable for this drop. found and onOpen speak in ids. |
name | string | What the card and the badge call it. |
rarity | "common" | "rare" | "epic" | "legendary" | How rare it is. Decides the colour and how big a show the card gets. |
kind | string | The small word next to the rarity: "Achievement", "Easter egg". |
line | string | One line on the back of the card, in this year's own numbers. |
art | PixelArt | Nine rows of nine characters: x lit, o half lit, . off. |
From a drop to a scene
Build your scenes from the list of opened ids. A drop that has been opened gets a scene, placed with drop set to its rarity (the timeline colours its segment) and with locked (nobody rewrites what was assigned):
import { PIXEL_ART, RARITY_LABELS, type SceneInstance } from "@react-wrapped/core";
import type { HiddenDrop } from "@react-wrapped/scan";
import { factScene } from "@react-wrapped/scenes";
const DROPS: readonly HiddenDrop[] = [
{
id: "long-haul",
name: "Long Haul",
rarity: "epic",
kind: "Achievement",
line: "48 days in a row without missing one.",
art: PIXEL_ART.flame,
},
];
/** The scenes of every drop opened so far. Add them to the story where they belong. */
export function dropScenes(opened: readonly string[]): SceneInstance[] {
return DROPS.filter((drop) => opened.includes(drop.id)).map((drop) =>
factScene(
{
stamp: RARITY_LABELS[drop.rarity],
rarity: drop.rarity,
title: [drop.name],
description: drop.line,
art: drop.art,
variant: 0,
},
{ key: `drop-${drop.id}`, label: drop.name, drop: drop.rarity, locked: true },
),
);
}Give a drop's scene a place in the story, not the end of it: put it right after the scene it continues. When the viewer has reordered the cut by hand, arrangeScenes still drops a new scene in after the one you put before it.
Which drops a year gets
cutDrops picks the drops a video really gets out of everything a year qualified for. It follows two rules. At most one easter egg: a video padded with eggs is a run of jokes. And every other drop is an achievement that can draw a picture of its own numbers, with no two drawing the same kind of picture.
import { cutDrops } from "@react-wrapped/scan";
interface Trait {
id: string;
kind: "achievement" | "easterEgg";
/** The chart this achievement would draw for this year, or null when it has none. */
chart: string | null;
}
export function dropsFor(rolled: readonly Trait[], earned: readonly Trait[]): Trait[] {
// `drops` is what was rolled: its length is how many slots there are, and its egg is the egg.
// `earned` is every achievement, hardest first. The hardest ones that can be drawn get the slots.
return cutDrops({ drops: rolled, earned }, (trait) => trait.chart);
}