...
Modifying the Loco Shed Script
When writing a new script it is often useful to start with a working script and add changes to observe the effects.
So I cloned the loco shed asset and renamed it "Pauls Engine Shed - Varying Texture". The original script was renamed Pauls_Shed.gs and the class name renamed from Tutorial to Pauls_Shed. The base class is Buildable which obviously is not suitable for Kieran's cars but that doesn't concern me at present.
Debugging
Andi Smith offers some good advice in his tutorial which is to make small incremental changes. Trainz has very primitive debugging facilities and the only way to validate or observe an effect is to use the Interface.Print or Interface.Log functions. Interface.Print uses the message window which is only visible in Driver so for this project I am mostly limited to using Interface.Log which writes a message to the JetLog.txt file. So, in the code snippets that I will include you will see lots of Interface.Log function calls.
(See Note 1)
OK, down to business:
The loco shed asset allows the user to select a texture from within a property browser from a list of textures obtained from a texture library. The user requirement is for a random texture to be allocated when the asset is created. To complicate matters a little, a new requirement is to retain any existing texture allocation if the asset was created earlier. So the basic logic is:
if the asset has a recorded texture in the Trainz database
then use that texture
else pick a random texture from the texture library
(See Note 2)
So where to execute this bit of logic? Andi's shed script is subclassed from Class Buildable is a sub class of PropertyObject that, conveniently, is also a parent class of many other classes including , hopefully, one I can use for Kieran's product asset. Lost? See http://online.ts2009.com/mediaWiki/index.php5/TrainzScript_Library_Reference
Class PropertyObject has a method called SetProperties() that is called as part of asset initialisation and can be used to recall the assets saved properties from the Trainz Database - also named the Soup. So here is my revised copy of an overridden SetProperties. Some parts of this code are original bits relating to the shed and I have marked those.
OK, where did numberOfSkins come from? This variable contains the number of textures in the texture library. I added some code in the class Init() method to get this value.
So far, this is the only part of the original code that I have changed. To test it I ran it in Surveyor and added five new loco sheds. The visual result is as follows:
You will note that the second from the left and the right hand sheds both have the same texture. This is confirmed by checking the jetlog.txt file that contains:
Note that I had to move and rotate the sheds to get them in a nice line so the jetlog entries don't reflect the order in the picture but you can see that texture 2 was used twice.
When I first ran this test I ended up with three sheds out of five with the same texture. This concerned me so I modified the code to add a loop of 100 rand calls and analysed the results. As I had six sheds showing I ended up with 600 results so I got more than I expected. The analsyis of the rand function showed this:
Texture 0 - 70
Texture 1 - 60
Texture 2 - 49
Texture 3 - 60
Texture 4 - 48
Texture 5 - 58
Texture 6 - 60
Texture 7 - 65
Texture 8 - 67
Texture 9 - 63
Overall the spread of texture selections is reasonably even. Perhaps the small range of values available for the rand function throws up more duplicates than one might expect.
Conclusions:
Using Andi's Loco Shed script as a basis for development of Kieran's Beetle requirement appears to be worthwhile. Currently, I am unsure which base class to use for Kieran's "product" Beetle so I need to investigate further and maybe find an example. If that fails I shall experiment with a likely base class.
Next
In the next episode I hope to have a new script, based on the modified loco shed script, that implements the random car texture. This may take a couple of days.
Notes
Note 1:
For the coding purists: The names for functions/procedures/subroutines/etc in class libraries are often called "methods" as they are a method of accessing an object. Sometimes it makes sense (to me) to call them methods and sometimes functions. But if I occasionally get them wrong then I apologise in advance. )
Note 2:
When writing code I like to write out the basic logic in simple English first before converting to the actual programming language in use. Often, I will leave that simple English as part of the programming comments. I find this helps me work out the process. This is a trick I learned when writing my first professional program back in the early 80's that was entirely in assembly language.
Modifying the Loco Shed Script
When writing a new script it is often useful to start with a working script and add changes to observe the effects.
So I cloned the loco shed asset and renamed it "Pauls Engine Shed - Varying Texture". The original script was renamed Pauls_Shed.gs and the class name renamed from Tutorial to Pauls_Shed. The base class is Buildable which obviously is not suitable for Kieran's cars but that doesn't concern me at present.
Debugging
Andi Smith offers some good advice in his tutorial which is to make small incremental changes. Trainz has very primitive debugging facilities and the only way to validate or observe an effect is to use the Interface.Print or Interface.Log functions. Interface.Print uses the message window which is only visible in Driver so for this project I am mostly limited to using Interface.Log which writes a message to the JetLog.txt file. So, in the code snippets that I will include you will see lots of Interface.Log function calls.
(See Note 1)
OK, down to business:
The loco shed asset allows the user to select a texture from within a property browser from a list of textures obtained from a texture library. The user requirement is for a random texture to be allocated when the asset is created. To complicate matters a little, a new requirement is to retain any existing texture allocation if the asset was created earlier. So the basic logic is:
if the asset has a recorded texture in the Trainz database
then use that texture
else pick a random texture from the texture library
(See Note 2)
So where to execute this bit of logic? Andi's shed script is subclassed from Class Buildable is a sub class of PropertyObject that, conveniently, is also a parent class of many other classes including , hopefully, one I can use for Kieran's product asset. Lost? See http://online.ts2009.com/mediaWiki/index.php5/TrainzScript_Library_Reference
Class PropertyObject has a method called SetProperties() that is called as part of asset initialisation and can be used to recall the assets saved properties from the Trainz Database - also named the Soup. So here is my revised copy of an overridden SetProperties. Some parts of this code are original bits relating to the shed and I have marked those.
Code:
public void SetProperties(Soup soup) {
inherited(soup); // This must be done as we are adding to the original method
roofVisible = soup.GetNamedTagAsBool ("roof",true); // original code - ignore
SetMeshVisible("roof",roofVisible,1.0); // original code - ignore
//nameText = soup.GetNamedTag("name"); // original code - ignore
nameText = GetLocalisedName(); // original code - ignore
SetNameText(); // original code - ignore
skin = soup.GetNamedTagAsInt("skin"); // this gets the saved texture (or skin as Andi called it) as a number (integer)
// note that the texture number could be 0 (zero) which means either nothing was saved or the default texture was
// used
// this bit is mine where if the saved texture is 0 then we will randomly allocate one
if (skin == 0)
{
skin = Math.Rand(0,numberOfSkins+1); // returns a random number between 0 and the number of skins
Interface.Log("Skin number is " + skin); // debug only
}
string temp = soup.GetNamedTag("skinDescription"); // original code - ignore
if (temp != "") skinDescription = temp; // original code - ignore
SetSkin(skin); // original code - this is where the actual texture is set
}
OK, where did numberOfSkins come from? This variable contains the number of textures in the texture library. I added some code in the class Init() method to get this value.
Code:
public void Init(void) {
inherited(); // original code - ignore
SetFXCoronaTexture("corona",null); // original code - ignore
SetNameText(); // original code - ignore
AddHandler(me,"Object","","ObjectHandler"); // original code - ignore
// This is new and gets the asset reference from the config.txt kuid table. "skins" is the entry for the texture library kuid.
skins = GetAsset().FindAsset("skins");
if (skins) // note if the skins asset is missing then the variable skins will be void.
{
// get a reference to the textures list in the texture library asset.
// note we are using Andi Smith's "AJS Texture Library" <kuid:122285:3599> for this.
Soup soup = skins.GetConfigSoup().GetNamedSoup("textures");
// this call provides the number of textures in Andi's library into numberOfSkins.
numberOfSkins = soup.CountTags();
Interface.Log("Number of skins is " + numberOfSkins); //more debug but a reassurance that we are using a valid library
// note that Andi's texture library has nine skins numbered 0 to 8 but 0 is a blank texture.
// result[result.size()] = Str.Tokens(soup.GetNamedTag(n),".")[0]; //original code - ignore
}
else
{
Interface.Log("Skins asset is missing from config.txt");
}
}
So far, this is the only part of the original code that I have changed. To test it I ran it in Surveyor and added five new loco sheds. The visual result is as follows:
You will note that the second from the left and the right hand sheds both have the same texture. This is confirmed by checking the jetlog.txt file that contains:
Code:
? 1:19.4 : SCRIPT> Skin number is 2
? 1:21.0 : SCRIPT> Skin number is 5
? 1:22.3 : SCRIPT> Skin number is 0
? 1:23.7 : SCRIPT> Skin number is 2
? 1:27.7 : SCRIPT> Skin number is 8
Note that I had to move and rotate the sheds to get them in a nice line so the jetlog entries don't reflect the order in the picture but you can see that texture 2 was used twice.
When I first ran this test I ended up with three sheds out of five with the same texture. This concerned me so I modified the code to add a loop of 100 rand calls and analysed the results. As I had six sheds showing I ended up with 600 results so I got more than I expected. The analsyis of the rand function showed this:
Texture 0 - 70
Texture 1 - 60
Texture 2 - 49
Texture 3 - 60
Texture 4 - 48
Texture 5 - 58
Texture 6 - 60
Texture 7 - 65
Texture 8 - 67
Texture 9 - 63
Overall the spread of texture selections is reasonably even. Perhaps the small range of values available for the rand function throws up more duplicates than one might expect.
Conclusions:
Using Andi's Loco Shed script as a basis for development of Kieran's Beetle requirement appears to be worthwhile. Currently, I am unsure which base class to use for Kieran's "product" Beetle so I need to investigate further and maybe find an example. If that fails I shall experiment with a likely base class.
Next
In the next episode I hope to have a new script, based on the modified loco shed script, that implements the random car texture. This may take a couple of days.
Notes
Note 1:
For the coding purists: The names for functions/procedures/subroutines/etc in class libraries are often called "methods" as they are a method of accessing an object. Sometimes it makes sense (to me) to call them methods and sometimes functions. But if I occasionally get them wrong then I apologise in advance. )
Note 2:
When writing code I like to write out the basic logic in simple English first before converting to the actual programming language in use. Often, I will leave that simple English as part of the programming comments. I find this helps me work out the process. This is a trick I learned when writing my first professional program back in the early 80's that was entirely in assembly language.