1 using System; 2 using OpenBveApi; 3 using OpenBveApi.Colors; 4 using OpenBveApi.Interface; 5 using OpenBveApi.Runtime; 6 using OpenBveApi.Trains; 7 using RouteManager2.MessageManager; 8 using SoundManager; 9 using TrainManager.Car; 10 using TrainManager.Handles; 11 12 namespace TrainManager.Trains 13 { 14 public partial class TrainBase 15 { 16 /// <inheritdoc/> EnterStation(int stationIndex, int direction)17 public override void EnterStation(int stationIndex, int direction) 18 { 19 if (direction < 0) 20 { 21 if (Handles.Reverser.Actual == ReverserPosition.Forwards && Handles.Power.Driver != 0 && TrainManagerBase.currentHost.SimulationState != SimulationState.MinimalisticSimulation && stationIndex == Station) 22 { 23 //Our reverser and power are in F, but we are rolling backwards 24 //Leave the station index alone, and we won't trigger again when we actually move forwards 25 return; 26 } 27 28 Station = -1; 29 } 30 else if (direction > 0) 31 { 32 if (Station == stationIndex || NextStopSkipped != StopSkipMode.None) 33 { 34 return; 35 } 36 37 Station = stationIndex; 38 if (StationState != TrainStopState.Jumping) 39 { 40 StationState = TrainStopState.Pending; 41 } 42 43 LastStation = stationIndex; 44 } 45 } 46 47 /// <inheritdoc/> LeaveStation(int stationIndex, int direction)48 public override void LeaveStation(int stationIndex, int direction) 49 { 50 if (direction < 0) 51 { 52 Station = stationIndex; 53 if (NextStopSkipped != StopSkipMode.None) 54 { 55 LastStation = stationIndex; 56 } 57 58 NextStopSkipped = StopSkipMode.None; 59 } 60 else if (direction > 0) 61 { 62 if (Station == stationIndex) 63 { 64 if (IsPlayerTrain) 65 { 66 if (TrainManagerBase.CurrentRoute.Stations[stationIndex].PlayerStops() & StationState == TrainStopState.Pending) 67 { 68 string s = Translations.GetInterfaceString("message_station_passed"); 69 s = s.Replace("[name]", TrainManagerBase.CurrentRoute.Stations[stationIndex].Name); 70 TrainManagerBase.currentHost.AddMessage(s, MessageDependency.None, GameMode.Normal, MessageColor.Orange, TrainManagerBase.currentHost.InGameTime + 10.0, null); 71 } 72 else if (TrainManagerBase.CurrentRoute.Stations[stationIndex].PlayerStops() & StationState == TrainStopState.Boarding) 73 { 74 string s = Translations.GetInterfaceString("message_station_passed_boarding"); 75 s = s.Replace("[name]", TrainManagerBase.CurrentRoute.Stations[stationIndex].Name); 76 TrainManagerBase.currentHost.AddMessage(s, MessageDependency.None, GameMode.Normal, MessageColor.Red, TrainManagerBase.currentHost.InGameTime + 10.0, null); 77 } 78 } 79 80 Station = -1; 81 if (StationState != TrainStopState.Jumping) 82 { 83 StationState = TrainStopState.Pending; 84 } 85 86 SafetySystems.PassAlarm.Halt(); 87 } 88 } 89 } 90 91 /// <summary>Is called once a frame to update the station state for the train</summary> 92 /// <param name="TimeElapsed">The frame time elapsed</param> UpdateStation(double TimeElapsed)93 private void UpdateStation(double TimeElapsed) 94 { 95 if (Station >= 0) 96 { 97 int i = Station; 98 int n = TrainManagerBase.CurrentRoute.Stations[Station].GetStopIndex(NumberOfCars); 99 double tf, tb; 100 if (n >= 0) 101 { 102 double p0 = Cars[0].FrontAxle.Follower.TrackPosition - Cars[0].FrontAxle.Position + 0.5 * Cars[0].Length; 103 double p1 = TrainManagerBase.CurrentRoute.Stations[i].Stops[n].TrackPosition; 104 tf = TrainManagerBase.CurrentRoute.Stations[i].Stops[n].ForwardTolerance; 105 tb = TrainManagerBase.CurrentRoute.Stations[i].Stops[n].BackwardTolerance; 106 StationDistanceToStopPoint = p1 - p0; 107 } 108 else 109 { 110 StationDistanceToStopPoint = 0.0; 111 tf = 5.0; 112 tb = 5.0; 113 } 114 115 if (StationState == TrainStopState.Pending) 116 { 117 StationDepartureSoundPlayed = false; 118 if (TrainManagerBase.CurrentRoute.Stations[i].StopsHere(this)) 119 { 120 StationDepartureSoundPlayed = false; 121 //Check whether all doors are controlled by the driver 122 if (Specs.DoorOpenMode != DoorMode.Manual) 123 { 124 //Check that we are not moving 125 if (Math.Abs(CurrentSpeed) < 0.1 / 3.6 & 126 Math.Abs(Specs.CurrentAverageAcceleration) < 0.1 / 3.6) 127 { 128 AttemptToOpenDoors(TrainManagerBase.CurrentRoute.Stations[i], tb, tf); 129 } 130 } 131 132 // detect arrival 133 if (CurrentSpeed > -0.277777777777778 & CurrentSpeed < 0.277777777777778) 134 { 135 bool left, right; 136 if (TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors) 137 { 138 left = false; 139 for (int j = 0; j < Cars.Length; j++) 140 { 141 if (Cars[j].Doors[0].AnticipatedOpen) 142 { 143 left = true; 144 break; 145 } 146 } 147 } 148 else 149 { 150 left = true; 151 } 152 153 if (TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors) 154 { 155 right = false; 156 for (int j = 0; j < Cars.Length; j++) 157 { 158 if (Cars[j].Doors[1].AnticipatedOpen) 159 { 160 right = true; 161 break; 162 } 163 } 164 } 165 else 166 { 167 right = true; 168 } 169 170 if (left & right) 171 { 172 // arrival 173 StationState = TrainStopState.Boarding; 174 SafetySystems.StationAdjust.Lit = false; 175 Specs.DoorClosureAttempted = false; 176 SafetySystems.PassAlarm.Halt(); 177 SoundBuffer buffer = (SoundBuffer) TrainManagerBase.CurrentRoute.Stations[i].ArrivalSoundBuffer; 178 if (buffer != null) 179 { 180 OpenBveApi.Math.Vector3 pos = TrainManagerBase.CurrentRoute.Stations[i].SoundOrigin; 181 TrainManagerBase.currentHost.PlaySound(buffer, 1.0, 1.0, pos, null, false); 182 } 183 184 StationArrivalTime = TrainManagerBase.CurrentRoute.SecondsSinceMidnight; 185 StationDepartureTime = TrainManagerBase.CurrentRoute.Stations[i].DepartureTime - TimetableDelta; 186 if (StationDepartureTime - TrainManagerBase.CurrentRoute.SecondsSinceMidnight < TrainManagerBase.CurrentRoute.Stations[i].StopTime) 187 { 188 StationDepartureTime = TrainManagerBase.CurrentRoute.SecondsSinceMidnight + TrainManagerBase.CurrentRoute.Stations[i].StopTime; 189 } 190 191 Passengers.PassengerRatio = TrainManagerBase.CurrentRoute.Stations[i].PassengerRatio; 192 TrainManagerBase.UpdateTrainMassFromPassengerRatio(this); 193 if (IsPlayerTrain) 194 { 195 double early = 0.0; 196 if (TrainManagerBase.CurrentRoute.Stations[i].ArrivalTime >= 0.0) 197 { 198 early = (TrainManagerBase.CurrentRoute.Stations[i].ArrivalTime - TimetableDelta) - StationArrivalTime; 199 } 200 201 string s; 202 if (early < -1.0) 203 { 204 s = Translations.GetInterfaceString("message_station_arrival_late"); 205 } 206 else if (early > 1.0) 207 { 208 s = Translations.GetInterfaceString("message_station_arrival_early"); 209 } 210 else 211 { 212 s = Translations.GetInterfaceString("message_station_arrival"); 213 } 214 215 System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; 216 TimeSpan a = TimeSpan.FromSeconds(Math.Abs(early)); 217 string b = a.Hours.ToString("00", Culture) + ":" + a.Minutes.ToString("00", Culture) + ":" + a.Seconds.ToString("00", Culture); 218 if (StationDistanceToStopPoint < -0.1) 219 { 220 s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_overrun"); 221 } 222 else if (StationDistanceToStopPoint > 0.1) 223 { 224 s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_underrun"); 225 } 226 227 double d = Math.Abs(StationDistanceToStopPoint); 228 string c = d.ToString("0.0", Culture); 229 if (TrainManagerBase.CurrentRoute.Stations[i].Type == StationType.Terminal) 230 { 231 s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_terminal"); 232 } 233 234 s = s.Replace("[name]", TrainManagerBase.CurrentRoute.Stations[i].Name); 235 s = s.Replace("[time]", b); 236 s = s.Replace("[difference]", c); 237 TrainManagerBase.currentHost.AddMessage(s, MessageDependency.StationArrival, GameMode.Normal, MessageColor.White, TrainManagerBase.CurrentRoute.SecondsSinceMidnight + 10.0, null); 238 if (TrainManagerBase.CurrentRoute.Stations[i].Type == StationType.Normal) 239 { 240 s = Translations.GetInterfaceString("message_station_deadline"); 241 TrainManagerBase.currentHost.AddMessage(s, MessageDependency.StationDeparture, GameMode.Normal, MessageColor.White, double.PositiveInfinity, null); 242 } 243 244 TrainManagerBase.currentHost.UpdateCustomTimetable(TrainManagerBase.CurrentRoute.Stations[i].TimetableDaytimeTexture, TrainManagerBase.CurrentRoute.Stations[i].TimetableNighttimeTexture); 245 } 246 247 // schedule door locks (passengers stuck between the doors) 248 for (int j = 0; j < Cars.Length; j++) 249 { 250 for (int k = 0; k < Cars[j].Doors.Length; k++) 251 { 252 Cars[j].Doors[k].DoorLockDuration = 0.0; 253 if (TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors & Cars[j].Doors[k].Direction == -1 | TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors & Cars[j].Doors[k].Direction == 1) 254 { 255 double p = 0.005 * TrainManagerBase.CurrentRoute.Stations[i].PassengerRatio * TrainManagerBase.CurrentRoute.Stations[i].PassengerRatio * TrainManagerBase.CurrentRoute.Stations[i].PassengerRatio * TrainManagerBase.CurrentRoute.Stations[i].PassengerRatio; 256 if (TrainManagerBase.RandomNumberGenerator.NextDouble() < p) 257 { 258 /* 259 * -- door lock at state -- 260 * minimum: 0.2 (nearly closed) 261 * maximum: 0.8 (nearly opened) 262 * */ 263 Cars[j].Doors[k].DoorLockState = 0.2 + 0.6 * TrainManagerBase.RandomNumberGenerator.NextDouble(); 264 /* -- waiting time -- 265 * minimum: 2.9 s 266 * maximum: 40.0 s 267 * average: 7.6 s 268 * */ 269 p = TrainManagerBase.RandomNumberGenerator.NextDouble(); 270 Cars[j].Doors[k].DoorLockDuration = (50.0 - 10.0 * p) / (17.0 - 16.0 * p); 271 } 272 } 273 } 274 } 275 } 276 else 277 { 278 if (SafetySystems.StationAdjust != null) 279 { 280 SafetySystems.StationAdjust.Update(tb, tf); 281 } 282 } 283 } 284 } 285 } 286 else if (StationState == TrainStopState.Boarding) 287 { 288 for (int j = 0; j < Cars.Length; j++) 289 { 290 if (Cars[j].GetDoorsState(TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors, TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors) == (TrainDoorState.Opened | TrainDoorState.AllOpened)) 291 { 292 //Check whether all doors are controlled by the driver, and whether this is a non-standard station type 293 //e.g. Change ends 294 if (Specs.DoorCloseMode != DoorMode.Manual & TrainManagerBase.CurrentRoute.Stations[i].Type == StationType.Normal) 295 { 296 AttemptToCloseDoors(); 297 298 if (Specs.DoorClosureAttempted) 299 { 300 if (TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors && !Cars[j].Doors[0].AnticipatedReopen && TrainManagerBase.RandomNumberGenerator.NextDouble() < TrainManagerBase.CurrentRoute.Stations[i].ReopenDoor) 301 { 302 Cars[j].Doors[0].ReopenLimit = TrainManagerBase.RandomNumberGenerator.Next(1, TrainManagerBase.CurrentRoute.Stations[i].ReopenStationLimit); 303 Cars[j].Doors[0].ReopenCounter = 0; 304 Cars[j].Doors[0].InterferingObjectRate = TrainManagerBase.RandomNumberGenerator.Next(1, TrainManagerBase.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; 305 if (Cars[j].Doors[0].InterferingObjectRate * Cars[j].Doors[0].Width >= Cars[j].Doors[0].MaxTolerance) 306 { 307 Cars[j].Doors[0].AnticipatedReopen = true; 308 } 309 } 310 311 if (TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors && !Cars[j].Doors[1].AnticipatedReopen && TrainManagerBase.RandomNumberGenerator.NextDouble() < TrainManagerBase.CurrentRoute.Stations[i].ReopenDoor) 312 { 313 Cars[j].Doors[1].ReopenLimit = TrainManagerBase.RandomNumberGenerator.Next(1, TrainManagerBase.CurrentRoute.Stations[i].ReopenStationLimit); 314 Cars[j].Doors[1].ReopenCounter = 0; 315 Cars[j].Doors[1].InterferingObjectRate = TrainManagerBase.RandomNumberGenerator.Next(1, TrainManagerBase.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; 316 if (Cars[j].Doors[1].InterferingObjectRate * Cars[j].Doors[1].Width >= Cars[j].Doors[1].MaxTolerance) 317 { 318 Cars[j].Doors[1].AnticipatedReopen = true; 319 } 320 } 321 } 322 } 323 } 324 } 325 326 // detect departure 327 bool left, right; 328 if (!TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors & !TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors) 329 { 330 left = true; 331 right = true; 332 } 333 else 334 { 335 if (TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors) 336 { 337 left = false; 338 for (int j = 0; j < Cars.Length; j++) 339 { 340 for (int k = 0; k < Cars[j].Doors.Length; k++) 341 { 342 if (Cars[j].Doors[k].State != 0.0) 343 { 344 left = true; 345 break; 346 } 347 } 348 349 if (left) break; 350 } 351 } 352 else 353 { 354 left = false; 355 } 356 357 if (TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors) 358 { 359 right = false; 360 for (int j = 0; j < Cars.Length; j++) 361 { 362 for (int k = 0; k < Cars[j].Doors.Length; k++) 363 { 364 if (Cars[j].Doors[k].State != 0.0) 365 { 366 right = true; 367 break; 368 } 369 } 370 371 if (right) break; 372 } 373 } 374 else 375 { 376 right = false; 377 } 378 } 379 380 // departure sound 381 if (!StationDepartureSoundPlayed) 382 { 383 SoundBuffer buffer = (SoundBuffer) TrainManagerBase.CurrentRoute.Stations[i].DepartureSoundBuffer; 384 if (buffer != null) 385 { 386 if (TrainManagerBase.CurrentRoute.SecondsSinceMidnight >= StationDepartureTime - buffer.Duration) 387 { 388 if (TrainManagerBase.currentHost.SimulationState == SimulationState.Running) 389 { 390 TrainManagerBase.currentHost.PlaySound(buffer, 1.0, 1.0, TrainManagerBase.CurrentRoute.Stations[i].SoundOrigin, null, false); 391 StationDepartureSoundPlayed = true; 392 } 393 } 394 } 395 } 396 397 for (int j = 0; j < Cars.Length; j++) 398 { 399 if (Cars[j].Doors[0].AnticipatedReopen && Cars[j].Doors[0].State == Cars[j].Doors[0].InterferingObjectRate) 400 { 401 if (Cars[j].Doors[0].NextReopenTime == 0.0) 402 { 403 Cars[j].Doors[0].NextReopenTime = TrainManagerBase.CurrentRoute.SecondsSinceMidnight + TrainManagerBase.CurrentRoute.Stations[i].InterferenceInDoor; 404 } 405 else if (Cars[j].Doors[0].ReopenCounter < Cars[j].Doors[0].ReopenLimit) 406 { 407 if (TrainManagerBase.CurrentRoute.SecondsSinceMidnight >= Cars[j].Doors[0].NextReopenTime) 408 { 409 Cars[j].OpenDoors(true, false); 410 } 411 } 412 else 413 { 414 Cars[j].Doors[0].AnticipatedReopen = false; 415 } 416 } 417 418 if (Cars[j].Doors[1].AnticipatedReopen && Cars[j].Doors[1].State == Cars[j].Doors[1].InterferingObjectRate) 419 { 420 if (Cars[j].Doors[1].NextReopenTime == 0.0) 421 { 422 Cars[j].Doors[1].NextReopenTime = TrainManagerBase.CurrentRoute.SecondsSinceMidnight + TrainManagerBase.CurrentRoute.Stations[i].InterferenceInDoor; 423 } 424 else if (Cars[j].Doors[1].ReopenCounter < Cars[j].Doors[1].ReopenLimit) 425 { 426 if (TrainManagerBase.CurrentRoute.SecondsSinceMidnight >= Cars[j].Doors[1].NextReopenTime) 427 { 428 Cars[j].OpenDoors(false, true); 429 } 430 } 431 else 432 { 433 Cars[j].Doors[1].AnticipatedReopen = false; 434 } 435 } 436 } 437 438 TrainDoorState doorState = GetDoorsState(TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors, TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors); 439 if (left | right) 440 { 441 /* 442 * Assume that passengers only board at a scheduled stop 443 * If the player has opened the doors somewhere else (lineside?) 444 * then passengers should not be boarding 445 */ 446 if (doorState != TrainDoorState.AllClosed && TrainManagerBase.CurrentOptions.LoadingSway) 447 { 448 // passengers boarding 449 for (int j = 0; j < Cars.Length; j++) 450 { 451 if (!Cars[j].EnableLoadingSway) continue; 452 double r = 2.0 * TrainManagerBase.CurrentRoute.Stations[i].PassengerRatio * TimeElapsed; 453 if (r >= TrainManagerBase.RandomNumberGenerator.NextDouble()) 454 { 455 int d = 456 (int) Math.Floor(TrainManagerBase.RandomNumberGenerator.NextDouble() * Cars[j].Doors.Length); 457 if (Cars[j].Doors[d].State == 1.0) 458 { 459 Cars[j].Specs.RollShakeDirection += Cars[j].Doors[d].Direction; 460 } 461 } 462 } 463 } 464 } 465 466 if (Specs.DoorCloseMode == DoorMode.Manual || doorState == TrainDoorState.None || doorState == (TrainDoorState.Closed | TrainDoorState.AllClosed) || (TrainManagerBase.CurrentRoute.Stations[Station].Type == StationType.ChangeEnds || TrainManagerBase.CurrentRoute.Stations[Station].Type == StationType.Jump)) 467 { 468 if (left | right) 469 { 470 // departure message 471 if (TrainManagerBase.CurrentRoute.SecondsSinceMidnight > StationDepartureTime && (TrainManagerBase.CurrentRoute.Stations[i].Type != StationType.Terminal || IsPlayerTrain == false)) 472 { 473 StationState = TrainStopState.Completed; 474 switch (TrainManagerBase.CurrentRoute.Stations[i].Type) 475 { 476 case StationType.Normal: 477 if (!IsPlayerTrain) 478 break; // Only trigger messages for the player train 479 if (!TrainManagerBase.CurrentRoute.Stations[i].OpenLeftDoors & !TrainManagerBase.CurrentRoute.Stations[i].OpenRightDoors | Specs.DoorCloseMode != DoorMode.Manual) 480 { 481 TrainManagerBase.currentHost.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, TrainManagerBase.CurrentRoute.SecondsSinceMidnight + 5.0, null); 482 } 483 else 484 { 485 TrainManagerBase.currentHost.AddMessage(Translations.GetInterfaceString("message_station_depart_closedoors"), MessageDependency.None, GameMode.Normal, MessageColor.White, TrainManagerBase.CurrentRoute.SecondsSinceMidnight + 5.0, null); 486 } 487 488 break; 489 case StationType.ChangeEnds: 490 // Change ends always jumps to the NEXT station 491 Jump(i + 1); 492 break; 493 case StationType.Jump: 494 // Jumps to an arbritrary station as defined in the routefile 495 Jump(TrainManagerBase.CurrentRoute.Stations[i].JumpIndex); 496 break; 497 } 498 } 499 } 500 else 501 { 502 StationState = TrainStopState.Completed; 503 if (IsPlayerTrain & TrainManagerBase.CurrentRoute.Stations[i].Type == StationType.Normal) 504 { 505 TrainManagerBase.currentHost.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, TrainManagerBase.CurrentRoute.SecondsSinceMidnight + 5.0, null); 506 } 507 } 508 } 509 } 510 } 511 else 512 { 513 if (StationState != TrainStopState.Jumping) 514 { 515 StationState = TrainStopState.Pending; 516 } 517 518 } 519 520 // automatically close doors 521 if (Specs.DoorCloseMode != DoorMode.Manual & !Specs.DoorClosureAttempted) 522 { 523 if (Station == -1 | StationState == TrainStopState.Completed) 524 { 525 if ((GetDoorsState(true, true) & TrainDoorState.AllClosed) == 0) 526 { 527 CloseDoors(true, true); 528 Specs.DoorClosureAttempted = true; 529 } 530 } 531 } 532 } 533 } 534 } 535