Welcome to the world that is AI scripting for Age of Mythology. Scripting for Age of Mythology is much more complicated than the random map scripts made for earlier Ensemble Studios games such as Age of Empires. In fact you are essentially programming in a cut down version of the C++ language. However this should not discourage potential AI Scripters as once you have learnt the basics of the AI library and have a grapple on the format in which you write the code you will find it extremely easy to produce scripts which do exactly as you want them to. This chapter deals with the various things you must do in order to allow you to create and debug scripts within Age of Mythology. I should also point out that my guides are designed for the Titans Expansion pack as by now almost everyone that is infatuated with Age of Mythology would have the expansion pack. For those still running the original Age of Mythology, consult the documentation for AI setup. Stage 1: Preparing Age of Mythology for AI Scripts In order to set Age of Mythology up to enable editing and debugging of AI scripts you must alter the command line on the shortcut to the game. To do this right click the shortcut and select properties. Currently the shortcut’s target line should look something like this: This must be altered in order for the game to load the AI debugging tools at start-up. Append the target line to include the following statement: Thus the final target line should look something like this: For those concerned with performance and others worried about what adding these tools to start-up will do to game play, I can only offer this comment. I have not noticed any difference in performance when running these tools at start-up nor do they seem to affect any part of the game itself. Essentially it just appears that the extra command line parameters simply make the tools visible rather than hidden. While it is possible to build scripts using nothing more than notepad, ideally you want build in them in an editor which supports C++ files as it will format you code and help in finding syntax errors. I use Microsoft Visual Studio.NET 2003 however using a suitable version of Visual C++ such as 4, 5 or 6 should also be ok. You may also want to consider products by other companies such as Borland C++ Builder. The list below offers some free alternatives however I cannot vouch for them: Once you have an editor to build your scripts you are ready to go! I also recommend you install the New X Editor by Reyk for Age of Mythology as it provides menu access from the Age of Mythology editor to most AI related tools within the game. You can download it from Age of Mythology Heaven at the following location: Also you will need the complete AOM AI Constant reference library for later use, download it from here: The C++ Language is not easy and unlike a language such as Visual Basic it is not immediately apparent what the code is actually doing. Fortunately for AOM AI Scripters a large amount of this does not apply as all you are essentially doing is sending commands to an AI library and because most of the time you are just interacting with this library the amount of work a scripter has to do compared to a full C++ programmer is significantly less. This chapter focuses on learning what a variable is, how you declare them and their purpose in AI Scripts. A variable is a combination of a user defined name e.g. “myVariable” and a specified data type such as an Int (a number) or a String (a collection of letters). Variables are used to hold information gathered from external sources or from the result of code operations. The purpose of having variables is so we can refer to the information they hold simply by using the name of the variable. Creating a variable is extremely easy, we choose the data type in this example we will use an Int and give it a name in this case “myIntegerVariable”. Finally we must assign a default value for the variable to hold in most cases where this is unknown we use “–1” for the Int data type or for the String data type we use 2 speech marks. Variables in C++ are written in the following format: Thus our example would look something like this: Most editors will also format the information you have just entered using different colours e.g. the colour light blue is often used for identifying data types, thus our example inside the editor might look like this: Next we add the ‘Extern’ C++ keyword to the beginning of all variables (existing C++ programmers may complain about this but for the purpose of AOM Scripting it will reduce the chances of users encountering compilation errors). The keyword should also be recognised by most editors and its colour changed. You should now have something like this: Finally it is important to remember that at the end of almost every line of code you must add a semicolon ';' so the script knows it can move to the next line and consider it a new statement. This is most certainly the case when we DECLARE (what we have just done) a variable. Thus our completed example should look like this: Below are some more examples, in almost every case with AOM scripting you will just be using the “Int”, “String” and “Float” data type. The first variable is called “firstVariable” and is of type Int with a value of -1 The third variable is called “thirdVariable” and is of type String with a value of “test string” The fifth variable is called “fifthVariable” and is of type Float with a value of 0.45 As you now have probably guessed the Float data type is used to hold decimal figures and aside from supporting a decimal, is no different to the Int data type. The final AOM data type worthy of mention is a unique data type for AOM which will not be recognised by most C++ editors. It is the Vector data type which contains three values which make up an X, Y and Z co-ordinate to specify the exact location of an object such as Hoplite Soldier on an AOM Map. It is written like so: However in most cases you won’t actually work out the exact vector, you will use a library function to work it out for you. For the moment you simply need to know that it exists, when it comes to using them in the script code I will cover them in more detail. A method is a subroutine which is performed from start to finish on a line by line basis. A compiler (in our case AOM) will run each line of code until it has reached the end of the method at which point the subroutine is terminated. A method contains certain information in its constructor (or heading). Fortunately for AOM you will most likely only write methods which use one formula. We will begin by creating a method which MUST be present in any AOM script and must be written before any other methods. This is called the MAIN method and is written like so: { … your code is written here … } Lets now analyse what we have written – the first word ( The next word is the user defined name of the method just like in the last chapter when we decided on “myVariable”. As I stated earlier every AOM script requires a MAIN method which is the first method AOM will run when a scenario starts (however when creating your own methods you can use any name e.g. “myMethod”). The final word is also called You will also notice an opening and closing curly bracket ( {} ), this is used to signify the start and end of the code for this method e.g. everything you write between these brackets will be performed when you run the method. That is pretty much it for explaining methods, below are some more examples using different types so that you can get a grasp of other method types. The first example is a method called “myMethod” which takes no parameters ( The second example is a method called “myMethod” which takes a single The third example is a method called “myMethod” which takes an The fourth example is a method called “myMethod” which takes an Request for a sample method (very basic) The method uses a existing library function (aiEcho) to send a message (hello) to the AI Debugger. If you do not understand this don't worry it will be covered later, this is just here because of a request. Condition statements allow you to perform checks on an equation and determine what cause of action to take based upon it, e.g. if there are 3 Cyclopes send them into battle, if there are only 2 do nothing. There are 2 types of condition statements in the XS language, the IF-ELSE approach or the SWITCH approach. This chapter will focus upon both. You use the if-else approach when wanting to perform a calculation, you use the switch approach when checking a value and performing an action based on that. The if else approach allows you to specify a check and perform a result based upon it ELSE do something different. It is written like so: } } Note than when checking if something is equal to something else we use 2 equals “=” signs. This is because one equal sign means that we are setting something to equal this value e.g. myVariable = mySVariable - this means that myVariable is now 2 The XS library supports a function called aiEcho. This sends a message (that you specify) to the debug window inside AOM (you will use the debug window to view errors and actions the script is performing). For the purposes of this chapter we will use this function to help explain what is happening. I have modified the condition to support this function and included an Int variable to assign a number to; this should help you understand how the statement works. Essentially all you have to remember is the syntax. The statement is also not limited to just one else statement you can have as many as you like, but if you add more you have to supply further check statements and ideally end with a single Else statement like so: You also do not have to have an else segment e.g. You can also include “nested” arguments (if statements inside if statements) e.g. The SWITCH approach checks a value and performs a set of instructions based upon this value, it is written like so: So the first line sets the value we are checking, each case statement is basically a bunch of if statements without an else option. From a logical standpoint it is easier to use a switch statement with a lot of options rather than many complicated if statements. The final item to cover is comments (they are completely unrelated to the above material). Comments are written by you to identify what is going on e.g. we have an int variable and want add three to it. To add a comment just type “//” without the speech marks and most editors should convert the text to green then you can type anything you want in any language (just remember that for each new line you must have a “//” at the beginning e.g. To create a long paragraph or header for your script you can start with a “/*” option. Until you create “*/” at the end, everything in between will be considered a comment so there is no need to keep adding a “//” for every line e.g. Thanks to some readers for pointing out some bugs in the code! You now have a basic enough knowledge to build AI Scripts. The remainder of this series will talk about the various library functions you can use and will provide introductions to the various plans you can use such as attack, defend and scouting. This chapter (and future chapters) will be accompanied with support files (a scenario and a script). These have and will be uploaded to the AI files section of the AOM Heaven download library. They can be downloaded here: The file you are looking for is tutorial 1. If you open the file "Training1.xs" you will see the code for our sample script. We will hence start inside the void main (void) method just below the "{" line. The first three lines have aiEcho statements. These send the message enclosed in the double quotes to the debug window. (this can be accessed inside the AOM editor by pressing the ALT+Left Shift+D keys together). Thus: The next three lines are system calls, if I am totally honest I am not sure what the last 2 do, however scripts seem to work a lot better with them in then out so put them in every script and forget about it. First we have to create an int variable for each plan which houses the unique number AOM uses to reference the plan. Next we declare that we are going to create a plan, which is called "Explore Land" and is of type "cPlanExplore". This is then assigned to our variable like so: Next we set the parameters of the plan (NOTE that every time we want to set a parameter of a specific plan we have to reference it by using the int variable, in this case "planID"). The next line as you might guess adds a free unit to the plan which will be used for scouting. Firstly we reference the plan (this parameter applies to "planID"), secondly we specific the type of unit to add "cUnitTypePriest", thirdly we specify our must, need and want values for the number of priests we want in the plan. For this simple plan we set these all to 1. NOTE all units have a prefix of "cUnitType", plans have "cPlan" and all technologies have a "cTech" prefix because they are constant values. There are also many other items in the reference file which contains these constant related prefixes. The next parameter is very common, we are specifying a parameter of type boolean (true or false). To determine what type to use e.g. aiPlanSetVariableBool, aiPlanSetVariableInt etc. check the reference file mentioned at the beginning of this series for the parameter's type e.g. the line in our code: is of type aiPlanSetVariableBool because cExplorePlanDoLoops is listed in the reference file as a function which returns a boolean result. (MORE ON THIS LATER) Back to the code, we are setting the plan (planID) to use a boolean value for the result of cExplorePlanDoLoops. We set it to false meaning that we do not want the priest to do loops, just random searches. (IGNORE THE ZERO IN THE LINE OF CODE) Finally we set the plan to be active (e.g. we have completed setting the parameters and we are ready for him to scout. Lastly we send a debug message (aiEcho) saying we have finished. Simple, yes? The next chapter will expand on this script by adding more functionality. In this chapter we will expand upon the scouting plan by adding a separate plan which sends all herdable animals that we find while scouting back to our town centre. To achieve this function we add the following code to our script just after the last line of the scouting plan and just before our final debug message saying the end of the script has been reached. // Send all herdable animals found on the scout back to the base The first line is of course comment. The second line is our plan creator function, we choose a herdable plan "cPlanHerd" and call it "Gather Herdable Plan" The third line tells the plan to gather all units of type "cUnitTypeHerdable" The fourth line tells the plan to send all animals to "cUnitTypeSettlementLevel1" (early in development it was not necessarily going to be called a town centre but this is what the AI considers a town centre as opposed to "cUnitTypeSettlement" which is just an uncolonised settlement. The final line sets the plan to be active. Now if you update your script to include this and place some herdable animals on the map e.g. goats, the priest will seek them out and send them back to your town centre! The next chapter will focus on creating a basic economy. This will add some basic economy functions to your script which we will work on in the next chapter so that they become a true economy. NOTE: You must add a cinematic block to the map and alter the script to the new block's value (mentioned in the code). You also need to add some farms, villagers, gold and forests. THE CODE Add just below the gather herdable plan. // ----- CREATE A BASIC ECONOMY ----- // First we set that all resources go into a single account // Next we set all gathered resources and farms to be part of // Set the importance of each resource (priorities) // Set percentages for each resource to be gathered by villagers // Finally we assign the types of each resource we collect [This message has been edited by NeoSoft_Studios (edited 05-21-2004 @ 08:50 AM).]
Stage 2: Choosing an Environment for Scripting
The second variable is called “secondVariable” and is of type Int with a value of 11
The fourth variable is called “fourthVariable” and is of type String which is empty or null
The sixth variable is called “sixth Variable” and is of type Float with a value of 0.1
{
aiEcho("Hello!");
}
{
{
Also remember that you do not add semicolons at the end of “if else” statements because they are almost sub methods.
{
aiEcho(“The value is equal to 6!”);
}
{
aiEcho(“The value is not equal to 6”);
}
{
aiEcho(“The value is equal to 6!”);
}
{
aiEcho(“The value is equal to 5!”);
}
{
aiEcho(“The value is equal to 4!”);
}
{
aiEcho(“The value is not 4, 5 or 6!”);
}
{
aiEcho(“Yes”);
}
{
{
aiEcho(“Yes”);
}
}
{
{
aiEcho(“The value is 3”);
}
{
aiEcho(“The value is 4”);
}
}
// This is another comment
myVariable = myVariable + 3;
continuing on next line
now finishing */
myVariable = 3;
CHAPTER 5 WILL FOCUS ON SCRIPTS!!!
int herdPlanID = aiPlanCreate("Gather Herdable Plan", cPlanHerd);
aiPlanAddUnitType(herdPlanID, cUnitTypeHerdable, 0, 100, 100);
aiPlanSetVariableInt(herdPlanID, cHerdPlanBuildingTypeID, 0, cUnitTypeSettlementLevel1);
aiPlanSetActive(herdPlanID);
--------
// For our early economy we will just create a huge base
// to encompass everything, as we proceed in later chapters
// this will be refined to be much more controlable.
kbBaseDestroyAll(1);
int myMainBase = kbBaseCreate(1, "Main Base", kbGetBlockPosition("685"), 75.0);
// NEW FUNCTION - kbGetBlockPosition, this returns a vector
// which represents an exact location of a Cinematic Block
// object which we place throughout the map for the AI to use
// as markers. You must create one for the script to use, in the
// AOM Editor use place object (cinematic block). Find the object
// info menu option and note the name value e.g. 1234. With this
// you can write the function kbGetBlockPosition("1234"). Note that
// this function is probably the most widely used XS function so
// remember it !!!!! Also remember if you do not add the block to your
// map and modify the script you will get some strange results.
kbEscrowSetPercentage(cEconomyEscrowID, cAllResources, 0.0);
kbEscrowSetPercentage(cMilitaryEscrowID, cAllResources, 0.0);
kbEscrowAllocateCurrentResources();
// root account. We also set the economy to use this script only
// and not to base actions on cost. (You can ignore this and just)
// always use the default values.
aiSetAutoGatherEscrowID(cRootEscrowID);
aiSetAutoFarmEscrowID(cRootEscrowID);
aiSetResourceGathererPercentageWeight(cRGPScript, 1);
aiSetResourceGathererPercentageWeight(cRGPCost, 0);
// You can tailor these to your desire
kbSetAICostWeight(cResourceFood, 1.00);
kbSetAICostWeight(cResourceWood, 0.75);
kbSetAICostWeight(cResourceGold, 0.75);
kbSetAICostWeight(cResourceFavor, 2.00);
// To get this you determine how many villagers you want to chop
// wood (6) and then how many villagers you have in total (31).
// 6 / 31 = 0.19 then you assign that value to the appropriate line
// e.g. aiSetResourceGathererPercentage(cResourceWood, 0.19, false, cRGPScript);
aiSetResourceGathererPercentage(cResourceFood, 0.4, false, cRGPScript);
aiSetResourceGathererPercentage(cResourceWood, 0.2, false, cRGPScript);
aiSetResourceGathererPercentage(cResourceGold, 0.2, false, cRGPScript);
aiSetResourceGathererPercentage(cResourceGold, 0.2, false, cRGPScript);
aiNormalizeResourceGathererPercentages(cRGPScript);
// Food - from farms, fish and herds
// Wood - from forests
// Gold - from mines
// Favor- from temples, town centres, monuments, fighting
// Note you must select a sub type for food, type Easy will not
// do anything, farming is usually the easiest.
aiSetResourceBreakdown(cResourceFood, cAIResourceSubTypeFarm, 1, 50, 1.0, myMainBase);
aiSetResourceBreakdown(cResourceWood, cAIResourceSubTypeEasy, 1, 50, 1.0, myMainBase);
aiSetResourceBreakdown(cResourceGold, cAIResourceSubTypeEasy, 1, 50, 1.0, myMainBase);
aiSetResourceBreakdown(cResourceFavor, cAIResourceSubTypeEasy, 1, 50, 1.0, myMainBase);