Skip to main content

Battle AI

MCE uses a pluggable strategy pattern for battle AI. Every AI is a BattleAI ScriptableObject, and the built-in ones are designed to be chained: each strategy tries to do its one thing and hands the decision to its Fallback AI when it cannot.

The battle state machine

The AI never drives the battle — it answers one question, "what would this trainer do now", and the battle loop decides when to ask. That separation is what lets any battle use a different strategy without touching the turn flow.

Architecture​

BattleAI (namespace OpenMon.MCE.Battle.AI) has two things to answer:

  1. RequestPerformAction -- the battler's turn. The AI calls back with a BattleAction, a small struct describing what to do: the battler (BattlerType and Index), the action Type (Move, Switch, Item or Run), whether to trigger the Mega form (TriggerMegaForm) and the numeric Parameters (which move and which target, which item, which monster).
  2. RequestNewMonster -- which monster of the roster to send in after one fainted.

It also has two optional hooks, OnBattlerFainted and OnBattleEnded (the ML AI uses them for rewards).

A chain is just assets pointing at assets:

MegaOrGigaIfCan  ──Fallback──▶  SwitchToEffectiveMonster  ──Fallback──▶  PerformEffectiveMove  ──Fallback──▶  PerformRandomMove  ──Fallback──▶  RandomBattleAI

Each link looks at the situation; if its condition is met it answers, otherwise it asks its fallback. RandomBattleAI is the end of every chain: it has no fallback.

Built-In AI Strategies​

All of them are created from Assets > Create > MCE > Battle > AI > ....

AssetWhat it doesSettings
RandomBattleAIChooses a random action between a random move and switching to a random monster. Meant as the ultimate fallback.--
PerformRandomMovePerforms a random move, or falls back if none is available.Fallback
PerformEffectiveMovePerforms the most powerful effective damaging move it has: anything above the threshold counts as effective; among effective moves it prefers multi-target ones, then more power. Falls back when there is no effective move or the chance roll fails.EffectivenessThreshold (default 1), Chance (0-1), Fallback
SwitchToEffectiveMonsterSwitches to a party member that has an effective move, with some randomness. It also chooses which monster to send in after a faint.EffectivenessThreshold, SwitchingChance (0-1), Fallback
UseBattleItemsUses battle items under certain conditions (healing items below an HP threshold, listed items); falls back otherwise.UseIfAvailable, UseOnOwnIfAvailable, HPThreshold, HPHealItems, Fallback
MegaOrGigaIfCanAsks its fallback for the action, then flags it to change to the Mega or giga form if the battler can.Fallback
IfWildWithRunChanceRunIf the monster is wild and its species has a chance to run, runs when the roll succeeds; otherwise passes on.Fallback
MLBattleAILets a trained ML-Agents brain decide (see below).AgentPrefab, Fallback

Choosing the AI of a Battle​

Each roster of a battle is controlled by one BattleAI:

  • For ordinary encounters -- wild fights and trainer fights launched by the CommandGraph nodes SingleWildFight, DoubleWildFight and SingleTrainerFight -- the BattleLauncher asset uses its Default AI field. Point it at the head of the chain you want.
  • To give a battle its own strategy, use a BattleTemplate (Create > MCE > Battle > Template): its AIs array holds one BattleAI per roster, next to the rosters, characters, bags and the rest of the battle's parameters. The FightFromTemplate node launches it.
  • From code, BattleLauncher.LaunchBattle(...) takes the array of AIs directly.

Example chains (the AI you assign to the BattleLauncher or the template is the first one listed):

Easy (Route 1)​

PerformRandomMove → RandomBattleAI

Medium (Gym puzzle)​

MegaOrGigaIfCan → PerformEffectiveMove → PerformRandomMove → RandomBattleAI

Hard (Champion)​

MegaOrGigaIfCan → UseBattleItems (HP below 25%) → SwitchToEffectiveMonster → PerformEffectiveMove → PerformRandomMove → RandomBattleAI

Writing Your Own AI (Source and Studio Editions)​

Subclass BattleAI, implement the two abstract methods and give your class a [CreateAssetMenu]:

using System;
using System.Collections;
using System.Collections.Generic;
using OpenMon.MCE.Battle;
using OpenMon.MCE.Battle.AI;
using OpenMon.MCE.Configuration;
using UnityEngine;

[CreateAssetMenu(menuName = "MyGame/AI/Always Struggle", fileName = "AlwaysStruggle")]
public class AlwaysStruggle : BattleAI
{
[SerializeField] private BattleAI Fallback;

public override IEnumerator RequestPerformAction(MCESettings settings, BattleManager battleManager,
BattlerType type, int inBattleIndex,
Action<BattleAction> callback)
{
// Ask the next link of the chain, then adjust what it answered.
BattleAction action = default;
yield return Fallback.RequestPerformAction(settings, battleManager, type, inBattleIndex,
answer => action = answer);
callback(action);
}

public override int RequestNewMonster(MCESettings settings, BattleManager battleManager,
BattlerType type, int inBattleIndex, List<Battler> forbiddenBattlers) =>
Fallback.RequestNewMonster(settings, battleManager, type, inBattleIndex, forbiddenBattlers);
}

The protected helper GenerateActionForMove(...) builds the BattleAction (move slot and valid target) for a chosen Move, and the static GetEnemies(battleManager, type) returns the opposing battlers that are fighting. See the BattleManager API for the module that owns the AIs.

ML-Agents Integration​

MCE includes an experimental ML-Agents-based AI.

MLBattleAI​

Uses Unity ML-Agents to make decisions with a trained neural network.

  • Difficulty: Variable (depends on training)
  • Use Case: End-game trainers, competitive AI, research
  • Setup: Requires the com.unity.ml-agents package and the MCE_ML_AGENTS scripting define; the ML code lives in its own assembly (OpenMon.MCE.ML)

For every battler the AI spawns an MCEAgent from its Agent Prefab, writes a vector of observations (the code documents 630 integers: the scenario, the statuses of both sides, the four active battlers and the rest of the roster, each battler described by species, form, level, IVs/EVs, ability, HP, status, moves and PP, held item, stat stages and volatile statuses) and asks the brain for discrete decisions that it translates into a BattleAction. Rewards are given when a monster faints (a share of -10 when your own faints, a share of +100 when an opponent faints). Choosing which monster to send in after a faint is not learned: it goes to the Fallback.

Training the AI​

Training runs OUTSIDE Unity, with the standard ML-Agents toolchain. MCE gives you the agent, the observations and the rewards; the trained model is yours to produce.

  1. Install the Python side in a virtual environment: pip install mlagents
  2. Write a configuration for the behaviour your agent prefab declares, following the ML-Agents configuration reference
  3. Run mlagents-learn <your-config>.yaml --run-id=<a name>, then press Play in Unity when it says it is waiting
  4. Watch it with tensorboard --logdir results
  5. Export the trained model (.onnx file)
  6. Assign the model to your agent prefab

The engine's own repository keeps wrappers for these steps (MLAgentsInstall.ps1, RunMLTraining.ps1, ViewMLResults.ps1 and MLAgentsConfig/) in its root folder. They are maintainer tooling and are NOT part of any package, so they are not in what you received: the steps above are the ones to follow.

ML-Agents Dependency

ML-Agents is a separate package with its own Python dependencies. The core MCE battle system works without it. Only install ML-Agents if you plan to use neural network-based AI.

AI Difficulty Guidelines​

DifficultyAI ChainWhere to Use
Very EasyRandomBattleAI onlyWild encounters
EasyPerformRandomMoveEarly routes, youngsters
MediumPerformEffectiveMove (type aware)Gym trainers, rivals
HardMega + items + switching + effective movesGym leaders, Elite Four
NeuralMLBattleAI with a trained modelCompetitive, challenge modes

Best Practices​

  1. Match AI to narrative context. A random bug catcher should not play like the Champion.
  2. Test AI against player teams of similar level. Unbalanced AI feels unfair.
  3. Build chains from the small strategies. They are easier to maintain than one monolithic AI.
  4. Always end a chain with RandomBattleAI (or PerformRandomMove backed by it) so the AI can never get stuck without an answer.
  5. Do not over-optimize AI for early trainers. Players need to learn the system before facing smart opponents.