Formats/Behavior tree
Behavior trees file format is game-specific (mods may modify it).
Contents
- 1 Introduction
- 2 Syntax Reference
- 2.1 Keywords
- 2.2 The behavior keyword
- 2.3 The action keyword
- 2.4 The concurrent keyword
- 2.5 The sequence keyword
- 2.6 The selector keyword
- 2.7 The fallback keyword
- 2.8 The decorator keyword
- 2.9 The return keyword
- 2.10 The invert keyword
- 2.11 The <code>timer keyword
- 2.12 The condition keyword
- 2.13 Operators
- 2.14 Functions
- 2.15 Pre-defined symbols
- 2.16 Notes
Introduction
Bot behaviors are decided individually, at each "frame" (read: server's main loop passage), starting from a root node (I am not sure when exactly evaluation ends). An overview can be found in the Wikipedia article.
Syntax Reference
Behavior trees use a specific syntax, which obey following rules (guessed from actual implementation and some C++ readings):
- Each node or leaf returns either
STATUS_SUCCESS
,STATUS_FAILURE
orSTATUS_RUNNING
. - at most one node, leaf, block start or block end per line
- empty lines are accepted
- comments can either:
- start with
//
and go to the end of line - start with
/*
and go to next*/
, including out of current line (TODO: verify).
- start with
- comments can not be nested
- actions and conditions taking parameters must enclose those withing parentheses, separated with commas (i.e.
roamInRadius( E_H_REACTOR, 500 )
) - if an action or a condition does not need parameters, parentheses are optional
- spaces and tabulations are meaningless
- strings start and end with a double quote
"
- keywords are reserved (TODO: verify, but even if wrong, better avoid their use)
- values can't be stored or modified (additions, subtractions, multiplications, etc)
- no user-defined function (but files can be included, and C++ functions can be written in the game logic and be used in the behavior tree)
- each possible path should (TODO: verify, even if I fail to understand why one would break that willingly) end by one or more action
Note:
It is not possible to do mathematical operations such as additions, multiplications, divisions, ...
Keywords
There are several keywords:
- behavior
- action
- sequence
- selector
- fallback
- concurrent
- decorator
- condition
The behavior
keyword
behavior NAME
Include named behavior at current place. It is unsure if this is just an inclusion of if the subtree is parsed and stored individually.
The action
keyword
action NAME
action NAME()
action NAME( param )
action NAME( param1, param2, ..., paramN )
Leaves of the tree, they trigger an action from the bot.
List of actions
Titles provide a link to more detailed pages. TODO: have something more time-resilient (one page per call, with it's changelog, maybe?)
0.53.1
- activateUpgrade
- aimAtGoal
- alternateStrafe
- buy
- changeGoal
- classDodge
- deactivateUpgrade
- equip
- evolve
- evolveTo
- fight
- fireWeapon
- flee
- gesture
- heal
- jump
- moveInDir
- moveTo
- moveToGoal
- repair
- resetStuckTime
- roam
- roamInRadius
- rush
- say
- strafeDodge
- suicide
- teleport
Creating new actions
Actions are exported in the AIActionMap_s AIActions[]
C array.
Actions always return a status.
Actions can take a variable number of parameters (they have minimum and maximum parameter numbers).
Parameter types are not described by API.
The concurrent
keyword
concurrent { NODE_1 NODE_2 ... NODE_N }
Concurrent nodes evaluate each children until one returns STATUS_FAILURE
.
Differences with sequence:
-
STATUS_RUNNING
does not trigger a return - Returns either
STATUS_SUCCESS
except if one child failedSTATUS_FAILURE
.
Notes:
- if 2 tasks were previously in
STATUS_RUNNING
and the 1st returnsSTATUS_FAILURE
, concurrent returns immediately.
The sequence
keyword
sequence { NODE_1 NODE_2 ... NODE_N }
Sequential nodes evaluate each children one after the other, as long as they return STATUS_SUCCESS
.
Return last child's status.
Differences with concurrent:
- Sequence breaks if a child returns
STATUS_RUNNING
- Starts at last
STATUS_RUNNING
, if any
The selector
keyword
selector { NODE_1 NODE_2 ... NODE_N }
Selector nodes evaluate each children one after the other, as long as they return STATUS_FAILURE
.
Return last child's status.
Does not restart from last STATUS_RUNNING
.
The fallback
keyword
fallback { NODE_1 NODE_2 ... NODE_N }
Fallback nodes start by evaluating the first children, and switch to the next one if a child returns STATUS_FAILURE
. It will continue from the last STATUS_RUNNING
child.
Return last child's status.
Difference with sequence:
- Sequence switch to the next child on
STATUS_SUCCESS
, while fallback switch to the next child on code>STATUS_SUCCESS</code>
Difference with selector:
- Starts at last
STATUS_RUNNING
, if any
The decorator
keyword
decorator TYPE( VALUE ) { NODE_1 NODE_2 ... NODE_N }
Decorator nodes alters their children's behavior. Decorator types:
- return
- invert
- timer
The return
keyword
Force children nodes to return a specific value
The invert
keyword
An invert
node will negate the return value of its node, STATUS_SUCCESS<code> is turned into <code>STATUS_FAILURE
and STATUS_FAILURE
is turned into STATUS_SUCCESS
. STATUS_RUNNINGcode> is unaffected.
The <code>timer keyword
On encountering a timer( N )
node, STATUS_FAILURE
is immediately returned if the timer's child node has already returned STATUS_FAILURE
within the last N milliseconds.
The node may run more often than every N milliseconds if it keeps returning success.
The condition
keyword
condition EXPRESSION
condition EXPRESSION { NODE }
The first form immediately returns EXPRESSION as its status: STATUS_SUCCESS
for true and STATUS_FAILURE
for false.
The second form returns STATUS_FAILURE
if the expression is false, or otherwise the child's status. Note that EXPRESSION is re-evaluated every frame. If it becomes false at any time during execution of the child node, the child subtree aborts and the condition node returns failure.
Note:
- only executes a single child.
- EXPRESSION can include function calls.
Operators
Operators are listed in the AIOpMap_s conditionOps[]
array.
operators sorted by precedence (1st have higher priority):
- "!"
- "<"
- "<="
- ">"
- ">="
- "=="
- "!="
- "&&"
- "||"
Functions
Return value is "boxed" (AIBox<T>()
) in the C++ code.
Conditions are exported in the AIConditionMap_s conditionFuncs[]
array.
List of functions (as of 0.53.1)
- alertedToEnemy
- aliveTime
- baseRushScore
- buildingIsDamaged
- canEvolveTo
- class
- cvar
- directPathTo
- distanceTo
- goalBuildingType
- goalIsDead
- goalTeam
- goalType
- haveUpgrade
- haveWeapon
- healScore
- inAttackRange
- isVisible
- matchTime
- momentum
- percentAmmo
- percentHealth
- random
- skill
- stuckTime
- team
- teamateHasWeapon
- weapon
Pre-defined symbols
Some values are pre-defined and usable as action's or function's parameters.
Those are exported by calling a macro named 'D' (yes, I know). List of exported symbols:
- human upgrades (including medkit)
- human weapons (excluding blaster)
- team names (aliens, humans, and none)
- alien buildings
- human buildings
-
E_GOAL
-
E_ENEMY
-
E_DAMAGEDBUILDING
-
E_SELF
- classes (human ones, alien ones, and
PCL_NONE
) - moves (forward, backward, right, left)
- some say commands (all, team, area, area_team)
- task/check status names (running, success, failure)
Notes
Parameters needs to be (un)wrapped in C++ before being accessed.
Parameters can be of the following (C) types:
- float
- int (probably better assume 32 bit signed integers)
- string (probably better assume null-terminated)
- double (Unboxed only)
- token (Boxed only, either a
TT_STRING
, a float or an int...)
To provide a value to an action or check, the C++ code must UnBox (AIUnBox<T>()
) it.