Difference between revisions of "String expansion"

From Elite Wiki
(When are things switched: Note correlation issues)
(more detailed explanation of string expansion)
Line 1: Line 1:
 
== Format ==
 
== Format ==
Oolite has a mechanism to replace text in strings by other text. At some places during script evaluation oolite searches for text between the square brackets []. If it finds this, it treats the text between the brackets as a kind of variable and tries to switch it, including the brackets fore the new text.
 
  +
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 ==
 
== What is switched ==
If the text is a [mission_variable], a [local_variable] or a system variable like [shipsFound_number], the content is replaced by the content of that variable. If not it looks at the key's in the [[descriptions.plist]]. If it finds a match it uses one of the strings belonging to that key for switching. (Note that OXP-specific entries in descriptions.plist and missiontext.plist should have an OXP or creator-specific prefix to ensure uniqueness.)
 
  +
'''Recognized % codes:'''
   
If you use [0] till [32] the number is replaced by strings from the "System Descriptions" key of the descriptions.plist inside Oolite itself. [0] is the first set of 5 arrays, [1] the second and so on. This was not meant for OXP use but a few numbers are still suitable.
 
  +
* %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 <code>system_description</code> 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 [[Oolite_JavaScript_Reference:_Global#expandDescription|expandDescription]] and [[Oolite_JavaScript_Reference:_Global#expandMissionText|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 (<code>[mission_FOO]</code> substitutes the value contained in <code>missionVariables.FOO</code>)
  +
*#* These are commonly used in OXPs as a method of including dynamic information in displayed text.
  +
*# Legacy local variables (<code>[local_FOO]</code> substitutes the value contained in <code>worldScripts.scriptname.FOO</code> 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 [[Scripting Oolite with JavaScript|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 <code>this.calculatedvalue</code> you could display the content of that variable with other text by calling <code>mission.addMissionText("various text [local_calculatedvalue] various other text");</code>
  +
*# 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 [[whitelist.plist|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])
  +
* 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 ==
 
== When are things switched ==
Oolite calls only at certain places the switching routine.
+
Oolite calls the switching routine only at certain places ('''this is not a complete list'''):
*The most often used place is in [[missiontext.plist]]. In every text line the text between brackets is replaced by a variable or a [[descriptions.plist]] entry.
+
*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 description.plist inside Oolite were 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.
+
*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 AI.plist it only uses the switching on the content of a "commsMessage".
+
*During evaluation of a [[OXP howto AI|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. For some reason nothing is switched in the CONDITIONS part of a command so you can not use anything in brackets there.
+
*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 [[Oolite_JavaScript_Reference:_Global#expandDescription|expandDescription]] and [[Oolite_JavaScript_Reference:_Global#expandMissionText|expandMissionText]].
   
The way thing work means that you can create complicated structures. You can use for example:
+
The way things work means that you can create complicated structures. You can use for example:
 
addSystemShips: my_ship 1 0.[d100_number]
 
addSystemShips: my_ship 1 0.[d100_number]
   
Oolite first offers above line to the replacement routine. That one replaces [d100_number] by a random number. e.g. 46. Than the routine returns:
+
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
 
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 ships roles. When you then use:
+
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]
 
addSystemShips: [my_random_ship] 1 0.[d100_number]
the system will randomly pick a ship from the list and replace the d100_number for a value and returns the expanded string. This will than be excecuted.
+
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.
   
 
[[Category:Oolite scripting]]
 
[[Category:Oolite scripting]]

Revision as of 22:00, 20 June 2020

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:
    1. 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).
    2. Special keys (these are hard-coded)
      • [commander_name]
      • [commander_shipname]
      • [commander_shipdisplayname]
      • [commander_rank]
      • [commander_kills]
      • [commander_legal_status]
      • [commander_bounty]
      • [credits_number]
    3. 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.
    4. 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]
    5. Mission variables ([mission_FOO] substitutes the value contained in missionVariables.FOO)
      • These are commonly used in OXPs as a method of including dynamic information in displayed text.
    6. Legacy local variables ([local_FOO] substitutes the value contained in worldScripts.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 calling mission.addMissionText("various text [local_calculatedvalue] various other text");
    7. 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])
  • 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.