The exact JSON format of a fight profile, every field with its type, values and default, conditions, actions and targets, how extends merges, and every error and warning Asterobot reports.
A fight profile is a JSON object. Players write them on the Fight Profiles page, where the editor shows the same JSON (Fight profiles), and packages ship them as files, one per profile, in their fightprofiles folder (Ship fight profiles). This page is the complete format. What each setting does in a fight is explained, for players, in Profile settings and Rules and conditions.
Every field is optional but name. A field left out plays its default, and a field Asterobot doesn't know is kept and ignored, so a profile written for a newer Asterobot still loads.
{
"version": 1,
"name": "Air Cra",
"role": "damage",
"range": "ranged",
"elements": ["air"],
"risk": 40,
"positioning": "kite",
"spells": [{ "id": 32465, "use": "prefer" }],
"rules": [
{ "when": { "self.life": { "below": "30%" } }, "do": { "retreat": { "to": "outOfReach" } } }
]
}
Fields
| Field | Type | Values | Default |
|---|---|---|---|
version |
number | 1 |
1 |
name |
string | Required, not blank | |
description |
string | "" |
|
extends |
string | A profile's name: "Idle", a pool profile's, or a package's "<package>/<name>" |
"": none |
role |
string | "damage", "tank", "support", "healer" |
The role the game ranks first for the character's class |
range |
string | "auto", "melee", "mid", "ranged" |
"auto" |
elements |
array of strings | "earth", "fire", "water", "air", "neutral" |
[]: whatever hits hardest |
risk |
number | 0 to 100 | 50 |
placement |
string | "auto", "close", "far" |
"auto" |
positioning |
string | "auto", "charge", "hold", "kite", "hide" |
"auto" |
focus |
string | "auto", "lowestLife", "closest", "mostDangerous" |
"auto" |
allies |
string | "ignore", "help", "protect" |
"help" |
waitForAllies |
number | 0 or more | 0 |
parts |
object | See parts | Every part on |
weapon |
object | See weapon | Normal use, no limit |
spells |
array | See spells | [] |
opening |
object | See opening | No opening |
monsters |
array | See monsters | [] |
rules |
array | See rules | [] |
weights |
object | See weights | The role's weights |
challenges |
any | Kept and ignored: challenges aren't played yet |
An empty string in a field that takes a word, such as "role": "", is the same as leaving it out. A word the field doesn't take is an error.
parts
| Field | Type | Default | When false |
|---|---|---|---|
placement |
boolean | true |
The module doesn't place the character. |
ready |
boolean | true |
The module doesn't say the character is ready. |
movement |
boolean | true |
The character never walks in its turns. |
challenges |
boolean | true |
No effect yet. |
Writing parts with some fields leaves the others true.
weapon
| Field | Type | Values | Default |
|---|---|---|---|
use |
string | "prefer", "normal", "avoid", "never" |
"normal" |
maxPerTurn |
number | 0 or more, 0 for no limit but the weapon's own | 0 |
spells
Each entry says what the profile does with one spell.
| Field | Type | Values | Default |
|---|---|---|---|
id |
number | A spell's id. Required, not 0: the weapon is set with weapon. |
|
use |
string | "prefer", "normal", "avoid", "never" |
"normal" |
maxPerTurn |
number | 0 or more, 0 for no limit but the spell's own | 0 |
target |
string | "any", "enemy", "ally", "self" |
"any" |
minTargets |
number | 0 or more: the enemies a cast must hit | 0 |
when |
object | A condition, where target.life and target.distance test the fighter the spell is cast on |
Always |
opening
| Field | Type | Values |
|---|---|---|
firstTurn |
array | Steps cast first in the character's first turn, before everyTurn's |
everyTurn |
array | Steps cast first in each of its turns |
A step is a spell's id, "weapon", or { "spell": <id or "weapon">, "target": <target> } with a target. Steps are cast in order, each when it can be; a step that can't be cast is passed over. A spell listed twice is cast twice.
monsters
| Field | Type | Values | Default |
|---|---|---|---|
id |
number | A monster's id | |
race |
number | A monster race's id | |
focus |
string | "first", "last", "ignore" |
As any other |
keepAway |
number | 0 or more: the fewest cells the character ends its turns from it | 0 |
noMelee |
boolean | true: ends its turns at least 2 cells from it |
false |
Each entry needs id or race. It applies to every enemy it matches.
weights
An object of numbers, 0 or more, by scoring term: damage, kill, heal, control, buff, summon, safety, position, waste, allyHarm and mark. A term left out keeps the role's weight. How a turn is scored gives each role's weights.
Rules
rules is an array of rules, applied in order. Each rule is an object:
| Field | Type | Value |
|---|---|---|
when |
object | A condition. Left out, the rule always applies. |
do |
object | Exactly one action. |
Actions
do holds one of these keys:
| Key | Value | Kind |
|---|---|---|
cast |
{ "spell": <id or "weapon">, "target": <target> }, spell required, target optional |
Forcing |
attack |
{ "target": <target> }, target optional |
Forcing |
heal |
{ "target": <target> }, target optional |
Forcing |
buff |
{ "target": <target> }, target optional |
Forcing |
endTurn |
{ "after": <seconds> }, after 0 or more, default 0 |
Forcing |
retreat |
{ "to": "cover" } or { "to": "outOfReach" } |
Movement |
approach |
{ "target": <target> }, target optional: the nearest enemy |
Movement |
keepDistance |
{ "min": <cells>, "max": <cells> }, 0 for no bound |
Movement |
focus |
{ "target": <target> } |
Modifier |
avoid |
{ "spell": <id or "weapon">, "target": <target> }, either or both |
Modifier |
Every matching modifier applies. Of the movement rules, the first matching one applies. Of the forcing actions, the opening's steps come first and then the rules in order, and the first one that can be played now is the character's next action. How rules apply describes it in full.
Targets
A target is one of these strings, or an object:
| Target | Names |
|---|---|
"self" |
The character |
"lowestLifeEnemy" |
The enemy with the least life left |
"lowestLifeAlly" |
The ally with the least life left, the character not counted |
"closestEnemy" |
The enemy nearest the character |
"mostDangerousEnemy" |
The enemy that could deal the character the most where it stands |
{ "monster": <id> } |
An enemy of that monster |
{ "race": <id> } |
An enemy of that monster race |
{ "fighter": <id> } |
The fighter with that id in the fight |
Ties go to the lowest fighter id.
Conditions
A condition is an object whose keys are subjects, each with its test, and the groups all, any and not. It holds when every test and every group in it holds.
{
"self.life": { "below": "50%" },
"any": [{ "enemies.within": { "cells": 1, "atLeast": 1 } }, { "fight.round": { "atLeast": 5 } }],
"not": { "self.state": { "is": 42 } }
}
| Group | Value | Holds when |
|---|---|---|
all |
An array of conditions | Every one holds |
any |
An array of conditions | At least one holds |
not |
A condition | It doesn't hold |
A test is an object with some of these fields:
| Field | Type | Holds when |
|---|---|---|
below |
number, or a percent as a string such as "30%" |
The value is strictly less |
above |
number, or a percent as a string | The value is strictly more |
is |
number | The count is exactly this, or that state, monster or race is there |
atLeast |
number | The count is this or more |
atMost |
number | The count is this or less |
cells |
number | For within: the distance counted |
| Subject | Test fields | Reads |
|---|---|---|
self.life |
below, above |
The character's life; a percent is of its maximum life |
self.shield |
below, above |
Its shield points; a percent is of its maximum life |
self.ap |
below, above |
Its AP |
self.mp |
below, above |
Its MP |
self.state |
is |
It is in that state |
enemy.state |
is |
A living enemy is in that state |
ally.state |
is |
A living ally, the character not counted, is in that state |
ally.life |
below, above |
A living ally's life is in that range; a percent is of that ally's maximum life |
enemies.count |
is, atLeast, atMost |
Living enemies, summons included |
allies.count |
is, atLeast, atMost |
Living allies, summons included, the character not counted |
enemies.within |
cells, then is, atLeast, atMost |
Living enemies within cells cells of the character |
allies.within |
cells, then is, atLeast, atMost |
Living allies within cells cells |
enemy.monster |
is |
That monster is among the living enemies |
enemy.race |
is |
A monster of that race is among the living enemies |
fight.round |
is, atLeast, atMost |
The fight's round |
fight.turn |
is, atLeast, atMost |
The character's turns in the fight, the current one included |
target.life |
below, above |
In a spell's when only: the life of the fighter it's cast on |
target.distance |
below, above |
In a spell's when only: that fighter's distance in cells |
Each subject takes only its own test fields, at least one of them, and within needs cells of 1 or more: anything else is refused, naming the subject. self.ap, self.mp and target.distance take numbers, not percents. A subject the module doesn't know never holds, with a warning, and so do target.life and target.distance in a rule's when, and challenge, reserved for challenges, until the module plays them.
How extends merges
A profile with extends starts from the profile it names and writes its own fields over it:
| Field | Merged |
|---|---|
A string, number or boolean, and the lists elements, opening.firstTurn and opening.everyTurn |
The profile's own replaces the extended one's |
An object (parts, weapon, opening) |
Field by field |
weights |
Term by term |
spells |
The extended profile's entries, then the profile's own; an entry with the same id replaces the extended one |
monsters |
The same, by id and race |
rules |
The extended profile's rules, then the profile's own |
The extended profile can't extend another one. A profile that extends a package's profile plays it from the newest version of that package installed.
Errors
Asterobot refuses a profile it can't play: the editor won't save it, an import is refused, and a pool file or a package's profile shows Can't be played on the Fight Profiles page, a bot set to it playing the default profile. The reason starts with fight: malformed profile:, followed by the field, in the JSON's own path, and what's wrong. Only the first problem is reported.
| Reason | Cause |
|---|---|
<field>: a <type>, not a <json type> |
A field holds the wrong kind of value, such as a string where a number goes. The types are Go's: int, int32, float64, string, bool. |
version: required, 1 |
version is less than 1 |
name: required |
name is missing or blank |
<field>: "<value>" is not one of <values> |
A word a field doesn't take: role, range, placement, positioning, focus, allies, weapon.use, spells[i].use, spells[i].target, monsters[i].focus, rules[i].do.retreat.to, or a target's name |
risk: <n> is not from 0 to 100 |
risk out of range |
elements[i]: "<value>" is not earth, fire, water, air or neutral |
An unknown element |
<field>: <n> is below 0 |
A negative waitForAllies, weapon.maxPerTurn, spells[i].maxPerTurn, spells[i].minTargets, monsters[i].keepAway, weights.<term> or rules[i].do.endTurn.after |
spells[i].id: required: the weapon is the profile's weapon |
A spell entry without a spell, or with spell 0 |
monsters[i]: an id or a race is required |
A monster entry with neither |
<target>: names nobody |
A target object without monster, race or fighter |
rules[i].do: one action, not <n> |
A rule's do with no action, or more than one |
rules[i].do.keepDistance: min <a> is above max <b> |
min above a non-zero max |
fight: a spell is an id or "weapon": "<text>" |
A spell named by any other text |
when "<key>": <reason> |
A condition's test that isn't an object of the right shape |
<path>.<subject>: takes below or above, takes is or takes is, atLeast or atMost |
A test without the fields its subject takes, or with others |
<path>.<subject>: a number, not a percent |
A percent on self.ap, self.mp or target.distance |
<path>.<subject>.cells: required, 1 or more |
enemies.within or allies.within without cells |
<path>.<subject>.cells: only allies.within and enemies.within take it |
cells on another subject |
rules[i].do.cast.spell: required: a spell's id, or "weapon" |
A cast without a spell |
extends: "<name>" extends another profile, and one level is all |
The extended profile extends another |
extends: "<name>": <reason> |
The extended profile can't be read |
spells[i] is the entry's place in the list, counted from 0, as in spells[0].use. A target's field is written in full, such as rules[2].do.attack.target or opening.firstTurn[0].target.
Pool profiles have their own name errors when they're saved: fightprofiles: a profile's name can't be empty, start with @, or hold / or a character a file name can't, and fightprofiles: a profile already has this name.
Warnings
Warnings don't stop a profile from playing. The Fight Profiles page counts them and the editor lists them.
| Warning | Cause |
|---|---|
version: <n> is newer than this module's 1: what it adds is ignored |
A profile written for a newer Asterobot |
<path>.<subject>: no such subject: never holds |
A condition's subject this Asterobot doesn't know |
<path>.target.<field>: only a spell's condition has a target: never holds |
target.life or target.distance in a rule's when |
<path>.challenge: challenges aren't played yet: never holds |
A challenge test, before the module plays challenges |
weights.<name>: no such scoring term: ignored |
A weight with an unknown name |
extends: no profile has this name: its own fields play over the default profile |
The extended profile is missing |
spell <id>: not in the game data, ignored |
An id the newest installed game version doesn't have, also given as monster, monster race and state |
name: "<name>", but its file is "<file>.json": it plays as "<file>" |
A pool file whose name differs from its file name |
Aucun avis à afficher.