Version 15 (modified by Radagast, 10 years ago) ( diff )

--

Triggers

This document will describe how to add triggers to maps, how to program triggers, and what the possibilities of triggers are.

We will try to give a lot of examples, that should be easy to copy-paste and modify where needed. But this isn't meant as an introductory course to JavaScript. For a JavaScript reference, it's best to look at the Mozilla Development Network (MDN). For creating and editing JavaScript files, it's best to use a decent text editor. Syntax highlighting is a must, so Notepad++ on Windows should be sufficient.

Modifying your map

For now, Atlas isn't very prepared for triggers. So we have no GUI to edit triggers (obviously), but also no GUI to add trigger scripts to your map. This means you need to edit your map data. For scenarios and skirmish maps, this map data is a JSON part in the map.xml file (see example). For random maps it's in the map.json file directly (see example). (TODO: add link to committed triggers examples).

In this JSON part of the data, you define an array of loaded trigger scripts. You can define multiple more general helper scripts, and your custom script(s).

{
  "CircularMap": true,
  "Description": "The first steps of playing 0 A.D.",
  "...": "...",
  "TriggerScripts": [
    "scripts/TriggerHelper.js",
    "skirmishes/basic_tutorial.js"
  ]
}

The root of these paths is in modName/maps/, and it's custom to put general scripts in the scripts directory, and map-specific scripts next to the existing map files.

Of course, you need to make sure these files exist by creating them with your favourite text editor. You can write a small warning in the script, so you know your script is executed

warn("My first trigger script");

This should show a yellow warning in the top left of your screen when the map loading is finished.

Tip: See Logging for more logging options, and use uneval(data) to transform any JS data into human-readable text.

Writing the trigger scripts

Trigger scripts are executed when the map is completely loaded, but right before the game starts. So you can already access all existing entities and their properties s.a. position, owner, ... This is the right time to bind actions to certain events, a.k.a. registering triggers.

Action functions

Actions are regular JavaScript functions, registered under the Trigger prototype. Actions called by triggers always receive a data object with more information about the event that just happened. How the data looks depends on the actual event. See the table below.

Trigger.prototype.MyAction = function(data)
{
    // Do Something
};

Note that these functions only exist, and aren't executed by default. They're only executed when bound to a trigger, or when a different function in your trigger calls it. Chaining functions like the following example is possible, and a good way to split up complicated logic.

Trigger.prototype.MyAction = function(data)
{
    var entities = this.GetRelevantEntities(data.player);
    // Do something with those entities
};

Trigger.prototype.GetRelevantEntities = function(player)
{
    // calculate something
    return relevantEntities;
};

The this keyword refers to the Trigger object anything defined under the prototype + additional data, which makes it easy to use.

Keeping track of data

Quite often, you'll want to keep track of data that changes throughout the game (number of units killed, number of times a certain trigger is executed, ...). As this data changes during the game, it's important that the last saved data is also loaded when loading a saved game, or when a player rejoins in a multiplayer game. Keeping track of data also happens under the Trigger object, so with the this keyword.

Data initialisation

Storing data you want to keep track of should happen at the end of you script, and be done in the Trigger component. By doing so, you can use the data later on (in the Trigger prototype functions) with the this keyword.

// get the cmpTrigger object (the trigger component)
var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);

// Add data to it
cmpTrigger.executedTriggers = 0;
cmpTrigger.killedUnits = 0;
cmpTrigger.state = "initialising";

Data usage

After data initialisation, you can just use it in other trigger functions.

Trigger.prototype.MyAction = function(data)
{
    this.executedTriggers++; // will enlarge the counter with one
    this.killedUnits += TriggerHelper.GetKilledEntities(data); // execute a function with separate logic, and add it to a counter
}

Note: both the Trigger prototype (e.g. Trigger.prototype.whatIWantedToSave) and the data you add directly to this (this.whatIWantedToSave) can be accessed via this. But only the data added directly to this will be saved in a saved game (e.g. this.myVar will be saved but Trigger.protoype.myVar not!). So although it's possible to add other stuff to the prototype than just functions, it shouldn't change throughout the game. Also note that functions can't be saved in saved games. So in short, functions should always be defined under the prototype, and never change.

Registering triggers

When you have your actions made, you need to bind them to a certain event. This is done via the predefined RegisterTrigger function. Triggers can be enabled in any method, but you usually need to enable some at the start of the game. You can use the following schema:

// get the cmpTrigger object
var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);

// register the trigger directly
var myData = {"enabled": true};
cmpTrigger.RegisterTrigger("OnPlayerCommand", "MyAction", myData);

The first parameter of the trigger is the event on which it will be executed. In the example, the trigger will be executed for every player command. See the table below for a list of all possible events. The second parameter is the name of the action, which has to be defined under the Trigger prototype. And the third part is a data object. For most triggers, this data will be just enabled = true or false. But for more complicated triggers (range triggers, time triggers, ...) this data can contain other elements, such as the the distance of the range trigger, or the interval of the timer. Again, see the data in the table below.

The combination of event and action name must be unique. This combination can be used to enable and disable triggers. Registering a trigger twice isn't possible, and tou will be warned when you do that.

When you have your first triggers registered, they can fire actions, but actions can again register new triggers. As this time, it happens within the Trigger prototype, you can access cmpTrigger simply with this.

Trigger.prototype.MyTriggerRegisteringAction = function(data)
{
    var myData = {"enabled": true};
    this.RegisterTrigger("OnPlayerCommand", "MySecondAction", myData);
};

Enabling and disabling triggers in action functions happens as shown:

Trigger.prototype.MySecondAction = function(data)
{
    this.EnableTrigger("OnPlayerCommand", "MyAction");
    // OR
    this.DisableTrigger("OnPlayerCommand", "MyAction");
};

You can enable and disable triggers as often as you want.

Reference Table

Event name

Accepted Data format

Returned data format

Notes

OnStructureBuilt

  {"enabled": bool}
  {"building": constructedBuilding}

Happens when a foundation gets finished or a building gets completely repaired

OnConstructionStarted

  {"enabled": bool}
  {
    "foundation": entityId,
    "template": this.templateName
  }

Happens when the building of the foundation starts

OnTrainingFinished

  {"enabled": bool}
  {
    "entities": [entityId],
    "owner": playerId,
    "metadata": metadata
  }

Happens when training of some units is finished

OnTrainingQueued

  {"enabled": bool}
  {
    "playerid": playerId,
    "unitTemplate": templateName,
    "count": number,
    "metadata": metadata,
    "trainerEntity": entityId
  }

OnResearchFinished

  {"enabled": bool}
  {
    "player": playerId,
    "tech": templateName
  }

OnResearchQueued

  {"enabled": bool}
  {
    "playerid": playerId,
    "technologyTemplate": templateName,
    "researcherEntity": entityId
  }

OnOwnershipChanged

  {"enabled": bool}
  {
    "entity": entityId,
    "from": playerId,
    "to": playerId
  }

This event is also fired when a unit/building is created or destroyed. In that case, the ownership goes from (resp. to) the invalid player entity -1.

OnPlayerCommand

  {"enabled": bool}
  {
    "player": playerId,
    "cmd": unitCommand
  }

Any command a player sends. The "cmd" part of a command always have a type, and per type can have different other atributes. Check the documentation in the source file (Commands.js) for documentation

OnInterval

  {
    "delay": number,
    "interval": number,
    "enabled": bool
  }

TODO

OnRange

  {
    "entities": [entityId],
    "players": [playerId],
    "minRange": number,
    "maxRange": number,
    "requiredComponent": componentId,
    "enabled": bool
  }
  {
    "added": [entityId],
    "removed": [entityId],
    "currenctCollection": [entityId]
  }
Note: See TracWiki for help on using the wiki.