Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info

This Please note that this is an officially-maintained documentation.

Please note To ensure that in order you can rely on the information having been checked by Egosoft, you won't will not be able to edit the this page.


The Mission Director (MD) is a subsystem of the game and interprets mission scripts, which are written in an XML-based language. The Mission Director in X Rebirth Mission Director and X4 is based on the MD in X3: Terran Conflict, with some major changes based on feedback from MD users.

An introduction to the original MD can be found in the Egosoft forums. There is also a PDF guide for the X3 Mission Director, which is partially used as a template for this document.

This document is primarily supposed to be a guide for MD users (people who use the MD to develop missions or write other MD scripts), not for MD programmers (people who work on the MD engine in C++).

The general MD scripting system is the same in XR and X4, so this guide applies to both games. However, each game has its own set of supported script features (i.e. actions, conditions and properties), so in general scripts from different games are not compatible.


Table of Contents

Table of Contents
excludeTable of Contents

...

This functionality is only available if the schema files md.xsd and common.xsd are in the correct folder. If you are editing the XML in the game folder directly, all is well and the files are loaded from the libraries folder. However, if you are editing in a separate folder, copy those XSD files from the libraries folder directly into the folder where your XML files are located. 

Note

MD script structure

In this section we will look at how to start the whole process by creating a new MD mission file and the basic steps in producing mission content with XML code. There will be a description of the key elements of the mission file.

The XML root node of an MD file is called “mdscript” and looks like this:

...

Even if your script is free of XSD errors, that does not mean that the script syntax is correct. For example, there are XML elements that require at least one of multiple attributes, but this requirement cannot be reflected in a schema (apart from documentation text). Please notice the XSD documentation of the elements and attributes, e.g. displayed via tooltips in Visual Studio / Visual Web Developer. Please also note additional requirements for MD cue attributes in this guide (see Conditions).

To check for errors, please pay attention to in-game error messages that are produced while your script is imported, and run-time errors while the script runs. The XSD files can help you a lot, but you should not rely on the absence of XSD errors.

Anchor
debugoutput
debugoutput
Script debug output

The game can print error messages and, when enabled, also general messages. Error messages can originate from the scripting system, but also from other game sub-systems. They can be viewed in the in-game DebugLog.

To collect all messages in a file, start the game with the following parameters on the command line:

-logfile debuglog.txt

All messages, including enabled non-error messages, will be written into the log file. You can find it in your personal folder, where your save folder is located. To enable scripting-specific debug messages, add the following to the command line:

-debug scripts

Other debug filters other than "scripts" can be enabled by repeating the -debug command for each filter name, but that is rarely needed for scripting.

The script action <debug_text> can be used to print debug messages from within a script.


MD script structure

In this section we will look at how to start the whole process by creating a new MD mission file and the basic steps in producing mission content with XML code. There will be a description of the key elements of the mission file.

The XML root node of an MD file is called “mdscript” and looks like this:

<?xml version="1.0" encoding="utf-8"?>
<
mdscript name="ScriptName" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="md.xsd">

“ScriptName” is the name used for this script regardless of the file name. It has to start with an upper case letter and must be unique among all MD script names. It also should not contain spaces, so other MD scripts can use it as an identifier to access this script’s contents easily.

...

<?xml version="1.0" encoding="utf-8"?>
<
mdscript name="ScriptName" ...>
  <
cues>
    <
cue name="RootCue1"> [...]
    </
cue>
    <
cue name="RootCue2"> [...]
    </
cue>
     </
cues>
</
mdscript>

 

Anchor
cues
cues
Cues

...

The rules for naming cues is the same for MD script names: The name starts with an upper case letter, and has to be unique within this file. So it is actually possible to use the same cue name in different scripts, which is different from the MD in X3.


Anchor
conditions
conditions
Conditions

The <conditions> node can contain one or multiple conditions, all of which must be met to activate the cue. If the node is missing, the cue will become active unconditionally. The conditions are checked in sequence, and if a check fails, the following conditions are ignored. There are two types of conditions: Events and non-event conditions.

Non-event conditions are checked in regular intervalseither once or repeatedly in a fixed interval. They may be based on simple values or ranges, such as a particular in-game time having been reached or the player having a certain amount of money. They may also be based on more complex player information, such as what ships they own, whether the player is in a particular area or near a particular object.

Event conditions are triggered when the corresponding event happens, such as the event that a particular object has been targeted, attacked or destroyed. All event nodes have the prefix “event_” so you can easily determine a condition type. NonAfter an event condition you can specify one or more non-event conditions can be used in combination with an event, so they , which will be checked additionally whenever the event happens. If a condition uses an event, it must be in the first sub-node of the <conditions> node. It is even possible to define multiple alternative events that should activate the cue. The first sub-node should be <check_any> in this case, so only one of its sub-conditions has to be met.

...

If a cue has a <conditions> node without any event, it must have one of the attributes onfail or checkinterval. For onfail, you can use the values

  • Use onfail if the conditions should be checked only once. The possible attribute values are cancel

...

  • and complete”. If the

...

  • conditions are met, the

...

  • cue will activate and perform the cue actions. Otherwise it's a failure and the cue will be

...

  • cancelled or completed

...

  • , based on the onfail attribute. Typically onfail="cancel" is used to prevent any further action. onfail="complete" can be used to continue with the sub-cues even in case of failure (but skipping the current cue actions).
  • With checkinterval, you can specify a constant time interval between condition checks. The conditions will be checked regularly forever until they are met, unless the cue’s state is changed explicitly by an external event.

Additionally, you can use the attribute checktime to set the time of the first condition check (also possible in combination with onfail). The checktime can be an expression with variables and is evaluated when the cue is enabled (when the condition checks would normally start , i.e. – for root cues that happens at game start for root cues, or otherwise after the parent cue becomes active).

...

<cue name="Foo" checktime="player.age + 3s" onfail="cancel">
  <
conditions>
  [...]
</
cue>

The attributes onfail, checkinterval, checktime are not allowed for cues with event conditions.


Note

Reminder: When using an XSD-capable editor, it's a great help, but you cannot rely on that alone to verify correctness. Please also check the documentation and look for errors in the game debug output. Concretely, the schema cannot tell whether the above cue attributes are used correctly.


Actions

The <actions> node contains the actions that are performed one after another, without any delay inbetween. You can enforce a delay after activation of the cue and actual action performance, using a <delay> node right before the <actions>:

...

Note

Messages printed with <debug_text> are usually only visible when the “scripts” debug filter is enabled. Launch the game with parameters like this:

-logfile debuglog.txt -debug scripts

Script errors and <debug_text> messages with filter="error" can also be viewed in the in-game DebugLog, see Script debug output.

Each child action in a <do_any> node can have a weight attribute, which can be used to control the random selection of an action node. The default weight of a child node is 1.

...

When the ref attribute is provided, all other attributes (except for name) will be ignored and taken from the library cue instead. (By default a library creates its own namespace, as if namespace="static" were specified. See the section about namespaces.)

Also all sub-cues of the library will be created as sub-cues of the cue that uses it. They are defined in the library as <cue>, not as <library>. (Although you can define a library as a sub-cue of another library, the location in the file does not matter, as already stated above.) It is even possible to reference other libraries in sub-cues of a library!

...

So when writing the library, you don’t have to worry about name confusion, just use the names of cues in your library and it will work as expected when the library is used. Names of cues that do not belong to the library will not be available in expressions (see Foo in the example above), however, names of other libraries in the file are available when referencing them in the ref attribute), however, names of other libraries in the file are available when referencing them in the ref attribute.

Notes:

  • It is not possible to directly call a cue which is 'inside' the library from 'outside' of the library, but it is possible to signal the library ref itself (possibly with parameters) and have a sub-cue inside the library listen to the signal on the library ref (possibly checking the parameters).
  • You can access variables in the library root but generally this should be avoided in favor of parameterizing the library!
    • there are some cases where you do want to access these variables directly, for example for maintaining savegame compatibility when patching.

Library Parameters

A library can be parametrised, so that it can be adapted to the needs of a missions that uses it. You can define required and/or optional parameters for a library, and it will be validated at load time that the user of the library has provided all required parameters.

...

Here is the complete list of numeric data types and corresponding unit suffixes:

...



Data type

Suffix

Examples

Description

null

(none)

null

Converted to non-null data type of value 0 when needed.

integer

i

42

32-bit signed integer. Default for integer literals, so the suffix is not required for them.

largeint

L

0x1ffffffffL

Large 64-bit signed integer.

float

f

3.14

0x100f

32-bit float (single precision). Default for floating point literals, so the suffix is not required for them.

largefloat

LF

1.5e300 LF

Large 64-bit floating point number (double precision).

money

ct (default)
Cr

200Cr
50ct

Money in Credits or cents, always stored in cents. Do not forget to write Cr when working with Credits.

length

m (default)

km

500m

2.3km

Length in metres or kilometres, respectively. A length value is always stored in metres.

angle

rad (default)

deg

90deg

3.14159rad

Angle in radians or degrees, respectively. An angle value is always stored in radians.

hitpoints

hp

100hp

Hit points

time

ms

s (default)

min

h

800ms

1.5s

10min

24h

Time in milliseconds, seconds, minutes, or hours, respectively. A time value is always stored in seconds.

Note

All unit data types are floating point types, except for money, which is an integer data type.

 


Anchor
operators
operators
Operators

You can build expressions by combining sub-expressions with operators. For Boolean operations, expressions are considered “false” if they are equal to zero, “true” otherwise. The following operators, delimiters, and constants are supported:

...



Operator / Delimiter / Constant

Type

Example

Result of example

Description

null

constant

null + 1

1

Null value, see above

false

constant

1 == 0

false

Integer value 0, useful in Boolean expressions

true

constant

null == 0

true

Integer value 1, useful in Boolean expressions

pi

constant

2 * pi

6.2831853rad

π as an angle (same as 180deg)

()

delimiter

(2 + 4) * (6 + 1)

42

Parentheses for arithmetic grouping

[]

delimiter

[1, 2, 2+1, 'string']

[1, 2, 3, 'string']

List of values

table[]delimitertable[$foo='bar', {1+1}=40+2]table[$foo='bar', {2}=42]Table of values

{}

delimiter

{101, 3}

'Some text'

Text lookup (page ID and text ID) from TextDB
(Note: Braces are also used for property lookups)

+

unary

+21 * (+2)

42

Denotes positive number (no effect)

-

unary

-(21 * -2)

42

Negates the following number

not

unary

not (21 == 42)

true

Yields true if the following expression is false (equal to zero), false otherwise

typeof

unary

typeof null

typeof 0

typeof 'Hello world'

datatype.null

datatype.integer

datatype.string

Yields the data type of the following sub-expression

sin

unary

sin(30deg)

sin(pi)

0.5

1.0

Sine (function-style, parentheses required)

cos

unary

cos(60deg)

cos(pi)

0.5

0.0

Cosine (function-style, parentheses required)

sqrt

unary

sqrt(2)

1.414213LF

Square root (function-style, parentheses required)

exp

unary

exp(1)

2.71828LF

Exponential function (function-style, parentheses required)

log

unary

log(8) / log(2)

3.0LF

Natural logarithm (function-style,

parentheses required

parentheses required)

absunary

abs(42)
abs(-42.0)

42
42.0
Absolute value (function-style, parentheses required) (added in X4 v6.00)
sgnunarysgn(-42)
sgn(0.0)
sgn(42.0)
-1
0
1
Sign function (function-style, parentheses required) (added in X4 v6.00)

^

binary

10 ^ 3

1000.0LF

Power

*

binary

21 * 2

42

Multiplication

/

binary

42 / 10
42.0 / 10.0

4
4.2

Division

%

binary

42 % 10

2

Modulus (remainder of integer division)

+

binary

1 + 1

'Hello' + ' world'

2

'Hello world'

Addition

String concatenation

-

binary

1 - 1

0

Subtraction

lt

&lt; (<)

binary

1 lt 3

1 &lt; 3

true

Less than

le

&lt;=

binary

1 le 3

1 &lt;= 3

true

Less than or equal to

gt

&gt; (>)

binary

1 gt 3

1 &gt; 3

false

Greater than

ge

&gt;=

binary

1 ge 3

1 &gt;= 3

false

Greater than or equal to

==

binary

1 + 1 == 2.0

true

Equal to

!=

binary

1 + 1 != 2.0

false

Not equal to

and

binary

true and false

false

Logical AND (short-circuit semantics)

or

binary

true or false

true

Logical OR (short-circuit semantics)

if ... then ...

if ... then ... else ...

ternary

if 1 == 2 then 'F'

if 1 == 2 then 'F' else 'T'

null

'T'

Conditional operator ("inline if")

 







Operator precedence rules

...

If you need a more sophisticated method for text substitution, try <substitute_text>. See the XML schema documentation for this script action.

[New as of X Rebirth post 34.610]

With the formatting syntax above, it is even possible to control how the parameter is formatted, using modifiers between "%" and the parameter specifier ("s" or the parameter number):

  • '%,s'.[12345678] '12,345,678' (the "," modifier shows a number with thousands separators, correctly localised)
  • '%.33s'.[123.4] '123.400' (show 3 fractional digits, rounding half away from zero - decimal point correctly localised)
  • '%,.11s'.[12345.67]' '12,345.7' (combination of the above)

...

Lists are stored in variables as references, so multiple variables can refer to the same shared list: If you change a shared list through a variable, e.g. by changing the value of an element, you change it as well for all other variables. However, the operators == and != can also be used on two distinct lists to compare their elements.multiple variables can refer to the same shared list: If you change a shared list through a variable, e.g. by changing the value of an element, you change it as well for all other variables. However, the operators == and != can also be used on two distinct lists to compare their elements.

Note

When using <remove_from_list/>, be aware that all elements are checked and potentially removed during the action. Do not provide this action with a index lookup of that list as it may become out of bounds.

Bad usage attempting to remove the last element of the list: <remove_from_list name="$List" exact="$List.{$List.count}"/>

If you know the index, simply use <remove_value/> e.g. <remove_value name="$List.{$List.count}"/>


Anchor
tables
tables
Tables

Tables are associative arrays - they are like lists, but you can assign values to (almost) arbitrary keys, not just to index numbers. A table is constructed within an expression using the table[] syntax. See the section about value properties for how to access the contents of a table. Creating and removing entries works similarly to lists, but instead of inserting, you simply assign a value to a table key. If the key does not exist yet, it will be created.

...

  • $table.keys.list: Yields a list of all keys in the table (reliably sorted by key if all keys are numeric)
  • $table.keys.sorted: Yields a sorted list of all keys in the table, sorted by their associated values (which requires that all keys values are numeric)
  • $table.keys.random: A randomly chosen key (which requires that the table is non-empty)

...

As you can see, an error is already prevented if any link in the property chain does not exist. But use the @ prefix with care, since error messages are really helpful for detecting problems in your scripts. The @ prefix only suppresses property-related error messages and does not change any in-game behaviour. 


Static lookups

There are a few data types which are basically enumerations: They only consist of a set of named values, e.g. the “class” data type, which is used for the component classes that exist in the game. For all these static enumeration classes there is a lookup value of the same name, from which you can get the named values as properties by their name. So for the type “class”, there is a value “class” that can be used to access the classes.

Here are a few enumeration classes and corresponding example lookup values:

...


Data type (= value name)

Examples

Description

class

class.ship

class.ship_xl

class.space

class.weapon

Component classes

purpose

purpose.combat

purpose.transportation

Purposes

killmethod

killmethod.hitbybullet

killmethod.hitbymissile

Ways to die (already used before destruction)

datatype

datatype.float

datatype.component

datatype.class

datatype.datatype

Script value datatypes

profile

profile.flat

profile.increasing

profile.bell

Probability distribution profile (see random ranges)

cuestate

cuestate.waiting

cuestate.active

cuestate.complete

Cue states

level

level.easy

level.medium

level.veryhard

Mission difficulty levels (comparable with each other using lt, gt, etc.)

attention

attention.insector

attention.visible

attention.adjacentzone

Attention levels (comparable with each other using lt, gt, etc.)

ware

ware.ore

ware.silicon

Wares

race

race.argon

race.boron

Races

faction

faction.player

faction.argongovernment

Factions

Note

Anchor
typeof
typeof
With the typeof operator you can get the datatype of any expression and compare it with what you expect, for example:

typeof $value == datatype.faction

However, you should not compare the type to datatype.string because there are strings that have different data types. To check for a string you should use the datatype's property "isstring" instead. For example, to check if the variable $value is a string, use the following term:

(typeof $value).isstring

Info

There is also the datatype “tag” with the lookup name “tag” - however, this is not an enumeration type. Looking up a value by name never fails, you actually create a tag value for a given name if it does not exist. For example, if you have a typo, like “tag.mision” instead of “tag.mission”, there won’t be an error because any name is valid for a tag, and the tag “mision” is created on its first use.


...

You can access many player-related game properties via the keyword “player”, for example:

  • player.name: The player’s name

  • player.age: The passed in-game time since game start

  • player.money: The money in the player’s account

  • player.ship: The ship the player is currently on (not necessarily the player's ship), or null if the player is on a station

  • player.primaryship (X Rebirth only): The player's own ship (but the player is not necessarily on board)
  • player.occupiedship (X4 only): The ship that the player is currently piloting, or null if the player is not piloting
  • player.entity: The actual player object
  • player.zone, player.sector, player.cluster, player.galaxy: Location of the player entity

  • player.copilot (X Rebirth only): The co-pilot NPC

The game consists of objects of different classes (zones, ships, stations, NPCs). They have the common datatype "component", however, they have different properties, e.g. NPCs have the property "race", but ships don't.

...

Anchor
moneytimeformatting
moneytimeformatting
Money and time formatting

[New as of X Rebirth post 34.610]

Numbers don't have any properties, except for money and time: They have a "formatted" property, which allows you to get a custom string representation with more advanced options than the generic formatting method for numbers.

...

When formatting the money value, any specifier (such as '%s') in the format string is replaced by the money value, so usually the format string only consists of this one specifier. The following modifiers can be used between '%' and the specifier character, to enable formatting options:

1-9TruncationTo enable truncation, specify the number of relevant digits that should be displayed. If the money string is too long, it can be truncated and a metric unit prefix (e.g. k = kilo) is appended. (All digits are shown unless truncation is enabled.)
cColouring

If truncation is enabled, the metric unit prefixes (e.g. k, M, G) can be coloured when displayed on the screen, using the escape sequence '\033C'.

.CentsUsually money values have no cent part, since cents are not used in accounts or trades. However, single ware prices can have a non-zero cent part. (Cents are not displayed if money is truncated)
_SpacesAn underscore adds trailing spaces to the result string for better right-aligned display in a tabular layout.

By default, these options are disabled.

...

  • (151s).formatted.{'%T'} '00:02:31'
  • (151s).formatted.default '00:02:31' (same as {'%T'})
  • (151s).formatted.{'%.3T'} '00:02:31.000'
  • (151s).formatted.{'%h:%M'} '0:02'

Complete property documentation

...

  • .formatted.{'%.3T'} '00:02:31.000'
  • (151s).formatted.{'%h:%M'} '0:02'

Complete property documentation

To access the script property documentation that is included in the game, you can extract the required files from the game's catalog files using the X Catalog Tool. Extract the HTML file scriptproperties.html in the game's root folder, and all files in the "libraries" sub-folder. For resolving text references in the browser automatically, also extract 0001-L044.xml in the "t" sub-folder.

The raw documentation data is located in libraries/scriptproperties.xml, but it is recommended to open scriptproperties.html in a browser.

Note

scriptproperties.html has to load files from different folders, which modern browsers do not allow by default for security reasons. In order to open scriptproperties.html, the following is required:

  • Firefox: On the about:config page, the value of "security.fileuri.strict_origin_policy" has to be changed to "false".
  • Chrome: The Chrome launcher has to be started with the command-line parameter --allow-file-access-from-files

This provides you with a complete list of all supported “base keywords” and properties. To filter in this list, you can enter an expression in the text field:

...

Note

The documentation contains some data types that are no real script data types, but which are useful for documentation purposes. For example, ships and stations are both of datatype “component”, but have different properties based on their component class.

...


MD refreshing and patching

When a saved game is loaded, the saved MD state is restored, but also all MD files are reloaded and changes in them are applied to the MD state. This is called “refresh”. It is also possible to refresh the MD at run-time using the command “refreshmd” on the in-game command line. This is a convenient way to update MD scripts while the game is already running. 


Details and restrictions

Here are some noteworthy facts about refreshing scripts and cues, and the restrictions:

...

Warning

When adding a variable in a new MD script version and using that variable in multiple places, be aware that the variable doesn't exist yet in older savegames. You may have to check the existence of the variable before accessing it, or add some patch logic that initiailses the variable after loading the savegame, if necessary.

 


Patching

Cues can have <patch> elements with actions that will be performed when an old savegame is loaded. To control which savegames should be affected, you can add a version attribute to the <cue> node and a sinceversion attribute in the patch. When a cue is loaded from a savegame that has an older version than sinceversion, the <patch> actions will be performed immediately after loading.

...

Note

The <patch> elements will be ignored when refreshing the MD at run-time. They only affect loaded savegames.

...


Common attribute groups

There are many commonly used actions and conditions which share groups of attributes. The most important ones are explained here.

 


Anchor
valuecomparisons
valuecomparisons
Value comparisons

...

Note

Values of most enumeration types cannot be compared via min or max (also not via lt, gt, etc.). The only data types that can be used with min and max are numbers and the enumeration types level and attention (see Boolean operators). The exact attribute can be used with any type, and is equivalent to using the == operator.

...


Anchor
randomranges
randomranges
Random ranges

...