String expansion
Format
Oolite has a mechanism to replace text in strings with other text. At certain times before displaying text (details below), Oolite searches for % symbols and square brackets [ ]. If it finds them, it checks if what immediately follows the % symbols, or what is between the square brackets, has a recognized special meaning, and if so, substitutes replacement text. Oolite also recognizes vertical bars ('|') and colons (':') inside square brackets as special characters. When you do not want Oolite to replace text, use a double % ('%%') to display a single %, and put a backslash ('\') before each square bracket to display the brackets and text inside them without substitution ('\[example\]' will be displayed as '[example]'). Additionally, the special value '\n' will be replaced by a new line, which can be prevented by preceding it with an extra slash ('\\n' will display '\n').
What is switched
Recognized % codes:
- %H - Current System name, e.g., Lave
- %I - Current System name with "ian" appended, e.g., Laveian
- %Jxxx - Name of System corresponding with ID number xxx in the current galaxy (must be a 3-digit number), e.g., %J007 is Lave while in Galaxy 1 (feature added in Oolite 1.73)
- %Gxxxyyy - Name of System corresponding with ID number xxx in galaxy number yyy (both must be 3-digit numbers), e.g. %G007000 is Lave (feature added in Oolite 1.87)
- %N - Random name (repetitions of %N in the same string generate the same result)
- %R - Random word (repetitions of %R in the same string generate unique results)
Text in brackets:
- If there are only numbers between the brackets (e.g., [0] or [35]), the corresponding Nth sub-array (counting from zero) is retrieved from the
system_description
array in descriptions.plist, which is expected to contain only sub-arrays of strings. When a sub-array is retrieved, one string is selected from it at random and the result is expanded recursively (if it contains %-codes or bracket expressions, those will be replaced too). As of Oolite 1.87, there are 36 entries in the system_description array (the last is [35]). This is used to generate planet descriptions but may be used in OXPs if the strings are suitable. For example, [10] is replaced with a random beverage. - Otherwise, the text between the brackets (hereafter called the "key") is evaluated recursively in the following priority order to find a replacement:
- Overrides (context-specific expansions, special keys that are only recognized in certain situations, and can also be used from JavaScript)
- In comms messages, [self:name] and [target:name] will be replaced respectively with the sender's displayName and the target's displayName (or the "unknown-target" description from descriptions.plist, if the scanner is jammed).
- In expandDescription and expandMissionText you can provide a dictionary of keys and replacement values (which can be JavaScript expressions calculated at run-time).
- Special keys (these are hard-coded)
- [commander_name]
- [commander_shipname]
- [commander_shipdisplayname]
- [commander_rank]
- [commander_kills]
- [commander_legal_status]
- [commander_bounty]
- [credits_number]
- Descriptions.plist (if there is a matching key, substitutes the associated value, which may be a single string or an array of string; if it is an array of strings, then one is selected at random.) Because descriptions.plist entries from all OXPs are merged at run-time with the ones in the game's Resources folder, it is recommended that OXP-specific entries in descriptions.plist have an OXP or creator-specific prefix to ensure uniqueness.
- For example, [nom] will match the "nom" key in descriptions.plist, the value of which is an array of two strings ("%R", "[nom1]"), one of which will be randomly selected, and then evaluated again.
- Keyboard bindings (will be replaced with corresponding key name; however, descriptions.plist overrides keybindings and is evaluated first, so using these keys in OXPs is discouraged; instead, look for the comment "translations of special keys" in descriptions.plist and use the keys there)
- [oolite_key_tab], [oolite_key_esc], [oolite_key_space], [oolite_key_f1], [oolite_key_f2], [oolite_key_f3], [oolite_key_f4], [oolite_key_f5], [oolite_key_f6], [oolite_key_f7], [oolite_key_f8], [oolite_key_f9], [oolite_key_f10, [oolite_key_f11]
- [oolite_key_right], [oolite_key_left], [oolite_key_down], [oolite_key_up], [oolite_key_home], [oolite_key_end], [oolite_key_insert], [oolite_key_delete], [oolite_key_pageup], [oolite_key_pagedown]
- [oolite_key_numpad0], [oolite_key_numpad1], [oolite_key_numpad2], [oolite_key_numpad3], [oolite_key_numpad4], [oolite_key_numpad5], [oolite_key_numpad6], [oolite_key_numpad7], [oolite_key_numpad8], [oolite_key_numpad9]
- Mission variables (
[mission_FOO]
substitutes the value contained inmissionVariables.FOO
)- These are commonly used in OXPs as a method of including dynamic information in displayed text.
- Legacy local variables (
[local_FOO]
substitutes the value contained inworldScripts.scriptname.FOO
with the scriptname depending on the context where the text is being evaluated)- A local variable is a property defined on a world script (e.g., a script.js in Config/, or any script listed in a world-scripts.plist). this.name from a script.js in Config/ is used as the key for that script in the global worldScripts dictionary. As of Oolite 1.87, none of the core game texts use local variables.
- In Oolite_JavaScript_Reference:_Mission#addMessageText and Oolite_JavaScript_Reference:_Mission#addMessageTextKey, the applicable scriptname is the calling script. As an example, if your script has a local variable
this.calculatedvalue
you could display the content of that variable with other text by callingmission.addMissionText("various text [local_calculatedvalue] various other text");
- Legacy script query methods
- As a last resort, Oolite attempts to expand a key by treating it as a legacy script query method and invoking it. Only whitelisted query_methods and query_method_aliases are permitted. Examples: [systemGovernment_string] (a whitelisted query method), [fuelLevel_number] (a whitelisted query method) or [fuel_level_number] (a whitelisted alias for [fuelLevel_number])
- Overrides (context-specific expansions, special keys that are only recognized in certain situations, and can also be used from JavaScript)
- If no suitable replacement is found, the text will be displayed unchanged and a warning will be logged.
Operators:
If a vertical bar ('|') is found between square brackets, Oolite will interpret whatever is on the left side of the bar as the key and whatever is on the right side of the bar as an operator. An operator tells Oolite to apply a change after the key has been expanded. Some operators also require an input value, a colon (':'). The available operators are:
- dcr - displays the text as a credit value, interpreting the value as floating point deci-credits (e.g., if the value is 100.7, display 10.7 credits)
- cr - displays the text as a credit value, interpreting the value as floating point credits (e.g., if the value is 100.7, display 100.7 credits)
- icr - displays the text as a credit value, interpreting the value as integer credits (e.g., if the value is 100.7, display 100.0 credits)
- idcr - displays the text as a credit value, interpreting the value as integer deci-credits, rounded (e.g., if the value is 100.7, display 10.0 credits)
- precision - displays the text as a floating point value with an integer-specified number of decimal places, must be specified as precision:number (e.g., [distance|precision:2])
- multiply - displays the text as a floating point value multiplied with another floating point value, which must be provided as multiply:othervalue (e.g., [fuelLevel_number|multiply:0.5])
- add - displays the text as a floating point value added to another floating point value, which must be provided as add:othervalue (e.g., [fuelLevel_number|add:1.5])
Multiple operators can be applied sequentially to the same key, each separated by a vertical bar. For example: [distance|multiply:0.5|precision:1].
When are things switched
Oolite calls the switching routine only at certain places (this is not a complete list):
- The most often used place is in missiontext.plist. Every text line is evaluated for string replacement. Most commonly this is used to replace text in brackets with the content of a variable or a descriptions.plist entry.
- Inside a descriptions.plist entry you can also use brackets. This way you can get a recursive process. Look for example in the descriptions.plist in Oolite's Resources\Config folder where it generates random names in a recursive way. (Recursion is limited to 32 times to prevent infinite looping.). Because of the random number generator used for expansion, the use of deep recursion may cause correlations which are not random - consider for example that "mud" is always followed by "tennis" or "hockey" in planetary descriptions, never anything else. In Oolite 1.79, certain ways of expanding descriptions from JavaScript will use a higher quality random number generator, which does not have correlation problems.
- During evaluation of a plist-based AI, it only uses switching on the content of a "commsMessage".
- During evaluation of a script.plist all the lines in the DO or ELSE part of a condition statement are switched. Nothing is switched in the CONDITIONS part of a command so you cannot use anything in brackets there.
- In expandDescription and expandMissionText.
The way things work means that you can create complicated structures. You can use for example:
addSystemShips: my_ship 1 0.[d100_number]
Oolite first offers the above line to the replacement routine. That replaces [d100_number] by a random number. e.g. 46. Than the routine returns:
addSystemShips: my_ship 1 0.46
This line will then be executed and the ship is placed at the calculated random position. You can also define a descriptions.plist entry of my_random_ship and define a list of several ship roles. When you then use:
addSystemShips: [my_random_ship] 1 0.[d100_number]
the system will randomly pick a ship from the list and replace the d100_number with a value and return the expanded string. This will than be executed.