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