Versions Compared

Key

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

...

  • Conditions with results: If the instantiating cue has conditions with results, those results are stored in variables - but in the variables of the static cue, not of the instance! So in the <actions> you have to access the variables via the static keyword:
    <debug_text text="static.$foo"/>
    It may even be necessary to copy the variables over to the instance because the static variables can be overwritten by the next condition check:
    <set_value name="$foo" exact="static.$foo"/>

  • Resetting completed/cancelled instances: As explained above, sub-instances are only created when needed (when going to the waiting state) and are destroyed when they are not needed any more (when they are completed or cancelled, including all sub-cues). There are cases in which you want to access cues that don’t exist any more - it simply doesn’t work. In some cases you are safe: You can be sure that all your ancestors exist, and instantiating cues won’t be removed until they are cancelled. In some other cases you simply don’t know and have to check if the instance is already (or still) there.

  • Lifetime of instances: Do not make assumptions about when an instance is removed! Just looking at it in the Debug Manager keeps it alive for the time being. So, sometimes you could still have a completed instance that wouldn’t exist under other circumstances.

 

Expressions

 

Most of the attribute values in actions and conditions are interpreted as script expressions and parsed accordingly. An expression is a phrase that can be evaluated to a single value. The simplest expressions are actual numeric values and strings, so called literals: 

  • 0 (integer number)

  • 0772 (leading 0 means octal integer number)

  • 3.14159 (floating point number)

  • 5e12 (float in exponent notation, “times ten to the power of”)

  • 0xCAFE (hexadecimal integer number)

...

You can write string literals by putting the string in single quotes:

 

  • 'Hello world'

  • '' (empty string)

  • 'String with a line break\n'

...

Numeric data types and suffixes

 

Numbers can have a suffix that determines their numeric type. There are also numerical data types like “money” or “time” which can only be expressed by using an appropriate unit suffix:

 

  • 5000000000L (large integer)

  • 1f (floating point number, same as 1.0, just 1 would be an integer)

  • 1000Cr (Money in Credits, converted to 100000 cents automatically)

  • 500m (Length in metres)

  • 10s (Time in seconds)

  • 1h (Time in hours, which is converted to 3600s automatically)

...

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.

 

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 (see below)

{}

delimiter

{101, 3}

'Some text'

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

+

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)

^

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 (strict semantics)

or

binary

true or false

true

Logical OR (strict semantics)

 

Operator precedence rules

 

You can group sub-expressions using parentheses, but if you don’t, the following order of operations is applied, so that 5-1+2*3 == 10 as you would expect. The order is the same as in the table above, but there are operators with the same precedence - these are applied from left to right. 

  • Unary operators: +, -, not, typeof, function-style operators (highest precedence)

  • Power operator: ^

  • Multiplicative: *, /, %

  • Additive: +, -

  • Comparison: lt, le, gt, ge

  • Equality: ==, !=

  • and

  • or (lowest precedence)

Type conversion

...

When a binary arithmetic operator is used on numbers of different types, they will be converted to a suitable output type. The resulting type depends on whether a unit data type is involved (types that are not plain integers or floats). The following cases may occur: 

  • Null and something else: The null value will be interpreted as “0” of the other type.

  • Two non-unit integers: The result will be an integer of the largest involved type.

  • Two non-unit numbers, not all integers: The result will be the largest involved float type.

  • Non-unit and unit: The result will be the unit type.

  • Two different units: The types are incompatible. This is an error, the result is undefined.

...

There is a way to convert a number into a different type manually: You append the corresponding suffix to a sub-expression in parentheses, like this:

 

  • (1 + 1)f ⟹ 2f ⟹ 2.0

  • (1h) m / (180deg) i ⟹ (3600s) m / (3.14rad) i ⟹ 3600m / 3 ⟹ 1200m

...

Every data type can be combined with a string with the + operator, and will be converted to a string representation. That way you can also concatenate strings and numbers:

 

  • 'One plus one is equal to ' + (1+1) + '.' ⟹ 'One plus one is equal to 2.'

  • 'One plus one is not equal to ' + 1 + 1 + '.' ⟹ 'One plus one is not equal to 11.'

 

As you can see, operators of the same precedence (+ in this case) are always evaluated from left to right.

Boolean operators

 

Some additional notes on Boolean operators (such as and, or, not, ==):

 

  • 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 strict semantics: Both operands (left and right side expressions) are evaluated and then the result is calculated. This is different from C/C++, in which short-circuit semantics are used and the right side expression can be ignored. If you need this, you can simply use nested XML nodes instead of a single expression, e.g. <check_all> and <check_any>.

  • 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).


Strings

...

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

 

  • 'The %1 %2 %3 jumps over the %5 %4'.['quick', 'brown', 'fox', 'dog', 'lazy']

  • '%1 + %2 = %3'.[$a, $b, $a + $b]

...

See also the section about value properties below.

...

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.

...

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.


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.

...

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

...

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

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'}

 

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

 

  • [0].count

  • $ship.name

  • $ship.class

...

(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, or null if the list is 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]

...

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, or error if the list has less than 5 elements (or if $list is not a list at all)

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

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 fifth element if the list exists and has a fifth element, null otherwise

  • @$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.

...

 

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

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 on (not necessarily the player's ship)

  • player.primaryship: The ship that the player controls (but the player is not necessarily on board)
  • player.zone, player.sector, player.cluster, player.galaxy: Location of the player entity

  • player.copilot: The co-pilot NPC

Components, like ships, spaces, or NPCs, have their own properties. The available properties depend on the particular component type, even if there is only one script datatype for components. So ships may have the property “speed”, but stations 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.

Complete property documentation

...

The game files include the HTML file scriptproperties.html in the game’s root folder, and more scriptproperties.* files in the libraries folder. Open the HTML file in a browser (only Firefox is officially supported, there may be problems with Chrome). 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:

 

  • Enter the beginning of a base keyword

  • Enter $ followed by the data type you are looking for (e.g. “$ship”), as if it were a variable

  • To see the properties of a base keyword or data type, enter a dot (“.”)

  • After the dot, you can enter a property name

  • You can also enter a dot (“.”) as first character to search globally for a property

...

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.