|
AOE3
RMS Tutorial:
by M0nTy_PyTh0n
Version 0.93
PROLOGUE
FAQs
I. INTRODUCTION:
- How to Script
- How to Create Map Files
- Variables
- Functions
- Conventions
- Script Order
- Map Grid
- Object Names:
- - Unit Names
- - Technology Names
- - Trigger Names
- - Terrain Names
- - Other Object Names
- XML Files
- A Simple Random Map
- How to Test the Map
- Now Let Us Have Some Fun!
- How to Get Help
II. RANDOM MAP SCRIPTING COMMANDS:
- General Purpose
- Players
- Areas
- Connections
- Objects
- Fair Objects
- Constraints
- Trade Routes
- Triggers
ADVANCED RM SCRIPTING TUTORIALS
DISCLAIMER
COMMENTS
This site is friendly supported by the
games news and hosting service
|
|
PROLOGUE
Welcome to Hyena Studios' Random Map Scripting (RMS) Tutorial for the Age of Empires III (AOE3) game. Below you will find a step-by-step tutorial to create your custom random maps for playing in skirmish mode - offline and online. This tutorial is suitable for both beginners and advanced random map scripters. If you are new to random map scripting you should read all FAQ's first, even if you know RM scripting from the Age of Kings (it is completely different) or Age of Mythology game. Chapter I is the main tutorial chapter. Chapter II will explain all predefined commands you will find in an AOE3 RM script. If you want to have quick success, check Chapter I (Now Let Us Have Some Fun!).
This tutorial is still a bit under construction - more RM commands will be added soon. However, it already allows you to learn creating both simple and complex maps.
Now enjoy!
Regards, M0nTy_PyTh0n.
FREQUENTLY ASKED QUESTIONS
Q: Why can I not play my custom scenarios in skirmish mode?
A: Only random maps can be played in skirmish mode.
Q: How do I convert my scenario into a random map/skirmish map?
A: You can't. A scenario is a map with a fixed size, player number, terrain, buildings, etc.
A random map/skirmish map will be generated new each time it gets started,
and with the random commands you will learn to create in this tutorial.
Q: Can I play my custom scenarios online?
A: Not yet. You will have to wait for a game patch with a version higher than 1.11 .
Q: Can I play my custom random maps online?
A: Yes.
Q: I joined a custom random map online. Can I host this map now?
A: Yes. When you join a game with a custom map online, the map will be automatically transfered onto your computer. In the game hosting menu you press the "Custom Maps" button on the upper right screen and the custom maps will appear for selection.
Q: I played a custom random map online. Where do I find the map files on my computer?
A: You will find the custom random maps in your windows profile directory. This is usually
"C:\...\My Documents\My Games\Age of Empires 3\RM\".
Each map consists of 2 files (*.xs and *.xml) - more informations in this tutorial below.
Q: I am new to RMS. How long does it take to make my first own RMS?
A: Long! You will need several hours to be able to correctly modify an already existing map, and a few weeks to create you very own random map from scratch. But, modifying maps makes MUCH fun and will enormously help you learning the commands.
Q: Why is there no RMS Editor for AOE3?
A: First of all, Ensemble Studios - the developers of AOE3 - do not support custom maps at all. They never did it in the past with AOE, AOK, and they will not do it in future. The main reason is: It is just too complicated for the regular players. Furthermore, it makes no sense in creating a drag and drop menu for RM scripting, since RM's are dynamic/random. So, be happy to have this tutorial here - made by a fan of this game. And, I hope it is also a bit of entertainment for you ;) |
|
|
|
|
|
|
|
|
|
I. INTRODUCTION |
|
|
|
|
|
|
|
|
|
1) HOW TO SCRIPT
Random Map Script
files (RMS files) will be created and edited only with
Windows Notepad or any other text editor tools. The AOE3 map editor
is not needed for creating a RMS, but it will help a lot for testing
the RMS.
(you create a random map with a text editor only)
The RMS are more like an actual programming language than
were the RM scripts from Age of Kings: The Conquerors. This gives
the AOE3 RM scripts a great deal of power, but also a potentially
steeper learning curve. However, users with any familiarity with
programming should have an easier time because many of the conventions
are similar. Furthermore, the script code is most similar to the
C++ programming language, but don't worry - this tutorial will make
you learing most commands to make your first RMS without any C++
programming knowledge.
In this tutorial examples for script text are shown in Courier Font.
Notes:
1.
Learing RM scripting is NOT easy. You should be at least
12 years old.
2.
If you are good in maths you will highly profit from
it! But I can tell, learing it can make even more fun than playing
AOE3!
3.
While reading this tutorial you will even learn some basic for "C/C++" programming!
4. If you do not know how to copy and paste text, then you have visited the wrong website!
5. If you do not know how to use "Notepad" or any other text editor, then you have visited the wrong website! |
|
|
|
|
|
|
|
|
|
2) HOW TO CREATE MAP FILES
For making a map we need to create two files: If you name your map "MyMap" then
we need to create files with this name and the extensions ".xs"
and ".xml", so we name them: MyMap.xs and MyMap.xml . To create each file, open Windows
Notepad. You will find the Notepad program under Windows in Start
-> Programs -> Accessoirs -> Notepad (or Editor, it's the
same). Open Notepad and select "New" from the "File"
menu bar. Well, let's save these two neccessary files without any
contents first:
1. Select "Save as..." from the "File"
menu bar.
2. At the top of the new opened box we chose our
directory, where we want to save our files. For the "Save in"
directory we chose the directory, where our AOE3 game was installed.
If you use the AOE3 version, use the directory: "C:\...\Programs\Microsoft Games\Age of Empires III\RM\" (if C: is the drive where you installed
it!)
3. At the bottom of the box insert the file name MyMap.xs
4. Below chose "All files" as data type.
5. And at the lowest bottom of the box chose "ANSI"
as code type.
6. Now you can press the "Save" button
at bottom right.
7. Repeat the actions 1. - 6. and chose the name MyMap.xml in (3.) .
Now we have all the files created we need for our first RMS. They
are still empty and don't work, but now we can open them one by
one and edit them with Notepad!
Note: Do NOT use map names which already
exist: Like "texas", "new england", etc. |
|
|
|
|
|
|
|
|
|
3) VARIABLES
Variables are the most important items in a RMS.
They let you define numbers, strings, operators (and even functions,
more later). All variables have to be defined before
using them, this means you have to give your variable a variable
type and a name:
For an integer number it looks like this:
int myCounter = 1;
Now we have defined a variable of type integer in the following
order:
<variable type> <variable
name> <is equal to> <predefined value> <execute
this>
It is recommended to use a name for a variable, which explains it
a bit. This is useful if you have many variables in your script.
Surely, you could also name it as int c1 =
1; , which is shorter.
Typical variables are:
Integer numbers (Numbers without decimal places):
Example definition: int myCounter = 1;
Float numbers (Numbers with decimal places): Example
definition: float myFraction = 0.87;
String (Strings contains letters, numbers and signs):
Example definition: string myUnitName = "My
Hero";
Bool expressions (Expression which checks validity)
: Example definition: bool myCheckIfItIsTrue
= "true";
The bool variable is special. It can only have two values, "true"
and "false". This variable is useful, if you want to compare
two or more values/variables.
Once a variable is defined in the script and you want to give your
variable a new value, you don't have write the variable type again:
myCounter = 2;
myFraction = 0.53;
myUnitName = "My New Hero";
myCheckIfItIsTrue = "false";
You can also add these variables:
float myNewFloat = myCounter + myFraction;
Here we defined a new float variable myNewFloat and gave it the value myCounter+myFraction, which is 2.53 . We also
could make other math operations with our variable numbers, like
substract (-) or divide (/).
string myNewName = myUnitName+" "+myCounter;
Here we defined a new string variable myNewName and gave it the value myUnitName+" "+myCounterFraction,
which is "My New Hero 2".
As you can see, we added a string, a "space" letter and
an integer to the new string variable.
Note: The maximum amount of "+"
signs for setting a string value is 3. Above we
just used 2 "+" signs!
In this section we also learned a bit more: The use of the operator
"=". When we use "=" in our
script, we always give our variable left of "="
the value which stands right of "=".
So in case of int myCounter = 1; the
variable myCounter has the value "1"
whenever we insert it into an equation. If we use our variable myCounter right of an equation, it will be not changed, it will just uses the value we gave it before.
AOE3 variables:
These variables are predefined by AOE3 when starting/launching the
finished RM script. The most important ones are:
cMapSize (integer) = 0, if map size
is chosen normal; = 1, if map size is chosen large
cNumberNonGaiaPlayers (integer) = amount
of players when running/starting the RMS
cNumberPlayers (integer) = amount of
players + gaia player when running/starting the RMS = cNumberNonGaiaPlayers+1
cNumberTeams (integer) = amount of
teams when running/starting the RMS.
Remember, you first get this values when setting
up and starting a game with your RMS!
Summary:
1. Variables will be defined exactly in this order:
<variable type> <variable
name> <is equal to> <predefined value> <execute
this>
2. There exist 4 types of variables: Integer, Float, String and Bool.
3. Once a variable is defined, you don't have to
write the variable type again and can use it's value in the script.
4. If our variables are numbers, we can use them
like it in a math expression.
5. String variables can be added and will add the
string or number to it's end.
6. AOE3 variables don't have to be defined nor have
to be set to a value. You get these values at game start. |
|
|
|
|
|
|
|
|
|
4) FUNCTIONS
You all know functions from Maths, like y=x+2.
As you can see, this function is also an equation. A function in
a program code (like the random map script) can also contain an
equation. It can even contain a few equations and also commands.
We can also write the Math function y=x+2 in RMS code. It would
look like this:
int myRMSFunction (int x=0) { return(x+2);
}
You see, we need a bit more to define, in order to tell the AOE3
program, which kind of variables we want to use. First, we gave
our function a name (myRMSFunction) and defined it as an integer
variable. In the brackets () we defined the input variable x. You
see, we defined also x as integer with value 0, but we can change
this value when we call this function later in
our script. To call a function just means that
we want to execute this function and want to get the result. Now,
let's call this function:
myRMSFunction (3);
Well, nothing happens! Really? No! What have we done? We inserted
"3" into the brackets, so this means that x now has the
value "3" instead of the old value "0". Since
we called this function, everything within the brackets {} will be executed. Since x is now "3", we get a value for
the variable myRMSFunction, which is "5" and surely of type integer,
as we defined it before. The command "return"
just executes the equation in the brackets of "return()".
Now you can easily see, that myRMSFunction(3) is nothing but the
y of our math equation, when using x=3. Even more, you are now be
able to use this function like a regular integer variable! However,
do not forget to insert a correct variable into the bracket, since
x has to be an integer. Well, why don't we use float for x and myRMSFunction?
Simple answer: Don't use it if not necessary, since calculating
integers works much faster than calculating floats.
Now we are able to call this function in our RMS as often as we
want and with different integer values.
In a random map script there are two different kinds of functions,
predefined functions and custom functions:
Predefined Function:
These functions are already defined by the AOE3 program. Most of
them start with the letters "rm...".
Example: "rmEchoInfo("Large
map");".
These functions are all explained
below. Some of these functions just execute something (they are
called void functions), others can return a value
or variable (they are called int, float or string functions), and some other functions
can compare values or variables (like if, else, while, case, etc. They are called operator functions). There are also some math functions
which work in RMS (like sqrt(x) for the square
root of x).
Custom Functions:
The most important custom function is called "void
main(void) {}" and ALWAYS has to be included
once in your RMS! The "void"
before "main" means, that this function
doesn't return any value, it just executes something. The "void"
in the brackets () means, that the function "main"
doesn't need any additional variables, so it just executes all commands
and definitions, which are added in the {} brackets.
Typically, there is no need for further custom functions in a random
map script. However, they get pretty useful, if we want to repeat
commands over and over again. If you want to use further custom
functions, there are two rules to follow:
1. You have to define all additional custom functions BEFORE you define the function main.
2. You can even call a custom function in a definition of a new
custom function, but the function you call has to be defined BEFORE you call it.
Using Operator Functions:
As we already have seen, operator functions are predefined functions.
They are needed to compare values or variables, using operators.
The most common operators are:
i == j , means "i"
has the same value as "j"
i < j , means "i"
has a lower value than "j"
i > j , means "i"
has a higher value than "j"
i <= j , means
"i" has a lower or the same value as "j"
i >= j , means
"i" has a higher or then same value as "j"
i != j , means "i"
has NOT the same value as "j"
i || j , means "i"
OR "j", this is a boolean operator, "i" OR "j"
has to be TRUE to proceed
i && j , means
"i" AND "j", this is a boolean operator, "i"
AND "j" has to be TRUE to proceed
i % j , means "i"
modulo "j". In simple words: "i" AND "j"
have to be integers. Devide "i" by "j" as much
as you can. If you can divide i (k*j)
times, (k is also an integer), then substract this
amount and look at the rest (i%j = i - k*j). If i<j, then k is
0.
Example: 1%4=1, 2%4=2, 3%4=3, 4%4=0, 5%4=1,
6%4=2, 7%4=3, 8%4=, 9%4=1, 10%4=2, ...
Now let's have a look at the operator functions. We insert the following
into the main function:
int i = 0;
int j = 0;
for (i=1; <= 10) {
if (i<4) j=1;
else if (i<6) j=2;
else j=3;
}
The first two lines just define the 2 integers i and j. Next line
start a for-loop, beginning from i=1 til i=10,
so it runs 10 times, since the for-loop increases
the variable i by 1 after each loop. If there is
more than 1 command in your function (here the for function) you need the brackets {}.
We have 3 commands in this bracket. The first command "if
(i<4) j=1;" can be read as "If i is smaller than
4, then set j equal to 1". The next command else if starts working (and only then!), when the if command above is not fulfilled anymore. For
this case j will be set to 2. The last command
in this loop, else, is always necessary when you
used an else if command before! If the last else
if command is not fulfilled, then set j=3. Surely, we could
have added more else if commands between if and else!
So, the result of "j" for each "i"
in the for-loop is:
i = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
j = 1, 1, 1, 2, 2, 3, 3, 3, 3, 3 |
|
|
|
|
|
|
|
|
|
5) CONVENTIONS
Some conventions to keep in mind:
1. The script is case-sensitive. Example: int,
Int and inT are 3 different things!
2. All variables (such as integers and floats)
must be defined the first time they are used. If they are redefined
after that, you must leave out the “int” of “float”.
Original: int numCows = 0;
Subsequent: numCows = 2;
3. Variables must not be named or defined by reserved names or commands,
like "int", "float", "string", as
well as all further "rm***" commands. Example: Avoid "int
int = 4;" or "string float = "A Name";".
4. Semicolon: The Semicolon ; always finishes a command line. This is where AOE3 will
know, that the definition or command before the semicolon shold
be executed next. Always use a semicolon after EVERY definition
or command.- There are many shortcuts for
making scripts, and some are shown at the end of this file. Conditional
statements can make use of “if”, “else”
and “if else”. Do loops can run through a list of commands
multiple times, and is particularly useful for duplicating areas
or objects for each player.
5. Comments can be specified by inserting // at the beginning of a line or enclosing text in /* and */. Comments can
remind you (and anyone else reading your map) what each section
is trying to do, and commenting out sections of a map script can
help in debugging problems. |
|
|
|
|
|
|
|
|
|
6) SCRIPT ORDER
Order of the scripts is important. You must build
land before you can place objects on it. Objects will only respect
the constraints of areas and objects that are already placed. You
can’t use a variable that you haven’t defined. |
|
|
|
|
|
|
|
|
|
7) MAP GRID
Maps are oriented along an X, Z grid. Y is elevation.
The origin of the grid, (0,0), is located at the bottom of the screen.
When using fractions, (0.5, 0.5) is the middle of the map, (1, 0)
is the right corner of the map at 3 o’clock, (0, 1) is the
left corner of the map at 9 o’clock, and (1, 1) is the top
of the map at 12 o’clock.
The grid uses 2x2meters square tiles.
You can see these tiles also in your scenario editor. Select "View" in the editor and chose "Grid". Each small and white rectangle is 1 tile. Each large and blue rectangle is 5x5 tiles (=10x10 meters).
A 4-player map is probably about 400 m along one side. Small buildings
occupy 1 tile (=2x2 meters) and small units (like small rocks or a bundle of grass) occupy a quarter tile (=1x1 meters) of space. |
|
|
|
|
|
|
|
|
|
8) OBJECT NAMES
1. Unit Names:
All object names refer to a protounit name. What’s
the difference between a unit and a protounit? Unit names in the
game are a friendlier version of the protounit name. For example,
the protounit name for “Villager” is “Settler”. The protounit name for “Town Center”
is “TownCenter”. So, most of the unit names have a similar or the same protounit name. You will find all protounit names in your AOE3 installation directory:
"\Age of Empires III\data\proto.xml".
2. Technology Names:
Technologies are for example "attack", "shield", "armor", "hitpoints", etc. . All technology names refer to a techtree name. You will find all techtree names in your AOE3 installation directory:
"\Age of Empires III\data\techtree.xml".
Both files are very long xml (text) files. I recommend to use an "XML Viewer" program to find the protounit you need. A very good freeware program is the "Mindfusion XML Viewer". You can download it here.
(the XML Viewer shows you a searchable treetable of the proto.xml content)
Note: Do NOT edit this file. It will cause problems in game.
3. Trigger Names:
Triggers are not needed for a regular random map, but we short want to explain it here for completeness. You can include all trigger effects and conditions from the scenario editor into a random map script. For the triggers we need the trigger effect and condition names, its parameter names (if any) and the corresponding custom variables. You will find all trigger related names in your AOE3 installation directory:
"\Age of Empires III\trigger\typetest.xml".
Now, we open this file in the XML Viewer (see above) and get something like this:
(how to find the trigger effect and condition names for our xs script file)
In the RM script it will be inserted, similar to this:
rmCreateTrigger("FoodForAllPlayers");
rmSwitchToTrigger(rmTriggerID("FoodForAllPlayers"));
for(i=1; <= cNumberNonGaiaPlayers){
rmAddTriggerEffect("Grant Resources");
rmSetTriggerEffectParamInt("PlayerID",i);
rmSetTriggerEffectParam("ResName","Food");
rmSetTriggerEffectParamInt("Amount",1000);
}
rmSetTriggerPriority(4);
rmSetTriggerActive(true);
rmSetTriggerRunImmediately(true);
rmSetTriggerLoop(false);
The explanations for the commands you can find in Chapter II - Triggers.
4. Terrain Names:
This is a bit different than for units and technologies. Terrains refer to a painted texture. These texture files can be found in a compressed archive of graphic files. The files are bar-compressed (similar to zip) and have the file extension "bar". You will find all texture files for terrains and other pictures and icons in your AOE3 installation directory:
"\Age of Empires III\art\art1.bar".
"\Age of Empires III\art\art2.bar".
"\Age of Empires III\art\art3.bar".
To view these files we need a "Bar Explorer". There are some freeware programs around, but I can highly recommend the "AoE3Ed" program made by Ykkrosh. It allows you to view and extract the files. Download link here.
However, we just need the names. Well, let us pick the terrain from the Scenario Editor. We chose "ground2_tex". In the AoE3Ed Archive Viewer we find the path "Art\terrain\texas\ground2_tex.ddt", as shown above. We only need the name shown as blue bold. In a RMS line it will be inserted, similar to this:
rmTerrainInitialize("texas\ground2_tex",4);
5. Other Object Names:
Names for water, cliffs, forests, etc. are mainly the same as shown in the scenario editor. Explore the original AOE3 scripts if you need a special name for an object you have seen in these maps. The "ALT+F" hotkey is your friend finding it!
Note: There might already be some lists for names around on the next. But remember: Each game patch can change the names, and even more the attributes and parameters. So with the methods above you are always on the secure side finding the correct names. |
|
|
|
|
|
|
|
|
|
9) XML FILES
Map scripts use the file extension xs but they are text files and can be edited with any text editor.
However, in order to play on a new map, you must also create an xml file with the same name as your xs file. xml is a markup language similar to HTML
used by Web pages. Most Web browsers can view xml files, but you may need to open the file in another application,
such as an xml editor or even a text editor. The
only things you need in your xml file are shown
below in blue bold: the map’s name and description, plus a
screenshot icon if you want. xml files for random
maps look like this:
<?xml version = "1.0" encoding = "UTF-8"?>
<mapinfo detailsText = "My Map Description." imagepath = "ui\random_map\all_maps" displayName = "My Map" cannotReplace = ""
loadDetails="" loadBackground="ui\random_map\all_maps">
<loadss>ui\random_map\new_england\new_england_ss_01</loadss>
<loadss>ui\random_map\new_england\new_england_ss_02</loadss>
<loadss>ui\random_map\new_england\new_england_ss_03</loadss>
</mapinfo>
(this image explains the effects of your xml commands while loading your xs map script)
|
|
|
|
|
|
|
|
|
|
10) A SIMPLE RANDOM MAP
Each custom random map consists
of two files. If your map is called "MyMap",
then you have to create two files, called "MyMap.xs"
and "MyMap.xml".
Example:
NOTE: The text in grey is not necessary,
its only for your help. Since it is marked as unreadable for the
RMS builder, you can include it into your real file!
MyMap.xs contains:
/* A SIMPLE RANDOM
MAP - by Author - Version 1.0 */
include "mercenaries.xs"; /* This loads the mercenaries needed for regular gameplay RMS! */
void main(void) { /* This is the main entry point for
your RMS! */
rmSetStatusText("",0.01); /* This is the value for the map
generation progress bar when starting the game */
/* (value 0.00 - 1.00) */
int myTiles = 8000; /*
This is a typical variable for the map size */
if(cMapSize == 1) { /* This if function checks if map
was selected normal or large. cMapSize
(1=large, 0=normal) */
myTiles = 12000; /*
This is a typical variable for the map size, when map chosen large! */
rmEchoInfo("Large map"); /* This is an output information only for the RMS debugger! */
}
int mySize = 2.0*sqrt(cNumberNonGaiaPlayers
* myTiles);
/* Above the size of the map will
be redefined. */
/* Since the map size is now proportional to the
square root of the player number, */
/* the map area will be proportional to the player
number! */
rmEchoInfo("Map size="+mySize+"m
x "+mySize+"m"); /* Another output information only for the RMS debugger! */
rmSetMapSize(mySize,mySize); /* This command will build the map
size */
rmSetSeaLevel(0); /* This
command will set the sea level in meters height */
rmSetSeaType("Amazon River"); /* This command will
set the sea texture, if there is water */
rmTerrainInitialize("texas\ground2_tex",4); /*
This command will set the map as water or land map */
/* If you want water, replace "texas\ground2_tex",4 with "water",0 and the defined SetSeaType
texture will be used! */
rmSetStatusText("",0.20); /* This is the next value for the map generation progress bar, the
bar increases */
/* Areas can be added here: */
rmSetStatusText("",0.40); /* This is the next value for the map generation progress bar, the
bar increases */
/* Players are added here: */
/* The following command will align
the place for the player areas randomly in a circle: */
rmPlacePlayersCircular(0.3, 0.3, rmDegreesToRadians(0.0));
/* Now we start a loop for
every player: */
for(i = 1; <= cNumberNonGaiaPlayers) {
/* We define an area for every
player: */
int id = rmCreateArea("Player"+i);
/* We define the height of
each player area: */
rmSetAreaBaseHeight(id,4.0);
/* We define the coherence
of each player area (1.0 = smooth circe, 0.0 = roughest circle): */
rmSetAreaCoherence(id,1.0);
/* We define the height blend
of each player area: */
rmSetAreaHeightBlend(id,2);
/* We define the center of the player area: */
rmSetAreaLocPlayer(id,i);
/* We define the size of the
player area: */
rmSetAreaSize(id,0.001,0.001);
/* We define the area of the
player area: */
rmSetPlayerArea(i,id);
/* We define the terrain of
the player area: */
rmSetAreaTerrainType(id,"texas\ground3_tex");
/* We have defined all neccessary
variables for the player area, now we can build it: */
rmBuildArea(id);
/* We 1. define a new object,
2. we add a starting TownCenter to it, 3. we place/build
it onto the map: */
int towncenterID = rmCreateObjectDef("Starting Towncenter"+i);
rmAddObjectDefItem(towncenterID,"TownCenter",1,0.0);
rmPlaceObjectDefAtLoc(towncenterID,i,rmPlayerLocXFraction(i),rmPlayerLocZFraction(i),1);
}
rmSetStatusText("",0.60); /* This is the next value for the
map generation progress bar, the bar increases */
/* Objects can be added here */
rmSetStatusText("",0.80); /* This is the next value for the
map generation progress bar, the bar increases */
/* Triggers can be added here */
rmSetStatusText("",1.00); /* This is the next value
for the map generation progress bar, the bar is full! */
}
/* This is the end of our
xs-file! */
MyMap.xml
contains:
<?xml version = "1.0" encoding = "UTF-8"?>
<mapinfo detailsText = "My Map Description." imagepath = "ui\random_map\all_maps" displayName = "My Map" cannotReplace = ""
loadDetails="" loadBackground="ui\random_map\all_maps">
<loadss>ui\random_map\new_england\new_england_ss_01</loadss>
<loadss>ui\random_map\new_england\new_england_ss_02</loadss>
<loadss>ui\random_map\new_england\new_england_ss_03</loadss>
</mapinfo>
Explanations for the XML commands you can find above.
The result of this map is a map with flat
terrain and terrain texture "texas\ground2_tex". An object for
each player is added: A starting towncenter object. The town centers are placed
symmetrical in a circle. The content in the xml file between <loadss> and </loadss> should not be changed, unless you know the image paths (see Terrains).
Now you can copy and paste the text above into your files and try
it out! |
|
|
|
|
|
|
|
|
|
11) HOW TO TEST THE MAP
First of all: This is different than in Age of Mythology game.
1. For TESTING the map script:
For testing the map script you should enable the debugger. The debugger is an AOE3 build-in program which allows you to find errors in your XS script. The debugger is deactivated by default - even if you see the "Debugger" button in the scenario editor.
1.a. How to enable the debugger:
The easiest way is to create a new link icon for AOE3 onto your desktop. Select your "Age of Empires III" icon on your desktop, then right click on it and chose "Create Link". You will get a new icon on your desktop: "Age of Empires III (2)". Now,
right click on your new "Age of Empires III (2)" icon, then right click on it and chose "Properties". In the popup menu you see the target line similar to this:
"C:\...\Age of Empires III\Age3.exe"
Add the green text below and press "ok". That's it!
"C:\...\Age of Empires III\Age3.exe" +debugRandomMaps
(for debugging AI scripts it is +debugAI, but not needed here)
Note: Do NOT use the "user.cfg" file to enable the debugger. This can cause problems with future patches of the game.
1.b. How to debug your random map:
Now run AOE3 by clicking your new AOE3 debugger icon, then select your new map in the Scenario Editor by chosing "File -> New -> Type='Your map' and enable the debugger button!". Then generate it. The following screen will occur, if and only if there are some unusual or wrong commands in your script:
(AOE3 XS Debugger. Click image for more informations)
2. For PLAYTESTING the map script with the computer players offline:
You MUST move your map files (MyMap.xs and MyMap.xml) from the AOE3 installation directory
"C:\...\Programs\Microsoft Games\Age of Empires III\RM\"
to your windows profile directory, usually
"C:\...\My Documents\My Games\Age of Empires III\RM\"
If you JUST COPY your files you get problems later!
For Re-editing your custom map files, move your files back to the previous directory!
Notes:
1. Do NOT publish or play any maps online which did NOT pass the debugger without errors!
2. NEVER EVER use map names for your new map which already exist, neither offline (SP) nor online (MP: LAN or ESO2)!
3. Feel free to modify AND rename the original AOE3 random map scripts - you have paid for it!
4.
Do NOT modify or use parts of custom random map scripts without permission by the authors.
Most RM scripters leave their name and address in the script header. So, contact them BEFORE you publish their modified script! |
|
|
|
|
|
|
|
|
|
12) NOW LET US HAVE SOME FUN!
We change the original AOE3 "Texas" map from summer skin to winter skin. After reading this small sub chapter you will be able to create and play the following map:
The original "Texas" map: |
|
Your new "Texas Winter" map: |
|
|
|
Features:
- The map you will create here has EXACTLY the same gameplay as the original "Texas" map!
- It can be played single player mode, multiplayer mode and even on ESO2 (It was already published, so avoid the last step - PUBLISHING. If you want to make this map unique, read the complete tutorial here.).
Step "0":
If you have any questions, later, scroll up to the top of this site and select the item in the menu to the left.
Step 1:
First we copy the original map files to another directory:
The original map files are called "texas.xs" and "texas.xml" and are usually located in the directory:
"C:\...\Programs\Microsoft Games\Age of Empires III\RM\".
Copy them to any other directory, as example "C:\".
Step 2:
Now rename these files as follows:
"texas.xs" -> "texas winter YourName.xs" - (just replace YourName with your nickname, in all Steps)
and
"texas.xml" -> "texas winter YourName.xml".
Then, right click on each file - one by one - and select "Properties". In the popup property menu disable "write protection" for each file. Now you are allowed to edit these files without any warnings.
Step 3:
Now, move both renamed files back to the directory where you copied them from, usually:
"C:\...\Programs\Microsoft Games\Age of Empires III\RM\".
Step 4:
Now open your "Notepad" and open your new file "texas winter YourName.xml".
Note: Better use a text editor which shows you the line you are currently working on. Like WinEdt 5.4, or any other. By the way, this text editor rocks, and I use it regularly - especially for RMS. "Texturizer 1.7" or higher is also great. But, now back to basics...
The file content is:
<?xml version = "1.0" encoding = "UTF-8"?>
<mapinfo details = "25997" imagepath = "ui\random_map\texas" displayNameID = "25996" cannotReplace = ""
loadDetails="35876" loadBackground="ui\random_map\texas\texas_map">
<loadss>ui\random_map\texas\texas_ss_01</loadss>
<loadss>ui\random_map\texas\texas_ss_02</loadss>
<loadss>ui\random_map\texas\texas_ss_03</loadss>
</mapinfo>
Change it into (black bold text only ... and avoid line breaks!):
<?xml version = "1.0" encoding = "UTF-8"?>
<mapinfo details = "25997" imagepath = "ui\random_map\texas" displayName = "Texas Winter YourName" cannotReplace = ""
loadDetails="35876" loadBackground="ui\random_map\texas\texas_map">
<loadss>ui\random_map\yukon\yukon_ss_01</loadss>
<loadss>ui\random_map\yukon\yukon_ss_02</loadss>
<loadss>ui\random_map\yukon\yukon_ss_03</loadss>
</mapinfo>
(If you want to know what the changes will do, just read the subchapter "XML FILES" above - but not needed here).
Now, save the XML file. Cool, your own XML file is finished! Now the XS file ...
Step 5:
Change the following lines in your "texas winter YourName.xs" file (the changes appear colored):
NOTES:
1.
Do NOT make line breaks while typing the text in!
2. ONLY the text in Courier Font should be changed in the corresponding script line!
3. The outlined grey text: /* old ... */ can be neglected if you like. It is only for your help.
/* Line 001:*/
// TEXAS WINTER - made by YourName - with help of M0nTy_PyTh0n's RMS Tutorial /* old was "TEXAS" */
/* Line 002:*/
// CurrentMonth Year - The Tutorial can be found here: http://hyenastudios.mugamo.com/aoe3rmstutorial.htm /* old was "October 2003" */
/* Line 061:*/
rmSetLightingSet("Yukon"); /* old lighting was "Texas" */
/* Line 065:*/
rmSetBaseTerrainMix("yukon snow"); /* old terrain mix was "texas_grass" */
/* Line 066:*/
rmTerrainInitialize("yukon\ground1_yuk", 5.0); /* old terrain was "texas\ground1_tex" */
/* Line 067:*/
rmSetMapType("yukon"); /* old map type was "texas" */
/* Line 068:*/
rmSetMapType("snow"); /* old map type was "grass" */
/* Line 184:*/
rmAddObjectDefItem(randomTreeID, "TreeYukon", 1, 0.0); /* old object was "TreeTexasDirt" */
/* Line 192:*/
rmAddObjectDefItem(StartAreaTreeID, "TreeYukon", 1, 0.0); /* old object was "TreeTexasDirt" */
/* Line 246:*/
rmSetAreaTerrainType(westDesertID, "yukon\ground4_yuk"); /* old terrain was "texas\ground4_tex" */
/* Line 247:*/
rmAddAreaTerrainLayer(westDesertID, "yukon\ground1_yuk", 0, 4); /* old terrain was "texas\ground1_tex" */
/* Line 248:*/
rmAddAreaTerrainLayer(westDesertID, "yukon\ground2_yuk", 4, 10); /* old terrain was "texas\ground2_tex" */
/* Line 249:*/
rmAddAreaTerrainLayer(westDesertID, "yukon\ground8_yuk", 10, 16); /* old terrain was "texas\ground3_tex" */
/* Line 251:*/
rmSetAreaMix(westDesertID, "yukon snow"); /* old terrain mix was "texas_dirt" */
/* Line 270:*/
rmSetAreaTerrainType(id, "yukon\ground2"); /* old terrain type was "texas\ground2" */
/* Line 704:*/
rmSetAreaCliffType(cliffEastID, "Rocky Mountain2"); /* old cliff type was "Texas Grass" */
/* Line 710:*/
rmAddAreaTerrainLayer(cliffEastID, "yukon\ground2_yuk", 0, 2); /* old terrain was "texas\ground2_tex" */
/* Line 745:*/
rmSetAreaCliffType(cliffWestID, "Rocky Mountain2"); /* old cliff tpye was "Texas" */
/* Line 751:*/
rmAddAreaTerrainLayer(cliffWestID, "yukon\ground2_yuk", 0, 2); /* old terrain was "texas\ground2_tex" */
/* Line 809:*/
rmSetAreaForestType(westForest, "yukon forest"); /* old forest type was "texas forest dirt" */
/* Line 845:*/
rmSetAreaForestType(eastForest, "yukon snow forest"); /* old forest type was "texas forest" */
Done Editing! Congratulations!!!
Step6:
Now, save both edited files and move (MOVE! NOT COPY!) both files to your custom random map directory, usually:
"C:\...\My Documents\My Games\Age of Empires III\RM\" .
Step7:
Play your new map! Select your map in the Skirmish startup menu by pressing the "Custom Maps" button.
Enjoy!
FINAL NOTES:
1.
You can also make the changes line-by-line and generate the map in the scenario editor to see the effects. Sometimes you have to load the map a few times (or with different player number) to see it.
2. If you did not succeed, go to this chapter above!
3. If you are too lazy modifying this map by your own, then shame on you ;) , but download it here!
4. Do NOT publish this map online. I already did it! But, now there are new possibilities for you to make new maps, right?!
|
|
|
|
|
|
13) HOW TO GET HELP
Some ways to get help:
1. Look at Ensemble Studios official
RM scripts to learn conventions and shortcuts. Alot lof lines in their RM scripts have comments for your better understanding.
2. Use the XS debugger (see above).
3. Using the console, you can get help for any
command by typing: help(“command”)”.
4. Look online. Fan sites are often a great source
of information on random map scripting.
5. Keep it simple. If something in your script
seems to be breaking, try and isolate the problem.
You
found no answers to your questions here?
Just post it in our tutorial thread of our
|
|
|
|
|
|
|
|
|
|
II. RANDOM MAP SCRIPTING COMMANDS |
|
|
|
|
|
|
|
|
|
1) GENERAL PURPOSE
rmEchoInfo( string echoString, int
level);
Random map echo. Use this to spit out information while debugging
a script. It is not shown to the player.
rmRandFloat(float min, float
max);
Returns a random float between min and max.
This is a random number generator useful for determining random
events. Because it is a float, it can handle decimals.
rmRandInt(int min, int max);
Returns a random integer between
min and max. This is a random number generator useful for determining
random events. Because it is a integer, it cannot handle decimals,
but that makes it useful for placing down numbers of objects.
rmSetMapSize( int x, int
z);
Sets the size of the map. X and Z
are in meters. They do not need to be the same if you want to create
a rectangular map. All ES maps scale map size by number of players.
rmSetSeaLevel();
Sets the sea level for the map.
rmGetSeaLevel();
Gets the sea level for the map.
rmSetSeaType(string name);
Sets the sea type for the map. This
is used if terrain is initialized to water.
You must use this command before you place water on a map. The sea
type must be a valid water type from the random map editor.
rmAreaFractionToTiles(float
fraction);
Converts an area from fraction of
the map to tile count. Fractions are relative to map size, so sometimes
you may want to use them.
rmAreaTilesToFraction(int
tiles);
Converts area tile count to fraction
of map.
rmXFractionToTiles(float fraction);
Converts an fraction of the map in the x direction to tile
count.
rmXTilesToFraction(int tiles);
Converts tile count in the x direction
to fraction of map.
rmZFractionToTiles(float fraction);
Converts an fraction of the map in the z direction to tile
count.
rmZTilesToFraction(int tiles);
Converts tile count in the z direction to fraction of map.
rmMetersToTiles(float meters);
Converts a distance in meters to a number of tiles.
rmTilesToMeters(int tiles);
Converts a number of tiles to a distance in meters.
rmXMetersToFraction(float meters);
Converts meters into a fraction of the map in the x direction.
rmZMetersToFraction(float meters);
Converts meters into a fraction of the map in the z direction.
rmXFractionToMeters(float meters);
Converts a fraction of the map in the x direction to meters.
rmZFractionToMeters(float meters);
Converts meters a fraction of the map in the z direction
to meters.
Fractions are relative to map size, so sometimes you may want to
use them instead of distances in meters.
rmDegreesToRadians(float
degrees);
Converts an angle in degrees to radians.
Particularly useful when players are placed in a circle.
rmTerrainInitialize( string
baseTerrain, float height);
Initializes the terrain to the base
type and height. Specifies the base terrain to use for a map. If
set to water, sea type needs to be defined. Initial terrain is usually
grass, sand, snow or water.
rmSetLightingSet(string
name);
Sets a lighting set. You can specify
a lighting set from the scenario editor to be used for your RMS.
This command must be placed after terrain is initialized.
rmSetGaiaCiv(long civ);
Sets Gaia's civilization. This is
only useful if you place Gaia objects that vary by civilization, such
as special civilisation units.
rmSetStatusText(status,
progress);
Sets the friendly cool loading screen text. This text will be seen
by players while the map is generating. If you do not at least specify
percentages in the progress parameter, the “loading bar”
will not advance during map generation.
rmDefineConstant(string
name, int value);
Defines an integer constant. |
|
|
|
|
|
|
|
|
|
2) PLAYERS
Player locations need to be determined so that objects
and area can be placed “by player.” Player areas must
still be created as individual areas. A special player in an RM script is the "Gaia" player and always has the player number "0" in a map script (see chapter above for more explanations). The following commands refer to human and computer players only:
rmGetPlayerCiv(int
playerID);
Gets the civilization the specified
player is on.
rmGetCivID(string civilization);
Sets the civilization to compare with the players civilization. Example:
if ( rmGetPlayerCiv(i) == rmGetCivID("Russians")) {
/* do something for player i if he is Russian */
};
These commands are useful for placing specific
objects or varying starting resources.
They are also useful for triggers that fire or prohibit specific
improvements.
rmGetNumberPlayersOnTeam(int
teamID);
Gets the number of players on the
given team. Useful for scaling area or resources in a team area
based on number of players on that team.
rmSetTeamSpacingModifier(float
modifier);
Sets the team spacing modifier. Normally,
all players are placed equidistant. This command allows you to force
team members closer together. Values of 0.3-0.5 return the best
results. Values less than 0.25 may not provide enough space for
starting resources.
rmPlacePlayersCircular(float
minFraction, float maxFraction, float angleVariation);
Makes a circle of player locations.
Places players in a circle. Variation is determined by the difference
between the min and max. Angle variation determines whether players
are equidistant or can be slightly closer or farther apart. Circular
placement is generally the most versatile, but will not work on
all map types, such as non-square maps.
rmPlacePlayersSquare(float
dist, float distVariation, float spacingVariationfloat);
Makes a square of player locations.
Places players in a square, which automatically adjusts to a rectangle
for rectangular maps. Unlike the circle, variance here is determined
by a plus or minus (the distVariation) off of the mean distance.
SpacingVariation determines whether players are equidistant or can
be slightly closer or farther apart.
rmPlacePlayersLine(float
x1, float z1, float x2, float z2, float distVariation, float spacingVariation);
Makes a line of player locations.
Sometimes you will want players to be placed in a line. Texas
places each time in a line, while New England places all players
in a circle. Using a line placement is not easy because there may
not be enough room for player areas or resources. X and Z determine
the starting and ending locations of the line. DistVariation determines
how far from the line player areas can vary, and spacingVariation
determines how much space there is among points along the line where
players are placed.
rmSetPlacementSection(float
fromPercent, float toPercent);
When placing players in a circle or square, this command allows
you to skip part of the circle or square, in essence removing a
slice from the pie (maybe you want to fit an ocean or sea in there like
in Texas). The default for fromPercent is 0, and the default
for toPercent is 1. That means use the whole circle or square. You
can pass in something like 0.25 and 0.50 to have the players placed
from 25% in to 50% in along the circle or square. For circular placement,
0 is at 9:00, 0.25 is at 12:00, 0.5 is at 3:00, and 0.75 is at 6:00.
For square placement (think of the square as a line that follows
a square), 0 is at 6:00, 0.25 is at 9:00, 0.5 is at 12:00, and 0.75
is at 3:00.
rmSetPlacementTeam(long
teamID);
Sets the team to place. Use this
before calling the various rmPlacePlayers functions, and only players
on the specified team will get placed. The first team is number
0, the second is number 1, etc. Pass in -1 for the teamID to place
all teams (or actually all players that haven't been placed yet).
rmPlacePlayer(int playerID, float
xFraction, float zFraction);
Sets one player location. You can use this to place players
anywhere. Once a player is placed, it won't be repositioned by any
future calls to the various rmPlacePlayers functions. This can be
tricky since you often do not know how many players will be placed
on the map, since from 2-12 players can play any map.
Here are a few examples of things you can do;
Put the first team in the center of the map and all other teams
around the outside:
rmSetPlacementTeam(0);
rmSetPlayerPlacementArea(0.3, 0.3, 0.7, 0.7);
rmPlacePlayersCircular(0.4,0.3,rmDegreesToRadians(5.0));
rmSetPlacementTeam(-1);
rmSetPlayerPlacementArea(0, 0, 1, 1);
rmPlacePlayersCircular(0.4,0.3,rmDegreesToRadians(5.0));
Put player one in the bottom corner, player two in the right corner,
and the rest of the players in a line from the left corner to the
top corner (9:00 to 12:00):
rmPlacePlayer(1, 0.2, 0.2);
rmPlacePlayer(2, 0.8, 0.2);
rmPlacePlayersLine(0.2, 0.8, 0.8, 0.8, 20, 10);
rmSetPlayerPlacementArea(float
minX, float minZ, float maxX, float maxZ);
Sets the area of the map to use for
player placement. Use this command if, for example, you want to
place players in one quadrant of a map.
rmSetPlayerArea(int playerID,
int areaID);
Sets a player's 'official' area.
rmSetTeamArea(int teamID, int areaID);
Sets a team's 'official' area. When you want an area to
belong to a player (i.e. this is the location from which you will
place player resources) assign it to a player. Teams work the same
way. Usually you want to iterate through number of players using
for(i=1; <cNumberPlayers).
rmPlayerLocXFraction(int
playerID);
Gets a player's start location x
fraction.
rmPlayerLocZFraction(int playerID);
Gets a player's start location z fraction. Use these commands
when you don’t know where a player’s starting location
is and you need the values to place other areas or resources. |
|
|
|
|
|
|
|
|
|
3) AREAS
Areas are regions on a map. They are often irregular
in shape, but can be rectangular as well. Some areas are used for
placing specific terrain, like a cliff or ocean, while others are
just used as boundaries for other areas. Special types of areas
are player areas, which belong to a certain player, or team areas,
which belong to a team. Saying these areas “belong”
is just a convenient method of making sure other areas or objects
are placed in that area.
rmCreateArea(string
name, int parentAreaID);
Creates an area. Creates an area
and lets you name it. Areas without a parentArea use the entire
map as their parentArea. You can also make existing areas the parentArea,
in order to place a sub-area within a player area, for example.
Areas will generally try to place several times and will return
an error message if they fail. To ignore this error message, use
setAreaWarnFailure below.
rmSetAreaSize(float minFraction,
float maxFraction);
Set the area size to a min/max fraction
of the map. The min and max can be set to the same value if you
want no size variation. Experiment with different values to make
sure your area is not too large or too small to be seen. Even if
your area does not place special terrain, it can be helpful to temporarily
paint the area with a distinct texture, such as black or snow, to
see where and if it is actually getting placed.
rmSetAreaLocation(int areaID,
float xFraction, float zFraction);
Set the area location. Sometimes
you want to place an area in a specific location, such as 0.5, 0.5,
the center of the map.
rmSetAreaLocPlayer(int
areaID, int playerID);
Set the area location to player's
location. This is a shortcut for placing an area at the player’s
location. Generally, this is used when tiny player areas are first
placed as placeholders, then SetAreaLocPlayer can be used to make
larger player areas later or to place a sub-area (such as a terrain
patch) near the player’s Town Center.
rmSetAreaLocTeam(int
areaID, int teamID);
Set the area location to team's location.
Just like SetAreaLocPlayer except it applies to team areas.
rmBuildArea(int areaID);
Builds the specified area. Actually
builds the area. Choosing when to use this command can have a big
effect on your map. For example, if you define a lake area and then
build it, land that is placed later can stick into the lake or be
placed as islands. On the other hand, if the land and water are
built at the same time, they will try to avoid each other (if the
proper constraints are set). Generally, player areas should all
be built at the same time to make sure there is enough space for
ever player.
rmBuildAllAreas();
Simulatenously builds all unbuilt
areas. Does not include connections.
rmSetAreaTerrainType(int
areaID, string terrainTypeName);
Sets the terrain type for an area.
Often, you want to paint in an area with a certain terrain. Use
the terrain names from the scenario editor. Forests, cliffs, and
water are handled differently. Use AreaTerrainType for terrains
such as grass, snow, ice, and sand.
rmPaintAreaTerrain(int
areaID);
Paints the terrain for a specified
area.
rmSetAreaBaseHeight(int
areaID, float height);
Sets the base height for an area.
If not specified, the area will adopt the height of the parent area,
including the base height of the map if no parent area is specified.
Make sure to place land higher than water if you want to place land
objects (such as TownCenter) later.
rmSetAreaWarnFailure(int
areaID, bool warn);
Sets whether the area build process
will warn if it fails. It is very easy to over-constrain areas to
the point where there is no room for them. This can cause two problems:
the map may take a long time to generate, or if you are in debug
mode (see above), the debugger will pop up and generation will stop.
Sometimes you want to catch these errors, but when you are done
with your map it is a good idea to set SetAreaWarnFailure to false.
rmSetAreaForestType(int
areaID, string forestName);
Sets the forest type for an area.
Paints the area with a forest type. Use the forest types from the
scenario editor.
rmSetAreaWaterType(int
areaID, string waterName);
Sets the water type for an area.
Paints the area with a water type. Use the water types from the
scenario editor. Because water types automatically change elevation
and can place objects, they tend to affect areas a little larger
than specified. Just allow plenty of room.
rmSetAreaCliffType(int
areaID, string cliffName);
Sets the cliff type for an area.
Cliffs are handled differently from other terrain in order to allow
you to handle features like ramps. However, you can use setAreaTerrainType
to place an impassable cliff-texture as a normal area as well. CliffName
should use a cliff type from the Editor.
rmSetAreaCliffPainting(int
areaID, bool paintGround, bool paintOutsideEdge, bool paintSide,
float minSideHeight, bool paintInsideEdge);
Set cliff painting options for an
area. Determines how a cliff is painted with impassable and passable
textures. PaintGround - Specifies if the ground should be painted
or just left whatever it already is. Defaults true. PaintSide -
Specifies if the cliff sides should be painted. Defaults true. PaintEdge
- Specifies if the cliff edge should be painted. This is the area
between the cliff side and the ground. Defaults true. MinSideHeight
- Specifies the minimum height that a cliff tile must be sloped
before treating it as a cliff side. Set to 0 to have the minimum
amount of cliff sides painted. Defaults to 1.5.
rmSetAreaCliffEdge(int
areaID, int count, float size, float variance, float spacing, int
mapEdge);
Set cliff edge parameters for an
area. Determines whether there should be pathable ramps or not connecting
the top of the cliff to the surrounding area. Count - Number of
cliff edges to create. The count times the size should not be more
than 1.0. Defaults to 1. size - This specifies how much of the area's
outline should be turned into cliff edges. It should be between
0.0 and 1.0. Set to 1.0 to make the whole area surrounded. Defaults
to 0.5. Variance - The variance to use for the size. Defaults to
0.0. Spacing - Spacing modifier. This should be between 0.0 and
1.0. The smaller this is, the closer together the cliff edges will
be. Defaults to 1.0. MapEdge - Specifies where the cliff edge should
be in relation to the map edge. Set to 0 for any, 1 to be away from
the map edge, or 2 to be close to the map edge. Defaults to 0.
rmSetAreaCliffHeight(int
areaID, float val, float variance, float ramp);
Set an area's cliff height. Val -
Make positive for raised cliffs and negative for lowered cliffs.
Defaults to 4.0.
Variance - The variance to use for the height. Ramp - This is used
to determine how quickly the height ramps up to the cliff height
(it refers to steepness in this context, not pathable ramps to reach
the top of a cliff). Defaults to 0.5.
rmAddAreaCliffEdgeAvoidClass(int
areaID, int avoidID, float minDist);
Adds a class for an area's cliff
edge to avoid. You can tell a cliff edge to avoid a certain class,
such as a connection. Remember that connections must be created
before the cliff (see below).
rmAddAreaTerrainLayer(int
areaID, string terrain, float minDist, float maxDist);
Adds a terrain layer to an area. Terrain layers
allow you to place a border of one or more textures around an area.
For example, you can have "texas\ground3_tex" and "texas\ground4_tex" around an
area of grass. You can specify multiple layers for an area, as long
as the minDistance for one starts where the maxDistance for another
leaves off. Because different textures overlap one another you may
need to experiment with distances to get the correct effect. Here
is an example:
rmSetAreaTerrainType(bonusIslandID,
"texas\ground2_tex");
rmAddAreaTerrainLayer(bonusIslandID, "texas\ground6_tex",
13, 20);
rmAddAreaTerrainLayer(bonusIslandID, "texas\ground5_tex",
6, 13);
rmAddAreaTerrainLayer(bonusIslandID, "texas\ground4_tex",
0, 6);
rmSetAreaTerrainLayerVariance(int
areaID, bool variance);
Specifies if the area should vary
the terrain layer edges. Usually, variance in terrain layers looks
better, but sometimes you might want to turn it off. Defaults to
true.
rmSetAreaMinBlobs(int
areaID, int blobs);
Sets minimum number of area blobs.
rmSetAreaMaxBlobs(int areaID, int
blobs);
Sets maximum number of area blobs. An area can be placed
with multiple blobs. Blobs are placed independently, using the minimum
and maximum distances below. Areas made with a single blob will
be circular. Areas made with multiple blobs can be come long and
sinuous.
rmSetAreaMinBlobDistance(int
areaID, float dist);
Sets minimum blob distance.
rmSetAreaMaxBlobDistance(int areaID,
float dist);
Sets maximum blob distance. Specifies how far apart blobs
can be from each other. The greater the distance, the more the area
will tend towards serpentine instead of circular (envision a chain
of beads). However, if you specify many blobs, this variation may
become obscured as more and more blobs are placed for the area.
rmSetAreaCoherence(int
areaID, float coherence);
Sets area coherence (0-1). Coherent
areas tend to stay together more. The effect is harder to notice
on smaller areas.
rmSetAreaSmoothDistance(int
areaID, int smoothDistance);
Sets area edge smoothing distance.
Distance is number of neighboring points to consider in each direction.
Water areas benefit from more smoothness as it eliminates small
bumps and indentations.
rmSetAreaHeightBlend(int
areaID, int heightBlend);
Sets how smoothly area height blends
into surroundings. Corresponds to the smooth tool in the Scenario
Editor. Usually a heightBlend of 0 will leave geometric-looking
jagged edges. A heightBlend of 1 will smooth smaller areas. A heightBlend
of 2 will smooth larger areas or areas of disproportionate heights.
Anything above 2 may flatten an area completely.
rmAreaID(string name);
Gets area ID for given area name.
rmAddAreaInfluencePoint(int
areaID, float xFraction, float zFraction);
Adds an area influence point.
rmAddAreaInfluenceSegment(int areaID,
float xFraction1, float zFraction1, float xFraction2, float zFraction2);
Adds an area influence segment. You may want an area to
grow towards specific points or lines. A circular area placed at
the center of the map with an influence point of 1, 1 will produce
a peninsula that protrudes towards 12 o’clock. Influence points
and segments can be useful in getting areas, such as rivers, to
extend beyond the edge of the map.
rmAddAreaRemoveType(int
areaID, string typeName);
Add an unit type that the specified
area removes. Sometimes you may want an area to clean itself of
objects, such as removing trees from ice. This will only work if
the objects are already placed before the area, which is the reverse
of how most ES maps are generated. You can reference specific units
or abstract types, such as “unit” and “building.”
rmAddAreaTerrainReplacement(int
areaID, string terrainTypeName, string newTypeName);
Adds a terrain replacement rule to
the area. If you place an area with no terrain specified, it will
use the terrain of the parent area (including the base map). However,
specifying terrain replacement will paint an area only when another
texture is present. This command is most useful with connections,
where you want to replace water with land where a connection goes
across a river, or replace rock with snow for mountain passes.
bool rmAddAreaToClass(int
areaID, int classID);
Add given area to specified class.
The reason to add areas to classes is so you can refer to the entire
class later on instead of the individual areas. This is most useful
when placing objects (you can say just place in a class) or constraints
(you can say to avoid a certain class).
int rmDefineClass(string
className);
Define a class with the given name.
Like any variable, Classes must be defined before they can be used
the first time.
rmClassID(string name);
Gets class ID for given class name. |
|
|
|
|
|
|
|
|
|
4) CONNECTIONS
Connections are special areas that are used to connect
other areas. They are typically used to place land-bridges or mountain-passes
among players. Connections must be placed after the areas they are
trying to connect, but often need to be defined before those areas
if the area needs the rmAddConnectionArea command.
rmCreateConnection(string
name);
Creates an connection. Defines a
new connection.
rmSetConnectionType(int
connectionID, int connectionType, bool connectAll, float connectPercentage);
Sets the connection type. This command determines which players
are connected. The valid values for connectionType are:
- cConnectAreas: This is the default that is used if you don't call
rmSetConnectionType. You have to specify each area to be connected
by calling rmAddConnectionArea.
- cConnectPlayers: Connect all player areas.
- cConnectAllies: Connect all ally player areas.
- cConnectEnemies: Connect enemy player areas. Currently this one
ignores the connectAll parameter (it treats it as true no matter
what). Just let me know if you think it's important for it to work
a certain way when connectAll is false... one thing I thought of
is connecting each player to its closest enemy.
Set the connectAll parameter to true if you want all of the areas
to get connected to all of the other areas. Set it to false to have
the areas connected sequentially where the first area gets connected
to the second area, the second area gets connected to the third
area, etc.
You can use the connectPercentage parameter to reduce the number
of connections that are generated. For example, if you set it to
0.5, then half of the connections will get generated. The ones that
are generated are randomly chosen. Some ES maps with connections
connect all players when player number is small (<6) and uses
a connection percentage on larger maps, otherwise so many connections
can get placed that the barrier (like water or rock) is obscured.
rmAddConnectionArea(int
connectionID, int areaID);
Adds an area to the connection. This
is only valid if you set the connection type is set to cConnectAreas.
You must specify this while defining the area, after the connection
is defined, and before building the connection.
rmSetConnectionPositionVariance(int
connectionID, float variance);
Sets the position variance of a connection.
The connection will normally start at the area's position, but this
allows it to vary from that position. You can set this to -1 for
it to pick completely random positions within the starting and ending
areas. This command is often needed when specifying multiple connections
(for example, one within a team and another between teams) so that
the connections do not overlap.
bool rmAddConnectionStartConstraint(int
connectionID, int constraintID);
Add specified constraint for a connection
start point.
bool rmAddConnectionEndConstraint(int
connectionID, int constraintID);
Add specified constraint for a connection end point. If
setConnectionPositionVariance isn’t working out, you can also
specify constaints for the endpoints of the connection itself. This
way you can have connection endpoints avoid impassable land or player
areas, for instance.
rmSetConnectionWidth(int
connectionID, float width, float variance);
Sets the width of a connection. Because
connections are often the only pathable area over a barrier such
as water or rock, set this wide enough to prevent pathing problems,
typically > 8.
rmSetConnectionBaseHeight(int
connectionID, float width);
Sets the base height of a connection.
rmSetConnectionCoherence(int connectionID,
float width);
Sets area coherence (0-1).
rmSetConnectionWarnFailure(int connectionID, bool warn);
Sets whether a connection warns on failure.
rmSetConnectionHeightBlend(int connectionID,
float width);
Sets how smoothly connection height blends into surroundings.
rmSetConnectionSmoothDistance(int connectionID, float width);
Sets connection edge smoothing distance (distance is number
of neighboring points to consider in each direction).
rmAddConnectionTerrainReplacement(int connectionID, string
terrainTypeName, string newTypeName);
Adds a terrain replacement rule to the connection. These
commands all work exactly as they do for areas, but must be called
out specifically for connections.
rmSetConnectionTerrainCost(int
connectionID, string terrainTypeName, float cost);
Sets the terrain cost for a connection.
When you need a connection to avoid a type of terrain, set this
value. If you place roads between players, you might want them to
avoid forests or cliffs. The cost must be greater than or equal
to 1, or set to -1 to specify a terrain is impassable.
rmSetConnectionBaseTerrainCost(int
connectionID, float cost);
Sets the base terrain cost for a
connection. This is the cost that will be used for all terrains
that don't have a cost set with rmSetConnectionTerrainCost. The
default cost for each terrain type is 1 if this is not called.
rmBuildConnection(int
connectionID);
Builds the given connection. Make
sure the areas are built first. RmBuildAllAreas does not include
connections.
rmAddConnectionToClass(int
connectionID, int classID);
Adds the connection to specified
class. Useful with constraints for areas or objects placed after
the connection.
bool rmAddConnectionConstraint(int
connectionID, int constraintID);
Add specified constraint to a connection.
Useful when the constraint is applied to the connection. As with
all constraints, the connection can only avoid areas or objects
that are placed before it is placed. |
|
|
|
|
|
|
|
|
|
5) OBJECTS
Objects include anything placed on a map that is
not a terrain. Buildings, units and resources are all objects. Objects
can even be collections of many different units. Just remember that
objects are always placed as clusters and sometimes it might be
easier to place 2 different objects than to add different types
of units to one object.
bool rmAddObjectDefToClass(int
objectDefID, int classID);
Add given object def to specified
class. Useful for using class constraints.
rmCreateObjectDef(string name);
Creates an object definition. Used to define a new object.
rmSetObjectDefMinDistance(int defID,
float dist);
Set the minimum distance for the object definition (in
meters).
rmSetObjectDefMaxDistance(int defID,
float dist);
Set the maximum distance for the object definition (in
meters). These distances apply to the object location. If the object
location equals a player’s location, then these are the min
and max from the player starting area (usually the Town Center).
A useful approach is to place the object at location 0.5, 0.5 (the
center of the map) and assign a maxDistance of half of the map.
See the subchapter "Map Grid" above for more explanations.
rmAddObjectDefItem(int
defID, string unitName, int count, float clusterDistance);
Add item to object definition. Objects
are defined in the random map script and can be named anything.
Units, on the other hand, use specific names from the game, such
as Villager Greek, Palm or TownCenter (the game’s
name for a Town Center). Cluster distance is only the maximum that
the object can be placed from the location, and is applied after
the min and max distance are applied (i.e. a maxDistance of 10 and
a clusterDistance of 5 could actually place an object 15 m from
the location). When placing a single unit, clusterDistance can be
0. If multiple units are added to the object, they should use a
clusterDistance > 0 or they will place on top of each other.
Here are two examples:
int farCowsID=rmCreateObjectDef("far
cows");
rmAddObjectDefItem(farCowsID, "cow", 1, 0.0);
int rock2ID=rmCreateObjectDef("rock2");
rmAddObjectDefItem(rock2ID, "rock limestone small",
1, 1.0);
rmAddObjectDefItem(rock2ID, "rock limestone sprite",
3, 3.0);
rmPlaceObjectDefAtLoc(int
defID, int playerID, float xFraction, float zFraction, long placeCount);
Place object definition at specific
location for given player. Placing objects this way is useful when
you don’t want to place them for every player, as in the case
where you place different units for different civilizations. You
can set int playerID to 0 to make sure nobody owns the object. Here’s
a nice shortcut:
for(i=0; <cNumberPlayers)
{
if(rmGetPlayerCiv(i) == rmGetCivID("Russians"))
rmPlaceObjectDefAtLoc(transportRussiansID, i, rmPlayerLocXFraction(i),
rmPlayerLocZFraction(i));
else if(rmGetPlayerCiv(i) == rmGetCivID("Germans"))
rmPlaceObjectDefAtLoc(transportGermansID, i, rmPlayerLocXFraction(i),
rmPlayerLocZFraction(i));
else if(rmGetPlayerCiv(i) == rmGetCivID("French"))
rmPlaceObjectDefAtLoc(transportFrenchID, i, rmPlayerLocXFraction(i),
rmPlayerLocZFraction(i));
}
rmPlaceObjectDefPerPlayer(int
defID, bool playerOwned, long placeCount);
Place object definition per player.
This command is often the fastest way to place objects, particularly
when compared to doinf for loops over player number. However, it
isn’t applicable when you don’t want to place the object
at least once for every player. Return playerOwned as false if you
want the object to belong to gaia.
rmPlaceObjectDefAtAreaLoc(int
defID, int playerID, int areaID, long placeCount);
Place object definition for the player
at the given area's location. The difference between this and placeObjectDefAtLoc
is that the latter needs an X, Z coordinate, while this command
just finds the area’s center location.
rmPlaceObjectDefInArea(int
defID, int playerID, int areaID, long placeCount);
Place object definition for the player
in the given area. Places the object randomly within the entire
area (as apposed to the center location).
rmPlaceObjectDefAtRandomAreaOfClass(int
defID, int playerID, int classID, long placeCount);
Place object definition for the player
at the location of a random area in the given class.
rmPlaceObjectDefInRandomAreaOfClass(int
defID, int playerID, int classID, long placeCount);
Place object definition for the player in a random area
in the given class. The difference between these two is that the
first uses the area’s location while the second just finds
a random location within an area. Return playerID as 0 to place
an object not owned.
rmGetNumberUnitsPlaced(int
objectDefID);
rmGetUnitPlaced(int objectDefID, int index);
rmGetUnitPlacedOfPlayer(int objectDefID, int playerID);
These three commands can be used
to detect failed cases of object placement. Perhaps you want to
try and place an object a second time with fewer constraints if
it fails the first time. Here is an example from Acropolis:
for(i=1; <cNumberPlayers)
{
int startingOutpostID=rmCreateObjectDef("Starting Outpost "+i);
int outpostRampConstraint=rmCreateCliffRampConstraint("onCliffRamp"+i,
rmAreaID("player"+i));
int outpostRampEdgeConstraint=rmCreateCliffEdgeMaxDistanceConstraint("nearCliffEdge"+i,
rmAreaID("player"+i), 2);
rmAddObjectDefItem(startingOutpostID, "Outpost", 1, 0.0);
rmAddObjectDefConstraint(startingOutpostID, avoidOutpost);
rmAddObjectDefConstraint(startingOutpostID, outpostRampConstraint);
rmAddObjectDefConstraint(startingOutpostID, outpostRampEdgeConstraint);
rmAddObjectDefToClass(startingOutpostID, classOutpost);
rmPlaceObjectDefInArea(startingOutpostID, i, rmAreaID("player"+i),
6);
/* backup to try again */
if(rmGetNumberUnitsPlaced(startingOutpostID) < 4)
{
int startingOutpostID2=rmCreateObjectDef("Less Optimal starting
Outpost"+i);
rmAddObjectDefItem(startingOutpostID2, "Outpost", 1, 0.0);
rmAddObjectDefConstraint(startingOutpostID2, avoidOutpost);
rmAddObjectDefConstraint(startingOutpostID2, outpostRampConstraint);
rmAddObjectDefToClass(startingOutpostID2, classOutpost);
rmPlaceObjectDefInArea(startingOutpostID2, i, rmAreaID("player"+i),
1);
}
}
rmSetIgnoreForceToGaia(bool
val);
Can be used to force any placed object, even resources, to belong
to a player. |
|
|
|
|
|
|
|
|
|
6) FAIR OBJECTS
These special commands are designed to place critical
resources, such as TownCenters and sometimes Silver Mines. They are
expensive and slow, so should not be used for many objects, but
can insure that objects that “must place” are present
on a map.
int rmAddFairLoc(string
unitName, bool forward, bool inside, float minPlayerDist, float
maxPlayerDist, float locDist, float edgeDist, bool playerArea, bool
teamArea);
Adds some fairLoc placement info.
For each fairLoc you specify the following settings: optional object
name to use (for check placement), forward or back (forward means
towards the enemy), inside or outside (inside means towards an ally),
min/max distance from the player, min distance from other locations,
and min distance from the edge of the map. You can also add regular
constraints to it. Specifying playerArea or teamArea forces the
location to that area, providing these areas are defined. Useful
for keeping a TownCenter on a player’s island rather than
across a river.
bool rmPlaceFairLocs();
Sets fairLoc placement locations.
After each fairLoc is created, you call rmPlaceFairLocs which will
calculate the actual positions, one per player per fairLoc. These
are just positions (or locations) so you can use them for anything
such as placing units or creating areas.
rmResetFairLocs();
Resets fairLoc placment info. Once
you are done with a set of fairLocs and want to create another set,
you should call rmResetFairLocs. This clears out any fairLocs you
previously added.
int rmGetNumberFairLocs(int
playerID);
Gets a player's number of fairLocs.
float rmFairLocXFraction(int playerID,
int index);
Gets a player's fairLoc x fraction.
float rmFairLocZFraction(int playerID,
int index);
Gets a player's fairLoc z fraction.
Fair Loc example for TownCenters:
id=rmAddFairLoc("TownCenter",
true, false, 70, 120, 60, 40); /* forward outside */
rmAddObjectDefConstraint(id, TownCenterAvoidImpassableLand);
rmAddObjectDefConstraint(id, playerConstraint);
if(rmPlaceFairLocs())
{
id=rmCreateObjectDef("far TownCenter2");
rmAddObjectDefItem(id, "TownCenter", 1, 0.0);
for(i=1; <cNumberPlayers)
{
for(j=0; <rmGetNumberFairLocs(i))
rmPlaceObjectDefAtLoc(id, i, rmFairLocXFraction(i, j), rmFairLocZFraction(i,
j), 1);
}
}
|
|
|
|
|
|
|
|
|
|
7) CONSTRAINTS
Constraints are used for areas, connections, and
objects. They make sure that objects avoid other objects, that objects
are placed near certain areas and similar restrictions.
int rmCreateBoxConstraint(string
name, float startX, float startZ, float endX, float endZ, float
bufferFraction);
Make a box constraint. Box constraints
are simply four line segments that subdivide the map. Creating a
box constraint can be used to keep areas or objects from getting
too close to the center
int rmCreateAreaOverlapConstraint(string
name, int areaID);
Make an area overlap constraint.
Prevents areas from overlapping.
int rmCreateAreaConstraint(string
name, int areaID);
Make a constraint that forces something
to remain within an area. You can’t specify distance, only
that an object or area must remain within the area.
int rmCreateAreaDistanceConstraint(string
name, int areaID, float distance);
Make an area distance constraint.
Specifies a minimum distance that an object or area can be from
an area. Useful for keeping resources from getting too close to
a TC or other resources.
int rmCreateAreaMaxDistanceConstraint(string
name, int areaID, float distance);
Make an area max distance constraint.
Specifies a maximum distance that an object or area can be from
an area. Useful for keeping resources from getting too far away
from a player.
int rmCreateEdgeConstraint(string
name, int areaID);
Make a constraint that forces something
to remain within an area's edge. Area edges are not large, so this
can be tricky.
int rmCreateEdgeDistanceConstraint(string
name, int areaID, float distance);
Make an area edge distance constraint.
The minimum distance an area or object can be to an edge.
int rmCreateEdgeMaxDistanceConstraint(string
name, int areaID, float distance);
Make an area edge max distance constraint.
The maximum distance an area or object can be from an edge.
int rmCreateCliffEdgeConstraint(string
name, int areaID);
Make a constraint that forces something
to remain within an area's cliff edge.
int rmCreateCliffEdgeDistanceConstraint(string
name, int areaID, float distance);
Make an area cliff edge distance constraint.
int rmCreateCliffEdgeMaxDistanceConstraint(string
name, int areaID, float distance);
Make an area cliff edge max distance constraint. Cliff
edges are handled differently from other areas and need their own
constraint. You are often better off telling an object or area to
avoid impassable land.
int rmCreateCliffRampConstraint(string
name, int areaID);
Make a constraint that forces something
to remain within an area's cliff ramp edge.
int rmCreateCliffRampDistanceConstraint(string
name, int areaID, float distance);
Make an area cliff ramp edge distance constraint.
int rmCreateCliffRampMaxDistanceConstraint(string
name, int areaID, float distance);
Make an area cliff ramp edge max distance constraint. Ramps
are the areas of cliffs that have pathable terrain, typically for
reaching the top of a cliff. Acropolis has ramps that have towers
on the edges of the ramps.
int rmCreateClassDistanceConstraint(string
name, int classID, float distance);
Make a class distance constraint.
Once you define a class of objects or areas, you can then set a
constraint to it.
- int playerConstraint=rmCreateClassDistanceConstraint("stay
away from players", classPlayer, 20);
int rmCreateTypeDistanceConstraint(string
name, int classID, float distance);
Make a type distance constraint.
Types can be specific units, such as Chicken, or abstract types,
such as Huntable.
int rmCreateTerrainDistanceConstraint(string
name, string type, bool passable, float distance);
Make a constraint to avoid terrain
with certain a passability. This is a useful constraint because
you can have land objects avoid impassable land, such as water or
cliffs, or have water objects avoid the shore.
int avoidImpassableLand=rmCreateTerrainDistanceConstraint("avoid
impassable land", "land", false, 10.0);
int rmCreateTerrainMaxDistanceConstraint(string
name, string type, bool passable, float distance);
Make a constraint to be close to
terrain with certain a passability. Useful for keeping seaweed or
transports in the water but near a beach, or Hippos or Crocodiles
on the beach near the water.
bool rmAddAreaConstraint(int
areaID, int constraintID);
Add specified constraint to an area.
Once you have a constraint defined, this is how you make an area
adhere to it.
bool rmAddFairLocConstraint(int
fairLocID, int constraintID);
Add specified constraint to a fairLoc
placement. Once you have a constraint defined, this is how you make
a Fair Loc adhere to it.
bool rmAddObjectDefConstraint(int
defID, int constraintID);
Add specified constraint to given
object def. Once you have a constraint defined, this is how you
make an object adhere to it.
rmConstraintID(string
name);
Gets constraint ID for given constraint
name. |
|
|
|
|
|
|
|
|
|
8) TRADE ROUTES
Trade routes are a new feature in the Age series. You create a trade route by placing waypoints for the paths. Later, you place "SocketTradeRoute" objects to the trade route. Trade routes should be created and built straight after the map terrain and player areas were created. All additional objects such as resources, forests, etc. should be placed with a trade route constraint to avoid the routes. Also, make sure you always place waypoints on connected land.
int rmCreateTradeRouteDistanceConstraint(string name, float distance);
Creates an area constraint of all trade routes to avoid them with distance in meters.
int rmCreateTradeRoute();
Creates a trade route. Like all other rm creations for objects and terrain it is of type integer and can be defined like:
int tradeRoute1ID = rmCreateTradeRoute();
bool rmAddTradeRouteWaypoint(int tradeRouteID, float startX, float startZ);
Adds a waypoint to the tradeRouteID with the startX, startZ coordinates (0.0-1.0) of the map.
bool rmAddRandomTradeRouteWaypoints(int tradeRouteID, float startX, float startZ, float variationX, float variationZ);
Adds a random waypoint to the tradeRouteID with the startX, startZ coordinates (0.0-1.0) of the map and a random variation variationX, variationZ in meters. If you want to make your trade route as a loop or circle then your last added waypoint should have the same coordinates as your first "rmAddTradeRouteWaypoint".
bool rmBuildTradeRoute(int tradeRouteID, string texture);
Builds the trade route defined before. The texture can be "dirt", as example.
vector rmGetTradeRouteWayPoint(int tradeRouteID, float distance);
Gets the location for a trade route socket on the trade route. The distance is the length of the trade route in percent, 0.0 is trade route starting point, 1.0 is the end of the route. This command will be appied after the tradeRouteID was created and built.
|
|
|
|
|
|
|
|
|
|
9) TRIGGERS
These commands are used to incorporate triggers
from the scenario editor into a random map script. Triggers can
get complicated quickly and can also create an unfair map, so they
must be used with caution. A full explanation of all the triggers
is beyond the scope of this article, but you can generate a random
map script within the scenario editor to see how the triggers are
created-just look at the triggers as if you had set them up manually.
You can also look at the "C:\...\Age of Empires III\trigger\typetest.xml"
file for help with debugging RMS triggers.
Furthermore, a trigtemp.xs file is generated every time when loading
your RMS into the map editor. It will show you all triggers you
set into your random map. This file is in xs-trigger code, which
looks a bit different from the RMS file. You can find this file
in the directory:
"C:\...\My Documents\My Games\Age of Empires 3\trigger\trigtemp.xs"
.
rmCreateTrigger(string
triggerName);
Used to create a new trigger. Example:
rmCreateTrigger("MyTrigger1");
rmSwitchToTrigger(int
triggerID);
This command is useful for setting up triggers by player. You need
to define all the triggers first, but then you can switch to different
ones to specify their conditions and effects. Example:
rmSwitchToTrigger(rmTriggerID("MyTrigger1"));
rmTriggerID(string
triggerName);
Like areas and objects, triggers must be defined before they can
be used.
rmSetTriggerPriority(int
priority);
Sets the trigger priority. priority has values between 0 (low) and 4 (highest).
rmSetTriggerActive(bool active);
Sets the activity of the trigger straight after the map was loaded. If you want to fire this trigger later in game select "false", else "true".
rmSetTriggerRunImmediately(bool runImmediately);
runImmediately should always be "true" if you use the trigger for firering cinematics or sounds.
rmSetTriggerLoop(bool loop);
Sets the repetition mode of the trigger. Be careful using loops, since they can easily run mad and stop. Better use 2 triggers instead and fire then towards each other, using the "Fire Event" trigger effect.
rmAddTriggerCondition(string
conditionType);
rmSetTriggerConditionParam(string paramName, string value, bool
add);
rmSetTriggerConditionParamInt(string paramName, int value, bool
add);
rmSetTriggerConditionParamFloat(string paramName, float value, bool
add);
rmSetTriggerConditionParamArmy(string paramName, int playerID, int
armyID, bool add);
Refer to C:\...\Age of Empires III\trigger\typetest.xml
for lists of available conditions and their parameters.
rmAddTriggerEffect(string
effectType);
rmSetTriggerEffectParam(string paramName, string value, bool add);
rmSetTriggerEffectParamInt(string paramName, int value, bool add);
rmSetTriggerEffectParamFloat(string paramName, float value, bool
add);
Refer to C:\...\Age of Empires III\trigger\typetest.xml
for lists of available effects and their parameters.
rmSetTriggerEffectParamArmy(string
paramName, int playerID, int armyID, bool add);
rmCreateArmy(int playerID, string armyName);
rmAddUnitsToArmy(int playerID, int armyID, int objectDefID);
Triggers that affect armies require
that the armies be defined first.
rmSetVCFile(string filename);
You can set up alternate victory
condition files for your RMS. This feature is fairly complex and
should only be attempted by an advanced user.
How to insert triggers in your script:
Order:
1. It is recommended to add all triggers at the end of the script since some triggers need parameters which were already defined and placed on the map. Add all triggers straight before the last "}" bracket in the xs script.
2. First create ALL triggers you want to insert in your script, then switch to each trigger separately and insert the conditions, effects and its parameters. This is very important since you cannot fire a trigger you have not created before, and the map will not load.
Example setup:
// Triggers:
for (j=1; <=14)
{
rmCreateTrigger("MyTrigger"+j);
}
/* above there are now 14 triggers created with the names "MyTrigger1", "MyTrigger2"..."MyTrigger14" */
rmSwitchToTrigger(rmTriggerID("MyTrigger1"));
for(i=1; <= cNumberNonGaiaPlayers){
rmAddTriggerEffect("Grant Resources");
rmSetTriggerEffectParamInt("PlayerID",i);
rmSetTriggerEffectParam("ResName","Food");
rmSetTriggerEffectParamInt("Amount",1000);
}
rmSetTriggerPriority(4);
rmSetTriggerActive(true);
rmSetTriggerRunImmediately(true);
rmSetTriggerLoop(false);
rmSwitchToTrigger(rmTriggerID("MyTrigger2"));
...
...
rmSwitchToTrigger(rmTriggerID( "MyTrigger14"));
...
|
|
|
|
|
|
|
|
|
|
DISCLAMER
Some of the text above, especially Chapter II, is
part of the Random Map Help File Document ©
2002, Microsoft
Corporation of the Age of Mythology game. All rights reserved.
If you have this game, you can find this file under the name AOM random map help
file.rtf in your C:\...\Age of Mythology\docs\ directory.
However, this tutorial here contains much more text and pictures, especially for beginners
in programming/scripting!
Important: Age of Empires 3
allows you to create your own random map scripts. You may share
these custom random map scripts for the purposes of gameplay, but
you may not sell or make other commercial uses of the custom random
map scripts. Microsoft®
reserves all other rights to the editors and files.
|
|
|
|
|
|
|
|
|
|
COMMENTS
This tutorial was already read by
unique visitors.
You
found no answers to your questions here?
Just post it in our tutorial thread of our
|
|
|
Age of Empires 3™ is a game by Ensemble
Studios / Microsoft.
|
|
|
|
|
|
|
|
|
|