Scripting Task - Random Car Liveries - Part 2

pcas1986

Well-known member
...

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:


5sheds2.jpg


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. :eek:)

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.
 
I didn't understand a word of this pcas lol, But thank you for the time and effort to produce it i would like to implement this at sum stage so i'll definitely be saving this tut.
Cheers Mick.
 
For Matruck,

It's not really a tutorial - if it was then I would write somewhat differently. Its a kind of experiment to record my thought processes when trying to solve a scripting problem. After telling my customer I would do it I then had second thoughts. :eek:

But feel free to ask a question on scripting if and when you have the need to do so.
 
As long as you keep on recording your thought's and process's then I'll eventually give it a try pcas, Thank's again.
Mick.
 
Paul, feel free to bail! i'm not paying you. Although if you do get a few extra dollars in your PayPal account, don't be surprised! :hehe:
 
I did read it over several times and wondered if anyone could make sense of it. It was a bit warm here yesterday and maybe my brain was fried. About a month ago we had snow in the hills and yesterday was 34 or so. It was a bit uncomfortable working outside on my deck.
 
Hello
Just noticed your account of finding you can't script a "product".
I've been playing with multiple random colour of carz as loads (products) on and off for maybe 2 years.
The closest I've come to "utopia" is to use the "conflat" script from the Freightliner FFA "Loaded" wagon (excellent DLS item). You have to put your various coloured "loads" in different queues but at the same attachment point. Also needed is the "Conflicts-With-Queues" tag - all as per the FFA wagon Confix.txt.

For pure texture replacement, several TC3 wagons provide an excellent starting point - examples are "12 ton ventilated van" and "HTO Hopper Wagon". But, yes, I,'ve yet to find a way of getting this to work on a Product. Currently, I'm telling myself its impossible - but there's no better invitation to have a go lol!

Essentially this assumes you have the same mesh asset in all cases. Unfortunately, this means you are stuck with one reference (from within the mesh) to a texture.txt file. If (a big if) there are multiple examples of the same mesh each with a different textute.txt file, then you CAN do texture-replace in a Product mesh table by quoting whichever texture.txt file you want to use with whichever mesh.

I feel a owe you an example here, but it is exactly as shown on the N3V Wiki.

The beauty is, that in a "product", Trainz randomly selects one mesh from several in the mesh table - totally unscripted. A DLS example is called "Multibox Product".

Regards

Steve
 
Back
Top