Aller au contenu
Afficher dans l'application

Une meilleure façon de naviguer. En savoir plus.

Asterobot

Une application plein écran sur votre écran d'accueil avec notifications push, badges et plus encore.

Pour installer cette application sur iOS et iPadOS
  1. Appuyez sur Icône de partage dans Safari
  2. Faites défiler le menu et appuyez sur Ajouter à l'écran d'accueil.
  3. Appuyez sur Ajouter dans le coin supérieur droit.
Pour installer cette application sur Android
  1. Appuyez sur le menu à trois points (⋮) dans le coin supérieur droit du navigateur.
  2. Appuyez sur Ajouter à l'écran d'accueil ou Installer l'application.
  3. Confirmez en appuyant sur Installer.

asterobot:fight

Drive the fight module from a script: switch it on or off, change its profile and speed, read the fight under way, follow every fight's events, and take its decisions yourself at the placement, ready and each action of a turn, with the rules engine checking every action.

asterobot:fight lets your script work with the bot's fight module, which plays its fights as its fight profile says. The guide describes the module for players: The fight module.

import {
  enable, disable, enabled,
  setProfile, profile, setSpeed, speed, setFights, fights, state,
  onFight, offFight, onDecision, offDecision,
} from "asterobot:fight";

These are the module's only exports. There's no fight object grouping them.

Export Returns What it does
enable(), disable() Nothing Switch the module on or off while the script runs
enabled() A boolean Whether the module is on, all settings considered
setProfile() Nothing Play another profile while the script runs
profile() An object The profile in effect
setSpeed(), speed() Nothing, a string or a timing object Set or read the speed
setFights(), fights() Nothing, an array Set or read the kinds of fight it plays
state() An object or undefined The fight under way
onFight(), offFight() A handle, nothing Follow every fight's events
onDecision(), offDecision() A handle, nothing Take the module's decisions

Every function is synchronous: none returns a promise.

Overrides

enable(), disable(), setProfile(), setSpeed() and setFights() override the settings the person saved on the bot's Fight tab. The tab then says what the script overrides, and the person's settings play again once the script stops.

  • They last while the script runs. A reload starts a new script: its first override replaces the old script's, and until then the old script's stay. A reload that fails to start leaves the running script's.
  • The switch takes effect at the next turn. Switched during a fight, the module starts or stops playing at the next turn's start, so one turn is never played by two. Before the first turn, and for the profile and the speed, the change applies at once.
  • On a relayed session the switch stays the person's. enable() and disable() change nothing there, and enabled() says what's in effect. The profile, the speed and the kinds of fight still apply.
  • A profile the script sets plays every fight, against players too, whatever Profile in PvP fights the tab sets.

enable() and disable()

enable() switches the module on and disable() off, as the switch on the Fight tab does. enabled() returns true when the module is on, all settings considered: the saved switch, or the script's.

import { disable } from "asterobot:fight";

// This package plays its fights itself.
disable();

setProfile()

setProfile(nameOrProfile) plays another profile while the script runs.

Argument Plays
A profile's name "Idle", a profile of the person's pool, or a package's, such as "@some-author:fighters/air-cra"
"" The default profile
An object A profile your script writes, in the fight profile format

An object is read as JSON.stringify writes it, then checked as a saved profile is. Its extends is merged in, one level as always. A malformed profile throws a TypeError naming the field, and its warnings go to the bot's console. A name no profile has, or a saved profile that can't be played, throws an Error.

import { setProfile } from "asterobot:fight";

setProfile({
  version: 1,
  name: "Careful",
  extends: "@some-author:fighters/air-cra",
  risk: 20,
  rules: [{ when: { "self.life": { below: "30%" } }, do: { retreat: { to: "cover" } } }],
});

profile()

profile() returns the profile in effect as a plain object in the format, the profile it extends already merged in, so without extends. The weapon is written "weapon". The object is a copy: change it, then set it back.

import { profile, setProfile } from "asterobot:fight";

// Keep everything, but walk no more.
const current = profile();
setProfile({ ...current, parts: { ...current.parts, movement: false } });

A field this Asterobot doesn't read isn't in the copy.

setSpeed() and speed()

setSpeed(speed) sets the speed while the script runs: "slow", "normal" or "turbo", or a custom timing object, as the Custom speed of the Fight tab sets it:

setSpeed({
  actions: { min: 0 },          // no wait between actions
  cast: { min: 300, max: 800 }, // 300 to 800 ms before each spell, at random
  endTurn: { min: 1000 },       // 1 s before ending the turn
  answerAtOnce: true,           // answer the server at once, as "turbo" does
});
Field What it is
actions The wait before every decision without one of its own: each move, spell and end of turn, the placement and ready. Required.
placement, ready, move, cast, endTurn The wait before a decision of that kind. Absent waits actions.
answerAtOnce true answers the server at once, as "turbo" does: the fight map's load, each action's acknowledgement and the turn-ready answers. Absent or false answers once the game client would have played what it answers.

A wait is { min, max } in milliseconds, each from 0 to 60000: min, or a random wait from min to max when max is above it. A wait never costs the turn: a decision goes out 2 seconds before the turn ends at the latest. The waits only change when the module acts, never what it decides.

Any other value, a field a timing doesn't have, or a wait out of bounds throws a TypeError. speed() returns the speed in effect: the preset's name, or the timing object for a custom speed. Speed says what each one does, and what "turbo" and answering at once risk.

setFights() and fights()

setFights(kinds) sets the kinds of fight the module plays while the script runs, as the Fights it plays checkboxes of the Fight tab do. An empty array plays none. In a fight of another kind, or of a type whose meaning isn't known yet, the module is off, as with disable(), from the moment the game says what the fight is, just after its map's load. An unknown kind throws a TypeError. fights() returns the kinds in effect: ["monsters"] unless the person or a script chose others.

Kind Fights
"monsters" Fights against monsters, quest fights where an NPC fights beside the character included, and breaches
"aggressions" A player attacking another
"duels" Duels between players
"kolizeum" Kolossium fights
"taxCollectorsAndPrisms" Fights for a perceptor (tax collector), a prism or an alliance's territory
import { setFights } from "asterobot:fight";

// Defend against aggressions while farming.
setFights(["monsters", "aggressions"]);

Starting a fight against players is your script's job, as for monsters: accepting a duel, registering for the Kolossium. Against players, the module reckons each enemy hits bare-handed, since it doesn't know their spells yet: it plays, but takes more risks than against monsters.

state()

state() returns the fight under way, as the module follows it from the server's messages, or undefined outside one. Every call returns a new snapshot.

Property Type Value
characterId BigInt The bot's character
round number The fight's round, 0 before it starts
turn BigInt The fighter whose turn it is; absent during the placement
fighters array Every fighter, the dead ones included

Each fighter:

Property Type Value
id BigInt Its id in the fight: negative for monsters and summons
kind string "character", "sidekick" or "creature"
ally boolean Whether it's on the character's side, the character included
team number Its team: fighters of one team share it
alive boolean Whether it's alive
cell number Its cell
invisible boolean Whether the server hides it from the other team: cell is then where it was last seen
level number Its level
life, maxLife, shield number Its life, its most life, its shield points
ap, mp number The AP and MP it has left
states array of numbers The states it's in, by spell_states id
breed number A character's class, by breeds id
monsterId, grade number A creature's monster, by monsters id, and its grade
summonerId BigInt The fighter that summoned it, for a summon
import { state } from "asterobot:fight";

const fight = state();
const mastogob = fight?.fighters.find((f) => f.monsterId === 2854 && f.alive);

onFight()

onFight(handler) calls handler with each event of every fight the bot is in, whether the module plays it or not, until offFight(handle). It returns the handle.

type When Also has
"started" The bot entered a fight, or got one back after a reconnection
"turnStarted" A fighter's turn started fighter (BigInt), round
"actionPlayed" The module played an action: only the module's own fighter, round, action, spell, cell, reasons, scripted
"turnEnded" A fighter's turn ended fighter, round
"ended" The fight ended, or the session did first round, summary

actionPlayed describes what the module sent, as the Turn log keeps it:

Property Value
fighter The character, or for a turn's action the summon it controls whose turn it is
action "placement", "ready", "move", "cast" or "endTurn"
spell The spell cast: its id, or "weapon"
cell The cell cast on, walked to, or placed on
reasons score, the score of the turn this action starts, terms, what the score counted by term, and options, the best options weighed, { spell, cell, score }. Absent for the placement, ready, and an end of turn a rule asked for.
scripted true when a decision hook decided it

ended's summary has rounds, turns (the character's), actions (the module's), alive (whether the character was alive at the end) and duration, the fight's length in milliseconds as the server gave it, absent when the session ended first.

import { onFight } from "asterobot:fight";
import { botInfo } from "asterobot:bot";

onFight((event) => {
  if (event.type === "ended") botInfo(`Fight over in ${event.summary.rounds} rounds, alive: ${event.summary.alive}`);
});

Handlers run in the order they were registered. What a handler returns is ignored, and an async handler is fine. A handler that throws stops the script, as an on() handler does.

An event reaches the script at the moment the module follows it: before your protocol handlers hear the message it comes from, on every PC. actionPlayed comes when the module sends the action.

onDecision()

onDecision(hook) asks hook at each of the module's decisions, until offDecision(handle). It returns the handle. The module decides first, as its profile says, then asks your hooks, and plays your answer instead when the rules engine accepts it.

The hook receives { stage, fight, options }:

stage When options, best first The hook can answer
"placement" Before the fight, when the profile places the character The placement cells, { cell, score } { cell }: one of the options
"ready" Before the fight, when the profile says the character is ready None { wait: true }
"action" Before each action of a turn the module plays, the character's and those of the summons it controls The best turns the AI found for the rest of the turn, { actions, score, terms } { option: i }, { actions: [...] } or { endTurn: true }

fight is what state() returns. The challenges' stage comes with the challenges, which the module doesn't choose yet.

Answers

  • { cell } places the character on that cell. It must be one of the options: the character's own cell, or one of its team's placement cells nobody stands on.
  • { wait: true } holds the character's ready. The hooks are asked again each time the fight changes, such as an ally joining, and the placement countdown still starts the fight when it runs out.
  • { option: i } plays the first action of options[i]. Then the module decides again: the AI searches again, and the hooks are asked again.
  • { actions: [...] } plays the actions one after the other, without asking again between them. Return every action and { endTurn: true } to play the whole turn.
  • { endTurn: true } ends the turn.

Each action is one of:

Action Does
{ move: cell } Walks to the cell
{ cast: spell, cell } Casts the spell on the cell: its id, or "weapon" for the weapon or bare hands
{ cast: spell, target } Casts the spell on a fighter's cell: target is an id of fight.fighters
{ endTurn: true } Ends the turn

Each turn of options lists its actions the same way, ending with { endTurn: true }, so { actions: options[i].actions } plays a whole turn the AI found.

undefined, null and a promise are no answer: the next hook is asked, then the AI decides. A hook is never awaited.

import { onDecision } from "asterobot:fight";

onDecision(({ stage, fight, options }) => {
  if (stage !== "action") return;

  // Open on the Royal Mastogob with Explosive Arrow when it's there.
  const mastogob = fight.fighters.find((f) => f.monsterId === 2854 && f.alive);
  if (mastogob && fight.round === 1) return { actions: [{ cast: 32445, target: mastogob.id }] };

  // Otherwise prefer the best turn that doesn't move.
  const still = options.findIndex((o) => !o.actions.some((a) => "move" in a));
  return still >= 0 ? { option: still } : undefined;
});

What the module checks

The rules engine checks each action before it's sent, as it checks the AI's: the AP, the range, the line of sight, the cooldowns, the cells a walk can reach with its tackle. The module also never sends an action the server refused earlier in the turn. When it can't play an answer, or a hook throws, runs past the time limit or answers something of another shape, the AI's decision is played and the bot's console says why.

A list of actions stops early, and the module decides again with the AI and the hooks:

  • at an action the rules engine or the server refuses;
  • when the fight isn't what the engine predicted after the action before: a fighter that died or lived otherwise, one on another cell, or the fighter acting with other AP or MP left. Life isn't compared, since the roll is the server's.

The console says why, with The fight module stops the script's actions:.

Timing

  • The module waits for your answer as long as it takes. The question goes through the script's queue like every message, so on a slow PC it's answered later, never skipped: such a PC may lose a turn, but it never decides otherwise. When no hook is registered, nothing is asked and nothing waits.
  • What a hook decides goes out after your frames. An answer takes its turn among what your script sends, as a movement does: a hook that calls send(X) and then returns a cast puts X on the wire first. What the AI decides alone is sent directly.
  • The speed still applies. Each action goes out after the wait the speed sets before it, as the AI's do.

The Turn log marks the decisions your hooks made By the script, and actionPlayed has scripted: true.

While Asterobot reads declarations

When Asterobot reads a package's settings, steps and conditions, it runs the top level of its modules without a bot (see Declarations). There's no fight then: state() returns undefined, onFight() and onDecision() return a handle that never fires, and offFight() and offDecision() do nothing. The other functions throw, since they read or change a bot's module.

Errors

Error Type Cause
fight.setProfile: fight: malformed profile: <field>: <reason> TypeError The profile object can't be played. The format's errors list each reason.
fight.setProfile: fightprofiles: no profile has this name: "<name>" Error No profile has that name. For a package's profile, the reason follows, such as no version of <package> is installed.
fight.setProfile: fight: malformed profile: <field>: <reason>, for a name Error The saved profile of that name can't be played
fight.setProfile takes a profile's name or a profile object TypeError The argument is neither a string nor an object
fight.setSpeed: fight: the speed must be slow, normal, turbo or custom, not "<speed>" TypeError Another speed
fight.setSpeed: fight: a custom speed needs its timing, each wait from 0 to 60000 ms TypeError "custom" passed as a string: pass the timing object
fight.setSpeed: fight: a custom speed needs its timing, each wait from 0 to 60000 ms, not <ms> TypeError A wait out of bounds
fight.setSpeed: json: unknown field "<field>" TypeError A field a timing doesn't have
fight.setSpeed takes "slow", "normal", "turbo" or a timing object TypeError The argument is neither a string nor an object
fight.onFight handler must be callable, fight.onDecision handler must be callable TypeError The handler isn't a function
maximum behavior handlers reached TypeError 256 handlers are registered already, protocol handlers included

These lines appear on the bot's Console and don't stop the script:

Line Cause
fight decision hook failed: <error>: the AI decides A hook threw, or ran for more than 5 s
fight decision hook answered <stage>: not an answer this stage takes: <shapes>: the AI decides A hook answered an object of another shape
fight decision hook returned a Promise, which counts as no answer: a hook answers synchronously, so it can't be async An async hook, or one returning a promise. Said once per script.
The script's decision can't be played: the fight module decides instead The rules engine refused the answer, with the reason. Said once a turn.
The fight module stops the script's actions: <reason> A list of actions stopped early
Nothing plays this fight unless the bot's script does: the server disconnects a character left idle for a few minutes On a Full socket bot, the module is off for the fight, or doesn't play its kind

A hook that fails again and again is reported at the 1st, 10th, 100th failure in a row, ending with (10 times) and so on.

Fights explains how scripts and the module share a fight, and what a script sends when it plays fights itself.

Compte

Navigation

Recherche

Recherche

Configurer les notifications push du navigateur

Chrome (Android)
  1. Appuyez sur l'icône de cadenas à côté de la barre d'adresse.
  2. Tap Autorisations → Notifications.
  3. Ajustez vos préférences.
Chrome (Desktop)
  1. Cliquez sur l'icône représentant un cadenas dans la barre d'adresse..
  2. Select Paramètres du site.
  3. Find Notifications et ajustez vos préférences.