Trainz/Scenarios (Scripted Activities)
Introduction
Scenarios are interactive scripted activities. They vary in nature considerably, some providing step-by-step instructions for each stage of an operation, others merely displaying the details of an objective and leaving it to user to devise a method of achieving it. Shunting puzzle scenarios tend to adopt the latter style. A wide variety of different events can be triggered within a scenario. It is possible to display HTML text and images, and to play sounds.
Scenarios are fully supported in TRS2004. They are only partially supported in TRS2006 and Trainz Classics.
Some Methods Of Creating Scenarios
- TRS2004 owners can use the built-in method of creating the initial structure of a scenario, then using a text editor program to write the necessary coding. This method involves programming in GameScript, which is a "C"-like language with numerous extensions.
- Both TRS2004 and TRS2006 owners can use the SCS (Scenario Creation System) program which is offered as freeware by the Trainz Pro Routes third-party Trainz fansite. There are separate versions of SCS for TRS2004 and TRS2006. SCS aims to make the scenario creation process as straightforward as possible but even then some considerable effort will be required. Full manuals are available for SCS.
An Overview Of TRS2004 Scenario Creation
The GameScript API (Application Programming Interface) documentation can be found in the "Trainz Railroad Simulator 2004 User Activity Creation Guide" which can be downloaded from Auran's web site. Some familiarity with the concepts and individual functions listed in this document is vital.
The following is a highly simplified overview of what is inherently quite a complicated procedure. To keep things as simple as possible, it omits many details.
Open the relevant layout in Surveyor.
Add any desired trackmarks, triggers and other trackside objects. Trains can be created at trackmarks. Events will be triggered when trains enter triggers. Do not save the changes to the layout.
In the Surveyor menu, invoke the "Export Scene Data" option and choose a suitable name for the new scenario.
Close Surveyor. Once again, do not save the changes to the layout.
A new folder will have been created in C:\Program Files\Auran\TRS2004\World\Custom\scenarios folder with the name that you specified in Surveyor. This newly created folder will contain the files which form the basis for the scenario.
The TSO (trackside object) file contains the details of the modifications to the layout which are individual to this scenario, for example the additional trackmarks and triggers that you added in Surveyor.
Using a text editor, open the scenario's config.txt file. The kuid-table will already include the layout. You need to add the rolling stock assets that the scenario uses, for example: (How!!!)
kuid-table
{
testscenario <kuid:154110:5701376>
AN_830_Class <kuid:-1:100737>
QR_QLX <kuid:-1:101154>
}
If you wish, also embellish the description text.
Using a text editor, open the gs (GameScript) file. By default this file contains a daunting amount of coding, almost all of which should be left unmodified. You need to add additional sections of code in the correct places.
In the "create consist specs" section, add coding to specify the rolling stock which makes up each train when it is initially placed on the layout. The following example has one train consisting of an AN830 Class loco pulling one QLX louvre wagon:
KUID[] PlayerTrainKuids = new KUID[ 2 ]; PlayerTrainKuids[ 0 ] = World.FindKUID( "AN_830_Class" ); PlayerTrainKuids[ 1 ] = World.FindKUID("QR_QLX" );
In the "create consists" section, add coding to place the trains at trackmarks. The following example places a train made up of the "PlayerTrainKuids" consist specification at a trackmark named TM01:
Train PlayerTrain = World.CreateTrain( PlayerTrainKuids, "TM01", true );
In the "gameplay" section, above the "scenarioDone = true;" line, add coding to set some initial parameters and perform the scenario's actions. The following is a very simple example which merely allows the user to drive the PlayerTrain and nothing else.
World.SetGameTimeRate( World.TIME_RATE_1X ); World.SetGameTime( 0.875 ); // 9am World.SetWeather( World.WEATHER_TYPE_CLEAR, World.WEATHER_CHANGEABILITY_NONE ); World.SetCamera( PlayerTrain, World.CAMERA_EXTERNAL ); World.SetCameraAngle( 75, -15, 50 ); PlayerTrain.SetAutopilotMode( Train.CONTROL_MANUAL ); while( 1 == 1 ) { Sleep( 0.1 ); }
In the above example the "while" loop will wait forever until the user exits the scenario. A real scenario would normally complete properly and shut down without user intervention being required.
No compilation is required. Once the gs file is correctly created, the scenario can be run. In practice, there is usually a need for numerous iterations around the editing and testing cycle.
Some Comments Regarding GameScript For People Familiar With Traditional BASIC or Visual BASIC
A few simple examples can be found at the end of this section.
The following applies to GameScript as used to create TRS2004 scenarios. Its use in scriptlets, scripted assets, etc is different in some important respects to what follows here.
For anyone familiar with any form of BASIC, learning GameScript will probably prove quite difficult. This section attempts to list some of the "gotchas" that may confuse beginners.
GameScript does not have a dedicated program editor or IDE (integrated developement environment). Instead, a text editor program such as Notepad or a superior alternative is used to edit programs.
In GameScript, entities such as trains, signals, junctions, etc are regarded as being objects with properties that can be specified and accessed by the program.
GameScript is based on the "C" language. "C" has numerous differences to BASIC, for example:
- In GameScript, all the coding is held in one file. Because there is no integrated development environment, all the parts of the code are visible when editing.
- Variables must be declared. They can be declared when first given a value if desired; the syntax is to specify the variable type (int, float, etc), then its name, then a equals sign, then an expression which gives the variable its initial value.
- The syntax for declaring arrays will seem very strange to BASIC programmers. See the example below.
- Comment lines start with two forward slashes.
- Array subscripts are enclosed in square brackets, not round brackets.
- Groups of dependent statements are enclosed in matching pairs of curly brackets. These include groups of statements in FOR loops, in WHILE loops, and in IF statements. Therefore there are no NEXT, WEND or ENDIF statements in "C".
- Most statements must have a semi-colon on the end, but not all. Examples of statements which must not have semi-colons include:
- case
- default
- FOR loop headers, WHILE loop headers and IF statement headers where the dependent statements that follow are enclosed in curly brackets.
- include
- switch
- function definition header lines
- labels (targets for goto statements)
- Most operations are performed using functions, or statements which have the same syntax as functions. Therefore in many cases parameters must be enclosed in round brackets where none are required in BASIC.
- Assignment statements take one equals sign, relational expressions in IF statements take two equals signs. Not equals is !=
- "C" features a large number of obscure methods of incrementing and decrementing variables using two adjacent arithmetical operators. Since these are difficult to remember and therefore open to confusion, they are probably best avoided by beginners. The difference in performance caused by not using these methods is trivial in the extreme compared to the millions of calculations required to generate each screen frame in Trainz.
- Instead of variables being converted from one type to another with functions such as STR$ and NUM, or automatically as in Visual BASIC, in "C" the <cast> statement is used.
- Unlike Visual BASIC, GameScript is not inherently event driven. There is no concept of "click on this object to run this little bit of code". However, event handlers (sections of code which are obeyed when an event occurs) can be created. These are particularly useful for handling AI train events, in situations where they can occur asynchronously with the scenario processing and the user's actions. However, GameScript also has the Schedule mechanism for controlling AI trains independently of scenario execution.
- A function prefixed with the word void does not return a value.
- In Auran's API documentation, a function with the parameter ( void ) does not take a parameter, but does require the empty pair of round brackets.
- "C" does not have an exponentiation operator.
- GameScript uses and and or in complex relational expressions, in the same way as in BASIC. Normal "C" is different. Bitwise Boolean operations are possible but probably best ignored by beginners.
- GameScript uses true and false for literal Boolean values.
- GameScript uses + for string concatenation, in the same way as BASIC.
- Whereas Visual BASIC consistently uses the Object.Property syntax, GameScript only does so to a limited extent. GameScript uses the SetOfFunctions.SpecificFunction syntax extensively (possibly universally). In GameScript, in most cases a property of an object must be specified using a function whose name starts with Set..., and a property of an object must be obtained using a function whose name starts with Get...
- Whereas in Visual BASIC it is vital to regularly perform a DoEvents statement so as to refresh the screen and prevent Windows locking up, in GameScript there is only a general fairly obvious need to avoid tight loops and insert Sleep functions in such situations. GameScript functions which pause scenario execution while waiting for an event will not lock up Trainz or Windows.
- Some of the functions in GameScript can take a varying number of parameters, usually with the omitted parameters being given sensible default values.
- Some of the functions in GameScript operate differently depending on the variable type of the parameters they are supplied with. For example, the function for specifying the camera angles regards its parameters as being in degrees if they are integers but in radians if they are floating point.
As with Visual BASIC, for the beginner there is considerable confusion regarding which aspects are handled by the operating system or the programming language, and which must be handled by the programmer. In Trainz there are the additional complicating possibilities of the user's train and AI trains performing train movements asynchronously with the scenario.
- Windows and Trainz handle all the hardware aspects of input and output.
- Trainz will automatically call the main() section of the program when the scenario starts.
- Once the user's train has been created and control given the user, the user can drive it forwards or backwards as they desire. The scenario needs to react as events occur, such as the user's train entering a trigger or a junction.
- Trainz will automatically call some other sections of the program when a user train overspeed event occurs, when a collision event occurs, etc. The default coding to handle these events is created when a new scenario is created but with sufficient knowledge it can be modified.
- There are functions available in GameScript to pause scenario execution at a point in the coding until an event occurs, such as the user's train entering a junction or a trigger.
As with Visual BASIC, in addition to the relatively straightforward core language there is also a need to learn the vast number of objects, their properties and their functions. These are documented in the Auran GameScript API.
An important practical comment: in the Auran GameScript API document, a pair of matching adjacent square brackets may be displayed so closely together as to appear to be an outline box.
In Trainz, objects internally pass messages between themselves. These internal messages are not seen by the user. GameScript coding can generate internal messages and also detect and receive internal messages. Each internal message has four properties: its source, its destination, its major (i.e. its major message type) and its minor (i.e. its minor message type). While this message passing concept can be ignored by the beginner, it is often featured in the Auran documentation and therefore worthy of mention here.
One final vital piece of advice: start with something really, really simple. Making a train appear on a track is in itself a very considerable achievement for someone starting to learn GameScript.
Example #1: IF Statement
//Equivalent to IF A=10 THEN... if (a==10) { //dependent statements go here }
Example #2: FOR Loop
// Equivalent to FOR N=1 TO 5 STEP 1 for (n=1; n<=5; n=n+1) { //statements within the loop go here }
Example #3: WHILE Loop
while (a<50) { //statements within the loop go here }
Example #4: Variable declaration and initialisation
int x=8;
Example #5: Declare an array
//Equivalent to DIM A(12) - possible subscript values will run from 0 to 12 float[] A = new float[ 13 ];
Example #6: Define a user-defined function
int Cube(x) { //local variable declarations go here //function statements go here return (x*x*x); }