1 using System;
2 using System.Collections.Generic;
3 using System.Drawing;
4 using System.IO;
5 using LibRender2.Screens;
6 using OpenBveApi;
7 using OpenBveApi.Colors;
8 using OpenBveApi.Hosts;
9 using OpenBveApi.Interface;
10 using OpenBveApi.Math;
11 using OpenBveApi.Objects;
12 using OpenBveApi.Routes;
13 using OpenBveApi.Sounds;
14 using OpenBveApi.Textures;
15 using OpenBveApi.Trains;
16 using OpenBveApi.World;
17 using RouteManager2.MessageManager;
18 using SoundManager;
19 using TrainManager.Trains;
20 
21 namespace OpenBve {
22 	/// <summary>Represents the host application.</summary>
23 	internal class Host : HostInterface {
24 
25 		// --- functions ---
26 
27 		/// <summary>Reports a problem to the host application.</summary>
28 		/// <param name="type">The type of problem that is reported.</param>
29 		/// <param name="text">The textual message that describes the problem.</param>
ReportProblem(ProblemType type, string text)30 		public override void ReportProblem(ProblemType type, string text) {
31 			switch (type)
32 			{
33 				case ProblemType.DirectoryNotFound:
34 				case ProblemType.FileNotFound:
35 				case ProblemType.PathNotFound:
36 					if (!MissingFiles.Contains(text))
37 					{
38 						Interface.AddMessage(MessageType.Error, true, type + " : " + text);
39 						MissingFiles.Add(text);
40 					}
41 					break;
42 				default:
43 					Interface.AddMessage(MessageType.Error, false, type + " : " + text);
44 					break;
45 			}
46 		}
47 
AddMessage(MessageType type, bool FileNotFound, string text)48 		public override void AddMessage(MessageType type, bool FileNotFound, string text)
49 		{
50 			Interface.AddMessage(type, FileNotFound, text);
51 		}
52 
AddMessage(object Message)53 		public override void AddMessage(object Message)
54 		{
55 			MessageManager.AddMessage((AbstractMessage)Message);
56 		}
57 
AddMessage(string Message, object MessageDependancy, GameMode Mode, MessageColor MessageColor, double MessageTimeOut, string Key)58 		public override void AddMessage(string Message, object MessageDependancy, GameMode Mode, MessageColor MessageColor, double MessageTimeOut, string Key)
59 		{
60 			MessageManager.AddMessage(Message, (MessageDependency)MessageDependancy, Mode, MessageColor, MessageTimeOut, Key);
61 		}
62 
63 		// --- texture ---
64 
65 		/// <summary>Queries the dimensions of a texture.</summary>
66 		/// <param name="path">The path to the file or folder that contains the texture.</param>
67 		/// <param name="width">Receives the width of the texture.</param>
68 		/// <param name="height">Receives the height of the texture.</param>
69 		/// <returns>Whether querying the dimensions was successful.</returns>
QueryTextureDimensions(string path, out int width, out int height)70 		public override bool QueryTextureDimensions(string path, out int width, out int height) {
71 			if (File.Exists(path) || Directory.Exists(path)) {
72 				for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) {
73 					if (Program.CurrentHost.Plugins[i].Texture != null) {
74 						try {
75 							if (Program.CurrentHost.Plugins[i].Texture.CanLoadTexture(path)) {
76 								try {
77 									if (Program.CurrentHost.Plugins[i].Texture.QueryTextureDimensions(path, out width, out height)) {
78 										return true;
79 									}
80 									Interface.AddMessage(MessageType.Error, false,
81 									                     "Plugin " + Program.CurrentHost.Plugins[i].Title + " returned unsuccessfully at QueryTextureDimensions"
82 									                    );
83 								} catch (Exception ex) {
84 									Interface.AddMessage(MessageType.Error, false,
85 									                     "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at QueryTextureDimensions:" + ex.Message
86 									                    );
87 								}
88 							}
89 						} catch (Exception ex) {
90 							Interface.AddMessage(MessageType.Error, false,
91 							                     "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at CanLoadTexture:" + ex.Message
92 							                    );
93 						}
94 					}
95 				}
96 				FileInfo f = new FileInfo(path);
97 				if (f.Length == 0)
98 				{
99 					Interface.AddMessage(MessageType.Error, false, "Zero-byte texture file encountered at " + path);
100 				}
101 				else
102 				{
103 					Interface.AddMessage(MessageType.Error, false, "No plugin found that is capable of loading texture " + path);
104 				}
105 			} else {
106 				ReportProblem(ProblemType.PathNotFound, path);
107 			}
108 			width = 0;
109 			height = 0;
110 			return false;
111 		}
112 
113 		/// <summary>Loads a texture and returns the texture data.</summary>
114 		/// <param name="path">The path to the file or folder that contains the texture.</param>
115 		/// <param name="parameters">The parameters that specify how to process the texture.</param>
116 		/// <param name="texture">Receives the texture.</param>
117 		/// <returns>Whether loading the texture was successful.</returns>
LoadTexture(string path, TextureParameters parameters, out Texture texture)118 		public override bool LoadTexture(string path, TextureParameters parameters, out Texture texture) {
119 			if (File.Exists(path) || Directory.Exists(path)) {
120 				for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) {
121 					if (Program.CurrentHost.Plugins[i].Texture != null) {
122 						try {
123 							if (Program.CurrentHost.Plugins[i].Texture.CanLoadTexture(path)) {
124 								try {
125 									if (Program.CurrentHost.Plugins[i].Texture.LoadTexture(path, out texture)) {
126 										texture.CompatibleTransparencyMode = Interface.CurrentOptions.OldTransparencyMode;
127 										texture = texture.ApplyParameters(parameters);
128 										return true;
129 									}
130 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " returned unsuccessfully at LoadTexture");
131 								} catch (Exception ex) {
132 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at LoadTexture:" + ex.Message);
133 								}
134 							}
135 						} catch (Exception ex) {
136 							Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at CanLoadTexture:" + ex.Message);
137 						}
138 					}
139 				}
140 				FileInfo f = new FileInfo(path);
141 				if (f.Length == 0)
142 				{
143 					Interface.AddMessage(MessageType.Error, false, "Zero-byte texture file encountered at " + path);
144 				}
145 				else
146 				{
147 					Interface.AddMessage(MessageType.Error, false, "No plugin found that is capable of loading texture " + path);
148 				}
149 			} else {
150 				ReportProblem(ProblemType.PathNotFound, path);
151 			}
152 			texture = null;
153 			return false;
154 		}
155 
LoadTexture(ref Texture Texture, OpenGlTextureWrapMode wrapMode)156 		public override bool LoadTexture(ref Texture Texture, OpenGlTextureWrapMode wrapMode)
157 		{
158 			return Program.Renderer.TextureManager.LoadTexture(ref Texture, wrapMode, CPreciseTimer.GetClockTicks(), Interface.CurrentOptions.Interpolation, Interface.CurrentOptions.AnisotropicFilteringLevel);
159 		}
160 
161 		/// <summary>Registers a texture and returns a handle to the texture.</summary>
162 		/// <param name="path">The path to the file or folder that contains the texture.</param>
163 		/// <param name="parameters">The parameters that specify how to process the texture.</param>
164 		/// <param name="handle">Receives the handle to the texture.</param>
165 		/// <param name="loadTexture">Whether the texture is to be pre-loaded</param>
166 		/// <returns>Whether loading the texture was successful.</returns>
RegisterTexture(string path, TextureParameters parameters, out Texture handle, bool loadTexture = false)167 		public override bool RegisterTexture(string path, TextureParameters parameters, out Texture handle, bool loadTexture = false) {
168 			if (File.Exists(path) || Directory.Exists(path)) {
169 				Texture data;
170 				if (Program.Renderer.TextureManager.RegisterTexture(path, parameters, out data)) {
171 					handle = data;
172 					if (loadTexture)
173 					{
174 						OpenBVEGame.RunInRenderThread(() =>
175 						{
176 							LoadTexture(ref data, OpenGlTextureWrapMode.ClampClamp);
177 						});
178 
179 					}
180 					return true;
181 				}
182 			} else {
183 				ReportProblem(ProblemType.PathNotFound, path);
184 			}
185 			handle = null;
186 			return false;
187 		}
188 
189 		/// <summary>Registers a texture and returns a handle to the texture.</summary>
190 		/// <param name="texture">The texture data.</param>
191 		/// <param name="parameters">The parameters that specify how to process the texture.</param>
192 		/// <param name="handle">Receives the handle to the texture.</param>
193 		/// <returns>Whether loading the texture was successful.</returns>
RegisterTexture(Texture texture, TextureParameters parameters, out Texture handle)194 		public override bool RegisterTexture(Texture texture, TextureParameters parameters, out Texture handle) {
195 			texture = texture.ApplyParameters(parameters);
196 			handle = Program.Renderer.TextureManager.RegisterTexture(texture);
197 			return true;
198 		}
199 
RegisterTexture(Bitmap texture, TextureParameters parameters, out Texture handle)200 		public override bool RegisterTexture(Bitmap texture, TextureParameters parameters, out Texture handle)
201 		{
202 			handle = new Texture(texture, parameters);
203 			return true;
204 		}
205 
206 		// --- sound ---
207 
208 		/// <summary>Loads a sound and returns the sound data.</summary>
209 		/// <param name="path">The path to the file or folder that contains the sound.</param>
210 		/// <param name="sound">Receives the sound.</param>
211 		/// <returns>Whether loading the sound was successful.</returns>
LoadSound(string path, out Sound sound)212 		public override bool LoadSound(string path, out Sound sound) {
213 			if (File.Exists(path) || Directory.Exists(path)) {
214 				for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) {
215 					if (Program.CurrentHost.Plugins[i].Sound != null) {
216 						try {
217 							if (Program.CurrentHost.Plugins[i].Sound.CanLoadSound(path)) {
218 								try {
219 									if (Program.CurrentHost.Plugins[i].Sound.LoadSound(path, out sound)) {
220 										return true;
221 									}
222 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " returned unsuccessfully at LoadSound");
223 								} catch (Exception ex) {
224 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at LoadSound:" + ex.Message);
225 								}
226 							}
227 						} catch (Exception ex) {
228 							Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at CanLoadSound:" + ex.Message);
229 						}
230 					}
231 				}
232 				Interface.AddMessage(MessageType.Error, false, "No plugin found that is capable of loading sound " + path);
233 			} else {
234 				ReportProblem(ProblemType.PathNotFound, path);
235 			}
236 			sound = null;
237 			return false;
238 		}
239 
240 
241 		/// <summary>Registers a sound and returns a handle to the sound.</summary>
242 		/// <param name="path">The path to the file or folder that contains the sound.</param>
243 		/// <param name="handle">Receives a handle to the sound.</param>
244 		/// <returns>Whether loading the sound was successful.</returns>
RegisterSound(string path, out SoundHandle handle)245 		public override bool RegisterSound(string path, out SoundHandle handle)
246 		{
247 			if (File.Exists(path) || Directory.Exists(path))
248 			{
249 				handle = Program.Sounds.RegisterBuffer(path, 0.0);
250 				return true;
251 			}
252 			ReportProblem(ProblemType.PathNotFound, path);
253 			handle = null;
254 			return false;
255 		}
256 
257 		/// <summary>Registers a sound and returns a handle to the sound.</summary>
258 		/// <param name="path">The path to the file or folder that contains the sound.</param>
259 		/// /// <param name="radius">The sound radius</param>
260 		/// <param name="handle">Receives a handle to the sound.</param>
261 		/// <returns>Whether loading the sound was successful.</returns>
RegisterSound(string path, double radius, out SoundHandle handle)262 		public override bool RegisterSound(string path, double radius, out SoundHandle handle)
263 		{
264 			if (File.Exists(path) || Directory.Exists(path))
265 			{
266 				handle = Program.Sounds.RegisterBuffer(path, radius);
267 				return true;
268 			}
269 			ReportProblem(ProblemType.PathNotFound, path);
270 			handle = null;
271 			return false;
272 		}
273 
274 		/// <summary>Registers a sound and returns a handle to the sound.</summary>
275 		/// <param name="sound">The sound data.</param>
276 		/// <param name="handle">Receives a handle to the sound.</param>
277 		/// <returns>Whether loading the sound was successful.</returns>
RegisterSound(Sound sound, out SoundHandle handle)278 		public override bool RegisterSound(Sound sound, out SoundHandle handle)
279 		{
280 			handle = Program.Sounds.RegisterBuffer(sound, 0.0);
281 			return true;
282 		}
283 
LoadStaticObject(string path, System.Text.Encoding Encoding, bool PreserveVertices, out StaticObject Object)284 		public override bool LoadStaticObject(string path, System.Text.Encoding Encoding, bool PreserveVertices, out StaticObject Object)
285 		{
286 			if (base.LoadStaticObject(path, Encoding, PreserveVertices, out Object))
287 			{
288 				return true;
289 			}
290 
291 			if (File.Exists(path) || Directory.Exists(path)) {
292 				Encoding = TextEncoding.GetSystemEncodingFromFile(path, Encoding);
293 
294 				for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) {
295 					if (Program.CurrentHost.Plugins[i].Object != null) {
296 						try {
297 							if (Program.CurrentHost.Plugins[i].Object.CanLoadObject(path)) {
298 								try {
299 									UnifiedObject unifiedObject;
300 									if (Program.CurrentHost.Plugins[i].Object.LoadObject(path, Encoding, out unifiedObject)) {
301 										StaticObject staticObject = unifiedObject as StaticObject;
302 										if (staticObject != null)
303 										{
304 											staticObject.OptimizeObject(PreserveVertices, Interface.CurrentOptions.ObjectOptimizationBasicThreshold, Interface.CurrentOptions.ObjectOptimizationVertexCulling);
305 											Object = staticObject;
306 											StaticObjectCache.Add(ValueTuple.Create(path, PreserveVertices), Object);
307 											return true;
308 										}
309 
310 										Object = null;
311 										Interface.AddMessage(MessageType.Error, false, "Attempted to load " + path + " which is an animated object where only static objects are allowed.");
312 									}
313 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " returned unsuccessfully at LoadObject");
314 								} catch (Exception ex) {
315 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at LoadObject:" + ex.Message);
316 								}
317 							}
318 						} catch (Exception ex) {
319 							Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at CanLoadObject:" + ex.Message);
320 						}
321 					}
322 				}
323 				Interface.AddMessage(MessageType.Error, false, "No plugin found that is capable of loading object " + path);
324 			} else {
325 				ReportProblem(ProblemType.PathNotFound, path);
326 			}
327 			Object = null;
328 			return false;
329 		}
330 
LoadObject(string path, System.Text.Encoding Encoding, out UnifiedObject Object)331 		public override bool LoadObject(string path, System.Text.Encoding Encoding, out UnifiedObject Object)
332 		{
333 			if (base.LoadObject(path, Encoding, out Object))
334 			{
335 				return true;
336 			}
337 
338 			if (File.Exists(path) || Directory.Exists(path)) {
339 				Encoding = TextEncoding.GetSystemEncodingFromFile(path, Encoding);
340 
341 				for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++) {
342 					if (Program.CurrentHost.Plugins[i].Object != null) {
343 						try {
344 							if (Program.CurrentHost.Plugins[i].Object.CanLoadObject(path)) {
345 								try
346 								{
347 									UnifiedObject obj;
348 									if (Program.CurrentHost.Plugins[i].Object.LoadObject(path, Encoding, out obj)) {
349 										obj.OptimizeObject(false, Interface.CurrentOptions.ObjectOptimizationBasicThreshold, Interface.CurrentOptions.ObjectOptimizationVertexCulling);
350 										Object = obj;
351 
352 										StaticObject staticObject = Object as StaticObject;
353 										if (staticObject != null)
354 										{
355 											StaticObjectCache.Add(ValueTuple.Create(path, false), staticObject);
356 											return true;
357 										}
358 
359 										AnimatedObjectCollection aoc = Object as AnimatedObjectCollection;
360 										if (aoc != null)
361 										{
362 											AnimatedObjectCollectionCache.Add(path, aoc);
363 										}
364 
365 										return true;
366 									}
367 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " returned unsuccessfully at LoadObject");
368 								} catch (Exception ex) {
369 									Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at LoadObject:" + ex.Message);
370 								}
371 							}
372 						} catch (Exception ex) {
373 							Interface.AddMessage(MessageType.Error, false, "Plugin " + Program.CurrentHost.Plugins[i].Title + " raised the following exception at CanLoadObject:" + ex.Message);
374 						}
375 					}
376 				}
377 				Interface.AddMessage(MessageType.Error, false, "No plugin found that is capable of loading object " + path);
378 			} else {
379 				ReportProblem(ProblemType.PathNotFound, path);
380 			}
381 			Object = null;
382 			return false;
383 		}
384 
ExecuteFunctionScript(OpenBveApi.FunctionScripting.FunctionScript functionScript, AbstractTrain train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed, int CurrentState)385 		public override void ExecuteFunctionScript(OpenBveApi.FunctionScripting.FunctionScript functionScript, AbstractTrain train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed, int CurrentState)
386 		{
387 			FunctionScripts.ExecuteFunctionScript(functionScript, (TrainBase)train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed, CurrentState);
388 		}
389 
CreateStaticObject(StaticObject Prototype, Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double TrackPosition, double Brightness)390 		public override int CreateStaticObject(StaticObject Prototype, Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double TrackPosition, double Brightness)
391 		{
392 			return Program.Renderer.CreateStaticObject(Prototype, Position, WorldTransformation, LocalTransformation, Program.CurrentRoute.AccurateObjectDisposal, AccurateObjectDisposalZOffset, StartingDistance, EndingDistance, Program.CurrentRoute.BlockLength, TrackPosition, Brightness);
393 		}
394 
CreateStaticObject(StaticObject Prototype, Transformation LocalTransformation, Matrix4D Rotate, Matrix4D Translate, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double TrackPosition, double Brightness)395 		public override int CreateStaticObject(StaticObject Prototype, Transformation LocalTransformation, Matrix4D Rotate, Matrix4D Translate, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double TrackPosition, double Brightness)
396 		{
397 			return Program.Renderer.CreateStaticObject(Prototype, LocalTransformation, Rotate, Translate, Program.CurrentRoute.AccurateObjectDisposal, AccurateObjectDisposalZOffset, StartingDistance, EndingDistance, Program.CurrentRoute.BlockLength, TrackPosition, Brightness);
398 		}
399 
CreateDynamicObject(ref ObjectState internalObject)400 		public override void CreateDynamicObject(ref ObjectState internalObject)
401 		{
402 			Program.Renderer.CreateDynamicObject(ref internalObject);
403 		}
404 
AddObjectForCustomTimeTable(AnimatedObject animatedObject)405 		public override void AddObjectForCustomTimeTable(AnimatedObject animatedObject)
406 		{
407 			Timetable.AddObjectForCustomTimetable(animatedObject);
408 		}
409 
ShowObject(ObjectState objectToShow, ObjectType objectType)410 		public override void ShowObject(ObjectState objectToShow, ObjectType objectType)
411 		{
412 			Program.Renderer.VisibleObjects.ShowObject(objectToShow, objectType);
413 		}
414 
HideObject(ObjectState objectToHide)415 		public override void HideObject(ObjectState objectToHide)
416 		{
417 			Program.Renderer.VisibleObjects.HideObject(objectToHide);
418 		}
419 
SoundIsPlaying(object SoundSource)420 		public override bool SoundIsPlaying(object SoundSource)
421 		{
422 			return Program.Sounds.IsPlaying(SoundSource);
423 		}
424 
PlaySound(SoundHandle buffer, double pitch, double volume, Vector3 position, object parent, bool looped)425 		public override object PlaySound(SoundHandle buffer, double pitch, double volume, Vector3 position, object parent, bool looped)
426 		{
427 			return Program.Sounds.PlaySound(buffer, pitch, volume, position, parent, looped);
428 		}
429 
PlayMicSound(Vector3 position, double backwardTolerance, double forwardTolerance)430 		public override void PlayMicSound(Vector3 position, double backwardTolerance, double forwardTolerance)
431 		{
432 			Program.Sounds.PlayMicSound(position, backwardTolerance, forwardTolerance);
433 		}
434 
StopSound(object SoundSource)435 		public override void StopSound(object SoundSource)
436 		{
437 			Program.Sounds.StopSound(SoundSource as SoundSource);
438 		}
439 
StopAllSounds(object parent)440 		public override void StopAllSounds(object parent)
441 		{
442 			Program.Sounds.StopAllSounds(parent);
443 		}
444 
445 		public override SimulationState SimulationState
446 		{
447 			get
448 			{
449 				if (!Loading.SimulationSetup)
450 				{
451 					return SimulationState.Loading;
452 				}
453 
454 				if (Game.MinimalisticSimulation)
455 				{
456 					return SimulationState.MinimalisticSimulation;
457 				}
458 				return SimulationState.Running;
459 			}
460 		}
461 
462 		public override int AnimatedWorldObjectsUsed
463 		{
464 			get
465 			{
466 				return ObjectManager.AnimatedWorldObjectsUsed;
467 			}
468 			set
469 			{
470 				int a = ObjectManager.AnimatedWorldObjectsUsed;
471 				if (ObjectManager.AnimatedWorldObjects.Length -1 == a)
472 				{
473 					/*
474 					 * HACK: We cannot resize an array via an accessor property
475 					 *       With this in mind, resize it via the indexer instead
476 					 */
477 					Array.Resize(ref ObjectManager.AnimatedWorldObjects, ObjectManager.AnimatedWorldObjects.Length << 1);
478 				}
479 
480 				ObjectManager.AnimatedWorldObjectsUsed = value;
481 			}
482 		}
483 
484 		public override WorldObject[] AnimatedWorldObjects
485 		{
486 			get
487 			{
488 				return ObjectManager.AnimatedWorldObjects;
489 			}
490 			set
491 			{
492 				ObjectManager.AnimatedWorldObjects = value;
493 			}
494 		}
495 
496 		public override Dictionary<int, Track> Tracks
497 		{
498 			get
499 			{
500 				return Program.CurrentRoute.Tracks;
501 			}
502 			set
503 			{
504 				Program.CurrentRoute.Tracks = value;
505 			}
506 		}
507 
UpdateCustomTimetable(Texture Daytime, Texture Nighttime)508 		public override void UpdateCustomTimetable(Texture Daytime, Texture Nighttime)
509 		{
510 			Timetable.UpdateCustomTimetable(Daytime, Nighttime);
511 		}
512 
ParseTrackFollowingObject(string objectPath, string tfoFile)513 		public override AbstractTrain ParseTrackFollowingObject(string objectPath, string tfoFile)
514 		{
515 			return TrackFollowingObjectParser.ParseTrackFollowingObject(objectPath, tfoFile);
516 		}
517 
AddMarker(Texture MarkerTexture)518 		public override void AddMarker(Texture MarkerTexture)
519 		{
520 			Program.Renderer.Marker.AddMarker(MarkerTexture);
521 		}
522 
RemoveMarker(Texture MarkerTexture)523 		public override void RemoveMarker(Texture MarkerTexture)
524 		{
525 			Program.Renderer.Marker.RemoveMarker(MarkerTexture);
526 		}
527 
CameraAtWorldEnd()528 		public override void CameraAtWorldEnd()
529 		{
530 			Program.Renderer.Camera.AtWorldEnd = !Program.Renderer.Camera.AtWorldEnd;
531 		}
532 
533 		public override double InGameTime => Program.CurrentRoute.SecondsSinceMidnight;
534 
AddBlackBoxEntry()535 		public override void AddBlackBoxEntry()
536 		{
537 			Game.AddBlackBoxEntry();
538 		}
539 
ProcessJump(AbstractTrain Train, int StationIndex)540 		public override void ProcessJump(AbstractTrain Train, int StationIndex)
541 		{
542 			ObjectManager.ProcessJump(Train);
543 			if (Train.IsPlayerTrain)
544 			{
545 				if (Game.CurrentScore.ArrivalStation <= StationIndex)
546 				{
547 					Game.CurrentScore.ArrivalStation = StationIndex + 1;
548 				}
549 				Game.CurrentScore.DepartureStation = StationIndex;
550 				Program.Renderer.CurrentInterface = InterfaceType.Normal;
551 				Program.TrainManager.UnderailTrains();
552 			}
553 		}
554 
AddScore(int Score, string Message, MessageColor Color, double Timeout)555 		public override void AddScore(int Score, string Message, MessageColor Color, double Timeout)
556 		{
557 			Game.CurrentScore.CurrentValue += Score;
558 			int n = Game.ScoreMessages.Length;
559 			Array.Resize(ref Game.ScoreMessages, n + 1);
560 			Game.ScoreMessages[n] = new Game.ScoreMessage
561 			{
562 				Value = Score,
563 				Color = Color,
564 				RendererPosition = new Vector2(0, 0),
565 				RendererAlpha = 0.0,
566 				Text = Message,
567 				Timeout = Timeout
568 			};
569 		}
570 
571 		public override AbstractTrain[] Trains
572 		{
573 			get
574 			{
575 				// ReSharper disable once CoVariantArrayConversion
576 				return Program.TrainManager.Trains;
577 			}
578 		}
579 
ClosestTrain(AbstractTrain Train)580 		public override AbstractTrain ClosestTrain(AbstractTrain Train)
581 		{
582 			TrainBase baseTrain = Train as TrainBase;
583 			AbstractTrain closestTrain = null;
584 			double bestLocation = double.MaxValue;
585 			if(baseTrain != null)
586 			{
587 				for (int i = 0; i < Program.TrainManager.Trains.Length; i++)
588 				{
589 					if (Program.TrainManager.Trains[i] != baseTrain & Program.TrainManager.Trains[i].State == TrainState.Available & baseTrain.Cars.Length > 0)
590 					{
591 						TrainBase train = Program.TrainManager.Trains[i];
592 						int c = train.Cars.Length - 1;
593 						double z = train.Cars[c].RearAxle.Follower.TrackPosition - train.Cars[c].RearAxle.Position - 0.5 * train.Cars[c].Length;
594 						if (z >= baseTrain.FrontCarTrackPosition() & z < bestLocation)
595 						{
596 							bestLocation = z;
597 							closestTrain = Program.TrainManager.Trains[i];
598 						}
599 					}
600 				}
601 			}
602 			return closestTrain;
603 		}
604 
ClosestTrain(double TrackPosition)605 		public override AbstractTrain ClosestTrain(double TrackPosition)
606 		{
607 			AbstractTrain closestTrain = null;
608 			double trainDistance = double.MaxValue;
609 			for (int j = 0; j < Program.TrainManager.Trains.Length; j++)
610 			{
611 				if (Program.TrainManager.Trains[j].State == TrainState.Available)
612 				{
613 					double distance;
614 					if (Program.TrainManager.Trains[j].Cars[0].FrontAxle.Follower.TrackPosition < TrackPosition)
615 					{
616 						distance = TrackPosition - Program.TrainManager.Trains[j].Cars[0].TrackPosition;
617 					}
618 					else if (Program.TrainManager.Trains[j].Cars[Program.TrainManager.Trains[j].Cars.Length - 1].RearAxle.Follower.TrackPosition > TrackPosition)
619 					{
620 						distance = Program.TrainManager.Trains[j].Cars[Program.TrainManager.Trains[j].Cars.Length - 1].RearAxle.Follower.TrackPosition - TrackPosition;
621 					}
622 					else
623 					{
624 						distance = 0;
625 					}
626 					if (distance < trainDistance)
627 					{
628 						closestTrain = Program.TrainManager.Trains[j];
629 						trainDistance = distance;
630 					}
631 				}
632 			}
633 			return closestTrain;
634 		}
635 
Host()636 		public Host() : base(HostApplication.OpenBve)
637 		{
638 		}
639 	}
640 }
641