GameTrophy

Methods

.effectCallback({ scene })
.unlock()
.define(tag, registry)
.subscribe(context, callback)

Custom States

StateDescription
:state(unlocked)The trophy has been unlocked

Shadow DOM Parts

PartDescription
iconThe icon container
nameThe trophy name label
tooltipThe description tooltip

A declarative achievement tile. Place <game-trophy> elements anywhere in the game layout — typically grouped together on the result screen or in a persistent HUD area. Each trophy manages its own locked/unlocked visual state and integrates with the shell's trophy tracking.

Icons are rendered using <game-icon>, which reads the sprite sheet URL from the shell's spriteSheet signal (set via the sprite-sheet attribute).

Attributes

id
string -- Unique trophy identifier. Used for persistence and for condition lookups via when-some-trophy="id".
name
string -- Display name shown below the icon.
icon
string -- Icon name. Renders a <game-icon name="..."> element, which reads the sprite sheet URL from shell.spriteSheet.
description
string -- Tooltip text shown when the trophy is tapped.
when-*
Any condition attribute for auto-unlock. The trophy is automatically unlocked when all conditions pass on entering the result state.
<game-trophy
  id="scorer"
  name="Scorer"
  icon="star"
  when-min-score="10"
  description="Score 10 or more"
>
</game-trophy>

<game-trophy
  id="hat-trick"
  name="Hat Trick"
  icon="fire"
  when-min-streak="3"
  description="Get 3 correct in a row"
>
</game-trophy>
<!-- Note: "streak" resolves via the stats map. It must be kept up to date
     via GameStatUpdateEvent("streak", n). <game-quiz> does this automatically.
     For custom games, dispatch the event yourself. -->

<game-trophy
  id="collector"
  name="Collector"
  icon="chest"
  when-min-trophy-count="5"
  description="Unlock 5 other trophies"
>
</game-trophy>

Properties

.unlocked
boolean -- true if the trophy has been unlocked.
.trophyId
string -- The element's id attribute.

Methods

.unlock()
Unlock this trophy. Idempotent — calling it on an already-unlocked trophy is a no-op. Registers the unlock with the shell (updating shell.trophyCount and shell.isTrophyUnlocked()), persists to localStorage, and dispatches a game-trophy-unlock event.

Events Dispatched

game-trophy-unlock
Dispatched when the trophy is unlocked. Bubbles to the shell. Carries .trophyId and .trophyName.
shell.addEventListener("game-trophy-unlock", (e) => {
  console.log(`Unlocked: ${e.trophyName} (${e.trophyId})`);
});

Auto-Unlock

Trophies with condition attributes are checked automatically when the game enters the result state. Any trophy whose conditions all pass is unlocked via .unlock().

Tooltip

Clicking or tapping a trophy shows its description in a tooltip for 1.8 seconds.

Persistence

Unlocked trophy IDs are stored as a JSON array in localStorage under the key {storage-key}-trophies. On connect, if the shell has the trophy registered as already unlocked, the visual state is restored immediately.

Signal Access

Signal Usage
shell.scene Watches for "result" to trigger auto-unlock checks
shell.score, shell.round, shell.stats, shell.difficulty, etc. Read by matchesConditions() for when-* auto-unlock conditions

CSS Custom Properties

Property Default Description
--game-trophy-color #fbbf24 Icon color for unlocked trophies

Usage

<div when-some-scene="result" data-overlay>
  <game-result-stat label="Score"></game-result-stat>

  <div style="display: flex; gap: 8px; justify-content: center;">
    <game-trophy
      id="scorer"
      name="Scorer"
      icon="star"
      when-min-score="10"
      description="Score 10 or more"
    >
    </game-trophy>

    <game-trophy
      id="hat-trick"
      name="Hat Trick"
      icon="fire"
      when-min-streak="3"
      description="Get 3 correct in a row"
    >
    </game-trophy>

    <game-trophy
      id="speed-demon"
      name="Speed Demon"
      icon="bolt"
      description="Complete the game in under 10 seconds"
    >
    </game-trophy>
  </div>

  <button commandfor="game" command="--restart">Play Again</button>
</div>

The speed-demon trophy has no auto-unlock condition, so it must be unlocked programmatically:

shell.addEventListener("game-lifecycle", (e) => {
  if (e.action === "result") {
    const totalTime = e.state.roundScores.reduce((a, b) => a + b, 0);
    if (totalTime < 10000) {
      document.querySelector("game-trophy#speed-demon").unlock();
    }
  }
});