/// <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 = Game.Stations[Train.Station].GetStopIndex(Train.Cars.Length); 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 = Game.Stations[i].Stops[n].TrackPosition; tf = Game.Stations[i].Stops[n].ForwardTolerance; tb = Game.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 (Game.StopsAtStation(i, 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.Specs.CurrentAverageSpeed) < 0.1 / 3.6 & Math.Abs(Train.Specs.CurrentAverageAcceleration) < 0.1 / 3.6) { //Check the interlock state for the doors switch (Train.Specs.DoorInterlockState) { case DoorInterlockStates.Unlocked: if (Game.Stations[i].OpenLeftDoors || Game.Stations[i].OpenRightDoors) { AttemptToOpenDoors(Train, i, tb, tf); } break; case DoorInterlockStates.Left: if (Game.Stations[i].OpenLeftDoors && !Game.Stations[i].OpenRightDoors) { AttemptToOpenDoors(Train, i, tb, tf); } break; case DoorInterlockStates.Right: if (!Game.Stations[i].OpenLeftDoors && Game.Stations[i].OpenRightDoors) { AttemptToOpenDoors(Train, i, tb, tf); } break; case DoorInterlockStates.Locked: //All doors are currently locked, do nothing break; } } } // detect arrival if (Train.Specs.CurrentAverageSpeed > -0.277777777777778 & Train.Specs.CurrentAverageSpeed < 0.277777777777778) { bool left, right; if (Game.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 (Game.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.StationAdjust = false; Train.Specs.DoorClosureAttempted = false; Sounds.StopSound(Train.Cars[Train.DriverCar].Sounds.Halt.Source); Sounds.SoundBuffer buffer = Game.Stations[i].ArrivalSoundBuffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Game.Stations[i].SoundOrigin; Sounds.PlaySound(buffer, 1.0, 1.0, pos, false); } Train.StationArrivalTime = Game.SecondsSinceMidnight; Train.StationDepartureTime = Game.Stations[i].DepartureTime - Train.TimetableDelta; if (Train.StationDepartureTime - Game.SecondsSinceMidnight < Game.Stations[i].StopTime) { Train.StationDepartureTime = Game.SecondsSinceMidnight + Game.Stations[i].StopTime; } Train.Passengers.PassengerRatio = Game.Stations[i].PassengerRatio; UpdateTrainMassFromPassengerRatio(Train); if (Train == PlayerTrain) { double early = 0.0; if (Game.Stations[i].ArrivalTime >= 0.0) { early = (Game.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 (Game.Stations[i].Type == StationType.Terminal) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_terminal"); } s = s.Replace("[name]", Game.Stations[i].Name); s = s.Replace("[time]", b); s = s.Replace("[difference]", c); Game.AddMessage(s, MessageManager.MessageDependency.StationArrival, Interface.GameMode.Normal, MessageColor.White, Game.SecondsSinceMidnight + 10.0, null); if (Game.Stations[i].Type == StationType.Normal) { s = Translations.GetInterfaceString("message_station_deadline"); Game.AddMessage(s, MessageManager.MessageDependency.StationDeparture, Interface.GameMode.Normal, MessageColor.White, double.PositiveInfinity, null); } Timetable.UpdateCustomTimetable(Game.Stations[i].TimetableDaytimeTexture, Game.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 (Game.Stations[i].OpenLeftDoors & Train.Cars[j].Doors[k].Direction == -1 | Game.Stations[i].OpenRightDoors & Train.Cars[j].Doors[k].Direction == 1) { double p = 0.005 * Game.Stations[i].PassengerRatio * Game.Stations[i].PassengerRatio * Game.Stations[i].PassengerRatio * Game.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.Specs.CurrentAverageSpeed > -0.277777777777778 & Train.Specs.CurrentAverageSpeed < 0.277777777777778) { // correct stop position if (!Train.StationAdjust & (Train.StationDistanceToStopPoint > tb | Train.StationDistanceToStopPoint < -tf)) { Sounds.SoundBuffer buffer = Train.Cars[Train.DriverCar].Sounds.Adjust.Buffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Train.Cars[Train.DriverCar].Sounds.Adjust.Position; Sounds.PlaySound(buffer, 1.0, 1.0, pos, Train, Train.DriverCar, false); } if (Train == TrainManager.PlayerTrain) { Game.AddMessage(Translations.GetInterfaceString("message_station_correct"), MessageManager.MessageDependency.None, Interface.GameMode.Normal, MessageColor.Orange, Game.SecondsSinceMidnight + 5.0, null); } Train.StationAdjust = true; } } else { Train.StationAdjust = false; } } } } else if (Train.StationState == TrainStopState.Boarding) { for (int j = 0; j < Train.Cars.Length; j++) { if (GetDoorsState(Train, j, Game.Stations[i].OpenLeftDoors, Game.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 & Game.Stations[i].Type == StationType.Normal) { //Check the interlock state for the doors switch (Train.Specs.DoorInterlockState) { case DoorInterlockStates.Unlocked: AttemptToCloseDoors(Train); break; case DoorInterlockStates.Left: if (Game.Stations[i].OpenLeftDoors) { AttemptToCloseDoors(Train); } break; case DoorInterlockStates.Right: if (Game.Stations[i].OpenRightDoors) { AttemptToCloseDoors(Train); } break; case DoorInterlockStates.Locked: //All doors are currently locked, do nothing break; } if (Train.Specs.DoorInterlockState != DoorInterlockStates.Locked & Train.Specs.DoorClosureAttempted) { if (Game.Stations[i].OpenLeftDoors && !Train.Cars[j].Doors[0].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Game.Stations[i].ReopenDoor) { Train.Cars[j].Doors[0].ReopenLimit = Program.RandomNumberGenerator.Next(1, Game.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[0].ReopenCounter = 0; Train.Cars[j].Doors[0].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Game.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[0].InterferingObjectRate * Train.Specs.DoorWidth >= Train.Specs.DoorMaxTolerance) { Train.Cars[j].Doors[0].AnticipatedReopen = true; } } if (Game.Stations[i].OpenRightDoors && !Train.Cars[j].Doors[1].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Game.Stations[i].ReopenDoor) { Train.Cars[j].Doors[1].ReopenLimit = Program.RandomNumberGenerator.Next(1, Game.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[1].ReopenCounter = 0; Train.Cars[j].Doors[1].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Game.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[1].InterferingObjectRate * Train.Specs.DoorWidth >= Train.Specs.DoorMaxTolerance) { Train.Cars[j].Doors[1].AnticipatedReopen = true; } } } } } } // detect departure bool left, right; if (!Game.Stations[i].OpenLeftDoors & !Game.Stations[i].OpenRightDoors) { left = true; right = true; } else { if (Game.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 (Game.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) { Sounds.SoundBuffer buffer = Game.Stations[i].DepartureSoundBuffer; if (buffer != null) { double dur = Sounds.GetDuration(buffer); if (Game.SecondsSinceMidnight >= Train.StationDepartureTime - dur) { Sounds.PlaySound(buffer, 1.0, 1.0, Game.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 = Game.SecondsSinceMidnight + Game.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[0].ReopenCounter < Train.Cars[j].Doors[0].ReopenLimit) { if (Game.SecondsSinceMidnight >= Train.Cars[j].Doors[0].NextReopenTime) { OpenTrainDoors(Train, j, 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 = Game.SecondsSinceMidnight + Game.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[1].ReopenCounter < Train.Cars[j].Doors[1].ReopenLimit) { if (Game.SecondsSinceMidnight >= Train.Cars[j].Doors[1].NextReopenTime) { OpenTrainDoors(Train, j, false, true); } } else { Train.Cars[j].Doors[1].AnticipatedReopen = false; } } } TrainDoorState doorState = GetDoorsState(Train, Game.Stations[i].OpenLeftDoors, Game.Stations[i].OpenRightDoors); if (Train.Specs.DoorCloseMode == DoorMode.Manual || doorState == TrainDoorState.None || doorState == (TrainDoorState.Closed | TrainDoorState.AllClosed)) { if (left | right) { // departure message if (Game.SecondsSinceMidnight > Train.StationDepartureTime && (Game.Stations[i].Type != StationType.Terminal || Train != PlayerTrain)) { Train.StationState = TrainStopState.Completed; if (Train == PlayerTrain & Game.Stations[i].Type == StationType.Normal) { if (!Game.Stations[i].OpenLeftDoors & !Game.Stations[i].OpenRightDoors | Train.Specs.DoorCloseMode != DoorMode.Manual) { Game.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageManager.MessageDependency.None, Interface.GameMode.Normal, MessageColor.White, Game.SecondsSinceMidnight + 5.0, null); } else { Game.AddMessage(Translations.GetInterfaceString("message_station_depart_closedoors"), MessageManager.MessageDependency.None, Interface.GameMode.Normal, MessageColor.White, Game.SecondsSinceMidnight + 5.0, null); } } else if (Game.Stations[i].Type == StationType.ChangeEnds) { JumpTrain(Train, i + 1); } } if (Interface.CurrentOptions.LoadingSway) { // passengers boarding for (int j = 0; j < Train.Cars.Length; j++) { double r = 2.0 * Game.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; } } } } } else { Train.StationState = TrainStopState.Completed; if (Train == PlayerTrain & Game.Stations[i].Type == StationType.Normal) { Game.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageManager.MessageDependency.None, Interface.GameMode.Normal, MessageColor.White, Game.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.DoorInterlockState != DoorInterlockStates.Locked & !Train.Specs.DoorClosureAttempted) { if (Train.Station == -1 | Train.StationState == TrainStopState.Completed) { if ((GetDoorsState(Train, true, true) & TrainDoorState.AllClosed) == 0) { CloseTrainDoors(Train, true, true); Train.Specs.DoorClosureAttempted = true; } } } }