1. General
The scriptsystem in OMSI works as a interface between the core of OMSI (including graphics- and soundengine) and sceneryobjects or vehicles. Communication takes place in both directions, so that scenery objects or vehicles can be equipped with individual script routines that interpret inputs or information from the OMSI game core or transmit information to the game core.
2. Requirements
The following requirements are needed, to expand your own content with script functions.
- one (or more) scriptfiles mit executable code (*.osc)
- varlist- und stringvarlist-files for the definition of local variables
- constfile-file for the definition of userconsstants and function table
All files which are read by the vehicle/sceneryobject must be defined in the corresponding configuration file. This couldn't apply to predefined variables, which can also be read and written without explicit definition in the script files.
3. Application
In the configuration, the following commands can be used to implement the different files:
- [script]
- [varnamelist]
- [stringvarnamelist]
- [constfile]
The command is followed by the number of defined files, then the files with the corresponding path relative to the configuration file. The file name can be chosen freely, but special characters such as "Ä,Ö,Ü" should be avoided, as this can lead to problems with systems that do not have German Windows installed. It is best to use the English alphabet - this is usually supported natively by every system.
4. Basics of the script language
The script system of OMSI uses the reverse polish notation (RPN in short). In contrast to the typical notation of an arithmetic operation (2+5), the operator is not between the operands but behind them (25+). Even if the RPN seems unfamiliar at first glance, it has some advantages, which we will discuss in more detail below. Apart from the more effective input of arithmetic operations, the RPN also enables batch processing, which is of great importance in OMSI. A more complicated example: " (1 + 2) * (3 + 4) " is similar to 1 2 + 3 4 + *.
OMSI text files and scripts should be case sensitive.
5. Stack and Register
By using the RPN, OMSI has a string stack (complex strings) and a floating point stack. In the following, the term "stack" always refers to the floating-point stack, otherwise it is referred to as "string stack".
Both stacks contain 8 memory locations, which are numbered from 0 to 7 (0-based). Each script operation inserts a new value into the stack (push) or pulls values out of the stack (pop/pull). Reading (peek) without changing the stack is also possible. The last value set takes the first position in the stack, each previously set value moves one place "higher".
If values are saved temporarily for later processing in the script without using local variables, OMSI offers eight indexed memory locations (0-7), which can be read and written directly and are located in the register. However, this only applies to floating point numbers, there is no register for strings.
6. Examples for operations
Example:
The operation is 1 + 2. The script code for this looks like this:
1 2 +
The following table illustrates how the stack behaves in the above operation:
Stackplace 0 | Stackplace 1 | Stackplace 2 | Stackplace 3 | Stackplace 4 | Stackplace 5 | Stackplace 6 | Stackplace 7 | |
before: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
"1" | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
"2" | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
"+" | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
In the initial state, the stack is usually filled with zeros. The command "1" moves the first to the top of the stack, all numbers already contained move up one place. The command "4" moves the fourth to the top of the stack, accordingly the one to the second and all other numbers one place further up. The command "+" compares the first two values in the stack and places the result in the first stack place. When operators are used, the values to be calculated are removed from the stack and the result is then placed at the top of the stack.
Another example:
(1 + 2) * (3 + 4) must be noted as follows, as mentioned above: 1 2 + 3 4 + *
In this case the stack works in the following way:
Stackplace 0 | Stackplace 1 | Stackplace 2 | Stackplace 3 | Stackplace 4 | Stackplace 5 | Stackplace 6 | Stackplace 7 | |
before: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
"1" | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
"2" | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
"+" | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
"3" | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
"4" | 4 | 3 | 3 | 0 | 0 | 0 | 0 | 0 |
"+" | 7 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
"*" | 21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
At the end of the operation, the correct result is on stack 0.
Instead of numbers, variables or results of other calculations can also be used as operands.
7. Floating point numbers and strings
OMSI works exclusively with the data types floating point number and string. These file types have separate stacks and thus usually run independently of each other. However, it is possible to convert floating point values to strings or strings to floating point values - certain functions may therefore access both areas simultaneously.
Boolean variables are not used in OMSI; as a rule, the numerical values 0 and 1 are used for true and false - the classic on/off or yes/no principle in computer science.
8. Script-Keywords
8.1. Comments
Comments are only possible by placing an apostrophe ' at the very front of the line to be commented out:
OMSI also does not recognise an indented area via tab stops as a comment - the tab stop is interpreted as a character. WHOLE FORWARD means WHOLE FORWARD!
8.2. Entry-/Exitpoints, Trigger, Macros
All commands must be between an entry and exit point. Entry points are identified by the following keywords:
{frame} | Scripts defined in this area are executed once in each frame. |
{init} | Scripts defined in this area are executed once when the script is initialised. |
{frame_ai} | Scripts defined in this area are executed once in each frame - but only if the vehicle is not in the player's focus. Does NOT work with scenery objects! |
{macro:''name''} | Scripts defined in this area are only executed when the macro is called. |
{trigger:''name''} | Scripts defined in this area are only executed if the player or OMSI calls this trigger, e.g. by pressing a key. |
{end} | Universal exit point. must be defined for each of the above commands. At the end of a script there must be as many exit points as entry points! |
8.3. Macro calls
A macro is called up via (M.L.''name''). The call of a macro must always be made BEFORE the macro itself.
8.4. Splitting the script into several files
For simple or short scripts, the use of the main {frame}-{end} block (possibly in combination with a {init}-{end} block) can be sufficient. For complex scripts, the use of a main script is recommended, which contains a main script and init block. Sub-scripts can then be defined in this main script using a {Macro:Name}, which are located in their own files.
The following guidelines should be observed:
- A main script containing the {frame} and {init} blocks, which however only call the subscripts with the help of a macro.
- Each subscript should have its own *.osc file and - as needed - its own varlist and consfiles. Key command triggers that belong to this script section should also be defined in the corresponding subscript..
- Macros should be named sensibly so that third parties can follow the scripts - for example {macro:''subsystem''_frame} and {macro:''subsystem''_init}.
- Remember: A macro must always be called BEFORE the macro itself. If subscripts are used, the main script in the configuration file must be called first. In the case of macro calls across files, care must also be taken to ensure the correct sequence.
8.5. Trigger
A {trigger:''name''}...{end}-section can be called from the main programme by various options. These include:
- Trigger by keyboard.
If the key combination has been named ''key combination'', {trigger:key_combination} is called up when the key is pressed, {trigger:key_combination_off} is called up when the key is released. - Triggering by mouse.
If a mesh with the [mouseevent] designation ''mouse_event'' was clicked, {trigger:mouse_event} is called. Holding down the mouse button calls {trigger:mouse_event_drag} and releasing the mouse button calls {trigger:mouse_event_off}. A mesh can also be assigned a normal trigger with [mouseevent]. - The distinction is usually only necessary where the mouse movement has an influence on the animation or the transmitted values (e.g. rotary switch).
* There are also a number of LINK:System triggers that are set by the OMSI game core.
9. Operations
9.1. Stack operations
%stackdump% | Outputs a dialogue box with the floating point stack contents (should only be used for debugging purposes). |
s0, s1, ..., s7 | Saves the current stack value in the register indicated by the digit. The value remains in the stack. |
l0, l1, ..., l7 | Load the corresponding register value and move it into the stack. The value remains in the register |
d | Duplicates the top stack value; all other stack values move to the back. |
$msg | Writes the top string stack value to the debug line of OMSI - no matter if it is a vehicle or scenery object. |
$d | analogous to "d" - duplicates the top string stack value. |
9.2. Logical operations
The logical operations work according to the principle 0 = FALSE, everything else is TRUE.
&& | If the value in stack 0 and stack 1 are identical, this operator outputs 1; otherwise 0. |
|| | or |
! | Negation (Inverts the variable) |
9.3. Comparision operations
The comparison operations compare the values in the two respective topmost stack locations and then insert a 1 or 0 in the topmost stack location depending on the result.
= | "1" if the topmost stack values are identical, otherwise "0". |
< |
"1" if stack value 1 is smaller than stack value 0, otherwise "0". |
> | "1" if stack value 1 is greater than stack value 0, otherwise "0". |
<= | "1" if stack value 1 is less than or equal to stack value 0, otherwise "0". |
>= | "1" if stack value 1 is less than or equal to stack value 0, otherwise "0". |
$= | Like "=" only for the top two stringstack places. |
$< | $< Less than (String). The unequal operations on strings check for alphabetical order. So "A" is smaller than "B". |
$> | $>greater than (string). The unequal operations on strings check for alphabetical order. So "A" is greater than "B". |
$<= | Less or equal (string). |
$>= | Greater or equal (string). |
9.4. Mathematical operations
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
% | Remaining part of the division (extended as follows for floating point numbers: Stack0 - trunc(Stack1 / Stack0) * Stack1 ) |
/-/ | Change of "+"/"-" |
sin | Sinus |
arcsin | Inverse function to the sine |
arctan | Inverse function to the tangent |
min | Selection of the smaller of the two uppermost stack values |
max | Selection of the larger of the two uppermost stack values |
exp | Exponential function to base e (e^Stack0) |
sqrt | Square root |
sqr | Square |
sgn | Return of the sign; either -1, 0 or 1, as the case may be. |
pi | Circle number pi (3,14159265...) |
random | random integer 0 <= x < Stack0 |
trunc | Round off to the nearest whole number |
9.5. String-operations
"blubb" | Inserting the string "blubb" on the top string stack space |
$+ | Joining two strings. "Tutti" "Frutti" $+ results in "TuttiFrutti". |
$* | The top stack string is repeated until the resulting character length is just less than or equal to the top stack value. Example: "nom" 8 $* results in "nomnomno". |
$length | Returns the number of characters of the top stack string back into the stack. |
$cutBegin | Cuts "stack0" characters off the front of the top stack string. |
$cutEnd | Cuts ''stack0'' characters off the back of the top stack string. |
$SetLengthR | Adjusts the length of the top stack string to ''stack0'' right-justified by removing characters or adding spaces at the beginning.* |
$SetLengthC | Adjusts the length of the top stack string to ''stack0'' in the middle by removing characters or adding spaces at the beginning.* |
$SetLengthL | Adjusts the length of the top stack string to ''stack0'' left-justified by removing characters or adding spaces at the beginning.* |
$IntToStr | Round off ''stack0'' and convert the resulting integer to a string. |
$IntToStrEnh | The extended version of IntToStr. Used to pad the string with characters. Example: 11 " 5" $IntToStrEnh leads to " 11" and 123456789 "X11" $IntToStrEnh leads to "XX123456789". If there is an error, "ERROR" is output. |
$StrToFloat | Converts the top stack string to a floating point number, if possible. Otherwise, a -1 is written. |
$RemoveSpaces | Removes all spaces before and after the actual string. " Blank character" becomes "Blank character". |
* Attention: The command does not remove the float operator from the stack. The value to whose length the string was shortened therefore remains on stack0.
10. Variable access
OMSI distinguishes between system variables and local variables. System variables can be read everywhere, local variables are vehicle/object related. System variables are, for example, the time or the weather, local variables are, for example, a switch position in the vehicle or the temperature of the heating.
Furthermore, there are predefined local variables that are not defined by the user. These include, for example, the speed of the bus. Local variables can be assigned by the user without limit. In addition, there are so-called "on-demand" variables, which are predefined by OMSI but still have to be defined by the user when used.
(L.S.''varname'') | Loads the system variable ''varname'' into the top stack space |
(L.L.''varname'') | Loads the local variable ''varname'' into the top stack space |
(S.L.''varname'') | Saves the top stack location in the local variable ''varname'' |
(L.$.''varname'') | Loads the local string variable ''varname'' into the topmost string stack location |
(S.$.''varname'') | Saves the top string stack location in the local string variable ''varname'' |
A detailed description of the individual available system and predefined local variables can be found here: LINK Variables
11. Constants and functions
Local constants and piecewise defined functions can be defined in the constant files (''constfiles'', ''~_constfile.txt''). The structure of each constant file consists of a keyword , a variable and the corresponding values. Only floating point values can be defined, no strings.
[const] defines a new constant and specifies its value:
Example:
[newcurve] initiates the definition of a new (piecewise linear) function:
[pnt] adds a new x-y pair to the function previously defined with [newcurve]. Each function should normally have at least two pairs. The order of the pairs ''must'' be ascending in x-direction!
Example:
Constants are called via the command (C.L.''Constant'') in the script and loaded into the stack.
Functions are called via the command (F.L.''Function''). The value on the topmost stack position is used as "input" (X-paramenter) for the curve. The Y-value defined by the function at position X is output. If the x-value moves outside the limits of the corner points defined by the [pnt] entries, the y-value of the nearest corner point is always used. The function is thus extended horizontally to infinity before the first and after the last corner point.
12. Sound-Trigger
Sound triggers are not triggers that can be controlled with keyboard or mouse inputs. Sound triggers are triggered by the vehicle/scenery script and are used to play sounds. There are two ways to do this:
(T.F. 'Sound Trigger') Plays a sound defined by the script once. Here, the topmost stack string is read and used as file name, whereby the file path is interpreted relative to the sound folder of the object.
Example:
The sound file "Test.wav" defined with the trigger "Soundtrigger" is played when (T.L.MeinTrigger) is called in the script.
"Test2.wav" (T.F.MyTrigger) plays the file Test2.wav. This is necessary, for example, for stop announcements where the current stop is calculated by the vehicle script and thus a different sound file can be played with the same trigger at each stop.
13. System-Macros
The functions highlighted so far allow the script to communicate with OMSI as follows:
- Read values via system and predefined local variables.
- Write values via these same variables
- Receive keyboard, mouse or system triggers.
In addition to these possibilities, there are so-called "system macros". These macros are complex functions that expect input from the OMSI script and transmit data back to the script according to their function. This is necessary, for example, to read out the farm file.
The application is identical to self-defined macros, but the system macro expects information that it fetches from the stack and writes the result back into the stack.
A list of all system macros can be found here: LINK System Macros
14. Conditions and loops
The only control of the programme flow is currently only possible with the IF condition. Loops and Go-To's are only conditionally possible with the OMSI script system.
14.1. IF-Conditions
'There must be a condition here:
(L.L.blubb) 1 =
{if}
'This section is executed when blubb = 1:
2 3 +
{else}
'This section will be executed otherwise:
3 4 +
{endif}
(S.L.bla)
Due to the backwards notation with stack processing, it also makes sense that the condition is defined first: the values must first be in the stack so that the operator can compare them. The result is 0 or 1, accordingly true or false. With this value, the {if} condition can then also be evaluated.
Thus, in the above example, if ''blubb'' has the value 1, ''bla'' is set to the value 5, otherwise to the value 7.
Nests of the IF condition serve as a substitute for the (non-existent) "Else-If" constructions:
'There must be a condition here, e.g.:
(L.L.blubb) 1 =
{if}
'This section is executed when blubb = 1:
2 3 +
{else}
(L.L.blubb) 5 =
{if}
'This section is executed when blubb = 5:
3 4 +
{else}
'This section will be executed otherwise:
13
{endif}
{endif}
(S.L.bla)
Alles anzeigen
Here must be paid special attention to the double {endif}! For each opened {if} condition, a {endif} must also be set.