Skip to content

Items & interactions

Items are things with identity that live in the world or in an inventory. Interactions are how players act on each other and on items. Together they cover tags, captures, pickups, and uses.

The authoritative registry of item kinds. Every kind referenced anywhere (items, interactions.itemKinds, spawn_item.item) must be declared here, or the ruleset fails to load.

interface ItemType {
kind: string;
label: string;
icon: ItemIcon;
useLabel?: string; // button text for a held-item "use" interaction
}
interface ItemIcon {
type: "glyph" | "mdi";
value: string; // a unicode char (glyph) or Material Design icon name (mdi)
color?: string; // hex; the symbol is drawn white on a chip of this color
}
itemTypes:
- kind: treasure_chest
label: "Treasure chest"
icon: { type: mdi, value: "treasure-chest", color: "#fbbf24" }
- kind: coin
label: "Coin"
icon: { type: glyph, value: "🪙", color: "#1e3a5f" }

Source: apps/wage-engine/src/games/pirates_booty/game.yaml.

  • glyphvalue is a literal unicode character/emoji ("⚑", "🪙").
  • mdivalue is a Material Design icon name ("flag", "treasure-chest"; a "mdi:" prefix is normalized away). If the name can’t resolve, clients draw the colored chip with no symbol — never an invisible item.
  • color should never be white (the white symbol would vanish).

Placement specs: how many of each kind spawn at game start, and where.

interface ItemSpawnSpec {
kind: string;
count: number;
spawnZoneId?: string; // random points inside this placed zone
spawnPointId?: string; // exactly at this placed map point
}

Provide one of spawnZoneId (scatter) or spawnPointId (precise). Zone spawns honor exclusions.

items:
- { kind: treasure_chest, count: 12, spawnZoneId: treasure_island }
- { kind: blue_flag, count: 1, spawnPointId: blue_flag }

The runtime item itself:

interface Item {
id: string;
kind: string;
position?: LatLng; // set while in the world
holderId?: string; // set while held; pickup is a custody transfer keeping the id
acquiredTs?: number; // orders inventory display
}

A player’s inventory is simply the items whose holderId is that player.

A player-initiated, range-gated, optionally-confirmed action. GPS proximity can’t tell the engine a real-world tag/handoff happened — an interaction lets a player assert it against a nearby eligible target, and the server validates it.

interface InteractionSpec {
verb: string;
label: string;
target?: "player" | "worldItem" | "heldItem"; // default "player"
trigger?: "auto" | "manual"; // default "manual"
itemKinds?: string[]; // for item targets
rangeMeters: number;
confirm?: "none" | "mutual"; // default "none"
confirmTimeoutMs?: number; // default 15000
initiatorFilters?: { key: string; value: unknown }[];
targetFilters?: { key: string; value: unknown }[];
teamRelation?: "same" | "other";
icon?: string;
}
  • verb — the key rules match on (tag, capture, grab_chest). The engine ascribes no meaning; only your rules do.
  • label — button text on the initiator’s phone.
  • target — what it acts on:
    • player (default) — a nearby player.
    • worldItem — an item lying in the world (a pickup/grab).
    • heldItem — an item already in the initiator’s inventory (a use).
  • triggermanual (tap a button; the default) or auto (the engine claims it the instant range + filters pass — a walk-over pickup). auto is only valid with target: worldItem.
  • itemKinds — for item targets, restrict which kinds the verb applies to.
  • rangeMeters — max initiator↔target distance (haversine). For worldItem it’s the pickup radius; ignored for heldItem.
  • confirmnone (apply immediately) or mutual (target must confirm within confirmTimeoutMs, default 15000 ms).
  • initiatorFilters / targetFilters — predicates that must all pass. Key is teamId or state.<name>; value is the required value.
  • teamRelation — constrain initiator/target teams: same or other. Omit for no constraint.
  • icon — optional UI hint.
interactions:
- verb: tag
label: "Tag"
rangeMeters: 5
confirm: none
initiatorFilters: [{ key: "state.it", value: true }]
targetFilters: [{ key: "state.it", value: false }]

Binds initiator = same, target = other. Source: tag/game.yaml.

interactions:
- verb: grab_chest
label: "Grab chest"
target: worldItem
trigger: auto
rangeMeters: 3
itemKinds: [treasure_chest]
initiatorFilters: [{ key: state.carrying, value: false }]

Binds initiator = same, item = other. Source: pirates_booty/game.yaml.

Whatever the target, you react with an interaction condition on the verb:

- id: pickup_chest
when: { kind: interaction, verb: grab_chest }
do:
- { kind: set_state, target: same, key: carrying, value: true }
- { kind: destroy_item } # consumes the item bound as `other`

See the interaction condition and item actions for the full picture.