Per-entity/player finite state machines for game phases

StateMachine ext

StateMachine manages per-entity state transitions for game phases, boss fights, NPC behavior, and more. Each entity attached to a state machine has a current state, and events trigger transitions between states.

from bridge.extensions import StateMachine

sm = StateMachine("boss_fight")

idle = sm.add_state("idle")
combat = sm.add_state("combat")
dead = sm.add_state("dead")

idle.transition("aggro", "combat")
combat.transition("die", "dead")
dead.transition("respawn", "idle")

Import

from bridge.extensions import StateMachine, State

Note: The State class here is bridge.extensions.state_machine.State, not bridge.helpers.State.

StateMachine

Constructor

sm = StateMachine(name="state_machine")

Properties

PropertyTypeDescription
initial_statestr | NoneName of the initial state (get/set)

Methods

.add_state(name)

Add a state. The first added state becomes the initial state.

.get_state(name)

.current_state(entity)

Get the current state name for an entity.

.attach(entity)

Attach an entity to this state machine, placing it in the initial state.

.detach(entity)

Remove an entity from this state machine.

await .trigger(entity, event)

Trigger an event for the entity. If the current state has a transition for this event, the state changes.

transitioned = await sm.trigger(boss, "aggro")

await .force_state(entity, state_name)

Force an entity into a specific state, firing exit/enter callbacks.

.start_ticking(interval_ticks=1, entity_resolver=None)

Start a tick loop that calls on_tick handlers for all attached entities.

.stop_ticking()

Stop the tick loop.


State

A single state with enter/exit/tick callbacks and event transitions.

Constructor

state = State(name)

Decorators

@state.on_enter

Called when an entity enters this state. Receives (entity, old_state_name).

@combat.on_enter
async def enter_combat(entity, old_state):
    await entity.set_custom_name("&cAngry!")

@state.on_exit

Called when an entity leaves this state. Receives (entity, new_state_name).

@state.on_tick

Called every tick while an entity is in this state. Receives (entity,).

@combat.on_tick
async def combat_tick(entity):
    # Attack nearest player every tick
    pass

Methods

.transition(event, target)

Register that event should transition to target state.


Full Example

from bridge import *
from bridge.extensions import StateMachine

sm = StateMachine("guard")

patrol = sm.add_state("patrol")
alert = sm.add_state("alert")
chase = sm.add_state("chase")

patrol.transition("see_enemy", "alert")
alert.transition("confirm_enemy", "chase")
alert.transition("timeout", "patrol")
chase.transition("lost_target", "patrol")

@patrol.on_tick
async def patrol_tick(entity):
    # Walk along patrol route
    pass

@alert.on_enter
async def alert_enter(entity, old):
    entity.custom_name = "&eAlert!"

@chase.on_enter
async def chase_enter(entity, old):
    entity.custom_name = "&cChasing!"

# Attach an NPC
sm.attach(guard_entity)
sm.start_ticking(interval_ticks=5)