Commands

Commands are the fundamental building blocks of game-object behaviour in escapy. A command is a callable with the signature:

Command = Callable[[GameProtocol], list[Event]]

It receives the current game state, optionally mutates it, and returns a list of events describing what happened.

All command factory functions live in escapy.commands and are re-exported from the top-level escapy package.

Basic commands

no_op() Command

Does nothing; returns an empty event list. Useful as a default on_unlock value.

pick(id) Command

Removes the object from the current room and adds it to the player’s inventory.

Emits: PickedUpEvent(object_id=id)

put_in_hand(id) Command

Sets the object as the active hand item.

Emits: PutInHandEvent(object_id=id)

inspect(id) Command

Triggers a zoomed-in view of the object.

Emits: InspectedEvent(object_id=id)

reveal(object_id, room_id, position) Command

Places a previously hidden object into a room at the given position.

Emits: RevealedEvent(object_id, room_id, position)

move_to_room(room_id) Command

Changes the current room.

Emits: MovedToRoomEvent(room_id)

add_to_inventory(object_id) Command

Adds an object to the inventory without removing it from a room.

Emits: AddedToInventoryEvent(object_id)

Lock commands

simple_lock(id) Command

Unlocks the object if it is Unlockable and currently locked, then executes its on_unlock command.

Emits: UnlockedEvent(object_id=id) + events from on_unlock.

key_lock(id, key_id) Command

Same as simple_lock, but only fires when game.in_hand_object_id == key_id.

ask_for_code(id) Command

Asks the UI to prompt the player for a code.

Emits: AskedForCodeEvent(object_id=id)

locked(id) Command

Signals that the player interacted with a locked object (usually paired with chain as a fallback).

Emits: InteractedWithLockedEvent(object_id=id)

Combinators

Combinators let you compose multiple commands into richer behaviours.

combine(*commands) Command

Executes all commands in sequence and returns their events as a flat list.

cmd = combine(
    move_to_room("cellar"),
    add_to_inventory("torch"),
)

cond(*clauses) Command

Evaluates conditions in order and executes only the first matching clause.

cmd = cond(
    (lambda: obj.state == "locked", ask_for_code("safe")),
    # implicit: no match → empty list
)

Each clause is a tuple (condition: () bool, command: Command).

chain(*clauses) Command

Like combine, but each clause’s condition receives the events emitted so far, enabling conditional follow-ups.

cmd = chain(
    (lambda _events: True, key_lock("door", key_id="gold_key")),
    (
        lambda events: not any(isinstance(e, UnlockedEvent) for e in events),
        locked("door"),
    ),
)

Each clause is a tuple (condition: (list[Event]) bool, command: Command). All matching clauses execute (unlike cond, which stops at the first match).