---
title: "Event wiring one trigger emits another responder reacts"
description: "A pressure plate over here should open a door over there; a switch should light a lamp across the room; stepping into a zone should start a sequence somewhere else. The sensor and the responder are DIFFERENT entities, so wire them with a named custom event rather than having the trigger reach into the other entity's components blindly."
engineVersion: v1.0.234
date: 2026-09-28
license: "(c) Gessa, proprietary. Cite with attribution to https://gessa.ai/docs/. Terms: https://gessa.ai/terms/."
canonical: https://gessa.ai/docs/knowledge/playbooks/trigger-to-custom-event-wiring/
---

# Event wiring one trigger emits another responder reacts

## When to use

A pressure plate over here should open a door over there; a switch should light
a lamp across the room; stepping into a zone should start a sequence somewhere
else. The sensor and the responder are DIFFERENT entities, so wire them with a
named custom event rather than having the trigger reach into the other entity's
components blindly.

## The recipe

Author both scripts with `project_apply_script_semantic_patch` using the
`ai_safe` op pack.

1. Sensor side: give the trigger object a trigger `ColliderComponent`
   (`collision:"trigger"`) via `project_add_component`, then `addTriggerHandler`
   (`{ "handlerKey":"onSignal" }`). In the whole-body GessaScript
   (`replaceScriptFromGessaScript`) emit the event:
   `ctx.events.emit("door_opened", { open: true })`. The `emitCustomEvent` op is
   the declarative equivalent when you want the event declared in the script's
   event set.
2. Responder side: on the door (or lamp, or gate) entity declare the matching
   handler with `createCustomEventHandler`
   (`{ "eventKey":"door_opened" }`), which creates a guarded `onCustomEvent`
   handler for that semantic key. patchComponent acts on the entity the handler
   lives on, so its body does the reaction directly, e.g.
   `ctx.entity.patchComponent("TransformComponent", { position: {...open} })`.
3. Idempotency: guard the responder with `setBooleanState`
   (`{ "field":"opened", "value": true }`) and check it first, so a second
   emit does not re-run the open.
4. Payload: pass ids or values in the emit payload (`{ source, amount }`) and
   read them in the handler; do not encode data in the event NAME.
5. Fan-out is free: any number of responders that declare the same
   `onCustomEvent` key all react to one emit. Use distinct event names per wire
   (`gate_north_open`, `lamp_hall_on`) so unrelated responders stay silent.

## Pitfalls

- `ctx.events.emit` is ROOM-scoped (audience room), so EVERY subscriber of that
  name reacts. Name events per wire, not generically, or a far responder fires
  unexpectedly.
- Do not make the trigger reach across and mutate the other entity directly by
  guessing its id; the event wire is what keeps the sensor and responder
  decoupled and reorderable.
- The responder needs its OWN script carrying the `onCustomEvent` handler; an
  emit with no declared subscriber is silently dropped.
- An event is ephemeral: a responder spawned AFTER the emit never sees it. Drive
  persistent open/closed truth through `setBooleanState`, and read that on spawn.

## Verify

- `simulation_run` (`qa.run.start`) tripping the sensor trigger and asserting the
  remote responder reached its reacted state.
- `project_get_graph_snapshot` to confirm the trigger handler on the sensor and
  the `onCustomEvent` handler on the responder both landed.
