John Cletheroe's
Trainz Hintz - A Step-By-Step Guide To Making A Very Simple TRS2004 Scenario


TRS2004 Scenario Creation Tutorial - Part Eleven: Editing The Scenario's gs File

Introduction

The scenario's gs file holds its GameScript/TrainzScript source code.

Editing the scenario's gs file, testing it, and correcting mistakes in it, are by far the most complex and time-consuming parts of the scenario creation process.

The scenario's gs file is in this folder:

C:\Program Files\Auran\TRS2004\World\Custom\scenarios\name
(where "name" is the name of the scenario)

The file will be called:

name.gs
(where "name" is the name of the scenario)

This file can be edited by a text editor program such as Notepad.

When it is first created by the Surveyor "Export Scene Data" option, the scenario's gs file will contain a template, in other words a default set of GameScript/TrainzScript programming language statements. This template must be modified and added to.

The template is quite lengthy, in several sections, and extremely daunting to the newcomer. However, most of the template can be left untouched and there is no need to understand much of it in detail.

We will now describe the purpose of each section of the template in sequence, whether it needs to be modified, and if so then how that is done.

The .gs File In Detail

include "trainz.gs"
This is default coding which adds most of the commonly used additional library files to the scenario's program. No changes are required to this part, but if your scenario uses other libraries then this is the place to specify them. Libraries are an advanced feature which can be ignored by the beginner. Note that unlike most statements, this statement intentionally and correctly does not have a semi-colon on the end.

//// class Scenario01
// brief This is the scenario class.  Modify this class with
// your own gameplay.
//
These are default comments. No changes are required.

game class MyScenario01 isclass Scenario
{
Train myConsist;
bool scenarioDone = false;
This is default coding which introduces the Scenario's class, declares a Train object variable and a boolean variable. No changes are required, but if your coding uses additional variables then this is the place to declare them. Declaring effectively reserves memory space for a variable. Variables must be declared once and only once in each function that they are used in. While an object-oriented purist might object, at this stage a "class" can be thought of as a set of related functions that are grouped together. There is no need for beginners to understand the syntax of the game class statement.

//
// Load will be called by Trainz to load the scenario map, and when the user presses Ctrl-L
// param data is the save game data if loading a saved game.
//
bool Load(string data)
{
  if(data and data.size())
  {
    Interface.Load(data);
  }

  // load the map
  if(!World.LoadMap(World.FindKUID("layout_01")))
  {
    Interface.Log("Error loading scenario map");
    return false;
  }
  return true;
}
This is default coding to handle the loading of the layout (map). No changes are required. This and the next few sections are all definitions of standard event handler functions. At this stage there is no need to understand them in detail.

//
// Save will be called by Trainz when the user presses Ctrl-S.
// return the save game string, such that load will be able to restore the save game
// from the last save check point.
//
string Save()
{
  return Interface.Save();
}
This is default coding to handle user Save Game (which doesn't work). No changes are required.

//
// TrainDerailed will be called by Trainz when a train derails
//  void TrainDerailed(int trainId)
{
  if(!scenarioDone)
  {
    World.EndScenario(10);
    scenarioDone = true;
  }
}
This is default coding to handle derailments. No changes are required.

//
// TrainCollided will be called by Trainz when a train collides
//
void TrainCollided(int trainId)
 {
  if(!scenarioDone)
  {
    World.EndScenario(10);
    scenarioDone = true;
  }
}
This is default coding to handle collisions. No changes are required.

//
// TrainSpeedingFine() is called by Trainz every second your train's speed exceeds the floating limit
//
void TrainSpeedingFine()
{
  //Interface.AdjustScore(-10);
}
This is default coding to handle speeding events. No changes are required. Note that the statement to decrement the score is intentionally commented out, and thus is intended as an example.

//
// TrainBadCouple() is called by Trainz when vehicles couple greater than 8KPH.
//
void TrainBadCouple(int vehicleId)
{
  //Interface.AdjustScore(-200);
}
This is default coding to handle bad couplings. No changes are required. Again, the example is commented out.
If your scenario uses its own event handler functions written by you, then this is where they should go. Event handling is a relatively advanced concept which is best ignored by beginners.

//
//
// main thread
// main is executed automatically after Load() is called.
// edit main to contain your scenario's gameplay.
//
//

thread void main(void)
{
  // Start the monitor thread to monitor speeding, derailing etc.
  Monitor();
This is default coding and comments at the start of the main part of the program. No changes are required. As before, there is no need to understand this in detail.

//
// create consist specs
//
KUID[] playertrainkuids = new KUID[1];
playertrainkuids[0] = World.FindKUID("AN830");
The comments are there by default. Statements must be inserted here to create the specifications for each consist (each train). The two statements above are examples which I have added.
The first statement (starting "KUID[]") declares an array which will hold the kuid's of the rolling stock vehicles in the train that the user will drive.
The second statement (starting "playertrainkuids[0]") causes TRS2004 to look up the kuid of the vehicle called "AN830" in the kuid-table section of the scenario's config.txt file. Most scenarios would have several trains defined, and each train would have several vehicles, typically a locomotive followed by some carriages or wagons. It is vital that each rolling stock vehicle name specified here matches precisely in every respect (spelling, upper case or lower case of each letter, plus the presence or absence of underline characters) with the one in the config.txt file's kuid-table. This is the name used for the vehicle in this scenario; it need not be the same as the asset's actual name.
Some documentation uses the name "usertrainkuids" instead of "playertrainkuids". Any name can be used, but it must be used with total consistency throughout the program.

//
// create consists
//
Train playertrain = World.CreateTrain(playertrainkuids, "Trackmark 0", true);
The comment is there by default. I have added the statement below it as an example. Statements must be inserted here to create each train at specified trackmarks on the layout.
The first parameter of the World.CreateTrain function specifies the name of the array which holds the kuids for this train.
The second parameter of the World.CreateTrain function specifies the trackmark at which this train is to be initially placed. This second parameter must be enclosed by double-quotes.
The third parameter of the World.CreateTrain specifies whether the train is to be placed in the same direction as the trackmark is pointing (true) or the opposite direction (false).
The purpose and parameters of functions such as World.CreateTrain are documented in the API. There are a great number of these functions, and becoming familiar with those which are most commonly used forms a large part of the GameScript/TrainScript learning process.
The same consist specification can be created at more than one trackmark. In other words, identical copies of the same train can be created at more than one place on the layout. It is also possible of course to create different trains at different places.
The name of the trackmark specified here must be identical in every respect with its name in the layout. That includes spelling, upper case or lower case of each letter, plus the presence or absence of spaces and underline characters.
Some documentation uses the name "usertrain" instead of "playertrain". Any name can be used, but it must be used with total consistency throughout the program.

//
// gameplay
//
These are default comments. No changes are required, except that if your scenario uses its own event handler functions, this is where you would probably want to insert the AddHandler functions to activate them. Event handling is an advanced feature best ignored by the beginner.

World.SetGameTimeRate( World.TIME_RATE_1X );
World.SetGameTime( 0.875 );	// 9am
World.SetWeather( World.WEATHER_TYPE_CLEAR, World.WEATHER_CHANGEABILITY_NONE );
All of the above are example statements which I have added. Statements must normally be inserted here to specify the scenario's time rate, its initial time of day, and its initial weather conditions and weather changeability.
In most cases, statements to set the initial directions of junctions would also be inserted here.
The syntax World.SetGameTimeRate specifies that the function SetGameTimeRate can be found in the World class, i.e. the World set of functions.

World.SetCamera( playertrain, World.CAMERA_EXTERNAL );
World.SetCameraAngle( 75, -15, 50 );
playertrain.SetAutopilotMode( Train.CONTROL_MANUAL );
The above are example statements which I have added. Statements must normally be inserted here to specify the object that the camera is initially pointing at (usually the playertrain), and the camera angles and distance. The third of the above statements gives the user control over the train.
The syntax playertrain.SetAutopilotMode specifies that the SetAutopilotMode function is to be performed on the playertrain.
Sometimes the full stop syntax is used to specify which class a function can be found in, and in other cases it specifies which game object the function is to be performed on. Sometimes the game object on which a function is to perform an operation is specified as one of its functions instead of by means of the full stop syntax. There is probably a logical rule which determines these things but to me they appear arbitary and inconsistent. The TRS2004 GameScript/TrainzScript API documentation lists the functions and some extent reveals which form of syntax each uses.

while( 1 == 1 )
	{
	Sleep( 0.1 );
	}
The above is all example coding which I have added. The main body of the scenario's code would go here. This example merely waits indefinitely until the user exits the scenario, while allowing the user to drive the train. In this case the Sleep function is vital so as to allow TRS2004, Windows and other programs some processor time.
While all this may seem an amazingly complicated way to merely drive a train along a track, you will realise that in a real scenario far more interesting things can be achieved. This scenario does very little because my aim was to keep this tutorial as simple as possible.

  scenarioDone = true;
  }
};
This is default coding to finish the scenario, plus the necessary matching closing curly brackets for the main function and the scenario's class. This is the only situation I am aware of where a semi-colon is needed after a close curly bracket.

Index

Part Ten: Editing The Scenario's config.txt File

Part Twelve: Running The Scenario


Overall Site Home Page
About this personal web site JohnCletheroe

EMail me

Most recently modified 16-Apr-10