Triggers

Triggers

Triggers are named conditions that fire in response to game state transitions or DOM events. They are the shared mechanism used by <game-audio> (to play sounds), <game-toast> (to show messages), and any component that needs to react to game lifecycle moments.

A trigger is set via the trigger attribute on a child element:

<game-audio>
  <game-sample
    trigger="pass"
    type="beep"
    notes="880:0"
    gain="0.2"
  ></game-sample>
  <game-sample trigger="fail" type="noise" gain="0.3"></game-sample>
</game-audio>

<game-toast when-some-scene="playing between paused" trigger="pass"
  >Nice!</game-toast
>
<game-toast when-some-scene="playing between paused" trigger="tier-up"
  >Level Up!</game-toast
>

The trigger system is defined in src/triggers.js and exported as STATE_TRIGGERS and DOM_TRIGGERS for programmatic use.

State Triggers

State triggers fire on game state transitions. They are detected by comparing the previous scene to the current scene after each signal change.

Trigger Fires When
start Game transitions to playing from ready or result
round Every transition to playing (including between rounds)
pass Round passed (enters between with lastRoundPassed true)
fail Round failed (enters between with lastRoundPassed false)
timeout Round failed due to timeout (feedback contains "time" and a timeout-specific child exists)
complete Game enters the result state
tier-up difficulty.tierIndex increased since the last state update

Timeout Fallback

The timeout trigger only fires if the component defines timeoutCallback or has trigger="timeout". Otherwise, timeouts fall through to fail. This means you only need to handle timeout distinctly when you want behaviour different from a regular failure.

Tier-Up Detection

The tier-up trigger compares the current state.difficulty.tierIndex to the previous value. It fires whenever the index increases (promotion). It does not fire on demotion.

DOM Event Triggers

DOM triggers fire on native DOM events during the playing state only. They are automatically bound when the game enters playing and unbound when it leaves, so they never fire during intro screens, result screens, or between rounds.

Trigger DOM Event Use Case
input game-tile-input Keystroke clicks for tile input
countdown game-timer-countdown Whole-second countdown ticks
keydown keydown Key press feedback
keyup keyup Key release feedback
click click Mouse click feedback
pointerdown pointerdown Touch/pointer press feedback
pointerup pointerup Touch/pointer release feedback

DOM event listeners are attached to the nearest <game-shell> ancestor.

Condition Filtering

Triggered elements can have when-* condition attributes to restrict when they fire:

<!-- Only plays when score is 10 or higher -->
<game-sample
  trigger="pass"
  type="marimba"
  notes="1047:0,1319:0.05"
  when-min-score="10"
  gain="0.3"
>
</game-sample>

<!-- Only shows when score is below 5 -->
<game-toast
  when-some-scene="playing between paused"
  trigger="complete"
  when-max-score="5"
  >Better luck next time</game-toast
>

Components That Use Triggers

Component Trigger Attribute On Behavior
<game-audio> <game-sample trigger="..."> Plays the matching sample(s)
<game-toast> <game-toast when-some-scene="playing between paused" trigger="..."> Shows the toast message

Programmatic Access

The trigger constants are exported for advanced use:

import { STATE_TRIGGERS, DOM_TRIGGERS } from "htmlgamekit";

console.log(STATE_TRIGGERS);
// ["start", "round", "pass", "fail", "timeout", "complete", "tier-up"]

console.log(DOM_TRIGGERS);
// { input: "game-tile-input", countdown: "game-timer-countdown", keydown: "keydown", ... }

Building Custom Triggered Components

Implement triggerCallback(name, event) on any GameComponent. The trigger system initialises automatically when the method is present:

class MyComponent extends GameComponent {
  triggerCallback(name, event) {
    if (name === "pass") this.#celebrate();
    if (name === "click") this.#handleClick(event);
  }
}

To distinguish timeouts from failures, also implement timeoutCallback:

class MyComponent extends GameComponent {
  triggerCallback(name, event) {
    if (name === "pass") this.#celebrate();
    if (name === "fail") this.#showFailure();
  }

  timeoutCallback(event) {
    this.#showTimeoutMessage();
  }
}

For advanced cases that need access to the raw trigger detection logic, detectStateTriggers is available:

import { GameComponent, matchesConditions } from "htmlgamekit";
import { detectStateTriggers } from "htmlgamekit/triggers";

class MyComponent extends GameComponent {
  #prevScene = "init";
  #prevTierIndex = -1;

  effectCallback({ scene, lastRoundPassed, lastFeedback, difficulty }) {
    const state = {
      scene: scene.get(),
      lastRoundPassed: lastRoundPassed.get(),
      lastFeedback: lastFeedback.get(),
      difficulty: difficulty.get(),
    };
    const { triggers, tierIndex } = detectStateTriggers(
      state,
      this.#prevScene,
      this.#prevTierIndex,
      () => false,
    );
    this.#prevScene = state.scene;
    this.#prevTierIndex = tierIndex;

    for (const t of triggers) {
      if (matchesConditions(this, this.shell)) {
        this.doSomething(t, state);
      }
    }
  }
}