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).