Entities are objects in the game world that participate in gameplay, such as units, buildings, and resource nodes (as opposed to purely decorative objects such as grass patches). Each entity can have a number of properties that are available to the game engine and game scripts. Entity properties are specified in XML files in binaries/data/mods/official/entities and its subdirectories. Because many objects have similar properties (for example, all infantry tend to have the same speed, all spearmen tend to have the same armour, etc), the entity files use inheritance to let you import traits from a "template" and just modify the ones you wish to change. Each file has a parent specified in the <Entity> tag and inherits that parent's properties, then any properties set or modified in its own XML file are applied on top of that.
Currently, the template_* entities in binaries/data/mods/official/entities, such as template_unit_infantry_spearman, hold most of the traits, and the civilization-specific entities in binaries/data/mods/official/entities/[civ], such as celt_infantry_spearman, just inherit from them.
The root parent for most entities is template_entity_full. Some very simple entities, such as trees, also have template_entity_quasi, which defines fewer event handlers and properties and is therefore more efficient to load.
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?> <!-- Beware! The string "false" maps to 'true'. The only string that maps to 'false' is "". --> <Entity parent="template_entity" > <Traits extant="true" corpse="template_corpse" > <Id> <Internal_Only /> <Generic>Infantry Spearman</Generic> <Specific>Caldeer</Specific> <Icon>sheet_civ</Icon> <Icon_Cell>42</Icon_Cell> <Classes>Unit, Infantry, Melee, Organic, CitizenSoldier, Male</Classes> <Civ>Celts</Civ> <Rollover>blah</Rollover> <History>blah</History> <Personal /> </Id> <AI> <Behaviour>Support</Behaviour> <Stance> <List> <Aggress /> <Defend /> <Avoid /> <Stand /> <Hold /> </List> <Curr>Defend</Curr> </Stance> </AI> <Anchor> <type>Ground</type> </Anchor> <Armour> <crush>2.0</crush> <hack>1.0</hack> <pierce>0.0</pierce> </Armour> <Audio> <path>audio/voices/hellenes/soldier</path> </Audio> <Creation> <Time>20</Time> <Resource> <Food>2</Food> <Wood>1</Wood> <Stone>3</Stone> </Resource> </Creation> <Footprint> <radius>1.5</radius> <width>3.0</width> <depth>3.0</depth> <height>7.5</height> </Footprint> <Formation> <Category>Melee</Category> <Curr>Loose</Curr> <List> <Loose/> <Box/> <Column_C/> <Line_C/> <Column_O/> <Line_O/> <Flank/> <Skirmish/> <Wedge/> <Testudo/> <Phalanx/> </List> </Formation> <Garrison> <max>6</max> </Garrison> <Health> <Bar_Height>-1.0</Bar_Height> <Bar_Size>20</Bar_Size> <Bar_Width>2.0</Bar_Width> <Regen_Rate>5.0</Regen_Rate> <Regen_Start>15.0</Regen_Start> <Decay_Rate>5.0</Decay_Rate> <Border_Height>7</Border_Height> <Border_Width>28</Border_Width> <Border_Name>bar.dds</Border_Name> </Health> <Stamina> <Bar_Height>-1.0</Bar_Height> <Bar_Size>20</Bar_Size> <Bar_Width>2.0</Bar_Width> <Border_Height>7</Border_Height> <Border_Width>28</Border_Width> <Border_Name>bar.dds</Border_Name> </Stamina> <Loot> <XP>200</XP> <Food>1</Food> <Wood>1</Wood> <Stone>1</Stone> <Ore>1</Ore> </Loot> <MiniMap> <type>Unit</type> <red>100</red> <green>200</green> <blue>50</blue> </MiniMap> <Population> <Add>1</Add> <Rem>1</Rem> </Population> <Promotion> <req>900</req> <newentity>bob</newentity> (only if you want to do something other than normal rank progression) </Promotion> <Rank> <Width>7.0</Width> <Size>17</Size> <Height>-1.0</Height> <Name></Name> </Rank> <Supply> <max>50</max> <type>food</type> <subtype>fruit</subtype> </Supply> <Vision> <LOS>4</LOS> <Permanent /> </Vision> <Auras> <Courage> <Radius>20</Radius> <Bonus>5</Bonus> </Courage> <Fear> <Radius>20</Radius> <Bonus>5</Bonus> </Fear> <Infidelity> <Radius>20</Radius> <Time>0</Time> </Infidelity> <Dropsite> <Radius>50</Radius> <Types> <Food/> <Wood/> <Stone/> <Ore/> </Types> </Dropsite> <Heal> <Radius>30</Radius> <Rate>5</Rate> <Speed>2000</Speed> </Heal> <Trample> <Radius>8</Radius> <Speed>1000</Speed> <Duration>3</Duration> <Damage>20.0</Damage> <Crush>0.0</Crush> <Hack>0.5</Hack> <Pierce>0.5</Pierce> </Trample> </Auras> <Flank_Penalty> <Sectors>6</Sectors> <Value>.2</Value> </Flank_Penalty> <Pitch> <Max_Actor>0.03</Max_Actor> <Min_Actor>-0.03</Min_Actor> <Divs>9</Divs> <Value>.2</Value> </Pitch> </Traits> <Actor>structures/celts/civil_centre.xml</Actor> <Actions> <Attack> <Crush>0.0</Crush> <Hack>5.0</Hack> <Pierce>5.0</Pierce> <Range>2.0</Range> <Speed>1500</Speed> </Attack> <Create> <List> <StructCiv>civil_centre;farmstead;house;mill;market;temple</StructCiv> <StructMil>barracks;dock;fortress;scout_tower;wall;wall_gate;wall_tower</StructMil> <Unit>infantry_swordsman_b;infantry_spearman_b;infantry_javelinist_b; infantry_archer_b;infantry_slinger_b;cavalry_swordsman_b; cavalry_spearman_b;cavalry_javelinist_b;cavalry_archer_b; female_citizen</Unit> <Tech>bob</Tech> </List> <Speed>100</Speed> </Create> <Gather> <Resource> <Food> <Meat>3000</Meat> <Fruit>3000</Fruit> <Grain>3000</Grain> <Fish>3000</Fish> </Food> <Wood>3000</Wood> <Stone>3000</Stone> <Ore>3000</Ore> </Resource> <Range>2.0</Range> </Gather> <Loot> <Resources /> <XP /> </Loot> <Move> <Speed>5.0</Speed> <TurningRadius>0.0</TurningRadius> <Run> <Speed>10.0</Speed> <Range>5.5</Range> <RangeMin>2.0</RangeMin> <Regen_Rate>10.0</Regen_Rate> <Decay_Rate>5.0</Decay_Rate> </Run> </Move> <Patrol /> </Actions> <Script File="entities/template_entity_script.js" /> <Event On="Initialize" Function="entityInit" /> <Event On="Death" Function="entityDeath" /> <Event On="TargetChanged" Function ="entityEvent_TargetChanged" /> <Event On="PrepareOrder" Function="entityEvent_PrepareOrder" /> <Event On="Attack" Function="entityEvent_Attack_Melee" /> <Event On="Attack" Function="entityEvent_Attack_Ranged" /> <Event On="Gather" Function="entityEvent_Gather" /> <Event On="StartProduction" Function="entityStartProduction" /> <Event On="CancelProduction" Function="entityCancelProduction" /> <Event On="FinishProduction" Function="entityFinishProduction" /> </Entity>
Set to "true" for the top-most entity in the hierarchy (i.e. template_entity). Otherwise defaults to false.
The entity is replaced with this entity when it dies. (For example, a dead tree, a corpse, or a broken siege weapon.)
TODO: Make corpse entities disappear from the map after a period of time. This will likely require properties to indicate the method of removal (fade / sink into the ground) and the delay period before removal occurs should be available to JS for the graphics options menu.
Indicates the name of the entity from which this entity inherits any unspecified properties. If specified, any attributes -- including Events -- that are not defined in this XML object will be inherited from the specified parent object (which in turn inherits from its own parent, and so forth).
This makes the declaration of properties more efficient, since shared properties can simply bem declared just once and propagate down the entity tree to those that share them.
This inheritance tree means that we can be very efficient about our XML content. For example, we can express the events of upgrading, getting a new appearance on certain ranks, etc, in a single generic Citizen Soldier entity (which in turn could inherit even more basic building blocks from the grandparent), which each Citizen Soldier inherits. Where they differ from the norm, each unit then just specifies any unique attributes.
There's less repetition of data, global changes are a snap (make all infantry faster? No problem), the XMLs are smaller and load faster too.
Group object for "passive" properties: innate attributes that do not require any action on the part of the entity and little to no intervention from the player. Here we have information such as the entity's health, armour, and vision, what units can garrison in it, and how much it costs to create one. It also covers abilities and effects that occur automatically.
These properties identify and classify an entity. Attributes include the unit's name, icon, class properties, tooltip rollover, historical information, and civilisation ownership.
Entity action that is controlled by the computer is required to have an Artificial Intelligence attached to it. TODO: These properties are distinctly the product of guesswork and speculation based on other games ... The precise content will ultimately depend on what's needed for unit AI.
This trait simply indicates the manner in which this entity should be attached to the terrain plane.
Armour is an attribute that absorbs/deflects attack damage.
These attributes handle the behaviours of sounds associated with this entity. TODO: The audio properties will be revised/implemented depending on those needed by the new sound requirements.
An "Aura" is a passive ability that affects other entities within a certain radius of the entity with an aura. It usually changes the statistics of affected units.
An entity with this trait can be created (constructed, trained) by an entity that has it in its Actions.Create.List. The Traits.Creation attributes define the requirements for creation of this entity (cost, time, pre-requisites, etc).
These attributes define the "bounding box" of an entity (the area that it occupies). This is used in collision detection, for example. This area is also highlighted when the unit is selected.
Indicates how this entity is used in formations (the role it takes in a formation, and which formations it can use).
If an entity has this trait, specified entities are able to garrison (be contained) within it.
An object with this trait has a certain number of hitpoints that sustains him.
This trait determines the quantity of resources automatically added to an opponent's Resource Pool, and/or the experience points he receives, when he destroys this entity.
This attribute specifies how the entity appears on the Mini-Map.
This object affects population in some way.
An entity with this trait can accumulate experience points (see Traits.Loot.XP) and eventually accumulate enough to gain a promotion. This basically replaces him with a more advanced version of the unit.
Attributes of the rank bar.
An object with this attribute contains some kind of resource which can be harvested (generically known as "Supply").
An entity with this attribute can be replaced with another entity under certain conditions. Not yet implemented.
The object is able to reveal areas of the map within his sight radius, lifting Shroud of Darkness and Fog of War.
Group object for "active" properties (those that relate to actions that the entity can take ... How much damage it inflicts when it attacks, its gather rate, its ability to heal other units, construction rate, and so forth.
Actions are abilities that the entity can use, such as attacking an opponent, gathering a resource, or patrolling an area. These in turn have additional attributes that further define the behaviour of the action (such as how much damage the entity inflicts in attacks, and against what armour types, and how quickly the entity moves when performing a move action).
If there is no action assigned to the entity, then we can assume that it doesn't have that ability. Actions typically have a cursor and GUI button that can be used to command the entity to perform this action.
An object with this ability is able to attack opponents or wild animals.
An entity with the Barter action has a special market interface to buy and sell resources in exchange for other resources (used by the Market). Not yet implemented.
An entity with the Create action is able to create (train, construct) other entities. For details about an entity's prerequisites and costs for creation, see the Traits.Creation trait.
Has an Escort GUI button (also performed by right-clicking a friendly entity). When commanded to Escort, the entity remains within close range of the target until directed otherwise, following and defending it. Not yet implemented.
An object with this ability can gather resources from some source of Supply and transfer it to the player's Resource Pool.
This is an action that's only used by animals. It simply causes the animal to move to the specified location and initiate a grazing animation there, to add more variety to its wander AI. Not yet implemented.
The Heal Action is used by the Healer unit to regenerate the health of the player's organic units. While the action is applied to a viable damaged target, his hitpoints increase until restored to maximum, while the entity performs his "Heal" animation. Not yet implemented.
The entity has alternating Lock and Unlock buttons in the GUI, which can be used to force its gate open or closed (unlocked gates stay open if a player entity is adjacent, but close to deny entry to the enemy). Not yet implemented.
This action allows a unit to retrieve an opponent's "loot" and experience points when he kills them (see Traits.Loot).
An object with this ability can be commanded to move from one location to another.
Has a Patrol button in the GUI. Once put on a patrol route, entity(s) repeatedly moves from one extreme limit back to the other, back and forth. There is no limit to the number of Patrol routes that may be set up.
The Repair Action is used by econ units (Citizen Soldiers) to regenerate the health of the player's mechanical mobile and non-mobile entities (structures, ships, siege weapons). While the action is applied to a viable damaged target, his hitpoints increase until restored to maximum, while the entity performs his "Repair" animation. Not yet implemented.
The entity has a Scout command in the GUI. Once put into Scout mode, the entity moves methodically back and forth through an enemy's Civ Territory in ever expanding arcs to reveal the terrain/entities there. Not yet implemented.
An entity with this ability is able to generate supply (resources) by travelling on a patrol route between two specified structures. (Note: The quantity it can carry and the type of resource is specified by the entity's Traits.Supply attributes.) Not yet implemented.
Events are the conditions under which effects occur. An event is true when an entity enters a certain state. Generally the event jumps in just before the actual event takes place. For example, logic written for the Death event occurs just before the unit is about to die.
See below for a listing of all intended events.
We want to hardcode the bare minimum of game logic into the engine. In designing these XML attributes, we strive to keep it flexible so that designers can easily adjust the nature and behaviour of the game's units and other objects with minimal bugging of overworked programmers.
However, trying to create an XML attribute for every contingency that could ever be required ("BelchesGreenFire=true") is an exercise in futility.
This allows custom logic to be written for an entity and encapsulated with its data, instead of having a special XML attribute that flags some logic which we assume to be "handled elsewhere".
Like the GUI, these commands could also call a function written in another file (good for reuse of code) or an engine function.
Before we describe how events can be used, a couple of warnings:
- Allowing script-based game logic gives modders and developers great power.. But that same power could be used to make 0 A.D. a hacker's paradise. We need to ensure that each player's data in a multiplayer game is sufficiently validated so that a player can't alter the script of his local version to give himself an unfair advantage (eg give himself resources every tick). Client/server architecture, out-of-sync checks, hashing, checksums, we need to use any means necessary to ensure modders have the freedom to adapt game data, while ensuring that players must have the same data version in order to play together.
- Wrap logic in a function wherever possible and call that, so that it can be called by other entities if needed.
- Entities can inherit attributes from each other, including event logic. For example, the behaviour for unit upgrading could be written once, in the generic-citizen-soldier entity. All Citizen Soldiers could then inherit from this entity, gaining the upgrade script automatically.
- Events should generally be used for one-offs (eg raising/lowering of sails) or complicated exceptions (eg auras). For example, it isn't worth scripting the upgrade system into each Citizen Soldier's XML because so many units make use of it ... that would probably double the size of the entity files. Even wrapped in a function, it means more repetition of the same commands, which all have to be parsed and processed.
This list will grow as we implement and hammer down the details of game logic (eg formations, repairing, town bell, AI) ... It's easy for programmers to add additional events, but remember to minimise the amount of script that needs to be executed every frame. For example, it's less costly to check XYZBegin and XYZCancel than check XYZ every frame.
All the relevant data for an event will be copied to a special 'ev' object, allowing easy passing of event information to a script (i.e., for TakesDamage, there'd be ev.inflictor, ev.damage.crush, ev.damage.pierce, ev.range and so on).
Currently planned events are:
Occurs when the entity is commanded to attack another entity.
When an entity has remained in aura radius for the length of time specified by Aura.Time. Not yet implemented.
When an entity enters the aura radius of this entity. Not yet implemented.
When an entity leaves the aura radius of this entity. Not yet implemented.
Occurs when production of an entity has been prematurely cancelled (usually deliberately by the user). This is the time to refund any spent resources, remove the item for the GUI queue, etc.
Occurs when creation (training/building/researching) of this entity has started. Not yet implemented.
Occurs when creation (training/building/researching) of this entity has been cancelled by the user. Not yet implemented.
Occurs when creation (training/building/researching) of this entity has finished. Not yet implemented.
Occurs immediately before the entity is destroyed.
Occurs when a production in the entity's queue completes its production time. At this point, the new entity should be spawned or the technology applied.
Occurs when all garrison slots are empty. Not yet implemented.
Occurs when an entity is garrisoned in this entity. Not yet implemented.
Occurs when an entity is ungarrisoned from this entity. Not yet implemented.
Occurs when all garrison slots are occupied. Not yet implemented.
Occurs when the entity is commanded to Gather supply from another entity.
Occurs when the player places the cursor over the entity. Not yet implemented.
Occurs when the entity first enters the world.
Occurs when the player moves the cursor off of the entity. Not yet implemented.
When an entity enters the sight radius of this entity. Not yet implemented.
When an entity leaves the sight radius of this entity. Not yet implemented.
Occurs when the player clicks on the entity. Not yet implemented.
Occurs when the player releases the mouse button, or moves the cursor off the entity. Not yet implemented.
Occurs when the entity has been given a command and is about to start carrying it out. Not yet implemented.
Occurs when the user cancels the order. Not yet implemented.
Occurs when the entity has finished an order. Not yet implemented.
Occurs when the entity is given a new order.
Occurs when an entity occupies a socket (prop point) in this entity. Not yet implemented.
Occurs when all sockets are empty. Not yet implemented.
Occurs when an entity occupies a socket (prop point) in this entity. Not yet implemented.
Occurs when all sockets are occupied. Not yet implemented.
Dispatched by ORDER_PRODUCE and occurs when the entity has started producing another entity which has been added to its queue. Resources and population space should be subtracted at this time.
- evt.name: Name of the production, usually its entity name.
- evt.productionType: int indicating the type of production (research, training, etc).
- evt.time: The time it will take to complete the production. The event handler must set this value for each production so the game knows how long it should last. (Usually calculated from the entity's property Traits.Creation properties, plus any build speed bonuses, etc).
- evt.preventDefault(): Prevent this production from starting (e.g. if the player doesn't have enough resources for it).
Occurs when all Supply has been gathered from an entity. Not yet implemented.
Occurs when an entity has reached its maximum Supply. Not yet implemented.
Occurs when the entity is attacked by another entity.
Occurs when the entity is given a new target.
Occurs when an entity moves over the surface of it. Typically only used by terrains. Not yet implemented.
Specifies a JS function that is executed when the event occurs. Note that the .js file that contains the function must first be included in the entity XML's scope using the <Script File="" /> attribute.
A number of entity properties are not specified in the entity XML files, but are provided by the game for each entity to allow scripts to interact with it.
The variable entity.last_combat_time is used by the game to decide when to start health regeneration. It should be set by the scripts for any millitary events (when the entity is either attacking or receiving damage).
The production queue of a given entity can be accessed through entity.production_queue. It provides the following fields and methods:
- entity.production_queue.length: The number of items in the queue (read-only).
- entity.production_queue.get(i): Get the production item at a particular index in the queue. A production item has the following properties (all read-only):
- item.type: The production type, as in the event above.
- item.name: The production name, as in the event above.
- item.totalTime: How long the production takes to complete.
- item.elapsedTime: How much time the entity has spent "working" on this production (i.e. with it at the head of its queue). When elapsedTime reaches totalTime, the production is complete and a FinishProduction event is fired.
- entity.production_queue.cancel(i): Cancel production of the item at a particular index in the queue. This will cause a CancelProduction event to be fired, where the entity can grant back resources, etc to the player.