Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added new formatting options (post 3.61)

...

  • Of course a Boolean operation always results in true or false (integer 1 or 0).

  • Values of any type can be used as Boolean operands, e.g. for “and”. They will be interpreted as “true” if they are non-zero or non-numeric.

  • != and == can be used with any data types, even non-numeric ones. When comparing two numeric values, they are converted using the rules above. Values of non-numeric types are never equal to null, or to any other numbers.

  • “and” and “or” use short-circuit semantics: The right side of the operation can be skipped if the left side already determines the outcome of the operation

    • Example: false and $foo false (the value of $foo is not checked at all)

  • Unlike != and ==, the comparison operators <, <=, >, >= are only supported for numeric values, difficulty levels, and attention levels. Comparing other non-numeric values will result in an error and an undefined result.

  • <, <=, >, >= cannot be used in XML directly, so lt, le, gt, ge are provided as alternatives. In some cases you won’t have to use them, though - using range checks with additional XML attributes can be more readable (see below).


Anchor
strings
strings
Strings and formatting

You can concatenate string literals using the + operator, but there is also a printf-like formatting syntax, which is easier to use than concatenating lots of small pieces:

...

See also the section about value properties below.

Instead of ‘%1 %2 %3’, you can also use ‘%s %s %s’ - however, which is also compatible with Lua string formatting in the UI system. However, this should not only be used for texts that require localisation. Translators should be aware that it is possible to change the order of the parametersif you are sure that the order is the same in all supported languages. If you want to make translators aware that they can change the order of parameters, you should prefer '%1 %2 %3'.

To get a percent character in the result string, use '%%' in the format string.

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

Lists

Another example for a non-numeric value is a list: It is an ordered collection of other arbitrary values (called array or vector in other languages). It can be constructed within an expression using the [] syntax, see above. It may also be generated by special actions and conditions, and there are actions that can insert or remove values.

A list can contain values of arbitrary data types, even mixed in the same list - so a list can actually contain other lists. However, some of the things that you can do with lists require that all contained elements are of a certain type. The contents of a list can be accessed via properties, see the section about value properties. Lists can be empty, they are written as “[ ]”.

Note

When accessing a list’s elements, the numbering is 1-based, so the first element has number 1. This is intuitive but different from 0-based numbering in most programming languages.

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.

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 above. See the next section about how to access the contents of a table.

Almost all values are allowed as table keys, but there are a few exceptions:

  • Strings must start with '$', like variables
  • null cannot be used as table key (but the number 0 is valid)
  • Lists, tables, groups and buildplans cannot be used as table keys

These restrictions only apply to the keys, there are no restrictions for values that you assign to them. For example:

  • table[] creates an empty table
  • table[{0} = null] creates a table that maps the number 0 to null
  • table[{'$foo'} = 'bar'] a table that maps the string '$foo' to the string 'bar'
  • table[$foo = 'bar'] exactly the same, just a shorter notation for string keys
  • table[foo = 'bar'] error, 'foo' does not start with a '$'
  • table[{1} = [], {2} = table[]] a table that maps 1 to an empty list and 2 to an empty table

Just like lists, tables are stored as references, so it's possible that multiple variables reference the same table (see above).

Value properties

Properties are a crucial concept in script expressions. In the previous sections you have seen mostly constant expressions, which are already evaluated when they are parsed at game start. For reading and writing variables and evaluating the game’s state, properties are used.

Numbers don’t have any properties. Lists, for example, have quite a few of them: You can access the number of elements; and each element is also a property of the list. A ship can have properties like its name, the ship class, its position etc.

You can imagine properties as key/value pairs in an associative mapping: You pass the key, and you get the value as result. For example, the list [42, null, 'text'] has the following mapping:

  • 1 ⟹ 42

  • 2 ⟹ null

  • 3 ⟹ 'text'

  • 'count' ⟹ 3

As you can see, a property key can be a number or a string. Actually there is no restriction regarding the data type of the key.

You can look up a property by appending a dot and the key in curly braces:

  • [100, 200, 300, 400].{1} ⟹ 100 (reading the first element)

  • [100, 200, ['Hello ', 'world']].{3}.{2} ⟹ 'world' (second element of the inner list, which is the third element of the outer list)

  • [].{'count'} ⟹ 0

  • table[{21} = 42].{21} 42

In most cases the property key is a fixed string, like “name” or “class”. You can write this like above:

  • [42].{'count'}

  • $ship.{'name'}

  • $ship.{'class'} 

  • table[$foo='bar'].{'$foo'}

But it is easier just to write the property key without braces, which is equivalent:

  • [0].count

  • $ship.name

  • $ship.class

  • table[$foo='bar'].$foo

(In this case, $ship is a variable. All variables start with a “$”, so they cannot be confused with keywords.)

A list has even more properties:

  • 'random' returns a randomly chosen element (which requires that the list is non-empty)

  • 'min' and 'max' return the minimum or maximum (all elements have to be numeric)

    • [1, 6, 8].min ⟹ 1

  • 'average' returns the average (but all element types have to be compatible)

    • [1, 6, 8].average ⟹ 5

  • 'indexof' is followed by another property, and the index of the first occurence of that key in the list is returned, or 0 if it’s not in the list

    • [1, 6, 8].indexof.{8} ⟹ 3

  • 'clone' creates a shallow copy of the list (i.e. lists that are contained as elements in the list are not copied, only the reference to them)

    • [1, 6, 8].clone[1, 6, 8]

A table has different properties:

  • 'clone' creates a shallow copy of the table
  • 'keys' allows you to access data about the table's keys

However, 'keys' alone will not give you a result. 'keys' must be followed by another keyword to retrieve the desired information, for example:

  • $table.keys.list: Yields a list of all keys in the table
  • $table.keys.sorted: Yields a sorted list of all keys in the table (which requires that all keys are numeric)
  • $table.keys.random: A randomly chosen key (which requires that the table is non-empty)
Note

The string formatting syntax that you have seen above is also based on the property system. You basically pass a list as property key to a string. Braces around the brackets are not required, so 'foo'.[...] is just a convenient alternative notation for 'foo'.{[...]}.

Property lookup tests and suppressing errors

If you look up a property that does not exist, there will be an error, and the result will be null. To test whether a property exists, you can append a question mark “?” to the lookup, which yields true or false:

  • $list.{5} ⟹ The fifth element of a list - however, if $list has less than 5 elements (and if it's also not a table with the key 5), there will be an error

  • $list.{5}? ⟹ true if $list exists and has the property 5, false otherwise

  • $table.$key? ⟹ Analogously, true if $table exists and has the string property '$key'

The question mark can even be applied to variables:

  • $list ⟹ The value stored under the name $list, or an error if there is no such variable

  • $list? ⟹ true if the variable exists, false otherwise

To look up the value of a property although it may not exist, you can use the at-sign “@” as prefix:

  • @$list.{5} ⟹ The result of the $list lookup if $list exists and has the property 5, otherwise null (without error message)

  • @$list ⟹ The list if this variable exists, null otherwise

  • @$list.{5}.{1} ⟹ The first element of the fifth element of $list, if it exists, null otherwise

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 (see typeof operator above)

...

profile

...

profile.flat

profile.increasing

profile.bell

...

Probability distribution profile (see random ranges below)

...

cuestate

...

cuestate.waiting

cuestate.active

cuestate.complete

...

Cue states (see above)

...

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

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.

Player properties

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

  • 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: The player's own ship (but the player is not necessarily on board)
  • player.entity: The actual player object
  • player.zone, player.sector, player.cluster, player.galaxy: Location of the player entity

  • player.copilot: 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.

Safe properties

Most properties cause errors if you use them on non-existing objects, such as destroyed ships. There are a few exceptions:

  • exists

  • isoperational

  • iswreck

  • isconstruction

  • available

  • isclass.(...)

These properties will not cause errors when used on “null” or on a destroyed object (which may still be accessible from scripts in some cases), and produce null or false as results, respectively. (The keyword “available” is used for trades, not for objects. Trades can also become invalid.) However, when using such a property on a different data type like a number, there will still be an error.[New as of X Rebirth post 3.61]

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)
  • '%.3'.[123.4] '123.400' (show 3 fractional digits, rounding half away from zero - decimal point correctly localised)
  • '%,.1'.[12345.67]' '12,345.7' (combination of the above)

Additional remarks:

  • The "," and "." formatting modifiers only apply to numbers. They are ignored if used on values of other types.
  •  If "," is used without "." then any fractional digits are discarded.
  • "." must be followed by a single digit (0-9). In case of ".0" any fractional digits are discarded (rounding towards zero, not half away from zero).

There are also special methods to format money values and time values using the "formatted" property, see below.


Lists

Another example for a non-numeric value is a list: It is an ordered collection of other arbitrary values (called array or vector in other languages). It can be constructed within an expression using the [] syntax, see above. It may also be generated by special actions and conditions, and there are actions that can insert or remove values.

A list can contain values of arbitrary data types, even mixed in the same list - so a list can actually contain other lists. However, some of the things that you can do with lists require that all contained elements are of a certain type. The contents of a list can be accessed via properties, see the section about value properties. Lists can be empty, they are written as “[ ]”.

Note

When accessing a list’s elements, the numbering is 1-based, so the first element has number 1. This is intuitive but different from 0-based numbering in most programming languages.

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.


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 above. See the next section about how to access the contents of a table.

Almost all values are allowed as table keys, but there are a few exceptions:

  • Strings must start with '$', like variables
  • null cannot be used as table key (but the number 0 is valid)
  • Lists, tables, groups and buildplans cannot be used as table keys

These restrictions only apply to the keys, there are no restrictions for values that you assign to them. For example:

  • table[] creates an empty table
  • table[{0} = null] creates a table that maps the number 0 to null
  • table[{'$foo'} = 'bar'] a table that maps the string '$foo' to the string 'bar'
  • table[$foo = 'bar'] exactly the same, just a shorter notation for string keys
  • table[foo = 'bar'] error, 'foo' does not start with a '$'
  • table[{1} = [], {2} = table[]] a table that maps 1 to an empty list and 2 to an empty table

Just like lists, tables are stored as references, so it's possible that multiple variables reference the same table (see above).


Value properties

Properties are a crucial concept in script expressions. In the previous sections you have seen mostly constant expressions, which are already evaluated when they are parsed at game start. For reading and writing variables and evaluating the game’s state, properties are used.

Numbers don’t have any properties. Lists, for example, have quite a few of them: You can access the number of elements; and each element is also a property of the list. A ship can have properties like its name, the ship class, its position etc.

You can imagine properties as key/value pairs in an associative mapping: You pass the key, and you get the value as result. For example, the list [42, null, 'text'] has the following mapping:

  • 1 ⟹ 42

  • 2 ⟹ null

  • 3 ⟹ 'text'

  • 'count' ⟹ 3

As you can see, a property key can be a number or a string. Actually there is no restriction regarding the data type of the key.

You can look up a property by appending a dot and the key in curly braces:

  • [100, 200, 300, 400].{1} ⟹ 100 (reading the first element)

  • [100, 200, ['Hello ', 'world']].{3}.{2} ⟹ 'world' (second element of the inner list, which is the third element of the outer list)

  • [].{'count'} ⟹ 0

  • table[{21} = 42].{21} 42

In most cases the property key is a fixed string, like “name” or “class”. You can write this like above:

  • [42].{'count'}

  • $ship.{'name'}

  • $ship.{'class'} 

  • table[$foo='bar'].{'$foo'}

But it is easier just to write the property key without braces, which is equivalent:

  • [0].count

  • $ship.name

  • $ship.class

  • table[$foo='bar'].$foo

(In this case, $ship is a variable. All variables start with a “$”, so they cannot be confused with keywords.)

A list has even more properties:

  • 'random' returns a randomly chosen element (which requires that the list is non-empty)

  • 'min' and 'max' return the minimum or maximum (all elements have to be numeric)

    • [1, 6, 8].min ⟹ 1

  • 'average' returns the average (but all element types have to be compatible)

    • [1, 6, 8].average ⟹ 5

  • 'indexof' is followed by another property, and the index of the first occurence of that key in the list is returned, or 0 if it’s not in the list

    • [1, 6, 8].indexof.{8} ⟹ 3

  • 'clone' creates a shallow copy of the list (i.e. lists that are contained as elements in the list are not copied, only the reference to them)

    • [1, 6, 8].clone[1, 6, 8]

A table has different properties:

  • 'clone' creates a shallow copy of the table
  • 'keys' allows you to access data about the table's keys

However, 'keys' alone will not give you a result. 'keys' must be followed by another keyword to retrieve the desired information, for example:

  • $table.keys.list: Yields a list of all keys in the table
  • $table.keys.sorted: Yields a sorted list of all keys in the table (which requires that all keys are numeric)
  • $table.keys.random: A randomly chosen key (which requires that the table is non-empty)
Note

The string formatting syntax that you have seen above is also based on the property system. You basically pass a list as property key to a string. Braces around the brackets are not required, so 'foo'.[...] is just a convenient alternative notation for 'foo'.{[...]}.

Lookup tests and suppressing errors

If you look up a property that does not exist, there will be an error, and the result will be null. To test whether a property exists, you can append a question mark “?” to the lookup, which yields true or false:

  • $list.{5} ⟹ The fifth element of a list - however, if $list has less than 5 elements (and if it's also not a table with the key 5), there will be an error

  • $list.{5}? ⟹ true if $list exists and has the property 5, false otherwise

  • $table.$key? ⟹ Analogously, true if $table exists and has the string property '$key'

The question mark can even be applied to variables:

  • $list ⟹ The value stored under the name $list, or an error if there is no such variable

  • $list? ⟹ true if the variable exists, false otherwise

To look up the value of a property although it may not exist, you can use the at-sign “@” as prefix:

  • @$list.{5} ⟹ The result of the $list lookup if $list exists and has the property 5, otherwise null (without error message)

  • @$list ⟹ The list if this variable exists, null otherwise

  • @$list.{5}.{1} ⟹ The first element of the fifth element of $list, if it exists, null otherwise

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 (see typeof operator above)

profile

profile.flat

profile.increasing

profile.bell

Probability distribution profile (see random ranges below)

cuestate

cuestate.waiting

cuestate.active

cuestate.complete

Cue states (see above)

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

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.


Player properties

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

  • 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: The player's own ship (but the player is not necessarily on board)
  • player.entity: The actual player object
  • player.zone, player.sector, player.cluster, player.galaxy: Location of the player entity

  • player.copilot: 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.

Safe properties

Most properties cause errors if you use them on non-existing objects, such as destroyed ships. There are a few exceptions:

  • exists

  • isoperational

  • iswreck

  • isconstruction

  • available

  • isclass.(...)

These properties will not cause errors when used on “null” or on a destroyed object (which may still be accessible from scripts in some cases), and produce null or false as results, respectively. (The keyword “available” is used for trades, not for objects. Trades can also become invalid.) However, when using such a property on a different data type like a number, there will still be an error.

Anchor
moneytimeformatting
moneytimeformatting
Money and time formatting

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.

  • $money.formatted.{'formatstring'} 
  • $money.formatted.default (using default format string '%s')
  • $time.formatted.{'formatstring'}
  • $time.formatted.default  (using default format string '%T')

In scripts, money is stored in cents, not Credits. The formatted representation always shows the value in Credits, including thousands separators.

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.

More available specifiers (in addition to %s):

  • %k: Credits (truncated) in kilo format
  • %M: Credits (truncated) in Mega format
  • %G: Credits (truncated) in Giga format
  • %T: Credits (truncated) in Tera format
  • %Cr: Localised "Cr" string
  • %%: A % sign

Examples:

  • (1234Cr).formatted.{'%s'}'1,234'
  • (1234Cr).formatted.default'1,234' (same as {'%s'})
  • (1234Cr).formatted.{'%.s %Cr'}'1,234.00 Cr'
  • (1234Cr).formatted.{'%1s'}'1 k' (rounding towards zero)
  • (1234Cr).formatted.{'%cM'}'0 M'

For documentation of time format strings, see the Lua function ConvertTimeString() in the Lua function overview.

Examples:

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

Complete property documentation

...