Sunday 22 February 2015

TS2015 - Scenario Scripting in LUA Part 13 - Example Script

I've had a couple of requests to put the entire script from the YouTube video tutorial up so that it can be reviewed and understood in its entirety - so without further delay, here it is!

Note that I didn't use some of the techniques discussed here such as the _G[]() idea because the goal was to be quick and simple, but using this and the learnings in previous blog posts you should be able to use this in an effective way.

-- true/false defn
FALSE = 0
TRUE = 1

-- condition return values
CONDITION_NOT_YET_MET = 0
CONDITION_SUCCEEDED = 1
CONDITION_FAILED = 2

-- Message types
MT_INFO = 0 
MT_ALERT = 1 

MSG_TOP = 1
MSG_VCENTRE = 2
MSG_BOTTOM = 4
MSG_LEFT = 8
MSG_CENTRE = 16
MSG_RIGHT = 32

MSG_SMALL = 0
MSG_REG = 1
MSG_LRG = 2

function DisplayRecordedMessage( messageName )
  SysCall("RegisterRecordedMessage", "StartDisplay" .. messageName,  
      "StopDisplay" .. messageName, 1);
end

function StartDisplayIntroText()
  SysCall ( "ScenarioManager:ShowInfoMessageExt", "Title of the box",  
      "intromessage.html", 0, MSG_VCENTRE + MSG_CENTRE, MSG_REG, TRUE );
end

function StopDisplayIntroText()
end

function StartDisplayMoving()
  SysCall ( "ScenarioManager:ShowInfoMessageExt", "You're off!", 
      "moving.html", 0, MSG_VCENTRE + MSG_CENTRE, MSG_REG, TRUE );
end

function StopDisplayMoving()
end

function StartDisplayOverspeed1()
  SysCall ( "ScenarioManager:ShowInfoMessageExt", "Too fast!", 
      "overspeed1.html", 0, MSG_VCENTRE + MSG_CENTRE, MSG_REG, TRUE );
end

function StopDisplayOverspeed1()
end

function StartDisplayOverspeed2()
  SysCall ( "ScenarioManager:ShowInfoMessageExt", "Final warning!", 
      "overspeed2.html", 0, MSG_VCENTRE + MSG_CENTRE, MSG_REG, TRUE );
end

function StopDisplayOverspeed2()
end

function OnEvent(event)
  if (event == "introtext") then
    DisplayRecordedMessage("IntroText");
  end
 
  if (event == "cinematic") then
    SysCall ( "CameraManager:ActivateCamera", "introcinematic", 0 );
  end
 
  if (event == "cabcamera") then
    SysCall ( "CameraManager:ActivateCamera", "CabCamera", 0 );
  end

  if (event == "startmovingcheck") then
    SysCall ( "ScenarioManager:BeginConditionCheck", "StartMovingCondition" )
  end
 
  if (event == "startoverspeedcheck") then
    SysCall ( "ScenarioManager:BeginConditionCheck", "OverspeedCondition" )
  end
 
  if (event == "starttoofastcheck2") then
    SysCall ( "ScenarioManager:BeginConditionCheck", "OverspeedCondition2" )
  end
 
  if (event == "starttoofastcheck3") then
    SysCall ( "ScenarioManager:BeginConditionCheck", "StopCondition" )
    SysCall ( "ScenarioManager:BeginConditionCheck", "OverspeedCondition3" )
  end
end

function TestCondition(condition)
  if (condition == "StartMovingCondition") then
    speed = SysCall ( "PlayerEngine:GetSpeed" );
    if (speed > 0.89) then
      DisplayRecordedMessage("Moving");
      return CONDITION_SUCCEEDED;
    end
    return CONDITION_NOT_YET_MET;
  end
 
  if (condition == "OverspeedCondition") then
    speed = SysCall("PlayerEngine:GetSpeed");
    if (speed > 8.904) then
      DisplayRecordedMessage("Overspeed1");
      SysCall ( "ScenarioManager:TriggerDeferredEvent", "starttoofastcheck2", 5 );
      return CONDITION_SUCCEEDED;
    end
    return CONDITION_NOT_YET_MET;
  end
 
  if (condition == "OverspeedCondition2") then
    speed = SysCall("PlayerEngine:GetSpeed");
    if (speed > 8.904) then
      DisplayRecordedMessage("Overspeed2");
      SysCall ( "PlayerEngine:SetControlValue", "Regulator", 0, 0);
      SysCall ( "PlayerEngine:SetControlValue", "TrainBrakeController", 0, 1);
      SysCall ( "PlayerEngine:SetControlValue", "VirtualThrottle", 0, 0);
      SysCall ( "PlayerEngine:SetControlValue", "VirtualBrake", 0, 1);
      SysCall ( "ScenarioManager:LockControls");
      SysCall ( "ScenarioManager:TriggerDeferredEvent", "starttoofastcheck3", 5 );
      return CONDITION_SUCCEEDED;
    end
    return CONDITION_NOT_YET_MET;
  end
 
  if (condition == "StopCondition") then
    speed = SysCall("PlayerEngine:GetSpeed");
    if (speed < 0.1) then
      SysCall("ScenarioManager:UnlockControls");
      return CONDITION_SUCCEEDED;
    end
    return CONDITION_NOT_YET_MET;
  end
 
  if (condition == "OverspeedCondition3") then
    speed = SysCall("PlayerEngine:GetSpeed");
    if (speed > 8.904) then
      SysCall ( "ScenarioManager:TriggerScenarioFailure", 
          "I give up, you're out of here! Too much fast driving! - DarknezzMonster" );
      return CONDITION_SUCCEEDED;
    end
    return CONDITION_NOT_YET_MET;
  end
end
 




Saturday 21 February 2015

TS2015 - Scenario Scripting in LUA Part 12 - Video

Apologies for the long time between articles!

In this article i'm going to show you how to incorporate videos in to your scenarios, done correctly they can really elevate your scenarios to a completely different level.

The overall API for playing videos is almost identical to the API for playing sounds - so if you haven't yet read the previous blog post on the subject then now would be a great time to do that.

At the top of your LUA script file you're going to want to drop in some definitions, just to make the rest of the script a bit easier to read:

FULLSCREEN = 0
FRONT_AND_CENTER = 1
CALL = 2

FALSE = 0
TRUE = 1

NO_CONTROLS = 0
PLAYPAUSE = 1
STOP = 2
SEEK = 4

Note: You may already have FALSE and TRUE defined, in which case there's no need to duplicate them here.

The calls to handle videos are as follows:

Start a video playing:
SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FRONT_AND_CENTER, FALSE, NO_CONTROLS,0)

Check to see if a video is playing:
SysCall("ScenarioManager:IsVideoMessagePlaying", "2013_startup.ogv")

Stop a video from playing:
SysCall("ScenarioManager:StopVideoMessage", "2013_startup.ogv")

Let's put it in a small example to provide some context:

function OnEvent(event)
  _G["OnEvent" .. event]();
end

function OnEventStart()
  SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FULLSCREEN, FALSE, NO_CONTROLS,0)
end

Here, i'm defining a trigger event "Start" (which should be initiated from a Trigger instruction in the scenario definition) and this will play a video, full screen.

Just as with audio, the call to play a video will return immediately and let the video carry on playing in the background, that's the main reason for the "IsVideoMessagePlaying" and "StopVideoMessage" calls.  I'll show them in action in a moment.

Parameters

There are a number of parameters to the PlayVideoMessage function that are interesting.

After the Filename, the next one tells the game how and where you want the video displayed.  The options are Fullscreen (0), Front and Center (1), and Video Call (2).  I've defined some constants above so that it's easier to see what it is doing rather than passing plain numbers in (never do that, trust me!).

Full Screen puts the video full screen, just as it says it will.  It will also include an X in the top right that allows the player to abort playback of the video if they wish.  In the screenshot below you can see it has completely filled the game screen.

Full Screen Playback
Front and Center gives you a video playback which does not fill the screen and therefore allows some context from within the game to still be seen, but it is meant to still command the users attention fully and is of sufficient size it can still contain some detail.
Front and Center Playback
Finally, Video Call is intended to show in the top left corner somewhat like if you had a video call going on with someone.  You could use this for giving brief snippets of information, perhaps a news bulletin about something that's just happened (based on the players action) or something similar.
Video Call Playback


The next option is either TRUE (1) or FALSE (0) and this tells the game whether to pause in-game action or leave it running.  If you're going to get in the way of the user such as a full screen video then I'd suggest pausing the game is a good idea.  If it's a video call, then you probably don't want to pause it so that it feels more like something that is happening as part of the game play.

The next option tells the game what kinds of controls to put on the video, do you want them to be able to pause it? skip to a particular bit? or stop it?  There are four constants defined for this in my example.  NO_CONTROLS (0) means show nothing, and is used in all the above three screenshot examples.  PLAYPAUSE (1), STOP (2) and SEEK (4) add the relevant controls that you would expect.  If you want to add more than one control, simply add them together like this:

SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FRONT_AND_CENTER, FALSE, (PLAYPAUSE+STOP+SEEK),0)

This would give you something like this::

Front and Center with all controls enabled along the bottom of the video
The final parameter is always a zero.

Format

The format of the video is an Ogg Vorbis ".ogv" video.  You will need to create your video and export it in some format using your preferred editing tool and then if it won't export to Ogv then you'll need to use an additional step to convert it.  As a reference example, the 2013_startup.ogv used as part of the start-up sequence of the game is in the correct format.  You can use this to confirm you're calling the video correctly and it is working, and then drop your own video in afterwards.  You can find this video in the data\videos folder.

I use FFMpeg2Theora to do my conversions, available here, once installed you can run it with this command line to get the right format:

ffmpeg2theora-0.29.exe myvideo.mp4 -vcodec libtheora -acodec libvorbis -v 10

Here i've used an mp4 video because that's what I exported from my video editing suite, the result will go out as myvideo.ogv .

The number at the end is the quality level, 10 is the maximum and reducing it can make a huge difference to the file size - as well as a huge difference to the quality.  You will likely find that you can drop it down one or two settings before you even notice a difference and then it will drop off quickly.  Find that right setting for your video that means you can get the file size down as far as possible while still remaining good quality.

Place the videos in the localised language folder, the same as the audio files.  So this would be in the "en" folder for English users, for example.

Bigger Example

So here's a bigger example showing how you can use conditions to work out when to continue with the scenario.

function OnEvent(event)
  _G["OnEvent" .. event]();
end

function TestCondition(condition)
  _G["TestCondition" .. condition]();
end

function OnEventStart()
  videoPlaying = SysCall("ScenarioManager:IsVideoMessagePlaying", "2013_startup.ogv")
  if (videoPlaying == TRUE) then
    SysCall("ScenarioManager:StopVideoMessage", "2013_startup.ogv")
  end
  SysCall("ScenarioManager:PlayVideoMessage", "2013_startup.ogv", FULLSCREEN, FALSE, NO_CONTROLS,0)
  SysCall("ScenarioManager:BeginConditionCheck", "HasItStoppedYet")
end

function OnEventNextStep()
  SysCall("ScenarioManager:ShowInfoMessageExt", "Get moving", "moving.html", 10, MSG_TOP + MSG_TOP, MSG_SMALL, TRUE);
end

function TestConditionHasItStoppedYet()
  videoPlaying = SysCall("ScenarioManager:IsVideoMessagePlaying", "2013_startup.ogv")
  if (videoPlaying == FALSE) then
    OnEventNextStep()
  end
end

This example is somewhat contrived to try and fit a few different things in to a small space but it should give you the "snippets" to do what you want to do.

In the scenario instructions we're triggering "Start", which ultimately calls OnEventStart().
In OnEventStart() we find out if the video is currently playing and if so, we stop it.  It's highly unlikely that would be the case here, but normally you might find a place where there's a chance a video is already playing and now as a result of a more gameplay based event you need to play a video so the first thing you do is make sure that nothing else is playing first.
We then kick off playing our video, and then finally start a condition check called HasItStoppedYet.

The game will now play the video, and while the user is enjoying this video the game will now be calling TestConditionHasItStoppedYet over and over again, each time it will find out if the video is currently playing.  At some point this is going to return FALSE because our video finally finished playing and when this happens we manually trigger another event NextStep.

When OnEventNextStep runs, it displays a message to the user, presumably saying "right, now get moving".  Had you just tried to put the message box up, it would have appeared over or simply interrupted the video.  In this way, you remain fully in control of when things are presented to the user.











Tuesday 17 February 2015

TS2015 - Scenario Scripting in LUA Part 11 - Audio

Audio is a really great way to kick your scenarios up a notch, you could use it to play some instructions for the user or to have scenario specific audio playing such as station announcements or other such things.  It's up to you to record the audio though of course!

In LUA, managing Audio is done through three commands:

SysCall("ScenarioManager:PlayDialogueSound", "test.wav")

SysCall("ScenarioManager:StopDialogSound", "test.wav")

SysCall("ScenarioManager:IsDialogSoundPlaying", "test.wav")

PlayDialogSound will begin playing the audio, it returns immediately so your script can continue doing things while the audio is playing.

This is great because it means you can get on and do other things, let the player carry on driving and so forth however if you want to play something straight afterwards, or if you want to play something new then it's good to know if something is already still playing.

IsDialogSoundPlaying will return "TRUE" if the audio is playing, and then you can issue a StopDialogSound to stop it playing.

You must always stop an audio file playing before you start another one, or the new one is simply ignored.

You can use this in a number of ways - when you arrive at a station perhaps an audio file plays something that has had suitable echo applied which sounds like a station announcement.  You could even work out how late the player is and play "sorry for the late arrival of this train" if late, or "the on-time service on platform 2..." if they're reasonably on time.

Using the functions you can even chain things together to be a little more dynamic, for example, let's say that you want to read out the current speed at some point - you would work out what files you're going to play, e.g. "fifty.wav" and "six.wav", then you would play the first one, use a condition to wait for it to finish (ie. the condition would keep calling IsDialogSoundPlaying(fifty.wav)  ) - and then kick off "six.wav" afterwards.  That way you can end up with some that says "you went through the checkpoint at... fifty...six...miles per hour".  This is basically the same way that telephone systems read out things like "you have...fifty...six... new voice mails".

So, where do you place the files?

Remember back when I covered where you place HTML files?  It's exactly the same.  They go in a localised folder name, so english sound files go in an "En" folder, that way the right sounds will be played depending on the preferred language of the person playing the scenario!

It's simple, it's easy to use, and you can use it to great effect in your scenarios!






Sunday 15 February 2015

TS2015 - Scenario Scripting in LUA Part 10 - Dynamic Weather

I'm going to split this topic in to two bits, first i'm going to cover how you actually make use of extended weather blueprints and then second i'll talk about how to actually make those blueprints.  That initially might sound like the wrong way around, however since there are extended weather blueprints already provided in some route packages you could use those in scenarios without having to think about making new weather blueprints at all, easier but less flexible.

Using Extended Weather Blueprints


I'm going to refer to the Extended Weather Blueprint that is supplied with Pacific Surfliner in this example, so if you have that route then enable its assets for the scenario (the same way you would if you wanted to use a loco from that pack).  If you don't have Pacific Surfliner then you will likely have to skip to the next section about creating your own Extended Weather Blueprint.

In order to use this Extended Weather Blueprint, having enabled it, we must then tell the scenario specifically to use it.  You can only point a scenario to one single Extended Weather Blueprint.

When you first edit a scenario you can see the scenario properties (such as title, time of day, season etc) on the top right fly-out.  At the top of this, you will see a picture which looks like some gears, this is the option that takes you to more advanced settings, click it.




This shows the next "tab" - to return to the normal one, click the orange train icon.

From this tab you can use the drop-down box to select from the available Extended Weather Blueprints - in this case, I'm seeing the two that Pacific Surfliner contains.




Within an Extended Weather Blueprint there are defined a number of "Chains" which define a sequence of weather patterns to apply, each Chain has a name, and it is this chain that the LUA script must activate.

There are three weather chains defined in the Pacific Surfliner weather pattern:
  • FirstChain
  • SecondChain
  • FinalStorm

Within our LUA script we simply need to tell the game to start one of these sequences, as follows:

SysCall ( "WeatherController:SetCurrentWeatherEventChain", "FirstChain");

This will cause the weather sequence defined by this chain to immediately start executing, this could cause fog to change, the sky colour to change, rain to start or change intensity, flashes in the sky, it can even place textures in the world to act as lightning bolts or rainbows.

Creating Extended Weather Blueprints


To create these blueprints you will need to set up a source folder for your assets if you do not already have one.  Within the Train Simulator 2015 home directory, you need to create a Source folder along with another folder for the "Provider" name and another for the "Product" name.  For example, Provider might be your name or some handle you go by online.  Product could just be "ScenarioAssets".  Beneath there, create a Weather folder, and inside that create an Audio folder.

When you're done, it should look like this:



Note I've used "MattPeddlesden" as my "Provider" and "ScenarioSupport" as my "Product".  If you're building this as part of a bigger pre-existing route or loco pack then you can of course just drop these assets right in there rather than creating something new.

From the RailWorks home directory, run BlueprintEditor2.exe and you will see something similar to this:




From here, we can start creating the blueprint.

Right click on the "Weather" folder, select "Add" and then "New Item".

From the dialog that pops up:



Select "Blueprint" then "Weather pattern extension blueprint" and enter a sensible name for the file, it must end with ".xml".  Click OK.

It will now load the blank blueprint in ready for you to edit it.

The blueprint is split in to a number of sections.  Before we go any further, enter something in to the "Display Name" field.  I've entered "MyCustomWeatherPattern".



As with any design process, the first place to start is with a pencil and paper.   What do we want this to do?  We can set up weather chains that do multiple things one after the other, or we can split them out in to their own chains and call them up as we want.  For this tutorial, let's make it four separate chains that each do one thing.  I'll leave it to you to get creative!

Chain 1 "Rain" - Light rain
Chain 2 "Foggy Rain" - Step 2: Light rain + Close fog
Chain 3 "Foggy Downpour" - Step 3: Heavy rain + Close fog

If you want things to happen in specific locations (e.g. start raining just at the time when you have the heaviest load on the steepest track perhaps!) then set up chains that you can start at specific locations via triggered events.  If you want sequences that happen over a period of fixed time, you can set up chains to do multiple things in sequence and then simply trigger it at an appropriate point in the script, so you really do have a lot of flexibility.

We need to attack this problem by breaking it down in to component parts defined by the blueprint, which means we need to define the clouds and the precipitation separately, then tie them together in a "weather type" (which adds more options on top) and then assemble chains that the LUA script can call.

Clouds



Set the name to something appropriate, we're going to just call this one "thick clouds".

When a route is set up, the route designer will set up four different sky dome textures and using the values in "Cloud Description" you can choose which ones will be shown  by setting them to 1 or 0.

The Colour Darkening Intensity allows you to make noticeable changes to the ambient lighting, particularly useful during a heavy storm where you might want to set it to 1.5 or even 2, at this point headlights become more visible and can provide a much more noticeable environmental change.

My example weather blueprint i've set up three cloud types:





"Thick Clouds" will be our normal sky, we'll then change to "Storm Clouds" for when it's raining, which will also dark the world, and then "Dark Storm Clouds" for when the rain really kicks in to high gear.

Precipitation

Precipitation will let you set up a number of different things including the textures for the rain drops, snow flakes, or whatever else you want to fall from the sky.  It is only an ALPHA texture however so I think you'll have problems trying to make cats and dogs actually fall from the sky!

You will need to create a small texture for the rain drop using your favouroute paint application (I use Paint.NET) , 64x64 pixels in size.  The "split raindrop" used in many weather patterns, looks something like this - the link to download the file is here and then use a tool like "RWAceTool" (available from many sites online including UKTrainSim here - RWAceTool on UKTS.  Note that i've made the texture show up here with a black background since with only alpha transparency, it would otherwise be rather hard to see.

This texture must be an ACE file and it must be placed in the Weather folder.  From here, we can start using it.

Here's an example of one set-up of the precipitation, you can tweak values such as "alpha top", "alpha bottom" and "particle stream multiplier" to affect how the rain looks - generally, bigger numbers mean "more".

I defined one called "intenserain" and one called "lightrain" - the only difference with "lightrain" is that the particle streak multiplier is 3, and the alpha bottom is 40. 

You can also see above where (and how) the raindrop texture is shown.

Weather Type


Now that we have our clouds and precipitation, we need to set up some weather types.  There are essentially three different weather types that i'm going to define in this example (of course you can do however many you wish!) - but in order to make it work the way I want, I actually need to double some of them up, i'll explain why as we go.

Whether you need to double up on your weather types depends on whether you want to have weather changes script-controlled, or if you just want to kick off a timed sequence from the script.  If it's fully script controlled, you need to essentially have "Start" and "Continue" weather types with a couple of small changes, if it's all done within a single chain then you can keep things simpler.

I'm going to be showing the more complex case, where it's entirely script controlled, so simply note that you can cut some steps out if you put all your weather changes in one chain.

In our weather types, the three main types we want to define are:
  • Rain
  • FoggyRain
  • FoggyDownpour
Add an element to the Weather section.  Below is how I set up my "Rain" weather type.





So what is this defining?

You can set up wind direction and speed, which will affect smoke particle effects.  You will refer to the precipitation entries you set up previously, and then say how much rain (0.1 is light, 1.0 is heavy), and how fast the rain is travelling.  The Glass Weather Boost we'll see in a moment, but basically this says whether there's more rain hitting the windscreen to create a much more intense effect.

You can also set up fog - we'll do that next too.

Blend times i'll cover in a moment as there are numerous blend times to consider in order to set things up correctly.

This is also where you set up the reference the cloud types you configured earlier.

The last parameter "audio" we'll look at in a future tutorial, but essentially if you want lightning cracks and thunder (Or indeed any other audio) then this is where you tell the weather blueprint how to access the audio blueprint that has the sound set-up in it.

So that's a simple example which hs no fog and sets up some light rain.  Let's look at the next one, which adds fog:


This example pushes up the precipitation density a little, basically making it rain a bit harder outside the train.  This time we also tick the "fog override" box and set up some settings which control what colour the fog is, how transparent it is, how far away does it start and when does it end.  I've also changed the sky dome so that it will use a different cloud formation.

Lastly, let's make it rain really heavily:

In this example you can see that the fog is similar, but i've drawn it in closer.  I've also set the precipitation density to its maximum value of 1, increase the speed of it, and boosted the glass weather effect.  At this point, most wipers will not be able to even remotely keep up with the rain, it's really quite unpleasant!

So that's the main three weather types.

The other three weather types i've defined are used for merging between them, they are identical but they have got a "blend in time" of 0 minutes, making them instant.  In the next section, where we finally set up the triggered event chains, i'll cover why we need these and how all the blend times work.

Triggered Weather Event Chains

Now that we've set up all the resources for the weather patterns, it's time to link them together.

In the Triggered Weather Event Chain section, click Add Element.

I'm going to start the sequence off by just making it light rain, so the first chain will be called "Rain" and will start the "Rain" weather type, as follows:


The "Name" is the name that we'll reference in the LUA script.  Loop mode is set up to be "Hold end" which should keep it going after the chain finishes - though i've had mixed results with this, which is why the duration of the weather type event is set to 999 minutes.  Inside the chain, you then set up a sequence of weather types - in my case i've just put one, but if you wanted to put a longer sequence of timed events together this is where you do it, just keep adding events and noting how long each one should last.

The blend out time is set to a very small number, let's look at the next chain and then we'll see how you get from chain to the next.

In the scenario set-up, i've set it such that when you go over a waypoint somewhere down the line it simply causes the next event to occur, this means i've got complete control over the weather happening at the right point in the scenario, so if I take a longer or shorter time to get to that point, it will still always start raining (or whatever) once I go past it.  This may or may not be useful - if you want specifically to have just a timed sequence of weather that means that the player is encouraged to get a move on to ensure they're off a hill before a storm starts, then you would just put everything in a single timed chain.  If you want to ensure that they always get a storm when they hit a hill, to make climbing the hill more challenging, then you want scripted control.

So let's take a look at the next sequence:

The main difference here is that we have TWO weather types in this sequence, so now we can talk about how blending works between the sequences.

When you kick a weather sequence off using a LUA script command, it will "blend in" to the first weather type, and then remain there until that weather types "duration" is expired.  At that point, it will "blend in" to the next weather type and so forth.  Once it runs out of weather types it will refer to "loop" mode to find out what to do next.  It will always blend from the state it's in, to the next one.  If it's set to "stop" then it will take "blend out" minutes to then go back to the scenario standard weather pattern.

If a weather chain is currently running when you tell it to start another one, it will take "blend out" minutes to blend the existing weather chain out before beginning the next one.

This presents us with a big problem if we are in the middle of a foggy thunderstorm and then we switch to the next weather chain and it wants to then gently take us out of the thunderstorm - because the first thing that happens is the game will blend it out to whatever the default weather is and then blend back in to the new weather.  In some cases this might be exactly what you're looking for, but in my case it wasn't - and that's why I had to add some extra weather types.

So we started the scenario with a nice sunny day, the we started "Rain" and over the course of a minute or so it started raining.  Now we've hit the next marker and we want to go from "rain" to "foggyrain" without going back to "sunny".  We set the "blend out time" of "rain" to be extremely short - so it will go back to sunny instantly, then the "Rain2" blueprint has a 0 minutes so it will instantly go back to raining again, which will last for about 6 seconds before it then begins a gentle fade over a minute or so towards foggy rain.  This isn't ideal, it means that if you're looking, you'll notice a very tiny flash as the weather system goes back to sunny and then back to raining, however this can just as easily look like a lightning flash too - so I leave it to you to creatively decide whether you like that approach or not.

Let's look at the next chain:

In this sequence we follow the same pattern, transitioning from Foggy Rain to Foggy Downpour using the same sequence of two chains.

The final chain, then, drops us back to Raining and then fades back to the sunny weather:
In this chain, the "Rain" sequence means it will actually blend from "foggy downpour" back to "rain", which might confuse you in to thinking it's a "blend out" time - but always think about blending in as going in to a new weather, not necessarily a worse one, and blend out time as returning to "default" weather.  The blend-in time for "Rain" is set to 1 minute in the weather type, so it'll take about a minute to lighten the weather, draw back the fog, lighten the world up and reduce the amount of rain.  We'll then wait a further minute or so at this level, and then the duration will expire so the chain will begin then to fade back out using the "blend out" time of 3 minutes - this means that over the course of 5 minutes from when this chain starts, the horrific weather will draw back to light rain and then draw back to sunny with clear skies.

Once you've set it up, right click on the weather blueprint in the left hand pane (the one that looks like an Explorer folder tree) and select Export, followed by Export with References.


You should see some output in the lower pane and it should end by saying "==== Export Succeeded ====" - if it doesn't then go back and check.  You should also see any error messages reported in that output window as well.

Script

Let's go back to our scenario script and see how we actually kick all these weather chains off, using the SysCall we talked about right at the start:

function OnEvent(event)
  _G["OnEvent" .. event]();
end

function OnEventWeather1()
  SysCall("WeatherController:SetCurrentWeatherEventChain", "Rain");
end

function OnEventWeather2()
  SysCall("WeatherController:SetCurrentWeatherEventChain", "FoggyRain");
end

function OnEventWeather3()
  SysCall("WeatherController:SetCurrentWeatherEventChain", "FoggyDownpour");
end

function OnEventWeather4()
  SysCall("WeatherController:SetCurrentWeatherEventChain", "Finish");
end

In the scenario i've set up a bunch of "stop at" instructions, each one with a minimum speed of 1mph (which turns them in to go-via instructions, but allows me to put a trigger on them). Here's an example of how I set one of them up:

The important bit is near the bottom where it says "Weather1" - this is where you can trigger a script event to occur on successful completion of an instruction. The box directly to the right is the event to trigger if the player fails to complete the instruction successfully.  So here you can see that in my first waypoint at Haymarket, I trigger "Weather1", this will go to the script and the script above will then execute the "Rain" weather sequence.

Concluding Notes

I've set lots of blend timings quite low here because it allows for a more clear and obvious example, when developing your own I would suggest that you use short blend times to start with to allow you to more clearly see the result of each change.  Once you've then got the right weather looking the way you want then you can dial up the blend times to get some splendid smooth transitions.

I have not covered the "Location" events, this is where you can add lightning bolts in the sky, rainbows, audio for thunder claps and also flash the sky.  I think this tutorial is definitely long enough for now, so i'll come back and do Location events in a future tutorial, perhaps once i've had some feedback on how people feel about this one.

When I set the blog up, it was intended to be only the most basic parts of scripting capable of accessing with the sole goal of enhancing scenarios.  This is one of the most complex examples because it involves you going in to the blueprint editor, this is not going to be a problem for anyone familiar with content creation piplines in Train Simulator but for the average user I expect this to be something you go in to with some degree of trepidation.  I hope that i've covered it with enough detail to show that it's not too hard and the results are clearly very well worth the effort.

What does it look like?

I'll leave you with some screenshots from my example scenario showing what it actually looks like in-game.

Departing Edinburgh on a lovely sunny day, clear skies

As we went through Haymarket, rain started to appear
The clouds seem to have taken a turn too, looks like we're in for a storm.

Passing Edinburgh Park, the skies darken and the fog starts to draw in.


The fog really seems to be closing in now after we pass Bathgate Junction and the rain is getting really itense, even with wipers on I can't keep up with the rain!

Passing Winchburg the fog has lifted and the skies are lightening up, maybe we're through the storm.

Heading towards our end point everything is brighter now and the rain is easing off.


Finally, the skies are back to clear and there's just an odd spot or two appearing on the window!