Give your package's own functions to the Studio with export const steps and export const conditions, with their fields, what run receives and returns, the ref field that asks for game data, and the errors Asterobot reports.
The Studio is where a plan is drawn: places on the world map, the order the bot visits them in, and what it does at each one. What a plan can do in the game comes from packages. A step is something done on a map, such as gathering, and a condition is a yes/no question, such as "are the bags fuller than 90%?". Your package gives the Studio its own steps and conditions by declaring them next to its settings and actions.
Plans are played by @asterobot:base, so a package that draws them depends on it. The Studio shows how a player draws one with your steps and conditions.
A function and its declaration
A step or a condition is an ordinary function your package exports, which your own code can call like any other. The declaration only describes it for the Studio, and points to it with run. Steps go in an object exported as steps from index.js, and conditions in one exported as conditions:
import { session, botInfo } from "asterobot:bot";
import { send } from "asterobot:protocol";
import { sleep } from "asterobot:timers";
/** Waits the given number of seconds, and ends early when the plan stops. */
export async function rest({ seconds }, context) {
for (let waited = 0; waited < seconds; waited++) {
// A plan only stops between steps, so a long step checks for itself.
if (context?.stopping) return;
await sleep(1000);
}
}
/** Whether the clock of the computer running Asterobot has passed the hour. */
export function after({ hour }) {
return new Date().getHours() >= hour;
}
/** @type {BehaviorSteps} */
export const steps = {
rest: {
label: "Rest",
description: "Waits, and ends early when the plan stops.",
args: {
seconds: { type: "int", label: "Duration", unit: "seconds", default: 30, min: 1 },
},
run: rest,
},
};
/** @type {BehaviorConditions} */
export const conditions = {
after: {
label: "After",
args: {
hour: { type: "int", label: "Hour", default: 20, min: 0, max: 23 },
},
run: after,
},
};
export default async function behavior(launch) {
// A new game connection starts with this message. MITM bots never launch
// with "initial": the Dofus client has already identified their session.
if (launch.reason === "initial") {
await send("IdentificationRequest", {
ticketKey: session.gameToken,
languageCode: session.language,
});
}
// Declared for the Studio, and still an ordinary function.
if (after({ hour: 20 })) botInfo("It's past 8 PM.");
}
Steps and conditions follow the rules of settings and actions about where they're declared:
- Only the exports of
index.jscount. - The Studio will list them in the order you write them.
- A package can export any of
parameters,actions,stepsandconditions, or none of them. - Asterobot reads them the way it reads settings, by running the top level of your modules without a bot. Declare settings describes what that means for your code.
- Each package declares its own, dependencies included. The Studio will offer the steps and conditions of the package you edit next to those of the packages it depends on.
Asterobot never calls run, neither while it reads the declarations nor while your script plays: only a plan calls it, when a bot plays the plan. Steps and conditions aren't buttons either, so they never appear under Actions on the bot's page. A function you want both in a plan and on a button is declared twice, once in steps and once in actions.
Fields
Steps and conditions have the same fields. Only what run gives back differs.
| Field | Required | What it does |
|---|---|---|
label |
No | The block's name in the Studio. Without it, the key is shown, such as rest. |
description |
No | A line of help about what the block does. |
args |
No | The values a plan gives on each call. Each one is declared exactly like a setting, with the same types, choices, bounds and defaults, and can ask for game data with ref. |
run |
Yes | The function. |
Other fields are ignored.
What run receives
A plan calls run(args, context):
argsholds every argument you declared: the value set in the plan, or the argument's default when the plan leaves it out. An argument's value is checked with the same rules as a setting: the type, the choices,minandmax. Without adefault, an argument starts at its type's zero value, 0 for a number, or at its first choice when it haschoices, as a setting does. When that breaks its own rules, such asmin: 1, or when the argument asks for one id withref, a plan can't leave it out: the Studio showsArgument <name> needs a value.and base refuses to play the plan until it's set.contextsays where the plan is:
| Field | What it holds |
|---|---|
map |
The map the bot is on, as a number |
stop |
The stop of the plan being run, as the plan describes it |
lap |
How many times the plan has gone round, from 0 |
stopping |
true once the plan or the bot is asked to stop |
A plan only stops between steps. A step that can take long, such as rest above, checks context.stopping as it goes and returns early once it's true.
When your own code calls the function, it passes whatever it wants, and there's no context unless you build one. Write context?.stopping, as above, in a function both a plan and your code call.
What run returns
A step's run may be async, and resolves with one of these:
- Nothing: the step did what it does on this map.
{ nothingToDo: true }: there was nothing to do on this map. AddretryAfter, a number of seconds, to say when there may be something again, such as{ nothingToDo: true, retryAfter: 120 }for a resource that grows back in two minutes.
A condition's run returns true or false, or a promise of one. Anything else, such as 1 or "yes", counts as an error.
A step or a condition that throws, or whose promise rejects, has failed, and the plan decides what happens next. A condition about something the game hasn't told your script yet should throw, with a message saying what's missing, rather than guess an answer.
Ask for game data with ref
ref names the game data table that a setting's or an argument's numbers come from. It goes on an int, or an array of int, of any declaration: settings, the arguments of actions, steps and conditions.
/** @type {BehaviorParameters} */
export const parameters = {
monsters: { type: "array", of: "int", label: "Monsters to fight", ref: "monsters" },
subarea: { type: "int", label: "Subarea", ref: "sub_areas" },
};
- The value is the name of a table of the game data, as the Game data tab shows it:
monsters,items,jobs,maps,sub_areas,areasand so on. - The script still reads plain numbers: keys of that table, the ids
search()returns andrecord()looks up. See Lookups. - Asterobot only checks that
refis a string. It doesn't check that the table exists, nor that the setting is anint, so a misspelled table name isn't reported.
Asteroboard shows a search by name in place of the number box wherever the setting or the argument is edited: the bot's Settings tab, an action's dialog and the Studio. For maps, the number box stays, since maps have no names worth searching by. For maps, sub_areas and areas, Pick on the world map is offered too when a world map is on screen, as in the Studio. A table Asteroboard can't search keeps the plain number box.
When Asterobot checks the declarations
Asterobot checks steps and conditions at the same moments as settings: when a bot's Settings tab shows Package settings, when someone picks the package in the Add a bot dialog, and at every start of the script. Problems fall into the same two groups as for settings and actions.
A mistake in the shape of the steps or conditions export stops the script from starting at all, and the Problems alert shows a text starting with read declarations of:
| Error | Cause |
|---|---|
package "<package>": steps: must be an object, got <type> |
steps is exported, but isn't an object |
package "<package>": steps: "<step>" must be an object |
A step isn't an object |
package "<package>": steps: "<step>" declares no run function |
A step has no run |
package "<package>": steps: "<step>": run must be a function |
A step's run isn't a function |
package "<package>": steps: "<step>": args: must be an object, got <type> |
args isn't an object |
package "<package>": steps: "<step>": args: "<argument>" must be an object |
An argument isn't an object |
Conditions give the same texts with conditions: and the condition's name.
A mistake in an argument doesn't stop the script, but it makes the whole package's declarations unreadable: Package settings shows Couldn't read this package's settings with the reason, and the script starts with no settings at all, even the ones that are fine. The reason starts with package "<package>" step "<step>": argument "<argument>": for a step's argument, or with package "<package>" condition "<condition>": argument "<argument>": for a condition's, followed by the texts Declarations lists for a setting. With default: 24 on the hour argument above, Package settings shows:
my-package@1.0.0: read declarations: package "my-package" condition "after": argument "hour": parameter "hour" default: 24 is above the maximum 23
A ref that isn't a string is refused the same way, with ref must be a string, got <type>, on a setting as well as on an argument.
Typing with JSDoc
Put /** @type {BehaviorSteps} */ above export const steps, and /** @type {BehaviorConditions} */ above export const conditions, as in the example above. The editor then completes the fields, and marks a condition whose run returns something other than a boolean, and a step whose run returns something other than nothing or { nothingToDo }.
These types need no import:
| Type | What it describes |
|---|---|
BehaviorSteps |
The whole steps object |
BehaviorConditions |
The whole conditions object |
BehaviorStep |
One step |
BehaviorCondition |
One condition |
BehaviorBlockContext |
The context that run receives |
BehaviorStepResult |
What a step's run may resolve with |
Next, Ship fight profiles explains how a package ships fight profiles.
Aucun avis à afficher.