/// <summary>This method returns the current aspect of the selected section</summary> /// <param name="Train">The train for which we wish to get the section index</param> /// <param name="SectionIndex">The section to get the aspect for</param> /// <param name="IsPartOfTrain">Defines whether we wish to get the section index for the selected train, or for when placed via a .SigF command</param> /// <returns>The aspect of the selected signal</returns> public static int CurrentAspect(TrainManager.Train Train, int SectionIndex, bool IsPartOfTrain) { if (IsPartOfTrain) { int nextSectionIndex = Train.CurrentSectionIndex + 1; if (nextSectionIndex >= 0 & nextSectionIndex < Game.Sections.Length) { int a = Game.Sections[nextSectionIndex].CurrentAspect; if (a >= 0 & a < Game.Sections[nextSectionIndex].Aspects.Length) { return Game.Sections[nextSectionIndex].Aspects[a].Number; } return 0; } } else if (SectionIndex >= 0 & SectionIndex < Game.Sections.Length) { int a = Game.Sections[SectionIndex].CurrentAspect; if (a >= 0 & a < Game.Sections[SectionIndex].Aspects.Length) { return Game.Sections[SectionIndex].Aspects[a].Number; } } return 0; }
/// <summary>Unloads the currently loaded plugin, if any.</summary> /// <param name="train">Unloads the plugin for the specified train.</param> internal static void UnloadPlugin(TrainManager.Train train) { if (train.Plugin != null) { train.Plugin.Unload(); train.Plugin = null; } }
/// <summary> /// close left door /// </summary> static public void RightDoorClose() { try { TrainManager.CloseTrainDoors(TrainManager.PlayerTrain, false, true); } catch (Exception ex) { } }
/// <summary> /// open left door /// </summary> static public void LeftDoorOpen() { try { TrainManager.OpenTrainDoors(TrainManager.PlayerTrain, true, false); } catch (Exception ex) { } }
/// <summary>Returns the number of cars in this train</summary> /// <param name="Train">The selected train</param> /// <returns>The number of cars</returns> public static int numberOfCars(TrainManager.Train Train) { if (Train != null) { return Train.Cars.Length; } return 0; }
static public void SetLeftDoorClose() { try { TrainManager.CloseTrainDoors(TrainManager.PlayerTrain, true, false); } catch (Exception) { }; }
static public void SetRightDoorOpen() { try { TrainManager.OpenTrainDoors(TrainManager.PlayerTrain, false, true); } catch (Exception) { }; }
// parse sound config internal static void ParseSoundConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { string FileName = OpenBveApi.Path.CombineFile(TrainPath, "sound.cfg"); if (System.IO.File.Exists(FileName)) { LoadBve4Sound(FileName, TrainPath, Encoding, Train); } else { LoadBve2Sound(TrainPath, Train); } }
/// <summary> /// set reverser /// </summary> static public void SetReverser(int value) { try { TrainManager.ApplyReverser(nowControl, value, false); return; } catch (Exception ex) { } }
internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.Camera) { if (Direction < 0) { BackgroundManager.TargetBackground = this.PreviousBackground; } else if (Direction > 0) { BackgroundManager.TargetBackground = this.NextBackground; } } }
/// <summary>Loads the default plugin for the specified train.</summary> /// <param name="train">The train to attach the plugin to.</param> /// <param name="trainFolder">The train folder.</param> /// <returns>Whether the plugin was loaded successfully.</returns> internal static bool LoadDefaultPlugin(TrainManager.Train train, string trainFolder) { string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Plugins"), "OpenBveAts.dll"); bool success = LoadPlugin(train, file, trainFolder); if (success) { train.Plugin.IsDefault = true; SoundCfgParser.LoadDefaultPluginSounds(train, trainFolder); } return success; }
/// <summary> /// reserver backward /// </summary> static public void ReverserBackward() { try { TrainManager.ApplyReverser(nowControl, -1, true); TrainManager.ApplyReverser(nowControl, -1, true); return; } catch (Exception ex) { } }
override internal void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.Camera) { if (Direction < 0) { World.TargetBackground = this.PreviousBackground; World.TargetBackgroundCountdown = World.TargetBackgroundDefaultCountdown; } else if (Direction > 0) { World.TargetBackground = this.NextBackground; World.TargetBackgroundCountdown = World.TargetBackgroundDefaultCountdown; } } }
internal override void Trigger(int Direction, EventTriggerType TriggerType, TrainManager.Train Train, int CarIndex) { if (TriggerType == EventTriggerType.Camera) { if (Direction < 0) { Game.PreviousFog = this.PreviousFog; Game.NextFog = this.CurrentFog; } else if (Direction > 0) { Game.PreviousFog = this.CurrentFog; Game.NextFog = this.NextFog; } } }
/// <summary>Returns the speed of the selected car </summary> /// <param name="Train">The selected train</param> /// <param name="CarIndex">The car for which to get the speed</param> /// <returns>The speed in m/s</returns> public static double speed(TrainManager.Train Train, int CarIndex) { if (Train == null) { return 0; } if (CarIndex > Train.Cars.Length) { return Train.Cars[0].Specs.CurrentSpeed; } return Train.Cars[CarIndex].Specs.CurrentSpeed; }
internal static void LoadDefaultPluginSounds(TrainManager.Train train, string trainFolder) { Vector3D position = new Vector3D(train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ + 1.0); const double radius = 2.0; train.Cars[train.DriverCar].Sounds.Plugin = new TrainManager.CarSound[] { TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "ats.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "atscnt.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "ding.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "toats.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "toatc.wav"), position, radius), TryLoadSound(OpenBveApi.Path.CombineFile(trainFolder, "eb.wav"), position, radius) }; }
// --- constructors --- /// <summary>Creates a new sound source.</summary> /// <param name="buffer">The sound buffer.</param> /// <param name="radius">The effective sound radius.</param> /// <param name="pitch">The pitch change factor.</param> /// <param name="volume">The volume change factor.</param> /// <param name="position">The position. If a train and car are specified, the position is relative to the car, otherwise absolute.</param> /// <param name="train">The train this sound source is attached to, or a null reference.</param> /// <param name="car">The car this sound source is attached to, or a null reference.</param> /// <param name="looped">Whether this sound source plays in a loop.</param> internal SoundSource(SoundBuffer buffer, double radius, double pitch, double volume, OpenBveApi.Math.Vector3D position, TrainManager.Train train, int car, bool looped) { this.Buffer = buffer; this.Radius = radius; this.Pitch = pitch; this.Volume = volume; this.Position = position; this.Train = train; this.Car = car; this.Looped = looped; this.State = SoundSourceState.PlayPending; this.OpenAlSourceName = 0; }
internal double Perform(TrainManager.Train Train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed, int CurrentState) { ExecuteFunctionScript(this, Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed, CurrentState); //Allows us to pin the result, but keep the underlying figure if (this.Minimum != Double.NaN & this.LastResult < Minimum) { return Minimum; } if (this.Maximum != Double.NaN & this.LastResult > Maximum) { return Maximum; } return this.LastResult; }
// load no sound internal static void LoadNoSound(TrainManager.Train train) { // initialize for (int i = 0; i < train.Cars.Length; i++) { train.Cars[i].Sounds.Run = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.Flange = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.Adjust = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Air = TrainManager.CarSound.Empty; train.Cars[i].Sounds.AirHigh = TrainManager.CarSound.Empty; train.Cars[i].Sounds.AirZero = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Brake = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleApply = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleMin = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleMax = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BrakeHandleRelease = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResume = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResumeOrInterrupt = TrainManager.CarSound.Empty; train.Cars[i].Sounds.CpEnd = TrainManager.CarSound.Empty; train.Cars[i].Sounds.CpLoop = TrainManager.CarSound.Empty; train.Cars[i].Sounds.CpStart = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorCloseL = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorCloseR = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorOpenL = TrainManager.CarSound.Empty; train.Cars[i].Sounds.DoorOpenR = TrainManager.CarSound.Empty; train.Cars[i].Sounds.EmrBrake = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Flange = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.FlangeVolume = new double[] { }; train.Cars[i].Sounds.Halt = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Horns = new TrainManager.Horn[] { TrainManager.Horn.Empty, TrainManager.Horn.Empty, TrainManager.Horn.Empty }; train.Cars[i].Sounds.Loop = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerUp = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerDown = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerMin = TrainManager.CarSound.Empty; train.Cars[i].Sounds.MasterControllerMax = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PilotLampOn = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PilotLampOff = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PointFrontAxle = TrainManager.CarSound.Empty; train.Cars[i].Sounds.PointRearAxle = TrainManager.CarSound.Empty; train.Cars[i].Sounds.ReverserOn = TrainManager.CarSound.Empty; train.Cars[i].Sounds.ReverserOff = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Rub = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Run = new TrainManager.CarSound[] { }; train.Cars[i].Sounds.RunVolume = new double[] { }; train.Cars[i].Sounds.SpringL = TrainManager.CarSound.Empty; train.Cars[i].Sounds.SpringR = TrainManager.CarSound.Empty; train.Cars[i].Sounds.Plugin = new TrainManager.CarSound[] { }; } }
/// <summary> /// main timer interrupt /// </summary> static private void MainTimerProcess(object state) { if (Interlocked.Exchange(ref inTimer_Main, 1) == 0) { try { nowControl = TrainManager.PlayerTrain; //Master key if (!MasterKey) { SetAutoPilot(0); SetBrake(0); SetPower(0); ReverserNeutral(); } //emergency if (Emergency) { SetAutoPilot(0); TrainManager.ApplyEmergencyBrake(nowControl); } else { TrainManager.UnapplyEmergencyBrake(nowControl); } //signal if (GetSignal() == 0) { inRedLight = true; } else { if (inRedLight) { SetBrake(nowControl.Handles.Brake.MaximumNotch); SetReverser(0); SetAutoPilot(0); } inRedLight = false; } } catch (Exception ex) { } Interlocked.Exchange(ref inTimer_Main, 0); } }
private OpenBveApi.Runtime.AIResponse PerformPlugin(TrainManager.Train Train) { OpenBveApi.Runtime.AIResponse response = Train.Plugin.UpdateAI(); if (response == OpenBveApi.Runtime.AIResponse.Short) { CurrentInterval = 0.2 + 0.1 * Program.RandomNumberGenerator.NextDouble(); } else if (response == OpenBveApi.Runtime.AIResponse.Medium) { CurrentInterval = 0.4 + 0.2 * Program.RandomNumberGenerator.NextDouble(); } else if (response == OpenBveApi.Runtime.AIResponse.Long) { CurrentInterval = 0.8 + 0.4 * Program.RandomNumberGenerator.NextDouble(); } return response; }
/// <summary> /// set emergency /// </summary> static public void SetEmergency(int value) { try { Emergency = value == 0 ? false : true; TrainManager.Train nowControl = TrainManager.PlayerTrain; if (Emergency) { TrainManager.ApplyEmergencyBrake(nowControl); } else { TrainManager.UnapplyEmergencyBrake(nowControl); } } catch (Exception ex) { } }
// functions internal SimpleHumanDriverAI(TrainManager.Train Train) { this.TimeLastProcessed = 0.0; this.CurrentInterval = 1.0; this.BrakeMode = false; this.PersonalitySpeedFactor = 0.90 + 0.10 * Program.RandomNumberGenerator.NextDouble(); this.CurrentSpeedFactor = this.PersonalitySpeedFactor; this.PowerNotchAtWhichWheelSlipIsObserved = Train.Specs.MaximumPowerNotch + 1; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { this.LastStation = Train.Station; } else { this.LastStation = -1; } }
/// <summary>Loads a custom plugin for the specified train.</summary> /// <param name="train">The train to attach the plugin to.</param> /// <param name="trainFolder">The absolute path to the train folder.</param> /// <param name="encoding">The encoding to be used.</param> /// <returns>Whether the plugin was loaded successfully.</returns> internal static bool LoadCustomPlugin(TrainManager.Train train, string trainFolder, System.Text.Encoding encoding) { string config = OpenBveApi.Path.CombineFile(trainFolder, "ats.cfg"); if (!System.IO.File.Exists(config)) { return false; } string[] lines = System.IO.File.ReadAllLines(config, encoding); if (lines.Length == 0) { return false; } string file = OpenBveApi.Path.CombineFile(trainFolder, lines[0]); string title = System.IO.Path.GetFileName(file); if (!System.IO.File.Exists(file)) { Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + title + " could not be found in " + config); return false; } return LoadPlugin(train, file, trainFolder); }
/// <summary> /// get left door state /// </summary> static public int GetRightDoorState() { try { TrainManager.TrainDoorState value; value = TrainManager.GetDoorsState(TrainManager.PlayerTrain, false, true); if (value == TrainManager.TrainDoorState.AllClosed) { return(0); } if (value == TrainManager.TrainDoorState.AllOpened) { return(1); } return(-1); } catch (Exception ex) { } return(-1); }
// --- constructors --- internal NetPlugin(string pluginFile, string trainFolder, IRuntime api, TrainManager.Train train) { base.PluginTitle = System.IO.Path.GetFileName(pluginFile); base.PluginValid = true; base.PluginMessage = null; base.Train = train; base.Panel = null; base.SupportsAI = false; base.LastTime = 0.0; base.LastReverser = -2; base.LastPowerNotch = -1; base.LastBrakeNotch = -1; base.LastAspects = new int[] { }; base.LastSection = -1; base.LastException = null; this.PluginFolder = System.IO.Path.GetDirectoryName(pluginFile); this.TrainFolder = trainFolder; this.Api = api; this.SoundHandles = new SoundHandleEx[16]; this.SoundHandlesCount = 0; }
// --- constructors --- internal Win32Plugin(string pluginFile, TrainManager.Train train) { base.PluginTitle = System.IO.Path.GetFileName(pluginFile); base.PluginValid = true; base.PluginMessage = null; base.Train = train; base.Panel = new int[256]; base.SupportsAI = false; base.LastTime = 0.0; base.LastReverser = -2; base.LastPowerNotch = -1; base.LastBrakeNotch = -1; base.LastAspects = new int[] { }; base.LastSection = -1; base.LastException = null; this.PluginFile = pluginFile; this.Sound = new int[256]; this.LastSound = new int[256]; this.PanelHandle = new GCHandle(); this.SoundHandle = new GCHandle(); }
/// <summary> /// power up /// </summary> static public void PowerUp() { try { int currretPower = nowControl.Handles.Power.Driver; int currentBrake = nowControl.Handles.Brake.Driver; if (nowControl.Handles.EmergencyBrake.Actual) { TrainManager.UnapplyEmergencyBrake(nowControl); } if (currentBrake > 0) { nowControl.ApplyNotch(0, true, -1, true); } else { nowControl.ApplyNotch(1, true, 0, true); } return; } catch (Exception ex) { } }
static public int GetRightDoorState() { int retValue = -1; try { TrainManager.TrainDoorState dataValue; dataValue = TrainManager.GetDoorsState(TrainManager.PlayerTrain, false, true); if (dataValue == TrainManager.TrainDoorState.AllClosed) { retValue = 0; } else if (dataValue == TrainManager.TrainDoorState.AllOpened) { retValue = 1; } return(retValue); } catch (Exception) { }; return(retValue); }
/// <summary>This method is called once the route and train data have been preprocessed, in order to physically setup the simulation</summary> private void SetupSimulation() { if (Loading.Cancel) { Close(); } Timetable.CreateTimetable(); //Check if any critical errors have occured during the route or train loading for (int i = 0; i < Interface.MessageCount; i++) { if (Interface.Messages[i].Type == Interface.MessageType.Critical) { MessageBox.Show("A critical error has occured:\n\n" + Interface.Messages[i].Text + "\n\nPlease inspect the error log file for further information.", "Load", MessageBoxButtons.OK, MessageBoxIcon.Hand); Close(); } } Renderer.InitializeLighting(); Game.LogRouteName = System.IO.Path.GetFileName(MainLoop.currentResult.RouteFile); Game.LogTrainName = System.IO.Path.GetFileName(MainLoop.currentResult.TrainFolder); Game.LogDateTime = DateTime.Now; if (Interface.CurrentOptions.LoadInAdvance) { Textures.LoadAllTextures(); } else { Textures.UnloadAllTextures(); } // camera ObjectManager.InitializeVisibility(); World.CameraTrackFollower.Update(0.0, true, false); World.CameraTrackFollower.Update(-0.1, true, false); World.CameraTrackFollower.Update(0.1, true, false); World.CameraTrackFollower.TriggerType = TrackManager.EventTriggerType.Camera; // starting time and track position Game.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; int PlayerFirstStationIndex = -1; double PlayerFirstStationPosition; int os = -1; bool f = false; for (int i = 0; i < Game.Stations.Length; i++) { if (!String.IsNullOrEmpty(Game.InitialStationName)) { if (Game.InitialStationName.ToLowerInvariant() == Game.Stations[i].Name.ToLowerInvariant()) { PlayerFirstStationIndex = i; } } if (Game.Stations[i].StopMode == StationStopMode.AllStop | Game.Stations[i].StopMode == StationStopMode.PlayerStop & Game.Stations[i].Stops.Length != 0) { if (f == false) { os = i; f = true; } } } if (PlayerFirstStationIndex == -1) { PlayerFirstStationIndex = os; } { int s = Game.GetStopIndex(PlayerFirstStationIndex, TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { PlayerFirstStationPosition = Game.Stations[PlayerFirstStationIndex].Stops[s].TrackPosition; double TrainLength = 0.0; for (int c = 0; c < TrainManager.Trains[TrainManager.PlayerTrain.TrainIndex].Cars.Length; c++) { TrainLength += TrainManager.Trains[TrainManager.PlayerTrain.TrainIndex].Cars[c].Length; } for (int j = 0; j < Game.BufferTrackPositions.Length; j++) { if (PlayerFirstStationPosition > Game.BufferTrackPositions[j] && PlayerFirstStationPosition - TrainLength < Game.BufferTrackPositions[j]) { /* * HACK: The initial start position for the player train is stuck on a set of buffers * This means we have to make some one the fly adjustments to the first station stop position */ //Set the start position to be the buffer position plus the train length plus 1m PlayerFirstStationPosition = Game.BufferTrackPositions[j] + TrainLength + 1; //Update the station stop location if (s >= 0) { Game.Stations[PlayerFirstStationIndex].Stops[s].TrackPosition = PlayerFirstStationPosition; } else { Game.Stations[PlayerFirstStationIndex].DefaultTrackPosition = PlayerFirstStationPosition; } break; } } } else { PlayerFirstStationPosition = Game.Stations[PlayerFirstStationIndex].DefaultTrackPosition; } if (Game.InitialStationTime != -1) { Game.SecondsSinceMidnight = Game.InitialStationTime; Game.StartupTime = Game.InitialStationTime; } else { if (Game.Stations[PlayerFirstStationIndex].ArrivalTime < 0.0) { if (Game.Stations[PlayerFirstStationIndex].DepartureTime < 0.0) { Game.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; } else { Game.SecondsSinceMidnight = Game.Stations[PlayerFirstStationIndex].DepartureTime - Game.Stations[PlayerFirstStationIndex].StopTime; Game.StartupTime = Game.Stations[PlayerFirstStationIndex].DepartureTime - Game.Stations[PlayerFirstStationIndex].StopTime; } } else { Game.SecondsSinceMidnight = Game.Stations[PlayerFirstStationIndex].ArrivalTime; Game.StartupTime = Game.Stations[PlayerFirstStationIndex].ArrivalTime; } } } int OtherFirstStationIndex = -1; double OtherFirstStationPosition = 0.0; double OtherFirstStationTime = 0.0; for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].StopMode == StationStopMode.AllStop | Game.Stations[i].StopMode == StationStopMode.PlayerPass & Game.Stations[i].Stops.Length != 0) { OtherFirstStationIndex = i; int s = Game.GetStopIndex(i, TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { OtherFirstStationPosition = Game.Stations[i].Stops[s].TrackPosition; } else { OtherFirstStationPosition = Game.Stations[i].DefaultTrackPosition; } if (Game.Stations[i].ArrivalTime < 0.0) { if (Game.Stations[i].DepartureTime < 0.0) { OtherFirstStationTime = 0.0; } else { OtherFirstStationTime = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; } } else { OtherFirstStationTime = Game.Stations[i].ArrivalTime; } break; } } if (Game.PrecedingTrainTimeDeltas.Length != 0) { OtherFirstStationTime -= Game.PrecedingTrainTimeDeltas[Game.PrecedingTrainTimeDeltas.Length - 1]; if (OtherFirstStationTime < Game.SecondsSinceMidnight) { Game.SecondsSinceMidnight = OtherFirstStationTime; } } // initialize trains for (int i = 0; i < TrainManager.Trains.Length; i++) { TrainManager.Trains[i].Initialize(); int s = i == TrainManager.PlayerTrain.TrainIndex ? PlayerFirstStationIndex : OtherFirstStationIndex; if (s >= 0) { if (Game.Stations[s].OpenLeftDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Doors[0].AnticipatedOpen = true; } } if (Game.Stations[s].OpenRightDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Doors[1].AnticipatedOpen = true; } } } if (Game.Sections.Length != 0) { Game.Sections[0].Enter(TrainManager.Trains[i]); } for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { double length = TrainManager.Trains[i].Cars[0].Length; TrainManager.Trains[i].Cars[j].Move(-length, 0.01); TrainManager.Trains[i].Cars[j].Move(length, 0.01); } } // score Game.CurrentScore.ArrivalStation = PlayerFirstStationIndex + 1; Game.CurrentScore.DepartureStation = PlayerFirstStationIndex; Game.CurrentScore.Maximum = 0; for (int i = 0; i < Game.Stations.Length; i++) { if (i != PlayerFirstStationIndex & Game.PlayerStopsAtStation(i)) { if (i == 0 || Game.Stations[i - 1].Type != StationType.ChangeEnds) { Game.CurrentScore.Maximum += Game.ScoreValueStationArrival; } } } if (Game.CurrentScore.Maximum <= 0) { Game.CurrentScore.Maximum = Game.ScoreValueStationArrival; } // signals if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // move train in position for (int i = 0; i < TrainManager.Trains.Length; i++) { double p; if (i == TrainManager.PlayerTrain.TrainIndex) { p = PlayerFirstStationPosition; } else if (TrainManager.Trains[i].State == TrainManager.TrainState.Bogus) { p = Game.BogusPretrainInstructions[0].TrackPosition; TrainManager.Trains[i].AI = new Game.BogusPretrainAI(TrainManager.Trains[i]); } else { p = OtherFirstStationPosition; } for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Move(p, 0.01); } } // timetable if (Timetable.DefaultTimetableDescription.Length == 0) { Timetable.DefaultTimetableDescription = Game.LogTrainName; } // initialize camera if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { World.CameraMode = World.CameraViewMode.InteriorLookAhead; } //Place the initial camera in the driver car TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].UpdateCamera(); World.CameraTrackFollower.Update(-1.0, true, false); ObjectManager.UpdateVisibility(World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z); World.CameraSavedExterior = new World.CameraAlignment(new OpenBveApi.Math.Vector3(-2.5, 1.5, -15.0), 0.3, -0.2, 0.0, PlayerFirstStationPosition, 1.0); World.CameraSavedTrack = new World.CameraAlignment(new OpenBveApi.Math.Vector3(-3.0, 2.5, 0.0), 0.3, 0.0, 0.0, TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition - 10.0, 1.0); // signalling sections for (int i = 0; i < TrainManager.Trains.Length; i++) { int s = TrainManager.Trains[i].CurrentSectionIndex; Game.Sections[s].Enter(TrainManager.Trains[i]); } if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // fast-forward until start time { Game.MinimalisticSimulation = true; const double w = 0.25; double u = Game.StartupTime - Game.SecondsSinceMidnight; if (u > 0) { while (true) { double v = u < w ? u : w; u -= v; Game.SecondsSinceMidnight += v; TrainManager.UpdateTrains(v); if (u <= 0.0) { break; } TotalTimeElapsedForSectionUpdate += v; if (TotalTimeElapsedForSectionUpdate >= 1.0) { if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } TotalTimeElapsedForSectionUpdate = 0.0; } } } Game.MinimalisticSimulation = false; } // animated objects ObjectManager.UpdateAnimatedWorldObjects(0.0, true); TrainManager.UpdateTrainObjects(0.0, true); //HACK: This function calls a single update on all objects attached to the player's train // but ignores any specified damping so that all needles etc. are in the correct place // for the first frame, rather than spinning wildly to get to the starting point. TrainManager.PlayerTrain.UpdateCabObjects(); // timetable if (TrainManager.PlayerTrain.Station >= 0) { Timetable.UpdateCustomTimetable(Game.Stations[TrainManager.PlayerTrain.Station].TimetableDaytimeTexture, Game.Stations[TrainManager.PlayerTrain.Station].TimetableNighttimeTexture); if (Timetable.CustomObjectsUsed != 0 & Timetable.CustomTimetableAvailable && Interface.CurrentOptions.TimeTableStyle != Interface.TimeTableMode.AutoGenerated && Interface.CurrentOptions.TimeTableStyle != Interface.TimeTableMode.None) { Timetable.CurrentTimetable = Timetable.TimetableState.Custom; } } //Create AI driver for the player train if specified via the commmand line if (Game.InitialAIDriver == true) { TrainManager.PlayerTrain.AI = new Game.SimpleHumanDriverAI(TrainManager.PlayerTrain); if (TrainManager.PlayerTrain.Plugin != null && !TrainManager.PlayerTrain.Plugin.SupportsAI) { Game.AddMessage(Interface.GetInterfaceString("notification_aiunable"), MessageManager.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.White, Game.SecondsSinceMidnight + 10.0, null); } } // warnings / errors if (Interface.MessageCount != 0) { int filesNotFound = 0; int errors = 0; int warnings = 0; for (int i = 0; i < Interface.MessageCount; i++) { if (Interface.Messages[i].FileNotFound) { filesNotFound++; } else if (Interface.Messages[i].Type == Interface.MessageType.Error) { errors++; } else if (Interface.Messages[i].Type == Interface.MessageType.Warning) { warnings++; } } string NotFound = null; string Messages = null; if (filesNotFound != 0) { NotFound = filesNotFound.ToString() + " file(s) not found"; Game.AddMessage(NotFound, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } if (errors != 0 & warnings != 0) { Messages = errors.ToString() + " error(s), " + warnings.ToString() + " warning(s)"; Game.AddMessage(Messages, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } else if (errors != 0) { Messages = errors.ToString() + " error(s)"; Game.AddMessage(Messages, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } else { Messages = warnings.ToString() + " warning(s)"; Game.AddMessage(Messages, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } Game.RouteInformation.FilesNotFound = NotFound; Game.RouteInformation.ErrorsAndWarnings = Messages; //Print the plugin error encountered (If any) for 10s //This must be done after the simulation has init, as otherwise the timeout doesn't work if (Loading.PluginError != null) { Game.AddMessage(Loading.PluginError, MessageManager.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0, null); Game.AddMessage(Interface.GetInterfaceString("errors_plugin_failure2"), MessageManager.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0, null); } } loadComplete = true; RenderRealTimeElapsed = 0.0; RenderTimeElapsed = 0.0; World.InitializeCameraRestriction(); Loading.SimulationSetup = true; switch (Game.InitialViewpoint) { case 1: //Switch camera to exterior MainLoop.SaveCameraSettings(); World.CameraMode = World.CameraViewMode.Exterior; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } //Make bogies visible for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; case 2: //Switch camera to track MainLoop.SaveCameraSettings(); World.CameraMode = World.CameraViewMode.Track; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; case 3: //Switch camera to flyby MainLoop.SaveCameraSettings(); World.CameraMode = World.CameraViewMode.FlyBy; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; case 4: //Switch camera to flyby MainLoop.SaveCameraSettings(); World.CameraMode = World.CameraViewMode.FlyByZooming; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; } }
protected override void OnUpdateFrame(FrameEventArgs e) { TimeFactor = MainLoop.TimeFactor; // timer double RealTimeElapsed; double TimeElapsed; if (Game.SecondsSinceMidnight >= Game.StartupTime) { RealTimeElapsed = CPreciseTimer.GetElapsedTime(); TimeElapsed = RealTimeElapsed * (double)TimeFactor; if (loadComplete && !firstFrame) { //Our current in-game time is equal to or greater than the startup time, but the first frame has not yet been processed //Therefore, reset the timer to zero as time consuming texture loads may cause us to be late at the first station RealTimeElapsed = 0.0; TimeElapsed = 0.0; firstFrame = true; } } else { RealTimeElapsed = 0.0; TimeElapsed = Game.StartupTime - Game.SecondsSinceMidnight; } //We only want to update the simulation if we aren't in a menu if (Game.CurrentInterface == Game.InterfaceType.Normal) { #if DEBUG //If we're in debug mode and a frame takes greater than a second to render, we can safely assume that VS has hit a breakpoint //Check this and the sim no longer barfs because the update time was too great if (RealTimeElapsed > 1) { RealTimeElapsed = 0.0; TimeElapsed = 0.0; } #endif TotalTimeElapsedForInfo += TimeElapsed; TotalTimeElapsedForSectionUpdate += TimeElapsed; if (TotalTimeElapsedForSectionUpdate >= 1.0) { if (Game.Sections.Length != 0) { Game.UpdateSection(Game.Sections.Length - 1); } TotalTimeElapsedForSectionUpdate = 0.0; } // events // update simulation in chunks { const double chunkTime = 1.0 / 2.0; if (TimeElapsed <= chunkTime) { Game.SecondsSinceMidnight += TimeElapsed; TrainManager.UpdateTrains(TimeElapsed); } else { const int maxChunks = 2; int chunks = Math.Min((int)Math.Round(TimeElapsed / chunkTime), maxChunks); double time = TimeElapsed / (double)chunks; for (int i = 0; i < chunks; i++) { Game.SecondsSinceMidnight += time; TrainManager.UpdateTrains(time); } } } Game.CurrentScore.Update(TimeElapsed); Game.UpdateMessages(); Game.UpdateScoreMessages(TimeElapsed); } RenderTimeElapsed += TimeElapsed; RenderRealTimeElapsed += RealTimeElapsed; }
/// <summary>Jumps a train to a new station</summary> /// <param name="train">The train</param> /// <param name="stationIndex">The zero-based index of the station</param> internal static void JumpTrain(Train train, int stationIndex) { train.SafetySystems.PassAlarm.Halt(); int currentTrackElement = train.Cars[0].FrontAxle.Follower.LastTrackElement; if (train.IsPlayerTrain) { for (int i = 0; i < ObjectManager.AnimatedWorldObjects.Length; i++) { var obj = ObjectManager.AnimatedWorldObjects[i] as OpenBveApi.Objects.TrackFollowingObject; if (obj != null) { //Track followers should be reset if we jump between stations obj.FrontAxleFollower.TrackPosition = ObjectManager.AnimatedWorldObjects[i].TrackPosition + obj.FrontAxlePosition; obj.FrontAxleFollower.TrackPosition = ObjectManager.AnimatedWorldObjects[i].TrackPosition + obj.RearAxlePosition; obj.FrontAxleFollower.UpdateWorldCoordinates(false); obj.RearAxleFollower.UpdateWorldCoordinates(false); } } } train.StationState = TrainStopState.Jumping; int stopIndex = Program.CurrentRoute.Stations[stationIndex].GetStopIndex(train.NumberOfCars); if (stopIndex >= 0) { if (train.IsPlayerTrain) { if (train.Plugin != null) { train.Plugin.BeginJump((OpenBveApi.Runtime.InitializationModes)Game.TrainStart); } } for (int h = 0; h < train.Cars.Length; h++) { train.Cars[h].CurrentSpeed = 0.0; } double d = Program.CurrentRoute.Stations[stationIndex].Stops[stopIndex].TrackPosition - train.Cars[0].FrontAxle.Follower.TrackPosition + train.Cars[0].FrontAxle.Position - 0.5 * train.Cars[0].Length; if (train.IsPlayerTrain) { SoundsBase.SuppressSoundEvents = true; } while (d != 0.0) { double x; if (Math.Abs(d) > 1.0) { x = (double)Math.Sign(d); } else { x = d; } for (int h = 0; h < train.Cars.Length; h++) { train.Cars[h].Move(x); } if (Math.Abs(d) >= 1.0) { d -= x; } else { break; } } if (train.IsPlayerTrain) { TrainManager.UnderailTrains(); SoundsBase.SuppressSoundEvents = false; } if (train.Handles.EmergencyBrake.Driver) { train.ApplyNotch(0, false, 0, true); } else { train.ApplyNotch(0, false, train.Handles.Brake.MaximumNotch, false); train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); } if (Program.CurrentRoute.Sections.Length > 0) { Program.CurrentRoute.UpdateAllSections(); } if (train.IsPlayerTrain) { if (Game.CurrentScore.ArrivalStation <= stationIndex) { Game.CurrentScore.ArrivalStation = stationIndex + 1; } } if (train.IsPlayerTrain) { if (Program.CurrentRoute.Stations[stationIndex].ArrivalTime >= 0.0) { Program.CurrentRoute.SecondsSinceMidnight = Program.CurrentRoute.Stations[stationIndex].ArrivalTime; } else if (Program.CurrentRoute.Stations[stationIndex].DepartureTime >= 0.0) { Program.CurrentRoute.SecondsSinceMidnight = Program.CurrentRoute.Stations[stationIndex].DepartureTime - Program.CurrentRoute.Stations[stationIndex].StopTime; } } for (int i = 0; i < train.Cars.Length; i++) { train.Cars[i].Doors[0].AnticipatedOpen = Program.CurrentRoute.Stations[stationIndex].OpenLeftDoors; train.Cars[i].Doors[1].AnticipatedOpen = Program.CurrentRoute.Stations[stationIndex].OpenRightDoors; } if (train.IsPlayerTrain) { Game.CurrentScore.DepartureStation = stationIndex; Program.Renderer.CurrentInterface = InterfaceType.Normal; } ObjectManager.UpdateAnimatedWorldObjects(0.0, true); TrainManager.UpdateTrainObjects(0.0, true); if (train.IsPlayerTrain) { if (train.Plugin != null) { train.Plugin.EndJump(); } } train.StationState = TrainStopState.Pending; if (train.IsPlayerTrain) { train.LastStation = stationIndex; } int newTrackElement = train.Cars[0].FrontAxle.Follower.LastTrackElement; if (newTrackElement < currentTrackElement) { for (int i = newTrackElement; i < currentTrackElement; i++) { for (int j = 0; j < Program.CurrentHost.Tracks[0].Elements[i].Events.Length; j++) { Program.CurrentHost.Tracks[0].Elements[i].Events[j].Reset(); } } } } }
private static void LoadEverythingThreaded() { Program.FileSystem.AppendToLogFile("Loading route file: " + CurrentRouteFile); Program.FileSystem.AppendToLogFile("INFO: Route file hash " + CsvRwRouteParser.GetChecksum(CurrentRouteFile)); string RailwayFolder = GetRailwayFolder(CurrentRouteFile); string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); Game.Reset(true, false); Game.MinimalisticSimulation = true; // screen Program.Renderer.Camera.CurrentMode = CameraViewMode.Interior; //First, check the format of the route file //RW routes were written for BVE1 / 2, and have a different command syntax bool IsRW = CsvRwRouteParser.isRWFile(CurrentRouteFile); Program.FileSystem.AppendToLogFile("Route file format is: " + (IsRW ? "RW" : "CSV")); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); Thread createIllustrations = new Thread(Game.RouteInformation.LoadInformation) { IsBackground = true }; createIllustrations.Start(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.CurrentRoute.Atmosphere.CalculateSeaLevelConstants(); if (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0) { double t = Program.CurrentRoute.BogusPreTrainInstructions[0].Time; double p = Program.CurrentRoute.BogusPreTrainInstructions[0].TrackPosition; for (int i = 1; i < Program.CurrentRoute.BogusPreTrainInstructions.Length; i++) { if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time > t) { t = Program.CurrentRoute.BogusPreTrainInstructions[i].Time; } else { t += 1.0; Program.CurrentRoute.BogusPreTrainInstructions[i].Time = t; } if (Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition > p) { p = Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition; } else { p += 1.0; Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition = p; } } } World.CameraTrackFollower = new TrackFollower(Program.CurrentHost) { Train = null, Car = null }; if (Program.CurrentRoute.Stations.Length == 1) { //Log the fact that only a single station is present, as this is probably not right Program.FileSystem.AppendToLogFile("The processed route file only contains a single station."); } Program.FileSystem.AppendToLogFile("Route file loaded successfully."); RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Program.CurrentRoute.BogusPreTrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { if (k == TrainManager.Trains.Length - 1 & Program.CurrentRoute.BogusPreTrainInstructions.Length != 0) { TrainManager.Trains[k] = new TrainManager.Train(TrainState.Bogus); } else { TrainManager.Trains[k] = new TrainManager.Train(TrainState.Pending); } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; UnifiedObject[] CarObjects = null; UnifiedObject[] BogieObjects = null; UnifiedObject[] CouplerObjects = null; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { //Sleep for 20ms to allow route loading locks to release Thread.Sleep(20); if (TrainManager.Trains[k].State == TrainState.Bogus) { // bogus train string TrainData = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat"); TrainDatParser.ParseTrainData(TrainData, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains[k].InitializeCarSounds(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { TrainManager.Trains[k].TrainFolder = CurrentTrainFolder; // real train if (TrainManager.Trains[k].IsPlayerTrain) { Program.FileSystem.AppendToLogFile("Loading player train: " + TrainManager.Trains[k].TrainFolder); } else { Program.FileSystem.AppendToLogFile("Loading AI train: " + TrainManager.Trains[k].TrainFolder); } TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; string TrainData = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.dat"); TrainDatParser.ParseTrainData(TrainData, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer); Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer != null) { Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].OpenSound.Buffer); double b = TrainManager.Trains[k].Cars[i].Doors[1].OpenSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null & TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer); Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer); double a = TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer != null) { Program.Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Doors[0].CloseSound.Buffer); double b = TrainManager.Trains[k].Cars[i].Doors[1].CloseSound.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } // add panel section if (TrainManager.Trains[k].IsPlayerTrain) { TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.FileSystem.AppendToLogFile("Train panel loaded sucessfully."); } // add exterior section if (TrainManager.Trains[k].State != TrainState.Bogus) { bool LoadObjects = false; if (CarObjects == null) { CarObjects = new UnifiedObject[TrainManager.Trains[k].Cars.Length]; BogieObjects = new UnifiedObject[TrainManager.Trains[k].Cars.Length * 2]; CouplerObjects = new UnifiedObject[TrainManager.Trains[k].Cars.Length]; LoadObjects = true; } string tXml = OpenBveApi.Path.CombineFile(TrainManager.Trains[k].TrainFolder, "train.xml"); if (System.IO.File.Exists(tXml)) { TrainXmlParser.Parse(tXml, TrainManager.Trains[k], ref CarObjects, ref BogieObjects, ref CouplerObjects); } else { ExtensionsCfgParser.ParseExtensionsConfig(TrainManager.Trains[k].TrainFolder, CurrentTrainEncoding, ref CarObjects, ref BogieObjects, ref CouplerObjects, TrainManager.Trains[k], LoadObjects); } World.CameraCar = TrainManager.Trains[k].DriverCar; System.Threading.Thread.Sleep(1); if (Cancel) { return; } //Stores the current array index of the bogie object to add //Required as there are two bogies per car, and we're using a simple linear array.... int currentBogieObject = 0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; so.ApplyScale(sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object TrainManager.Trains[k].Cars[i].LoadCarSections(CarObjects[i]); } if (CouplerObjects[i] != null) { TrainManager.Trains[k].Cars[i].Coupler.LoadCarSections(CouplerObjects[i]); } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { TrainManager.Trains[k].Cars[i].FrontBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; if (BogieObjects[currentBogieObject] != null) { TrainManager.Trains[k].Cars[i].RearBogie.LoadCarSections(BogieObjects[currentBogieObject]); } currentBogieObject++; } } // place cars TrainManager.Trains[k].PlaceCars(0.0); // configure ai / timetable if (TrainManager.Trains[k].IsPlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } /* * HACK: Store the TrainManager.Trains reference in the RouteManager also * Note that this may change when the TrainManager is separated from the lump * Remember not to modify via this ref */ // ReSharper disable once CoVariantArrayConversion Program.CurrentRoute.Trains = TrainManager.Trains; TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } Array.Resize(ref ObjectManager.AnimatedWorldObjects, ObjectManager.AnimatedWorldObjectsUsed); // update sections if (Program.CurrentRoute.Sections.Length > 0) { Program.CurrentRoute.UpdateAllSections(); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainState.Bogus) { if (TrainManager.Trains[i].IsPlayerTrain) { if (!TrainManager.Trains[i].LoadCustomPlugin(TrainManager.Trains[i].TrainFolder, CurrentTrainEncoding)) { TrainManager.Trains[i].LoadDefaultPlugin(TrainManager.Trains[i].TrainFolder); } } else { TrainManager.Trains[i].LoadDefaultPlugin(TrainManager.Trains[i].TrainFolder); } } } }
internal override void Trigger(double TimeElapsed) { // Trains need to stop more than 2 points. if (Data == null || Data.Count < 2 || SecondsSinceMidnight == TimeLastProcessed) { return; } // Initialize if (TimeLastProcessed == 0.0) { SetupTravelData(SecondsSinceMidnight); CheckTravelData(SecondsSinceMidnight); CurrentPosition = Data[0].StopPosition; } TimeLastProcessed = SecondsSinceMidnight; // Calculate the position where the train is at the present time. double DeltaT; double Position = Data[0].StopPosition; int RailIndex = Data[0].RailIndex; bool OpenLeftDoors = false; bool OpenRightDoors = false; if (SecondsSinceMidnight >= Data[0].ArrivalTime) { OpenLeftDoors = Data[0].OpenLeftDoors; OpenRightDoors = Data[0].OpenRightDoors; } if (SecondsSinceMidnight >= Data[0].ClosingDoorsStartTime) { OpenLeftDoors = false; OpenRightDoors = false; } // The start point does not slow down. Acceleration only. if (SecondsSinceMidnight >= Data[0].DepartureTime) { if (SecondsSinceMidnight < Data[0].AccelerationEndTime) { DeltaT = SecondsSinceMidnight - Data[0].DepartureTime; } else { DeltaT = Data[0].AccelerationEndTime - Data[0].DepartureTime; } Position += (int)Data[0].Direction * (0.5 * Data[0].Accelerate * Math.Pow(DeltaT, 2.0)); } for (int i = 1; i < Data.Count; i++) { if (SecondsSinceMidnight >= Data[i - 1].AccelerationEndTime) { if (SecondsSinceMidnight < Data[i].DecelerationStartTime) { DeltaT = SecondsSinceMidnight - Data[i - 1].AccelerationEndTime; } else { DeltaT = Data[i].DecelerationStartTime - Data[i - 1].AccelerationEndTime; } Position += (int)Data[i - 1].Direction * (Data[i - 1].TargetSpeed * DeltaT); } if (SecondsSinceMidnight >= Data[i].DecelerationStartTime) { if (SecondsSinceMidnight < Data[i].ArrivalTime) { DeltaT = SecondsSinceMidnight - Data[i].DecelerationStartTime; } else { DeltaT = Data[i].ArrivalTime - Data[i].DecelerationStartTime; } Position += (int)Data[i - 1].Direction * (Data[i - 1].TargetSpeed * DeltaT - 0.5 * Data[i].Decelerate * Math.Pow(DeltaT, 2.0)); } if (SecondsSinceMidnight >= Data[i].ArrivalTime) { OpenLeftDoors = Data[i].OpenLeftDoors; OpenRightDoors = Data[i].OpenRightDoors; } if (SecondsSinceMidnight >= Data[i].ClosingDoorsStartTime) { OpenLeftDoors = false; OpenRightDoors = false; } if (SecondsSinceMidnight >= Data[i].DepartureTime) { if (SecondsSinceMidnight < Data[i].AccelerationEndTime) { DeltaT = SecondsSinceMidnight - Data[i].DepartureTime; } else { DeltaT = Data[i].AccelerationEndTime - Data[i].DepartureTime; } Position += (int)Data[i].Direction * (0.5 * Data[i].Accelerate * Math.Pow(DeltaT, 2.0)); RailIndex = Data[i].RailIndex; } } // Calculate the travel distance of the train. double Delta = Position - CurrentPosition; CurrentPosition = Position; // Set the state quantity of the train. if (Delta < 0) { Train.Handles.Reverser.Driver = TrainManager.ReverserPosition.Reverse; } else if (Delta > 0) { Train.Handles.Reverser.Driver = TrainManager.ReverserPosition.Forwards; } Train.Handles.Reverser.Actual = Train.Handles.Reverser.Driver; TrainManager.OpenTrainDoors(Train, OpenLeftDoors, OpenRightDoors); TrainManager.CloseTrainDoors(Train, !OpenLeftDoors, !OpenRightDoors); foreach (var Car in Train.Cars) { Car.FrontAxle.Follower.TrackIndex = RailIndex; Car.RearAxle.Follower.TrackIndex = RailIndex; Car.FrontBogie.FrontAxle.Follower.TrackIndex = RailIndex; Car.FrontBogie.RearAxle.Follower.TrackIndex = RailIndex; Car.RearBogie.FrontAxle.Follower.TrackIndex = RailIndex; Car.RearBogie.RearAxle.Follower.TrackIndex = RailIndex; Car.Move(Delta); if (TimeElapsed != 0.0) { Car.Specs.CurrentSpeed = Delta / TimeElapsed; Car.Specs.CurrentPerceivedSpeed = Car.Specs.CurrentSpeed; if (Car.Specs.CurrentPerceivedSpeed < 0) { Car.Specs.CurrentAcceleration = -(Car.Specs.CurrentSpeed / TimeElapsed); } else { Car.Specs.CurrentAcceleration = Car.Specs.CurrentSpeed / TimeElapsed; } Car.Specs.CurrentAccelerationOutput = Car.Specs.CurrentAcceleration; } else { Car.Specs.CurrentSpeed = 0.0; Car.Specs.CurrentPerceivedSpeed = 0.0; Car.Specs.CurrentAcceleration = 0.0; Car.Specs.CurrentAccelerationOutput = 0.0; } } Train.Specs.CurrentAverageSpeed = Train.Cars[0].Specs.CurrentSpeed; Train.Specs.CurrentAverageAcceleration = Train.Cars[0].Specs.CurrentAcceleration; // Dispose the train if it is past the leave time of the train if (LeaveTime > AppearanceTime && SecondsSinceMidnight >= LeaveTime) { Train.Dispose(); } }
internal double Perform(TrainManager.Train Train, int CarIndex, World.Vector3D Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed) { ExecuteFunctionScript(this, Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); return this.LastResult; }
// load bve 4 sound private static void LoadBve4Sound(string FileName, string TrainPath, System.Text.Encoding Encoding, TrainManager.Train train) { // set sound positions and radii Vector3 center = new Vector3(0.0, 0.0, 0.0); Vector3 left = new Vector3(-1.3, 0.0, 0.0); Vector3 right = new Vector3(1.3, 0.0, 0.0); Vector3 front = new Vector3(0.0, 0.0, 0.5 * train.Cars[train.DriverCar].Length); Vector3 cab = new Vector3(-train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ - 0.5); Vector3 panel = new Vector3(train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ + 1.0); double large = 30.0; double medium = 10.0; double small = 5.0; double tiny = 2.0; LoadNoSound(train); // parse configuration file System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } } if (Lines.Length < 1 || string.Compare(Lines[0], "version 1.0", StringComparison.OrdinalIgnoreCase) != 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid file format encountered in " + FileName + ". The first line is expected to be \"Version 1.0\"."); } string[] MotorFiles = new string[] { }; double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } switch (Lines[i].ToLowerInvariant()) { case "[run]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { for (int c = 0; c < train.Cars.Length; c++) { int n = train.Cars[c].Sounds.Run.Length; if (k >= n) { Array.Resize<TrainManager.CarSound>(ref train.Cars[c].Sounds.Run, k + 1); for (int h = n; h < k; h++) { train.Cars[c].Sounds.Run[h] = TrainManager.CarSound.Empty; } } train.Cars[c].Sounds.Run[k] = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Index must be greater or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[flange]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { for (int c = 0; c < train.Cars.Length; c++) { int n = train.Cars[c].Sounds.Flange.Length; if (k >= n) { Array.Resize<TrainManager.CarSound>(ref train.Cars[c].Sounds.Flange, k + 1); for (int h = n; h < k; h++) { train.Cars[c].Sounds.Flange[h] = TrainManager.CarSound.Empty; } } train.Cars[c].Sounds.Flange[k] = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Index must be greater or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[motor]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { if (k >= MotorFiles.Length) { Array.Resize<string>(ref MotorFiles, k + 1); } MotorFiles[k] = OpenBveApi.Path.CombineFile(TrainPath, b); if (!System.IO.File.Exists(MotorFiles[k])) { Interface.AddMessage(Interface.MessageType.Error, true, "File " + MotorFiles[k] + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); MotorFiles[k] = null; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Index is invalid at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } i++; } i--; break; case "[switch]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (a == "0") { for (int c = 0; c < train.Cars.Length; c++) { Vector3 frontaxle = new Vector3(0.0, 0.0, train.Cars[c].FrontAxlePosition); Vector3 rearaxle = new Vector3(0.0, 0.0, train.Cars[c].RearAxlePosition); train.Cars[c].Sounds.PointFrontAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), frontaxle, small); train.Cars[c].Sounds.PointRearAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), rearaxle, small); } } else { Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported index " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; case "[brake]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "bc release high": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.AirHigh = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; case "bc release": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.Air = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; case "bc release full": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.AirZero = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; case "emergency": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.EmrBrake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } break; case "bp decomp": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.Brake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, small); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[compressor]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.AirBrake.Type == TrainManager.AirBrakeType.Main) { switch (a.ToLowerInvariant()) { case "attack": train.Cars[c].Sounds.CpStart = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); break; case "loop": train.Cars[c].Sounds.CpLoop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); break; case "release": train.Cars[c].Sounds.CpEnd = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } } } i++; } i--; break; case "[suspension]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.SpringL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.SpringR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), right, small); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[horn]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "primary": train.Cars[train.DriverCar].Sounds.Horns[0].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), front, large); train.Cars[train.DriverCar].Sounds.Horns[0].Loop = false; break; case "secondary": train.Cars[train.DriverCar].Sounds.Horns[1].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), front, large); train.Cars[train.DriverCar].Sounds.Horns[1].Loop = false; break; case "music": train.Cars[train.DriverCar].Sounds.Horns[2].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), front, medium); train.Cars[train.DriverCar].Sounds.Horns[2].Loop = true; break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[door]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "open left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorOpenL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "open right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorOpenR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "close left": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorCloseL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; case "close right": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.DoorCloseR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), left, small); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[ats]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { int k; if (!int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out k)) { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid index appeared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (k >= 0) { int n = train.Cars[train.DriverCar].Sounds.Plugin.Length; if (k >= n) { Array.Resize<TrainManager.CarSound>(ref train.Cars[train.DriverCar].Sounds.Plugin, k + 1); for (int h = n; h < k; h++) { train.Cars[train.DriverCar].Sounds.Plugin[h] = TrainManager.CarSound.Empty; } } train.Cars[train.DriverCar].Sounds.Plugin[k] = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); } else { Interface.AddMessage(Interface.MessageType.Warning, false, "Index must be greater or equal to zero at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; break; case "[buzzer]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "correct": train.Cars[train.DriverCar].Sounds.Adjust = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[pilot lamp]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "on": train.Cars[train.DriverCar].Sounds.PilotLampOn = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "off": train.Cars[train.DriverCar].Sounds.PilotLampOff = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[brake handle]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "apply": train.Cars[train.DriverCar].Sounds.BrakeHandleApply = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "release": train.Cars[train.DriverCar].Sounds.BrakeHandleRelease = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "min": train.Cars[train.DriverCar].Sounds.BrakeHandleMin = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "max": train.Cars[train.DriverCar].Sounds.BrakeHandleMax = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[master controller]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "up": train.Cars[train.DriverCar].Sounds.MasterControllerUp = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "down": train.Cars[train.DriverCar].Sounds.MasterControllerDown = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "min": train.Cars[train.DriverCar].Sounds.MasterControllerMin = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "max": train.Cars[train.DriverCar].Sounds.MasterControllerMax = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[reverser]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "on": train.Cars[train.DriverCar].Sounds.ReverserOn = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; case "off": train.Cars[train.DriverCar].Sounds.ReverserOff = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, tiny); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[breaker]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "on": train.Cars[train.DriverCar].Sounds.BreakerResume = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, small); break; case "off": train.Cars[train.DriverCar].Sounds.BreakerResumeOrInterrupt = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), panel, small); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; case "[others]": i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal)) { int j = Lines[i].IndexOf("="); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); if (b.Length == 0 || Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters or is empty at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "noise": for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.IsMotorCar | c == train.DriverCar) { train.Cars[c].Sounds.Loop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } } break; case "shoe": for (int c = 0; c < train.Cars.Length; c++) { train.Cars[c].Sounds.Rub = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, b), center, medium); } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } } i++; } i--; break; } } for (int i = 0; i < train.Cars.Length; i++) { train.Cars[i].Sounds.RunVolume = new double[train.Cars[i].Sounds.Run.Length]; train.Cars[i].Sounds.FlangeVolume = new double[train.Cars[i].Sounds.Flange.Length]; } // motor sound for (int c = 0; c < train.Cars.Length; c++) { if (train.Cars[c].Specs.IsMotorCar) { train.Cars[c].Sounds.Motor.Position = center; for (int i = 0; i < train.Cars[c].Sounds.Motor.Tables.Length; i++) { train.Cars[c].Sounds.Motor.Tables[i].Buffer = null; train.Cars[c].Sounds.Motor.Tables[i].Source = null; for (int j = 0; j < train.Cars[c].Sounds.Motor.Tables[i].Entries.Length; j++) { int index = train.Cars[c].Sounds.Motor.Tables[i].Entries[j].SoundIndex; if (index >= 0 && index < MotorFiles.Length && MotorFiles[index] != null) { train.Cars[c].Sounds.Motor.Tables[i].Entries[j].Buffer = Sounds.RegisterBuffer(MotorFiles[index], medium); } } } } } }
/// <summary>Loads a custom plugin for the specified train.</summary> /// <param name="train">The train to attach the plugin to.</param> /// <param name="trainFolder">The absolute path to the train folder.</param> /// <param name="encoding">The encoding to be used.</param> /// <returns>Whether the plugin was loaded successfully.</returns> internal static bool LoadCustomPlugin(TrainManager.Train train, string trainFolder, System.Text.Encoding encoding) { string config = OpenBveApi.Path.CombineFile(trainFolder, "ats.cfg"); if (!System.IO.File.Exists(config)) { return false; } string[] lines = System.IO.File.ReadAllLines(config, encoding); if (lines.Length == 0) { return false; } string file = OpenBveApi.Path.CombineFile(trainFolder, lines[0]); string title = System.IO.Path.GetFileName(file); if (!System.IO.File.Exists(file)) { Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + title + " could not be found in " + config); return false; } Program.AppendToLogFile("Loading train plugin: " + file); bool success = LoadPlugin(train, file, trainFolder); if (success == false) { Loading.PluginError = Interface.GetInterfaceString("errors_plugin_failure1").Replace("[plugin]", file); } else { Program.AppendToLogFile("Train plugin loaded successfully."); } return success; }
// create element private static int CreateElement(TrainManager.Train Train, double Left, double Top, double Width, double Height, double FullWidth, double FullHeight, double WorldLeft, double WorldTop, double WorldWidth, double WorldHeight, double WorldZ, double DriverX, double DriverY, double DriverZ, Textures.Texture Texture, Color32 Color, bool AddStateToLastElement) { // create object ObjectManager.StaticObject Object = new ObjectManager.StaticObject(); Vector3[] v = new Vector3[4]; double sx = 0.5 * WorldWidth * Width / FullWidth; double sy = 0.5 * WorldHeight * Height / FullHeight; v[0] = new Vector3(-sx, -sy, 0); v[1] = new Vector3(-sx, sy, 0); v[2] = new Vector3(sx, sy, 0); v[3] = new Vector3(sx, -sy, 0); World.Vertex t0 = new World.Vertex(v[0], new Vector2(0.0f, 1.0f)); World.Vertex t1 = new World.Vertex(v[1], new Vector2(0.0f, 0.0f)); World.Vertex t2 = new World.Vertex(v[2], new Vector2(1.0f, 0.0f)); World.Vertex t3 = new World.Vertex(v[3], new Vector2(1.0f, 1.0f)); Object.Mesh.Vertices = new World.Vertex[] { t0, t1, t2, t3 }; Object.Mesh.Faces = new World.MeshFace[] { new World.MeshFace(new int[] { 0, 1, 2, 3 }) }; Object.Mesh.Materials = new World.MeshMaterial[1]; Object.Mesh.Materials[0].Flags = Texture != null ? (byte)World.MeshMaterial.TransparentColorMask : (byte)0; Object.Mesh.Materials[0].Color = Color; Object.Mesh.Materials[0].TransparentColor = new Color24(0, 0, 255); Object.Mesh.Materials[0].DaytimeTexture = Texture; Object.Mesh.Materials[0].NighttimeTexture = null; Object.Dynamic = true; // calculate offset Vector3 o; o.X = WorldLeft + sx + WorldWidth * Left / FullWidth; o.Y = WorldTop - sy - WorldHeight * Top / FullHeight; o.Z = WorldZ; // add object if (AddStateToLastElement) { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length - 1; int j = Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States.Length; Array.Resize<ObjectManager.AnimatedObjectState>(ref Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States, j + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[j].Object = Object; return n; } else { int n = Train.Cars[Train.DriverCar].CarSections[0].Elements.Length; Array.Resize<ObjectManager.AnimatedObject>(ref Train.Cars[Train.DriverCar].CarSections[0].Elements, n + 1); Train.Cars[Train.DriverCar].CarSections[0].Elements[n] = new ObjectManager.AnimatedObject(); Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States = new ObjectManager.AnimatedObjectState[1]; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Position = o; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].States[0].Object = Object; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].CurrentState = 0; Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex = ObjectManager.CreateDynamicObject(); ObjectManager.Objects[Train.Cars[Train.DriverCar].CarSections[0].Elements[n].ObjectIndex] = ObjectManager.CloneObject(Object); return n; } }
private static void LoadEverythingThreaded() { string RailwayFolder = GetRailwayFolder(CurrentRouteFile); // if (RailwayFolder == null) { // Interface.AddMessage(Interface.MessageType.Critical, false, "The Railway folder could not be found. Please check your folder structure."); // return; // } string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower(); World.CameraTrackFollower.Train = null; World.CameraTrackFollower.CarIndex = -1; World.CameraMode = World.CameraViewMode.Interior; // load route bool IsRW = string.Equals(System.IO.Path.GetExtension(CurrentRouteFile), ".rw", StringComparison.OrdinalIgnoreCase); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { TrainManager.Trains[k] = new TrainManager.Train(); TrainManager.Trains[k].TrainIndex = k; if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k].State = TrainManager.TrainState.Bogus; } else { TrainManager.Trains[k].State = TrainManager.TrainState.Pending; } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string Folder = Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"); TrainDatParser.ParseTrainData(Folder, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } SoundCfgParser.LoadNoSound(TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { // real train TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; TrainDatParser.ParseTrainData(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].BeaconReceiver.Train = TrainManager.Trains[k]; } // add panel section if (k == TrainManager.PlayerTrain.TrainIndex) { TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections = new TrainManager.CarSection[1]; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay = true; TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { ObjectManager.UnifiedObject[] CarObjects; ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; CsvB3dObjectParser.ApplyScale(so, sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object int j = TrainManager.Trains[k].Cars[i].CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1); if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } } } // place cars { double z = 0.0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxlePosition; TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxlePosition; TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition; z -= TrainManager.Trains[k].Cars[i].Length; if (i < TrainManager.Trains[k].Cars.Length - 1) { z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars); } } } // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } ObjectManager.FinishCreatingObjects(); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } } }
/// <summary>Jumps a train to a new station</summary> /// <param name="train">The train</param> /// <param name="stationIndex">The zero-based index of the station</param> internal static void JumpTrain(Train train, int stationIndex) { if (train == PlayerTrain) { for (int i = 0; i < ObjectManager.AnimatedWorldObjects.Length; i++) { var obj = ObjectManager.AnimatedWorldObjects[i] as ObjectManager.TrackFollowingObject; if (obj != null) { //Track followers should be reset if we jump between stations obj.FrontAxleFollower.TrackPosition = ObjectManager.AnimatedWorldObjects[i].TrackPosition + obj.FrontAxlePosition; obj.FrontAxleFollower.TrackPosition = ObjectManager.AnimatedWorldObjects[i].TrackPosition + obj.RearAxlePosition; obj.FrontAxleFollower.UpdateWorldCoordinates(false); obj.RearAxleFollower.UpdateWorldCoordinates(false); } } } train.StationState = TrainStopState.Jumping; int stopIndex = Game.GetStopIndex(stationIndex, train.Cars.Length); if (stopIndex >= 0) { if (train == PlayerTrain) { if (train.Plugin != null) { train.Plugin.BeginJump((OpenBveApi.Runtime.InitializationModes)Game.TrainStart); } } for (int h = 0; h < train.Cars.Length; h++) { train.Cars[h].Specs.CurrentSpeed = 0.0; } double d = Game.Stations[stationIndex].Stops[stopIndex].TrackPosition - train.Cars[0].FrontAxle.Follower.TrackPosition + train.Cars[0].FrontAxle.Position - 0.5 * train.Cars[0].Length; if (train == PlayerTrain) { TrackManager.SuppressSoundEvents = true; } while (d != 0.0) { double x; if (Math.Abs(d) > 1.0) { x = (double)Math.Sign(d); } else { x = d; } for (int h = 0; h < train.Cars.Length; h++) { train.Cars[h].Move(x, 0.0); } if (Math.Abs(d) >= 1.0) { d -= x; } else { break; } } if (train == PlayerTrain) { TrainManager.UnderailTrains(); TrackManager.SuppressSoundEvents = false; } if (train.Handles.EmergencyBrake.Driver) { train.ApplyNotch(0, false, 0, true); } else { train.ApplyNotch(0, false, train.Handles.Brake.MaximumNotch, false); train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); } if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } if (train == PlayerTrain) { if (Game.CurrentScore.ArrivalStation <= stationIndex) { Game.CurrentScore.ArrivalStation = stationIndex + 1; } } if (train == PlayerTrain) { if (Game.Stations[stationIndex].ArrivalTime >= 0.0) { Game.SecondsSinceMidnight = Game.Stations[stationIndex].ArrivalTime; } else if (Game.Stations[stationIndex].DepartureTime >= 0.0) { Game.SecondsSinceMidnight = Game.Stations[stationIndex].DepartureTime - Game.Stations[stationIndex].StopTime; } } for (int i = 0; i < train.Cars.Length; i++) { train.Cars[i].Doors[0].AnticipatedOpen = Game.Stations[stationIndex].OpenLeftDoors; train.Cars[i].Doors[1].AnticipatedOpen = Game.Stations[stationIndex].OpenRightDoors; } if (train == PlayerTrain) { Game.CurrentScore.DepartureStation = stationIndex; Game.CurrentInterface = Game.InterfaceType.Normal; //Game.Messages = new Game.Message[] { }; } ObjectManager.UpdateAnimatedWorldObjects(0.0, true); TrainManager.UpdateTrainObjects(0.0, true); if (train == PlayerTrain) { if (train.Plugin != null) { train.Plugin.EndJump(); } } train.StationState = TrainStopState.Pending; } }
/// <summary>Renders all default HUD elements</summary> /// <param name="Element">The HUD element these are to be rendererd onto</param> /// <param name="TimeElapsed">The time elapsed</param> private static void RenderHUDElement(HUD.Element Element, double TimeElapsed) { TrainManager.TrainDoorState LeftDoors = TrainManager.GetDoorsState(TrainManager.PlayerTrain, true, false); TrainManager.TrainDoorState RightDoors = TrainManager.GetDoorsState(TrainManager.PlayerTrain, false, true); System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string Command = Element.Subject.ToLowerInvariant(); // default double w, h; if (Element.CenterMiddle.BackgroundTexture != null) { if (Program.CurrentHost.LoadTexture(Element.CenterMiddle.BackgroundTexture, OpenGlTextureWrapMode.ClampClamp)) { w = (double)Element.CenterMiddle.BackgroundTexture.Width; h = (double)Element.CenterMiddle.BackgroundTexture.Height; } else { w = 0.0; h = 0.0; } } else { w = 0.0; h = 0.0; } double x = Element.Alignment.X < 0 ? 0.0 : Element.Alignment.X == 0 ? 0.5 * (LibRender.Screen.Width - w) : LibRender.Screen.Width - w; double y = Element.Alignment.Y < 0 ? 0.0 : Element.Alignment.Y == 0 ? 0.5 * (LibRender.Screen.Height - h) : LibRender.Screen.Height - h; x += Element.Position.X; y += Element.Position.Y; // command const double speed = 1.0; MessageColor sc = MessageColor.None; string t; switch (Command) { case "reverser": if (TrainManager.PlayerTrain.Handles.Reverser.Driver < 0) { sc = MessageColor.Orange; if (TrainManager.PlayerTrain.ReverserDescriptions != null && TrainManager.PlayerTrain.ReverserDescriptions.Length > 2) { t = TrainManager.PlayerTrain.ReverserDescriptions[2]; } else { t = Translations.QuickReferences.HandleBackward; } } else if (TrainManager.PlayerTrain.Handles.Reverser.Driver > 0) { sc = MessageColor.Blue; if (TrainManager.PlayerTrain.ReverserDescriptions != null && TrainManager.PlayerTrain.ReverserDescriptions.Length > 0) { t = TrainManager.PlayerTrain.ReverserDescriptions[0]; } else { t = Translations.QuickReferences.HandleForward; } } else { sc = MessageColor.Gray; if (TrainManager.PlayerTrain.ReverserDescriptions != null && TrainManager.PlayerTrain.ReverserDescriptions.Length > 1) { t = TrainManager.PlayerTrain.ReverserDescriptions[1]; } else { t = Translations.QuickReferences.HandleNeutral; } } Element.TransitionState = 0.0; break; case "power": if (TrainManager.PlayerTrain.Handles.SingleHandle) { return; } if (TrainManager.PlayerTrain.Handles.Power.Driver == 0) { sc = MessageColor.Gray; if (TrainManager.PlayerTrain.PowerNotchDescriptions != null && TrainManager.PlayerTrain.PowerNotchDescriptions.Length > 0) { t = TrainManager.PlayerTrain.PowerNotchDescriptions[0]; } else { t = Translations.QuickReferences.HandlePowerNull; } } else { sc = MessageColor.Blue; if (TrainManager.PlayerTrain.PowerNotchDescriptions != null && TrainManager.PlayerTrain.Handles.Power.Driver < TrainManager.PlayerTrain.PowerNotchDescriptions.Length) { t = TrainManager.PlayerTrain.PowerNotchDescriptions[TrainManager.PlayerTrain.Handles.Power.Driver]; } else { t = Translations.QuickReferences.HandlePower + TrainManager.PlayerTrain.Handles.Power.Driver.ToString(Culture); } } Element.TransitionState = 0.0; break; case "brake": if (TrainManager.PlayerTrain.Handles.SingleHandle) { return; } if (TrainManager.PlayerTrain.Handles.Brake is TrainManager.AirBrakeHandle) { if (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver) { sc = MessageColor.Red; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 0) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[0]; } else { t = Translations.QuickReferences.HandleEmergency; } } else if (TrainManager.PlayerTrain.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Release) { sc = MessageColor.Gray; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 1) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[1]; } else { t = Translations.QuickReferences.HandleRelease; } } else if (TrainManager.PlayerTrain.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap) { sc = MessageColor.Blue; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 2) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[2]; } else { t = Translations.QuickReferences.HandleLap; } } else { sc = MessageColor.Orange; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 3) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[3]; } else { t = Translations.QuickReferences.HandleService; } } } else { if (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver) { sc = MessageColor.Red; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 0) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[0]; } else { t = Translations.QuickReferences.HandleEmergency; } } else if (TrainManager.PlayerTrain.Handles.HoldBrake.Driver) { sc = MessageColor.Green; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 2) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[2]; } else { t = Translations.QuickReferences.HandleHoldBrake; } } else if (TrainManager.PlayerTrain.Handles.Brake.Driver == 0) { sc = MessageColor.Gray; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 1) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[1]; } else { t = Translations.QuickReferences.HandleBrakeNull; } } else { sc = MessageColor.Orange; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && ((TrainManager.PlayerTrain.Handles.HasHoldBrake && TrainManager.PlayerTrain.Handles.Brake.Driver + 2 < TrainManager.PlayerTrain.BrakeNotchDescriptions.Length) || (!TrainManager.PlayerTrain.Handles.HasHoldBrake && TrainManager.PlayerTrain.Handles.Brake.Driver + 1 < TrainManager.PlayerTrain.BrakeNotchDescriptions.Length))) { t = TrainManager.PlayerTrain.Handles.HasHoldBrake ? TrainManager.PlayerTrain.BrakeNotchDescriptions[TrainManager.PlayerTrain.Handles.Brake.Driver + 2] : TrainManager.PlayerTrain.BrakeNotchDescriptions[TrainManager.PlayerTrain.Handles.Brake.Driver + 1]; } else { t = Translations.QuickReferences.HandleBrake + TrainManager.PlayerTrain.Handles.Brake.Driver.ToString(Culture); } } } Element.TransitionState = 0.0; break; case "locobrake": if (!TrainManager.PlayerTrain.Handles.HasLocoBrake) { return; } if (TrainManager.PlayerTrain.Handles.LocoBrake is TrainManager.LocoAirBrakeHandle) { if (TrainManager.PlayerTrain.Handles.LocoBrake.Driver == (int)TrainManager.AirBrakeHandleState.Release) { sc = MessageColor.Gray; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 1) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[1]; } else { t = Translations.QuickReferences.HandleRelease; } } else if (TrainManager.PlayerTrain.Handles.LocoBrake.Driver == (int)TrainManager.AirBrakeHandleState.Lap) { sc = MessageColor.Blue; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 2) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[2]; } else { t = Translations.QuickReferences.HandleLap; } } else { sc = MessageColor.Orange; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 3) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[3]; } else { t = Translations.QuickReferences.HandleService; } } } else { if (TrainManager.PlayerTrain.Handles.LocoBrake.Driver == 0) { sc = MessageColor.Gray; if (TrainManager.PlayerTrain.LocoBrakeNotchDescriptions != null && TrainManager.PlayerTrain.LocoBrakeNotchDescriptions.Length > 1) { t = TrainManager.PlayerTrain.LocoBrakeNotchDescriptions[1]; } else { t = Translations.QuickReferences.HandleBrakeNull; } } else { sc = MessageColor.Orange; if (TrainManager.PlayerTrain.LocoBrakeNotchDescriptions != null && TrainManager.PlayerTrain.Handles.LocoBrake.Driver < TrainManager.PlayerTrain.LocoBrakeNotchDescriptions.Length) { t = TrainManager.PlayerTrain.LocoBrakeNotchDescriptions[TrainManager.PlayerTrain.Handles.LocoBrake.Driver]; } else { t = Translations.QuickReferences.HandleLocoBrake + TrainManager.PlayerTrain.Handles.LocoBrake.Driver.ToString(Culture); } } } Element.TransitionState = 0.0; break; case "single": if (!TrainManager.PlayerTrain.Handles.SingleHandle) { return; } if (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver) { sc = MessageColor.Red; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 0) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[0]; } else { t = Translations.QuickReferences.HandleEmergency; } } else if (TrainManager.PlayerTrain.Handles.HoldBrake.Driver) { sc = MessageColor.Green; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.BrakeNotchDescriptions.Length > 1) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[1]; } else { t = Translations.QuickReferences.HandleHoldBrake; } } else if (TrainManager.PlayerTrain.Handles.Brake.Driver > 0) { sc = MessageColor.Orange; if (TrainManager.PlayerTrain.BrakeNotchDescriptions != null && TrainManager.PlayerTrain.Handles.Brake.Driver + 3 < TrainManager.PlayerTrain.BrakeNotchDescriptions.Length) { t = TrainManager.PlayerTrain.BrakeNotchDescriptions[TrainManager.PlayerTrain.Handles.Brake.Driver + 3]; } else { t = Translations.QuickReferences.HandleBrake + TrainManager.PlayerTrain.Handles.Brake.Driver.ToString(Culture); } } else if (TrainManager.PlayerTrain.Handles.Power.Driver > 0) { sc = MessageColor.Blue; if (TrainManager.PlayerTrain.PowerNotchDescriptions != null && TrainManager.PlayerTrain.Handles.Power.Driver < TrainManager.PlayerTrain.PowerNotchDescriptions.Length) { t = TrainManager.PlayerTrain.PowerNotchDescriptions[TrainManager.PlayerTrain.Handles.Power.Driver]; } else { t = Translations.QuickReferences.HandlePower + TrainManager.PlayerTrain.Handles.Power.Driver.ToString(Culture); } } else { sc = MessageColor.Gray; if (TrainManager.PlayerTrain.PowerNotchDescriptions != null && TrainManager.PlayerTrain.PowerNotchDescriptions.Length > 0) { t = TrainManager.PlayerTrain.PowerNotchDescriptions[0]; } else { t = Translations.QuickReferences.HandlePowerNull; } } Element.TransitionState = 0.0; break; case "doorsleft": case "doorsright": { if ((LeftDoors & TrainManager.TrainDoorState.AllClosed) == 0 | (RightDoors & TrainManager.TrainDoorState.AllClosed) == 0) { Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } TrainManager.TrainDoorState Doors = Command == "doorsleft" ? LeftDoors : RightDoors; if ((Doors & TrainManager.TrainDoorState.Mixed) != 0) { sc = MessageColor.Orange; } else if ((Doors & TrainManager.TrainDoorState.AllClosed) != 0) { sc = MessageColor.Gray; } else if (TrainManager.PlayerTrain.Specs.DoorCloseMode == TrainManager.DoorMode.Manual) { sc = MessageColor.Green; } else { sc = MessageColor.Blue; } t = Command == "doorsleft" ? Translations.QuickReferences.DoorsLeft : Translations.QuickReferences.DoorsRight; } break; case "stopleft": case "stopright": case "stopnone": { int s = TrainManager.PlayerTrain.Station; if (s >= 0 && CurrentRoute.Stations[s].PlayerStops() && Interface.CurrentOptions.GameMode != GameMode.Expert) { bool cond; if (Command == "stopleft") { cond = CurrentRoute.Stations[s].OpenLeftDoors; } else if (Command == "stopright") { cond = CurrentRoute.Stations[s].OpenRightDoors; } else { cond = !CurrentRoute.Stations[s].OpenLeftDoors & !CurrentRoute.Stations[s].OpenRightDoors; } if (TrainManager.PlayerTrain.StationState == TrainStopState.Pending & cond) { Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } t = Element.Text; } break; case "stoplefttick": case "stoprighttick": case "stopnonetick": { int s = TrainManager.PlayerTrain.Station; if (s >= 0 && CurrentRoute.Stations[s].PlayerStops() && Interface.CurrentOptions.GameMode != GameMode.Expert) { int c = CurrentRoute.Stations[s].GetStopIndex(TrainManager.PlayerTrain.Cars.Length); if (c >= 0) { bool cond; if (Command == "stoplefttick") { cond = CurrentRoute.Stations[s].OpenLeftDoors; } else if (Command == "stoprighttick") { cond = CurrentRoute.Stations[s].OpenRightDoors; } else { cond = !CurrentRoute.Stations[s].OpenLeftDoors & !CurrentRoute.Stations[s].OpenRightDoors; } if (TrainManager.PlayerTrain.StationState == TrainStopState.Pending & cond) { Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } double d = TrainManager.PlayerTrain.StationDistanceToStopPoint; double r; if (d > 0.0) { r = d / CurrentRoute.Stations[s].Stops[c].BackwardTolerance; } else { r = d / CurrentRoute.Stations[s].Stops[c].ForwardTolerance; } if (r < -1.0) { r = -1.0; } if (r > 1.0) { r = 1.0; } y -= r * (double)Element.Value1; } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } t = Element.Text; } break; case "clock": { int hours = (int)Math.Floor(Game.SecondsSinceMidnight); int seconds = hours % 60; hours /= 60; int minutes = hours % 60; hours /= 60; hours %= 24; t = hours.ToString(Culture).PadLeft(2, '0') + ":" + minutes.ToString(Culture).PadLeft(2, '0') + ":" + seconds.ToString(Culture).PadLeft(2, '0'); if (OptionClock) { Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } } break; case "gradient": if (OptionGradient == GradientDisplayMode.Percentage) { if (World.CameraTrackFollower.Pitch != 0) { double pc = World.CameraTrackFollower.Pitch; t = Math.Abs(pc).ToString("0.00", Culture) + "%" + (Math.Abs(pc) == pc ? " ↗" : " ↘"); } else { t = "Level"; } Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else if (OptionGradient == GradientDisplayMode.UnitOfChange) { if (World.CameraTrackFollower.Pitch != 0) { double gr = 1000 / World.CameraTrackFollower.Pitch; t = "1 in " + Math.Abs(gr).ToString("0", Culture) + (Math.Abs(gr) == gr ? " ↗" : " ↘"); } else { t = "Level"; } Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else if (OptionGradient == GradientDisplayMode.Permil) { if (World.CameraTrackFollower.Pitch != 0) { double pm = World.CameraTrackFollower.Pitch; t = Math.Abs(pm).ToString("0.00", Culture) + "‰" + (Math.Abs(pm) == pm ? " ↗" : " ↘"); } else { t = "Level"; } Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { if (World.CameraTrackFollower.Pitch != 0) { double gr = 1000 / World.CameraTrackFollower.Pitch; t = "1 in " + Math.Abs(gr).ToString("0", Culture) + (Math.Abs(gr) == gr ? " ↗" : " ↘"); } else { t = "Level"; } Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } break; case "speed": if (OptionSpeed == SpeedDisplayMode.Kmph) { double kmph = Math.Abs(TrainManager.PlayerTrain.CurrentSpeed) * 3.6; t = kmph.ToString("0.00", Culture) + " km/h"; Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else if (OptionSpeed == SpeedDisplayMode.Mph) { double mph = Math.Abs(TrainManager.PlayerTrain.CurrentSpeed) * 2.2369362920544; t = mph.ToString("0.00", Culture) + " mph"; Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { double mph = Math.Abs(TrainManager.PlayerTrain.CurrentSpeed) * 2.2369362920544; t = mph.ToString("0.00", Culture) + " mph"; Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } break; case "dist_next_station": int i; if (TrainManager.PlayerTrain.Station >= 0 && TrainManager.PlayerTrain.StationState != TrainStopState.Completed) { i = TrainManager.PlayerTrain.LastStation; } else { i = TrainManager.PlayerTrain.LastStation + 1; } if (i > CurrentRoute.Stations.Length - 1) { i = TrainManager.PlayerTrain.LastStation; } int n = CurrentRoute.Stations[i].GetStopIndex(TrainManager.PlayerTrain.NumberOfCars); double p0 = TrainManager.PlayerTrain.FrontCarTrackPosition(); double p1; if (CurrentRoute.Stations[i].Stops.Length > 0) { p1 = CurrentRoute.Stations[i].Stops[n].TrackPosition; } else { p1 = CurrentRoute.Stations[i].DefaultTrackPosition; } double m = p1 - p0; if (OptionDistanceToNextStation == DistanceToNextStationDisplayMode.Km) { if (CurrentRoute.Stations[i].PlayerStops()) { t = "Stop: "; if (Math.Abs(m) <= 10.0) { t += m.ToString("0.00", Culture) + " m"; } else { m /= 1000.0; t += m.ToString("0.000", Culture) + " km"; } } else { m /= 1000.0; t = "Pass: "******"0.000", Culture) + " km"; } Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else if (OptionDistanceToNextStation == DistanceToNextStationDisplayMode.Mile) { m /= 1609.34; if (CurrentRoute.Stations[i].PlayerStops()) { t = "Stop: "; } else { t = "Pass: "******"0.0000", Culture) + " miles"; Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { m /= 1609.34; if (CurrentRoute.Stations[i].PlayerStops()) { t = "Stop: "; } else { t = "Pass: "******"0.0000", Culture) + " miles"; Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } break; case "fps": int fps = (int)Math.Round(LibRender.Renderer.FrameRate); t = fps.ToString(Culture) + " fps"; if (OptionFrameRates) { Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } break; case "ai": t = "A.I."; if (TrainManager.PlayerTrain.AI != null) { Element.TransitionState -= speed * TimeElapsed; if (Element.TransitionState < 0.0) { Element.TransitionState = 0.0; } } else { Element.TransitionState += speed * TimeElapsed; if (Element.TransitionState > 1.0) { Element.TransitionState = 1.0; } } break; case "score": if (Interface.CurrentOptions.GameMode == GameMode.Arcade) { t = Game.CurrentScore.CurrentValue.ToString(Culture) + " / " + Game.CurrentScore.Maximum.ToString(Culture); if (Game.CurrentScore.CurrentValue < 0) { sc = MessageColor.Red; } else if (Game.CurrentScore.CurrentValue > 0) { sc = MessageColor.Green; } else { sc = MessageColor.Gray; } Element.TransitionState = 0.0; } else { Element.TransitionState = 1.0; t = ""; } break; default: t = Element.Text; break; } // transitions float alpha = 1.0f; if ((Element.Transition & HUD.Transition.Move) != 0) { double s = Element.TransitionState; x += Element.TransitionVector.X * s * s; y += Element.TransitionVector.Y * s * s; } if ((Element.Transition & HUD.Transition.Fade) != 0) { alpha = (float)(1.0 - Element.TransitionState); } else if (Element.Transition == HUD.Transition.None) { alpha = (float)(1.0 - Element.TransitionState); } // render if (alpha != 0.0f) { // background if (Element.Subject == "reverser") { w = Math.Max(w, TrainManager.PlayerTrain.MaxReverserWidth); //X-Pos doesn't need to be changed } if (Element.Subject == "power") { w = Math.Max(w, TrainManager.PlayerTrain.MaxPowerNotchWidth); if (TrainManager.PlayerTrain.MaxReverserWidth > 48) { x += (TrainManager.PlayerTrain.MaxReverserWidth - 48); } } if (Element.Subject == "brake") { w = Math.Max(w, TrainManager.PlayerTrain.MaxBrakeNotchWidth); if (TrainManager.PlayerTrain.MaxReverserWidth > 48) { x += (TrainManager.PlayerTrain.MaxReverserWidth - 48); } if (TrainManager.PlayerTrain.MaxPowerNotchWidth > 48) { x += (TrainManager.PlayerTrain.MaxPowerNotchWidth - 48); } } if (Element.Subject == "single") { w = Math.Max(Math.Max(w, TrainManager.PlayerTrain.MaxPowerNotchWidth), TrainManager.PlayerTrain.MaxBrakeNotchWidth); if (TrainManager.PlayerTrain.MaxReverserWidth > 48) { x += (TrainManager.PlayerTrain.MaxReverserWidth - 48); } } if (Element.CenterMiddle.BackgroundTexture != null) { if (Program.CurrentHost.LoadTexture(Element.CenterMiddle.BackgroundTexture, OpenGlTextureWrapMode.ClampClamp)) { Color128 c = Element.BackgroundColor.CreateBackColor(sc, alpha); GL.Color4(c.R, c.G, c.B, c.A); LibRender.Renderer.RenderOverlayTexture(Element.CenterMiddle.BackgroundTexture, x, y, x + w, y + h); } } { // text System.Drawing.Size size = Element.Font.MeasureString(t); float u = size.Width; float v = size.Height; double p = Math.Round(Element.TextAlignment.X < 0 ? x : Element.TextAlignment.X == 0 ? x + 0.5 * (w - u) : x + w - u); double q = Math.Round(Element.TextAlignment.Y < 0 ? y : Element.TextAlignment.Y == 0 ? y + 0.5 * (h - v) : y + h - v); p += Element.TextPosition.X; q += Element.TextPosition.Y; Color128 c = Element.TextColor.CreateTextColor(sc, alpha); LibRender.Renderer.DrawString(Element.Font, t, new System.Drawing.Point((int)p, (int)q), TextAlignment.TopLeft, c, Element.TextShadow); } // overlay if (Element.CenterMiddle.OverlayTexture != null) { if (Program.CurrentHost.LoadTexture(Element.CenterMiddle.OverlayTexture, OpenGlTextureWrapMode.ClampClamp)) { Color128 c = Element.OverlayColor.CreateBackColor(sc, alpha); GL.Color4(c.R, c.G, c.B, c.A); LibRender.Renderer.RenderOverlayTexture(Element.CenterMiddle.OverlayTexture, x, y, x + w, y + h); } } } }
// update internal static void Update(double TimeElapsed) { if (OpenAlContext != ContextHandle.Zero) { // listener double vx = World.CameraTrackFollower.WorldDirection.X * World.CameraSpeed; double vy = World.CameraTrackFollower.WorldDirection.Y * World.CameraSpeed; double vz = World.CameraTrackFollower.WorldDirection.Z * World.CameraSpeed; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { ListenerVelocity[0] = 0.0f; ListenerVelocity[1] = 0.0f; ListenerVelocity[2] = 0.0f; } else { ListenerVelocity[0] = (float)vx; ListenerVelocity[1] = (float)vy; ListenerVelocity[2] = (float)vz; } ListenerOrientation[0] = (float)World.AbsoluteCameraDirection.X; ListenerOrientation[1] = (float)World.AbsoluteCameraDirection.Y; ListenerOrientation[2] = (float)World.AbsoluteCameraDirection.Z; ListenerOrientation[3] = (float)-World.AbsoluteCameraUp.X; ListenerOrientation[4] = (float)-World.AbsoluteCameraUp.Y; ListenerOrientation[5] = (float)-World.AbsoluteCameraUp.Z; AL.Listener(ALListener3f.Position, ListenerPosition[0], ListenerPosition[1], ListenerPosition[2]); AL.Listener(ALListener3f.Velocity, ListenerVelocity[0], ListenerVelocity[1], ListenerVelocity[2]); AL.Listener(ALListenerfv.Orientation, ref ListenerOrientation); double cx = World.AbsoluteCameraPosition.X; double cy = World.AbsoluteCameraPosition.Y; double cz = World.AbsoluteCameraPosition.Z; if (Mute) { // mute for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null && !SoundSources[i].FinishedPlaying) { if (!SoundSources[i].Suppressed) { if (SoundSources[i].Looped) { if (SoundSources[i].OpenAlSourceIndex.Valid) { int j = SoundSources[i].OpenAlSourceIndex.Index; AL.SourceStop(j); AL.DeleteSources(1, ref j); } SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].Suppressed = true; } else { StopSound(i, false); } } else if (!SoundSources[i].Looped) { StopSound(i, false); } } } } else { // outer radius int n = Interface.CurrentOptions.SoundNumber - 3; if (SoundsActuallyPlaying >= n) { OuterRadiusSpeed -= OuterRadiusDeceleration * TimeElapsed; if (OuterRadiusSpeed < -1.0) { OuterRadiusSpeed = -1.0; } } else if (SoundsQueriedPlaying < n) { OuterRadiusSpeed += OuterRadiusAcceleration * TimeElapsed; if (OuterRadiusSpeed > 1.0) { OuterRadiusSpeed = 1.0; } } else { OuterRadiusSpeed -= (double)Math.Sign(OuterRadiusSpeed) * OuterRadiusDeceleration * TimeElapsed; if (OuterRadiusSpeed * OuterRadiusSpeed <= TimeElapsed * TimeElapsed) { OuterRadiusSpeed = 0.0; } } OuterRadiusFactor += OuterRadiusSpeed * TimeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; } // sources SoundsQueriedPlaying = 0; SoundsActuallyPlaying = 0; for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null && !SoundSources[i].FinishedPlaying) { double rx = SoundSources[i].Position.X; double ry = SoundSources[i].Position.Y; double rz = SoundSources[i].Position.Z; double px, py, pz; if (SoundSources[i].Train != null) { int c = SoundSources[i].CarIndex; double tx, ty, tz; TrainManager.CreateWorldCoordinates(SoundSources[i].Train, c, rx, ry, rz, out px, out py, out pz, out tx, out ty, out tz); px -= cx; py -= cy; pz -= cz; double sp = SoundSources[i].Train.Specs.CurrentAverageSpeed; if (World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) { SoundSources[i].OpenAlVelocity[0] = (float)(tx * sp); SoundSources[i].OpenAlVelocity[1] = (float)(ty * sp); SoundSources[i].OpenAlVelocity[2] = (float)(tz * sp); } else { SoundSources[i].OpenAlVelocity[0] = (float)(tx * sp - vx); SoundSources[i].OpenAlVelocity[1] = (float)(ty * sp - vy); SoundSources[i].OpenAlVelocity[2] = (float)(tz * sp - vz); } } else { px = rx - cx; py = ry - cy; pz = rz - cz; if (World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) { SoundSources[i].OpenAlVelocity[0] = 0.0f; SoundSources[i].OpenAlVelocity[1] = 0.0f; SoundSources[i].OpenAlVelocity[2] = 0.0f; } else { SoundSources[i].OpenAlVelocity[0] = (float)-vx; SoundSources[i].OpenAlVelocity[1] = (float)-vy; SoundSources[i].OpenAlVelocity[2] = (float)-vz; } } // play the sound only if within the outer radius double distanceSquared = px * px + py * py + pz * pz; double distance = Math.Sqrt(distanceSquared); double innerRadius = SoundSources[i].Radius; double outerRadius = OuterRadiusFactor * innerRadius; double outerRadiusSquared = outerRadius * outerRadius; if (distanceSquared < outerRadiusSquared) { // sound is in range double gain; double innerRadiusSquared = innerRadius * innerRadius; const double rollOffFactor = 0.9; if (distanceSquared < innerRadiusSquared) { gain = 1.0 - (1.0 - rollOffFactor) * distanceSquared / innerRadiusSquared; } else { double value = distance / outerRadius; gain = innerRadius * rollOffFactor * (1.0 - value * value * value) / distance; } SoundsQueriedPlaying++; SoundsActuallyPlaying++; bool startPlaying = false; // play sound if currently suppressed if (SoundSources[i].Suppressed) { if (SoundSources[i].SoundBufferIndex >= 0) { UseSoundBuffer(SoundSources[i].SoundBufferIndex); if (SoundBuffers[SoundSources[i].SoundBufferIndex].OpenAlBufferIndex.Valid) { int j; AL.GetError(); AL.GenSources(1, out j); ALError err = AL.GetError(); if (err == ALError.NoError) { SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(j, true); AL.Source(j, ALSourcei.Buffer, SoundBuffers[SoundSources[i].SoundBufferIndex].OpenAlBufferIndex.Index); SoundSources[i].Suppressed = false; startPlaying = true; } else { continue; } } else { StopSound(i, false); continue; } } else { StopSound(i, false); continue; } } // play or stop sound if (startPlaying || IsPlaying(i)) { SoundSources[i].OpenAlPosition[0] = (float)px; SoundSources[i].OpenAlPosition[1] = (float)py; SoundSources[i].OpenAlPosition[2] = (float)pz; if (!SoundSources[i].OpenAlSourceIndex.Valid) { throw new InvalidOperationException("A bug in the sound manager. (9431)"); } int j = SoundSources[i].OpenAlSourceIndex.Index; AL.Source(j, ALSource3f.Position, SoundSources[i].OpenAlPosition[0], SoundSources[i].OpenAlPosition[1], SoundSources[i].OpenAlPosition[2]); AL.Source(j, ALSource3f.Velocity, SoundSources[i].OpenAlVelocity[0], SoundSources[i].OpenAlVelocity[1], SoundSources[i].OpenAlVelocity[2]); AL.Source(j, ALSourcef.Pitch, SoundSources[i].Pitch); float g = SoundSources[i].Gain * SoundSources[i].Gain * (float)gain; if (g > 1.0f) { g = 1.0f; } AL.Source(j, ALSourcef.Gain, g); } else { StopSound(i, false); continue; } // update position and velocity of sound if (startPlaying) { if (!SoundSources[i].OpenAlSourceIndex.Valid) { throw new InvalidOperationException("A bug in the sound manager. (7625)"); } int j = SoundSources[i].OpenAlSourceIndex.Index; AL.Source(j, ALSourceb.Looping, SoundSources[i].Looped); AL.Source(j, ALSourcef.ReferenceDistance, SoundBuffers[SoundSources[i].SoundBufferIndex].Radius); AL.SourcePlay(j); } } else { // sound is not in range if (!SoundSources[i].Suppressed) { if (SoundSources[i].Looped) { if (SoundSources[i].OpenAlSourceIndex.Valid) { int j = SoundSources[i].OpenAlSourceIndex.Index; AL.SourceStop(j); AL.DeleteSources(1, ref j); } SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].Suppressed = true; } else { StopSound(i, false); } } else if (!SoundSources[i].Looped) { StopSound(i, false); } } } } } // infrequent updates InternalTimer += TimeElapsed; if (InternalTimer > 1.0) { InternalTimer = 0.0; double Elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double AirTemperature = Game.GetAirTemperature(Elevation); double AirPressure = Game.GetAirPressure(Elevation, AirTemperature); double SpeedOfSound = Game.GetSpeedOfSound(AirPressure, AirTemperature); AL.SpeedOfSound((float)SpeedOfSound); } } }
//This renders the frame protected override void OnRenderFrame(FrameEventArgs e) { if (!firstFrame) { //If the load is not complete, then we shouldn't be running the mainloop return; } double TimeElapsed = RenderTimeElapsed; double RealTimeElapsed = RenderRealTimeElapsed; //Next, check if we're in paused/ in a menu if (Game.CurrentInterface != Game.InterfaceType.Normal) { MainLoop.UpdateControlRepeats(0.0); MainLoop.ProcessKeyboard(); MainLoop.ProcessControls(0.0); if (Game.CurrentInterface == Game.InterfaceType.Pause) { System.Threading.Thread.Sleep(10); } //Renderer.UpdateLighting(); Renderer.RenderScene(TimeElapsed); Program.currentGameWindow.SwapBuffers(); if (MainLoop.Quit) { Close(); } //If the menu state has not changed, don't update the rendered simulation return; } //Use the OpenTK framerate as this is much more accurate //Also avoids running a calculation if (TotalTimeElapsedForInfo >= 0.2) { Game.InfoFrameRate = RenderFrequency; TotalTimeElapsedForInfo = 0.0; } if (Game.PreviousInterface != Game.InterfaceType.Normal) { ObjectManager.UpdateAnimatedWorldObjects(0.0, false); Game.PreviousInterface = Game.InterfaceType.Normal; } else { ObjectManager.UpdateAnimatedWorldObjects(TimeElapsed, false); } //We need to update the camera position in the render sequence //Not doing this means that the camera doesn't move // update in one piece if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { //Update the in-car camera based upon the current driver car (Cabview or passenger view) TrainManager.PlayerTrain.Cars[World.CameraCar].UpdateCamera(); } else if (World.CameraMode == World.CameraViewMode.Exterior) { //Update the camera position based upon the relative car position TrainManager.PlayerTrain.Cars[World.CameraCar].UpdateCamera(); } if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { World.UpdateDriverBody(TimeElapsed); } //Check if we are running at an accelerated time factor- //Camera motion speed should be the same whatever the game speed is if (TimeFactor != 1) { World.UpdateAbsoluteCamera(TimeElapsed / TimeFactor); } else { World.UpdateAbsoluteCamera(TimeElapsed); } TrainManager.UpdateTrainObjects(TimeElapsed, false); if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { ObjectManager.UpdateVisibility(World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z); int d = TrainManager.PlayerTrain.DriverCar; World.CameraSpeed = TrainManager.PlayerTrain.Cars[d].Specs.CurrentSpeed; } else { World.CameraSpeed = 0.0; } World.CameraAlignmentDirection = new World.CameraAlignment(); if (MainLoop.Quit) { Program.currentGameWindow.Exit(); } Renderer.UpdateLighting(); Renderer.RenderScene(TimeElapsed); Sounds.Update(TimeElapsed, Interface.CurrentOptions.SoundModel); Program.currentGameWindow.SwapBuffers(); Game.UpdateBlackBox(); // pause/menu // limit framerate if (MainLoop.LimitFramerate) { System.Threading.Thread.Sleep(10); } MainLoop.UpdateControlRepeats(RealTimeElapsed); MainLoop.ProcessKeyboard(); World.UpdateMouseGrab(TimeElapsed); MainLoop.ProcessControls(TimeElapsed); for (int i = 0; i < JoystickManager.AttachedJoysticks.Length; i++) { var railDriver = JoystickManager.AttachedJoysticks[i] as JoystickManager.Raildriver; if (railDriver != null) { if (Interface.CurrentOptions.RailDriverMPH) { railDriver.SetDisplay((int)(TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Specs .CurrentPerceivedSpeed * 2.23694)); } else { railDriver.SetDisplay((int)(TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].Specs .CurrentPerceivedSpeed * 3.6)); } } } RenderRealTimeElapsed = 0.0; RenderTimeElapsed = 0.0; #if DEBUG MainLoop.CheckForOpenGlError("MainLoop"); #endif if (Interface.CurrentOptions.UnloadUnusedTextures) { Renderer.UnloadUnusedTextures(TimeElapsed); Renderer.LastBoundTexture = null; } // finish try { Interface.SaveLogs(); } catch { } }
// load bve 2 sound private static void LoadBve2Sound(string TrainPath, TrainManager.Train train) { // set sound positions and radii Vector3 front = new Vector3(0.0, 0.0, 0.5 * train.Cars[train.DriverCar].Length); Vector3 center = new Vector3(0.0, 0.0, 0.0); Vector3 left = new Vector3(-1.3, 0.0, 0.0); Vector3 right = new Vector3(1.3, 0.0, 0.0); Vector3 cab = new Vector3(-train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ - 0.5); Vector3 panel = new Vector3(train.Cars[train.DriverCar].DriverX, train.Cars[train.DriverCar].DriverY, train.Cars[train.DriverCar].DriverZ + 1.0); const double large = 30.0; const double medium = 10.0; const double small = 5.0; const double tiny = 2.0; LoadNoSound(train); // load sounds for driver's car train.Cars[train.DriverCar].Sounds.Adjust = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Adjust.wav"), panel, tiny); train.Cars[train.DriverCar].Sounds.Brake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Brake.wav"), center, small); train.Cars[train.DriverCar].Sounds.Halt = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Halt.wav"), cab, tiny); train.Cars[train.DriverCar].Sounds.Horns[0].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon0.wav"), front, large); train.Cars[train.DriverCar].Sounds.Horns[0].Loop = false; if (train.Cars[train.DriverCar].Sounds.Horns[0].Sound.Buffer == null) { train.Cars[train.DriverCar].Sounds.Horns[0].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon.wav"), front, large); } train.Cars[train.DriverCar].Sounds.Horns[1].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon1.wav"), front, large); train.Cars[train.DriverCar].Sounds.Horns[1].Loop = false; train.Cars[train.DriverCar].Sounds.Horns[2].Sound = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Klaxon2.wav"), front, medium); train.Cars[train.DriverCar].Sounds.Horns[2].Loop = true; train.Cars[train.DriverCar].Sounds.PilotLampOn = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Leave.wav"), cab, tiny); train.Cars[train.DriverCar].Sounds.PilotLampOff = TrainManager.CarSound.Empty; // load sounds for all cars for (int i = 0; i < train.Cars.Length; i++) { Vector3 frontaxle = new Vector3(0.0, 0.0, train.Cars[i].FrontAxlePosition); Vector3 rearaxle = new Vector3(0.0, 0.0, train.Cars[i].RearAxlePosition); train.Cars[i].Sounds.Air = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Air.wav"), center, small); train.Cars[i].Sounds.AirHigh = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "AirHigh.wav"), center, small); train.Cars[i].Sounds.AirZero = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "AirZero.wav"), center, small); if (train.Cars[i].Specs.AirBrake.Type == TrainManager.AirBrakeType.Main) { train.Cars[i].Sounds.CpEnd = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "CpEnd.wav"), center, medium); train.Cars[i].Sounds.CpLoop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "CpLoop.wav"), center, medium); train.Cars[i].Sounds.CpStart = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "CpStart.wav"), center, medium); } train.Cars[i].Sounds.BreakerResume = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResumeOrInterrupt = TrainManager.CarSound.Empty; train.Cars[i].Sounds.BreakerResumed = false; train.Cars[i].Sounds.DoorCloseL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorClsL.wav"), left, small); train.Cars[i].Sounds.DoorCloseR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorClsR.wav"), right, small); if (train.Cars[i].Sounds.DoorCloseL.Buffer == null) { train.Cars[i].Sounds.DoorCloseL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorCls.wav"), left, small); } if (train.Cars[i].Sounds.DoorCloseR.Buffer == null) { train.Cars[i].Sounds.DoorCloseR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorCls.wav"), right, small); } train.Cars[i].Sounds.DoorOpenL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpnL.wav"), left, small); train.Cars[i].Sounds.DoorOpenR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpnR.wav"), right, small); if (train.Cars[i].Sounds.DoorOpenL.Buffer == null) { train.Cars[i].Sounds.DoorOpenL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpn.wav"), left, small); } if (train.Cars[i].Sounds.DoorOpenR.Buffer == null) { train.Cars[i].Sounds.DoorOpenR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "DoorOpn.wav"), right, small); } train.Cars[i].Sounds.EmrBrake = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "EmrBrake.wav"), center, medium); train.Cars[i].Sounds.Flange = TryLoadSoundArray(TrainPath, "Flange", ".wav", center, medium); train.Cars[i].Sounds.FlangeVolume = new double[train.Cars[i].Sounds.Flange.Length]; train.Cars[i].Sounds.Loop = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Loop.wav"), center, medium); train.Cars[i].Sounds.PointFrontAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Point.wav"), frontaxle, small); train.Cars[i].Sounds.PointRearAxle = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Point.wav"), rearaxle, small); train.Cars[i].Sounds.Rub = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Rub.wav"), center, medium); train.Cars[i].Sounds.Run = TryLoadSoundArray(TrainPath, "Run", ".wav", center, medium); train.Cars[i].Sounds.RunVolume = new double[train.Cars[i].Sounds.Run.Length]; train.Cars[i].Sounds.SpringL = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "SpringL.wav"), left, small); train.Cars[i].Sounds.SpringR = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "SpringR.wav"), right, small); // motor sound if (train.Cars[i].Specs.IsMotorCar) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; train.Cars[i].Sounds.Motor.Position = center; for (int j = 0; j < train.Cars[i].Sounds.Motor.Tables.Length; j++) { for (int k = 0; k < train.Cars[i].Sounds.Motor.Tables[j].Entries.Length; k++) { int idx = train.Cars[i].Sounds.Motor.Tables[j].Entries[k].SoundIndex; if (idx >= 0) { TrainManager.CarSound snd = TryLoadSound(OpenBveApi.Path.CombineFile(TrainPath, "Motor" + idx.ToString(Culture) + ".wav"), center, medium); train.Cars[i].Sounds.Motor.Tables[j].Entries[k].Buffer = snd.Buffer; } } } } } }
internal static void UpdateAnimatedObject(ref AnimatedObject Object, bool IsPartOfTrain, TrainManager.Train Train, int CarIndex, int SectionIndex, double TrackPosition, Vector3 Position, Vector3 Direction, Vector3 Up, Vector3 Side, bool Overlay, bool UpdateFunctions, bool Show, double TimeElapsed) { int s = Object.CurrentState; int i = Object.ObjectIndex; // state change if (Object.StateFunction != null & UpdateFunctions) { double sd = Object.StateFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); int si = (int)Math.Round(sd); int sn = Object.States.Length; if (si < 0 | si >= sn) si = -1; if (s != si) { InitializeAnimatedObject(ref Object, si, Overlay, Show); s = si; } } if (s == -1) return; // translation if (Object.TranslateXFunction != null) { double x; if (UpdateFunctions) { x = Object.TranslateXFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { x = Object.TranslateXFunction.LastResult; } double rx = Object.TranslateXDirection.X, ry = Object.TranslateXDirection.Y, rz = Object.TranslateXDirection.Z; World.Rotate(ref rx, ref ry, ref rz, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); Position.X += x * rx; Position.Y += x * ry; Position.Z += x * rz; } if (Object.TranslateYFunction != null) { double y; if (UpdateFunctions) { y = Object.TranslateYFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { y = Object.TranslateYFunction.LastResult; } double rx = Object.TranslateYDirection.X, ry = Object.TranslateYDirection.Y, rz = Object.TranslateYDirection.Z; World.Rotate(ref rx, ref ry, ref rz, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); Position.X += y * rx; Position.Y += y * ry; Position.Z += y * rz; } if (Object.TranslateZFunction != null) { double z; if (UpdateFunctions) { z = Object.TranslateZFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { z = Object.TranslateZFunction.LastResult; } double rx = Object.TranslateZDirection.X, ry = Object.TranslateZDirection.Y, rz = Object.TranslateZDirection.Z; World.Rotate(ref rx, ref ry, ref rz, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); Position.X += z * rx; Position.Y += z * ry; Position.Z += z * rz; } // rotation bool rotateX = Object.RotateXFunction != null; bool rotateY = Object.RotateYFunction != null; bool rotateZ = Object.RotateZFunction != null; double cosX, sinX; if (rotateX) { double a; if (UpdateFunctions) { a = Object.RotateXFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { a = Object.RotateXFunction.LastResult; } ObjectManager.UpdateDamping(ref Object.RotateXDamping, TimeElapsed, ref a); cosX = Math.Cos(a); sinX = Math.Sin(a); } else { cosX = 0.0; sinX = 0.0; } double cosY, sinY; if (rotateY) { double a; if (UpdateFunctions) { a = Object.RotateYFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { a = Object.RotateYFunction.LastResult; } ObjectManager.UpdateDamping(ref Object.RotateYDamping, TimeElapsed, ref a); cosY = Math.Cos(a); sinY = Math.Sin(a); } else { cosY = 0.0; sinY = 0.0; } double cosZ, sinZ; if (rotateZ) { double a; if (UpdateFunctions) { a = Object.RotateZFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { a = Object.RotateZFunction.LastResult; } ObjectManager.UpdateDamping(ref Object.RotateZDamping, TimeElapsed, ref a); cosZ = Math.Cos(a); sinZ = Math.Sin(a); } else { cosZ = 0.0; sinZ = 0.0; } // texture shift bool shiftx = Object.TextureShiftXFunction != null; bool shifty = Object.TextureShiftYFunction != null; if ((shiftx | shifty) & UpdateFunctions) { for (int k = 0; k < ObjectManager.Objects[i].Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates = Object.States[s].Object.Mesh.Vertices[k].TextureCoordinates; } if (shiftx) { double x = Object.TextureShiftXFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); x -= Math.Floor(x); for (int k = 0; k < ObjectManager.Objects[i].Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.X += (float)(x * Object.TextureShiftXDirection.X); ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.Y += (float)(x * Object.TextureShiftXDirection.Y); } } if (shifty) { double y = Object.TextureShiftYFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); y -= Math.Floor(y); for (int k = 0; k < ObjectManager.Objects[i].Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.X += (float)(y * Object.TextureShiftYDirection.X); ObjectManager.Objects[i].Mesh.Vertices[k].TextureCoordinates.Y += (float)(y * Object.TextureShiftYDirection.Y); } } } // led bool led = Object.LEDFunction != null; double ledangle; if (led) { if (UpdateFunctions) { double lastangle = Object.LEDFunction.LastResult; ledangle = Object.LEDFunction.Perform(Train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed); } else { ledangle = Object.LEDFunction.LastResult; } } else { ledangle = 0.0; } // null object if (Object.States[s].Object == null) { return; } // initialize vertices for (int k = 0; k < Object.States[s].Object.Mesh.Vertices.Length; k++) { ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates = Object.States[s].Object.Mesh.Vertices[k].Coordinates; } // led if (led) { /* * Edges: Vertices: * 0 - bottom 0 - bottom-left * 1 - left 1 - top-left * 2 - top 2 - top-right * 3 - right 3 - bottom-right * 4 - center * */ int v = 1; if (Object.LEDClockwiseWinding) { /* winding is clockwise*/ if (ledangle < Object.LEDInitialAngle) { ledangle = Object.LEDInitialAngle; } if (ledangle < Object.LEDLastAngle) { double currentEdgeFloat = Math.Floor(0.636619772367582 * (ledangle + 0.785398163397449)); int currentEdge = ((int)currentEdgeFloat % 4 + 4) % 4; double lastEdgeFloat = Math.Floor(0.636619772367582 * (Object.LEDLastAngle + 0.785398163397449)); int lastEdge = ((int)lastEdgeFloat % 4 + 4) % 4; if (lastEdge < currentEdge | lastEdge == currentEdge & Math.Abs(currentEdgeFloat - lastEdgeFloat) > 2.0) { lastEdge += 4; } if (currentEdge == lastEdge) { /* current angle to last angle */ { double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(cx, cy, cz); v++; } { double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(lx, ly, lz); v++; } } else { { /* current angle to square vertex */ double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = new Vector3(cx, cy, cz); Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[currentEdge]; v += 2; } for (int j = currentEdge + 1; j < lastEdge; j++) { /* square-vertex to square-vertex */ Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(j + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[j % 4]; v += 2; } { /* square vertex to last angle */ double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge % 4].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge % 4].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge % 4].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(lastEdge + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = new Vector3(lx, ly, lz); v += 2; } } } } else { /* winding is counter-clockwise*/ if (ledangle > Object.LEDInitialAngle) { ledangle = Object.LEDInitialAngle; } if (ledangle > Object.LEDLastAngle) { double currentEdgeFloat = Math.Floor(0.636619772367582 * (ledangle + 0.785398163397449)); int currentEdge = ((int)currentEdgeFloat % 4 + 4) % 4; double lastEdgeFloat = Math.Floor(0.636619772367582 * (Object.LEDLastAngle + 0.785398163397449)); int lastEdge = ((int)lastEdgeFloat % 4 + 4) % 4; if (currentEdge < lastEdge | lastEdge == currentEdge & Math.Abs(currentEdgeFloat - lastEdgeFloat) > 2.0) { currentEdge += 4; } if (currentEdge == lastEdge) { /* current angle to last angle */ { double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(lx, ly, lz); v++; } { double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = t - Math.Floor(t); t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge].Z; Object.States[s].Object.Mesh.Vertices[v].Coordinates = new Vector3(cx, cy, cz); v++; } } else { { /* current angle to square vertex */ double t = 0.5 + (0.636619772367582 * ledangle) - currentEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double cx = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].X + t * Object.LEDVectors[currentEdge % 4].X; double cy = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Y + t * Object.LEDVectors[currentEdge % 4].Y; double cz = (1.0 - t) * Object.LEDVectors[(currentEdge + 3) % 4].Z + t * Object.LEDVectors[currentEdge % 4].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(currentEdge + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = new Vector3(cx, cy, cz); v += 2; } for (int j = currentEdge - 1; j > lastEdge; j--) { /* square-vertex to square-vertex */ Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = Object.LEDVectors[(j + 3) % 4]; Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[j % 4]; v += 2; } { /* square vertex to last angle */ double t = 0.5 + (0.636619772367582 * Object.LEDLastAngle) - lastEdgeFloat; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } t = 0.5 * (1.0 - Math.Tan(0.25 * (Math.PI - 2.0 * Math.PI * t))); double lx = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].X + t * Object.LEDVectors[lastEdge].X; double ly = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Y + t * Object.LEDVectors[lastEdge].Y; double lz = (1.0 - t) * Object.LEDVectors[(lastEdge + 3) % 4].Z + t * Object.LEDVectors[lastEdge].Z; Object.States[s].Object.Mesh.Vertices[v + 0].Coordinates = new Vector3(lx, ly, lz); Object.States[s].Object.Mesh.Vertices[v + 1].Coordinates = Object.LEDVectors[lastEdge % 4]; v += 2; } } } } for (int j = v; v < 11; v++) { Object.States[s].Object.Mesh.Vertices[j].Coordinates = Object.LEDVectors[4]; } } // update vertices for (int k = 0; k < Object.States[s].Object.Mesh.Vertices.Length; k++) { // rotate if (rotateX) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Object.RotateXDirection.X, Object.RotateXDirection.Y, Object.RotateXDirection.Z, cosX, sinX); } if (rotateY) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Object.RotateYDirection.X, Object.RotateYDirection.Y, Object.RotateYDirection.Z, cosY, sinY); } if (rotateZ) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Object.RotateZDirection.X, Object.RotateZDirection.Y, Object.RotateZDirection.Z, cosZ, sinZ); } // translate if (Overlay & World.CameraRestriction != World.CameraRestrictionMode.NotAvailable) { ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += Object.States[s].Position.X - Position.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += Object.States[s].Position.Y - Position.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += Object.States[s].Position.Z - Position.Z; World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, World.AbsoluteCameraDirection.X, World.AbsoluteCameraDirection.Y, World.AbsoluteCameraDirection.Z, World.AbsoluteCameraUp.X, World.AbsoluteCameraUp.Y, World.AbsoluteCameraUp.Z, World.AbsoluteCameraSide.X, World.AbsoluteCameraSide.Y, World.AbsoluteCameraSide.Z); double dx = -Math.Tan(World.CameraCurrentAlignment.Yaw) - World.CameraCurrentAlignment.Position.X; double dy = -Math.Tan(World.CameraCurrentAlignment.Pitch) - World.CameraCurrentAlignment.Position.Y; double dz = -World.CameraCurrentAlignment.Position.Z; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += World.AbsoluteCameraPosition.X + dx * World.AbsoluteCameraSide.X + dy * World.AbsoluteCameraUp.X + dz * World.AbsoluteCameraDirection.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += World.AbsoluteCameraPosition.Y + dx * World.AbsoluteCameraSide.Y + dy * World.AbsoluteCameraUp.Y + dz * World.AbsoluteCameraDirection.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += World.AbsoluteCameraPosition.Z + dx * World.AbsoluteCameraSide.Z + dy * World.AbsoluteCameraUp.Z + dz * World.AbsoluteCameraDirection.Z; } else { ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += Object.States[s].Position.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += Object.States[s].Position.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += Object.States[s].Position.Z; World.Rotate(ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y, ref ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.X += Position.X; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Y += Position.Y; ObjectManager.Objects[i].Mesh.Vertices[k].Coordinates.Z += Position.Z; } } // update normals for (int k = 0; k < Object.States[s].Object.Mesh.Faces.Length; k++) { for (int h = 0; h < Object.States[s].Object.Mesh.Faces[k].Vertices.Length; h++) { ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal = Object.States[s].Object.Mesh.Faces[k].Vertices[h].Normal; } for (int h = 0; h < Object.States[s].Object.Mesh.Faces[k].Vertices.Length; h++) { if (!Object.States[s].Object.Mesh.Faces[k].Vertices[h].Normal.IsZero()) { if (rotateX) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Object.RotateXDirection.X, Object.RotateXDirection.Y, Object.RotateXDirection.Z, cosX, sinX); } if (rotateY) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Object.RotateYDirection.X, Object.RotateYDirection.Y, Object.RotateYDirection.Z, cosY, sinY); } if (rotateZ) { World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Object.RotateZDirection.X, Object.RotateZDirection.Y, Object.RotateZDirection.Z, cosZ, sinZ); } World.Rotate(ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.X, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Y, ref ObjectManager.Objects[i].Mesh.Faces[k].Vertices[h].Normal.Z, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); } } // visibility changed if (Show) { if (Overlay) { Renderer.ShowObject(i, Renderer.ObjectType.Overlay); } else { Renderer.ShowObject(i, Renderer.ObjectType.Dynamic); } } else { Renderer.HideObject(i); } } }
// ================================ // parse panel config internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated"); if (System.IO.File.Exists(File)) { ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding, ObjectManager.ObjectLoadMode.DontAllowUnloadOfTextures); for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } Train.Cars[Train.DriverCar].CarSections[0].Elements = a.Objects; World.CameraRestriction = World.CameraRestrictionMode.NotAvailable; } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); if (System.IO.File.Exists(File)) { Panel2CfgParser.ParsePanel2Config(TrainPath, Encoding, Train); World.CameraRestriction = World.CameraRestrictionMode.On; } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); if (System.IO.File.Exists(File)) { PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train); World.CameraRestriction = World.CameraRestrictionMode.On; } else { World.CameraRestriction = World.CameraRestrictionMode.NotAvailable; } } } }
public override void Trigger(double TimeElapsed) { if (Program.CurrentRoute.SecondsSinceMidnight == TimeLastProcessed) { return; } // Initialize if (TimeLastProcessed == 0.0) { SetupTravelData(Program.CurrentRoute.SecondsSinceMidnight); CheckTravelData(Program.CurrentRoute.SecondsSinceMidnight); CurrentPosition = Data[0].Position; } TimeLastProcessed = Program.CurrentRoute.SecondsSinceMidnight; // Dispose the train if it is past the leave time of the train if (LeaveTime > AppearanceTime && Program.CurrentRoute.SecondsSinceMidnight >= LeaveTime) { Train.Dispose(); return; } // Calculate the position where the train is at the present time. double NewMileage; double NewPosition; TravelDirection NewDirection; bool OpenLeftDoors; bool OpenRightDoors; GetNewState(Program.CurrentRoute.SecondsSinceMidnight, out NewMileage, out NewPosition, out NewDirection, out OpenLeftDoors, out OpenRightDoors); // Calculate the travel distance of the train. double DeltaPosition = NewPosition - CurrentPosition; // Set the state quantity of the train. if (DeltaPosition < 0) { Train.Handles.Reverser.Driver = TrainManager.ReverserPosition.Reverse; } else if (DeltaPosition > 0) { Train.Handles.Reverser.Driver = TrainManager.ReverserPosition.Forwards; } Train.Handles.Reverser.Actual = Train.Handles.Reverser.Driver; TrainManager.OpenTrainDoors(Train, OpenLeftDoors, OpenRightDoors); TrainManager.CloseTrainDoors(Train, !OpenLeftDoors, !OpenRightDoors); if (TimeElapsed != 0.0) { Train.CurrentSpeed = DeltaPosition / TimeElapsed; Train.Specs.CurrentAverageAcceleration = Math.Sign(Train.CurrentSpeed) * Train.CurrentSpeed / TimeElapsed; } else { Train.CurrentSpeed = 0.0; Train.Specs.CurrentAverageAcceleration = 0.0; } foreach (var Car in Train.Cars) { SetRailIndex(NewMileage, NewDirection, Car.FrontAxle.Follower); SetRailIndex(NewMileage, NewDirection, Car.RearAxle.Follower); SetRailIndex(NewMileage, NewDirection, Car.FrontBogie.FrontAxle.Follower); SetRailIndex(NewMileage, NewDirection, Car.FrontBogie.RearAxle.Follower); SetRailIndex(NewMileage, NewDirection, Car.RearBogie.FrontAxle.Follower); SetRailIndex(NewMileage, NewDirection, Car.RearBogie.RearAxle.Follower); Car.Move(DeltaPosition); Car.CurrentSpeed = Train.CurrentSpeed; Car.Specs.CurrentPerceivedSpeed = Train.CurrentSpeed; Car.Specs.CurrentAcceleration = Train.Specs.CurrentAverageAcceleration; Car.Specs.CurrentAccelerationOutput = Train.Specs.CurrentAverageAcceleration; } CurrentPosition = NewPosition; }
/// <summary>Plays a sound.</summary> /// <param name="buffer">The sound buffer.</param> /// <param name="pitch">The pitch change factor.</param> /// <param name="volume">The volume change factor.</param> /// <param name="position">The position. If a train and car are specified, the position is relative to the car, otherwise absolute.</param> /// <param name="train">The train the sound is attached to, or a null reference.</param> /// <param name="car">The car in the train the sound is attached to.</param> /// <param name="looped">Whether to play the sound in a loop.</param> /// <returns>The sound source.</returns> internal static SoundSource PlaySound(SoundBuffer buffer, double pitch, double volume, OpenBveApi.Math.Vector3 position, TrainManager.Train train, int car, bool looped) { if (Sources.Length == SourceCount) { Array.Resize<SoundSource>(ref Sources, Sources.Length << 1); } Sources[SourceCount] = new SoundSource(buffer, buffer.Radius, pitch, volume, position, train, car, looped); SourceCount++; return Sources[SourceCount - 1]; }
// // PROCESS MENU COMMAND // /// <summary>Processes a user command for the current menu</summary> /// <param name="cmd">The command to apply to the current menu</param> /// <param name="timeElapsed">The time elapsed since previous frame</param> internal void ProcessCommand(Translations.Command cmd, double timeElapsed) { if (CurrMenu < 0) { return; } // MenuBack is managed independently from single menu data if (cmd == Translations.Command.MenuBack) { PopMenu(); return; } SingleMenu menu = Menus[CurrMenu]; if (menu.Selection == SelectionNone) // if menu has no selection, do nothing { return; } switch (cmd) { case Translations.Command.MenuUp: // UP if (menu.Selection > 0 && !(menu.Items[menu.Selection - 1] is MenuCaption)) { menu.Selection--; PositionMenu(); } break; case Translations.Command.MenuDown: // DOWN if (menu.Selection < menu.Items.Length - 1) { menu.Selection++; PositionMenu(); } break; // case Translations.Command.MenuBack: // ESC: managed above // break; case Translations.Command.MenuEnter: // ENTER if (menu.Items[menu.Selection] is MenuCommand) { MenuCommand menuItem = (MenuCommand)menu.Items[menu.Selection]; switch (menuItem.Tag) { // menu management commands case MenuTag.MenuBack: // BACK TO PREVIOUS MENU Menu.instance.PopMenu(); break; case MenuTag.MenuJumpToStation: // TO STATIONS MENU Menu.instance.PushMenu(MenuType.JumpToStation); break; case MenuTag.MenuExitToMainMenu: // TO EXIT MENU Menu.instance.PushMenu(MenuType.ExitToMainMenu); break; case MenuTag.MenuQuit: // TO QUIT MENU Menu.instance.PushMenu(MenuType.Quit); break; case MenuTag.MenuControls: // TO CONTROLS MENU Menu.instance.PushMenu(MenuType.Controls); break; case MenuTag.BackToSim: // OUT OF MENU BACK TO SIMULATION Reset(); Game.PreviousInterface = Game.InterfaceType.Menu; Game.CurrentInterface = Game.InterfaceType.Normal; break; // simulation commands case MenuTag.JumpToStation: // JUMP TO STATION Reset(); TrainManager.JumpTrain(TrainManager.PlayerTrain, menuItem.Data); TrainManager.JumpTFO(); break; case MenuTag.ExitToMainMenu: // BACK TO MAIN MENU Reset(); Program.RestartArguments = Interface.CurrentOptions.GameMode == GameMode.Arcade ? "/review" : ""; MainLoop.Quit = MainLoop.QuitMode.ExitToMenu; break; case MenuTag.Control: // CONTROL CUSTOMIZATION PushMenu(MenuType.Control, ((MenuCommand)menu.Items[menu.Selection]).Data); isCustomisingControl = true; CustomControlIdx = ((MenuCommand)menu.Items[menu.Selection]).Data; break; case MenuTag.Quit: // QUIT PROGRAMME Reset(); MainLoop.Quit = MainLoop.QuitMode.QuitProgram; break; } } break; case Translations.Command.MiscFullscreen: // fullscreen Screen.ToggleFullscreen(); break; case Translations.Command.MiscMute: // mute Program.Sounds.GlobalMute = !Program.Sounds.GlobalMute; Program.Sounds.Update(timeElapsed, Interface.CurrentOptions.SoundModel); break; } }
/// <summary>Updates the sound component. Should be called every frame.</summary> /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> private static void UpdateInverseModel(double timeElapsed) { /* * Set up the listener. * */ OpenBveApi.Math.Vector3D listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3D listenerVelocity; if (World.CameraMode == World.CameraViewMode.Interior || World.CameraMode == World.CameraViewMode.InteriorLookAhead || World.CameraMode == World.CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; OpenBveApi.Math.Vector3D diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; if (diff.IsNullVector()) { listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3D.Forward; } else { listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3D.Normalize(diff); } } else { listenerVelocity = OpenBveApi.Math.Vector3D.Null; } float[] vectors = { (float)listenerOrientation.Z.X, (float)listenerOrientation.Z.Y, (float)listenerOrientation.Z.Z, -(float)listenerOrientation.Y.X, -(float)listenerOrientation.Y.Y, -(float)listenerOrientation.Y.Z }; AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); AL.Listener(ALListenerfv.Orientation, ref vectors); /* * Set up the atmospheric attributes. * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); // double airDensity = Game.GetAirDensity(airPressure, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { AL.SpeedOfSound((float)speedOfSound); } catch { } /* * Collect all sounds that are to be played * and ensure that all others are stopped. * */ List <SoundSourceAttenuation> toBePlayed = new List <SoundSourceAttenuation>(); for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * */ if (Sources[i].State == SoundSourceState.Playing) { int state; AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out state); if (state != (int)ALSourceState.Initial && state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; continue; } } /* * Calculate the gain, then add the sound * to the list of sounds to be played. * */ OpenBveApi.Math.Vector3D position; if (Sources[i].Train != null) { OpenBveApi.Math.Vector3D direction; TrainManager.CreateWorldCoordinates(Sources[i].Train, Sources[i].Car, Sources[i].Position.X, Sources[i].Position.Y, Sources[i].Position.Z, out position.X, out position.Y, out position.Z, out direction.X, out direction.Y, out direction.Z); } else { position = Sources[i].Position; } OpenBveApi.Math.Vector3D positionDifference = position - listenerPosition; double distance = positionDifference.Norm(); double radius = Sources[i].Radius; if (World.CameraMode == World.CameraViewMode.Interior || World.CameraMode == World.CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { radius *= 0.5; } } double gain; if (distance < 2.0 * radius) { gain = 1.0 - distance * distance * (4.0 * radius - distance) / (16.0 * radius * radius * radius); } else { gain = radius / distance; } gain *= Sources[i].Volume; if (gain <= 0.0) { /* * The gain is too low. Stop the sound if playing, * but keep looping sounds pending. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Add the source. * */ toBePlayed.Add(new SoundSourceAttenuation(Sources[i], gain, distance)); } } } /* * Now that we have the list of sounds that are to be played, * sort them by their gain so that we can determine and * adjust the clamp factor. * */ double clampFactor = Math.Exp(LogClampFactor); for (int i = 0; i < toBePlayed.Count; i++) { toBePlayed[i].Gain -= clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; } toBePlayed.Sort(); for (int i = 0; i < toBePlayed.Count; i++) { toBePlayed[i].Gain += clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; } double desiredLogClampFactor; int index = Options.Current.SoundNumber; if (toBePlayed.Count <= index) { desiredLogClampFactor = MinLogClampFactor; } else { double cutoffDistance = toBePlayed[index].Distance; if (cutoffDistance <= 0.0) { desiredLogClampFactor = MaxLogClampFactor; } else { double cutoffGain = toBePlayed[index].Gain; desiredLogClampFactor = Math.Log(cutoffGain / (cutoffDistance * cutoffDistance)); if (desiredLogClampFactor < MinLogClampFactor) { desiredLogClampFactor = MinLogClampFactor; } else if (desiredLogClampFactor > MaxLogClampFactor) { desiredLogClampFactor = MaxLogClampFactor; } } } const double rate = 3.0; if (LogClampFactor < desiredLogClampFactor) { LogClampFactor += timeElapsed * rate; if (LogClampFactor > desiredLogClampFactor) { LogClampFactor = desiredLogClampFactor; } } else if (LogClampFactor > desiredLogClampFactor) { LogClampFactor -= timeElapsed * rate; if (LogClampFactor < desiredLogClampFactor) { LogClampFactor = desiredLogClampFactor; } } /* * Play the sounds. * */ clampFactor = Math.Exp(LogClampFactor); for (int i = index; i < toBePlayed.Count; i++) { toBePlayed[i].Gain = 0.0; } for (int i = 0; i < toBePlayed.Count; i++) { SoundSource source = toBePlayed[i].Source; double gain = toBePlayed[i].Gain - clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; if (gain <= 0.0) { /* * Stop the sound. * */ if (source.State == SoundSourceState.Playing) { AL.DeleteSources(1, ref source.OpenAlSourceName); source.State = SoundSourceState.PlayPending; source.OpenAlSourceName = 0; } if (!source.Looped) { source.State = SoundSourceState.Stopped; source.OpenAlSourceName = 0; } } else { /* * Ensure the buffer is loaded, then play the sound. * */ if (source.State != SoundSourceState.Playing) { LoadBuffer(source.Buffer); if (source.Buffer.Loaded) { AL.GenSources(1, out source.OpenAlSourceName); AL.Source(source.OpenAlSourceName, ALSourcei.Buffer, source.Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ source.State = SoundSourceState.Stopped; continue; } } OpenBveApi.Math.Vector3D position; OpenBveApi.Math.Vector3D velocity; if (source.Train != null) { OpenBveApi.Math.Vector3D direction; TrainManager.CreateWorldCoordinates(source.Train, source.Car, source.Position.X, source.Position.Y, source.Position.Z, out position.X, out position.Y, out position.Z, out direction.X, out direction.Y, out direction.Z); velocity = source.Train.Cars[source.Car].Specs.CurrentSpeed * direction; } else { position = source.Position; velocity = OpenBveApi.Math.Vector3D.Null; } position -= listenerPosition; AL.Source(source.OpenAlSourceName, ALSource3f.Position, (float)position.X, (float)position.Y, (float)position.Z); AL.Source(source.OpenAlSourceName, ALSource3f.Velocity, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); AL.Source(source.OpenAlSourceName, ALSourcef.Pitch, (float)source.Pitch); AL.Source(source.OpenAlSourceName, ALSourcef.Gain, (float)gain); if (source.State != SoundSourceState.Playing) { AL.Source(source.OpenAlSourceName, ALSourceb.Looping, source.Looped); AL.SourcePlay(source.OpenAlSourceName); source.State = SoundSourceState.Playing; } } } }
// parse panel config internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { // read lines System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); } } // initialize double FullWidth = 480, FullHeight = 440, SemiHeight = 240; double AspectRatio = FullWidth / FullHeight; double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance; WorldWidth = WorldHeight * AspectRatio; } World.CameraRestrictionBottomLeft = new Vector3(-0.5 * WorldWidth, -0.5 * WorldHeight, EyeDistance); World.CameraRestrictionTopRight = new Vector3(0.5 * WorldWidth, 0.5 * WorldHeight, EyeDistance); double WorldLeft = Train.Cars[Train.DriverCar].DriverX - 0.5 * WorldWidth; double WorldTop = Train.Cars[Train.DriverCar].DriverY + 0.5 * WorldHeight; double WorldZ = Train.Cars[Train.DriverCar].DriverZ; const double UpDownAngleConstant = -0.191986217719376; double PanelYaw = 0.0; double PanelPitch = UpDownAngleConstant; string PanelBackground = OpenBveApi.Path.CombineFile(TrainPath, "panel.bmp"); // parse lines for panel and view for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // panel case "panel": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "background": if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; PanelBackground = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelBackground)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelBackground + "could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; } } i++; } i--; break; // view case "view": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "yaw": { double yaw = 0.0; if (Value.Length > 0 && !Interface.TryParseDoubleVb6(Value, out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); yaw = 0.0; } PanelYaw = Math.Atan(yaw); } break; case "pitch": { double pitch = 0.0; if (Value.Length > 0 && !Interface.TryParseDoubleVb6(Value, out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); pitch = 0.0; } PanelPitch = Math.Atan(pitch) + UpDownAngleConstant; } break; } } i++; } i--; break; } } } } Train.Cars[Train.DriverCar].DriverYaw = PanelYaw; Train.Cars[Train.DriverCar].DriverPitch = PanelPitch; // panel { if (!System.IO.File.Exists(PanelBackground)) { Interface.AddMessage(Interface.MessageType.Error, true, "The panel image could not be found in " + FileName); } else { Textures.Texture t; Textures.RegisterTexture(PanelBackground, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; SemiHeight = FullHeight - h; CreateElement(Train, 0, SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } } // parse lines for rest double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Lines[i].Length != 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // pressuregauge case "pressuregauge": case "pressuremeter": case "pressureindicator": case "圧力計": { int Type = 0; Color32[] NeedleColor = new Color32[] { new Color32(0, 0, 0, 255), new Color32(0, 0, 0, 255) }; int[] NeedleType = new int[] { 0, 0 }; double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null, Cover = null; double Angle = 0.785398163397449, Minimum = 0.0, Maximum = 1000.0; double UnitFactor = 1000.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "type": case "形態": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Type is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } else if (Type != 0 & Type != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Type must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } break; case "lowerneedle": case "lowerhand": case "下針": case "upperneedle": case "upperhand": case "上針": int k = Key.ToLowerInvariant() == "lowerneedle" | Key.ToLowerInvariant() == "lowerhand" | Key == "下針" ? 0 : 1; if (Arguments.Length >= 1 && Arguments[0].Length > 0) { switch (Arguments[0].ToLowerInvariant()) { case "bc": case "ブレーキシリンダ": NeedleType[k] = 1; break; case "sap": case "直通管": NeedleType[k] = 2; break; case "bp": case "ブレーキ管": case "制動管": NeedleType[k] = 3; break; case "er": case "釣り合い空気溜め": case "釣り合い空気ダメ": case "つりあい空気溜め": case "ツリアイ空気ダメ": NeedleType[k] = 4; break; case "mr": case "元空気溜め": case "元空気ダメ": NeedleType[k] = 5; break; default: { int a; if (!Interface.TryParseIntVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Subject is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); a = 0; } NeedleType[k] = a; } break; } } int r = 0, g = 0, b = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + Key + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + Key + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } NeedleColor[k] = new Color32((byte)r, (byte)g, (byte)b, 255); break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 1.0; } break; case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "cover": case "ふた": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Cover = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Cover)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Cover + "could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Cover = null; } } break; case "unit": case "単位": if (Arguments.Length >= 1 && Arguments[0].Length > 0) { string a = Arguments[0].ToLowerInvariant(); int Unit = 0; if (a == "kpa") { Unit = 0; } else if (a == "kgf/cm2" | a == "kgf/cm^2" | a == "kg/cm2" | a == "kg/cm^2") { Unit = 1; } else if (!Interface.TryParseIntVb6(Arguments[0], out Unit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } else if (Unit != 0 & Unit != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } if (Unit == 1) { UnitFactor = 98066.5; } else { UnitFactor = 1000.0; } } break; case "maximum": case "最大": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "PressureValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Maximum = 1000.0; } break; case "minimum": case "最小": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "PressureValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Minimum = 0.0; } break; case "angle": case "角度": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Angle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Angle = 0.785398163397449; } else { Angle *= 0.0174532925199433; } break; } } i++; } i--; // units Minimum *= UnitFactor; Maximum *= UnitFactor; // background if (Background != null) { Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } // cover if (Cover != null) { Textures.Texture t; Textures.RegisterTexture(Cover, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Type == 0) { // needles for (int k = 0; k < 2; k++) { if (NeedleType[k] != 0) { string Folder = Program.FileSystem.GetDataFolder("Compatibility"); string File = OpenBveApi.Path.CombineFile(Folder, k == 0 ? "needle_pressuregauge_lower.png" : "needle_pressuregauge_upper.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - (double)(4 + k) * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, NeedleColor[k], false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); double c0 = (Angle * (Maximum - Minimum) - 2.0 * Minimum * Math.PI) / (Maximum - Minimum) + Math.PI; double c1 = 2.0 * (Math.PI - Angle) / (Maximum - Minimum); string Variable = "0"; switch (NeedleType[k]) { case 1: Variable = "brakecylinder"; break; case 2: Variable = "straightairpipe"; break; case 3: Variable = "brakepipe"; break; case 4: Variable = "equalizingreservoir"; break; case 5: Variable = "mainreservoir"; break; } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(Variable + " " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } } else if (Type == 1) { // leds if (NeedleType[1] != 0) { int j = CreateElement(Train, CenterX - Radius, CenterY + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, null, NeedleColor[1], false); double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, NeedleColor[1]); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = true; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = Angle - 2.0 * Math.PI; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = 2.0 * Math.PI - Angle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; double c0 = (Angle * (Maximum - Minimum) - 2.0 * Minimum * Math.PI) / (Maximum - Minimum); double c1 = 2.0 * (Math.PI - Angle) / (Maximum - Minimum); string Variable; switch (NeedleType[1]) { case 1: Variable = "brakecylinder"; break; case 2: Variable = "straightairpipe"; break; case 3: Variable = "brakepipe"; break; case 4: Variable = "equalizingreservoir"; break; case 5: Variable = "mainreservoir"; break; default: Variable = "0"; break; } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(Variable + " " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } } break; // speedometer case "speedometer": case "speedindicator": case "速度計": { int Type = 0; Color32 Needle = new Color32(255, 255, 255, 255); bool NeedleOverridden = false; double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null, Cover = null, Atc = null; double Angle = 1.0471975511966, Maximum = 33.3333333333333, AtcRadius = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "type": case "形態": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } else if (Type != 0 & Type != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } break; case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "needle": case "hand": case "針": { int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 255; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 255; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 255; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } Needle = new Color32((byte)r, (byte)g, (byte)b, 255); NeedleOverridden = true; } break; case "cover": case "ふた": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; Cover = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Cover)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Cover + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Cover = null; } break; case "atc": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; Atc = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Atc)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Atc + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Atc = null; } break; case "atcradius": case "atc半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out AtcRadius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); AtcRadius = 0.0; } break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 0.0; } break; case "angle": case "角度": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Angle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Angle = 1.0471975511966; } else { Angle *= 0.0174532925199433; } break; case "maximum": case "最大": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "SpeedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Maximum = 33.3333333333333; } else { Maximum *= 0.277777777777778; } break; } } i++; } i--; if (Background != null) { // background/led Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Cover != null) { // cover Textures.Texture t; Textures.RegisterTexture(Cover, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Atc != null) { // atc int w, h; Program.CurrentHost.QueryTextureDimensions(Atc, out w, out h); if (w > 0 & h > 0) { int n = w / h; int k = -1; for (int j = 0; j < n; j++) { double s; switch (j) { case 1: s = 0.0; break; case 2: s = 15.0; break; case 3: s = 25.0; break; case 4: s = 45.0; break; case 5: s = 55.0; break; case 6: s = 65.0; break; case 7: s = 75.0; break; case 8: s = 90.0; break; case 9: s = 100.0; break; case 10: s = 110.0; break; case 11: s = 120.0; break; default: s = -1.0; break; } s *= 0.277777777777778; double a; if (s >= 0.0) { a = 2.0 * s * (Math.PI - Angle) / Maximum + Angle + Math.PI; } else { a = Math.PI; } double x = CenterX - 0.5 * h + Math.Sin(a) * AtcRadius; double y = CenterY - 0.5 * h - Math.Cos(a) * AtcRadius + SemiHeight; Textures.Texture t; Textures.RegisterTexture(Atc, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(j * h, 0, h, h), Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); if (j == 0) { k = CreateElement(Train, x, y, (double)h, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, x, y, (double)h, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("271 pluginstate"); } } if (Type == 0) { // needle string Folder = Program.FileSystem.GetDataFolder("Compatibility"); string File = OpenBveApi.Path.CombineFile(Folder, "needle_speedometer.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); double c0 = Angle + Math.PI; double c1 = 2.0 * (Math.PI - Angle) / Maximum; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } else if (Type == 1) { // led if (!NeedleOverridden) Needle = new Color32(0, 0, 0, 255); int j = CreateElement(Train, CenterX - Radius, CenterY + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, null, Needle, false); double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, Needle); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = true; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = Angle - 2.0 * Math.PI; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = 2.0 * Math.PI - Angle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; double c0 = Angle; double c1 = 2.0 * (Math.PI - Angle) / Maximum; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } break; // digitalindicator case "digitalindicator": { string Number = null; double CornerX = 0.0, CornerY = 0.0; int Width = 0, Height = 0; double UnitFactor = 3.6; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "number": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Number = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Number)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Number + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Number = null; } } break; case "corner": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; case "size": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out Height)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Height = 0; } break; case "unit": if (Arguments.Length >= 1 && Arguments[0].Length > 0) { string a = Arguments[0].ToLowerInvariant(); int Unit = 0; if (a == "km/h") { Unit = 0; } else if (a == "mph") { Unit = 1; } else if (a == "m/s") { Unit = 2; } else if (!Interface.TryParseIntVb6(Arguments[0], out Unit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } else if (Unit < 0 | Unit > 2) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be between 0 and 2 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } if (Unit == 1) { UnitFactor = 2.2369362920544; } else if (Unit == 2) { UnitFactor = 1.0; } else { UnitFactor = 3.6; } } break; } } i++; } i--; if (Number == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Number is required to be specified in " + Section + " in " + FileName); } if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Height <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is required to be specified in " + Section + " in " + FileName); } if (Number != null & Width > 0 & Height > 0) { int w, h; Program.CurrentHost.QueryTextureDimensions(Number, out w, out h); if (w > 0 & h > 0) { //Generate an error message rather than crashing if the clip region is invalid if (Width > w) { Width = w; Interface.AddMessage(Interface.MessageType.Warning, false, "Clip region width was greater than the texture width " + Section + " in " + FileName); } if (Height > h) { Height = h; Interface.AddMessage(Interface.MessageType.Warning, false, "Clip region height was greater than the texture height " + Section + " in " + FileName); } int n = h / Height; Textures.Texture[] t = new Textures.Texture[n]; for (int j = 0; j < n; j++) { Textures.RegisterTexture(Number, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(w - Width, j * Height, Width, Height), Color24.Blue), out t[j]); //TextureManager.UseTexture(t[j], TextureManager.UseMode.Normal); } { // hundreds int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 100 >= <> 100 quotient 10 mod 10 ?"); } { // tens int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX + (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX + (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 10 >= <> 10 quotient 10 mod 10 ?"); } { // ones int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX + 2.0 * (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX + 2.0 * (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * floor 10 mod"); } } } } break; // pilotlamp case "pilotlamp": case "知らせ灯": { double CornerX = 0.0, CornerY = 0.0; string TurnOn = null, TurnOff = null; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "turnon": case "点灯": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { TurnOn = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(TurnOn)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + TurnOn + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); TurnOn = null; } } break; case "turnoff": case "消灯": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { TurnOff = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(TurnOff)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + TurnOff + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); TurnOff = null; } } break; case "corner": case "左上": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; } } i++; } i--; if (TurnOn != null & TurnOff != null) { Textures.Texture t0, t1; Textures.RegisterTexture(TurnOn, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t0); Textures.RegisterTexture(TurnOff, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t1); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t0, Textures.OpenGlTextureWrapMode.ClampClamp); Textures.LoadTexture(t1, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t0.Width; double h = (double)t0.Height; int j = CreateElement(Train, CornerX, CornerY + SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 2.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t0, new Color32(255, 255, 255, 255), false); w = (double)t1.Width; h = (double)t1.Height; CreateElement(Train, CornerX, CornerY + SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 2.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t1, new Color32(255, 255, 255, 255), true); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("doors 0 !="); } } break; // watch case "watch": case "時計": { Color32 Needle = new Color32(0, 0, 0, 255); double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "needle": case "hand": case "針": { int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } Needle = new Color32((byte)r, (byte)g, (byte)b, 255); } break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; } } i++; } i--; if (Background != null) { Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } string Folder = Program.FileSystem.GetDataFolder("Compatibility"); { // hour string File = OpenBveApi.Path.CombineFile(Folder, "needle_hour.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time 0.000277777777777778 * floor 0.523598775598298 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } { // minute string File = OpenBveApi.Path.CombineFile(Folder, "needle_minute.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time 0.0166666666666667 * floor 0.10471975511966 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } { // second string File = OpenBveApi.Path.CombineFile(Folder, "needle_second.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time floor 0.10471975511966 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } } break; // brakeindicator case "brakeindicator": { double CornerX = 0.0, CornerY = 0.0; string Image = null; int Width = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "image": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Image = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Image)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Image + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Image = null; } } break; case "corner": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; case "width": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 1; } else if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is expected to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 1; } break; } } i++; } i--; if (Image == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Image is required to be specified in " + Section + " in " + FileName); } if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Image != null & Width > 0) { int w, h; Program.CurrentHost.QueryTextureDimensions(Image, out w, out h); if (w > 0 & h > 0) { int n = w / Width; int k = -1; for (int j = 0; j < n; j++) { Textures.Texture t; OpenBveApi.Textures.TextureClipRegion clip = new OpenBveApi.Textures.TextureClipRegion(j * Width, 0, Width, h); Textures.RegisterTexture(Image, new OpenBveApi.Textures.TextureParameters(clip, Color24.Blue), out t); //TextureManager.UseTexture(t, TextureManager.UseMode.Normal); if (j == 0) { k = CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), true); } } if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { int maxpow = Train.Specs.MaximumPowerNotch; int em = maxpow + 3; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " brakeNotch 0 > " + maxpow.ToString(Culture) + " BrakeNotch + " + maxpow.ToString(Culture) + " powerNotch - ? ?"); } else { if (Train.Specs.HasHoldBrake) { int em = Train.Specs.MaximumPowerNotch + 2 + Train.Specs.MaximumBrakeNotch; int maxpow = Train.Specs.MaximumPowerNotch; int maxpowp1 = maxpow + 1; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " holdBrake " + maxpowp1.ToString(Culture) + " brakeNotch 0 > brakeNotch " + maxpowp1.ToString(Culture) + " + " + maxpow.ToString(Culture) + " powerNotch - ? ? ?"); } else { int em = Train.Specs.MaximumPowerNotch + 1 + Train.Specs.MaximumBrakeNotch; int maxpow = Train.Specs.MaximumPowerNotch; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " brakeNotch 0 > brakeNotch " + maxpow.ToString(Culture) + " + " + maxpow.ToString(Culture) + " powerNotch - ? ?"); } } } } } break; } } } } }
/// <summary>This method is called once the route and train data have been preprocessed, in order to physically setup the simulation</summary> private void SetupSimulation() { if (Loading.Cancel) { Close(); } Timetable.CreateTimetable(); //Check if any critical errors have occured during the route or train loading for (int i = 0; i < Interface.MessageCount; i++) { if (Interface.Messages[i].Type == Interface.MessageType.Critical) { MessageBox.Show("A critical error has occured:\n\n" + Interface.Messages[i].Text + "\n\nPlease inspect the error log file for further information.", "Load", MessageBoxButtons.OK, MessageBoxIcon.Hand); Close(); } } Renderer.InitializeLighting(); Game.LogRouteName = System.IO.Path.GetFileName(MainLoop.currentResult.RouteFile); Game.LogTrainName = System.IO.Path.GetFileName(MainLoop.currentResult.TrainFolder); Game.LogDateTime = DateTime.Now; if (Interface.CurrentOptions.LoadInAdvance) { Textures.LoadAllTextures(); } else { Textures.UnloadAllTextures(); } // camera ObjectManager.InitializeVisibility(); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, 0.0, true, false); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, -0.1, true, false); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, 0.1, true, false); World.CameraTrackFollower.TriggerType = TrackManager.EventTriggerType.Camera; // starting time and track position Game.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; int PlayerFirstStationIndex = -1; double PlayerFirstStationPosition = 0.0; for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].StopMode == Game.StationStopMode.AllStop | Game.Stations[i].StopMode == Game.StationStopMode.PlayerStop & Game.Stations[i].Stops.Length != 0) { PlayerFirstStationIndex = i; int s = Game.GetStopIndex(i, TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { PlayerFirstStationPosition = Game.Stations[i].Stops[s].TrackPosition; double TrainLength = 0.0; for (int c = 0; c < TrainManager.Trains[TrainManager.PlayerTrain.TrainIndex].Cars.Length; c++) { TrainLength += TrainManager.Trains[TrainManager.PlayerTrain.TrainIndex].Cars[c].Length; } for (int j = 0; j < Game.BufferTrackPositions.Length; j++) { if (PlayerFirstStationPosition > Game.BufferTrackPositions[j] && PlayerFirstStationPosition - TrainLength < Game.BufferTrackPositions[j]) { /* * HACK: The initial start position for the player train is stuck on a set of buffers * This means we have to make some one the fly adjustments to the first station stop position */ //Set the start position to be the buffer position plus the train length plus 1m PlayerFirstStationPosition = Game.BufferTrackPositions[j] + TrainLength + 1; //Update the station stop location if (s >= 0) { Game.Stations[PlayerFirstStationIndex].Stops[s].TrackPosition = PlayerFirstStationPosition; } else { Game.Stations[PlayerFirstStationIndex].DefaultTrackPosition = PlayerFirstStationPosition; } break; } } } else { PlayerFirstStationPosition = Game.Stations[i].DefaultTrackPosition; } if (Game.Stations[i].ArrivalTime < 0.0) { if (Game.Stations[i].DepartureTime < 0.0) { Game.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; } else { Game.SecondsSinceMidnight = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; Game.StartupTime = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; } } else { Game.SecondsSinceMidnight = Game.Stations[i].ArrivalTime; Game.StartupTime = Game.Stations[i].ArrivalTime; } break; } } int OtherFirstStationIndex = -1; double OtherFirstStationPosition = 0.0; double OtherFirstStationTime = 0.0; for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].StopMode == Game.StationStopMode.AllStop | Game.Stations[i].StopMode == Game.StationStopMode.PlayerPass & Game.Stations[i].Stops.Length != 0) { OtherFirstStationIndex = i; int s = Game.GetStopIndex(i, TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { OtherFirstStationPosition = Game.Stations[i].Stops[s].TrackPosition; } else { OtherFirstStationPosition = Game.Stations[i].DefaultTrackPosition; } if (Game.Stations[i].ArrivalTime < 0.0) { if (Game.Stations[i].DepartureTime < 0.0) { OtherFirstStationTime = 0.0; } else { OtherFirstStationTime = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; } } else { OtherFirstStationTime = Game.Stations[i].ArrivalTime; } break; } } if (Game.PrecedingTrainTimeDeltas.Length != 0) { OtherFirstStationTime -= Game.PrecedingTrainTimeDeltas[Game.PrecedingTrainTimeDeltas.Length - 1]; if (OtherFirstStationTime < Game.SecondsSinceMidnight) { Game.SecondsSinceMidnight = OtherFirstStationTime; } } // initialize trains for (int i = 0; i < TrainManager.Trains.Length; i++) { TrainManager.InitializeTrain(TrainManager.Trains[i]); int s = i == TrainManager.PlayerTrain.TrainIndex ? PlayerFirstStationIndex : OtherFirstStationIndex; if (s >= 0) { if (Game.Stations[s].OpenLeftDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Specs.AnticipatedLeftDoorsOpened = true; } } if (Game.Stations[s].OpenRightDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Specs.AnticipatedRightDoorsOpened = true; } } } if (Game.Sections.Length != 0) { Game.Sections[0].Enter(TrainManager.Trains[i]); } for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { double length = TrainManager.Trains[i].Cars[0].Length; TrainManager.MoveCar(TrainManager.Trains[i], j, -length, 0.01); TrainManager.MoveCar(TrainManager.Trains[i], j, length, 0.01); } } // score Game.CurrentScore.ArrivalStation = PlayerFirstStationIndex + 1; Game.CurrentScore.DepartureStation = PlayerFirstStationIndex; Game.CurrentScore.Maximum = 0; for (int i = 0; i < Game.Stations.Length; i++) { if (i != PlayerFirstStationIndex & Game.PlayerStopsAtStation(i)) { if (i == 0 || Game.Stations[i - 1].StationType != Game.StationType.ChangeEnds) { Game.CurrentScore.Maximum += Game.ScoreValueStationArrival; } } } if (Game.CurrentScore.Maximum <= 0) { Game.CurrentScore.Maximum = Game.ScoreValueStationArrival; } // signals if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // move train in position for (int i = 0; i < TrainManager.Trains.Length; i++) { double p; if (i == TrainManager.PlayerTrain.TrainIndex) { p = PlayerFirstStationPosition; } else if (TrainManager.Trains[i].State == TrainManager.TrainState.Bogus) { p = Game.BogusPretrainInstructions[0].TrackPosition; TrainManager.Trains[i].AI = new Game.BogusPretrainAI(TrainManager.Trains[i]); } else { p = OtherFirstStationPosition; } for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.MoveCar(TrainManager.Trains[i], j, p, 0.01); } } // timetable if (Timetable.DefaultTimetableDescription.Length == 0) { Timetable.DefaultTimetableDescription = Game.LogTrainName; } // initialize camera if (World.CameraRestriction == World.CameraRestrictionMode.NotAvailable) { World.CameraMode = World.CameraViewMode.InteriorLookAhead; } //Place the initial camera in the driver car TrainManager.UpdateCamera(TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar); TrackManager.UpdateTrackFollower(ref World.CameraTrackFollower, -1.0, true, false); ObjectManager.UpdateVisibility(World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z); World.CameraSavedInterior = new World.CameraAlignment(); World.CameraSavedExterior = new World.CameraAlignment(new OpenBveApi.Math.Vector3(-2.5, 1.5, -15.0), 0.3, -0.2, 0.0, PlayerFirstStationPosition, 1.0); World.CameraSavedTrack = new World.CameraAlignment(new OpenBveApi.Math.Vector3(-3.0, 2.5, 0.0), 0.3, 0.0, 0.0, TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition - 10.0, 1.0); // signalling sections for (int i = 0; i < TrainManager.Trains.Length; i++) { int s = TrainManager.Trains[i].CurrentSectionIndex; Game.Sections[s].Enter(TrainManager.Trains[i]); } if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // fast-forward until start time { Game.MinimalisticSimulation = true; const double w = 0.25; double u = Game.StartupTime - Game.SecondsSinceMidnight; if (u > 0) { while (true) { double v = u < w ? u : w; u -= v; Game.SecondsSinceMidnight += v; TrainManager.UpdateTrains(v); if (u <= 0.0) { break; } TotalTimeElapsedForSectionUpdate += v; if (TotalTimeElapsedForSectionUpdate >= 1.0) { if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } TotalTimeElapsedForSectionUpdate = 0.0; } } } Game.MinimalisticSimulation = false; } // animated objects ObjectManager.UpdateAnimatedWorldObjects(0.0, true); TrainManager.UpdateTrainObjects(0.0, true); // timetable if (TrainManager.PlayerTrain.Station >= 0) { Timetable.UpdateCustomTimetable(Game.Stations[TrainManager.PlayerTrain.Station].TimetableDaytimeTexture, Game.Stations[TrainManager.PlayerTrain.Station].TimetableNighttimeTexture); if (Timetable.CustomObjectsUsed != 0 & Timetable.CustomTimetableAvailable && Interface.CurrentOptions.TimeTableStyle != Interface.TimeTableMode.AutoGenerated && Interface.CurrentOptions.TimeTableStyle != Interface.TimeTableMode.None) { Timetable.CurrentTimetable = Timetable.TimetableState.Custom; } } // warnings / errors if (Interface.MessageCount != 0) { int filesNotFound = 0; int errors = 0; int warnings = 0; for (int i = 0; i < Interface.MessageCount; i++) { if (Interface.Messages[i].FileNotFound) { filesNotFound++; } else if (Interface.Messages[i].Type == Interface.MessageType.Error) { errors++; } else if (Interface.Messages[i].Type == Interface.MessageType.Warning) { warnings++; } } string NotFound = null; string Messages = null; if (filesNotFound != 0) { NotFound = filesNotFound.ToString() + " file(s) not found"; Game.AddDebugMessage(NotFound, 10.0); } if (errors != 0 & warnings != 0) { Messages = errors.ToString() + " error(s), " + warnings.ToString() + " warning(s)"; Game.AddDebugMessage(Messages, 10.0); } else if (errors != 0) { Messages = errors.ToString() + " error(s)"; Game.AddDebugMessage(Messages, 10.0); } else { Messages = warnings.ToString() + " warning(s)"; Game.AddDebugMessage(Messages, 10.0); } Game.RouteInformation.FilesNotFound = NotFound; Game.RouteInformation.ErrorsAndWarnings = Messages; //Print the plugin error encountered (If any) for 10s //This must be done after the simulation has init, as otherwise the timeout doesn't work if (Loading.PluginError != null) { Game.AddMessage(Loading.PluginError, Game.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0); Game.AddMessage(Interface.GetInterfaceString("errors_plugin_failure2"), Game.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0); } } loadComplete = true; RenderRealTimeElapsed = 0.0; RenderTimeElapsed = 0.0; World.InitializeCameraRestriction(); }
/// <summary>Loads the specified plugin for the specified train.</summary> /// <param name="train">The train to attach the plugin to.</param> /// <param name="pluginFile">The file to the plugin.</param> /// <param name="trainFolder">The train folder.</param> /// <returns>Whether the plugin was loaded successfully.</returns> private static bool LoadPlugin(TrainManager.Train train, string pluginFile, string trainFolder) { string pluginTitle = System.IO.Path.GetFileName(pluginFile); if (!System.IO.File.Exists(pluginFile)) { Interface.AddMessage(Interface.MessageType.Error, true, "The train plugin " + pluginTitle + " could not be found."); return false; } /* * Unload plugin if already loaded. * */ if (train.Plugin != null) { UnloadPlugin(train); } /* * Prepare initialization data for the plugin. * */ BrakeTypes brakeType = (BrakeTypes)train.Cars[train.DriverCar].Specs.BrakeType; int brakeNotches; int powerNotches; bool hasHoldBrake; if (brakeType == BrakeTypes.AutomaticAirBrake) { brakeNotches = 2; powerNotches = train.Specs.MaximumPowerNotch; hasHoldBrake = false; } else { brakeNotches = train.Specs.MaximumBrakeNotch + (train.Specs.HasHoldBrake ? 1 : 0); powerNotches = train.Specs.MaximumPowerNotch; hasHoldBrake = train.Specs.HasHoldBrake; } int cars = train.Cars.Length; VehicleSpecs specs = new VehicleSpecs(powerNotches, brakeType, brakeNotches, hasHoldBrake, cars); InitializationModes mode = (InitializationModes)Game.TrainStart; /* * Check if the plugin is a .NET plugin. * */ Assembly assembly; try { assembly = Assembly.LoadFile(pluginFile); } catch (BadImageFormatException) { assembly = null; } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be loaded due to the following exception: " + ex.Message); return false; } if (assembly != null) { Type[] types; try { types = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { foreach (Exception e in ex.LoaderExceptions) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " raised an exception on loading: " + e.Message); } return false; } foreach (Type type in types) { if (typeof(IRuntime).IsAssignableFrom(type)) { IRuntime api = assembly.CreateInstance(type.FullName) as IRuntime; train.Plugin = new NetPlugin(pluginFile, trainFolder, api, train); if (train.Plugin.Load(specs, mode)) { return true; } else { train.Plugin = null; return false; } } } Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE."); return false; } /* * Check if the plugin is a Win32 plugin. * */ try { if (!CheckWin32Header(pluginFile)) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " is of an unsupported binary format and therefore cannot be used with openBVE."); return false; } } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " could not be read due to the following reason: " + ex.Message); return false; } if (!Program.CurrentlyRunningOnWindows | IntPtr.Size != 4) { Interface.AddMessage(Interface.MessageType.Warning, false, "The train plugin " + pluginTitle + " can only be used on 32-bit Microsoft Windows or compatible."); return false; } if (Program.CurrentlyRunningOnWindows && !System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "\\AtsPluginProxy.dll")) { Interface.AddMessage(Interface.MessageType.Warning, false, "AtsPluginProxy.dll is missing or corrupt- Please reinstall."); return false; } train.Plugin = new Win32Plugin(pluginFile, train); if (train.Plugin.Load(specs, mode)) { return true; } else { train.Plugin = null; Interface.AddMessage(Interface.MessageType.Error, false, "The train plugin " + pluginTitle + " does not export a train interface and therefore cannot be used with openBVE."); return false; } }
/// <summary>Updates the sound component. Should be called every frame.</summary> /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> private static void UpdateLinearModel(double timeElapsed) { /* * Set up the listener * */ OpenBveApi.Math.Vector3D listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3D listenerVelocity; if (World.CameraMode == World.CameraViewMode.Interior || World.CameraMode == World.CameraViewMode.InteriorLookAhead || World.CameraMode == World.CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; OpenBveApi.Math.Vector3D diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3D.Normalize(diff) + World.CameraAlignmentSpeed.Position; } else { listenerVelocity = World.CameraAlignmentSpeed.Position; } float[] vectors = { (float)listenerOrientation.Z.X, (float)listenerOrientation.Z.Y, (float)listenerOrientation.Z.Z, -(float)listenerOrientation.Y.X, -(float)listenerOrientation.Y.Y, -(float)listenerOrientation.Y.Z }; AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); AL.Listener(ALListenerfv.Orientation, ref vectors); /* * Set up the atmospheric attributes * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); // double airDensity = Game.GetAirDensity(airPressure, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { AL.SpeedOfSound((float)speedOfSound); } catch { } /* * Update the sound sources * */ int actuallyPlaying = 0; for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * Calculate the sound gain. * */ OpenBveApi.Math.Vector3D position; OpenBveApi.Math.Vector3D velocity; if (Sources[i].Train != null) { OpenBveApi.Math.Vector3D direction; TrainManager.CreateWorldCoordinates(Sources[i].Train, Sources[i].Car, Sources[i].Position.X, Sources[i].Position.Y, Sources[i].Position.Z, out position.X, out position.Y, out position.Z, out direction.X, out direction.Y, out direction.Z); velocity = Sources[i].Train.Cars[Sources[i].Car].Specs.CurrentSpeed * direction; } else { position = Sources[i].Position; velocity = OpenBveApi.Math.Vector3D.Null; } OpenBveApi.Math.Vector3D positionDifference = position - listenerPosition; double gain; if (GlobalMute) { gain = 0.0; } else { double distance = positionDifference.Norm(); double innerRadius = Sources[i].Radius; if (World.CameraMode == World.CameraViewMode.Interior || World.CameraMode == World.CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { innerRadius *= 0.5; } } double outerRadius = OuterRadiusFactor * innerRadius; if (distance < outerRadius) { if (distance <= innerRadius) { gain = Sources[i].Volume; } else { gain = (distance - outerRadius) / (innerRadius - outerRadius); gain *= Sources[i].Volume; } gain = 3.0 * gain * gain - 2.0 * gain * gain * gain; } else { gain = 0.0; } } if (gain <= GainThreshold) { /* * If the gain is too low to be audible, stop the sound. * If the sound is not looping, stop it if necessary, * then remove it from the list of sound sources. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Play the sound and update position, velocity, pitch and gain. * For non-looping sounds, check if the sound is still playing. * */ gain = (gain - GainThreshold) / (1.0 - GainThreshold); if (Sources[i].State != SoundSourceState.Playing) { LoadBuffer(Sources[i].Buffer); if (Sources[i].Buffer.Loaded) { AL.GenSources(1, out Sources[i].OpenAlSourceName); AL.Source(Sources[i].OpenAlSourceName, ALSourcei.Buffer, Sources[i].Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ Sources[i].State = SoundSourceState.Stopped; continue; } } AL.Source(Sources[i].OpenAlSourceName, ALSource3f.Position, (float)positionDifference.X, (float)positionDifference.Y, (float)positionDifference.Z); AL.Source(Sources[i].OpenAlSourceName, ALSource3f.Velocity, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); AL.Source(Sources[i].OpenAlSourceName, ALSourcef.Pitch, (float)Sources[i].Pitch); AL.Source(Sources[i].OpenAlSourceName, ALSourcef.Gain, (float)gain); if (Sources[i].State != SoundSourceState.Playing) { AL.Source(Sources[i].OpenAlSourceName, ALSourceb.Looping, Sources[i].Looped); AL.SourcePlay(Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Playing; } if (!Sources[i].Looped) { int state; AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out state); if (state != (int)ALSourceState.Initial && state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else { actuallyPlaying++; } } else { actuallyPlaying++; } } } } /* * Adjust the outer radius factor / the clamp factor. * */ if (actuallyPlaying >= Options.Current.SoundNumber - 2) { /* * Too many sounds are playing. * Reduce the outer radius factor. * */ OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < -OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = -OuterRadiusFactorMaximumSpeed; } } else if (actuallyPlaying <= Options.Current.SoundNumber - 6) { /* * Only few sounds are playing. * Increase the outer radius factor. * */ OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = OuterRadiusFactorMaximumSpeed; } } else { /* * Neither too many nor too few sounds are playing. * Stabilize the outer radius factor. * */ if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > 0.0) { OuterRadiusFactorSpeed = 0.0; } } else { OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed = 0.0; } } } OuterRadiusFactor += OuterRadiusFactorSpeed * timeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; OuterRadiusFactorSpeed = 0.0; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; OuterRadiusFactorSpeed = 0.0; } }
private static void LoadEverythingThreaded() { Program.AppendToLogFile("Loading route file: " + CurrentRouteFile); string RailwayFolder = GetRailwayFolder(CurrentRouteFile); string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower { Train = null, CarIndex = -1 }; World.CameraMode = World.CameraViewMode.Interior; //First, check the format of the route file //RW routes were written for BVE1 / 2, and have a different command syntax bool IsRW = CsvRwRouteParser.isRWFile(CurrentRouteFile); Program.AppendToLogFile("Route file format is: " + (IsRW ? "RW" : "CSV")); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); Thread createIllustrations = new Thread(Game.RouteInformation.LoadInformation) { IsBackground = true }; createIllustrations.Start(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } if (Game.Stations.Length == 1) { //Log the fact that only a single station is present, as this is probably not right Program.AppendToLogFile("The processed route file only contains a single station."); } Program.AppendToLogFile("Route file loaded successfully."); RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { TrainManager.Trains[k] = new TrainManager.Train { TrainIndex = k }; if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k].State = TrainManager.TrainState.Bogus; } else { TrainManager.Trains[k].State = TrainManager.TrainState.Pending; } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { //Sleep for 10ms to allow route loading locks to release Thread.Sleep(20); if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string TrainData = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat"); TrainDatParser.ParseTrainData(TrainData, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains[k].InitializeCarSounds(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { // real train Program.AppendToLogFile("Loading player train: " + CurrentTrainFolder); TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; string TrainData = OpenBveApi.Path.CombineFile(CurrentTrainFolder, "train.dat"); TrainDatParser.ParseTrainData(TrainData, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].BeaconReceiver.Train = TrainManager.Trains[k]; } // add panel section if (k == TrainManager.PlayerTrain.TrainIndex) { TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections = new TrainManager.CarSection[1]; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay = true; TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.AppendToLogFile("Train panel loaded sucessfully."); } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { ObjectManager.UnifiedObject[] CarObjects; ObjectManager.UnifiedObject[] BogieObjects; ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, out BogieObjects, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } //Stores the current array index of the bogie object to add //Required as there are two bogies per car, and we're using a simple linear array.... int currentBogieObject = 0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; CsvB3dObjectParser.ApplyScale(so, sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object int j = TrainManager.Trains[k].Cars[i].CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1); if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { int j = TrainManager.Trains[k].Cars[i].FrontBogie.CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].FrontBogie.CarSections, j + 1); if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } currentBogieObject++; //Can't think of a better way to do this than two functions...... if (BogieObjects[currentBogieObject] != null) { int j = TrainManager.Trains[k].Cars[i].RearBogie.CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].RearBogie.CarSections, j + 1); if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } currentBogieObject++; } } // place cars { double z = 0.0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { //Front axle track position TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxle.Position; //Bogie for front axle TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Position; TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Position; //Rear axle track position TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxle.Position; //Bogie for rear axle TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Position; TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Position; //Beacon reciever (AWS, ATC etc.) TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition; z -= TrainManager.Trains[k].Cars[i].Length; if (i < TrainManager.Trains[k].Cars.Length - 1) { z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars); } } } // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } ObjectManager.FinishCreatingObjects(); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } } }
/// <summary>Sets the driver handles or the virtual handles.</summary> /// <param name="handles">The handles.</param> /// <param name="virtualHandles">Whether to set the virtual handles.</param> private void SetHandles(Handles handles, bool virtualHandles) { /* * Process the handles. */ if (this.Train.Specs.SingleHandle & handles.BrakeNotch != 0) { handles.PowerNotch = 0; } /* * Process the reverser. */ if (handles.Reverser >= -1 & handles.Reverser <= 1) { if (virtualHandles) { this.Train.Specs.CurrentReverser.Actual = handles.Reverser; } else { TrainManager.ApplyReverser(this.Train, handles.Reverser, false); } } else { if (virtualHandles) { this.Train.Specs.CurrentReverser.Actual = this.Train.Specs.CurrentReverser.Driver; } this.PluginValid = false; } /* * Process the power. * */ if (handles.PowerNotch >= 0 & handles.PowerNotch <= this.Train.Specs.MaximumPowerNotch) { if (virtualHandles) { this.Train.Specs.CurrentPowerNotch.Safety = handles.PowerNotch; } else { TrainManager.ApplyNotch(this.Train, handles.PowerNotch, false, 0, true); } } else { if (virtualHandles) { this.Train.Specs.CurrentPowerNotch.Safety = this.Train.Specs.CurrentPowerNotch.Driver; } this.PluginValid = false; } // if (handles.BrakeNotch != 0) { // if (virtualHandles) { // this.Train.Specs.CurrentPowerNotch.Safety = 0; // } // } /* * Process the brakes. * */ if (virtualHandles) { this.Train.Specs.CurrentEmergencyBrake.Safety = false; this.Train.Specs.CurrentHoldBrake.Actual = false; } if (this.Train.Cars[this.Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (handles.BrakeNotch == 0) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Release; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release); } } else if (handles.BrakeNotch == 1) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Lap; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Lap); } } else if (handles.BrakeNotch == 2) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Release); } } else if (handles.BrakeNotch == 3) { if (virtualHandles) { this.Train.Specs.AirBrake.Handle.Safety = TrainManager.AirBrakeHandleState.Service; this.Train.Specs.CurrentEmergencyBrake.Safety = true; } else { TrainManager.ApplyAirBrakeHandle(this.Train, TrainManager.AirBrakeHandleState.Service); TrainManager.ApplyEmergencyBrake(this.Train); } } else { this.PluginValid = false; } } else { if (this.Train.Specs.HasHoldBrake) { if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 2) { if (virtualHandles) { this.Train.Specs.CurrentEmergencyBrake.Safety = true; this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch; } else { TrainManager.ApplyHoldBrake(this.Train, false); TrainManager.ApplyNotch(this.Train, 0, true, this.Train.Specs.MaximumBrakeNotch, false); TrainManager.ApplyEmergencyBrake(this.Train); } } else if (handles.BrakeNotch >= 2 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch + 1) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch - 1; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyHoldBrake(this.Train, false); TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch - 1, false); } } else if (handles.BrakeNotch == 1) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = 0; this.Train.Specs.CurrentHoldBrake.Actual = true; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyNotch(this.Train, 0, true, 0, false); TrainManager.ApplyHoldBrake(this.Train, true); } } else if (handles.BrakeNotch == 0) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = 0; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyNotch(this.Train, 0, true, 0, false); TrainManager.ApplyHoldBrake(this.Train, false); } } else { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver; } this.PluginValid = false; } } else { if (handles.BrakeNotch == this.Train.Specs.MaximumBrakeNotch + 1) { if (virtualHandles) { this.Train.Specs.CurrentEmergencyBrake.Safety = true; this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.MaximumBrakeNotch; } else { TrainManager.ApplyHoldBrake(this.Train, false); TrainManager.ApplyEmergencyBrake(this.Train); } } else if (handles.BrakeNotch >= 0 & handles.BrakeNotch <= this.Train.Specs.MaximumBrakeNotch | this.Train.Specs.CurrentBrakeNotch.DelayedChanges.Length == 0) { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = handles.BrakeNotch; } else { TrainManager.UnapplyEmergencyBrake(this.Train); TrainManager.ApplyNotch(this.Train, 0, true, handles.BrakeNotch, false); } } else { if (virtualHandles) { this.Train.Specs.CurrentBrakeNotch.Safety = this.Train.Specs.CurrentBrakeNotch.Driver; } this.PluginValid = false; } } } /* * Process the const speed system. * */ this.Train.Specs.CurrentConstSpeed = handles.ConstSpeed & this.Train.Specs.HasConstSpeed; }
/// <summary>Stops all sounds that are attached to the specified train.</summary> /// <param name="train">The train.</param> internal static void StopAllSounds(TrainManager.Train train) { for (int i = 0; i < SourceCount; i++) { if (Sources[i].Train == train) { if (Sources[i].State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].OpenAlSourceName = 0; } Sources[i].State = SoundSourceState.Stopped; } } }
private void PerformDefault() { if (Train.Derailed) { if (Train.Handles.EmergencyBrake.Driver != true) { Train.ApplyEmergencyBrake(); } return; } // personality double spd = Train.CurrentSpeed; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { if (Train.Station != this.LastStation) { this.LastStation = Train.Station; double time; if (Stations[Train.Station].ArrivalTime >= 0.0) { time = Stations[Train.Station].ArrivalTime - Train.TimetableDelta; } else if (Stations[Train.Station].DepartureTime >= 0.0) { time = Stations[Train.Station].DepartureTime - Train.TimetableDelta; if (time > SecondsSinceMidnight) { time -= Stations[Train.Station].StopTime; if (time > SecondsSinceMidnight) { time = double.MinValue; } } } else { time = double.MinValue; } if (time != double.MinValue) { const double largeThreshold = 30.0; const double largeChangeFactor = 0.0025; const double smallThreshold = 15.0; const double smallChange = 0.05; double diff = SecondsSinceMidnight - time; if (diff < -largeThreshold) { /* The AI is too fast. Decrease the preferred speed. */ this.CurrentSpeedFactor -= largeChangeFactor * (-diff - largeThreshold); if (this.CurrentSpeedFactor < 0.7) { this.CurrentSpeedFactor = 0.7; } } else if (diff > largeThreshold) { /* The AI is too slow. Increase the preferred speed. */ this.CurrentSpeedFactor += largeChangeFactor * (diff - largeThreshold); if (this.CurrentSpeedFactor > 1.1) { this.CurrentSpeedFactor = 1.1; } } else if (Math.Abs(diff) < smallThreshold) { /* The AI is at about the right speed. Change the preferred speed toward the personality default. */ if (this.CurrentSpeedFactor < this.PersonalitySpeedFactor) { this.CurrentSpeedFactor += smallChange; if (this.CurrentSpeedFactor > this.PersonalitySpeedFactor) { this.CurrentSpeedFactor = this.PersonalitySpeedFactor; } } else if (this.CurrentSpeedFactor > this.PersonalitySpeedFactor) { this.CurrentSpeedFactor -= smallChange; if (this.CurrentSpeedFactor < this.PersonalitySpeedFactor) { this.CurrentSpeedFactor = this.PersonalitySpeedFactor; } } } } } } // door states bool doorsopen = false; for (int i = 0; i < Train.Cars.Length; i++) { for (int j = 0; j < Train.Cars[i].Doors.Length; j++) { if (Train.Cars[i].Doors[j].State != 0.0) { doorsopen = true; break; } if (doorsopen) { break; } } } // do the ai Train.Specs.CurrentConstSpeed = false; Train.ApplyHoldBrake(false); int stopIndex = Train.Station >= 0 ? Game.Stations[Train.Station].GetStopIndex(Train.Cars.Length) : -1; if (Train.CurrentSectionLimit == 0.0) { // passing red signal Train.ApplyEmergencyBrake(); Train.ApplyNotch(-1, true, 1, true); CurrentInterval = 0.5; } else if (doorsopen | Train.StationState == TrainManager.TrainStopState.Boarding) { // door opened or boarding at station this.PowerNotchAtWhichWheelSlipIsObserved = Train.Handles.Power.MaximumNotch + 1; if (Train.Station >= 0 && Stations[Train.Station].Type != StationType.Normal && Stations[Train.Station].Type != StationType.RequestStop && Train == TrainManager.PlayerTrain) { // player's terminal station if (Train.Plugin == null || Train.Plugin.LastReverser == -2) { Train.ApplyReverser(0, false); } Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); CurrentInterval = 1.0; } else { CurrentInterval = 1.0; Train.ApplyNotch(-1, true, 0, true); if (Train.Handles.Brake is TrainManager.AirBrakeHandle) { if (Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.CurrentPressure < 0.3 * Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.ServiceMaximumPressure) { Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); } else if (Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.CurrentPressure > 0.9 * Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.EmergencyMaximumPressure) { Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); } else { Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Lap); } } else { int b; if (Math.Abs(spd) < 0.02) { b = (int)Math.Ceiling(0.5 * (double)Train.Handles.Brake.MaximumNotch); CurrentInterval = 0.3; } else { b = Train.Handles.Brake.MaximumNotch; } if (Train.Handles.Brake.Driver < b) { Train.ApplyNotch(0, true, 1, true); } else if (Train.Handles.Brake.Driver > b) { Train.ApplyNotch(0, true, -1, true); } } Train.UnapplyEmergencyBrake(); if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Completed) { // ready for departure - close doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic && Train.Specs.DoorInterlockState == DoorInterlockStates.Unlocked) { TrainManager.CloseTrainDoors(Train, true, true); } } else if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { } else { // not at station - close doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic && Train.Specs.DoorInterlockState == DoorInterlockStates.Unlocked) { TrainManager.CloseTrainDoors(Train, true, true); } } } } else if (Train.Station >= 0 && stopIndex >= 0 && Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].BackwardTolerance && (StopsAtStation(Train.Station, Train) & (Stations[Train.Station].OpenLeftDoors | Stations[Train.Station].OpenRightDoors) & Math.Abs(Train.CurrentSpeed) < 0.25 & Train.StationState == TrainManager.TrainStopState.Pending)) { // arrived at station - open doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic && Train.Specs.DoorInterlockState == DoorInterlockStates.Unlocked) { TrainManager.OpenTrainDoors(Train, Stations[Train.Station].OpenLeftDoors, Stations[Train.Station].OpenRightDoors); } CurrentInterval = 1.0; } else if (Train.Station >= 0 && stopIndex >= 0 && Stations[Train.Station].Type != StationType.Normal && Stations[Train.Station].Type != StationType.RequestStop && Train == TrainManager.PlayerTrain && Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].BackwardTolerance && -Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].ForwardTolerance && Math.Abs(Train.CurrentSpeed) < 0.25) { // player's terminal station (not boarding any longer) if (Train.Plugin != null || Train.Plugin.LastReverser == -2) { Train.ApplyReverser(0, false); } Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); CurrentInterval = 10.0; } else { // drive Train.ApplyReverser(1, false); if (Train.Cars[Train.DriverCar].FrontAxle.CurrentWheelSlip | Train.Cars[Train.DriverCar].RearAxle.CurrentWheelSlip) { // react to wheel slip if (Train.Handles.Power.Driver > 1) { this.PowerNotchAtWhichWheelSlipIsObserved = Train.Handles.Power.Driver; Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); this.CurrentInterval = 2.5; return; } } // initialize double acc = Train.Specs.CurrentAverageAcceleration; double lim = PrecedingTrainSpeedLimit * 1.2; if (Train.CurrentRouteLimit < lim) { lim = Train.CurrentRouteLimit; } if (Train.CurrentSectionLimit < lim) { lim = Train.CurrentSectionLimit; } double powerstart, powerend, brakestart; if (double.IsPositiveInfinity(lim)) { powerstart = lim; powerend = lim; brakestart = lim; } else { lim *= this.CurrentSpeedFactor; if (spd < 8.0) { powerstart = 0.75 * lim; powerend = 0.95 * lim; } else { powerstart = lim - 2.5; powerend = lim - 1.5; } if (this.BrakeMode) { brakestart = powerend; } else { brakestart = lim + 0.5; } } double dec = 0.0; double decelerationCruise; /* power below this deceleration, cruise above */ double decelerationStart; /* brake above this deceleration, cruise below */ double decelerationStep; /* the deceleration step per brake notch */ double BrakeDeceleration = Train.Cars[Train.DriverCar].CarBrake.DecelerationAtServiceMaximumPressure(Train.Handles.Brake.Actual, Train.Cars[Train.DriverCar].Specs.CurrentSpeed); for (int i = 0; i < Train.Cars.Length; i++) { if (Train.Cars[i].Specs.IsMotorCar) { if (Train.Cars[Train.DriverCar].Specs.MotorDeceleration != 0 && Train.Cars[Train.DriverCar].Specs.MotorDeceleration < BrakeDeceleration) { BrakeDeceleration = Train.Cars[Train.DriverCar].Specs.MotorDeceleration; } break; } } if (Train.Handles.Brake is TrainManager.AirBrakeHandle | Train.Handles.Brake.MaximumNotch <= 0) { decelerationCruise = 0.3 * BrakeDeceleration; decelerationStart = 0.5 * BrakeDeceleration; decelerationStep = 0.1 * BrakeDeceleration; } else if (Train.Handles.Brake.MaximumNotch <= 2) { decelerationCruise = 0.2 * BrakeDeceleration; decelerationStart = 0.4 * BrakeDeceleration; decelerationStep = 0.5 * BrakeDeceleration; } else { decelerationCruise = 0.2 * BrakeDeceleration; decelerationStart = 0.5 * BrakeDeceleration; decelerationStep = BrakeDeceleration / (double)Train.Handles.Brake.MaximumNotch; } if (this.CurrentSpeedFactor >= 1.0) { decelerationCruise *= 1.25; decelerationStart *= 1.25; decelerationStep *= 1.25; } if (spd > 0.0 & spd > brakestart) { dec = decelerationStep + 0.1 * (spd - brakestart); } bool reduceDecelerationCruiseAndStart = false; // look ahead double lookahead = (Train.Station >= 0 ? 150.0 : 50.0) + (spd * spd) / (2.0 * decelerationCruise); double tp = Train.FrontCarTrackPosition(); double stopDistance = double.MaxValue; { // next station stop int te = Train.Cars[0].FrontAxle.Follower.LastTrackElement; for (int i = te; i < TrackManager.Tracks[0].Elements.Length; i++) { double stp = TrackManager.Tracks[0].Elements[i].StartingTrackPosition; if (tp + lookahead <= stp) { break; } for (int j = 0; j < TrackManager.Tracks[0].Elements[i].Events.Length; j++) { if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationStartEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.None) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > 0.0 & dist < stopDistance) { stopDistance = dist; } } } } } } } { // events int te = Train.Cars[0].FrontAxle.Follower.LastTrackElement; for (int i = te; i < TrackManager.Tracks[0].Elements.Length; i++) { double stp = TrackManager.Tracks[0].Elements[i].StartingTrackPosition; if (tp + lookahead <= stp) { break; } for (int j = 0; j < TrackManager.Tracks[0].Elements[i].Events.Length; j++) { if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.LimitChangeEvent) { // speed limit TrackManager.LimitChangeEvent e = (TrackManager.LimitChangeEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (e.NextSpeedLimit < spd) { double dist = stp + e.TrackPositionDelta - tp; double edec = (spd * spd - e.NextSpeedLimit * e.NextSpeedLimit * this.CurrentSpeedFactor) / (2.0 * dist); if (edec > dec) { dec = edec; } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.SectionChangeEvent) { // section TrackManager.SectionChangeEvent e = (TrackManager.SectionChangeEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (stp + e.TrackPositionDelta > tp) { if (!CurrentRoute.Sections[e.NextSectionIndex].Invisible & CurrentRoute.Sections[e.NextSectionIndex].CurrentAspect >= 0) { double elim = CurrentRoute.Sections[e.NextSectionIndex].Aspects[CurrentRoute.Sections[e.NextSectionIndex].CurrentAspect].Speed * this.CurrentSpeedFactor; if (elim < spd | spd <= 0.0) { double dist = stp + e.TrackPositionDelta - tp; double edec; if (elim == 0.0) { double redstopdist; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Completed & dist < 120.0) { dist = 1.0; redstopdist = 25.0; } else if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Pending | stopDistance < dist) { redstopdist = 1.0; } else if (spd > 9.72222222222222) { redstopdist = 55.0; } else { redstopdist = 35.0; } if (dist > redstopdist) { edec = (spd * spd) / (2.0 * (dist - redstopdist)); } else { edec = BrakeDeceleration; } if (dist < 100.0) { reduceDecelerationCruiseAndStart = true; } } else { if (dist >= 1.0) { edec = (spd * spd - elim * elim) / (2.0 * dist); } else { edec = 0.0; } } if (edec > dec) { dec = edec; } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationStartEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.None) { // station start if (Train.Station == -1) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationStartEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.Decelerate) { // Brakes the train when passing through a request stop, which is not to be passed at linespeed if (Train.Station == -1) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } if (dist > 25) { var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationEndEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.None) { // station end if (Train.Station == -1) { TrackManager.StationEndEvent e = (TrackManager.StationEndEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.TrackEndEvent) { // track end if (Train == TrainManager.PlayerTrain) { TrackManager.TrackEndEvent e = (TrackManager.TrackEndEvent)TrackManager.Tracks[0].Elements[i].Events[j]; double dist = stp + e.TrackPositionDelta - tp; double edec; if (dist >= 15.0) { edec = spd * spd / (2.0 * dist); } else { edec = BrakeDeceleration; } if (edec > dec) { dec = edec; } } } } } } // buffers ahead if (Train == TrainManager.PlayerTrain) { for (int i = 0; i < BufferTrackPositions.Length; i++) { double dist = BufferTrackPositions[i] - tp; if (dist > 0.0) { double edec; if (dist >= 10.0) { edec = spd * spd / (2.0 * dist); } else if (dist >= 5.0) { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); this.CurrentInterval = 0.1; return; } else { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); this.CurrentInterval = 10.0; return; } if (edec > dec) { dec = edec; } } } } // trains ahead for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != Train && TrainManager.Trains[i].State == TrainState.Available) { double pos = TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].RearAxle.Follower.TrackPosition - TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].RearAxle.Position - 0.5 * TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].Length; double dist = pos - tp; if (dist > -10.0 & dist < lookahead) { const double minDistance = 10.0; const double maxDistance = 100.0; double edec; if (dist > minDistance) { double shift = 0.75 * minDistance + 1.0 * spd; edec = spd * spd / (2.0 * (dist - shift)); } else if (dist > 0.5 * minDistance) { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); this.CurrentInterval = 0.1; return; } else { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); this.CurrentInterval = 1.0; return; } if (dist < maxDistance) { reduceDecelerationCruiseAndStart = true; } if (edec > dec) { dec = edec; } } } } Train.UnapplyEmergencyBrake(); // current station if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Pending) { if (StopsAtStation(Train.Station, Train)) { int s = Game.Stations[Train.Station].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[Train.Station].Stops[s].TrackPosition - tp; if (dist > 0.0) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } else { dec = BrakeDeceleration; } } } } // power / brake if (reduceDecelerationCruiseAndStart) { decelerationCruise *= 0.3; decelerationStart *= 0.3; } double brakeModeBrakeThreshold = 0.75 * decelerationStart + 0.25 * decelerationCruise; if (!BrakeMode & dec > decelerationStart | BrakeMode & dec > brakeModeBrakeThreshold | false) { // brake BrakeMode = true; double decdiff = -acc - dec; if (decdiff < -decelerationStep) { // brake start if (Train.Handles.Power.Driver == 0) { Train.ApplyNotch(0, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); } else { Train.ApplyNotch(-1, true, 0, true); } CurrentInterval *= 0.4; if (CurrentInterval < 0.3) { CurrentInterval = 0.3; } } else if (decdiff > decelerationStep) { // brake stop Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); CurrentInterval *= 0.4; if (CurrentInterval < 0.3) { CurrentInterval = 0.3; } } else { // keep brake Train.ApplyNotch(-1, true, 0, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Lap); CurrentInterval *= 1.2; if (CurrentInterval > 1.0) { CurrentInterval = 1.0; } } if (Train.Handles.Power.Driver == 0 & Train.Handles.Brake.Driver == 0) { Train.ApplyHoldBrake(Train.Handles.HasHoldBrake); } if (Train.Handles.Brake is TrainManager.AirBrakeHandle) { CurrentInterval = 0.1; } } else if (dec > decelerationCruise) { // cut power/brake BrakeMode = false; Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); if (Train.Handles.Power.Driver == 0 & Train.Handles.Brake.Driver == 0) { Train.ApplyHoldBrake(Train.Handles.HasHoldBrake); } CurrentInterval *= 0.4; if (CurrentInterval < 0.3) { CurrentInterval = 0.3; } } else { // power BrakeMode = false; double acclim; if (!double.IsInfinity(lim)) { double d = lim - spd; if (d > 0.0) { acclim = 0.1 / (0.1 * d + 1.0) - 0.12; } else { acclim = -1.0; } } else { acclim = -1.0; } if (spd < powerstart) { // power start (under-speed) if (Train.Handles.Brake.Driver == 0) { if (Train.Handles.Power.Driver < this.PowerNotchAtWhichWheelSlipIsObserved - 1) { Train.ApplyNotch(1, true, 0, true); } } else { Train.ApplyNotch(0, true, -1, true); } Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); if (double.IsPositiveInfinity(powerstart)) { CurrentInterval = 0.3 + 0.1 * Train.Handles.Power.Driver; } else { double p = (double)Train.Handles.Power.Driver / (double)Train.Handles.Power.MaximumNotch; CurrentInterval = 0.3 + 15.0 * p / (powerstart - spd + 1.0); } if (CurrentInterval > 1.3) { CurrentInterval = 1.3; } } else if (spd > powerend) { // power end (over-speed) Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); CurrentInterval *= 0.3; if (CurrentInterval < 0.2) { CurrentInterval = 0.2; } } else if (acc < acclim) { // power start (under-acceleration) if (Train.Handles.Brake.Driver == 0) { if (Train.Handles.Power.Driver < this.PowerNotchAtWhichWheelSlipIsObserved - 1) { if (Train.Handles.Power.Driver == Train.Handles.Power.Actual) { Train.ApplyNotch(1, true, 0, true); } } } else { Train.ApplyNotch(0, true, -1, true); } Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); CurrentInterval = 1.3; } else { // keep power Train.ApplyNotch(0, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); if (Train.Handles.Power.Driver != 0) { Train.Specs.CurrentConstSpeed = Train.Specs.HasConstSpeed; } if (Train.Handles.Power.Driver == 0 & Train.Handles.Brake.Driver == 0) { Train.ApplyHoldBrake(Train.Handles.HasHoldBrake); } CurrentInterval *= 1.1; if (CurrentInterval > 1.5) { CurrentInterval = 1.5; } } } } }
// execute function script private static void ExecuteFunctionScript(FunctionScript Function, TrainManager.Train Train, int CarIndex, World.Vector3D Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed) { int s = 0, c = 0; for (int i = 0; i < Function.Instructions.Length; i++) { switch (Function.Instructions[i]) { // system case Instructions.SystemHalt: i = Function.Instructions.Length; break; case Instructions.SystemConstant: Function.Stack[s] = Function.Constants[c]; s++; c++; break; case Instructions.SystemConstantArray: { int n = (int)Function.Instructions[i + 1]; for (int j = 0; j < n; j++) { Function.Stack[s + j] = Function.Constants[c + j]; } s += n; c += n; i++; } break; case Instructions.SystemValue: Function.Stack[s] = Function.LastResult; s++; break; case Instructions.SystemDelta: Function.Stack[s] = TimeElapsed; s++; break; // stack case Instructions.StackCopy: Function.Stack[s] = Function.Stack[s - 1]; s++; break; case Instructions.StackSwap: { double a = Function.Stack[s - 1]; Function.Stack[s - 1] = Function.Stack[s - 2]; Function.Stack[s - 2] = a; } break; // math case Instructions.MathPlus: Function.Stack[s - 2] += Function.Stack[s - 1]; s--; break; case Instructions.MathSubtract: Function.Stack[s - 2] -= Function.Stack[s - 1]; s--; break; case Instructions.MathMinus: Function.Stack[s - 1] = -Function.Stack[s - 1]; break; case Instructions.MathTimes: Function.Stack[s - 2] *= Function.Stack[s - 1]; s--; break; case Instructions.MathDivide: Function.Stack[s - 2] = Function.Stack[s - 1] == 0.0 ? 0.0 : Function.Stack[s - 2] / Function.Stack[s - 1]; s--; break; case Instructions.MathReciprocal: Function.Stack[s - 1] = Function.Stack[s - 1] == 0.0 ? 0.0 : 1.0 / Function.Stack[s - 1]; break; case Instructions.MathPower: { double a = Function.Stack[s - 2]; double b = Function.Stack[s - 1]; if (b == 2.0) { Function.Stack[s - 2] = a * a; } else if (b == 3.0) { Function.Stack[s - 2] = a * a * a; } else if (b == 4.0) { double t = a * a; Function.Stack[s - 2] = t * t; } else if (b == 5.0) { double t = a * a; Function.Stack[s - 2] = t * t * a; } else if (b == 6.0) { double t = a * a * a; Function.Stack[s - 2] = t * t; } else if (b == 7.0) { double t = a * a * a; Function.Stack[s - 2] = t * t * a; } else if (b == 8.0) { double t = a * a; t *= t; Function.Stack[s - 2] = t * t; } else if (b == 0.0) { Function.Stack[s - 2] = 1.0; } else if (b < 0.0) { Function.Stack[s - 2] = 0.0; } else { Function.Stack[s - 2] = Math.Pow(a, b); } s--; break; } case Instructions.MathIncrement: Function.Stack[s - 1] += 1.0; break; case Instructions.MathDecrement: Function.Stack[s - 1] -= 1.0; break; case Instructions.MathFusedMultiplyAdd: Function.Stack[s - 3] = Function.Stack[s - 3] * Function.Stack[s - 2] + Function.Stack[s - 1]; s -= 2; break; case Instructions.MathQuotient: Function.Stack[s - 2] = Function.Stack[s - 1] == 0.0 ? 0.0 : Math.Floor(Function.Stack[s - 2] / Function.Stack[s - 1]); s--; break; case Instructions.MathMod: Function.Stack[s - 2] = Function.Stack[s - 1] == 0.0 ? 0.0 : Function.Stack[s - 2] - Function.Stack[s - 1] * Math.Floor(Function.Stack[s - 2] / Function.Stack[s - 1]); s--; break; case Instructions.MathFloor: Function.Stack[s - 1] = Math.Floor(Function.Stack[s - 1]); break; case Instructions.MathCeiling: Function.Stack[s - 1] = Math.Ceiling(Function.Stack[s - 1]); break; case Instructions.MathRound: Function.Stack[s - 1] = Math.Round(Function.Stack[s - 1]); break; case Instructions.MathMin: Function.Stack[s - 2] = Function.Stack[s - 2] < Function.Stack[s - 1] ? Function.Stack[s - 2] : Function.Stack[s - 1]; s--; break; case Instructions.MathMax: Function.Stack[s - 2] = Function.Stack[s - 2] > Function.Stack[s - 1] ? Function.Stack[s - 2] : Function.Stack[s - 1]; s--; break; case Instructions.MathAbs: Function.Stack[s - 1] = Math.Abs(Function.Stack[s - 1]); break; case Instructions.MathSign: Function.Stack[s - 1] = Math.Sign(Function.Stack[s - 1]); break; case Instructions.MathExp: Function.Stack[s - 1] = Math.Exp(Function.Stack[s - 1]); break; case Instructions.MathLog: Function.Stack[s - 1] = Log(Function.Stack[s - 1]); break; case Instructions.MathSqrt: Function.Stack[s - 1] = Sqrt(Function.Stack[s - 1]); break; case Instructions.MathSin: Function.Stack[s - 1] = Math.Sin(Function.Stack[s - 1]); break; case Instructions.MathCos: Function.Stack[s - 1] = Math.Cos(Function.Stack[s - 1]); break; case Instructions.MathTan: Function.Stack[s - 1] = Tan(Function.Stack[s - 1]); break; case Instructions.MathArcTan: Function.Stack[s - 1] = Math.Atan(Function.Stack[s - 1]); break; // comparisons case Instructions.CompareEqual: Function.Stack[s - 2] = Function.Stack[s - 2] == Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareUnequal: Function.Stack[s - 2] = Function.Stack[s - 2] != Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareLess: Function.Stack[s - 2] = Function.Stack[s - 2] < Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareGreater: Function.Stack[s - 2] = Function.Stack[s - 2] > Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareLessEqual: Function.Stack[s - 2] = Function.Stack[s - 2] <= Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareGreaterEqual: Function.Stack[s - 2] = Function.Stack[s - 2] >= Function.Stack[s - 1] ? 1.0 : 0.0; s--; break; case Instructions.CompareConditional: Function.Stack[s - 3] = Function.Stack[s - 3] != 0.0 ? Function.Stack[s - 2] : Function.Stack[s - 1]; s -= 2; break; // logical case Instructions.LogicalNot: Function.Stack[s - 1] = Function.Stack[s - 1] != 0.0 ? 0.0 : 1.0; break; case Instructions.LogicalAnd: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 & Function.Stack[s - 1] != 0.0 ? 1.0 : 0.0; s--; break; case Instructions.LogicalOr: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 | Function.Stack[s - 1] != 0.0 ? 1.0 : 0.0; s--; break; case Instructions.LogicalNand: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 & Function.Stack[s - 1] != 0.0 ? 0.0 : 1.0; s--; break; case Instructions.LogicalNor: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 | Function.Stack[s - 1] != 0.0 ? 0.0 : 1.0; s--; break; case Instructions.LogicalXor: Function.Stack[s - 2] = Function.Stack[s - 2] != 0.0 ^ Function.Stack[s - 1] != 0.0 ? 1.0 : 0.0; s--; break; // time/camera case Instructions.TimeSecondsSinceMidnight: Function.Stack[s] = Game.SecondsSinceMidnight; s++; break; case Instructions.CameraDistance: { double dx = World.AbsoluteCameraPosition.X - Position.X; double dy = World.AbsoluteCameraPosition.Y - Position.Y; double dz = World.AbsoluteCameraPosition.Z - Position.Z; Function.Stack[s] = Math.Sqrt(dx * dx + dy * dy + dz * dz); s++; } break; // train case Instructions.TrainCars: if (Train != null) { Function.Stack[s] = (double)Train.Cars.Length; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainSpeed: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.CurrentSpeed; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainSpeedOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentSpeed; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainSpeedometer: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.CurrentPerceivedSpeed; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainSpeedometerOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentPerceivedSpeed; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainAcceleration: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.CurrentAcceleration; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainAccelerationOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentAcceleration; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainAccelerationMotor: if (Train != null) { Function.Stack[s] = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.IsMotorCar) { // hack: CurrentAccelerationOutput does not distinguish between forward/backward if (Train.Cars[j].Specs.CurrentAccelerationOutput < 0.0) { Function.Stack[s] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Math.Sign(Train.Cars[j].Specs.CurrentSpeed); } else if (Train.Cars[j].Specs.CurrentAccelerationOutput > 0.0) { Function.Stack[s] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Train.Specs.CurrentReverser.Actual; } else { Function.Stack[s] = 0.0; } break; } } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainAccelerationMotorOfCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { // hack: CurrentAccelerationOutput does not distinguish between forward/backward if (Train.Cars[j].Specs.CurrentAccelerationOutput < 0.0) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Math.Sign(Train.Cars[j].Specs.CurrentSpeed); } else if (Train.Cars[j].Specs.CurrentAccelerationOutput > 0.0) { Function.Stack[s - 1] = Train.Cars[j].Specs.CurrentAccelerationOutput * (double)Train.Specs.CurrentReverser.Actual; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainDistance: if (Train != null) { double dist = double.MaxValue; for (int j = 0; j < Train.Cars.Length; j++) { double fx = Train.Cars[j].FrontAxle.Follower.WorldPosition.X - Position.X; double fy = Train.Cars[j].FrontAxle.Follower.WorldPosition.Y - Position.Y; double fz = Train.Cars[j].FrontAxle.Follower.WorldPosition.Z - Position.Z; double f = fx * fx + fy * fy + fz * fz; if (f < dist) dist = f; double rx = Train.Cars[j].RearAxle.Follower.WorldPosition.X - Position.X; double ry = Train.Cars[j].RearAxle.Follower.WorldPosition.Y - Position.Y; double rz = Train.Cars[j].RearAxle.Follower.WorldPosition.Z - Position.Z; double r = rx * rx + ry * ry + rz * rz; if (r < dist) dist = r; } Function.Stack[s] = Math.Sqrt(dist); } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainDistanceToCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { double x = 0.5 * (Train.Cars[j].FrontAxle.Follower.WorldPosition.X + Train.Cars[j].RearAxle.Follower.WorldPosition.X) - Position.X; double y = 0.5 * (Train.Cars[j].FrontAxle.Follower.WorldPosition.Y + Train.Cars[j].RearAxle.Follower.WorldPosition.Y) - Position.Y; double z = 0.5 * (Train.Cars[j].FrontAxle.Follower.WorldPosition.Z + Train.Cars[j].RearAxle.Follower.WorldPosition.Z) - Position.Z; Function.Stack[s - 1] = Math.Sqrt(x * x + y * y + z * z); } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.TrainTrackDistance: if (Train != null) { int r = Train.Cars.Length - 1; double t0 = Train.Cars[0].FrontAxle.Follower.TrackPosition - Train.Cars[0].FrontAxlePosition + 0.5 * Train.Cars[0].Length; double t1 = Train.Cars[r].RearAxle.Follower.TrackPosition - Train.Cars[r].RearAxlePosition - 0.5 * Train.Cars[r].Length; Function.Stack[s] = TrackPosition > t0 ? TrackPosition - t0 : TrackPosition < t1 ? TrackPosition - t1 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.TrainTrackDistanceToCar: if (Train != null) { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { double p = 0.5 * (Train.Cars[j].FrontAxle.Follower.TrackPosition + Train.Cars[j].RearAxle.Follower.TrackPosition); Function.Stack[s - 1] = TrackPosition - p; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; } break; // door case Instructions.Doors: if (Train != null) { double a = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s] = a; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.DoorsIndex: if (Train != null) { double a = 0.0; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s - 1] = a; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.LeftDoors: if (Train != null) { double a = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == -1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s] = a; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.LeftDoorsIndex: if (Train != null) { double a = 0.0; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == -1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s - 1] = a; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.RightDoors: if (Train != null) { double a = 0.0; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == 1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s] = a; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.RightDoorsIndex: if (Train != null) { double a = 0.0; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.Doors[k].Direction == 1 & Train.Cars[j].Specs.Doors[k].State > a) { a = Train.Cars[j].Specs.Doors[k].State; } } } Function.Stack[s - 1] = a; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.LeftDoorsTarget: if (Train != null) { bool q = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.AnticipatedLeftDoorsOpened) { q = true; break; } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.LeftDoorsTargetIndex: if (Train != null) { bool q = false; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.AnticipatedLeftDoorsOpened) { q = true; break; } } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s - 1] = 0.0; } break; case Instructions.RightDoorsTarget: if (Train != null) { bool q = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Specs.AnticipatedRightDoorsOpened) { q = true; break; } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.RightDoorsTargetIndex: if (Train != null) { bool q = false; int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { for (int k = 0; k < Train.Cars[j].Specs.Doors.Length; k++) { if (Train.Cars[j].Specs.AnticipatedRightDoorsOpened) { q = true; break; } } } Function.Stack[s] = q ? 1.0 : 0.0; } else { Function.Stack[s - 1] = 0.0; } break; // handles case Instructions.ReverserNotch: if (Train != null) { Function.Stack[s] = (double)Train.Specs.CurrentReverser.Driver; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.PowerNotch: if (Train != null) { Function.Stack[s] = (double)Train.Specs.CurrentPowerNotch.Driver; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.PowerNotches: if (Train != null) { Function.Stack[s] = (double)Train.Specs.MaximumPowerNotch; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotch: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Function.Stack[s] = (double)Train.Specs.AirBrake.Handle.Driver; } else { Function.Stack[s] = (double)Train.Specs.CurrentBrakeNotch.Driver; } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotches: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Function.Stack[s] = 2.0; } else { Function.Stack[s] = (double)Train.Specs.MaximumBrakeNotch; } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotchLinear: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { if (Train.Specs.CurrentEmergencyBrake.Driver) { Function.Stack[s] = 3.0; } else { Function.Stack[s] = (double)Train.Specs.AirBrake.Handle.Driver; } } else if (Train.Specs.HasHoldBrake) { if (Train.Specs.CurrentEmergencyBrake.Driver) { Function.Stack[s] = (double)Train.Specs.MaximumBrakeNotch + 2.0; } else if (Train.Specs.CurrentBrakeNotch.Driver > 0) { Function.Stack[s] = (double)Train.Specs.CurrentBrakeNotch.Driver + 1.0; } else { Function.Stack[s] = Train.Specs.CurrentHoldBrake.Driver ? 1.0 : 0.0; } } else { if (Train.Specs.CurrentEmergencyBrake.Driver) { Function.Stack[s] = (double)Train.Specs.MaximumBrakeNotch + 1.0; } else { Function.Stack[s] = (double)Train.Specs.CurrentBrakeNotch.Driver; } } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeNotchesLinear: if (Train != null) { if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { Function.Stack[s] = 3.0; } else if (Train.Specs.HasHoldBrake) { Function.Stack[s] = Train.Specs.MaximumBrakeNotch + 2.0; } else { Function.Stack[s] = Train.Specs.MaximumBrakeNotch + 1.0; } } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.EmergencyBrake: if (Train != null) { Function.Stack[s] = Train.Specs.CurrentEmergencyBrake.Driver ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HasAirBrake: if (Train != null) { Function.Stack[s] = Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HoldBrake: if (Train != null) { Function.Stack[s] = Train.Specs.CurrentHoldBrake.Driver ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HasHoldBrake: if (Train != null) { Function.Stack[s] = Train.Specs.HasHoldBrake ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.ConstSpeed: if (Train != null) { Function.Stack[s] = Train.Specs.CurrentConstSpeed ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.HasConstSpeed: if (Train != null) { Function.Stack[s] = Train.Specs.HasConstSpeed ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; // brake case Instructions.BrakeMainReservoir: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.MainReservoirCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeMainReservoirOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.MainReservoirCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeEqualizingReservoir: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.EqualizingReservoirCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeEqualizingReservoirOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.EqualizingReservoirCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeBrakePipe: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.BrakePipeCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeBrakePipeOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.BrakePipeCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeBrakeCylinder: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.BrakeCylinderCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeBrakeCylinderOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.BrakeCylinderCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; case Instructions.BrakeStraightAirPipe: if (Train != null) { Function.Stack[s] = Train.Cars[CarIndex].Specs.AirBrake.StraightAirPipeCurrentPressure; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.BrakeStraightAirPipeOfCar: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int j = (int)Math.Round(Function.Stack[s - 1]); if (j < 0) j += Train.Cars.Length; if (j >= 0 & j < Train.Cars.Length) { Function.Stack[s - 1] = Train.Cars[j].Specs.AirBrake.StraightAirPipeCurrentPressure; } else { Function.Stack[s - 1] = 0.0; } } break; // safety case Instructions.SafetyPluginAvailable: if (Train == TrainManager.PlayerTrain) { Function.Stack[s] = TrainManager.PlayerTrain.Specs.Safety.Mode == TrainManager.SafetySystem.Plugin ? 1.0 : 0.0; } else { Function.Stack[s] = 0.0; } s++; break; case Instructions.SafetyPluginState: if (Train == null) { Function.Stack[s - 1] = 0.0; } else { int n = (int)Math.Round(Function.Stack[s - 1]); if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.Plugin) { if (n >= 0 & n < PluginManager.CurrentPlugin.Panel.Length) { Function.Stack[s - 1] = (double)PluginManager.CurrentPlugin.Panel[n]; } else { Function.Stack[s - 1] = 0.0; } } else { Function.Stack[s - 1] = 0.0; switch(n) { case 256: // ATS if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsSn) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Normal | Train.Specs.Safety.State == TrainManager.SafetyState.Initialization) { Function.Stack[s - 1] = 1.0; } } break; case 257: // ATS RUN (separate flashing) if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsSn) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Ringing) { Function.Stack[s - 1] = 1.0; } else if (Train.Specs.Safety.State == TrainManager.SafetyState.Emergency | Train.Specs.Safety.State == TrainManager.SafetyState.Pattern | Train.Specs.Safety.State == TrainManager.SafetyState.Service) { Function.Stack[s - 1] = 2.0; } } break; case 258: // ATS RUN (integrated flashing) if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsSn) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Ringing) { Function.Stack[s - 1] = 1.0; } else if (Train.Specs.Safety.State == TrainManager.SafetyState.Emergency | Train.Specs.Safety.State == TrainManager.SafetyState.Pattern | Train.Specs.Safety.State == TrainManager.SafetyState.Service) { if (((int)Math.Floor(2.0 * Game.SecondsSinceMidnight) & 1) == 0) { Function.Stack[s - 1] = 1.0; } else { Function.Stack[s - 1] = 0.0; } } } break; case 259: // P POWER if ((Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsSn | Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsP) & Train.Specs.Safety.Ats.AtsPAvailable) { Function.Stack[s - 1] = 1.0; } break; case 260: // PATTERN APPROACH if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsP) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Pattern | Train.Specs.Safety.State == TrainManager.SafetyState.Service) { Function.Stack[s - 1] = 1.0; } } break; case 261: // BRAKE RELEASE if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsP) { if (Train.Specs.Safety.Ats.AtsPOverride) { Function.Stack[s - 1] = 1.0; } } break; case 262: // BRAKE OPERATION if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsP) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Service & !Train.Specs.Safety.Ats.AtsPOverride) { Function.Stack[s - 1] = 1.0; } } break; case 263: // ATS-P if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsP) { Function.Stack[s - 1] = 1.0; } break; case 264: // FAILURE if (Train.Specs.Safety.Mode != TrainManager.SafetySystem.None) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Initialization) { Function.Stack[s - 1] = 1.0; } else if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.AtsP) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Ringing | Train.Specs.Safety.State == TrainManager.SafetyState.Emergency) { Function.Stack[s - 1] = 1.0; } } } break; case 265: // ATC if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.Atc) { Function.Stack[s - 1] = 1.0; } break; case 266: // ATC POWER if ((Train.Specs.Safety.Mode == TrainManager.SafetySystem.Atc | Train.Specs.Safety.Mode != TrainManager.SafetySystem.None & Train.Specs.Safety.Atc.AutomaticSwitch)) { Function.Stack[s - 1] = 1.0; } break; case 267: // ATC SERVICE if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.Atc) { if (Train.Specs.Safety.State == TrainManager.SafetyState.Service) { Function.Stack[s - 1] = 1.0; } } break; case 268: // ATC EMERGENCY if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.Atc) { if (!Train.Specs.Safety.Atc.Transmitting) { Function.Stack[s - 1] = 1.0; } } break; case 269: // EB if (Train.Specs.Safety.Mode != TrainManager.SafetySystem.None) { if (Train.Specs.Safety.Eb.BellState == TrainManager.SafetyState.Ringing) { Function.Stack[s - 1] = 1.0; } } break; case 270: // CONST SPEED if (Train.Specs.HasConstSpeed) { if (Train.Specs.CurrentConstSpeed) { Function.Stack[s - 1] = 1.0; } } break; case 271: // atc speedometer state if (Train.Specs.Safety.Mode == TrainManager.SafetySystem.Atc) { if (!Train.Specs.Safety.Atc.Transmitting) { Function.Stack[s - 1] = 0.0; } else { if (Train.Specs.Safety.Atc.SpeedRestriction < 4.1666) { Function.Stack[s - 1] = 1.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 6.9443) { Function.Stack[s - 1] = 2.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 12.4999) { Function.Stack[s - 1] = 3.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 15.2777) { Function.Stack[s - 1] = 4.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 18.0555) { Function.Stack[s - 1] = 5.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 20.8333) { Function.Stack[s - 1] = 6.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 24.9999) { Function.Stack[s - 1] = 7.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 27.7777) { Function.Stack[s - 1] = 8.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 30.5555) { Function.Stack[s - 1] = 9.0; } else if (Train.Specs.Safety.Atc.SpeedRestriction < 33.3333) { Function.Stack[s - 1] = 10.0; } else { Function.Stack[s - 1] = 11.0; } } } else { Function.Stack[s - 1] = 12.0; } break; } } } break; // timetable case Instructions.TimetableVisible: Function.Stack[s] = Timetable.CurrentTimetable == Timetable.TimetableState.Custom & Timetable.CustomTimetableAvailable ? 0.0 : -1.0; s++; break; // sections case Instructions.SectionAspectNumber: if (IsPartOfTrain) { int nextSectionIndex = Train.CurrentSectionIndex + 1; if (nextSectionIndex >= 0 & nextSectionIndex < Game.Sections.Length) { int a = Game.Sections[nextSectionIndex].CurrentAspect; if (a >= 0 & a < Game.Sections[nextSectionIndex].Aspects.Length) { Function.Stack[s] = (double)Game.Sections[nextSectionIndex].Aspects[a].Number; } else { Function.Stack[s] = 0; } } } else if (SectionIndex >= 0 & SectionIndex < Game.Sections.Length) { int a = Game.Sections[SectionIndex].CurrentAspect; if (a >= 0 & a < Game.Sections[SectionIndex].Aspects.Length) { Function.Stack[s] = (double)Game.Sections[SectionIndex].Aspects[a].Number; } else { Function.Stack[s] = 0; } } else { Function.Stack[s] = 0; } s++; break; // default default: throw new System.InvalidOperationException("The unknown instruction " + Function.Instructions[i].ToString() + " was encountered in ExecuteFunctionScript."); } } Function.LastResult = Function.Stack[s - 1]; }
private static void Main(string[] args) { // Add handler for UI thread exceptions Application.ThreadException += (CrashHandler.UIThreadException); // Force all WinForms errors to go through handler Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); // This handler is for catching non-UI thread exceptions AppDomain.CurrentDomain.UnhandledException += (CrashHandler.CurrentDomain_UnhandledException); //Determine the current CPU architecture- //ARM will generally only support OpenGL-ES PortableExecutableKinds peKind; typeof(object).Module.GetPEKind(out peKind, out CurrentCPUArchitecture); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); CurrentHost = new Host(); if (IntPtr.Size == 4) { Joysticks = new JoystickManager32(); } else { Joysticks = new JoystickManager64(); } try { FileSystem = FileSystem.FromCommandLineArgs(args, CurrentHost); FileSystem.CreateFileSystem(); } catch (Exception ex) { MessageBox.Show(Translations.GetInterfaceString("errors_filesystem_invalid") + Environment.NewLine + Environment.NewLine + ex.Message, Translations.GetInterfaceString("program_title"), MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } Renderer = new NewRenderer(); Sounds = new Sounds(); CurrentRoute = new CurrentRoute(CurrentHost, Renderer); //Platform specific startup checks // --- Check if we're running as root, and prompt not to --- if (CurrentHost.Platform == HostPlatform.GNULinux && getuid() == 0) { MessageBox.Show( "You are currently running as the root user." + System.Environment.NewLine + "This is a bad idea, please dont!", Translations.GetInterfaceString("program_title"), MessageBoxButtons.OK, MessageBoxIcon.Hand); } // --- load options and controls --- try { Interface.LoadOptions(); } catch { // ignored } TrainManager = new TrainManager(CurrentHost, Renderer, Interface.CurrentOptions, FileSystem); //Switch between SDL2 and native backends; use native backend by default var options = new ToolkitOptions(); if (Interface.CurrentOptions.PreferNativeBackend) { options.Backend = PlatformBackend.PreferNative; } Toolkit.Init(options); // --- load language --- string folder = Program.FileSystem.GetDataFolder("Languages"); Translations.LoadLanguageFiles(folder); folder = Program.FileSystem.GetDataFolder("Cursors"); Cursors.LoadCursorImages(folder); Interface.LoadControls(null, out Interface.CurrentControls); folder = Program.FileSystem.GetDataFolder("Controls"); string file = OpenBveApi.Path.CombineFile(folder, "Default keyboard assignment.controls"); Control[] controls; Interface.LoadControls(file, out controls); Interface.AddControls(ref Interface.CurrentControls, controls); InputDevicePlugin.LoadPlugins(Program.FileSystem); // --- check the command-line arguments for route and train --- formMain.MainDialogResult result = new formMain.MainDialogResult(); CommandLine.ParseArguments(args, ref result); // --- check whether route and train exist --- if (result.RouteFile != null) { if (!System.IO.File.Exists(result.RouteFile)) { result.RouteFile = null; } } if (result.TrainFolder != null) { if (!System.IO.Directory.Exists(result.TrainFolder)) { result.TrainFolder = null; } } // --- if a route was provided but no train, try to use the route default --- if (result.RouteFile != null & result.TrainFolder == null) { string error; if (!CurrentHost.LoadPlugins(FileSystem, Interface.CurrentOptions, out error, TrainManager, Renderer)) { MessageBox.Show(error, @"OpenBVE", MessageBoxButtons.OK, MessageBoxIcon.Error); throw new Exception("Unable to load the required plugins- Please reinstall OpenBVE"); } Game.Reset(false); bool loaded = false; for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) { if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(result.RouteFile)) { object Route = (object)Program.CurrentRoute; //must cast to allow us to use the ref keyword. Program.CurrentHost.Plugins[i].Route.LoadRoute(result.RouteFile, result.RouteEncoding, null, null, null, true, ref Route); Program.CurrentRoute = (CurrentRoute)Route; Program.Renderer.Lighting.OptionAmbientColor = CurrentRoute.Atmosphere.AmbientLightColor; Program.Renderer.Lighting.OptionDiffuseColor = CurrentRoute.Atmosphere.DiffuseLightColor; Program.Renderer.Lighting.OptionLightPosition = CurrentRoute.Atmosphere.LightPosition; loaded = true; break; } } if (!CurrentHost.UnloadPlugins(out error)) { MessageBox.Show(error, @"OpenBVE", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (!loaded) { throw new Exception("No plugins capable of loading routefile " + result.RouteFile + " were found."); } if (!string.IsNullOrEmpty(Interface.CurrentOptions.TrainName)) { folder = System.IO.Path.GetDirectoryName(result.RouteFile); while (true) { string trainFolder = OpenBveApi.Path.CombineDirectory(folder, "Train"); if (System.IO.Directory.Exists(trainFolder)) { try { folder = OpenBveApi.Path.CombineDirectory(trainFolder, Interface.CurrentOptions.TrainName); } catch (Exception ex) { if (ex is ArgumentException) { break; } } if (System.IO.Directory.Exists(folder)) { file = OpenBveApi.Path.CombineFile(folder, "train.dat"); if (System.IO.File.Exists(file)) { result.TrainFolder = folder; result.TrainEncoding = System.Text.Encoding.UTF8; for (int j = 0; j < Interface.CurrentOptions.TrainEncodings.Length; j++) { if (string.Compare(Interface.CurrentOptions.TrainEncodings[j].Value, result.TrainFolder, StringComparison.InvariantCultureIgnoreCase) == 0) { result.TrainEncoding = System.Text.Encoding.GetEncoding(Interface.CurrentOptions.TrainEncodings[j].Codepage); break; } } } } break; } if (folder == null) { continue; } System.IO.DirectoryInfo info = System.IO.Directory.GetParent(folder); if (info != null) { folder = info.FullName; } else { break; } } } Game.Reset(false); } // --- show the main menu if necessary --- if (result.RouteFile == null | result.TrainFolder == null) { Joysticks.RefreshJoysticks(); // end HACK // result = formMain.ShowMainDialog(result); } else { result.Start = true; //Apply translations Translations.SetInGameLanguage(Translations.CurrentLanguageCode); } // --- start the actual program --- if (result.Start) { if (Initialize()) { #if !DEBUG try { #endif MainLoop.StartLoopEx(result); #if !DEBUG } catch (Exception ex) { bool found = false; for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != null && TrainManager.Trains[i].Plugin != null) { if (TrainManager.Trains[i].Plugin.LastException != null) { CrashHandler.LoadingCrash(ex.Message, true); MessageBox.Show("The train plugin " + TrainManager.Trains[i].Plugin.PluginTitle + " caused a runtime exception: " + TrainManager.Trains[i].Plugin.LastException.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); found = true; RestartArguments = ""; break; } } } if (!found) { if (ex is System.DllNotFoundException) { Interface.AddMessage(MessageType.Critical, false, "The required system library " + ex.Message + " was not found on the system."); switch (ex.Message) { case "libopenal.so.1": MessageBox.Show("openAL was not found on this system. \n Please install libopenal1 via your distribtion's package management system.", Translations.GetInterfaceString("program_title"), MessageBoxButtons.OK, MessageBoxIcon.Hand); break; default: MessageBox.Show("The required system library " + ex.Message + " was not found on this system.", Translations.GetInterfaceString("program_title"), MessageBoxButtons.OK, MessageBoxIcon.Hand); break; } } else { Interface.AddMessage(MessageType.Critical, false, "The route and train loader encountered the following critical error: " + ex.Message); CrashHandler.LoadingCrash(ex + Environment.StackTrace, false); } RestartArguments = ""; } } #endif } Deinitialize(); } // --- restart the program if necessary --- if (RestartArguments != null) { string arguments; if (FileSystem.RestartArguments.Length != 0 & RestartArguments.Length != 0) { arguments = FileSystem.RestartArguments + " " + RestartArguments; } else { arguments = FileSystem.RestartArguments + RestartArguments; } try { System.Diagnostics.Process.Start(System.IO.File.Exists(FileSystem.RestartProcess) ? FileSystem.RestartProcess : Application.ExecutablePath, arguments); } catch (Exception ex) { MessageBox.Show(ex.Message + "\n\nProcess = " + FileSystem.RestartProcess + "\nArguments = " + arguments, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } }