The brain system
Behavior-tree AI for NPCs, evaluated every tick via the objective system.
Overview
A brain is a tree of Brain nodes attached to an agent's __BRAIN__ inventory slot. The root is always a Select node — it runs each child in order and stops at the first success. Children can be plain labels (Simple nodes), or composite Sequence / Select nodes built from structured dicts.
Each tick, brains_run_all iterates all agents that have a __BRAIN__ entry and calls brain.run(). A Simple node runs its MAST label synchronously in a temporary task; the label result (BT_SUCCESS / BT_FAIL) propagates up the tree.
brain_add is the main entry point. Call it once per behavior you want to add; the root Select node grows with each call. Use structured dicts for composite nodes:
{"SEL_name": [child1, child2]}— Select composite{"SEQ_name": [child1, child2]}— Sequence composite
Within a brain label you have access to BRAIN, BRAIN_AGENT, and BRAIN_AGENT_ID task variables.
Note
The brain system piggybacks on the objective tick. Call brain_schedule() (or brain_add(), which calls it automatically) to register the tick handler.
Quick example
== setup ==
brain_add(enemy_id, patrol_label)
brain_add(enemy_id, attack_label)
->END
== patrol_label ==
///test
if get_pos(BRAIN_AGENT_ID).distance(target_pos) > 500: BT_FAIL
BT_SUCCESS
== attack_label ==
target(BRAIN_AGENT_ID, player_id)
BT_SUCCESS
from sbs_utils.procedural.brain import brain_add, brain_clear
# Simple behavior list — Select runs them in order
brain_add(enemy_id, patrol_label)
brain_add(enemy_id, attack_label)
# Nested composite example
brain_add(enemy_id, {
"SEL_combat": [attack_label, evade_label]
})
# Remove the brain entirely
brain_clear(enemy_id)
Behavior tree result values
| Return | Meaning |
|---|---|
BT_SUCCESS |
This node succeeded |
BT_FAIL |
This node failed |
OK_IDLE |
Still running (not yet resolved) |
API
Manage all brains
brain_add(agent_id_or_set, label, data=None, client_id=0, parent=None)
Add a behaviour-tree node to one or more agents.
Creates or extends the agent's brain tree. The root is a Select node (runs children in order, stops at first success). Labels can be plain label references, strings, or structured dicts/lists for nested trees.
Structured dict forms:
- {"label": my_label, "data": {...}} — simple node with data
- {"SEL_name": [child1, child2]} — Select composite node
- {"SEQ_name": [child1, child2]} — Sequence composite node
A list of labels adds multiple sibling nodes under the parent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent_id_or_set
|
Agent ID, object, or set/list of either. |
required | |
label
|
label | str | dict | list
|
Behaviour node(s) to add. |
required |
data
|
dict
|
Variables passed when the label runs. Defaults to None. |
None
|
client_id
|
int
|
Client context for GUI-task resolution. Defaults to 0 (server). |
0
|
parent
|
Brain | None
|
Parent node to attach to. Defaults to None (attaches to the agent's root Select node). |
None
|
Example
brain_add(ENEMY_ID, patrol_label) brain_add(ENEMY_ID, {"SEL_combat": [attack_label, evade_label]})
brain_add_parent(parent, agent, label, data=None, client_id=0)
Add one or more brain nodes as children of an existing brain node.
Handles plain labels, strings, lists (multiple siblings), and structured
dicts ({"SEL_name": [...]} or {"SEQ_name": [...]}) recursively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent
|
Brain
|
Parent brain node to attach children to. |
required |
agent
|
int
|
Agent ID owning the brain. |
required |
label
|
label | str | list | dict
|
Brain node specification. |
required |
data
|
dict
|
Variables passed to child tasks. Defaults to None. |
None
|
client_id
|
int
|
Client context for GUI-task resolution. Defaults to 0 (server). |
0
|
brain_clear(agent_id_or_set)
Remove the behaviour-tree brain from one or more agents.
Clears the __BRAIN__ inventory key so the agent's brain stops running
on the next tick. Does not explicitly stop any sub-tasks already started
by brain labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent_id_or_set
|
Agent ID, object, or set/list of either. |
required |
Example
brain_clear(ENEMY_ID)
brain_schedule()
Schedule the brain tick task via the objective system.
brains_run_all(tick_task)
Run all agent brains for the current tick.
Iterates every agent with a __BRAIN__ inventory entry and calls
brain.run(). Re-entrant calls are suppressed with a guard flag.
Agents whose Agent.get returns None are silently skipped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tick_task
|
The tick task or event that triggered this run. |
required |