AI Help File Document
Age of Mythology™
© 2002, Microsoft Corporation. All rights reserved.
Important: Age of Mythology allows you to create your own AI scripts. You may share these custom AI scripts for the purposes of gameplay, but you may not sell or make other commercial uses of the custom AI scripts. Microsoft® reserves all other rights to the editors and files.
The AoM AI scripting system is a very powerful system that allows the user to craft the AI using the same language as the folks at Ensemble Studios. While this is a useful enduser feature, it is officially unsupported.
The high level overview for creating your own random map AI scripts is as follows:
· Write your random map script.
· Add an XML file into your AI directory following the format of default.xml, defaultboom.xml, or defaultrush.xml.
· See your AI appear in the list of dropdown choices when you run the game.
· Debug it using the ingame debugging facilities:
o Add “aiDebug” to your user.cfg file in the startup directory.
o When the game is running, hit ALT+SHIFT+D to bring up the debugger.
For a scenario, the steps are largely the same, except that you need to associate the AI script filename with the right player via the scenario editor (Player Data dialog).
A list of the type enumerations and function calls that can be made from the AI scripts is included at the end of this document.
AoM includes a source debugger for your XS scripts. This is accessed by including a line with “aiDebug” on it in the user.cfg in the Startup directory of your AoM installation. If you do not have a user.cfg file, create one with any text editor. Once this is in your user.cfg, hitting ALT+SHIFT+D will bring up the debugger. Different XS runtimes (AI for each player, triggers, etc.) will be available for debugging. The debugger is an unsupported feature.
You can also use the “aiEcho(…)” syscall (see the syscall reference at the end of this document) to print out debugging strings, etc. during the execution of your AI. To see these, you must put a “showAIEchoes” line in your user.cfg.
The following script code is a sample script from one of the AoM single-player scenarios. As such, it does not demonstrate everything that is possible with the AI scripting system, but it does provide a working example. More examples (including all of the script code for the Random Map AI for AoM) can be found in the “AI” subdirectory of your AoM installation.
I. Sample Script File
//==============================================================================
// Scn08p2: AI Scenario Script for scenario 8 player 2
/*
Overview:
The human player rescues Ajax from the Trojan army, then joins forces at the
allied TC. His goal is to build up and take out the red town to the north, without
the availability of siege units.
The CP sends one trivial attack early, which dies hopelessly to the HP's huge
army left over from the start. Several of the following attack rules are currently
implemented by triggers which attempt to build military buildings near
the HP's town.
The CP attacks down any one of three channels, with the west channel reserved for
higher difficulty levels.
No attempt is made to train units out of the forward buildings, even if the
feeble attempt to build succeeds. If the forward building concept stays in, we should
add rules to make a local army out of each building and use them for extra attacks.
CP starts as age 2 with Animal Magnet (not used) and Pestilence.
Age 3: Aphrodite: Curse
Age 4: Hephaestus: Plenty
*/
include "scn lib.xs";
// Globals
int lastAttackPlanID = -1; // Updated with plan ID as each attack is launched
int defendPlan = -1;
int reservePlan = -1; // Extra defend plan, higher priority, for final resistance
int startTime = -1; // Updated with start time when cinematic is done.
// In seconds.
int age3Time = 1500; // Time (in seconds) to go to age 3
int age4Time = 10000;
int siegeMaintainPlan = -1;
int mythMaintainPlan = -1;
int hopliteMaintainPlan = -1;
int toxotesMaintainPlan = -1;
int hippikonMaintainPlan = -1;
int villagerMaintainPlan = -1;
int routeWestPass = -1;
int routeNorthPass = -1;
int routeEastPass = -1;
// Initial settings are for the easiest difficulty level, others are set in main().
int nextAttackTime = 480000; // 8 minutes
float attackSize = 4.0;
float maxAttackSize = 9.0;
float attackSizeMultiplier = 1.2;
int attackInterval = 300000; // 5 minutes
float reserveSize = 15.0; // Army to keep in reserve
int attackCount = 0;
int trainDelay = 30;
int cavTrainDelay = 30;
int archerTrainDelay = 15;
int infantryTrainDelay = 20;
int builderQty = 1; // For forward building
int defendQty = 0; // For escort group
// Cinematic block markers
const string cbVillagerGather = "1872";
const string cbSiegeGather = "1873";
const string cbArcherGather = "1878";
const string cbCavalryGather = "1874";
const string cbInfantryGather = "1875";
const string cbP2TC = "1876";
const string cbGateEast = "1826";
const string cbGateWest = "1827";
const string cbWestPass1 = "1833";
const string cbWestPass2 = "1832";
const string cbNorthWestPlateau = "1834";
const string cbNorthEastPlateau = "1835";
const string cbP1Gold = "1831";
const string cbP1Granary = "1830";
const string cbSouthField = "1877";
const string cbEastPass1 = "1828";
const string cbEastPass2 = "1829";
const string cbP1TC = "1836";
const string cbNorthPass = "2272";
const string cbAttack2Stable = "1882";
const string cbAttack2Army = "1883";
const string cbAttack5Tower = "1880";
const string cbAttack3Army = "1881";
// Econ
int maxVills = 20; // Will scale with difficulty
int maxFishBoats = 0; // Including one to scout
float goldPercent = 0.25;
float woodPercent = 0.15;
float foodPercent = 0.60;
int gathererTypeID = -1;
int fishGatherer = -1;
int mainBase = -1;
float mainRadius = 100.0;
// *****************************************************************************
//
// Building plan support for forward building
int buildPlan = -1;
int buildDefend = -1;
// Set the following four vars, then just enable testBuildPlan.
int buildUnit = cUnitTypeTower;
vector buildVec = cInvalidVector;
vector startPoint = cInvalidVector;
rule buildPlanRule
inactive
{
buildPlan = aiPlanCreate("Build Plan", cPlanBuild);
if(buildPlan >= 0)
aiPlanSetVariableInt(buildPlan, cBuildPlanBuildingTypeID, 0, buildUnit);
aiPlanSetDesiredPriority(buildPlan, 40);
aiPlanSetVariableVector(buildPlan, cBuildPlanCenterPosition, 0, buildVec);
aiPlanSetInitialPosition(buildPlan, startPoint);
aiPlanSetVariableFloat(buildPlan, cBuildPlanCenterPositionDistance, 0, 12);
aiPlanAddUnitType(buildPlan,
kbTechTreeGetUnitIDTypeByFunctionIndex(cUnitFunctionBuilder, 0),
1, builderQty, builderQty);
aiPlanSetEscrowID(buildPlan, cRootEscrowID);
aiPlanSetInitialPosition(buildPlan, buildVec);
aiPlanSetActive(buildPlan);
}
buildDefend =aiPlanCreate("Build Defend Plan", cPlanDefend);
if (buildDefend >= 0)
aiPlanAddUnitType(buildDefend, cUnitTypeMilitary, 0, defendQty, defendQty);
aiPlanSetDesiredPriority(buildDefend, 60); // Above other defend plans
aiPlanSetVariableVector(buildDefend, cDefendPlanDefendPoint, 0, buildVec);
aiPlanSetVariableFloat(buildDefend, cDefendPlanEngageRange, 0, 15);
aiPlanSetVariableBool(buildDefend, cDefendPlanPatrol, 0, false);
aiPlanSetVariableFloat(buildDefend, cDefendPlanGatherDistance, 0, 8.0);
aiPlanSetInitialPosition(buildDefend, kbGetBlockPosition(cbInfantryGather));
aiPlanSetUnitStance(buildDefend, cUnitStanceDefensive);
aiPlanSetVariableInt(buildDefend, cDefendPlanRefreshFrequency, 0, 5);
aiPlanSetNumberVariableValues(buildDefend, cDefendPlanAttackTypeID, 2, true);
aiPlanSetVariableInt(buildDefend, cDefendPlanAttackTypeID, 0, cUnitTypeUnit);
aiPlanSetVariableInt(buildDefend, cDefendPlanAttackTypeID, 1, cUnitTypeBuilding);
aiPlanSetActive(buildDefend);
aiEcho("Creating defend plan");
xsEnableRule("endBuildDefend");
xsDisableSelf();
rule endBuildDefend
minInterval 7
aiEcho("Build Plan state is "+aiPlanGetState(buildPlan));
if (aiPlanGetState(buildPlan) == -1)
aiPlanDestroy(buildDefend);
aiEcho("Destroying build defend plan");
// FUNCTIONS
void age2EventHandler(int bogus=-1)
xsEnableRule("usePestilence");
xsEnableRule("goToAge3");
xsEnableRule("getAge2UnitUpgrades");
xsEnableRule("getAge2ArmoryUpgrades");
void age3EventHandler(int bogus=-1)
xsEnableRule("useCurse");
siegeMaintainPlan = maintainUnit(cUnitTypePetrobolos, reserveSize/8,
kbGetBlockPosition(cbSiegeGather), 60);
xsEnableRule("getAge3UnitUpgrades");
xsEnableRule("getAge3ArmoryUpgrades");
void age4EventHandler(int bogus=-1)
xsEnableRule("usePlenty");
xsEnableRule("getAge4UnitUpgrades");
xsEnableRule("getAge4ArmoryUpgrades");
// Called by a trigger, to let AI know that the game has started
void wakeup(int parm=-1)
startTime = xsGetTime()/1000;
aiEcho("Wakeup running at "+startTime);
if (kbGetAge() >= cAge2)
age2EventHandler(0);
if (kbGetAge() >= cAge3)
age3EventHandler(0);
if (kbGetAge() >= cAge4)
age4EventHandler(0);
xsEnableRule("attackGenerator");
xsEnableRule("scout");
hippikonMaintainPlan = maintainUnit(cUnitTypeHippikon, reserveSize/3,
kbGetBlockPosition(cbCavalryGather),
cavTrainDelay);
toxotesMaintainPlan = maintainUnit(cUnitTypeToxotes, reserveSize/3,
kbGetBlockPosition(cbArcherGather),
archerTrainDelay);
hopliteMaintainPlan = maintainUnit(cUnitTypeHoplite, reserveSize/3,
kbGetBlockPosition(cbInfantryGather),
infantryTrainDelay);
// Init low-priority defend plan to manage spare units
defendPlan =aiPlanCreate("Defend Plan", cPlanDefend);
if (defendPlan >= 0)
// All unassigned mil units
aiPlanAddUnitType(defendPlan, cUnitTypeMilitary, 0, 200, 200);
...
deichman5