public override void AddMessage(object Message) { MessageManager.AddMessage((AbstractMessage)Message); }
protected override void OnUpdateFrame(FrameEventArgs e) { TimeFactor = MainLoop.TimeFactor; // timer double RealTimeElapsed; double TimeElapsed; if (Program.CurrentRoute.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 - Program.CurrentRoute.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 += RealTimeElapsed; TotalTimeElapsedForSectionUpdate += TimeElapsed; if (TotalTimeElapsedForSectionUpdate >= 1.0) { if (Program.CurrentRoute.Sections.Length != 0) { Program.CurrentRoute.UpdateAllSections(); } TotalTimeElapsedForSectionUpdate = 0.0; } // events // update simulation in chunks { const double chunkTime = 1.0 / 2.0; if (TimeElapsed <= chunkTime) { Program.CurrentRoute.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++) { Program.CurrentRoute.SecondsSinceMidnight += time; TrainManager.UpdateTrains(time); } } } Game.CurrentScore.Update(TimeElapsed); MessageManager.UpdateMessages(); Game.UpdateScoreMessages(TimeElapsed); for (int i = 0; i < InputDevicePlugin.AvailablePluginInfos.Count; i++) { if (InputDevicePlugin.AvailablePluginInfos[i].Status == InputDevicePlugin.PluginInfo.PluginStatus.Enable) { InputDevicePlugin.AvailablePlugins[i].OnUpdateFrame(); } } } RenderTimeElapsed += TimeElapsed; RenderRealTimeElapsed += RealTimeElapsed; }
/// <summary>Updates the physics and controls for this train</summary> /// <param name="TimeElapsed">The time elapsed</param> private void UpdatePhysicsAndControls(double TimeElapsed) { if (TimeElapsed == 0.0 || TimeElapsed > 1000) { //HACK: The physics engine really does not like update times above 1000ms //This works around a bug experienced when jumping to a station on a steep hill //causing exessive acceleration return; } // move cars for (int i = 0; i < Cars.Length; i++) { Cars[i].Move(Cars[i].CurrentSpeed * TimeElapsed); if (State == TrainState.Disposed) { return; } } // update station and doors UpdateStation(TimeElapsed); UpdateDoors(TimeElapsed); // delayed handles if (Plugin == null) { Handles.Power.Safety = Handles.Power.Driver; Handles.Brake.Safety = Handles.Brake.Driver; Handles.EmergencyBrake.Safety = Handles.EmergencyBrake.Driver; } Handles.Power.Update(); Handles.Brake.Update(); Handles.Brake.Update(); Handles.EmergencyBrake.Update(); Handles.HoldBrake.Actual = Handles.HoldBrake.Driver; // update speeds UpdateSpeeds(TimeElapsed); // Update Run and Motor sounds for (int i = 0; i < Cars.Length; i++) { Cars[i].UpdateRunSounds(TimeElapsed); Cars[i].UpdateMotorSounds(TimeElapsed); } // safety system if (!Game.MinimalisticSimulation | !IsPlayerTrain) { UpdateSafetySystem(); } { // breaker sound bool breaker; if (Cars[DriverCar].CarBrake is AutomaticAirBrake) { breaker = Handles.Reverser.Actual != 0 & Handles.Power.Safety >= 1 & Handles.Brake.Safety == (int)AirBrakeHandleState.Release & !Handles.EmergencyBrake.Safety & !Handles.HoldBrake.Actual; } else { breaker = Handles.Reverser.Actual != 0 & Handles.Power.Safety >= 1 & Handles.Brake.Safety == 0 & !Handles.EmergencyBrake.Safety & !Handles.HoldBrake.Actual; } Cars[DriverCar].Breaker.Update(breaker); } // passengers Passengers.Update(Specs.CurrentAverageAcceleration, TimeElapsed); // signals if (CurrentSectionLimit == 0.0) { if (Handles.EmergencyBrake.Driver & CurrentSpeed > -0.03 & CurrentSpeed < 0.03) { CurrentSectionLimit = 6.94444444444444; if (IsPlayerTrain) { string s = Translations.GetInterfaceString("message_signal_proceed"); double a = (3.6 * CurrentSectionLimit) * Interface.CurrentOptions.SpeedConversionFactor; s = s.Replace("[speed]", a.ToString("0", CultureInfo.InvariantCulture)); s = s.Replace("[unit]", Game.UnitOfSpeed); MessageManager.AddMessage(s, MessageDependency.None, GameMode.Normal, MessageColor.Red, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } } } // infrequent updates InternalTimerTimeElapsed += TimeElapsed; if (InternalTimerTimeElapsed > 10.0) { InternalTimerTimeElapsed -= 10.0; Synchronize(); } }
/// <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(); } lock (BaseRenderer.GdiPlusLock) { Timetable.CreateTimetable(); } //Check if any critical errors have occured during the route or train loading for (int i = 0; i < Interface.LogMessages.Count; i++) { if (Interface.LogMessages[i].Type == MessageType.Critical) { MessageBox.Show("A critical error has occured:\n\n" + Interface.LogMessages[i].Text + "\n\nPlease inspect the error log file for further information.", "Load", MessageBoxButtons.OK, MessageBoxIcon.Hand); Close(); } } Program.Renderer.Lighting.Initialize(); 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 { Program.Renderer.TextureManager.UnloadAllTextures(); } // camera Program.Renderer.InitializeVisibility(); TrainManager.PlayerTrain.DriverBody = new DriverBody(TrainManager.PlayerTrain); Program.Renderer.CameraTrackFollower.UpdateAbsolute(0.0, true, false); Program.Renderer.CameraTrackFollower.UpdateAbsolute(-0.1, true, false); Program.Renderer.CameraTrackFollower.UpdateAbsolute(0.1, true, false); Program.Renderer.CameraTrackFollower.TriggerType = EventTriggerType.Camera; // starting time and track position Program.CurrentRoute.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; int PlayerFirstStationIndex = -1; double PlayerFirstStationPosition; int os = -1; bool f = false; for (int i = 0; i < Program.CurrentRoute.Stations.Length; i++) { if (!String.IsNullOrEmpty(Program.CurrentRoute.InitialStationName)) { if (Program.CurrentRoute.InitialStationName.ToLowerInvariant() == Program.CurrentRoute.Stations[i].Name.ToLowerInvariant()) { PlayerFirstStationIndex = i; } } if (Program.CurrentRoute.Stations[i].StopMode == StationStopMode.AllStop | Program.CurrentRoute.Stations[i].StopMode == StationStopMode.PlayerStop & Program.CurrentRoute.Stations[i].Stops.Length != 0) { if (f == false) { os = i; f = true; } } } if (PlayerFirstStationIndex == -1) { if (os == -1) { PlayerFirstStationIndex = 0; } else { PlayerFirstStationIndex = os; } } { int s = Program.CurrentRoute.Stations[PlayerFirstStationIndex].GetStopIndex(TrainManager.PlayerTrain.NumberOfCars); if (s >= 0) { PlayerFirstStationPosition = Program.CurrentRoute.Stations[PlayerFirstStationIndex].Stops[s].TrackPosition; double TrainLength = 0.0; for (int c = 0; c < TrainManager.PlayerTrain.Cars.Length; c++) { TrainLength += TrainManager.PlayerTrain.Cars[c].Length; } for (int j = 0; j < Program.CurrentRoute.BufferTrackPositions.Length; j++) { if (PlayerFirstStationPosition > Program.CurrentRoute.BufferTrackPositions[j] && PlayerFirstStationPosition - TrainLength < Program.CurrentRoute.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 = Program.CurrentRoute.BufferTrackPositions[j] + TrainLength + 1; //Update the station stop location if (s >= 0) { Program.CurrentRoute.Stations[PlayerFirstStationIndex].Stops[s].TrackPosition = PlayerFirstStationPosition; } else { Program.CurrentRoute.Stations[PlayerFirstStationIndex].DefaultTrackPosition = PlayerFirstStationPosition; } break; } } } else { PlayerFirstStationPosition = Program.CurrentRoute.Stations[PlayerFirstStationIndex].DefaultTrackPosition; } if (Program.CurrentRoute.InitialStationTime != -1) { Program.CurrentRoute.SecondsSinceMidnight = Program.CurrentRoute.InitialStationTime; Game.StartupTime = Program.CurrentRoute.InitialStationTime; } else { if (Program.CurrentRoute.Stations[PlayerFirstStationIndex].ArrivalTime < 0.0) { if (Program.CurrentRoute.Stations[PlayerFirstStationIndex].DepartureTime < 0.0) { Program.CurrentRoute.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; } else { Program.CurrentRoute.SecondsSinceMidnight = Program.CurrentRoute.Stations[PlayerFirstStationIndex].DepartureTime - Program.CurrentRoute.Stations[PlayerFirstStationIndex].StopTime; Game.StartupTime = Program.CurrentRoute.Stations[PlayerFirstStationIndex].DepartureTime - Program.CurrentRoute.Stations[PlayerFirstStationIndex].StopTime; } } else { Program.CurrentRoute.SecondsSinceMidnight = Program.CurrentRoute.Stations[PlayerFirstStationIndex].ArrivalTime; Game.StartupTime = Program.CurrentRoute.Stations[PlayerFirstStationIndex].ArrivalTime; } } } int OtherFirstStationIndex = -1; double OtherFirstStationPosition = 0.0; double OtherFirstStationTime = 0.0; for (int i = 0; i < Program.CurrentRoute.Stations.Length; i++) { if (Program.CurrentRoute.Stations[i].StopMode == StationStopMode.AllStop | Program.CurrentRoute.Stations[i].StopMode == StationStopMode.PlayerPass & Program.CurrentRoute.Stations[i].Stops.Length != 0) { OtherFirstStationIndex = i; int s = Program.CurrentRoute.Stations[i].GetStopIndex(TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { OtherFirstStationPosition = Program.CurrentRoute.Stations[i].Stops[s].TrackPosition; } else { OtherFirstStationPosition = Program.CurrentRoute.Stations[i].DefaultTrackPosition; } if (Program.CurrentRoute.Stations[i].ArrivalTime < 0.0) { if (Program.CurrentRoute.Stations[i].DepartureTime < 0.0) { OtherFirstStationTime = 0.0; } else { OtherFirstStationTime = Program.CurrentRoute.Stations[i].DepartureTime - Program.CurrentRoute.Stations[i].StopTime; } } else { OtherFirstStationTime = Program.CurrentRoute.Stations[i].ArrivalTime; } break; } } if (Program.CurrentRoute.PrecedingTrainTimeDeltas.Length != 0) { OtherFirstStationTime -= Program.CurrentRoute.PrecedingTrainTimeDeltas[Program.CurrentRoute.PrecedingTrainTimeDeltas.Length - 1]; if (OtherFirstStationTime < Program.CurrentRoute.SecondsSinceMidnight) { Program.CurrentRoute.SecondsSinceMidnight = OtherFirstStationTime; } } // initialize trains for (int i = 0; i < TrainManager.Trains.Length; i++) { TrainManager.Trains[i].Initialize(); int s = TrainManager.Trains[i].IsPlayerTrain ? PlayerFirstStationIndex : OtherFirstStationIndex; if (s >= 0) { if (Program.CurrentRoute.Stations[s].OpenLeftDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Doors[0].AnticipatedOpen = true; } } if (Program.CurrentRoute.Stations[s].OpenRightDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Doors[1].AnticipatedOpen = true; } } } if (Program.CurrentRoute.Sections.Length != 0) { Program.CurrentRoute.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); TrainManager.Trains[i].Cars[j].Move(length); } } // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop foreach (TrainManager.TrackFollowingObject Train in TrainManager.TFOs) //Must not use var, as otherwise the wrong inferred type { Train.Initialize(); foreach (var Car in Train.Cars) { double length = Train.Cars[0].Length; Car.Move(-length); Car.Move(length); } } // score Game.CurrentScore.ArrivalStation = PlayerFirstStationIndex + 1; Game.CurrentScore.DepartureStation = PlayerFirstStationIndex; Game.CurrentScore.Maximum = 0; for (int i = 0; i < Program.CurrentRoute.Stations.Length; i++) { if (i != PlayerFirstStationIndex & Program.CurrentRoute.Stations[i].PlayerStops()) { if (i == 0 || Program.CurrentRoute.Stations[i - 1].Type != StationType.ChangeEnds && Program.CurrentRoute.Stations[i - 1].Type != StationType.Jump) { Game.CurrentScore.Maximum += Game.ScoreValueStationArrival; } } } if (Game.CurrentScore.Maximum <= 0) { Game.CurrentScore.Maximum = Game.ScoreValueStationArrival; } // signals if (Program.CurrentRoute.Sections.Length > 0) { Program.CurrentRoute.UpdateAllSections(); } // move train in position for (int i = 0; i < TrainManager.Trains.Length; i++) { double p; if (TrainManager.Trains[i].IsPlayerTrain) { p = PlayerFirstStationPosition; } else if (TrainManager.Trains[i].State == TrainState.Bogus) { p = Program.CurrentRoute.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); } } // timetable if (Program.CurrentRoute.Information.DefaultTimetableDescription.Length == 0) { Program.CurrentRoute.Information.DefaultTimetableDescription = Game.LogTrainName; } // initialize camera if (Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D) { Program.Renderer.Camera.CurrentMode = CameraViewMode.InteriorLookAhead; } //Place the initial camera in the driver car TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].UpdateCamera(); Program.Renderer.CameraTrackFollower.UpdateAbsolute(-1.0, true, false); Program.Renderer.UpdateVisibility(Program.Renderer.CameraTrackFollower.TrackPosition + Program.Renderer.Camera.Alignment.Position.Z); Program.Renderer.Camera.SavedExterior = new CameraAlignment(new OpenBveApi.Math.Vector3(-2.5, 1.5, -15.0), 0.3, -0.2, 0.0, PlayerFirstStationPosition, 1.0); Program.Renderer.Camera.SavedTrack = new CameraAlignment(new OpenBveApi.Math.Vector3(-3.0, 2.5, 0.0), 0.3, 0.0, 0.0, TrainManager.PlayerTrain.Cars[0].TrackPosition - 10.0, 1.0); // signalling sections for (int i = 0; i < TrainManager.Trains.Length; i++) { int s = TrainManager.Trains[i].CurrentSectionIndex; Program.CurrentRoute.Sections[s].Enter(TrainManager.Trains[i]); } if (Program.CurrentRoute.Sections.Length > 0) { Program.CurrentRoute.UpdateAllSections(); } // fast-forward until start time { Game.MinimalisticSimulation = true; const double w = 0.25; double u = Game.StartupTime - Program.CurrentRoute.SecondsSinceMidnight; if (u > 0) { while (true) { double v = u < w ? u : w; u -= v; Program.CurrentRoute.SecondsSinceMidnight += v; TrainManager.UpdateTrains(v); if (u <= 0.0) { break; } TotalTimeElapsedForSectionUpdate += v; if (TotalTimeElapsedForSectionUpdate >= 1.0) { if (Program.CurrentRoute.Sections.Length > 0) { Program.CurrentRoute.UpdateAllSections(); } 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(Program.CurrentRoute.Stations[TrainManager.PlayerTrain.Station].TimetableDaytimeTexture, Program.CurrentRoute.Stations[TrainManager.PlayerTrain.Station].TimetableNighttimeTexture); if (Timetable.CustomObjectsUsed != 0 & Timetable.CustomTimetableAvailable && Interface.CurrentOptions.TimeTableStyle != TimeTableMode.AutoGenerated && Interface.CurrentOptions.TimeTableStyle != TimeTableMode.None) { Program.Renderer.CurrentTimetable = DisplayedTimetable.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, Double.PositiveInfinity); if (TrainManager.PlayerTrain.Plugin != null && !TrainManager.PlayerTrain.Plugin.SupportsAI) { MessageManager.AddMessage(Translations.GetInterfaceString("notification_aiunable"), MessageDependency.None, GameMode.Expert, OpenBveApi.Colors.MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } } // warnings / errors if (Interface.LogMessages.Count != 0) { int filesNotFound = 0; int errors = 0; int warnings = 0; for (int i = 0; i < Interface.LogMessages.Count; i++) { if (Interface.LogMessages[i].FileNotFound) { filesNotFound++; } else if (Interface.LogMessages[i].Type == MessageType.Error) { errors++; } else if (Interface.LogMessages[i].Type == MessageType.Warning) { warnings++; } } string NotFound = null; string Messages; if (filesNotFound != 0) { NotFound = filesNotFound.ToString() + " file(s) not found"; MessageManager.AddMessage(NotFound, MessageDependency.None, GameMode.Expert, MessageColor.Magenta, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } if (errors != 0 & warnings != 0) { Messages = errors.ToString() + " error(s), " + warnings.ToString() + " warning(s)"; MessageManager.AddMessage(Messages, MessageDependency.None, GameMode.Expert, MessageColor.Magenta, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } else if (errors != 0) { Messages = errors.ToString() + " error(s)"; MessageManager.AddMessage(Messages, MessageDependency.None, GameMode.Expert, MessageColor.Magenta, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } else { Messages = warnings.ToString() + " warning(s)"; MessageManager.AddMessage(Messages, MessageDependency.None, GameMode.Expert, MessageColor.Magenta, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } Program.CurrentRoute.Information.FilesNotFound = NotFound; Program.CurrentRoute.Information.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) { MessageManager.AddMessage(Loading.PluginError, MessageDependency.None, GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); MessageManager.AddMessage(Translations.GetInterfaceString("errors_plugin_failure2"), MessageDependency.None, GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } } loadComplete = true; RenderRealTimeElapsed = 0.0; RenderTimeElapsed = 0.0; World.InitializeCameraRestriction(); Loading.SimulationSetup = true; switch (Interface.CurrentOptions.InitialViewpoint) { case 0: if (Game.InitialReversedConsist) { /* * HACK: The cab view has been created using the position of the initial driver car. * Trigger a forced change to ensure that it is now correctly positioned in the consist */ TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].ChangeCarSection(CarSectionType.Interior); } break; case 1: //Switch camera to exterior MainLoop.SaveCameraSettings(); Program.Renderer.Camera.CurrentMode = CameraViewMode.Exterior; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(CarSectionType.Exterior); //Make bogies visible TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].Coupler.ChangeSection(0); } Program.Renderer.Camera.AlignmentDirection = new CameraAlignment(); Program.Renderer.Camera.AlignmentSpeed = new CameraAlignment(); Program.Renderer.UpdateViewport(ViewportChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); Program.Renderer.UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); break; case 2: //Switch camera to track MainLoop.SaveCameraSettings(); Program.Renderer.Camera.CurrentMode = CameraViewMode.Track; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(CarSectionType.Exterior); TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].Coupler.ChangeSection(0); } Program.Renderer.Camera.AlignmentDirection = new CameraAlignment(); Program.Renderer.Camera.AlignmentSpeed = new CameraAlignment(); Program.Renderer.UpdateViewport(ViewportChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); Program.Renderer.UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); break; case 3: //Switch camera to flyby MainLoop.SaveCameraSettings(); Program.Renderer.Camera.CurrentMode = CameraViewMode.FlyBy; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(CarSectionType.Exterior); TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].Coupler.ChangeSection(0); } Program.Renderer.Camera.AlignmentDirection = new CameraAlignment(); Program.Renderer.Camera.AlignmentSpeed = new CameraAlignment(); Program.Renderer.UpdateViewport(ViewportChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); Program.Renderer.UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); break; case 4: //Switch camera to flyby MainLoop.SaveCameraSettings(); Program.Renderer.Camera.CurrentMode = CameraViewMode.FlyByZooming; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(CarSectionType.Exterior); TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].Coupler.ChangeSection(0); } Program.Renderer.Camera.AlignmentDirection = new CameraAlignment(); Program.Renderer.Camera.AlignmentSpeed = new CameraAlignment(); Program.Renderer.UpdateViewport(ViewportChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); Program.Renderer.UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); break; } }
/// <summary>May be called from a .Net plugin, in order to add a message to the in-game display</summary> /// <param name="Message">The message to display</param> /// <param name="Color">The color in which to display the message</param> /// <param name="Time">The time in seconds for which to display the message</param> internal void AddInterfaceMessage(string Message, MessageColor Color, double Time) { MessageManager.AddMessage(Message, MessageDependency.Plugin, GameMode.Expert, Color, Program.CurrentRoute.SecondsSinceMidnight + Time, null); }
/// <inheritdoc/> public override void Update(double TimeElapsed) { if (State == TrainState.Pending) { // pending train bool forceIntroduction = !IsPlayerTrain && !Game.MinimalisticSimulation; double time = 0.0; if (!forceIntroduction) { for (int i = 0; i < Program.CurrentRoute.Stations.Length; i++) { if (Program.CurrentRoute.Stations[i].StopMode == StationStopMode.AllStop | Program.CurrentRoute.Stations[i].StopMode == StationStopMode.PlayerPass) { if (Program.CurrentRoute.Stations[i].ArrivalTime >= 0.0) { time = Program.CurrentRoute.Stations[i].ArrivalTime; } else if (Program.CurrentRoute.Stations[i].DepartureTime >= 0.0) { time = Program.CurrentRoute.Stations[i].DepartureTime - Program.CurrentRoute.Stations[i].StopTime; } break; } } time -= TimetableDelta; } if (Program.CurrentRoute.SecondsSinceMidnight >= time | forceIntroduction) { bool introduce = true; if (!forceIntroduction) { if (CurrentSectionIndex >= 0) { if (!Program.CurrentRoute.Sections[CurrentSectionIndex].IsFree()) { introduce = false; } } } if (this == PlayerTrain && Loading.SimulationSetup) { /* Loading has finished, but we still have an AI train in the current section * This may be caused by an iffy RunInterval value, or simply by having no sections * * * We must introduce the player's train as otherwise the cab and loop sounds are missing * NOTE: In this case, the signalling cannot prevent the player from colliding with * the AI train */ introduce = true; } if (introduce) { // train is introduced State = TrainState.Available; for (int j = 0; j < Cars.Length; j++) { if (Cars[j].CarSections.Length != 0) { if (j == this.DriverCar && IsPlayerTrain && Interface.CurrentOptions.InitialViewpoint == 0) { this.Cars[j].ChangeCarSection(CarSectionType.Interior); } else { /* * HACK: Load in exterior mode first to ensure everything is cached * before switching immediately to not visible * https://github.com/leezer3/OpenBVE/issues/226 * Stuff like the R142A really needs to downsize the textures supplied, * but we have no control over external factors.... */ this.Cars[j].ChangeCarSection(CarSectionType.Exterior); if (IsPlayerTrain && Interface.CurrentOptions.InitialViewpoint == 0) { this.Cars[j].ChangeCarSection(CarSectionType.NotVisible, true); } } } Cars[j].FrontBogie.ChangeSection(!IsPlayerTrain ? 0 : -1); Cars[j].RearBogie.ChangeSection(!IsPlayerTrain ? 0 : -1); Cars[j].Coupler.ChangeSection(!IsPlayerTrain ? 0 : -1); if (Cars[j].Specs.IsMotorCar && Cars[j].Sounds.Loop != null) { Cars[j].Sounds.Loop.Play(Cars[j], true); } } } } } else if (State == TrainState.Available) { // available train UpdatePhysicsAndControls(TimeElapsed); if (CurrentSpeed > CurrentRouteLimit) { if (previousRouteLimit != CurrentRouteLimit || Interface.CurrentOptions.GameMode == GameMode.Arcade) { /* * HACK: If the limit has changed, or we are in arcade mode, notify the player * This conforms to the original behaviour, but doesn't need to raise the message from the event. */ MessageManager.AddMessage(Translations.GetInterfaceString("message_route_overspeed"), MessageDependency.RouteLimit, GameMode.Normal, MessageColor.Orange, Double.PositiveInfinity, null); } } previousRouteLimit = CurrentRouteLimit; if (Interface.CurrentOptions.GameMode == GameMode.Arcade) { if (CurrentSectionLimit == 0.0) { MessageManager.AddMessage(Translations.GetInterfaceString("message_signal_stop"), MessageDependency.PassedRedSignal, GameMode.Normal, MessageColor.Red, double.PositiveInfinity, null); } else if (CurrentSpeed > CurrentSectionLimit) { MessageManager.AddMessage(Translations.GetInterfaceString("message_signal_overspeed"), MessageDependency.SectionLimit, GameMode.Normal, MessageColor.Orange, Double.PositiveInfinity, null); } } if (AI != null) { AI.Trigger(TimeElapsed); } } else if (State == TrainState.Bogus) { // bogus train if (AI != null) { AI.Trigger(TimeElapsed); } } //Trigger point sounds if appropriate for (int i = 0; i < Cars.Length; i++) { CarSound c = null; if (Cars[i].FrontAxle.PointSoundTriggered) { Cars[i].FrontAxle.PointSoundTriggered = false; int bufferIndex = Cars[i].FrontAxle.RunIndex; if (bufferIndex > Cars[i].FrontAxle.PointSounds.Length - 1) { //If the switch sound does not exist, return zero //Required to handle legacy trains which don't have idx specific run sounds defined bufferIndex = 0; } if (Cars[i].FrontAxle.PointSounds == null || Cars[i].FrontAxle.PointSounds.Length == 0) { //No point sounds defined at all continue; } c = (CarSound)Cars[i].FrontAxle.PointSounds[bufferIndex]; if (c.Buffer == null) { c = (CarSound)Cars[i].FrontAxle.PointSounds[0]; } } if (c != null) { double spd = Math.Abs(CurrentSpeed); double pitch = spd / 12.5; double gain = pitch < 0.5 ? 2.0 * pitch : 1.0; if (pitch > 0.2 && gain > 0.2) { c.Play(pitch, gain, Cars[i], false); } } } }
public override void AddMessage(string Message, object MessageDependancy, GameMode Mode, MessageColor MessageColor, double MessageTimeOut, string Key) { MessageManager.AddMessage(Message, (MessageDependency)MessageDependancy, Mode, MessageColor, MessageTimeOut, Key); }
/// <summary>Is called once a frame to update the station state for the given train</summary> /// <param name="Train">The train</param> /// <param name="TimeElapsed">The frame time elapsed</param> private static void UpdateTrainStation(Train Train, double TimeElapsed) { if (Train.Station >= 0) { int i = Train.Station; int n = Program.CurrentRoute.Stations[Train.Station].GetStopIndex(Train.NumberOfCars); double tf, tb; if (n >= 0) { double p0 = Train.Cars[0].FrontAxle.Follower.TrackPosition - Train.Cars[0].FrontAxle.Position + 0.5 * Train.Cars[0].Length; double p1 = Program.CurrentRoute.Stations[i].Stops[n].TrackPosition; tf = Program.CurrentRoute.Stations[i].Stops[n].ForwardTolerance; tb = Program.CurrentRoute.Stations[i].Stops[n].BackwardTolerance; Train.StationDistanceToStopPoint = p1 - p0; } else { Train.StationDistanceToStopPoint = 0.0; tf = 5.0; tb = 5.0; } if (Train.StationState == TrainStopState.Pending) { Train.StationDepartureSoundPlayed = false; if (Program.CurrentRoute.Stations[i].StopsHere(Train)) { Train.StationDepartureSoundPlayed = false; //Check whether all doors are controlled by the driver if (Train.Specs.DoorOpenMode != DoorMode.Manual) { //Check that we are not moving if (Math.Abs(Train.CurrentSpeed) < 0.1 / 3.6 & Math.Abs(Train.Specs.CurrentAverageAcceleration) < 0.1 / 3.6) { Train.AttemptToOpenDoors(i, tb, tf); } } // detect arrival if (Train.CurrentSpeed > -0.277777777777778 & Train.CurrentSpeed < 0.277777777777778) { bool left, right; if (Program.CurrentRoute.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[0].AnticipatedOpen) { left = true; break; } } } else { left = true; } if (Program.CurrentRoute.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[1].AnticipatedOpen) { right = true; break; } } } else { right = true; } if (left & right) { // arrival Train.StationState = TrainStopState.Boarding; Train.SafetySystems.StationAdjust.Lit = false; Train.Specs.DoorClosureAttempted = false; Train.SafetySystems.PassAlarm.Halt(); SoundBuffer buffer = (SoundBuffer)Program.CurrentRoute.Stations[i].ArrivalSoundBuffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Program.CurrentRoute.Stations[i].SoundOrigin; Program.Sounds.PlaySound(buffer, 1.0, 1.0, pos, false); } Train.StationArrivalTime = Program.CurrentRoute.SecondsSinceMidnight; Train.StationDepartureTime = Program.CurrentRoute.Stations[i].DepartureTime - Train.TimetableDelta; if (Train.StationDepartureTime - Program.CurrentRoute.SecondsSinceMidnight < Program.CurrentRoute.Stations[i].StopTime) { Train.StationDepartureTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].StopTime; } Train.Passengers.PassengerRatio = Program.CurrentRoute.Stations[i].PassengerRatio; UpdateTrainMassFromPassengerRatio(Train); if (Train.IsPlayerTrain) { double early = 0.0; if (Program.CurrentRoute.Stations[i].ArrivalTime >= 0.0) { early = (Program.CurrentRoute.Stations[i].ArrivalTime - Train.TimetableDelta) - Train.StationArrivalTime; } string s; if (early < -1.0) { s = Translations.GetInterfaceString("message_station_arrival_late"); } else if (early > 1.0) { s = Translations.GetInterfaceString("message_station_arrival_early"); } else { s = Translations.GetInterfaceString("message_station_arrival"); } System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; TimeSpan a = TimeSpan.FromSeconds(Math.Abs(early)); string b = a.Hours.ToString("00", Culture) + ":" + a.Minutes.ToString("00", Culture) + ":" + a.Seconds.ToString("00", Culture); if (Train.StationDistanceToStopPoint < -0.1) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_overrun"); } else if (Train.StationDistanceToStopPoint > 0.1) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_underrun"); } double d = Math.Abs(Train.StationDistanceToStopPoint); string c = d.ToString("0.0", Culture); if (Program.CurrentRoute.Stations[i].Type == StationType.Terminal) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_terminal"); } s = s.Replace("[name]", Program.CurrentRoute.Stations[i].Name); s = s.Replace("[time]", b); s = s.Replace("[difference]", c); MessageManager.AddMessage(s, MessageDependency.StationArrival, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); if (Program.CurrentRoute.Stations[i].Type == StationType.Normal) { s = Translations.GetInterfaceString("message_station_deadline"); MessageManager.AddMessage(s, MessageDependency.StationDeparture, GameMode.Normal, MessageColor.White, double.PositiveInfinity, null); } Timetable.UpdateCustomTimetable(Program.CurrentRoute.Stations[i].TimetableDaytimeTexture, Program.CurrentRoute.Stations[i].TimetableNighttimeTexture); } // schedule door locks (passengers stuck between the doors) for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { Train.Cars[j].Doors[k].DoorLockDuration = 0.0; if (Program.CurrentRoute.Stations[i].OpenLeftDoors & Train.Cars[j].Doors[k].Direction == -1 | Program.CurrentRoute.Stations[i].OpenRightDoors & Train.Cars[j].Doors[k].Direction == 1) { double p = 0.005 * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio; if (Program.RandomNumberGenerator.NextDouble() < p) { /* * -- door lock at state -- * minimum: 0.2 (nearly closed) * maximum: 0.8 (nearly opened) * */ Train.Cars[j].Doors[k].DoorLockState = 0.2 + 0.6 * Program.RandomNumberGenerator.NextDouble(); /* -- waiting time -- * minimum: 2.9 s * maximum: 40.0 s * average: 7.6 s * */ p = Program.RandomNumberGenerator.NextDouble(); Train.Cars[j].Doors[k].DoorLockDuration = (50.0 - 10.0 * p) / (17.0 - 16.0 * p); } } } } } else { if (Train.SafetySystems.StationAdjust != null) { Train.SafetySystems.StationAdjust.Update(tb, tf); } } } } } else if (Train.StationState == TrainStopState.Boarding) { for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].GetDoorsState(Program.CurrentRoute.Stations[i].OpenLeftDoors, Program.CurrentRoute.Stations[i].OpenRightDoors) == (TrainDoorState.Opened | TrainDoorState.AllOpened)) { //Check whether all doors are controlled by the driver, and whether this is a non-standard station type //e.g. Change ends if (Train.Specs.DoorCloseMode != DoorMode.Manual & Program.CurrentRoute.Stations[i].Type == StationType.Normal) { Train.AttemptToCloseDoors(); if (Train.Specs.DoorClosureAttempted) { if (Program.CurrentRoute.Stations[i].OpenLeftDoors && !Train.Cars[j].Doors[0].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Program.CurrentRoute.Stations[i].ReopenDoor) { Train.Cars[j].Doors[0].ReopenLimit = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[0].ReopenCounter = 0; Train.Cars[j].Doors[0].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[0].InterferingObjectRate * Train.Cars[j].Doors[0].Width >= Train.Cars[j].Doors[0].MaxTolerance) { Train.Cars[j].Doors[0].AnticipatedReopen = true; } } if (Program.CurrentRoute.Stations[i].OpenRightDoors && !Train.Cars[j].Doors[1].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Program.CurrentRoute.Stations[i].ReopenDoor) { Train.Cars[j].Doors[1].ReopenLimit = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[1].ReopenCounter = 0; Train.Cars[j].Doors[1].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[1].InterferingObjectRate * Train.Cars[j].Doors[1].Width >= Train.Cars[j].Doors[1].MaxTolerance) { Train.Cars[j].Doors[1].AnticipatedReopen = true; } } } } } } // detect departure bool left, right; if (!Program.CurrentRoute.Stations[i].OpenLeftDoors & !Program.CurrentRoute.Stations[i].OpenRightDoors) { left = true; right = true; } else { if (Program.CurrentRoute.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { if (Train.Cars[j].Doors[k].State != 0.0) { left = true; break; } } if (left) { break; } } } else { left = false; } if (Program.CurrentRoute.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { if (Train.Cars[j].Doors[k].State != 0.0) { right = true; break; } } if (right) { break; } } } else { right = false; } } // departure sound if (!Train.StationDepartureSoundPlayed) { SoundBuffer buffer = (SoundBuffer)Program.CurrentRoute.Stations[i].DepartureSoundBuffer; if (buffer != null) { double dur = Program.Sounds.GetDuration(buffer); if (Program.CurrentRoute.SecondsSinceMidnight >= Train.StationDepartureTime - dur) { if (!Game.MinimalisticSimulation) { Program.Sounds.PlaySound(buffer, 1.0, 1.0, Program.CurrentRoute.Stations[i].SoundOrigin, false); } Train.StationDepartureSoundPlayed = true; } } } for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[0].AnticipatedReopen && Train.Cars[j].Doors[0].State == Train.Cars[j].Doors[0].InterferingObjectRate) { if (Train.Cars[j].Doors[0].NextReopenTime == 0.0) { Train.Cars[j].Doors[0].NextReopenTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[0].ReopenCounter < Train.Cars[j].Doors[0].ReopenLimit) { if (Program.CurrentRoute.SecondsSinceMidnight >= Train.Cars[j].Doors[0].NextReopenTime) { Train.Cars[j].OpenDoors(true, false); } } else { Train.Cars[j].Doors[0].AnticipatedReopen = false; } } if (Train.Cars[j].Doors[1].AnticipatedReopen && Train.Cars[j].Doors[1].State == Train.Cars[j].Doors[1].InterferingObjectRate) { if (Train.Cars[j].Doors[1].NextReopenTime == 0.0) { Train.Cars[j].Doors[1].NextReopenTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[1].ReopenCounter < Train.Cars[j].Doors[1].ReopenLimit) { if (Program.CurrentRoute.SecondsSinceMidnight >= Train.Cars[j].Doors[1].NextReopenTime) { Train.Cars[j].OpenDoors(false, true); } } else { Train.Cars[j].Doors[1].AnticipatedReopen = false; } } } TrainDoorState doorState = Train.GetDoorsState(Program.CurrentRoute.Stations[i].OpenLeftDoors, Program.CurrentRoute.Stations[i].OpenRightDoors); if (left | right) { /* * Assume that passengers only board at a scheduled stop * If the player has opened the doors somewhere else (lineside?) * then passengers should not be boarding */ if (doorState != TrainDoorState.AllClosed && Interface.CurrentOptions.LoadingSway) { // passengers boarding for (int j = 0; j < Train.Cars.Length; j++) { if (!Train.Cars[j].EnableLoadingSway) { continue; } double r = 2.0 * Program.CurrentRoute.Stations[i].PassengerRatio * TimeElapsed; if (r >= Program.RandomNumberGenerator.NextDouble()) { int d = (int)Math.Floor(Program.RandomNumberGenerator.NextDouble() * (double)Train.Cars[j].Doors.Length); if (Train.Cars[j].Doors[d].State == 1.0) { Train.Cars[j].Specs.CurrentRollShakeDirection += (double)Train.Cars[j].Doors[d].Direction; } } } } } if (Train.Specs.DoorCloseMode == DoorMode.Manual || doorState == TrainDoorState.None || doorState == (TrainDoorState.Closed | TrainDoorState.AllClosed) || (Program.CurrentRoute.Stations[Train.Station].Type == StationType.ChangeEnds || Program.CurrentRoute.Stations[Train.Station].Type == StationType.Jump)) { if (left | right) { // departure message if (Program.CurrentRoute.SecondsSinceMidnight > Train.StationDepartureTime && (Program.CurrentRoute.Stations[i].Type != StationType.Terminal || Train != PlayerTrain)) { Train.StationState = TrainStopState.Completed; switch (Program.CurrentRoute.Stations[i].Type) { case StationType.Normal: if (!Train.IsPlayerTrain) { break; // Only trigger messages for the player train } if (!Program.CurrentRoute.Stations[i].OpenLeftDoors & !Program.CurrentRoute.Stations[i].OpenRightDoors | Train.Specs.DoorCloseMode != DoorMode.Manual) { MessageManager.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } else { MessageManager.AddMessage(Translations.GetInterfaceString("message_station_depart_closedoors"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } break; case StationType.ChangeEnds: // Change ends always jumps to the NEXT station JumpTrain(Train, i + 1); break; case StationType.Jump: // Jumps to an arbritrary station as defined in the routefile JumpTrain(Train, Program.CurrentRoute.Stations[i].JumpIndex); break; } } } else { Train.StationState = TrainStopState.Completed; if (Train.IsPlayerTrain & Program.CurrentRoute.Stations[i].Type == StationType.Normal) { MessageManager.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } } } } } else { if (Train.StationState != TrainStopState.Jumping) { Train.StationState = TrainStopState.Pending; } } // automatically close doors if (Train.Specs.DoorCloseMode != DoorMode.Manual & !Train.Specs.DoorClosureAttempted) { if (Train.Station == -1 | Train.StationState == TrainStopState.Completed) { if ((Train.GetDoorsState(true, true) & TrainDoorState.AllClosed) == 0) { Train.CloseDoors(true, true); Train.Specs.DoorClosureAttempted = true; } } } }
/// <inheritdoc/> public override void RequestStop(RequestStop stopRequest) { if (stopRequest.MaxCars != 0 && NumberOfCars > stopRequest.MaxCars) { //Check whether our train length is valid for this before doing anything else Cars[DriverCar].Sounds.RequestStop[2].Play(Cars[DriverCar], false); return; } if (Program.RandomNumberGenerator.Next(0, 100) <= stopRequest.Probability) { //We have hit our probability roll if (Program.CurrentRoute.Stations[stopRequest.StationIndex].StopMode == StationStopMode.AllRequestStop || (IsPlayerTrain && Program.CurrentRoute.Stations[stopRequest.StationIndex].StopMode == StationStopMode.PlayerRequestStop)) { //If our train can stop at this station, set it's index accordingly Station = stopRequest.StationIndex; NextStopSkipped = StopSkipMode.None; //Play sound Cars[DriverCar].Sounds.RequestStop[0].Play(Cars[DriverCar], false); } else { //We don't meet the conditions for this request stop if (stopRequest.FullSpeed) { //Pass at linespeed, rather than braking as if for stop NextStopSkipped = StopSkipMode.Linespeed; } else { NextStopSkipped = StopSkipMode.Decelerate; } //Play sound Cars[DriverCar].Sounds.RequestStop[1].Play(Cars[DriverCar], false); //If message is not empty, add it if (!string.IsNullOrEmpty(stopRequest.PassMessage) && IsPlayerTrain) { MessageManager.AddMessage(stopRequest.PassMessage, MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } return; } //Play sound Cars[DriverCar].Sounds.RequestStop[0].Play(Cars[DriverCar], false); //If message is not empty, add it if (!string.IsNullOrEmpty(stopRequest.StopMessage) && IsPlayerTrain) { MessageManager.AddMessage(stopRequest.StopMessage, MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } } else { Cars[DriverCar].Sounds.RequestStop[1].Play(Cars[DriverCar], false); if (stopRequest.FullSpeed) { //Pass at linespeed, rather than braking as if for stop NextStopSkipped = StopSkipMode.Linespeed; } else { NextStopSkipped = StopSkipMode.Decelerate; } //Play sound Cars[DriverCar].Sounds.RequestStop[1].Play(Cars[DriverCar], false); //If message is not empty, add it if (!string.IsNullOrEmpty(stopRequest.PassMessage) && IsPlayerTrain) { MessageManager.AddMessage(stopRequest.PassMessage, MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); } } }
/// <summary>Moves the camera to a point of interest</summary> /// <param name="Value">The value of the jump to perform: /// -1= Previous POI /// 0= Return to currently selected POI (From cab etc.) /// 1= Next POI</param> /// <param name="Relative">Whether the relative camera position should be retained</param> /// <returns>False if the previous / next POI would be outside those defined, true otherwise</returns> internal static bool ApplyPointOfInterest(int Value, bool Relative) { double t = 0.0; int j = -1; if (Relative) { // relative if (Value < 0) { // previous poi t = double.NegativeInfinity; for (int i = 0; i < Program.CurrentRoute.PointsOfInterest.Length; i++) { if (Program.CurrentRoute.PointsOfInterest[i].TrackPosition < Program.Renderer.CameraTrackFollower.TrackPosition) { if (Program.CurrentRoute.PointsOfInterest[i].TrackPosition > t) { t = Program.CurrentRoute.PointsOfInterest[i].TrackPosition; j = i; } } } } else if (Value > 0) { // next poi t = double.PositiveInfinity; for (int i = 0; i < Program.CurrentRoute.PointsOfInterest.Length; i++) { if (Program.CurrentRoute.PointsOfInterest[i].TrackPosition > Program.Renderer.CameraTrackFollower.TrackPosition) { if (Program.CurrentRoute.PointsOfInterest[i].TrackPosition < t) { t = Program.CurrentRoute.PointsOfInterest[i].TrackPosition; j = i; } } } } } else { // absolute j = Value >= 0 & Value < Program.CurrentRoute.PointsOfInterest.Length ? Value : -1; } // process poi if (j < 0) { return(false); } Program.Renderer.CameraTrackFollower.UpdateAbsolute(t, true, false); Program.Renderer.Camera.Alignment.Position = Program.CurrentRoute.PointsOfInterest[j].TrackOffset; Program.Renderer.Camera.Alignment.Yaw = Program.CurrentRoute.PointsOfInterest[j].TrackYaw; Program.Renderer.Camera.Alignment.Pitch = Program.CurrentRoute.PointsOfInterest[j].TrackPitch; Program.Renderer.Camera.Alignment.Roll = Program.CurrentRoute.PointsOfInterest[j].TrackRoll; Program.Renderer.Camera.Alignment.TrackPosition = t; World.UpdateAbsoluteCamera(0.0); if (Program.CurrentRoute.PointsOfInterest[j].Text != null) { double n = 3.0 + 0.5 * Math.Sqrt((double)Program.CurrentRoute.PointsOfInterest[j].Text.Length); MessageManager.AddMessage(Program.CurrentRoute.PointsOfInterest[j].Text, MessageDependency.PointOfInterest, GameMode.Expert, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + n, null); } return(true); }
/// <summary>Called once a frame to update the messages displayed on-screen</summary> internal static void UpdateMessages() { MessageManager.UpdateMessages(); }