Skip to content

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