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