Skip to content

Capture & carry

The carry loop — grab, run, score, or get tagged and drop — built from items and interactions. Worked from Capture the Flag and Pirate’s Booty.

itemTypes:
- kind: red_flag
label: "Red flag"
icon: { type: glyph, value: "", color: "#ef4444" }
items:
- { kind: red_flag, count: 1, spawnPointId: red_flag }

A worldItem + auto interaction is a walk-over grab. Restrict who can take it with initiatorFilters:

interactions:
- verb: grab_red_flag
label: "Grab flag"
target: worldItem
trigger: auto
rangeMeters: 5
itemKinds: [red_flag]
initiatorFilters: [{ key: teamId, value: blue }] # only blue can take the red flag
- id: pickup_red_flag
when: { kind: interaction, verb: grab_red_flag } # same = grabber, other = the item
do:
- { kind: set_state, target: same, key: hasFlag, value: red }
- { kind: destroy_item } # consume the world item
- { kind: send_event, to: same, event: { type: toast, text: "Grabbed it — run home!" } }

Track carriage in player state (hasFlag) so other rules can check it; the world item itself is consumed and re-spawned later.

- id: blue_score
when:
kind: and
children:
- { kind: entered_zone, who: any, zoneId: blue_base }
- { kind: compare, op: eq, left: { ref: same.teamId }, right: blue }
- { kind: state_equals, who: same, key: hasFlag, value: red }
do:
- { kind: set_state, target: same, key: hasFlag, value: null }
- { kind: increment_state, target: { byTeam: blue }, key: score, by: 1 }
- { kind: spawn_item, item: red_flag, atPoint: red_flag } # reset for next round

A proximity rule where same is the carrier and other is an enemy drops the item where it fell so it’s contestable:

- id: blue_carrier_tagged
when:
kind: and
children:
- { kind: proximity, a: any, b: any, meters: 5 }
- { kind: compare, op: eq, left: { ref: same.teamId }, right: blue }
- { kind: state_equals, who: same, key: hasFlag, value: red }
- { kind: compare, op: eq, left: { ref: other.teamId }, right: red }
do:
- { kind: set_state, target: same, key: hasFlag, value: null }
- { kind: spawn_item, item: red_flag, at: same } # drop at the carrier's position

All four snippets: apps/wage-engine/src/games/capture_the_flag/game.yaml.

  • Marker glow while carrying. Add a theme.playerMarkers rule keyed on the carry state so carriers stand out:
    theme:
    playerMarkers:
    - { when: { key: carrying, value: true }, color: "#fbbf24" }
    (Pirate’s Booty does this with carrying.)
  • One-at-a-time. Add initiatorFilters: [{ key: state.carrying, value: false }] to the grab so a loaded player can’t grab a second.
  • Consumable currency. For coins kept in inventory rather than a carry flag, remember the picked-up item’s id (set_state … value: { ref: other.id }) so you can destroy_item exactly that one on deposit (Pirate’s Booty’s coin loop).