Behaviors: Difference between revisions
Created page with "= Behaviors = The Behaviors system allows NPCs to run AI behaviors defined in YAML format. Behaviors can be simple actions like walking or waiting, or complex sequences with conditions, loops, and parallel execution. == Tree Structure == All behavior trees start with a <code>tree:</code> root node: <syntaxhighlight lang="yaml"> tree: sequence: - behavior_name arg1 arg2 - another_behavior </syntaxhighlight> == Built-in Types == === sequence === Runs child..." |
|||
| (3 intermediate revisions by the same user not shown) | |||
| Line 7: | Line 7: | ||
All behavior trees start with a <code>tree:</code> root node: | All behavior trees start with a <code>tree:</code> root node: | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
- behavior_name arg1 arg2 | - behavior_name arg1 arg2 | ||
- another_behavior | - another_behavior | ||
</ | </pre>}} | ||
== Built-in Types == | == Built-in Types == | ||
| Line 20: | Line 20: | ||
Runs children in order until one fails. | Runs children in order until one fails. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 26: | Line 26: | ||
- wait 1s | - wait 1s | ||
- say "Second" | - say "Second" | ||
</ | </pre>}} | ||
=== selector / random === | === selector / random === | ||
Runs one sub-behavior at random. | Runs one sub-behavior at random. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
selector: | selector: | ||
| Line 38: | Line 38: | ||
- say "I see you!" | - say "I see you!" | ||
- wander radius=15 | - wander radius=15 | ||
</ | </pre>}} | ||
=== parallel === | === parallel === | ||
Runs multiple behaviors at the same time. | Runs multiple behaviors at the same time. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
parallel: | parallel: | ||
| Line 50: | Line 50: | ||
- wait 5s | - wait 5s | ||
- say "Still wandering..." | - say "Still wandering..." | ||
</ | </pre>}} | ||
=== loop [condition] === | === loop [condition] === | ||
| Line 56: | Line 56: | ||
Runs the children in sequence if the condition is true. | Runs the children in sequence if the condition is true. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
loop `memory.get('counter', 0) < 5`: | loop `memory.get('counter', 0) < 5`: | ||
| Line 62: | Line 62: | ||
- set counter `memory.get('counter', 0) + 1` | - set counter `memory.get('counter', 0) + 1` | ||
- wait 1s | - wait 1s | ||
</ | </pre>}} | ||
=== if [condition] / else === | === if [condition] / else === | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 74: | Line 74: | ||
- say "Nice to meet you!" | - say "Nice to meet you!" | ||
- set greeted true | - set greeted true | ||
</ | </pre>}} | ||
=== invert === | === invert === | ||
| Line 80: | Line 80: | ||
Inverts the success or failure of its child. | Inverts the success or failure of its child. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
invert: | invert: | ||
- fail | - fail | ||
</ | </pre>}} | ||
=== timeout [duration] === | === timeout [duration] === | ||
| Line 90: | Line 90: | ||
If the child doesn't complete within the specified duration, it fails. | If the child doesn't complete within the specified duration, it fails. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
timeout 5s: | timeout 5s: | ||
- walkto 500 64 500 | - walkto 500 64 500 | ||
</ | </pre>}} | ||
=== eval [language] === | === eval [language] === | ||
| Line 100: | Line 100: | ||
Evaluates multiple expressions. | Evaluates multiple expressions. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
eval: | eval: | ||
| Line 106: | Line 106: | ||
- memory.set('y', 64) | - memory.set('y', 64) | ||
- memory.set('z', 200) | - memory.set('z', 200) | ||
</ | </pre>}} | ||
Or with a specific language (javascript, molang): | Or with a specific language (javascript, molang): | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
eval molang: | eval molang: | ||
- v.test = 5 | - v.test = 5 | ||
- v.result = v.test * 2 | - v.result = v.test * 2 | ||
</ | </pre>}} | ||
=== Movement === | === Movement === | ||
| Line 124: | Line 124: | ||
'''Inline usage:''' | '''Inline usage:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- walkto 100 64 200 speed=1.5 range=50 | - walkto 100 64 200 speed=1.5 range=50 | ||
</ | </pre>}} | ||
'''Named parameters:''' | '''Named parameters:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- walkto: | - walkto: | ||
x: 100 | x: 100 | ||
| Line 137: | Line 137: | ||
range: 50 | range: 50 | ||
distance_margin: 2 | distance_margin: 2 | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 149: | Line 149: | ||
Random wandering within a radius. | Random wandering within a radius. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- wander radius=10 pathfind=true | - wander radius=10 pathfind=true | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 161: | Line 161: | ||
Makes the NPC look at specific coordinates. | Makes the NPC look at specific coordinates. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- look 100 64 200 | - look 100 64 200 | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 172: | Line 172: | ||
Teleports the NPC to coordinates. | Teleports the NPC to coordinates. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- teleport 100 64 200 | - teleport 100 64 200 | ||
- teleport 100 64 200 world_name | - teleport 100 64 200 world_name | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 187: | Line 187: | ||
Waits for a specified duration. | Waits for a specified duration. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- wait 3s # 3 seconds | - wait 3s # 3 seconds | ||
- wait 100t # 100 ticks | - wait 100t # 100 ticks | ||
- wait 2m # 2 minutes | - wait 2m # 2 minutes | ||
</ | </pre>}} | ||
'''Duration formats:''' | '''Duration formats:''' | ||
| Line 203: | Line 203: | ||
Waits until a condition becomes true. | Waits until a condition becomes true. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- wait_until `player.distance < 10` | - wait_until `player.distance < 10` | ||
</ | </pre>}} | ||
==== cooldown ==== | ==== cooldown ==== | ||
| Line 211: | Line 211: | ||
Rate limits behaviors. Only succeeds if enough time has passed since the last time it was run. | Rate limits behaviors. Only succeeds if enough time has passed since the last time it was run. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- cooldown greeting 30s | - cooldown greeting 30s | ||
- say "Hello there!" | - say "Hello there!" | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 226: | Line 226: | ||
Sets a memory variable. | Sets a memory variable. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- set counter 5 | - set counter 5 | ||
- set name "Bob" | - set name "Bob" | ||
- set calculated `10 * 5` | - set calculated `10 * 5` | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 240: | Line 240: | ||
Removes a memory variable. | Removes a memory variable. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- forget counter | - forget counter | ||
</ | </pre>}} | ||
==== clear_memory ==== | ==== clear_memory ==== | ||
| Line 248: | Line 248: | ||
Clears all memory variables. | Clears all memory variables. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- clear_memory | - clear_memory | ||
</ | </pre>}} | ||
=== Communication === | === Communication === | ||
| Line 258: | Line 258: | ||
Makes the NPC say a message to nearby players. | Makes the NPC say a message to nearby players. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- say "Hello, world!" | - say "Hello, world!" | ||
- say `"Player count: " + player.count` | - say `"Player count: " + player.count` | ||
</ | </pre>}} | ||
==== emit_signal ==== | ==== emit_signal ==== | ||
| Line 267: | Line 267: | ||
Emits a signal. | Emits a signal. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- emit_signal "start_patrol" | - emit_signal "start_patrol" | ||
</ | </pre>}} | ||
==== emit_signal_to ==== | ==== emit_signal_to ==== | ||
| Line 275: | Line 275: | ||
Emits a signal to a specific NPC by ID or UUID. | Emits a signal to a specific NPC by ID or UUID. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- emit_signal_to 123 "alert" | - emit_signal_to 123 "alert" | ||
- emit_signal_to "uuid-string" "alert" | - emit_signal_to "uuid-string" "alert" | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 288: | Line 288: | ||
Emits a signal that all NPCs receive. | Emits a signal that all NPCs receive. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- emit_global_signal "server_restart_warning" | - emit_global_signal "server_restart_warning" | ||
</ | </pre>}} | ||
==== wait_for_signal ==== | ==== wait_for_signal ==== | ||
| Line 296: | Line 296: | ||
Waits until a signal is received. | Waits until a signal is received. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- wait_for_signal "start_patrol" | - wait_for_signal "start_patrol" | ||
- say "Signal received!" | - say "Signal received!" | ||
</ | </pre>}} | ||
=== World Interaction === | === World Interaction === | ||
| Line 307: | Line 307: | ||
Breaks a block realistically using the NPC's held item at the specified coordinates. | Breaks a block realistically using the NPC's held item at the specified coordinates. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- break_block 100 64 200 radius=3 | - break_block 100 64 200 radius=3 | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 321: | Line 321: | ||
Always returns SUCCESS. | Always returns SUCCESS. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- succeed | - succeed | ||
</ | </pre>}} | ||
==== fail ==== | ==== fail ==== | ||
| Line 329: | Line 329: | ||
Always returns FAILURE. | Always returns FAILURE. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- fail | - fail | ||
</ | </pre>}} | ||
| Line 338: | Line 338: | ||
Repeats a specified number of times. | Repeats a specified number of times. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- repeat 5: | - repeat 5: | ||
- say "Counting..." | - say "Counting..." | ||
- wait 1s | - wait 1s | ||
</ | </pre>}} | ||
'''Parameters:''' | '''Parameters:''' | ||
| Line 353: | Line 353: | ||
Lines starting with <code>/</code> are executed as commands: | Lines starting with <code>/</code> are executed as commands: | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
- /npc speak "Hello from command!" | - /npc speak "Hello from command!" | ||
- /say This is a server command | - /say This is a server command | ||
</ | </pre>}} | ||
=== Expression Evaluation === | === Expression Evaluation === | ||
| Line 364: | Line 364: | ||
Lines starting with backtick (<code>`</code>) are evaluated as expressions: | Lines starting with backtick (<code>`</code>) are evaluated as expressions: | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
- `inv.add(item.json('{"material":"DIAMOND","amount":64}'))` | - `inv.add(item.json('{"material":"DIAMOND","amount":64}'))` | ||
- `memory.set('flag', true)` | - `memory.set('flag', true)` | ||
</ | </pre>}} | ||
== Expression System == | == Expression System == | ||
| Line 380: | Line 380: | ||
'''Inline in conditions:''' | '''Inline in conditions:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- if `memory.get('counter') < 10`: | - if `memory.get('counter') < 10`: | ||
- say "Counter is low" | - say "Counter is low" | ||
</ | </pre>}} | ||
'''In parameter values:''' | '''In parameter values:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- walkto `npc.location.x + 10` `npc.location.y` `npc.location.z` | - walkto `npc.location.x + 10` `npc.location.y` `npc.location.z` | ||
</ | </pre>}} | ||
'''In text:''' | '''In text:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- say "Counter value: {memory.get('counter')}" | - say "Counter value: {memory.get('counter')}" | ||
</ | </pre>}} | ||
'''Standalone evaluation:''' | '''Standalone evaluation:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
- `memory.set('result', 10 * 5)` | - `memory.set('result', 10 * 5)` | ||
</ | </pre>}} | ||
== Memory System == | == Memory System == | ||
| Line 406: | Line 406: | ||
=== Usage === | === Usage === | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 417: | Line 417: | ||
- set greeted true | - set greeted true | ||
- forget temporary_flag | - forget temporary_flag | ||
</ | </pre>}} | ||
=== Memory Methods === | === Memory Methods === | ||
| Line 433: | Line 433: | ||
=== Basic Patrol === | === Basic Patrol === | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 446: | Line 446: | ||
- say "Patrol complete!" | - say "Patrol complete!" | ||
- forget patrol_count | - forget patrol_count | ||
</ | </pre>}} | ||
=== Signal Coordination === | === Signal Coordination === | ||
'''Guard NPC:''' | '''Guard NPC:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 458: | Line 458: | ||
- walkto `signal.data.x` `signal.data.y` `signal.data.z` | - walkto `signal.data.x` `signal.data.y` `signal.data.z` | ||
- emit_global_signal "area_secured" | - emit_global_signal "area_secured" | ||
</ | </pre>}} | ||
'''Lookout NPC:''' | '''Lookout NPC:''' | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 468: | Line 468: | ||
- emit_global_signal "intruder_alert" | - emit_global_signal "intruder_alert" | ||
- say "Alert! Intruder spotted!" | - say "Alert! Intruder spotted!" | ||
</ | </pre>}} | ||
=== Conditional Greeter === | === Conditional Greeter === | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 484: | Line 484: | ||
- wait 2s | - wait 2s | ||
- say "Have a nice day!" | - say "Have a nice day!" | ||
</ | </pre>}} | ||
=== Resource Gathering === | === Resource Gathering === | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
tree: | tree: | ||
sequence: | sequence: | ||
| Line 500: | Line 500: | ||
- say "Resource gathering complete!" | - say "Resource gathering complete!" | ||
- teleport 0 64 0 | - teleport 0 64 0 | ||
</ | </pre>}} | ||
== Loading Behavior Trees == | == Loading Behavior Trees == | ||
| Line 508: | Line 508: | ||
You can load trees from external files in the <code>behaviors/</code> directory. | You can load trees from external files in the <code>behaviors/</code> directory. | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
traits: | traits: | ||
- name: behavior | - name: behavior | ||
tree: patrol_behavior.yml | tree: patrol_behavior.yml | ||
</ | </pre>}} | ||
The behaviors will be loaded from <code>plugins/Citizens/behaviors/patrol_behavior.yml</code> | The behaviors will be loaded from <code>plugins/Citizens/behaviors/patrol_behavior.yml</code> | ||
| Line 519: | Line 519: | ||
Behavior trees can be included in templates: | Behavior trees can be included in templates: | ||
< | {{codebox|height=200px|width=100%||<pre lang="yaml"> | ||
greeter: | greeter: | ||
traits: | traits: | ||
| Line 528: | Line 528: | ||
- say "Hello there, traveler!" | - say "Hello there, traveler!" | ||
- wait 2s | - wait 2s | ||
</ | </pre>}} | ||
== API == | == API == | ||
| Line 536: | Line 536: | ||
Register custom behaviors with the BehaviorRegistry: | Register custom behaviors with the BehaviorRegistry: | ||
< | {{codebox|height=200px|width=100%||<pre lang="java"> | ||
registry.registerBehavior("my_behavior", (params, context) -> { | registry.registerBehavior("my_behavior", (params, context) -> { | ||
String arg = context.getArgOrParam(0, "param_name", params, "default"); | String arg = context.getArgOrParam(0, "param_name", params, "default"); | ||
| Line 559: | Line 559: | ||
}; | }; | ||
}); | }); | ||
</ | </pre>}} | ||
=== Custom Expression Languages === | === Custom Expression Languages === | ||
Register custom expression engines with the ExpressionRegistry for domain-specific languages. | Register custom expression engines with the ExpressionRegistry for domain-specific languages. | ||
Latest revision as of 18:55, 1 December 2025
Behaviors
The Behaviors system allows NPCs to run AI behaviors defined in YAML format. Behaviors can be simple actions like walking or waiting, or complex sequences with conditions, loops, and parallel execution.
Tree Structure
All behavior trees start with a tree: root node:
Code: |
tree:
sequence:
- behavior_name arg1 arg2
- another_behavior
|
Built-in Types
sequence
Runs children in order until one fails.
Code: |
tree:
sequence:
- say "First"
- wait 1s
- say "Second"
|
selector / random
Runs one sub-behavior at random.
Code: |
tree:
selector:
- sequence:
- if `player_nearby`:
- say "I see you!"
- wander radius=15
|
parallel
Runs multiple behaviors at the same time.
Code: |
tree:
parallel:
- wander radius=10 pathfind=true
- sequence:
- wait 5s
- say "Still wandering..."
|
loop [condition]
Runs the children in sequence if the condition is true.
Code: |
tree:
loop `memory.get('counter', 0) < 5`:
- say "Count is {memory.get('counter', 0)}"
- set counter `memory.get('counter', 0) + 1`
- wait 1s
|
if [condition] / else
Code: |
tree:
sequence:
- if `memory.has('greeted')`:
- say "Welcome back!"
else:
- say "Nice to meet you!"
- set greeted true
|
invert
Inverts the success or failure of its child.
Code: |
tree:
invert:
- fail
|
timeout [duration]
If the child doesn't complete within the specified duration, it fails.
Code: |
tree:
timeout 5s:
- walkto 500 64 500
|
eval [language]
Evaluates multiple expressions.
Code: |
tree:
eval:
- memory.set('x', 100)
- memory.set('y', 64)
- memory.set('z', 200)
|
Or with a specific language (javascript, molang):
Code: |
tree:
eval molang:
- v.test = 5
- v.result = v.test * 2
|
Movement
walkto
Walks to specified coordinates.
Inline usage:
Code: |
- walkto 100 64 200 speed=1.5 range=50
|
Named parameters:
Code: |
- walkto:
x: 100
y: 64
z: 200
speed: 1.5
range: 50
distance_margin: 2
|
Parameters:
x,y,z- Target coordinates (required)speed- Movement speed multiplier (optional)range- Maximum pathfinding range (optional)distance_margin/margin- How close in blocks to get to target (optional)
wander
Random wandering within a radius.
Code: |
- wander radius=10 pathfind=true
|
Parameters:
radius- Wander radius in blocks (default: 10)pathfind- Whether to use pathfinding (default: true)
look
Makes the NPC look at specific coordinates.
Code: |
- look 100 64 200
|
Parameters:
x,y,z- Target coordinates (required)
teleport
Teleports the NPC to coordinates.
Code: |
- teleport 100 64 200
- teleport 100 64 200 world_name
|
Parameters:
x,y,z- Target coordinates (required)world- World name (optional, defaults to current world)
Timing
wait
Waits for a specified duration.
Code: |
- wait 3s # 3 seconds
- wait 100t # 100 ticks
- wait 2m # 2 minutes
|
Duration formats:
s- secondst- ticksm- minutes- Can also use expressions:
wait `some_expression`
wait_until
Waits until a condition becomes true.
Code: |
- wait_until `player.distance < 10`
|
cooldown
Rate limits behaviors. Only succeeds if enough time has passed since the last time it was run.
Code: |
- cooldown greeting 30s
- say "Hello there!"
|
Parameters:
key- Cooldown identifier (required)duration- Cooldown duration (default: 1s)
Memory Management
set
Sets a memory variable.
Code: |
- set counter 5
- set name "Bob"
- set calculated `10 * 5`
|
Parameters:
key- Variable name (required)value- Value to set (required, can be expression)
forget
Removes a memory variable.
Code: |
- forget counter
|
clear_memory
Clears all memory variables.
Code: |
- clear_memory
|
Communication
say
Makes the NPC say a message to nearby players.
Code: |
- say "Hello, world!"
- say `"Player count: " + player.count`
|
emit_signal
Emits a signal.
Code: |
- emit_signal "start_patrol"
|
emit_signal_to
Emits a signal to a specific NPC by ID or UUID.
Code: |
- emit_signal_to 123 "alert"
- emit_signal_to "uuid-string" "alert"
|
Parameters:
npc- NPC ID or UUID (required)signal- Signal name (required)
emit_global_signal
Emits a signal that all NPCs receive.
Code: |
- emit_global_signal "server_restart_warning"
|
wait_for_signal
Waits until a signal is received.
Code: |
- wait_for_signal "start_patrol"
- say "Signal received!"
|
World Interaction
break_block
Breaks a block realistically using the NPC's held item at the specified coordinates.
Code: |
- break_block 100 64 200 radius=3
|
Parameters:
x,y,z- Block coordinates (required)radius- Maximum distance in blocks to break from (default: 3)
Control Flow
succeed
Always returns SUCCESS.
Code: |
- succeed
|
fail
Always returns FAILURE.
Code: |
- fail
|
repeat
Repeats a specified number of times.
Code: |
- repeat 5:
- say "Counting..."
- wait 1s
|
Parameters:
count- Number of repetitions (default: 1)
Special Syntax
Command Execution
Lines starting with / are executed as commands:
Code: |
tree:
sequence:
- /npc speak "Hello from command!"
- /say This is a server command
|
Expression Evaluation
Lines starting with backtick (`) are evaluated as expressions:
Code: |
tree:
sequence:
- `inv.add(item.json('{"material":"DIAMOND","amount":64}'))`
- `memory.set('flag', true)`
|
Expression System
The behavior tree system integrates with the Expression Registry for dynamic values.
Expression Syntax
Expressions can be used in several ways:
Inline in conditions:
Code: |
- if `memory.get('counter') < 10`:
- say "Counter is low"
|
In parameter values:
Code: |
- walkto `npc.location.x + 10` `npc.location.y` `npc.location.z`
|
In text:
Code: |
- say "Counter value: {memory.get('counter')}"
|
Standalone evaluation:
Code: |
- `memory.set('result', 10 * 5)`
|
Memory System
The memory system provides persistent storage across behavior executions.
Usage
Code: |
tree:
sequence:
- set visited_waypoint_1 true
- set visit_count `memory.get('visit_count', 0) + 1`
- if `memory.has('greeted')`:
- say "Welcome back! Visit #{memory.get('visit_count')}"
else:
- say "First visit!"
- set greeted true
- forget temporary_flag
|
Memory Methods
memory.set(key, value)- Set a valuememory.get(key, default)- Get a value with optional defaultmemory.has(key)- Check if key existsmemory.remove(key)- Remove a keymemory.clear()- Clear all memory
Memory is automatically saved and loaded with the NPC.
Complete Examples
Basic Patrol
Code: |
tree:
sequence:
- set patrol_started true
- loop `memory.get('patrol_count', 0) < 3`:
- walkto `npc.location.x + 10` `npc.location.y` `npc.location.z`
- wait 2s
- cooldown speak 10s
- say "Patrolling area {memory.get('patrol_count', 0)}"
- walkto `npc.location.x - 10` `npc.location.y` `npc.location.z`
- set patrol_count `memory.get('patrol_count', 0) + 1`
- say "Patrol complete!"
- forget patrol_count
|
Signal Coordination
Guard NPC:
Code: |
tree:
sequence:
- wait_for_signal "intruder_alert"
- say "Intruder detected! Responding!"
- walkto `signal.data.x` `signal.data.y` `signal.data.z`
- emit_global_signal "area_secured"
|
Lookout NPC:
Code: |
tree:
sequence:
- if `player.distance < 10`:
- cooldown alert 60s
- emit_global_signal "intruder_alert"
- say "Alert! Intruder spotted!"
|
Conditional Greeter
Code: |
tree:
sequence:
- if `memory.has('greeted')`:
- say "Welcome back!"
else:
- sequence:
- say "Nice to meet you!"
- set greeted true
- cooldown greet 10s
- wait 2s
- say "Have a nice day!"
|
Resource Gathering
Code: |
tree:
sequence:
- set gathered 0
- loop `memory.get('gathered', 0) < 5`:
- wander radius=20 pathfind=true
- wait 5s
- break_block `npc.location.x` `npc.location.y - 1` `npc.location.z`
- set gathered `memory.get('gathered', 0) + 1`
- say "Gathered {memory.get('gathered')} resources"
- say "Resource gathering complete!"
- teleport 0 64 0
|
Loading Behavior Trees
External File
You can load trees from external files in the behaviors/ directory.
Code: |
traits:
- name: behavior
tree: patrol_behavior.yml
|
The behaviors will be loaded from plugins/Citizens/behaviors/patrol_behavior.yml
Template System
Behavior trees can be included in templates:
Code: |
greeter:
traits:
- name: behavior
tree:
sequence:
- cooldown greet 10s
- say "Hello there, traveler!"
- wait 2s
|
API
Registering Custom Behaviors
Register custom behaviors with the BehaviorRegistry:
Code: |
registry.registerBehavior("my_behavior", (params, context) -> {
String arg = context.getArgOrParam(0, "param_name", params, "default");
ExpressionValue argHolder = registry.getExpressionRegistry().parseValue(arg);
return new InstantBehavior() {
@Override
public void reset() {
}
@Override
public BehaviorStatus run() {
NPC npc = context.getNPC();
// Your behavior logic here
return BehaviorStatus.SUCCESS;
}
@Override
public boolean shouldExecute() {
return true;
}
};
});
|
Custom Expression Languages
Register custom expression engines with the ExpressionRegistry for domain-specific languages.