Modifying AI Components
Contents
Methods for Modifying the AI Mid Scenario
The AI and its parameters are usually set up at the beginning of a scenario and often left unchanged for the entire scenario. See AiWML and Creating Custom AIs for instructions on how to do this, or the complete list of AI resources at Wesnoth AI.
Sometimes it is, however, desirable to modify parts or all of the AI mid-scenario. Wesnoth provides different tools to accomplish this:
- Standard aspects can be modified mid scenario using the [modify_side] tag
- Composite aspects, goals and all other AI components can be modified mid scenario using the [modify_ai] tag.
- A large number of helper macros are available to facilitate these tasks.
- Micro AIs can be modified mid scenario using the [micro_ai] tag
- The [micro_ai] tag is always used in an event. There is no difference between using it at the beginning of the scenario, that is, in a prestart or start event, or in any other event. We therefore refer to the Micro AIs page for this.
Using [modify_side] to Change Aspects Mid Scenario
The [modify_side] tag can contain an [ai] tag for modifying aspects while a game is in progress. This works, however, only for standard aspects. An example is
[modify_side]
side=2
[ai]
aggression=0.765
[/ai]
[/modify_side]
Composite aspects and all other AI components cannot be modified in this way. Use the [modify_ai] tag described below for these tasks.
Note that modifying an aspect in this fashion does not replace the facet used to define the aspect in the AI configuration. It simply adds another facet that then takes precedence over the already existing ones. In most situations, this does not make a difference, but there might be some cases (for example when facets are only defined for certain times of day or turns), when it might be necessary to replace the existing facet rather than adding a new one. This can also be done using the [modify_ai] tag as described below.
Note that it is, in principle, also possible to define the aspect using this syntax
[modify_side]
side=2
[ai]
[aspect]
id=aggression
[facet]
value=0.765
[/facet]
[/aspect]
[/ai]
[/modify_side]
However, as this only works for standard aspects which can also be set with the simpler syntax shown above, there is really no point in doing so.
The [modify_ai] Tag
The [modify_ai] tag is a tool for modifying stages, candidate actions, aspects and goals of the active AI on the fly at any time during a scenario. Since all this functionality is packed into a single tag, its syntax is a bit more complex than that of other tags. To facilitate this, Wesnoth also provides a large variety of helper macros, but even for using those one still needs to understand the general syntax of the [modify_ai] tag.
Note that the [modify_ai] tag can be used both in [event] tags and in [side][ai], [era][ai] or [modification][ai] tags, meaning it can also be used to change the AI configuration at game setup time.
[modify_ai] Tag Syntax
The [modify_ai] tag takes the following keys:
- side: The side of the AI to be modified if used in an event. This key is not needed if used in [side][ai]. In fact, if given there, it is simply ignored.
- More generally, a StandardSideFilter can be used.
- action: (string) The action to take concerning the component. The following values are allowed:
- 'add': Add a component.
- 'change': Change an existing component.
- 'delete': Delete an existing component.
- 'try_delete': Delete a component if it exists. This does not produce a warning if [modify_ai] fails to change the AI.
- path: (string) Defines the component of the AI to be modified. See below.
- New configuration for the component: This depends on the type of component and is shown in the examples below.
Possible values for [modify_ai]path
Depending on the AI component one wants to modify, the path key can take on different values. The general form of a path is a series of elements separated by dots; each element consists of a component type followed by an ID in square brackets. In many cases, the path consists of only a single level (goals or stages) or two levels (aspect/facet and stage/CA), but (Version 1.13.5 and later only) three or even four levels is occasionally possible with aspects. Some examples:
- Select facet 'quark' of the aggression (composite) aspect:
aspect[aggression].facet[quark]
- (Version 1.13.5 and later only) Select recruit job 'bobby' of the recruitment_instructions aspect defined as a standard aspect:
aspect[recruitment_instructions].recruit[bobby]
- (Version 1.13.5 and later only) Select the default facet of the (composite) village_value aspect:
aspect[village_value].facet[default_facet]
- Select goal 'lighthouse':
goal[lighthouse]
- Select the 'main_loop' stage:
stage[main_loop]
- Select candidate action 'healer_support' of the 'main_loop' stage:
stage[main_loop].candidate_action[healer_support]
Notes on the different ids:
- aspect_id: name of the aspect, such as 'aggression'
- CA_evaluation_loop_stage_id: usually this will be 'main_loop', although it is in principle possible to assign a different id to this stage
- CA_id: The id of the candidate action. See here for the ids of the default CAs.
- facet_id, goal_id and stage_id: these can take on a variety of different formats:
- When adding one of these components, these ids are meaningless. In fact, they do not define the actual ids and can therefore simply be left empty. Instead, the ids are either assigned automatically or can be defined in the body of the component configuration. More on that below.
- By contrast, when deleting or changing a component, these ids need to be given. The following values are possible:
- A string identifying the id used when defining the component
- An integer, starting at 0, defining the number of the component in the order in which they were defined
- *: All components of this type
If all this seemed a bit theoretical, don't worry. Below, we provide many examples for how to do this in practice.
Modifying AI Components Helper Macros
A large number of helper macros are available to simplify the WML needed for modifying AI components. These macros are defined in file 'data/core/macros/ai.cfg', see the list of available macros or the full definitions.
Notes on the macros:
- We do not describe the individual macros here. There are way too many of them and, quite frankly, most of them are not really needed as they barely simplify the syntax compared to the full [modify_ai] tag. Instead, a variety of examples are given in the following sections. If in doubt how to use a macro, or if you want to see all available macros, check out their definitions at the link above.
- There are several macros with SIMPLE_ASPECT in the name. Formally, there is no such things as a simple aspect, only standard and composite aspects. This is just a convention used by the macros for standard aspects which consist of a single scalar value. Thus, all simple aspects (such as aggression) are standard aspects, but not all standard aspects (such as avoid) are simple aspects.
- There are several macros with ALWAYS_ASPECT in the name. These are macros for aspects which apply at all times of day and at all turns. However, the 'always' used as macro argument is actually the facet id, not the values of either the time_of_day or turns keys. This is done so that the facet ids are standardized should removal of these aspects be desired later.
Modifying AI Component Examples
Modifying Standard Aspects
As was shown above, standard aspects, and only standard aspects, can be modified using [modify_side]. A simple example is
[modify_side]
side=1
[ai]
aggression=0.765
[/ai]
[/modify_side]
This is the simplest syntax for modifying standard aspects (without using macros). However, as we explain here, internally all aspects are set up as composite aspects. Thus, [modify_ai] and composite aspect syntax can also be used for simple aspects and we use this example to demonstrate this:
[modify_ai]
side=1
action=add
path=aspect[aggression].facet[]
[facet]
value=0.765
[/facet]
[/modify_ai]
Finally, using one of the helper macros results in this:
{MODIFY_AI_ADD_SIMPLE_ASPECT 1 aggression 0.765}
Removing this setting of the aggression aspect again is done as:
[modify_ai]
side=1
action=delete # or try_delete
path=aspect[aggression].facet[0]
[/modify_ai]
Assuming we know that it is the first non-default facet of the aggression aspect that needs to be removed. If, instead, we want to remove all custom definitions of aggression, we use
[modify_ai]
side=1
action=delete # or try_delete
path=aspect[aggression].facet[*]
[/modify_ai]
Using macros, this is done with
{MODIFY_AI_DELETE_ASPECT 1 aggression 0}
and
{MODIFY_AI_DELETE_ASPECT 1 aggression "*"}
If we don't know the number of the facet and do not want to remove all of them, we need to assign an id to the facet defining the aspect (the simple syntax of the very first example cannot be used in that case) and use that id for selective removal. For example, the definition could look like this
[modify_ai]
side=1
action=add
path=aspect[aggression].facet[]
[facet]
id=my_custom_aggression
value=0.765
[/facet]
[/modify_ai]
and removal like this
[modify_ai]
side=1
action=delete # or try_delete
path=aspect[aggression].facet[my_custom_aggression]
[/modify_ai]
The macro versions of these are
{MODIFY_AI_ADD_ASPECT 1 aggression (
[facet]
id=my_custom_aggression
value=0.765
[/facet]
)}
and
{MODIFY_AI_DELETE_ASPECT 1 aggression my_custom_aggression}
Finally, we stress that a standard aspect does not need to be restricted to a single, scalar value. For example, avoid is a standard aspect and can be defined using both types of syntax, such as
[modify_side]
side=1
[ai]
[avoid]
x,y=20,16
radius=6
[/avoid]
[/ai]
[/modify_side]
or
[modify_ai]
side=1
action=add
path=aspect[avoid].facet[]
[facet]
[value]
x,y=20,16
radius=6
[/value]
[/facet]
[/modify_ai]
Finale note: Any aspect listed at AiWML which is not specifically pointed out to be a composite aspect (and there are very few of those), is a standard aspect.
Modifying Composite Aspects
A small number of the aspects listed at AiWML are available in composite aspect syntax only. However, there is really nothing new about modifying them compared to what is shown above for standard aspects. The only difference (as far as modifying the aspects is concerned) is that we have to use composite aspect syntax, standard aspect syntax does not work. Thus, we only show one example of the attacks aspect here.
[modify_ai]
side=2
action=add
path=aspect[attacks].facet[]
[facet]
invalidate_on_gamestate_change=yes
[filter_own]
type=Elvish Sorceress,Elvish Enchantress,Elvish Sylph
[/filter_own]
[filter_enemy]
race=undead
[/filter_enemy]
[/facet]
[/modify_ai]
A Few More Notes on Modifying Aspects
- The aspect itself and its [default] facet cannot be deleted, only the custom facets. (Version 1.13.5 and later only) However, they can be changed, replacing them with a new definition. (The id attribute must match when changing an aspect.) This could change a standard aspect to a composite aspect or vice versa.
- For standard aspects, it is generally not necessary to delete custom facets. One can simply overwrite the current value with a new one. The only times when one might have to remove a standard aspect is when it is defined for specific times of day or turns, or when it is defined so many times that it might bloat the AI configuration (and therefore the savefile).
- We can also use
action=change
to delete an existing facet and overwrite it with a new definition. If*
is used as facet_id, this deletes all existing facets of this aspect and replaces them with a single facet with the new definition. - Adding a facet with the same id as an existing facet overwrites the previous occurrence, making this equivalent to changing that facet.
- If in doubt about the exact syntax of a given aspect, one can always open the gamestate inspector (by typing
:inspect
in-game in debug mode) and check out the [default] tag of the respective aspect under 'ai config full' for a given side (team). Note though that it might be necessary to play through at least one partial turn of that side, as the AI config of the side is not necessarily fully initialized until that happens.
Modifying Goals
Goals can be added and removed using a syntax that is very similar to that for composite aspects
[modify_ai]
side=1
action=add
path=goal[]
[goal]
id=my_custom_goal
[criteria]
side=3
[/criteria]
value=5
[/goal]
[/modify_ai]
and
[modify_ai]
side=1
action=delete
path=goal[my_custom_goal]
[/modify_ai]
Note that we assigned a custom id to the goal here, that was then used for removal. We could also use the method described above for aspects, using the number of the goal or *
.
The macro versions of these examples are
{MODIFY_AI_ADD_GOAL 1 (
[goal]
id=my_custom_goal
[criteria]
side=3
[/criteria]
value=5
[/goal]
)}
and
{MODIFY_AI_DELETE_GOAL 1 my_custom_goal}
Modifying Candidate Action of the main_loop Stage
The syntax for modifying candidate actions is entirely equivalent to that for aspects and goals shown above. If one wants to, for example, set up an AI that does not fight, this can be done by removing the combat CA. (Version 1.15.3 and later only) There are now additional CAs which also perform attacks. Check out the candidate actions of the default AI to see which CAs need to be remove in order to produce an AI that truly does not attack.
[modify_ai]
side=2
action=delete
path=stage[main_loop].candidate_action[combat]
[/modify_ai]
or
{MODIFY_AI_DELETE_CANDIDATE_ACTION 2 main_loop combat}
Adding a custom Lua candidate action is done as follows
[modify_ai]
side=1
action=add
path=stage[main_loop].candidate_action[]
[candidate_action]
engine=lua
name=return_guardian_bob
id=return_guardian_bob
max_score=100010
location="~add-ons/my_addon/lua/return_guardian.lua"
eval_parms="id = 'Bob', return_x = 10, return_y = 15"
exec_parms="id = 'Bob', return_x = 10, return_y = 15"
[/candidate_action]
[/modify_ai]
We'll omit the macro version here as it is almost identical to previous examples.
Modifying Stages
Working with a single stage, the main_loop stage, should be sufficient for essentially all Wesnoth AI projects these days. It should therefore rarely ever be necessary to add or remove stages any more. It can, however, be done in the same way as for the other AI components. The macro definitions linked to above can be used as syntax reference if you ever need to do so.
See also: Wesnoth AI