/// <summary>Creates a mirrored copy of the prototype object (Animated objects)</summary> /// <param name="Prototype">The prototype</param> /// <returns>The mirrored copy</returns> private static UnifiedObject GetMirroredObject(UnifiedObject Prototype) { if (Prototype is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)Prototype; return(GetMirroredStaticObject(s)); } if (Prototype is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)Prototype; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection { Objects = new ObjectManager.AnimatedObject[a.Objects.Length] }; for (int i = 0; i < a.Objects.Length; i++) { Result.Objects[i] = a.Objects[i].Clone(); for (int j = 0; j < a.Objects[i].States.Length; j++) { Result.Objects[i].States[j].Object = GetMirroredStaticObject(a.Objects[i].States[j].Object); } Result.Objects[i].TranslateXDirection.X *= -1.0; Result.Objects[i].TranslateYDirection.X *= -1.0; Result.Objects[i].TranslateZDirection.X *= -1.0; Result.Objects[i].RotateXDirection.X *= -1.0; Result.Objects[i].RotateYDirection.X *= -1.0; Result.Objects[i].RotateZDirection.X *= -1.0; } return(Result); } return(null); }
internal void LoadCarSections(UnifiedObject currentObject) { int j = CarSections.Length; Array.Resize(ref CarSections, j + 1); CarSections[j] = new CarSection(); if (currentObject is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)currentObject; CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; CarSections[j].Elements[0] = new ObjectManager.AnimatedObject { States = new ObjectManager.AnimatedObjectState[1] }; CarSections[j].Elements[0].States[0].Position = Vector3.Zero; CarSections[j].Elements[0].States[0].Object = s; CarSections[j].Elements[0].CurrentState = 0; CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (currentObject is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)currentObject; CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { CarSections[j].Elements[h] = a.Objects[h].Clone(); CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } }
// parse animated object config /// <summary>Loads a collection of animated objects from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[4]; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } if (Program.CurrentProgramType == Program.ProgramType.ObjectViewer | Program.CurrentProgramType == Program.ProgramType.RouteViewer) { if (Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0) { rpnUsed = true; } } } if (rpnUsed) { Interface.AddMessage(Interface.MessageType.Warning, false, "An animated object file contains non-official RPN functions. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; World.Vector3D position = new World.Vector3D(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { position = new World.Vector3D(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { string Folder = System.IO.Path.GetDirectoryName(FileName); if (Interface.ContainsInvalidPathChars(Lines[i])) { Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = Interface.GetCombinedFileName(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = 1; if (ObjectCount >= Result.Objects.Length) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState(); aos.Object = s; aos.Position = position; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject(); Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; Result.Objects[ObjectCount].CurrentState = -1; Result.Objects[ObjectCount].TranslateXDirection = new World.Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].TranslateYDirection = new World.Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].TranslateZDirection = new World.Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].RotateXDirection = new World.Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].RotateYDirection = new World.Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].RotateZDirection = new World.Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].TextureShiftXDirection = new World.Vector2D(1.0, 0.0); Result.Objects[ObjectCount].TextureShiftYDirection = new World.Vector2D(0.0, 1.0); Result.Objects[ObjectCount].RefreshRate = 0.0; Result.Objects[ObjectCount].ObjectIndex = -1; World.Vector3D Position = new World.Vector3D(0.0, 0.0, 0.0); string[] StateFiles = null; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Position = new World.Vector3D(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "states": { string[] s = b.Split(','); if (s.Length >= 1) { string Folder = System.IO.Path.GetDirectoryName(FileName); StateFiles = new string[s.Length]; for (int k = 0; k < s.Length; k++) { s[k] = s[k].Trim(); if (Interface.ContainsInvalidPathChars(s[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else { StateFiles[k] = Interface.GetCombinedFileName(Folder, s[k]); if (!System.IO.File.Exists(StateFiles[k])) { Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } break; case "statefunction": try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatexdirection": case "translateydirection": case "translatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "translatexdirection": Result.Objects[ObjectCount].TranslateXDirection = new World.Vector3D(x, y, z); break; case "translateydirection": Result.Objects[ObjectCount].TranslateYDirection = new World.Vector3D(x, y, z); break; case "translatezdirection": Result.Objects[ObjectCount].TranslateZDirection = new World.Vector3D(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "translatexfunction": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunction": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunction": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatexfunctionrpn": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunctionrpn": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunctionrpn": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdirection": case "rotateydirection": case "rotatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (x == 0.0 & y == 0.0 & z == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdirection": Result.Objects[ObjectCount].RotateXDirection = new World.Vector3D(x, y, z); break; case "rotateydirection": Result.Objects[ObjectCount].RotateYDirection = new World.Vector3D(x, y, z); break; case "rotatezdirection": Result.Objects[ObjectCount].RotateZDirection = new World.Vector3D(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "rotatexfunction": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunction": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunction": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexfunctionrpn": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunctionrpn": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunctionrpn": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdamping": case "rotateydamping": case "rotatezdamping": { string[] s = b.Split(','); if (s.Length == 2) { double nf, dr; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf)) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr)) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (nf <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (dr <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdamping": Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr); break; case "rotateydamping": Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr); break; case "rotatezdamping": Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxdirection": case "textureshiftydirection": { string[] s = b.Split(','); if (s.Length == 2) { double x, y; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "textureshiftxdirection": Result.Objects[ObjectCount].TextureShiftXDirection = new World.Vector2D(x, y); break; case "textureshiftydirection": Result.Objects[ObjectCount].TextureShiftYDirection = new World.Vector2D(x, y); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxfunction": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunction": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftxfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "refreshrate": { double r; if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } i++; } i--; if (StateFiles != null) { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new World.Vector3D(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); Result.Objects[ObjectCount].States[k].Object.Dynamic = 1; } else { Result.Objects[ObjectCount].States[k].Object = null; } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { Result.Objects[ObjectCount].States[j].Position = Position; } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } } Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return Result; }
internal static ObjectManager.AnimatedObjectCollection ReadObject(string fileName) { MsTsShape shape = new MsTsShape(); ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection { Objects = new ObjectManager.AnimatedObject[4] }; currentFolder = Path.GetDirectoryName(fileName); Stream fb = new FileStream(fileName, FileMode.Open, FileAccess.Read); byte[] buffer = new byte[34]; fb.Read(buffer, 0, 2); bool unicode = (buffer[0] == 0xFF && buffer[1] == 0xFE); string headerString; if (unicode) { fb.Read(buffer, 0, 32); headerString = Encoding.Unicode.GetString(buffer, 0, 16); } else { fb.Read(buffer, 2, 14); headerString = Encoding.ASCII.GetString(buffer, 0, 8); } // SIMISA@F means compressed // SIMISA@@ means uncompressed if (headerString.StartsWith("SIMISA@F")) { fb = new ZlibStream(fb, CompressionMode.Decompress); } else if (headerString.StartsWith("\r\nSIMISA")) { // ie us1rd2l1000r10d.s, we are going to allow this but warn Console.Error.WriteLine("Improper header in " + fileName); fb.Read(buffer, 0, 4); } else if (!headerString.StartsWith("SIMISA@@")) { throw new Exception("Unrecognized shape file header " + headerString + " in " + fileName); } string subHeader; if (unicode) { fb.Read(buffer, 0, 32); subHeader = Encoding.Unicode.GetString(buffer, 0, 16); } else { fb.Read(buffer, 0, 16); subHeader = Encoding.ASCII.GetString(buffer, 0, 8); } if (subHeader[7] == 't') { using (BinaryReader reader = new BinaryReader(fb)) { byte[] newBytes = reader.ReadBytes((int)(fb.Length - fb.Position)); string s; if (unicode) { s = Encoding.Unicode.GetString(newBytes); } else { s = Encoding.ASCII.GetString(newBytes); } s = s.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " ").Trim(); if (!s.StartsWith("shape", StringComparison.InvariantCultureIgnoreCase)) { throw new Exception(); //Shape definition } TextualBlock block = new TextualBlock(s, KujuTokenID.shape); ParseBlock(block, ref shape); } } else if (subHeader[7] != 'b') { throw new Exception("Unrecognized subHeader \"" + subHeader + "\" in " + fileName); } else { using (BinaryReader reader = new BinaryReader(fb)) { KujuTokenID currentToken = (KujuTokenID)reader.ReadUInt16(); if (currentToken != KujuTokenID.shape) { throw new Exception(); //Shape definition } reader.ReadUInt16(); uint remainingBytes = reader.ReadUInt32(); byte[] newBytes = reader.ReadBytes((int)remainingBytes); BinaryBlock block = new BinaryBlock(newBytes, KujuTokenID.shape); ParseBlock(block, ref shape); } } Array.Resize(ref Result.Objects, shape.totalObjects); int idx = 0; double[] previousLODs = new double[shape.totalObjects]; for (int i = 0; i < shape.LODs.Count; i++) { for (int j = 0; j < shape.LODs[i].subObjects.Count; j++) { Result.Objects[idx] = new ObjectManager.AnimatedObject(); Result.Objects[idx].States = new AnimatedObjectState[1]; AnimatedObjectState aos = new AnimatedObjectState(null, Vector3.Zero); shape.LODs[i].subObjects[j].Apply(out aos.Object); aos.Position = new Vector3(0, 0, 0); Result.Objects[idx].States[0] = aos; previousLODs[idx] = shape.LODs[i].viewingDistance; int k = idx; while (k > 0) { if (previousLODs[k] < shape.LODs[i].viewingDistance) { break; } k--; } if (k != 0) { Result.Objects[idx].StateFunction = new FunctionScript(Program.CurrentHost, "if[cameraDistance <" + shape.LODs[i].viewingDistance + ",if[cameraDistance >" + previousLODs[k] + ",0,-1],-1]", true); } else { Result.Objects[idx].StateFunction = new FunctionScript(Program.CurrentHost, "if[cameraDistance <" + shape.LODs[i].viewingDistance + ",0,-1]", true); } idx++; } } return(Result); }
// apply route data private static void ApplyRouteData(string FileName, System.Text.Encoding Encoding, ref RouteData Data, bool PreviewOnly) { string SignalPath, LimitPath, LimitGraphicsPath, TransponderPath; ObjectManager.StaticObject SignalPost, LimitPostStraight, LimitPostLeft, LimitPostRight, LimitPostInfinite; ObjectManager.StaticObject LimitOneDigit, LimitTwoDigits, LimitThreeDigits, StopPost; ObjectManager.StaticObject TransponderS, TransponderSN, TransponderFalseStart, TransponderPOrigin, TransponderPStop; if (!PreviewOnly) { string CompatibilityFolder = Program.FileSystem.GetDataFolder("Compatibility"); // load compatibility objects SignalPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Signals"); SignalPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(SignalPath, "signal_post.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Limits"); LimitGraphicsPath = OpenBveApi.Path.CombineDirectory(LimitPath, "Graphics"); LimitPostStraight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_straight.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPostLeft = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_left.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPostRight = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_right.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitPostInfinite = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_infinite.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitOneDigit = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_1_digit.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitTwoDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_2_digits.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); LimitThreeDigits = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(LimitPath, "limit_3_digits.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); StopPost = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(CompatibilityFolder, "stop.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderPath = OpenBveApi.Path.CombineDirectory(CompatibilityFolder, "Transponders"); TransponderS = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "s.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderSN = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "sn.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderFalseStart = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "falsestart.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderPOrigin = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "porigin.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); TransponderPStop = ObjectManager.LoadStaticObject(OpenBveApi.Path.CombineFile(TransponderPath, "pstop.csv"), Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { SignalPath = null; LimitPath = null; LimitGraphicsPath = null; TransponderPath = null; SignalPost = null; LimitPostStraight = null; LimitPostLeft = null; LimitPostRight = null; LimitPostInfinite = null; LimitOneDigit = null; LimitTwoDigits = null; LimitThreeDigits = null; StopPost = null; TransponderS = null; TransponderSN = null; TransponderFalseStart = null; TransponderPOrigin = null; TransponderPStop = null; } // initialize System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int LastBlock = (int)Math.Floor((Data.TrackPosition + 600.0) / Data.BlockInterval + 0.001) + 1; int BlocksUsed = Data.Blocks.Length; CreateMissingBlocks(ref Data, ref BlocksUsed, LastBlock, PreviewOnly); Array.Resize<Block>(ref Data.Blocks, BlocksUsed); // interpolate height if (!PreviewOnly) { int z = 0; for (int i = 0; i < Data.Blocks.Length; i++) { if (!double.IsNaN(Data.Blocks[i].Height)) { for (int j = i - 1; j >= 0; j--) { if (!double.IsNaN(Data.Blocks[j].Height)) { double a = Data.Blocks[j].Height; double b = Data.Blocks[i].Height; double d = (b - a) / (double)(i - j); for (int k = j + 1; k < i; k++) { a += d; Data.Blocks[k].Height = a; } break; } } z = i; } } for (int i = z + 1; i < Data.Blocks.Length; i++) { Data.Blocks[i].Height = Data.Blocks[z].Height; } } // background if (!PreviewOnly) { if (Data.Blocks[0].Background >= 0 & Data.Blocks[0].Background < Data.Backgrounds.Length) { World.CurrentBackground = Data.Backgrounds[Data.Blocks[0].Background]; } else { World.CurrentBackground = new World.Background(null, 6, false); } World.TargetBackground = World.CurrentBackground; } // brightness int CurrentBrightnessElement = -1; int CurrentBrightnessEvent = -1; float CurrentBrightnessValue = 1.0f; double CurrentBrightnessTrackPosition = (double)Data.FirstUsedBlock * Data.BlockInterval; if (!PreviewOnly) { for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) { if (Data.Blocks[i].Brightness != null && Data.Blocks[i].Brightness.Length != 0) { CurrentBrightnessValue = Data.Blocks[i].Brightness[0].Value; CurrentBrightnessTrackPosition = Data.Blocks[i].Brightness[0].Value; break; } } } // create objects and track Vector3 Position = new Vector3(0.0, 0.0, 0.0); World.Vector2D Direction = new World.Vector2D(0.0, 1.0); TrackManager.CurrentTrack = new TrackManager.Track(); TrackManager.CurrentTrack.Elements = new TrackManager.TrackElement[] { }; double CurrentSpeedLimit = double.PositiveInfinity; int CurrentRunIndex = 0; int CurrentFlangeIndex = 0; if (Data.FirstUsedBlock < 0) Data.FirstUsedBlock = 0; TrackManager.CurrentTrack.Elements = new TrackManager.TrackElement[256]; int CurrentTrackLength = 0; int PreviousFogElement = -1; int PreviousFogEvent = -1; Game.Fog PreviousFog = new Game.Fog(Game.NoFogStart, Game.NoFogEnd, new Color24(128, 128, 128), -Data.BlockInterval); Game.Fog CurrentFog = new Game.Fog(Game.NoFogStart, Game.NoFogEnd, new Color24(128, 128, 128), 0.0); // process blocks double progressFactor = Data.Blocks.Length - Data.FirstUsedBlock == 0 ? 0.5 : 0.5 / (double)(Data.Blocks.Length - Data.FirstUsedBlock); for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) { Loading.RouteProgress = 0.6667 + (double)(i - Data.FirstUsedBlock) * progressFactor; if ((i & 15) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } double StartingDistance = (double)i * Data.BlockInterval; double EndingDistance = StartingDistance + Data.BlockInterval; // normalize World.Normalize(ref Direction.X, ref Direction.Y); // track if (!PreviewOnly) { if (Data.Blocks[i].Cycle.Length == 1 && Data.Blocks[i].Cycle[0] == -1) { if (Data.Structure.Cycle.Length == 0 || Data.Structure.Cycle[0] == null) { Data.Blocks[i].Cycle = new int[] { 0 }; } else { Data.Blocks[i].Cycle = Data.Structure.Cycle[0]; } } } TrackManager.TrackElement WorldTrackElement = Data.Blocks[i].CurrentTrackState; int n = CurrentTrackLength; if (n >= TrackManager.CurrentTrack.Elements.Length) { Array.Resize<TrackManager.TrackElement>(ref TrackManager.CurrentTrack.Elements, TrackManager.CurrentTrack.Elements.Length << 1); } CurrentTrackLength++; TrackManager.CurrentTrack.Elements[n] = WorldTrackElement; TrackManager.CurrentTrack.Elements[n].WorldPosition = Position; TrackManager.CurrentTrack.Elements[n].WorldDirection = World.GetVector3(Direction, Data.Blocks[i].Pitch); TrackManager.CurrentTrack.Elements[n].WorldSide = new Vector3(Direction.Y, 0.0, -Direction.X); World.Cross(TrackManager.CurrentTrack.Elements[n].WorldDirection.X, TrackManager.CurrentTrack.Elements[n].WorldDirection.Y, TrackManager.CurrentTrack.Elements[n].WorldDirection.Z, TrackManager.CurrentTrack.Elements[n].WorldSide.X, TrackManager.CurrentTrack.Elements[n].WorldSide.Y, TrackManager.CurrentTrack.Elements[n].WorldSide.Z, out TrackManager.CurrentTrack.Elements[n].WorldUp.X, out TrackManager.CurrentTrack.Elements[n].WorldUp.Y, out TrackManager.CurrentTrack.Elements[n].WorldUp.Z); TrackManager.CurrentTrack.Elements[n].StartingTrackPosition = StartingDistance; TrackManager.CurrentTrack.Elements[n].Events = new TrackManager.GeneralEvent[] { }; TrackManager.CurrentTrack.Elements[n].AdhesionMultiplier = Data.Blocks[i].AdhesionMultiplier; TrackManager.CurrentTrack.Elements[n].CsvRwAccuracyLevel = Data.Blocks[i].Accuracy; // background if (!PreviewOnly) { if (Data.Blocks[i].Background >= 0) { int typ; if (i == Data.FirstUsedBlock) { typ = Data.Blocks[i].Background; } else { typ = Data.Backgrounds.Length > 0 ? 0 : -1; for (int j = i - 1; j >= Data.FirstUsedBlock; j--) { if (Data.Blocks[j].Background >= 0) { typ = Data.Blocks[j].Background; break; } } } if (typ >= 0 & typ < Data.Backgrounds.Length) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.BackgroundChangeEvent(0.0, Data.Backgrounds[typ], Data.Backgrounds[Data.Blocks[i].Background]); } } } // brightness if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].Brightness.Length; j++) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Brightness[j].TrackPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.BrightnessChangeEvent(d, Data.Blocks[i].Brightness[j].Value, CurrentBrightnessValue, Data.Blocks[i].Brightness[j].TrackPosition - CurrentBrightnessTrackPosition, Data.Blocks[i].Brightness[j].Value, 0.0); if (CurrentBrightnessElement >= 0 & CurrentBrightnessEvent >= 0) { TrackManager.BrightnessChangeEvent bce = (TrackManager.BrightnessChangeEvent)TrackManager.CurrentTrack.Elements[CurrentBrightnessElement].Events[CurrentBrightnessEvent]; bce.NextBrightness = Data.Blocks[i].Brightness[j].Value; bce.NextDistance = Data.Blocks[i].Brightness[j].TrackPosition - CurrentBrightnessTrackPosition; } CurrentBrightnessElement = n; CurrentBrightnessEvent = m; CurrentBrightnessValue = Data.Blocks[i].Brightness[j].Value; CurrentBrightnessTrackPosition = Data.Blocks[i].Brightness[j].TrackPosition; } } // fog if (!PreviewOnly) { if (Data.FogTransitionMode) { if (Data.Blocks[i].FogDefined) { Data.Blocks[i].Fog.TrackPosition = StartingDistance; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.FogChangeEvent(0.0, PreviousFog, Data.Blocks[i].Fog, Data.Blocks[i].Fog); if (PreviousFogElement >= 0 & PreviousFogEvent >= 0) { TrackManager.FogChangeEvent e = (TrackManager.FogChangeEvent)TrackManager.CurrentTrack.Elements[PreviousFogElement].Events[PreviousFogEvent]; e.NextFog = Data.Blocks[i].Fog; } else { Game.PreviousFog = PreviousFog; Game.CurrentFog = PreviousFog; Game.NextFog = Data.Blocks[i].Fog; } PreviousFog = Data.Blocks[i].Fog; PreviousFogElement = n; PreviousFogEvent = m; } } else { Data.Blocks[i].Fog.TrackPosition = StartingDistance + Data.BlockInterval; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.FogChangeEvent(0.0, PreviousFog, CurrentFog, Data.Blocks[i].Fog); PreviousFog = CurrentFog; CurrentFog = Data.Blocks[i].Fog; } } // rail sounds if (!PreviewOnly) { int j = Data.Blocks[i].RailType[0]; int r = j < Data.Structure.Run.Length ? Data.Structure.Run[j] : 0; int f = j < Data.Structure.Flange.Length ? Data.Structure.Flange[j] : 0; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.RailSoundsChangeEvent(0.0, CurrentRunIndex, CurrentFlangeIndex, r, f); CurrentRunIndex = r; CurrentFlangeIndex = f; } // point sound if (!PreviewOnly) { if (i < Data.Blocks.Length - 1) { bool q = false; for (int j = 0; j < Data.Blocks[i].Rail.Length; j++) { if (Data.Blocks[i].Rail[j].RailStart & Data.Blocks[i + 1].Rail.Length > j) { bool qx = Math.Sign(Data.Blocks[i].Rail[j].RailStartX) != Math.Sign(Data.Blocks[i + 1].Rail[j].RailEndX); bool qy = Data.Blocks[i].Rail[j].RailStartY * Data.Blocks[i + 1].Rail[j].RailEndY <= 0.0; if (qx & qy) { q = true; break; } } } if (q) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(0.0, null, false, false, true, new Vector3(0.0, 0.0, 0.0), 12.5); } } } // station if (Data.Blocks[i].Station >= 0) { // station int s = Data.Blocks[i].Station; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.StationStartEvent(0.0, s); double dx, dy = 3.0; if (Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors) { dx = -5.0; } else if (!Game.Stations[s].OpenLeftDoors & Game.Stations[s].OpenRightDoors) { dx = 5.0; } else { dx = 0.0; } Game.Stations[s].SoundOrigin.X = Position.X + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.X + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.X; Game.Stations[s].SoundOrigin.Y = Position.Y + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Y + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Y; Game.Stations[s].SoundOrigin.Z = Position.Z + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Z + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Z; // passalarm if (!PreviewOnly) { if (Data.Blocks[i].StationPassAlarm) { int b = i - 6; if (b >= 0) { int j = b - Data.FirstUsedBlock; if (j >= 0) { m = TrackManager.CurrentTrack.Elements[j].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[j].Events, m + 1); TrackManager.CurrentTrack.Elements[j].Events[m] = new TrackManager.StationPassAlarmEvent(0.0); } } } } } // stop for (int j = 0; j < Data.Blocks[i].Stop.Length; j++) { int s = Data.Blocks[i].Stop[j].Station; int t = Game.Stations[s].Stops.Length; Array.Resize<Game.StationStop>(ref Game.Stations[s].Stops, t + 1); Game.Stations[s].Stops[t].TrackPosition = Data.Blocks[i].Stop[j].TrackPosition; Game.Stations[s].Stops[t].ForwardTolerance = Data.Blocks[i].Stop[j].ForwardTolerance; Game.Stations[s].Stops[t].BackwardTolerance = Data.Blocks[i].Stop[j].BackwardTolerance; Game.Stations[s].Stops[t].Cars = Data.Blocks[i].Stop[j].Cars; double dx, dy = 2.0; if (Game.Stations[s].OpenLeftDoors & !Game.Stations[s].OpenRightDoors) { dx = -5.0; } else if (!Game.Stations[s].OpenLeftDoors & Game.Stations[s].OpenRightDoors) { dx = 5.0; } else { dx = 0.0; } Game.Stations[s].SoundOrigin.X = Position.X + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.X + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.X; Game.Stations[s].SoundOrigin.Y = Position.Y + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Y + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Y; Game.Stations[s].SoundOrigin.Z = Position.Z + dx * TrackManager.CurrentTrack.Elements[n].WorldSide.Z + dy * TrackManager.CurrentTrack.Elements[n].WorldUp.Z; } // limit for (int j = 0; j < Data.Blocks[i].Limit.Length; j++) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Limit[j].TrackPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.LimitChangeEvent(d, CurrentSpeedLimit, Data.Blocks[i].Limit[j].Speed); CurrentSpeedLimit = Data.Blocks[i].Limit[j].Speed; } // marker if (!PreviewOnly) { for (int j = 0; j < Data.Markers.Length; j++) { if (Data.Markers[j].StartingPosition >= StartingDistance & Data.Markers[j].StartingPosition < EndingDistance) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Markers[j].StartingPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.MarkerStartEvent(d, Data.Markers[j].Texture); } if (Data.Markers[j].EndingPosition >= StartingDistance & Data.Markers[j].EndingPosition < EndingDistance) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Markers[j].EndingPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.MarkerEndEvent(d, Data.Markers[j].Texture); } } } // sound if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].Sound.Length; j++) { if (Data.Blocks[i].Sound[j].Type == SoundType.TrainStatic | Data.Blocks[i].Sound[j].Type == SoundType.TrainDynamic) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Sound[j].TrackPosition - StartingDistance; switch (Data.Blocks[i].Sound[j].Type) { case SoundType.TrainStatic: TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(d, Data.Blocks[i].Sound[j].SoundBuffer, true, true, false, new Vector3(0.0, 0.0, 0.0), 0.0); break; case SoundType.TrainDynamic: TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.SoundEvent(d, Data.Blocks[i].Sound[j].SoundBuffer, false, false, true, new Vector3(0.0, 0.0, 0.0), Data.Blocks[i].Sound[j].Speed); break; } } } } // turn if (Data.Blocks[i].Turn != 0.0) { double ag = -Math.Atan(Data.Blocks[i].Turn); double cosag = Math.Cos(ag); double sinag = Math.Sin(ag); World.Rotate(ref Direction, cosag, sinag); World.RotatePlane(ref TrackManager.CurrentTrack.Elements[n].WorldDirection, cosag, sinag); World.RotatePlane(ref TrackManager.CurrentTrack.Elements[n].WorldSide, cosag, sinag); World.Cross(TrackManager.CurrentTrack.Elements[n].WorldDirection.X, TrackManager.CurrentTrack.Elements[n].WorldDirection.Y, TrackManager.CurrentTrack.Elements[n].WorldDirection.Z, TrackManager.CurrentTrack.Elements[n].WorldSide.X, TrackManager.CurrentTrack.Elements[n].WorldSide.Y, TrackManager.CurrentTrack.Elements[n].WorldSide.Z, out TrackManager.CurrentTrack.Elements[n].WorldUp.X, out TrackManager.CurrentTrack.Elements[n].WorldUp.Y, out TrackManager.CurrentTrack.Elements[n].WorldUp.Z); } // curves double a = 0.0; double c = Data.BlockInterval; double h = 0.0; if (WorldTrackElement.CurveRadius != 0.0 & Data.Blocks[i].Pitch != 0.0) { double d = Data.BlockInterval; double p = Data.Blocks[i].Pitch; double r = WorldTrackElement.CurveRadius; double s = d / Math.Sqrt(1.0 + p * p); h = s * p; double b = s / Math.Abs(r); c = Math.Sqrt(2.0 * r * r * (1.0 - Math.Cos(b))); a = 0.5 * (double)Math.Sign(r) * b; World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a)); } else if (WorldTrackElement.CurveRadius != 0.0) { double d = Data.BlockInterval; double r = WorldTrackElement.CurveRadius; double b = d / Math.Abs(r); c = Math.Sqrt(2.0 * r * r * (1.0 - Math.Cos(b))); a = 0.5 * (double)Math.Sign(r) * b; World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a)); } else if (Data.Blocks[i].Pitch != 0.0) { double p = Data.Blocks[i].Pitch; double d = Data.BlockInterval; c = d / Math.Sqrt(1.0 + p * p); h = c * p; } double TrackYaw = Math.Atan2(Direction.X, Direction.Y); double TrackPitch = Math.Atan(Data.Blocks[i].Pitch); World.Transformation GroundTransformation = new World.Transformation(TrackYaw, 0.0, 0.0); World.Transformation TrackTransformation = new World.Transformation(TrackYaw, TrackPitch, 0.0); World.Transformation NullTransformation = new World.Transformation(0.0, 0.0, 0.0); // ground if (!PreviewOnly) { int cb = (int)Math.Floor((double)i + 0.001); int ci = (cb % Data.Blocks[i].Cycle.Length + Data.Blocks[i].Cycle.Length) % Data.Blocks[i].Cycle.Length; int gi = Data.Blocks[i].Cycle[ci]; if (gi >= 0 & gi < Data.Structure.Ground.Length) { if (Data.Structure.Ground[gi] != null) { ObjectManager.CreateObject(Data.Structure.Ground[Data.Blocks[i].Cycle[ci]], Position + new Vector3(0.0, -Data.Blocks[i].Height, 0.0), GroundTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } // ground-aligned free objects if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].GroundFreeObj.Length; j++) { int sttype = Data.Blocks[i].GroundFreeObj[j].Type; double d = Data.Blocks[i].GroundFreeObj[j].TrackPosition - StartingDistance; double dx = Data.Blocks[i].GroundFreeObj[j].X; double dy = Data.Blocks[i].GroundFreeObj[j].Y; Vector3 wpos = Position + new Vector3(Direction.X * d + Direction.Y * dx, dy - Data.Blocks[i].Height, Direction.Y * d - Direction.X * dx); double tpos = Data.Blocks[i].GroundFreeObj[j].TrackPosition; ObjectManager.CreateObject(Data.Structure.FreeObj[sttype], wpos, GroundTransformation, new World.Transformation(Data.Blocks[i].GroundFreeObj[j].Yaw, Data.Blocks[i].GroundFreeObj[j].Pitch, Data.Blocks[i].GroundFreeObj[j].Roll), Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos); } } // rail-aligned objects if (!PreviewOnly) { for (int j = 0; j < Data.Blocks[i].Rail.Length; j++) { if (j > 0 && !Data.Blocks[i].Rail[j].RailStart) continue; // rail Vector3 pos; World.Transformation RailTransformation; double planar, updown; if (j == 0) { // rail 0 pos = Position; planar = 0.0; updown = 0.0; RailTransformation = new World.Transformation(TrackTransformation, planar, updown, 0.0); pos = Position; } else { // rails 1-infinity double x = Data.Blocks[i].Rail[j].RailStartX; double y = Data.Blocks[i].Rail[j].RailStartY; Vector3 offset = new Vector3(Direction.Y * x, y, -Direction.X * x); pos = Position + offset; double dh; if (i < Data.Blocks.Length - 1 && Data.Blocks[i + 1].Rail.Length > j) { // take orientation of upcoming block into account World.Vector2D Direction2 = Direction; Vector3 Position2 = Position; Position2.X += Direction.X * c; Position2.Y += h; Position2.Z += Direction.Y * c; if (a != 0.0) { World.Rotate(ref Direction2, Math.Cos(-a), Math.Sin(-a)); } if (Data.Blocks[i + 1].Turn != 0.0) { double ag = -Math.Atan(Data.Blocks[i + 1].Turn); double cosag = Math.Cos(ag); double sinag = Math.Sin(ag); World.Rotate(ref Direction2, cosag, sinag); } double a2 = 0.0; double c2 = Data.BlockInterval; double h2 = 0.0; if (Data.Blocks[i + 1].CurrentTrackState.CurveRadius != 0.0 & Data.Blocks[i + 1].Pitch != 0.0) { double d2 = Data.BlockInterval; double p2 = Data.Blocks[i + 1].Pitch; double r2 = Data.Blocks[i + 1].CurrentTrackState.CurveRadius; double s2 = d2 / Math.Sqrt(1.0 + p2 * p2); h2 = s2 * p2; double b2 = s2 / Math.Abs(r2); c2 = Math.Sqrt(2.0 * r2 * r2 * (1.0 - Math.Cos(b2))); a2 = 0.5 * (double)Math.Sign(r2) * b2; World.Rotate(ref Direction2, Math.Cos(-a2), Math.Sin(-a2)); } else if (Data.Blocks[i + 1].CurrentTrackState.CurveRadius != 0.0) { double d2 = Data.BlockInterval; double r2 = Data.Blocks[i + 1].CurrentTrackState.CurveRadius; double b2 = d2 / Math.Abs(r2); c2 = Math.Sqrt(2.0 * r2 * r2 * (1.0 - Math.Cos(b2))); a2 = 0.5 * (double)Math.Sign(r2) * b2; World.Rotate(ref Direction2, Math.Cos(-a2), Math.Sin(-a2)); } else if (Data.Blocks[i + 1].Pitch != 0.0) { double p2 = Data.Blocks[i + 1].Pitch; double d2 = Data.BlockInterval; c2 = d2 / Math.Sqrt(1.0 + p2 * p2); h2 = c2 * p2; } double TrackYaw2 = Math.Atan2(Direction2.X, Direction2.Y); double TrackPitch2 = Math.Atan(Data.Blocks[i + 1].Pitch); World.Transformation GroundTransformation2 = new World.Transformation(TrackYaw2, 0.0, 0.0); World.Transformation TrackTransformation2 = new World.Transformation(TrackYaw2, TrackPitch2, 0.0); double x2 = Data.Blocks[i + 1].Rail[j].RailEndX; double y2 = Data.Blocks[i + 1].Rail[j].RailEndY; Vector3 offset2 = new Vector3(Direction2.Y * x2, y2, -Direction2.X * x2); Vector3 pos2 = Position2 + offset2; double rx = pos2.X - pos.X; double ry = pos2.Y - pos.Y; double rz = pos2.Z - pos.Z; World.Normalize(ref rx, ref ry, ref rz); RailTransformation.Z = new Vector3(rx, ry, rz); RailTransformation.X = new Vector3(rz, 0.0, -rx); World.Normalize(ref RailTransformation.X.X, ref RailTransformation.X.Z); RailTransformation.Y = Vector3.Cross(RailTransformation.Z, RailTransformation.X); double dx = Data.Blocks[i + 1].Rail[j].RailEndX - Data.Blocks[i].Rail[j].RailStartX; double dy = Data.Blocks[i + 1].Rail[j].RailEndY - Data.Blocks[i].Rail[j].RailStartY; planar = Math.Atan(dx / c); dh = dy / c; updown = Math.Atan(dh); } else { planar = 0.0; dh = 0.0; updown = 0.0; RailTransformation = new World.Transformation(TrackTransformation, 0.0, 0.0, 0.0); } } if (Data.Blocks[i].RailType[j] < Data.Structure.Rail.Length) { if (Data.Structure.Rail[Data.Blocks[i].RailType[j]] != null) { ObjectManager.CreateObject(Data.Structure.Rail[Data.Blocks[i].RailType[j]], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } // points of interest for (int k = 0; k < Data.Blocks[i].PointsOfInterest.Length; k++) { if (Data.Blocks[i].PointsOfInterest[k].RailIndex == j) { double d = Data.Blocks[i].PointsOfInterest[k].TrackPosition - StartingDistance; double x = Data.Blocks[i].PointsOfInterest[k].X; double y = Data.Blocks[i].PointsOfInterest[k].Y; int m = Game.PointsOfInterest.Length; Array.Resize<Game.PointOfInterest>(ref Game.PointsOfInterest, m + 1); Game.PointsOfInterest[m].TrackPosition = Data.Blocks[i].PointsOfInterest[k].TrackPosition; if (i < Data.Blocks.Length - 1 && Data.Blocks[i + 1].Rail.Length > j) { double dx = Data.Blocks[i + 1].Rail[j].RailEndX - Data.Blocks[i].Rail[j].RailStartX; double dy = Data.Blocks[i + 1].Rail[j].RailEndY - Data.Blocks[i].Rail[j].RailStartY; dx = Data.Blocks[i].Rail[j].RailStartX + d / Data.BlockInterval * dx; dy = Data.Blocks[i].Rail[j].RailStartY + d / Data.BlockInterval * dy; Game.PointsOfInterest[m].TrackOffset = new Vector3(x + dx, y + dy, 0.0); } else { double dx = Data.Blocks[i].Rail[j].RailStartX; double dy = Data.Blocks[i].Rail[j].RailStartY; Game.PointsOfInterest[m].TrackOffset = new Vector3(x + dx, y + dy, 0.0); } Game.PointsOfInterest[m].TrackYaw = Data.Blocks[i].PointsOfInterest[k].Yaw + planar; Game.PointsOfInterest[m].TrackPitch = Data.Blocks[i].PointsOfInterest[k].Pitch + updown; Game.PointsOfInterest[m].TrackRoll = Data.Blocks[i].PointsOfInterest[k].Roll; Game.PointsOfInterest[m].Text = Data.Blocks[i].PointsOfInterest[k].Text; } } // poles if (Data.Blocks[i].RailPole.Length > j && Data.Blocks[i].RailPole[j].Exists) { double dz = StartingDistance / Data.Blocks[i].RailPole[j].Interval; dz -= Math.Floor(dz + 0.5); if (dz >= -0.01 & dz <= 0.01) { if (Data.Blocks[i].RailPole[j].Mode == 0) { if (Data.Blocks[i].RailPole[j].Location <= 0.0) { ObjectManager.CreateObject(Data.Structure.Poles[0][Data.Blocks[i].RailPole[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } else { ObjectManager.UnifiedObject Pole = GetMirroredObject(Data.Structure.Poles[0][Data.Blocks[i].RailPole[j].Type]); ObjectManager.CreateObject(Pole, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } else { int m = Data.Blocks[i].RailPole[j].Mode; double dx = -Data.Blocks[i].RailPole[j].Location * 3.8; double wa = Math.Atan2(Direction.Y, Direction.X) - planar; double wx = Math.Cos(wa); double wy = Math.Tan(updown); double wz = Math.Sin(wa); World.Normalize(ref wx, ref wy, ref wz); double sx = Direction.Y; double sy = 0.0; double sz = -Direction.X; Vector3 wpos = pos + new Vector3(sx * dx + wx * dz, sy * dx + wy * dz, sz * dx + wz * dz); int type = Data.Blocks[i].RailPole[j].Type; ObjectManager.CreateObject(Data.Structure.Poles[m][type], wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } // walls if (Data.Blocks[i].RailWall.Length > j && Data.Blocks[i].RailWall[j].Exists) { if (Data.Blocks[i].RailWall[j].Direction <= 0) { ObjectManager.CreateObject(Data.Structure.WallL[Data.Blocks[i].RailWall[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].RailWall[j].Direction >= 0) { ObjectManager.CreateObject(Data.Structure.WallR[Data.Blocks[i].RailWall[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } // dikes if (Data.Blocks[i].RailDike.Length > j && Data.Blocks[i].RailDike[j].Exists) { if (Data.Blocks[i].RailDike[j].Direction <= 0) { ObjectManager.CreateObject(Data.Structure.DikeL[Data.Blocks[i].RailDike[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].RailDike[j].Direction >= 0) { ObjectManager.CreateObject(Data.Structure.DikeR[Data.Blocks[i].RailDike[j].Type], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } // sounds if (j == 0) { for (int k = 0; k < Data.Blocks[i].Sound.Length; k++) { if (Data.Blocks[i].Sound[k].Type == SoundType.World) { if (Data.Blocks[i].Sound[k].SoundBuffer != null) { double d = Data.Blocks[i].Sound[k].TrackPosition - StartingDistance; double dx = Data.Blocks[i].Sound[k].X; double dy = Data.Blocks[i].Sound[k].Y; double wa = Math.Atan2(Direction.Y, Direction.X) - planar; double wx = Math.Cos(wa); double wy = Math.Tan(updown); double wz = Math.Sin(wa); World.Normalize(ref wx, ref wy, ref wz); double sx = Direction.Y; double sy = 0.0; double sz = -Direction.X; double ux, uy, uz; World.Cross(wx, wy, wz, sx, sy, sz, out ux, out uy, out uz); Vector3 wpos = pos + new Vector3(sx * dx + ux * dy + wx * d, sy * dx + uy * dy + wy * d, sz * dx + uz * dy + wz * d); Sounds.PlaySound(Data.Blocks[i].Sound[k].SoundBuffer, 1.0, 1.0, wpos, true); } } } } // forms for (int k = 0; k < Data.Blocks[i].Form.Length; k++) { // primary rail if (Data.Blocks[i].Form[k].PrimaryRail == j) { if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailStub) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } else if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailL) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCL.Length || Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCL.Length || Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else if (Data.Blocks[i].Form[k].SecondaryRail == Form.SecondaryRailR) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCR.Length || Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCR.Length || Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateStaticObject(Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else if (Data.Blocks[i].Form[k].SecondaryRail > 0) { int p = Data.Blocks[i].Form[k].PrimaryRail; double px0 = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0; double px1 = p > 0 ? Data.Blocks[i + 1].Rail[p].RailEndX : 0.0; int s = Data.Blocks[i].Form[k].SecondaryRail; if (s < 0 || s >= Data.Blocks[i].Rail.Length || !Data.Blocks[i].Rail[s].RailStart) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is out of range in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName); } else { double sx0 = Data.Blocks[i].Rail[s].RailStartX; double sx1 = Data.Blocks[i + 1].Rail[s].RailEndX; double d0 = sx0 - px0; double d1 = sx1 - px1; if (d0 < 0.0) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCL.Length || Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject FormC = GetTransformedStaticObject(Data.Structure.FormCL[Data.Blocks[i].Form[k].FormType], d0, d1); ObjectManager.CreateStaticObject(FormC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCL.Length || Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject RoofC = GetTransformedStaticObject(Data.Structure.RoofCL[Data.Blocks[i].Form[k].RoofType], d0, d1); ObjectManager.CreateStaticObject(RoofC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else if (d0 > 0.0) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormCR.Length || Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject FormC = GetTransformedStaticObject(Data.Structure.FormCR[Data.Blocks[i].Form[k].FormType], d0, d1); ObjectManager.CreateStaticObject(FormC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofCR.Length || Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofCR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject RoofC = GetTransformedStaticObject(Data.Structure.RoofCR[Data.Blocks[i].Form[k].RoofType], d0, d1); ObjectManager.CreateStaticObject(RoofC, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } } } // secondary rail if (Data.Blocks[i].Form[k].SecondaryRail == j) { int p = Data.Blocks[i].Form[k].PrimaryRail; double px = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0; int s = Data.Blocks[i].Form[k].SecondaryRail; double sx = Data.Blocks[i].Rail[s].RailStartX; double d = px - sx; if (d < 0.0) { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormL.Length || Data.Structure.FormL[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormL[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofL.Length || Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofL not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofL[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } else { if (Data.Blocks[i].Form[k].FormType >= Data.Structure.FormR.Length || Data.Structure.FormR[Data.Blocks[i].Form[k].FormType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "FormStructureIndex references a FormR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.FormR[Data.Blocks[i].Form[k].FormType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } if (Data.Blocks[i].Form[k].RoofType > 0) { if (Data.Blocks[i].Form[k].RoofType >= Data.Structure.RoofR.Length || Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "RoofStructureIndex references a RoofR not loaded in Track.Form at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.CreateObject(Data.Structure.RoofR[Data.Blocks[i].Form[k].RoofType], pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } } // cracks for (int k = 0; k < Data.Blocks[i].Crack.Length; k++) { if (Data.Blocks[i].Crack[k].PrimaryRail == j) { int p = Data.Blocks[i].Crack[k].PrimaryRail; double px0 = p > 0 ? Data.Blocks[i].Rail[p].RailStartX : 0.0; double px1 = p > 0 ? Data.Blocks[i + 1].Rail[p].RailEndX : 0.0; int s = Data.Blocks[i].Crack[k].SecondaryRail; if (s < 0 || s >= Data.Blocks[i].Rail.Length || !Data.Blocks[i].Rail[s].RailStart) { Interface.AddMessage(Interface.MessageType.Error, false, "RailIndex2 is out of range in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName); } else { double sx0 = Data.Blocks[i].Rail[s].RailStartX; double sx1 = Data.Blocks[i + 1].Rail[s].RailEndX; double d0 = sx0 - px0; double d1 = sx1 - px1; if (d0 < 0.0) { if (Data.Blocks[i].Crack[k].Type >= Data.Structure.CrackL.Length || Data.Structure.CrackL[Data.Blocks[i].Crack[k].Type] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex references a CrackL not loaded in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject Crack = GetTransformedStaticObject(Data.Structure.CrackL[Data.Blocks[i].Crack[k].Type], d0, d1); ObjectManager.CreateStaticObject(Crack, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } else if (d0 > 0.0) { if (Data.Blocks[i].Crack[k].Type >= Data.Structure.CrackR.Length || Data.Structure.CrackR[Data.Blocks[i].Crack[k].Type] == null) { Interface.AddMessage(Interface.MessageType.Error, false, "CrackStructureIndex references a CrackR not loaded in Track.Crack at track position " + StartingDistance.ToString(Culture) + " in file " + FileName + "."); } else { ObjectManager.StaticObject Crack = GetTransformedStaticObject(Data.Structure.CrackR[Data.Blocks[i].Crack[k].Type], d0, d1); ObjectManager.CreateStaticObject(Crack, pos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, StartingDistance); } } } } } // free objects if (Data.Blocks[i].RailFreeObj.Length > j && Data.Blocks[i].RailFreeObj[j] != null) { for (int k = 0; k < Data.Blocks[i].RailFreeObj[j].Length; k++) { int sttype = Data.Blocks[i].RailFreeObj[j][k].Type; double dx = Data.Blocks[i].RailFreeObj[j][k].X; double dy = Data.Blocks[i].RailFreeObj[j][k].Y; double dz = Data.Blocks[i].RailFreeObj[j][k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].RailFreeObj[j][k].TrackPosition; ObjectManager.CreateObject(Data.Structure.FreeObj[sttype], wpos, RailTransformation, new World.Transformation(Data.Blocks[i].RailFreeObj[j][k].Yaw, Data.Blocks[i].RailFreeObj[j][k].Pitch, Data.Blocks[i].RailFreeObj[j][k].Roll), -1, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false); } } // transponder objects if (j == 0) { for (int k = 0; k < Data.Blocks[i].Transponder.Length; k++) { ObjectManager.UnifiedObject obj = null; if (Data.Blocks[i].Transponder[k].ShowDefaultObject) { switch (Data.Blocks[i].Transponder[k].Type) { case 0: obj = TransponderS; break; case 1: obj = TransponderSN; break; case 2: obj = TransponderFalseStart; break; case 3: obj = TransponderPOrigin; break; case 4: obj = TransponderPStop; break; } } else { int b = Data.Blocks[i].Transponder[k].BeaconStructureIndex; if (b >= 0 & b < Data.Structure.Beacon.Length) { obj = Data.Structure.Beacon[b]; } } if (obj != null) { double dx = Data.Blocks[i].Transponder[k].X; double dy = Data.Blocks[i].Transponder[k].Y; double dz = Data.Blocks[i].Transponder[k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Transponder[k].TrackPosition; if (Data.Blocks[i].Transponder[k].ShowDefaultObject) { double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.CreateObject(obj, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Transponder[k].Yaw, Data.Blocks[i].Transponder[k].Pitch, Data.Blocks[i].Transponder[k].Roll), -1, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else { ObjectManager.CreateObject(obj, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Transponder[k].Yaw, Data.Blocks[i].Transponder[k].Pitch, Data.Blocks[i].Transponder[k].Roll), Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos); } } } } // sections/signals/transponders if (j == 0) { // signals for (int k = 0; k < Data.Blocks[i].Signal.Length; k++) { SignalData sd; if (Data.Blocks[i].Signal[k].SignalCompatibilityObjectIndex >= 0) { sd = Data.CompatibilitySignalData[Data.Blocks[i].Signal[k].SignalCompatibilityObjectIndex]; } else { sd = Data.SignalData[Data.Blocks[i].Signal[k].SignalObjectIndex]; } // objects double dz = Data.Blocks[i].Signal[k].TrackPosition - StartingDistance; if (Data.Blocks[i].Signal[k].ShowPost) { // post double dx = Data.Blocks[i].Signal[k].X; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Signal[k].TrackPosition; double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.CreateStaticObject(SignalPost, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } if (Data.Blocks[i].Signal[k].ShowObject) { // signal object double dx = Data.Blocks[i].Signal[k].X; double dy = Data.Blocks[i].Signal[k].Y; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dy * RailTransformation.Y.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dy * RailTransformation.Y.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dy * RailTransformation.Y.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Signal[k].TrackPosition; if (sd is AnimatedObjectSignalData) { AnimatedObjectSignalData aosd = (AnimatedObjectSignalData)sd; ObjectManager.CreateObject(aosd.Objects, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false); } else if (sd is CompatibilitySignalData) { CompatibilitySignalData csd = (CompatibilitySignalData)sd; if (csd.Numbers.Length != 0) { double brightness = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.AnimatedObjectCollection aoc = new ObjectManager.AnimatedObjectCollection(); aoc.Objects = new ObjectManager.AnimatedObject[1]; aoc.Objects[0] = new ObjectManager.AnimatedObject(); aoc.Objects[0].States = new ObjectManager.AnimatedObjectState[csd.Numbers.Length]; for (int l = 0; l < csd.Numbers.Length; l++) { aoc.Objects[0].States[l].Object = ObjectManager.CloneObject(csd.Objects[l]); } string expr = ""; for (int l = 0; l < csd.Numbers.Length - 1; l++) { expr += "section " + csd.Numbers[l].ToString(Culture) + " <= " + l.ToString(Culture) + " "; } expr += (csd.Numbers.Length - 1).ToString(Culture); for (int l = 0; l < csd.Numbers.Length - 1; l++) { expr += " ?"; } aoc.Objects[0].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(expr); aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Program.RandomNumberGenerator.NextDouble(); ObjectManager.CreateObject(aoc, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, brightness, false); } } else if (sd is Bve4SignalData) { Bve4SignalData b4sd = (Bve4SignalData)sd; if (b4sd.SignalTextures.Length != 0) { int m = Math.Max(b4sd.SignalTextures.Length, b4sd.GlowTextures.Length); int zn = 0; for (int l = 0; l < m; l++) { if (l < b4sd.SignalTextures.Length && b4sd.SignalTextures[l] != null || l < b4sd.GlowTextures.Length && b4sd.GlowTextures[l] != null) { zn++; } } ObjectManager.AnimatedObjectCollection aoc = new ObjectManager.AnimatedObjectCollection(); aoc.Objects = new ObjectManager.AnimatedObject[1]; aoc.Objects[0] = new ObjectManager.AnimatedObject(); aoc.Objects[0].States = new ObjectManager.AnimatedObjectState[zn]; int zi = 0; string expr = ""; for (int l = 0; l < m; l++) { bool qs = l < b4sd.SignalTextures.Length && b4sd.SignalTextures[l] != null; bool qg = l < b4sd.GlowTextures.Length && b4sd.GlowTextures[l] != null; if (qs & qg) { ObjectManager.StaticObject so = ObjectManager.CloneObject(b4sd.BaseObject, b4sd.SignalTextures[l], null); ObjectManager.StaticObject go = ObjectManager.CloneObject(b4sd.GlowObject, b4sd.GlowTextures[l], null); ObjectManager.JoinObjects(ref so, go); aoc.Objects[0].States[zi].Object = so; } else if (qs) { ObjectManager.StaticObject so = ObjectManager.CloneObject(b4sd.BaseObject, b4sd.SignalTextures[l], null); aoc.Objects[0].States[zi].Object = so; } else if (qg) { ObjectManager.StaticObject go = ObjectManager.CloneObject(b4sd.GlowObject, b4sd.GlowTextures[l], null); aoc.Objects[0].States[zi].Object = go; } if (qs | qg) { if (zi < zn - 1) { expr += "section " + l.ToString(Culture) + " <= " + zi.ToString(Culture) + " "; } else { expr += zi.ToString(Culture); } zi++; } } for (int l = 0; l < zn - 1; l++) { expr += " ?"; } aoc.Objects[0].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(expr); aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Program.RandomNumberGenerator.NextDouble(); ObjectManager.CreateObject(aoc, wpos, RailTransformation, new World.Transformation(Data.Blocks[i].Signal[k].Yaw, Data.Blocks[i].Signal[k].Pitch, Data.Blocks[i].Signal[k].Roll), Data.Blocks[i].Signal[k].Section, Data.AccurateObjectDisposal, StartingDistance, EndingDistance, Data.BlockInterval, tpos, 1.0, false); } } } } // sections for (int k = 0; k < Data.Blocks[i].Section.Length; k++) { int m = Game.Sections.Length; Array.Resize<Game.Section>(ref Game.Sections, m + 1); Game.Sections[m].SignalIndices = new int[] { }; // create associated transponders for (int g = 0; g <= i; g++) { for (int l = 0; l < Data.Blocks[g].Transponder.Length; l++) { if (Data.Blocks[g].Transponder[l].Type != -1 & Data.Blocks[g].Transponder[l].Section == m) { int o = TrackManager.CurrentTrack.Elements[n - i + g].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n - i + g].Events, o + 1); double dt = Data.Blocks[g].Transponder[l].TrackPosition - StartingDistance + (double)(i - g) * Data.BlockInterval; TrackManager.CurrentTrack.Elements[n - i + g].Events[o] = new TrackManager.TransponderEvent(dt, Data.Blocks[g].Transponder[l].Type, Data.Blocks[g].Transponder[l].Data, m); Data.Blocks[g].Transponder[l].Type = -1; } } } // create section Game.Sections[m].TrackPosition = Data.Blocks[i].Section[k].TrackPosition; Game.Sections[m].Aspects = new Game.SectionAspect[Data.Blocks[i].Section[k].Aspects.Length]; for (int l = 0; l < Data.Blocks[i].Section[k].Aspects.Length; l++) { Game.Sections[m].Aspects[l].Number = Data.Blocks[i].Section[k].Aspects[l]; if (Data.Blocks[i].Section[k].Aspects[l] >= 0 & Data.Blocks[i].Section[k].Aspects[l] < Data.SignalSpeeds.Length) { Game.Sections[m].Aspects[l].Speed = Data.SignalSpeeds[Data.Blocks[i].Section[k].Aspects[l]]; } else { Game.Sections[m].Aspects[l].Speed = double.PositiveInfinity; } } Game.Sections[m].Type = Data.Blocks[i].Section[k].Type; Game.Sections[m].CurrentAspect = -1; if (m > 0) { Game.Sections[m].PreviousSection = m - 1; Game.Sections[m - 1].NextSection = m; } else { Game.Sections[m].PreviousSection = -1; } Game.Sections[m].NextSection = -1; Game.Sections[m].StationIndex = Data.Blocks[i].Section[k].DepartureStationIndex; Game.Sections[m].Invisible = Data.Blocks[i].Section[k].Invisible; Game.Sections[m].Trains = new TrainManager.Train[] { }; // create section change event double d = Data.Blocks[i].Section[k].TrackPosition - StartingDistance; int p = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, p + 1); TrackManager.CurrentTrack.Elements[n].Events[p] = new TrackManager.SectionChangeEvent(d, m - 1, m); } // transponders introduced after corresponding sections for (int l = 0; l < Data.Blocks[i].Transponder.Length; l++) { if (Data.Blocks[i].Transponder[l].Type != -1) { int t = Data.Blocks[i].Transponder[l].Section; if (t >= 0 & t < Game.Sections.Length) { int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double dt = Data.Blocks[i].Transponder[l].TrackPosition - StartingDistance; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TransponderEvent(dt, Data.Blocks[i].Transponder[l].Type, Data.Blocks[i].Transponder[l].Data, t); Data.Blocks[i].Transponder[l].Type = -1; } } } } // limit if (j == 0) { for (int k = 0; k < Data.Blocks[i].Limit.Length; k++) { if (Data.Blocks[i].Limit[k].Direction != 0) { double dx = 2.2 * (double)Data.Blocks[i].Limit[k].Direction; double dz = Data.Blocks[i].Limit[k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Limit[k].TrackPosition; double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); if (Data.Blocks[i].Limit[k].Speed <= 0.0 | Data.Blocks[i].Limit[k].Speed >= 1000.0) { ObjectManager.CreateStaticObject(LimitPostInfinite, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else { if (Data.Blocks[i].Limit[k].Cource < 0) { ObjectManager.CreateStaticObject(LimitPostLeft, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else if (Data.Blocks[i].Limit[k].Cource > 0) { ObjectManager.CreateStaticObject(LimitPostRight, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } else { ObjectManager.CreateStaticObject(LimitPostStraight, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } double lim = Data.Blocks[i].Limit[k].Speed / Data.UnitOfSpeed; if (lim < 10.0) { int d0 = (int)Math.Round(lim); int o = ObjectManager.CreateStaticObject(LimitOneDigit, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true); if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture); } } else if (lim < 100.0) { int d1 = (int)Math.Round(lim); int d0 = d1 % 10; d1 /= 10; int o = ObjectManager.CreateStaticObject(LimitTwoDigits, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true); if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d1 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture); } if (ObjectManager.Objects[o].Mesh.Materials.Length >= 2) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[1].DaytimeTexture); } } else { int d2 = (int)Math.Round(lim); int d0 = d2 % 10; int d1 = (d2 / 10) % 10; d2 /= 100; int o = ObjectManager.CreateStaticObject(LimitThreeDigits, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, true); if (ObjectManager.Objects[o].Mesh.Materials.Length >= 1) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d2 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[0].DaytimeTexture); } if (ObjectManager.Objects[o].Mesh.Materials.Length >= 2) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d1 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[1].DaytimeTexture); } if (ObjectManager.Objects[o].Mesh.Materials.Length >= 3) { Textures.RegisterTexture(OpenBveApi.Path.CombineFile(LimitGraphicsPath, "limit_" + d0 + ".png"), out ObjectManager.Objects[o].Mesh.Materials[2].DaytimeTexture); } } } } } } // stop if (j == 0) { for (int k = 0; k < Data.Blocks[i].Stop.Length; k++) { if (Data.Blocks[i].Stop[k].Direction != 0) { double dx = 1.8 * (double)Data.Blocks[i].Stop[k].Direction; double dz = Data.Blocks[i].Stop[k].TrackPosition - StartingDistance; Vector3 wpos = pos; wpos.X += dx * RailTransformation.X.X + dz * RailTransformation.Z.X; wpos.Y += dx * RailTransformation.X.Y + dz * RailTransformation.Z.Y; wpos.Z += dx * RailTransformation.X.Z + dz * RailTransformation.Z.Z; double tpos = Data.Blocks[i].Stop[k].TrackPosition; double b = 0.25 + 0.75 * GetBrightness(ref Data, tpos); ObjectManager.CreateStaticObject(StopPost, wpos, RailTransformation, NullTransformation, Data.AccurateObjectDisposal, 0.0, StartingDistance, EndingDistance, Data.BlockInterval, tpos, b, false); } } } } } // finalize block Position.X += Direction.X * c; Position.Y += h; Position.Z += Direction.Y * c; if (a != 0.0) { World.Rotate(ref Direction, Math.Cos(-a), Math.Sin(-a)); } } // orphaned transponders if (!PreviewOnly) { for (int i = Data.FirstUsedBlock; i < Data.Blocks.Length; i++) { for (int j = 0; j < Data.Blocks[i].Transponder.Length; j++) { if (Data.Blocks[i].Transponder[j].Type != -1) { int n = i - Data.FirstUsedBlock; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); double d = Data.Blocks[i].Transponder[j].TrackPosition - TrackManager.CurrentTrack.Elements[n].StartingTrackPosition; int s = Data.Blocks[i].Transponder[j].Section; if (s >= 0) s = -1; TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TransponderEvent(d, Data.Blocks[i].Transponder[j].Type, Data.Blocks[i].Transponder[j].Data, s); Data.Blocks[i].Transponder[j].Type = -1; } } } } // insert station end events for (int i = 0; i < Game.Stations.Length; i++) { int j = Game.Stations[i].Stops.Length - 1; if (j >= 0) { double p = Game.Stations[i].Stops[j].TrackPosition + Game.Stations[i].Stops[j].ForwardTolerance + Data.BlockInterval; int k = (int)Math.Floor(p / (double)Data.BlockInterval) - Data.FirstUsedBlock; if (k >= 0 & k < Data.Blocks.Length) { double d = p - (double)(k + Data.FirstUsedBlock) * (double)Data.BlockInterval; int m = TrackManager.CurrentTrack.Elements[k].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[k].Events, m + 1); TrackManager.CurrentTrack.Elements[k].Events[m] = new TrackManager.StationEndEvent(d, i); } } } // create default point of interests if (Game.PointsOfInterest.Length == 0) { Game.PointsOfInterest = new OpenBve.Game.PointOfInterest[Game.Stations.Length]; int n = 0; for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].Stops.Length != 0) { Game.PointsOfInterest[n].Text = Game.Stations[i].Name; Game.PointsOfInterest[n].TrackPosition = Game.Stations[i].Stops[0].TrackPosition; Game.PointsOfInterest[n].TrackOffset = new Vector3(0.0, 2.8, 0.0); if (Game.Stations[i].OpenLeftDoors & !Game.Stations[i].OpenRightDoors) { Game.PointsOfInterest[n].TrackOffset.X = -2.5; } else if (!Game.Stations[i].OpenLeftDoors & Game.Stations[i].OpenRightDoors) { Game.PointsOfInterest[n].TrackOffset.X = 2.5; } n++; } } Array.Resize<Game.PointOfInterest>(ref Game.PointsOfInterest, n); } // convert block-based cant into point-based cant for (int i = CurrentTrackLength - 1; i >= 1; i--) { if (TrackManager.CurrentTrack.Elements[i].CurveCant == 0.0) { TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i - 1].CurveCant; } else if (TrackManager.CurrentTrack.Elements[i - 1].CurveCant != 0.0) { if (Math.Sign(TrackManager.CurrentTrack.Elements[i - 1].CurveCant) == Math.Sign(TrackManager.CurrentTrack.Elements[i].CurveCant)) { if (Math.Abs(TrackManager.CurrentTrack.Elements[i - 1].CurveCant) > Math.Abs(TrackManager.CurrentTrack.Elements[i].CurveCant)) { TrackManager.CurrentTrack.Elements[i].CurveCant = TrackManager.CurrentTrack.Elements[i - 1].CurveCant; } } else { TrackManager.CurrentTrack.Elements[i].CurveCant = 0.5 * (TrackManager.CurrentTrack.Elements[i].CurveCant + TrackManager.CurrentTrack.Elements[i - 1].CurveCant); } } } // finalize Array.Resize<TrackManager.TrackElement>(ref TrackManager.CurrentTrack.Elements, CurrentTrackLength); for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].Stops.Length == 0 & Game.Stations[i].StopMode != Game.StationStopMode.AllPass) { Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " expects trains to stop but does not define stop points at track position " + Game.Stations[i].DefaultTrackPosition.ToString(Culture) + " in file " + FileName); Game.Stations[i].StopMode = Game.StationStopMode.AllPass; } if (Game.Stations[i].StationType == Game.StationType.ChangeEnds) { if (i < Game.Stations.Length - 1) { if (Game.Stations[i + 1].StopMode != Game.StationStopMode.AllStop) { Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " is marked as \"change ends\" but the subsequent station does not expect all trains to stop in file " + FileName); Game.Stations[i + 1].StopMode = Game.StationStopMode.AllStop; } } else { Interface.AddMessage(Interface.MessageType.Warning, false, "Station " + Game.Stations[i].Name + " is marked as \"change ends\" but there is no subsequent station defined in file " + FileName); Game.Stations[i].StationType = Game.StationType.Terminal; } } } if (Game.Stations.Length != 0) { Game.Stations[Game.Stations.Length - 1].StationType = Game.StationType.Terminal; } if (TrackManager.CurrentTrack.Elements.Length != 0) { int n = TrackManager.CurrentTrack.Elements.Length - 1; int m = TrackManager.CurrentTrack.Elements[n].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[n].Events, m + 1); TrackManager.CurrentTrack.Elements[n].Events[m] = new TrackManager.TrackEndEvent(Data.BlockInterval); } // insert compatibility beacons if (!PreviewOnly) { List<TrackManager.TransponderEvent> transponders = new List<TrackManager.TransponderEvent>(); bool atc = false; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (!atc) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent station = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Atc) { Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 0, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 1, 0); atc = true; } } } else { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent station = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Ats) { Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 2, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 3, 0); } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationEndEvent) { TrackManager.StationEndEvent station = (TrackManager.StationEndEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Atc) { Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 1, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 2, 0); } else if (Game.Stations[station.StationIndex].SafetySystem == Game.SafetySystem.Ats) { Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[i].Events, TrackManager.CurrentTrack.Elements[i].Events.Length + 2); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 2] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 3, 0); TrackManager.CurrentTrack.Elements[i].Events[TrackManager.CurrentTrack.Elements[i].Events.Length - 1] = new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcTrackStatus, 0, 0); atc = false; } } else if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.LimitChangeEvent) { TrackManager.LimitChangeEvent limit = (TrackManager.LimitChangeEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; int speed = (int)Math.Round(Math.Min(4095.0, 3.6 * limit.NextSpeedLimit)); int distance = Math.Min(1048575, (int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + limit.TrackPositionDelta)); unchecked { int value = (int)((uint)speed | ((uint)distance << 12)); transponders.Add(new TrackManager.TransponderEvent(0.0, TrackManager.SpecialTransponderTypes.AtcSpeedLimit, value, 0)); } } } if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.TransponderEvent) { TrackManager.TransponderEvent transponder = TrackManager.CurrentTrack.Elements[i].Events[j] as TrackManager.TransponderEvent; if (transponder.Type == TrackManager.SpecialTransponderTypes.InternalAtsPTemporarySpeedLimit) { int speed = Math.Min(4095, transponder.Data); int distance = Math.Min(1048575, (int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition + transponder.TrackPositionDelta)); unchecked { int value = (int)((uint)speed | ((uint)distance << 12)); transponder.DontTriggerAnymore = true; } } } } } int n = TrackManager.CurrentTrack.Elements[0].Events.Length; Array.Resize<TrackManager.GeneralEvent>(ref TrackManager.CurrentTrack.Elements[0].Events, n + transponders.Count); for (int i = 0; i < transponders.Count; i++) { TrackManager.CurrentTrack.Elements[0].Events[n + i] = transponders[i]; } } // cant if (!PreviewOnly) { ComputeCantTangents(); int subdivisions = (int)Math.Floor(Data.BlockInterval / 5.0); if (subdivisions >= 2) { SmoothenOutTurns(subdivisions); ComputeCantTangents(); } } }
// parse animated object config /// <summary>Loads a collection of animated objects from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[4]; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); // cut comments out Lines[i] = j >= 0 ? Lines[i].Substring(0, j).Trim() : Lines[i].Trim(); rpnUsed = Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0; } if (rpnUsed) { Debug.AddMessage(Debug.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; Vector3D position = new Vector3D(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int equals = Lines[i].IndexOf("=", StringComparison.Ordinal); if (equals > 0) { /* * Process key-value pair, the only supported key is position. */ string before = Lines[i].Substring(0, equals).TrimEnd(); string after = Lines[i].Substring(equals + 1).TrimStart(); switch (before.ToLowerInvariant()) { case "position": ParsePosition(after, ref position, before, i + 1, FileName); break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { /* * Process object with file name relative to the location of this ANIMATED file. */ string Folder = System.IO.Path.GetDirectoryName(FileName); if (Path.ContainsInvalidPathChars(Lines[i])) { Debug.AddMessage(Debug.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Debug.AddMessage(Debug.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; if (ObjectCount >= Result.Objects.Length) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState(); aos.Object = s; aos.Position = position; a.States = new[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject(); Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; Result.Objects[ObjectCount].CurrentState = -1; Result.Objects[ObjectCount].TranslateXDirection = new Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].TranslateYDirection = new Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].TranslateZDirection = new Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].RotateXDirection = new Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].RotateYDirection = new Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].RotateZDirection = new Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2D(1.0, 0.0); Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2D(0.0, 1.0); Result.Objects[ObjectCount].RefreshRate = 0.0; Result.Objects[ObjectCount].ObjectIndex = -1; Vector3D Position = new Vector3D(0.0, 0.0, 0.0); bool timetableUsed = false; string[] StateFiles = null; string StateFunctionRpn = null; int StateFunctionLine = -1; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int equals = Lines[i].IndexOf("=", StringComparison.Ordinal); if (equals > 0) { string before = Lines[i].Substring(0, equals).TrimEnd(); string after = Lines[i].Substring(equals + 1).TrimStart(); switch (before.ToLowerInvariant()) { case "position": ParsePosition(after, ref Position, before, i + 1, FileName); break; case "states": if (!ParseState(after, ref StateFiles, before, i + 1, FileName)) return null; break; case "statefunction": try { StateFunctionLine = i; StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(after); } catch (Exception ex) { Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": { StateFunctionLine = i; StateFunctionRpn = after; } break; case "translatexdirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateXDirection, before, i + 1, FileName); break; case "translateydirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateYDirection, before, i + 1, FileName); break; case "translatezdirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateZDirection, before, i + 1, FileName); break; case "translatexfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName); break; case "translateyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName); break; case "translatezfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName); break; case "translatexfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName); break; case "translateyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName); break; case "translatezfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName); break; case "rotatexdirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateXDirection, before, i + 1, FileName); break; case "rotateydirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateYDirection, before, i + 1, FileName); break; case "rotatezdirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateZDirection, before, i + 1, FileName); break; case "rotatexfunction": ParseInfixFunc(after,ref Result.Objects[ObjectCount].RotateXFunction, before,i+1,FileName); break; case "rotateyfunction": ParseInfixFunc(after,ref Result.Objects[ObjectCount].RotateYFunction, before,i+1,FileName); break; case "rotatezfunction": ParseInfixFunc(after,ref Result.Objects[ObjectCount].RotateZFunction, before,i+1,FileName); break; case "rotatexfunctionrpn": ParsePostfixFunc(after,ref Result.Objects[ObjectCount].RotateXFunction, before,i+1,FileName); break; case "rotateyfunctionrpn": ParsePostfixFunc(after,ref Result.Objects[ObjectCount].RotateYFunction, before,i+1,FileName); break; case "rotatezfunctionrpn": ParsePostfixFunc(after,ref Result.Objects[ObjectCount].RotateZFunction, before,i+1,FileName); break; case "rotatexdamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateXDamping,before, i + 1, FileName); break; case "rotateydamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateYDamping,before, i + 1, FileName); break; case "rotatezdamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateZDamping,before, i + 1, FileName); break; case "textureshiftxdirection": ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftXDirection, before, i + 1, FileName); break; case "textureshiftydirection": ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftYDirection, before, i + 1, FileName); break; case "textureshiftxfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName); break; case "textureshiftyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName); break; case "textureshiftxfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName); break; case "textureshiftyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName); break; case "textureoverride": switch (after.ToLowerInvariant()) { case "none": break; case "timetable": if (!timetableUsed) { Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]); timetableUsed = true; } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "Unrecognized value in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } break; case "refreshrate": { double r; if (!double.TryParse(after, System.Globalization.NumberStyles.Float, Culture, out r)) { Debug.AddMessage(Debug.MessageType.Error, false, "Value is invalid in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Debug.AddMessage(Debug.MessageType.Error, false, "Value is expected to be non-negative in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } i++; } i--; if (StateFiles != null) { // create the object if (timetableUsed) { if (StateFunctionRpn != null) { StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?"; } else { StateFunctionRpn = "timetable"; } } if (StateFunctionRpn != null) { try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn); } catch (Exception ex) { Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName); } } Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 || Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 || Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new Vector3D(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); if (Result.Objects[ObjectCount].States[k].Object != null) { Result.Objects[ObjectCount].States[k].Object.Dynamic = true; } } else { Result.Objects[ObjectCount].States[k].Object = null; } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { Result.Objects[ObjectCount].States[j].Position = Position; } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } } Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return Result; }
/// <summary>Loads a Loksim3D GruppenObject</summary> /// <param name="FileName">The filename to load</param> /// <param name="Encoding">The text encoding of the containing file (Currently ignored, REMOVE??)</param> /// <param name="Rotation">A three-dimemsional vector describing the rotation to be applied</param> /// <returns>A new animated object collection, containing the GruppenObject's meshes etc.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, Encoding Encoding, Vector3 Rotation) { XmlDocument currentXML = new XmlDocument(); ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[0]; try { currentXML.Load(FileName); } catch (Exception ex) { //The XML is not strictly valid string[] Lines = File.ReadAllLines(FileName); using (var stringReader = new StringReader(Lines[0])) { var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment }; using (var xmlReader = XmlReader.Create(stringReader, settings)) { if (xmlReader.Read()) { //Attempt to find the text encoding and re-read the file var result = xmlReader.GetAttribute("encoding"); if (result != null) { var e = System.Text.Encoding.GetEncoding(result); Lines = File.ReadAllLines(FileName, e); //Turf out the old encoding, as our string array should now be UTF-8 Lines[0] = "<?xml version=\"1.0\"?>"; } } } } for (int i = 0; i < Lines.Length; i++) { while (Lines[i].IndexOf("\"\"", StringComparison.InvariantCulture) != -1) { //Loksim parser tolerates multiple quotes, strict XML does not Lines[i] = Lines[i].Replace("\"\"", "\""); } while (Lines[i].IndexOf(" ", StringComparison.InvariantCulture) != -1) { //Replace double-spaces with singles Lines[i] = Lines[i].Replace(" ", " "); } } bool tryLoad = false; try { //Horrible hack: Write out our string array to a new memory stream, then load from this stream //Why can't XmlDocument.Load() just take a string array...... using (var stream = new MemoryStream()) { var sw = new StreamWriter(stream); foreach (var line in Lines) { sw.Write(line); sw.Flush(); } sw.Flush(); stream.Position = 0; currentXML.Load(stream); tryLoad = true; } } catch { //Generic catch-all clause } if (!tryLoad) { //Pass out the *original* XML error, not anything generated when we've tried to correct it Interface.AddMessage(MessageType.Error, false, "Error parsing Loksim3D XML: " + ex.Message); return(null); } } string BaseDir = System.IO.Path.GetDirectoryName(FileName); GruppenObject[] CurrentObjects = new GruppenObject[0]; //Check for null if (currentXML.DocumentElement != null) { UnifiedObject[] obj = new UnifiedObject[0]; XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT"); if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode node in outerNode.ChildNodes) { if (node.Name == "Object" && node.ChildNodes.OfType <XmlElement>().Any()) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { GruppenObject Object = new GruppenObject { Rotation = Rotation }; foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { case "Name": string ObjectFile = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory); if (!File.Exists(ObjectFile)) { Object.Name = null; Interface.AddMessage(MessageType.Warning, true, "Ls3d Object file " + attribute.Value + " not found."); } else { Object.Name = ObjectFile; } break; case "Position": string[] SplitPosition = attribute.Value.Split(';'); double.TryParse(SplitPosition[0], out Object.Position.X); double.TryParse(SplitPosition[1], out Object.Position.Y); double.TryParse(SplitPosition[2], out Object.Position.Z); break; case "Rotation": string[] SplitRotation = attribute.Value.Split(';'); Vector3 r; double.TryParse(SplitRotation[0], out r.X); double.TryParse(SplitRotation[1], out r.Y); double.TryParse(SplitRotation[2], out r.Z); Object.Rotation += r; break; case "ShowOn": //Defines when the object should be shown Object.FunctionScript = FunctionScriptNotation.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, false)); break; case "HideOn": //Defines when the object should be hidden Object.FunctionScript = FunctionScriptNotation.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, true)); break; case "FixedDynamicVisibility": if (attribute.Value.ToLowerInvariant() == "true") { Object.FixedDynamicVisibility = true; } else { Object.FixedDynamicVisibility = false; } break; case "DynamicVisibility": if (Object.FixedDynamicVisibility) { Object.FunctionScript = FunctionScriptNotation.GetPostfixNotationFromInfixNotation(GetDynamicFunction(attribute.Value)); } break; } } if (Object.Name != null) { Array.Resize <GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1); CurrentObjects[CurrentObjects.Length - 1] = Object; } } } } } } } //We've loaded the XML references, now load the objects into memory //Single mesh object, containing all static components of the LS3D object //If we use multiples, the Z-sorting throws a wobbly StaticObject staticObject = null; for (int i = 0; i < CurrentObjects.Length; i++) { if (CurrentObjects[i] == null || string.IsNullOrEmpty(CurrentObjects[i].Name)) { continue; } StaticObject Object = null; ObjectManager.AnimatedObjectCollection AnimatedObject = null; try { if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dgrp")) { AnimatedObject = ReadObject(CurrentObjects[i].Name, Encoding, CurrentObjects[i].Rotation); } else if (CurrentObjects[i].Name.ToLowerInvariant().EndsWith(".l3dobj")) { Object = (StaticObject)ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, false, false, false, CurrentObjects[i].Rotation); } else { throw new Exception("Format " + System.IO.Path.GetExtension(CurrentObjects[i].Name) + " is not currently supported by the Loksim3D object parser"); } } catch (Exception ex) { Interface.AddMessage(MessageType.Error, false, ex.Message); } if (Object != null) { if (!string.IsNullOrEmpty(CurrentObjects[i].FunctionScript)) { //If the function script is not empty, this is a new animated object bit Array.Resize <UnifiedObject>(ref obj, obj.Length + 1); obj[obj.Length - 1] = Object; int aL = Result.Objects.Length; Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, aL + 1); ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); AnimatedObjectState aos = new AnimatedObjectState(Object, CurrentObjects[i].Position); a.States = new AnimatedObjectState[] { aos }; Result.Objects[aL] = a; Result.Objects[aL].StateFunction = new FunctionScript(Program.CurrentHost, CurrentObjects[i].FunctionScript + " 1 == --", false); } else { //Otherwise, join to the main static mesh & update co-ords for (int j = 0; j < Object.Mesh.Vertices.Length; j++) { Object.Mesh.Vertices[j].Coordinates += CurrentObjects[i].Position; } staticObject.JoinObjects(Object); } } else if (AnimatedObject != null) { int rl = Result.Objects.Length; int l = AnimatedObject.Objects.Length; Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + l); for (int o = rl; o < rl + l; o++) { if (AnimatedObject.Objects[o - rl] != null) { Result.Objects[o] = AnimatedObject.Objects[o - rl].Clone(); for (int si = 0; si < Result.Objects[o].States.Length; si++) { Result.Objects[o].States[si].Position += CurrentObjects[i].Position; } } else { Result.Objects[o] = new ObjectManager.AnimatedObject(); Result.Objects[o].States = new AnimatedObjectState[0]; } } } } if (staticObject != null) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1); ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); AnimatedObjectState aos = new AnimatedObjectState(staticObject, Vector3.Zero); a.States = new AnimatedObjectState[] { aos }; Result.Objects[Result.Objects.Length - 1] = a; } } return(Result); } //Didn't find an acceptable XML object //Probably will cause things to throw an absolute wobbly somewhere.... return(null); }
// parse animated object config /// <summary>Loads a collection of animated objects from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection { Objects = new ObjectManager.AnimatedObject[4] }; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } if (Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0) { rpnUsed = true; } } if (rpnUsed) { Interface.AddMessage(Interface.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; Vector3 position = new Vector3(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { position = new Vector3(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { string Folder = System.IO.Path.GetDirectoryName(FileName); if (Path.ContainsInvalidChars(Lines[i])) { Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = s, Position = position }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject { States = new ObjectManager.AnimatedObjectState[] {}, CurrentState = -1, TranslateXDirection = new Vector3(1.0, 0.0, 0.0), TranslateYDirection = new Vector3(0.0, 1.0, 0.0), TranslateZDirection = new Vector3(0.0, 0.0, 1.0), RotateXDirection = new Vector3(1.0, 0.0, 0.0), RotateYDirection = new Vector3(0.0, 1.0, 0.0), RotateZDirection = new Vector3(0.0, 0.0, 1.0), TextureShiftXDirection = new Vector2(1.0, 0.0), TextureShiftYDirection = new Vector2(0.0, 1.0), RefreshRate = 0.0, ObjectIndex = -1 }; Vector3 Position = new Vector3(0.0, 0.0, 0.0); bool timetableUsed = false; string[] StateFiles = null; string StateFunctionRpn = null; int StateFunctionLine = -1; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Position = new Vector3(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "states": { string[] s = b.Split(','); if (s.Length >= 1) { string Folder = System.IO.Path.GetDirectoryName(FileName); StateFiles = new string[s.Length]; for (int k = 0; k < s.Length; k++) { s[k] = s[k].Trim(); if (s[k].Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " is an empty string - did you mean something else? - in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else if (Path.ContainsInvalidChars(s[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else { StateFiles[k] = OpenBveApi.Path.CombineFile(Folder, s[k]); if (!System.IO.File.Exists(StateFiles[k])) { Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } break; case "statefunction": try { StateFunctionLine = i; StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": { StateFunctionLine = i; StateFunctionRpn = b; } break; case "translatexdirection": case "translateydirection": case "translatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "translatexdirection": Result.Objects[ObjectCount].TranslateXDirection = new Vector3(x, y, z); break; case "translateydirection": Result.Objects[ObjectCount].TranslateYDirection = new Vector3(x, y, z); break; case "translatezdirection": Result.Objects[ObjectCount].TranslateZDirection = new Vector3(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "translatexfunction": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunction": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunction": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatexfunctionrpn": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunctionrpn": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunctionrpn": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdirection": case "rotateydirection": case "rotatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (x == 0.0 & y == 0.0 & z == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdirection": Result.Objects[ObjectCount].RotateXDirection = new Vector3(x, y, z); break; case "rotateydirection": Result.Objects[ObjectCount].RotateYDirection = new Vector3(x, y, z); break; case "rotatezdirection": Result.Objects[ObjectCount].RotateZDirection = new Vector3(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "rotatexfunction": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunction": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunction": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexfunctionrpn": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunctionrpn": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunctionrpn": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdamping": case "rotateydamping": case "rotatezdamping": { string[] s = b.Split(','); if (s.Length == 2) { double nf, dr; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf)) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr)) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (nf <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (dr <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdamping": Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr); break; case "rotateydamping": Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr); break; case "rotatezdamping": Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxdirection": case "textureshiftydirection": { string[] s = b.Split(','); if (s.Length == 2) { double x, y; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "textureshiftxdirection": Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2(x, y); break; case "textureshiftydirection": Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2(x, y); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxfunction": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunction": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftxfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureoverride": switch (b.ToLowerInvariant()) { case "none": break; case "timetable": if (!timetableUsed) { Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]); timetableUsed = true; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognized value in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } break; case "refreshrate": { double r; if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } i++; } i--; if (StateFiles != null) { // create the object if (timetableUsed) { if (StateFunctionRpn != null) { StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?"; } else { StateFunctionRpn = "timetable"; } } if (StateFunctionRpn != null) { try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName); } } Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.X != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.Y != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new Vector3(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); if (Result.Objects[ObjectCount].States[k].Object != null) { Result.Objects[ObjectCount].States[k].Object.Dynamic = true; } } else { Result.Objects[ObjectCount].States[k].Object = null; } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { Result.Objects[ObjectCount].States[j].Position = Position; } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; case "[sound]": case "[statechangesound]": //Only show the sound nag once per route, otherwise this could cause spam... if (!Program.SoundError) { Interface.AddMessage(Interface.MessageType.Information, false, "Animated objects containing sounds are only supported in openBVE v1.5.2.4+"); Interface.AddMessage(Interface.MessageType.Information, false, "Object Viewer does not support sounds. Please use the main game to test these!"); Program.SoundError = true; } i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { i++; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } } Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return(Result); }
/// <summary>Attempts to load and parse the current train's panel configuration file.</summary> /// <param name="TrainPath">The absolute on-disk path to the train folder.</param> /// <param name="Encoding">The automatically detected or manually set encoding of the panel configuration file.</param> /// <param name="Train">The base train on which to apply the panel configuration.</param> internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, Train Train) { Train.Cars[Train.DriverCar].CarSections = new CarSection[1]; Train.Cars[Train.DriverCar].CarSections[0] = new CarSection { Groups = new ElementsGroup[1] }; Train.Cars[Train.DriverCar].CarSections[0].Groups[0] = new ElementsGroup { Elements = new ObjectManager.AnimatedObject[] { }, Overlay = true }; string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.xml"); if (!System.IO.File.Exists(File)) { //Try animated variant too File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated.xml"); } if (System.IO.File.Exists(File)) { Program.FileSystem.AppendToLogFile("Loading train panel: " + File); try { /* * First load the XML. We use this to determine * whether this is a 2D or a 3D animated panel */ XDocument CurrentXML = XDocument.Load(File, LoadOptions.SetLineInfo); // Check for null if (CurrentXML.Root != null) { IEnumerable <XElement> DocumentElements = CurrentXML.Root.Elements("PanelAnimated"); if (DocumentElements.Any()) { PanelAnimatedXmlParser.ParsePanelAnimatedXml(System.IO.Path.GetFileName(File), TrainPath, Train, Train.DriverCar); Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.NotAvailable; World.CameraRestriction = Camera.RestrictionMode.NotAvailable; } DocumentElements = CurrentXML.Root.Elements("Panel"); if (DocumentElements.Any()) { PanelXmlParser.ParsePanelXml(System.IO.Path.GetFileName(File), TrainPath, Train, Train.DriverCar); Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On; World.CameraRestriction = Camera.RestrictionMode.On; } } } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", "panel.xml"); MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.RestartArguments = " "; Loading.Cancel = true; return; } if (Train.Cars[Train.DriverCar].CarSections[0].Groups[0].Elements.Any()) { World.UpdateViewingDistances(); return; } Interface.AddMessage(MessageType.Error, false, "The panel.xml file " + File + " failed to load. Falling back to legacy panel."); } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated"); if (System.IO.File.Exists(File)) { Program.FileSystem.AppendToLogFile("Loading train panel: " + File); if (System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg")) || System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"))) { Program.FileSystem.AppendToLogFile("INFO: This train contains both a 2D and a 3D panel. The 3D panel will always take precedence"); } ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding); if (a != null) { //HACK: If a == null , loading our animated object completely failed (Missing objects?). Fallback to trying the panel2.cfg try { for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } Train.Cars[Train.DriverCar].CarSections[0].Groups[0].Elements = a.Objects; Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.NotAvailable; World.CameraRestriction = Camera.RestrictionMode.NotAvailable; World.UpdateViewingDistances(); return; } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", "panel.animated"); MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.RestartArguments = " "; Loading.Cancel = true; return; } } Interface.AddMessage(MessageType.Error, false, "The panel.animated file " + File + " failed to load. Falling back to 2D panel."); } } var Panel2 = false; try { File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); if (System.IO.File.Exists(File)) { Program.FileSystem.AppendToLogFile("Loading train panel: " + File); Panel2 = true; Panel2CfgParser.ParsePanel2Config("panel2.cfg", TrainPath, Encoding, Train, Train.DriverCar); Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On; World.CameraRestriction = Camera.RestrictionMode.On; } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); if (System.IO.File.Exists(File)) { Program.FileSystem.AppendToLogFile("Loading train panel: " + File); PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train); Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On; World.CameraRestriction = Camera.RestrictionMode.On; } else { World.CameraRestriction = Camera.RestrictionMode.NotAvailable; } } } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", Panel2 ? "panel2.cfg" : "panel.cfg"); MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.RestartArguments = " "; Loading.Cancel = true; } }
internal static void ParseExtensionsConfig(string filePath, Encoding encoding, out UnifiedObject[] carObjects, out UnifiedObject[] bogieObjects, out double[] axleLocations, out TrainManager.Train train, bool loadObjects) { CultureInfo Culture = CultureInfo.InvariantCulture; carObjects = new UnifiedObject[] { }; bogieObjects = new UnifiedObject[] { }; axleLocations = new double[] { }; train = new TrainManager.Train(); if (!System.IO.File.Exists(filePath)) { return; } train.Cars = new TrainManager.Car[] { }; bool[] carObjectsReversed = new bool[train.Cars.Length]; bool[] bogieObjectsReversed = new bool[train.Cars.Length * 2]; bool[] carsDefined = new bool[train.Cars.Length]; bool[] bogiesDefined = new bool[train.Cars.Length * 2]; axleLocations = new double[train.Cars.Length * 2]; string trainPath = System.IO.Path.GetDirectoryName(filePath); System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.InvariantCulture; TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(filePath); if (newEncoding != TextEncoding.Encoding.Unknown) { switch (newEncoding) { case TextEncoding.Encoding.Utf7: encoding = Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: encoding = Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: encoding = Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: encoding = Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: encoding = Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: encoding = Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: encoding = Encoding.GetEncoding(932); break; } } string[] lines = System.IO.File.ReadAllLines(filePath, encoding); for (int i = 0; i < lines.Length; i++) { int j = lines[i].IndexOf(';'); if (j >= 0) { lines[i] = lines[i].Substring(0, j).Trim(); } else { lines[i] = lines[i].Trim(); } } for (int i = 0; i < lines.Length; i++) { if (lines[i].Length != 0) { switch (lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath); } else { string file = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { carObjects[n] = ObjectManager.LoadObject(file, encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The car object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath); } } } else { Interface.AddMessage(MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } } i++; } i--; break; default: if (lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = lines[i].Substring(4, lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, culture, out n)) { if (n >= 0) { if (n >= train.Cars.Length) { Array.Resize(ref train.Cars, n + 1); Array.Resize(ref carObjects, n + 1); Array.Resize(ref bogieObjects, (n + 1) * 2); Array.Resize(ref carObjectsReversed, n + 1); Array.Resize(ref bogieObjectsReversed, (n + 1) * 2); Array.Resize(ref carsDefined, n + 1); Array.Resize(ref bogiesDefined, (n + 1) * 2); Array.Resize(ref axleLocations, (n + 1) * 2); } if (carsDefined[n]) { Interface.AddMessage(MessageType.Error, false, "Car " + n.ToString(culture) + " has already been declared at line " + (i + 1).ToString(culture) + " in file " + filePath); } carsDefined[n] = true; i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, "An empty car object was supplied at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath); } else { string file = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { carObjects[n] = ObjectManager.LoadObject(file, encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The car object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, culture, out m)) { if (m > 0.0) { train.Cars[n].Length = m; } else { Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(culture) + " in file " + filePath); } } break; case "reversed": carObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } else if (rear >= front) { Interface.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } else { axleLocations[n] = rear; axleLocations[n + 1] = front; } } else { Interface.AddMessage(MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + filePath); } break; default: Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } } else { Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else if (lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = lines[i].Substring(6, lines[i].Length - 7); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, culture, out n)) { if (n >= train.Cars.Length * 2) { Array.Resize(ref train.Cars, n / 2 + 1); Array.Resize(ref carObjects, n / 2 + 1); Array.Resize(ref bogieObjects, n + 2); Array.Resize(ref carObjectsReversed, n / 2 + 1); Array.Resize(ref bogieObjectsReversed, n + 2); Array.Resize(ref carsDefined, n / 2 + 1); Array.Resize(ref bogiesDefined, n + 2); } if (n > bogiesDefined.Length - 1) { continue; } if (bogiesDefined[n]) { Interface.AddMessage(MessageType.Error, false, "Bogie " + n.ToString(culture) + " has already been declared at line " + (i + 1).ToString(culture) + " in file " + filePath); } bogiesDefined[n] = true; //Assuming that there are two bogies per car if (n >= 0 & n < train.Cars.Length * 2) { i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { if (lines[i].Length != 0) { int j = lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = lines[i].Substring(0, j).TrimEnd(); string b = lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(culture) + " in file " + filePath); } else { if (string.IsNullOrEmpty(b)) { Interface.AddMessage(MessageType.Error, true, "An empty bogie object was supplied at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } string file = OpenBveApi.Path.CombineFile(trainPath, b); if (System.IO.File.Exists(file)) { if (loadObjects) { bogieObjects[n] = ObjectManager.LoadObject(file, encoding, false, false, false); } } else { Interface.AddMessage(MessageType.Error, true, "The bogie object " + file + " does not exist at line " + (i + 1).ToString(culture) + " in file " + filePath); } } break; case "length": { Interface.AddMessage(MessageType.Error, false, "A defined length is not supported for bogies at line " + (i + 1).ToString(culture) + " in file " + filePath); } break; case "reversed": bogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; case "axles": //Axles aren't used in bogie positioning, just in rotation break; default: Interface.AddMessage(MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); break; } } else { Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } } i++; } i--; } else { Interface.AddMessage(MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else { Interface.AddMessage(MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(culture) + " in file " + filePath); } } else if (lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & lines[i].EndsWith("]", StringComparison.Ordinal)) { i++; while (i < lines.Length && !lines[i].StartsWith("[", StringComparison.Ordinal) & !lines[i].EndsWith("]", StringComparison.Ordinal)) { /* * Coupler statments are currently not supported in Object Viewer */ i++; } i--; } else { // default if (lines.Length == 1 && encoding.Equals(Encoding.Unicode)) { /* * If only one line, there's a good possibility that our file is NOT Unicode at all * and that the misdetection has turned it into garbage * * Try again with ASCII instead */ ParseExtensionsConfig(filePath, Encoding.GetEncoding(1252), out carObjects, out bogieObjects, out axleLocations, out train, loadObjects); return; } Interface.AddMessage(MessageType.Error, false, "Invalid statement " + lines[i] + " encountered at line " + (i + 1).ToString(culture) + " in file " + filePath); } break; } } } // check for car objects and reverse if necessary int carObjectsCount = 0; for (int i = 0; i < train.Cars.Length; i++) { if (carObjects[i] != null) { carObjectsCount++; if (carObjectsReversed[i] && loadObjects) { if (carObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)carObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (carObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)carObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjectsCount = 0; for (int i = 0; i < train.Cars.Length * 2; i++) { if (bogieObjects[i] != null) { bogieObjectsCount++; if (bogieObjectsReversed[i] && loadObjects) { if (bogieObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)bogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (bogieObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)bogieObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjectsCount > 0 & carObjectsCount < train.Cars.Length) { Interface.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + filePath); } if (bogieObjectsCount > 0 & bogieObjectsCount < train.Cars.Length * 2) { Interface.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + filePath); } }
/// <summary>Loads a Loksim3D GruppenObject</summary> /// <param name="FileName">The filename to load</param> /// <param name="Encoding">The text encoding of the containing file (Currently ignored, REMOVE??)</param> /// <param name="LoadMode">The object load mode</param> /// <returns>A new animated object collection, containing the GruppenObject's meshes etc.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { XmlDocument currentXML = new XmlDocument(); //May need to be changed to use de-DE System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[0]; try { currentXML.Load(FileName); } catch (Exception ex) { //The XML is not strictly valid string[] Lines = File.ReadAllLines(FileName); using (var stringReader = new StringReader(Lines[0])) { var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment }; using (var xmlReader = XmlReader.Create(stringReader, settings)) { if (xmlReader.Read()) { //Attempt to find the text encoding and re-read the file var result = xmlReader.GetAttribute("encoding"); var e = System.Text.Encoding.GetEncoding(result); Lines = File.ReadAllLines(FileName, e); //Turf out the old encoding, as our string array should now be UTF-8 Lines[0] = "<?xml version=\"1.0\"?>"; } } } for (int i = 0; i < Lines.Length; i++) { while (Lines[i].IndexOf("\"\"") != -1) { //Loksim parser tolerates multiple quotes, strict XML does not Lines[i] = Lines[i].Replace("\"\"", "\""); } while (Lines[i].IndexOf(" ") != -1) { //Replace double-spaces with singles Lines[i] = Lines[i].Replace(" ", " "); } } bool tryLoad = false; try { //Horrible hack: Write out our string array to a new memory stream, then load from this stream //Why can't XmlDocument.Load() just take a string array...... using (var stream = new MemoryStream()) { var sw = new StreamWriter(stream); foreach (var line in Lines) { sw.Write(line); sw.Flush(); } sw.Flush(); stream.Position = 0; currentXML.Load(stream); tryLoad = true; } } catch { //Generic catch-all clause } if (!tryLoad) { //Pass out the *original* XML error, not anything generated when we've tried to correct it Interface.AddMessage(Interface.MessageType.Error, false, "Error parsing Loksim3D XML: " + ex.Message); return(null); } } string BaseDir = System.IO.Path.GetDirectoryName(FileName); GruppenObject[] CurrentObjects = new GruppenObject[0]; //Check for null if (currentXML.DocumentElement != null) { ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[0]; XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT"); if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.HasChildNodes) { foreach (XmlNode node in outerNode.ChildNodes) { if (node.Name == "Object" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { GruppenObject Object = new GruppenObject(); foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { case "Name": string ObjectFile = OpenBveApi.Path.Loksim3D.CombineFile(BaseDir, attribute.Value, Program.FileSystem.LoksimPackageInstallationDirectory); if (!File.Exists(ObjectFile)) { Object.Name = null; Interface.AddMessage(Interface.MessageType.Warning, true, "Ls3d Object file " + attribute.Value + " not found."); } else { Object.Name = ObjectFile; } break; case "Position": string[] SplitPosition = attribute.Value.Split(';'); double.TryParse(SplitPosition[0], out Object.Position.X); double.TryParse(SplitPosition[1], out Object.Position.Y); double.TryParse(SplitPosition[2], out Object.Position.Z); break; case "Rotation": string[] SplitRotation = attribute.Value.Split(';'); double.TryParse(SplitRotation[0], out Object.Rotation.X); double.TryParse(SplitRotation[1], out Object.Rotation.Y); double.TryParse(SplitRotation[2], out Object.Rotation.Z); break; case "ShowOn": //Defines when the object should be shown Object.FunctionScript = FunctionScripts.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, false)); break; case "HideOn": //Defines when the object should be hidden Object.FunctionScript = FunctionScripts.GetPostfixNotationFromInfixNotation(GetAnimatedFunction(attribute.Value, true)); break; } } if (Object.Name != null) { Array.Resize <GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1); CurrentObjects[CurrentObjects.Length - 1] = Object; } } } } } } } //We've loaded the XML references, now load the objects into memory for (int i = 0; i < CurrentObjects.Length; i++) { if (CurrentObjects[i] == null || string.IsNullOrEmpty(CurrentObjects[i].Name)) { continue; } var Object = (ObjectManager.StaticObject)ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, LoadMode, false, false, false, CurrentObjects[i].Rotation); if (Object != null) { Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length + 1); obj[obj.Length - 1] = Object; Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1); ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = Object, Position = CurrentObjects[i].Position, }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[i] = a; if (!string.IsNullOrEmpty(CurrentObjects[i].FunctionScript)) { Result.Objects[i].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(CurrentObjects[i].FunctionScript + " 1 == --"); } } } } return(Result); } //Didn't find an acceptable XML object //Probably will cause things to throw an absolute wobbly somewhere.... return(null); }
internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { XmlDocument currentXML = new XmlDocument(); //May need to be changed to use de-DE System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[0]; int ObjectCount = 0; currentXML.Load(FileName); string BaseDir = System.IO.Path.GetDirectoryName(FileName); GruppenObject[] CurrentObjects = new GruppenObject[0]; //Check for null if (currentXML.DocumentElement != null) { ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[0]; XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT"); if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.HasChildNodes) { foreach (XmlNode node in outerNode.ChildNodes) { if (node.Name == "Object" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { Array.Resize <GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1); GruppenObject Object = new GruppenObject(); foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { case "Name": string ObjectFile = OpenBveApi.Path.CombineFile(BaseDir, attribute.Value); Object.Name = ObjectFile; ObjectCount++; break; case "Position": string[] SplitPosition = attribute.Value.Split(';'); double.TryParse(SplitPosition[0], out Object.Position.X); double.TryParse(SplitPosition[1], out Object.Position.Y); double.TryParse(SplitPosition[2], out Object.Position.Z); break; case "Rotation": string[] SplitRotation = attribute.Value.Split(';'); double.TryParse(SplitRotation[0], out Object.RotationX); double.TryParse(SplitRotation[1], out Object.RotationY); double.TryParse(SplitRotation[2], out Object.RotationZ); break; } } CurrentObjects[CurrentObjects.Length - 1] = Object; } } } } } } //We've loaded the XML references, now load the objects into memory for (int i = 0; i < CurrentObjects.Length; i++) { var Object = ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, LoadMode, false, false, false, CurrentObjects[i].RotationX, CurrentObjects[i].RotationY, CurrentObjects[i].RotationZ); if (Object != null) { Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length + 1); obj[obj.Length - 1] = Object; } } for (int j = 0; j < obj.Length; j++) { if (obj[j] != null) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1); if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = s, Position = CurrentObjects[j].Position }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[j] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += CurrentObjects[j].Position.X; a.Objects[k].States[h].Position.Y += CurrentObjects[j].Position.Y; a.Objects[k].States[h].Position.Z += CurrentObjects[j].Position.Z; } Result.Objects[j] = a.Objects[k]; ObjectCount++; } } } } } return(Result); } //Didn't find an acceptable XML object //Probably will cause things to throw an absolute wobbly somewhere.... return(null); }
internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { XmlDocument currentXML = new XmlDocument(); //May need to be changed to use de-DE System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[0]; int ObjectCount = 0; currentXML.Load(FileName); string BaseDir = System.IO.Path.GetDirectoryName(FileName); GruppenObject[] CurrentObjects = new GruppenObject[0]; //Check for null if (currentXML.DocumentElement != null) { ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[0]; XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/GRUPPENOBJECT"); if (DocumentNodes != null) { foreach (XmlNode outerNode in DocumentNodes) { if (outerNode.HasChildNodes) { foreach (XmlNode node in outerNode.ChildNodes) { if (node.Name == "Object" && node.HasChildNodes) { foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name == "Props" && childNode.Attributes != null) { Array.Resize<GruppenObject>(ref CurrentObjects, CurrentObjects.Length + 1); GruppenObject Object = new GruppenObject(); foreach (XmlAttribute attribute in childNode.Attributes) { switch (attribute.Name) { case "Name": string ObjectFile = OpenBveApi.Path.CombineFile(BaseDir,attribute.Value); Object.Name = ObjectFile; ObjectCount++; break; case "Position": string[] SplitPosition = attribute.Value.Split(';'); double.TryParse(SplitPosition[0], out Object.Position.X); double.TryParse(SplitPosition[1], out Object.Position.Y); double.TryParse(SplitPosition[2], out Object.Position.Z); break; case "Rotation": string[] SplitRotation = attribute.Value.Split(';'); double.TryParse(SplitRotation[0], out Object.RotationX); double.TryParse(SplitRotation[1], out Object.RotationY); double.TryParse(SplitRotation[2], out Object.RotationZ); break; } } CurrentObjects[CurrentObjects.Length - 1] = Object; } } } } } } //We've loaded the XML references, now load the objects into memory for (int i = 0; i < CurrentObjects.Length; i++) { var Object = ObjectManager.LoadObject(CurrentObjects[i].Name, Encoding, LoadMode, false, false, false, CurrentObjects[i].RotationX, CurrentObjects[i].RotationY, CurrentObjects[i].RotationZ); if (Object != null) { Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length +1); obj[obj.Length - 1] = Object; } } for (int j = 0; j < obj.Length; j++) { if (obj[j] != null) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length + 1); if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject) obj[j]; s.Dynamic = true; ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = s, Position = CurrentObjects[j].Position }; a.States = new ObjectManager.AnimatedObjectState[] {aos}; Result.Objects[j] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection) obj[j]; for (int k = 0; k < a.Objects.Length; k++) { for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += CurrentObjects[j].Position.X; a.Objects[k].States[h].Position.Y += CurrentObjects[j].Position.Y; a.Objects[k].States[h].Position.Z += CurrentObjects[j].Position.Z; } Result.Objects[j] = a.Objects[k]; ObjectCount++; } } } } } return Result; } //Didn't find an acceptable XML object //Probably will cause things to throw an absolute wobbly somewhere.... return null; }
// parse animated object config /// <summary>Loads a collection of animated objects from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[4]; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); // cut comments out Lines[i] = j >= 0 ? Lines[i].Substring(0, j).Trim() : Lines[i].Trim(); rpnUsed = Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0; } if (rpnUsed) { Debug.AddMessage(Debug.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; Vector3D position = new Vector3D(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int equals = Lines[i].IndexOf("=", StringComparison.Ordinal); if (equals > 0) { /* * Process key-value pair, the only supported key is position. */ string before = Lines[i].Substring(0, equals).TrimEnd(); string after = Lines[i].Substring(equals + 1).TrimStart(); switch (before.ToLowerInvariant()) { case "position": ParsePosition(after, ref position, before, i + 1, FileName); break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { /* * Process object with file name relative to the location of this ANIMATED file. */ string Folder = System.IO.Path.GetDirectoryName(FileName); if (Path.ContainsInvalidPathChars(Lines[i])) { Debug.AddMessage(Debug.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Debug.AddMessage(Debug.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState(); aos.Object = s; aos.Position = position; a.States = new[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject(); Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; Result.Objects[ObjectCount].CurrentState = -1; Result.Objects[ObjectCount].TranslateXDirection = new Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].TranslateYDirection = new Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].TranslateZDirection = new Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].RotateXDirection = new Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].RotateYDirection = new Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].RotateZDirection = new Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2D(1.0, 0.0); Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2D(0.0, 1.0); Result.Objects[ObjectCount].RefreshRate = 0.0; Result.Objects[ObjectCount].ObjectIndex = -1; Vector3D Position = new Vector3D(0.0, 0.0, 0.0); bool timetableUsed = false; string[] StateFiles = null; string StateFunctionRpn = null; int StateFunctionLine = -1; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int equals = Lines[i].IndexOf("=", StringComparison.Ordinal); if (equals > 0) { string before = Lines[i].Substring(0, equals).TrimEnd(); string after = Lines[i].Substring(equals + 1).TrimStart(); switch (before.ToLowerInvariant()) { case "position": ParsePosition(after, ref Position, before, i + 1, FileName); break; case "states": if (!ParseState(after, ref StateFiles, before, i + 1, FileName)) { return(null); } break; case "statefunction": try { StateFunctionLine = i; StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(after); } catch (Exception ex) { Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": { StateFunctionLine = i; StateFunctionRpn = after; } break; case "translatexdirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateXDirection, before, i + 1, FileName); break; case "translateydirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateYDirection, before, i + 1, FileName); break; case "translatezdirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateZDirection, before, i + 1, FileName); break; case "translatexfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName); break; case "translateyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName); break; case "translatezfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName); break; case "translatexfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName); break; case "translateyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName); break; case "translatezfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName); break; case "rotatexdirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateXDirection, before, i + 1, FileName); break; case "rotateydirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateYDirection, before, i + 1, FileName); break; case "rotatezdirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateZDirection, before, i + 1, FileName); break; case "rotatexfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateXFunction, before, i + 1, FileName); break; case "rotateyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateYFunction, before, i + 1, FileName); break; case "rotatezfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateZFunction, before, i + 1, FileName); break; case "rotatexfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateXFunction, before, i + 1, FileName); break; case "rotateyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateYFunction, before, i + 1, FileName); break; case "rotatezfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateZFunction, before, i + 1, FileName); break; case "rotatexdamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateXDamping, before, i + 1, FileName); break; case "rotateydamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateYDamping, before, i + 1, FileName); break; case "rotatezdamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateZDamping, before, i + 1, FileName); break; case "textureshiftxdirection": ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftXDirection, before, i + 1, FileName); break; case "textureshiftydirection": ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftYDirection, before, i + 1, FileName); break; case "textureshiftxfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName); break; case "textureshiftyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName); break; case "textureshiftxfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName); break; case "textureshiftyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName); break; case "textureoverride": switch (after.ToLowerInvariant()) { case "none": break; case "timetable": if (!timetableUsed) { Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]); timetableUsed = true; } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "Unrecognized value in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } break; case "refreshrate": { double r; if (!double.TryParse(after, System.Globalization.NumberStyles.Float, Culture, out r)) { Debug.AddMessage(Debug.MessageType.Error, false, "Value is invalid in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Debug.AddMessage(Debug.MessageType.Error, false, "Value is expected to be non-negative in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } i++; } i--; if (StateFiles != null) { // create the object if (timetableUsed) { if (StateFunctionRpn != null) { StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?"; } else { StateFunctionRpn = "timetable"; } } if (StateFunctionRpn != null) { try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn); } catch (Exception ex) { Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName); } } Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 || Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 || Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new Vector3D(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); if (Result.Objects[ObjectCount].States[k].Object != null) { Result.Objects[ObjectCount].States[k].Object.Dynamic = true; } } else { Result.Objects[ObjectCount].States[k].Object = null; } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { Result.Objects[ObjectCount].States[j].Position = Position; } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } } Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return(Result); }
/// <summary>Attempts to load and parse the current train's panel configuration file.</summary> /// <param name="TrainPath">The absolute on-disk path to the train folder.</param> /// <param name="Encoding">The automatically detected or manually set encoding of the panel configuration file.</param> /// <param name="Train">The base train on which to apply the panel configuration.</param> internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated"); if (System.IO.File.Exists(File)) { Program.AppendToLogFile("Loading train panel: " + File); ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding, ObjectManager.ObjectLoadMode.DontAllowUnloadOfTextures); try { for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } Train.Cars[Train.DriverCar].CarSections[0].Elements = a.Objects; Train.Cars[Train.DriverCar].CameraRestrictionMode = World.CameraRestrictionMode.NotAvailable; World.CameraRestriction = World.CameraRestrictionMode.NotAvailable; World.UpdateViewingDistances(); } catch { var currentError = Interface.GetInterfaceString("error_critical_file"); currentError = currentError.Replace("[file]", "panel.animated"); MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.RestartArguments = " "; Loading.Cancel = true; } } else { var Panel2 = false; try { File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); if (System.IO.File.Exists(File)) { Program.AppendToLogFile("Loading train panel: " + File); Panel2 = true; Panel2CfgParser.ParsePanel2Config("panel2.cfg", TrainPath, Encoding, Train, Train.DriverCar); Train.Cars[Train.DriverCar].CameraRestrictionMode = World.CameraRestrictionMode.On; World.CameraRestriction = World.CameraRestrictionMode.On; } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); if (System.IO.File.Exists(File)) { Program.AppendToLogFile("Loading train panel: " + File); PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train); Train.Cars[Train.DriverCar].CameraRestrictionMode = World.CameraRestrictionMode.On; World.CameraRestriction = World.CameraRestrictionMode.On; } else { World.CameraRestriction = World.CameraRestrictionMode.NotAvailable; } } } catch { var currentError = Interface.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", Panel2 == true ? "panel2.cfg" : "panel.cfg"); MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.RestartArguments = " "; Loading.Cancel = true; } } }
private static void LoadEverythingThreaded() { string RailwayFolder = GetRailwayFolder(CurrentRouteFile); // if (RailwayFolder == null) { // Interface.AddMessage(Interface.MessageType.Critical, false, "The Railway folder could not be found. Please check your folder structure."); // return; // } string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower(); World.CameraTrackFollower.Train = null; World.CameraTrackFollower.CarIndex = -1; World.CameraMode = World.CameraViewMode.Interior; // load route bool IsRW = string.Equals(System.IO.Path.GetExtension(CurrentRouteFile), ".rw", StringComparison.OrdinalIgnoreCase); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { TrainManager.Trains[k] = new TrainManager.Train(); TrainManager.Trains[k].TrainIndex = k; if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k].State = TrainManager.TrainState.Bogus; } else { TrainManager.Trains[k].State = TrainManager.TrainState.Pending; } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string Folder = Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"); TrainDatParser.ParseTrainData(Folder, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } SoundCfgParser.LoadNoSound(TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { // real train TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; TrainDatParser.ParseTrainData(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].BeaconReceiver.Train = TrainManager.Trains[k]; } // add panel section if (k == TrainManager.PlayerTrain.TrainIndex) { TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections = new TrainManager.CarSection[1]; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay = true; TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { ObjectManager.UnifiedObject[] CarObjects; ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; CsvB3dObjectParser.ApplyScale(so, sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object int j = TrainManager.Trains[k].Cars[i].CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1); if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } } } // place cars { double z = 0.0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxlePosition; TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxlePosition; TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition; z -= TrainManager.Trains[k].Cars[i].Length; if (i < TrainManager.Trains[k].Cars.Length - 1) { z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars); } } } // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } ObjectManager.FinishCreatingObjects(); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } } }
// parse extensions config internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, ref ObjectManager.UnifiedObject[] CarObjects, ref ObjectManager.UnifiedObject[] BogieObjects, TrainManager.Train Train, bool LoadObjects) { bool[] CarObjectsReversed = new bool[Train.Cars.Length]; bool[] BogieObjectsReversed = new bool[Train.Cars.Length * 2]; bool[] CarsDefined = new bool[Train.Cars.Length]; bool[] BogiesDefined = new bool[Train.Cars.Length * 2]; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg"); if (System.IO.File.Exists(FileName)) { TextEncoding.Encoding newEncoding = TextEncoding.GetEncodingFromFile(FileName); if (newEncoding != TextEncoding.Encoding.Unknown) { switch (newEncoding) { case TextEncoding.Encoding.Utf7: Encoding = System.Text.Encoding.UTF7; break; case TextEncoding.Encoding.Utf8: Encoding = System.Text.Encoding.UTF8; break; case TextEncoding.Encoding.Utf16Le: Encoding = System.Text.Encoding.Unicode; break; case TextEncoding.Encoding.Utf16Be: Encoding = System.Text.Encoding.BigEndianUnicode; break; case TextEncoding.Encoding.Utf32Le: Encoding = System.Text.Encoding.UTF32; break; case TextEncoding.Encoding.Utf32Be: Encoding = System.Text.Encoding.GetEncoding(12001); break; case TextEncoding.Encoding.Shift_JIS: Encoding = System.Text.Encoding.GetEncoding(932); break; } } string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; default: if (Lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(4, Lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (CarsDefined[n]) { Interface.AddMessage(Interface.MessageType.Error, false, "Car " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } CarsDefined[n] = true; bool DefinedLength = false; bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (string.IsNullOrEmpty(b)) { Interface.AddMessage(Interface.MessageType.Error, true, "An empty car object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out m)) { if (m > 0.0) { Train.Cars[n].Length = m; Train.Cars[n].BeaconReceiverPosition = 0.5 * m; DefinedLength = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(Interface.MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Cars[n].RearAxle.Position = rear; Train.Cars[n].FrontAxle.Position = front; DefinedAxles = true; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": CarObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (DefinedLength & !DefinedAxles) { double AxleDistance = 0.4 * Train.Cars[n].Length; Train.Cars[n].RearAxle.Position = -AxleDistance; Train.Cars[n].FrontAxle.Position = AxleDistance; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = Lines[i].Substring(8, Lines[i].Length - 9); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Couplers.Length) { i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "distances": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double min, max; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min)) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out max)) { Interface.AddMessage(Interface.MessageType.Error, false, "Maximum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (min > max) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Couplers[n].MinimumDistanceBetweenCars = min; Train.Couplers[n].MaximumDistanceBetweenCars = max; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index " + t + " does not reference an existing coupler at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[bogie", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(6, Lines[i].Length - 7); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n > BogiesDefined.Length - 1) { continue; } if (BogiesDefined[n]) { Interface.AddMessage(Interface.MessageType.Error, false, "Bogie " + n.ToString(Culture) + " has already been declared at line " + (i + 1).ToString(Culture) + " in file " + FileName); } BogiesDefined[n] = true; //Assuming that there are two bogies per car bool IsOdd = (n % 2 != 0); int CarIndex = n / 2; if (n >= 0 & n < Train.Cars.Length * 2) { bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (Path.ContainsInvalidChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (string.IsNullOrEmpty(b)) { Interface.AddMessage(Interface.MessageType.Error, true, "An empty bogie object was supplied at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { if (LoadObjects) { BogieObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } } else { Interface.AddMessage(Interface.MessageType.Error, true, "The bogie object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { Interface.AddMessage(Interface.MessageType.Error, false, "A defined length is not supported for bogies at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(Interface.MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { if (IsOdd) { Train.Cars[CarIndex].FrontBogie.RearAxle.Position = rear; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = front; } else { Train.Cars[CarIndex].RearBogie.RearAxle.Position = rear; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = front; } DefinedAxles = true; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": BogieObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (!DefinedAxles) { if (IsOdd) { double AxleDistance = 0.4 * Train.Cars[CarIndex].FrontBogie.Length; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -AxleDistance; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = AxleDistance; } else { double AxleDistance = 0.4 * Train.Cars[CarIndex].RearBogie.Length; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -AxleDistance; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = AxleDistance; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { // default Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // check for car objects and reverse if necessary int carObjects = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] != null) { carObjects++; if (CarObjectsReversed[i] && LoadObjects) { { // reverse axle positions double temp = Train.Cars[i].FrontAxle.Position; Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position; Train.Cars[i].RearAxle.Position = -temp; } if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } //Check for bogie objects and reverse if necessary..... int bogieObjects = 0; for (int i = 0; i < Train.Cars.Length * 2; i++) { bool IsOdd = (i % 2 != 0); int CarIndex = i / 2; if (BogieObjects[i] != null) { bogieObjects++; if (BogieObjectsReversed[i] && LoadObjects) { { // reverse axle positions if (IsOdd) { double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position; Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position; Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -temp; } else { double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position; Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position; Train.Cars[CarIndex].RearBogie.RearAxle.Position = -temp; } } if (BogieObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)BogieObjects[i]; obj.ApplyScale(-1.0, 1.0, -1.0); } else if (BogieObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)BogieObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { obj.Objects[j].States[h].Object.ApplyScale(-1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjects > 0 & carObjects < Train.Cars.Length) { Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); } if (bogieObjects > 0 & bogieObjects < Train.Cars.Length * 2) { Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + FileName); } } }
// parse extensions config internal static void ParseExtensionsConfig(string TrainPath, System.Text.Encoding Encoding, out ObjectManager.UnifiedObject[] CarObjects, TrainManager.Train Train) { CarObjects = new ObjectManager.UnifiedObject[Train.Cars.Length]; bool[] CarObjectsReversed = new bool[Train.Cars.Length]; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "extensions.cfg"); if (System.IO.File.Exists(FileName)) { // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[exterior]": // exterior i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); int n; if (int.TryParse(a, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { if (Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + a + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; break; default: if (Lines[i].StartsWith("[car", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // car string t = Lines[i].Substring(4, Lines[i].Length - 5); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Cars.Length) { bool DefinedLength = false; bool DefinedAxles = false; i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "object": if (Interface.ContainsInvalidPathChars(b)) { Interface.AddMessage(Interface.MessageType.Error, false, "File contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string File = OpenBveApi.Path.CombineFile(TrainPath, b); if (System.IO.File.Exists(File)) { CarObjects[n] = ObjectManager.LoadObject(File, Encoding, ObjectManager.ObjectLoadMode.Normal, false, false, false); } else { Interface.AddMessage(Interface.MessageType.Error, true, "The car object " + File + " does not exist at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "length": { double m; if (double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out m)) { if (m > 0.0) { Train.Cars[n].Length = m; Train.Cars[n].BeaconReceiverPosition = 0.5 * m; DefinedLength = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be a positive floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "axles": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double rear, front; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out rear)) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out front)) { Interface.AddMessage(Interface.MessageType.Error, false, "Front is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (rear >= front) { Interface.AddMessage(Interface.MessageType.Error, false, "Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Cars[n].RearAxlePosition = rear; Train.Cars[n].FrontAxlePosition = front; DefinedAxles = true; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "reversed": CarObjectsReversed[n] = b.Equals("true", StringComparison.OrdinalIgnoreCase); break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; if (DefinedLength & !DefinedAxles) { double AxleDistance = 0.4 * Train.Cars[n].Length; Train.Cars[n].RearAxlePosition = -AxleDistance; Train.Cars[n].FrontAxlePosition = AxleDistance; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index " + t + " does not reference an existing car at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The car index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else if (Lines[i].StartsWith("[coupler", StringComparison.OrdinalIgnoreCase) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { // coupler string t = Lines[i].Substring(8, Lines[i].Length - 9); int n; if (int.TryParse(t, System.Globalization.NumberStyles.Integer, Culture, out n)) { if (n >= 0 & n < Train.Couplers.Length) { i++; while (i < Lines.Length && !Lines[i].StartsWith("[", StringComparison.Ordinal) & !Lines[i].EndsWith("]", StringComparison.Ordinal)) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j >= 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "distances": { int k = b.IndexOf(','); if (k >= 0) { string c = b.Substring(0, k).TrimEnd(); string d = b.Substring(k + 1).TrimStart(); double min, max; if (!double.TryParse(c, System.Globalization.NumberStyles.Float, Culture, out min)) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(d, System.Globalization.NumberStyles.Float, Culture, out max)) { Interface.AddMessage(Interface.MessageType.Error, false, "Maximum is expected to be a floating-point number in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (min > max) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum is expected to be less than Maximum in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Train.Couplers[n].MinimumDistanceBetweenCars = min; Train.Couplers[n].MaximumDistanceBetweenCars = max; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "An argument-separating comma is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Warning, false, "Unsupported key-value pair " + a + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } i++; } i--; } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index " + t + " does not reference an existing coupler at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "The coupler index is expected to be an integer at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } else { // default Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; } } } // check for car objects and reverse if necessary int carObjects = 0; for (int i = 0; i < Train.Cars.Length; i++) { if (CarObjects[i] != null) { carObjects++; if (CarObjectsReversed[i]) { { // reverse axle positions double temp = Train.Cars[i].FrontAxlePosition; Train.Cars[i].FrontAxlePosition = -Train.Cars[i].RearAxlePosition; Train.Cars[i].RearAxlePosition = -temp; } if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject obj = (ObjectManager.StaticObject)CarObjects[i]; CsvB3dObjectParser.ApplyScale(obj, -1.0, 1.0, -1.0); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection obj = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; for (int j = 0; j < obj.Objects.Length; j++) { for (int h = 0; h < obj.Objects[j].States.Length; h++) { CsvB3dObjectParser.ApplyScale(obj.Objects[j].States[h].Object, -1.0, 1.0, -1.0); obj.Objects[j].States[h].Position.X *= -1.0; obj.Objects[j].States[h].Position.Z *= -1.0; } obj.Objects[j].TranslateXDirection.X *= -1.0; obj.Objects[j].TranslateXDirection.Z *= -1.0; obj.Objects[j].TranslateYDirection.X *= -1.0; obj.Objects[j].TranslateYDirection.Z *= -1.0; obj.Objects[j].TranslateZDirection.X *= -1.0; obj.Objects[j].TranslateZDirection.Z *= -1.0; } } else { throw new NotImplementedException(); } } } } if (carObjects > 0 & carObjects < Train.Cars.Length) { Interface.AddMessage(Interface.MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); } } }
// parse animated object config /// <summary>Loads a collection of animated objects from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection { Objects = new ObjectManager.AnimatedObject[4] }; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); //Trim out comments Lines[i] = j >= 0 ? Lines[i].Substring(0, j).Trim() : Lines[i].Trim(); //Test whether RPN functions have been used rpnUsed = Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0; } if (rpnUsed) { Interface.AddMessage(Interface.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; Vector3 position = new Vector3(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { position = new Vector3(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { string Folder = System.IO.Path.GetDirectoryName(FileName); if (Interface.ContainsInvalidPathChars(Lines[i])) { Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize<ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; if (ObjectCount >= Result.Objects.Length) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState { Object = s, Position = position }; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject { States = new ObjectManager.AnimatedObjectState[] {}, CurrentState = -1, TranslateXDirection = new Vector3(1.0, 0.0, 0.0), TranslateYDirection = new Vector3(0.0, 1.0, 0.0), TranslateZDirection = new Vector3(0.0, 0.0, 1.0), RotateXDirection = new Vector3(1.0, 0.0, 0.0), RotateYDirection = new Vector3(0.0, 1.0, 0.0), RotateZDirection = new Vector3(0.0, 0.0, 1.0), TextureShiftXDirection = new Vector2(1.0, 0.0), TextureShiftYDirection = new Vector2(0.0, 1.0), RefreshRate = 0.0, ObjectIndex = -1 }; Vector3 Position = new Vector3(0.0, 0.0, 0.0); double RotateX = 0; bool StaticXRotation = false; double RotateY = 0; bool StaticYRotation = false; double RotateZ = 0; bool StaticZRotation = false; bool timetableUsed = false; string[] StateFiles = null; string StateFunctionRpn = null; int StateFunctionLine = -1; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Position = new Vector3(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "states": { string[] s = b.Split(','); if (s.Length >= 1) { string Folder = System.IO.Path.GetDirectoryName(FileName); StateFiles = new string[s.Length]; bool NullObject = true; for (int k = 0; k < s.Length; k++) { s[k] = s[k].Trim(); if (s[k].Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " is an empty string - did you mean something else? - in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else if (Interface.ContainsInvalidPathChars(s[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else { StateFiles[k] = OpenBveApi.Path.CombineFile(Folder, s[k]); if (!System.IO.File.Exists(StateFiles[k])) { Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } } if (StateFiles[k] != null) { NullObject = false; } } if (NullObject == true) { Interface.AddMessage(Interface.MessageType.Error, false, "None of the specified files were found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } break; case "statefunction": try { StateFunctionLine = i; StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": { StateFunctionLine = i; StateFunctionRpn = b; } break; case "translatexdirection": case "translateydirection": case "translatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "translatexdirection": Result.Objects[ObjectCount].TranslateXDirection = new Vector3(x, y, z); break; case "translateydirection": Result.Objects[ObjectCount].TranslateYDirection = new Vector3(x, y, z); break; case "translatezdirection": Result.Objects[ObjectCount].TranslateZDirection = new Vector3(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "translatexfunction": try { double X; if (double.TryParse(b, NumberStyles.Float, Culture, out X)) { Position.X = X; //A function script must be evaluated every frame, no matter if it is a constant value //If we add this to the position instead, this gives a minor speedup break; } Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatexscript": try { Result.Objects[ObjectCount].TranslateXScriptFile = OpenBveApi.Path.CombineDirectory(System.IO.Path.GetDirectoryName(FileName), b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunction": try { double Y; if (double.TryParse(b, NumberStyles.Float, Culture, out Y)) { Position.Y = Y; //A function script must be evaluated every frame, no matter if it is a constant value //If we add this to the position instead, this gives a minor speedup break; } Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyscript": try { Result.Objects[ObjectCount].TranslateYScriptFile = OpenBveApi.Path.CombineDirectory(System.IO.Path.GetDirectoryName(FileName), b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunction": try { double Z; if (double.TryParse(b, NumberStyles.Float, Culture, out Z)) { Position.Z = Z; //A function script must be evaluated every frame, no matter if it is a constant value //If we add this to the position instead, this gives a minor speedup break; } Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezscript": try { Result.Objects[ObjectCount].TranslateZScriptFile = OpenBveApi.Path.CombineDirectory(System.IO.Path.GetDirectoryName(FileName), b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "trackfollowerfunction": try { Result.Objects[ObjectCount].TrackFollowerFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "axles": try { double FrontAxlePosition; double RearAxlePosition; var splitValue = b.Split(','); Double.TryParse(splitValue[0], out FrontAxlePosition); Double.TryParse(splitValue[1], out RearAxlePosition); if (FrontAxlePosition > RearAxlePosition) { Result.Objects[ObjectCount].FrontAxlePosition = FrontAxlePosition; Result.Objects[ObjectCount].RearAxlePosition = RearAxlePosition; } else if (FrontAxlePosition < RearAxlePosition) { Interface.AddMessage(Interface.MessageType.Error, false,"Rear is expected to be less than Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " +FileName); } else { Interface.AddMessage(Interface.MessageType.Error, false, "Rear must not equal Front in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } catch(Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; /* * RPN Functions were added by Michelle, and she stated that they should not be used other than in debugging * Not aware of any uses, but these should stay there anyway * */ case "translatexfunctionrpn": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunctionrpn": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunctionrpn": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdirection": case "rotateydirection": case "rotatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (x == 0.0 & y == 0.0 & z == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdirection": Result.Objects[ObjectCount].RotateXDirection = new Vector3(x, y, z); break; case "rotateydirection": Result.Objects[ObjectCount].RotateYDirection = new Vector3(x, y, z); break; case "rotatezdirection": Result.Objects[ObjectCount].RotateZDirection = new Vector3(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "rotatexfunction": try { if (double.TryParse(b, NumberStyles.Float, Culture, out RotateX)) { //A function script must be evaluated every frame, no matter if it is a constant value //If we add this to the position instead, this gives a minor speedup StaticXRotation = true; } Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunction": try { if (double.TryParse(b, NumberStyles.Float, Culture, out RotateY)) { StaticYRotation = true; } Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunction": try { if (double.TryParse(b, NumberStyles.Float, Culture, out RotateZ)) { StaticZRotation = true; } Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexfunctionrpn": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunctionrpn": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunctionrpn": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdamping": case "rotateydamping": case "rotatezdamping": { string[] s = b.Split(','); if (s.Length == 2) { double nf, dr; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf)) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr)) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (nf <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (dr <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdamping": Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr); break; case "rotateydamping": Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr); break; case "rotatezdamping": Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxdirection": case "textureshiftydirection": { string[] s = b.Split(','); if (s.Length == 2) { double x, y; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "textureshiftxdirection": Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2(x, y); break; case "textureshiftydirection": Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2(x, y); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxfunction": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunction": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftxfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureoverride": switch (b.ToLowerInvariant()) { case "none": break; case "timetable": if (!timetableUsed) { Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]); timetableUsed = true; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognized value in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } break; case "refreshrate": { double r; if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } i++; } i--; if (StateFiles != null) { // create the object if (timetableUsed) { if (StateFunctionRpn != null) { StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?"; } else { StateFunctionRpn = "timetable"; } } if (StateFunctionRpn != null) { try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName); } } Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new Vector3(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); if (Result.Objects[ObjectCount].States[k].Object != null) { Result.Objects[ObjectCount].States[k].Object.Dynamic = true; } } else { Result.Objects[ObjectCount].States[k].Object = null; } } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { //Rotate X if (Result.Objects[ObjectCount].States[j].Object == null) { continue; } for (int l = 0; l < Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices.Length; l++) { //Apply position Result.Objects[ObjectCount].States[j].Position = Position; //Test whether the object contains non static rotation functions //If so, the results may be off so don't optimise if (!StaticXRotation) { if (Result.Objects[ObjectCount].RotateXFunction != null) { break; } } if (!StaticYRotation) { if (Result.Objects[ObjectCount].RotateYFunction != null) { break; } } if (!StaticZRotation) { if (Result.Objects[ObjectCount].RotateZFunction != null) { break; } } if (StaticXRotation) { double x = Result.Objects[ObjectCount].RotateXDirection.X; double y = Result.Objects[ObjectCount].RotateXDirection.Y; double z = Result.Objects[ObjectCount].RotateXDirection.Z; double t = x * x + y * y + z * z; if (t == 0.0) { x = 1.0; y = 0.0; z = 0.0; t = 1.0; } if (RotateX != 0.0) { t = 1.0 / Math.Sqrt(t); x *= t; y *= t; z *= t; } double cosX = Math.Cos(RotateX); double sinX = Math.Sin(RotateX); World.Rotate(ref Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices[l].Coordinates, x, y, z, cosX, sinX); Result.Objects[ObjectCount].RotateXFunction = null; } if (StaticYRotation) { double x = Result.Objects[ObjectCount].RotateYDirection.X; double y = Result.Objects[ObjectCount].RotateYDirection.Y; double z = Result.Objects[ObjectCount].RotateYDirection.Z; double t = x * x + y * y + z * z; if (t == 0.0) { x = 1.0; y = 0.0; z = 0.0; t = 1.0; } if (RotateY != 0.0) { t = 1.0 / Math.Sqrt(t); x *= t; y *= t; z *= t; } double cosY = Math.Cos(RotateY); double sinY = Math.Sin(RotateY); World.Rotate(ref Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices[l].Coordinates, x, y, z, cosY, sinY); Result.Objects[ObjectCount].RotateYFunction = null; } if (StaticZRotation) { double x = Result.Objects[ObjectCount].RotateZDirection.X; double y = Result.Objects[ObjectCount].RotateZDirection.Y; double z = Result.Objects[ObjectCount].RotateZDirection.Z; double t = x * x + y * y + z * z; if (t == 0.0) { x = 1.0; y = 0.0; z = 0.0; t = 1.0; } if (RotateZ != 0.0) { t = 1.0 / Math.Sqrt(t); x *= t; y *= t; z *= t; } double cosZ = Math.Cos(RotateZ); double sinZ = Math.Sin(RotateZ); World.Rotate(ref Result.Objects[ObjectCount].States[j].Object.Mesh.Vertices[l].Coordinates, x, y, z, cosZ, sinZ); Result.Objects[ObjectCount].RotateZFunction = null; } } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return null; } } } Array.Resize<ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return Result; }
// get mirrored object private static ObjectManager.UnifiedObject GetMirroredObject(ObjectManager.UnifiedObject Prototype) { if (Prototype is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)Prototype; return GetMirroredStaticObject(s); } else if (Prototype is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)Prototype; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int i = 0; i < a.Objects.Length; i++) { Result.Objects[i] = a.Objects[i].Clone(); for (int j = 0; j < a.Objects[i].States.Length; j++) { Result.Objects[i].States[j].Object = GetMirroredStaticObject(a.Objects[i].States[j].Object); } Result.Objects[i].TranslateXDirection.X *= -1.0; Result.Objects[i].TranslateYDirection.X *= -1.0; Result.Objects[i].TranslateZDirection.X *= -1.0; Result.Objects[i].RotateXDirection.X *= -1.0; Result.Objects[i].RotateYDirection.X *= -1.0; Result.Objects[i].RotateZDirection.X *= -1.0; } return Result; } else { return null; } }
private static void LoadEverythingThreaded() { Program.AppendToLogFile("Loading route file: " + CurrentRouteFile); string RailwayFolder = GetRailwayFolder(CurrentRouteFile); string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound"); // reset Game.Reset(true); Game.MinimalisticSimulation = true; // screen World.CameraTrackFollower = new TrackManager.TrackFollower { Train = null, CarIndex = -1 }; World.CameraMode = World.CameraViewMode.Interior; //First, check the format of the route file //RW routes were written for BVE1 / 2, and have a different command syntax bool IsRW = CsvRwRouteParser.isRWFile(CurrentRouteFile); Program.AppendToLogFile("Route file format is: " + (IsRW ? "RW" : "CSV")); CsvRwRouteParser.ParseRoute(CurrentRouteFile, IsRW, CurrentRouteEncoding, CurrentTrainFolder, ObjectFolder, SoundFolder, false); Thread createIllustrations = new Thread(Game.RouteInformation.LoadInformation) { IsBackground = true }; createIllustrations.Start(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } Game.CalculateSeaLevelConstants(); if (Game.BogusPretrainInstructions.Length != 0) { double t = Game.BogusPretrainInstructions[0].Time; double p = Game.BogusPretrainInstructions[0].TrackPosition; for (int i = 1; i < Game.BogusPretrainInstructions.Length; i++) { if (Game.BogusPretrainInstructions[i].Time > t) { t = Game.BogusPretrainInstructions[i].Time; } else { t += 1.0; Game.BogusPretrainInstructions[i].Time = t; } if (Game.BogusPretrainInstructions[i].TrackPosition > p) { p = Game.BogusPretrainInstructions[i].TrackPosition; } else { p += 1.0; Game.BogusPretrainInstructions[i].TrackPosition = p; } } } if (Game.Stations.Length == 1) { //Log the fact that only a single station is present, as this is probably not right Program.AppendToLogFile("The processed route file only contains a single station."); } Program.AppendToLogFile("Route file loaded successfully."); RouteProgress = 1.0; // initialize trains System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains = new TrainManager.Train[Game.PrecedingTrainTimeDeltas.Length + 1 + (Game.BogusPretrainInstructions.Length != 0 ? 1 : 0)]; for (int k = 0; k < TrainManager.Trains.Length; k++) { TrainManager.Trains[k] = new TrainManager.Train { TrainIndex = k }; if (k == TrainManager.Trains.Length - 1 & Game.BogusPretrainInstructions.Length != 0) { TrainManager.Trains[k].State = TrainManager.TrainState.Bogus; } else { TrainManager.Trains[k].State = TrainManager.TrainState.Pending; } } TrainManager.PlayerTrain = TrainManager.Trains[Game.PrecedingTrainTimeDeltas.Length]; // load trains double TrainProgressMaximum = 0.7 + 0.3 * (double)TrainManager.Trains.Length; for (int k = 0; k < TrainManager.Trains.Length; k++) { //Sleep for 10ms to allow route loading locks to release Thread.Sleep(20); if (TrainManager.Trains[k].State == TrainManager.TrainState.Bogus) { // bogus train string TrainData = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility", "PreTrain"), "train.dat"); TrainDatParser.ParseTrainData(TrainData, System.Text.Encoding.UTF8, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainManager.Trains[k].InitializeCarSounds(); System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.3 / TrainProgressMaximum; TrainProgressCurrentSum += TrainProgressCurrentWeight; } else { // real train Program.AppendToLogFile("Loading player train: " + CurrentTrainFolder); TrainProgressCurrentWeight = 0.1 / TrainProgressMaximum; string TrainData = OpenBveApi.Path.CombineFile(CurrentTrainFolder, "train.dat"); TrainDatParser.ParseTrainData(TrainData, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } TrainProgressCurrentWeight = 0.2 / TrainProgressMaximum; SoundCfgParser.ParseSoundConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } // door open/close speed for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorOpenL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorOpenR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency = 0.8; } } if (TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency <= 0.0) { if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null & TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a + b > 0.0 ? 2.0 / (a + b) : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double a = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = a > 0.0 ? 1.0 / a : 0.8; } else if (TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer != null) { Sounds.LoadBuffer(TrainManager.Trains[k].Cars[i].Sounds.DoorCloseL.Buffer); double b = TrainManager.Trains[k].Cars[i].Sounds.DoorCloseR.Buffer.Duration; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = b > 0.0 ? 1.0 / b : 0.8; } else { TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency = 0.8; } } const double f = 0.015; const double g = 2.75; TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = Math.Exp(f * Math.Tan(g * (Program.RandomNumberGenerator.NextDouble() - 0.5))); TrainManager.Trains[k].Cars[i].Specs.DoorOpenFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch; TrainManager.Trains[k].Cars[i].Specs.DoorCloseFrequency /= TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch; /* * Remove the following two lines, then the pitch at which doors play * takes their randomized opening and closing times into account. * */ TrainManager.Trains[k].Cars[i].Specs.DoorOpenPitch = 1.0; TrainManager.Trains[k].Cars[i].Specs.DoorClosePitch = 1.0; } } for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { TrainManager.Trains[k].Cars[i].FrontAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].RearAxle.Follower.Train = TrainManager.Trains[k]; TrainManager.Trains[k].Cars[i].BeaconReceiver.Train = TrainManager.Trains[k]; } // add panel section if (k == TrainManager.PlayerTrain.TrainIndex) { TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections = new TrainManager.CarSection[1]; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Elements = new ObjectManager.AnimatedObject[] { }; TrainManager.Trains[k].Cars[TrainManager.Trains[k].DriverCar].CarSections[0].Overlay = true; TrainProgressCurrentWeight = 0.7 / TrainProgressMaximum; TrainManager.ParsePanelConfig(CurrentTrainFolder, CurrentTrainEncoding, TrainManager.Trains[k]); TrainProgressCurrentSum += TrainProgressCurrentWeight; System.Threading.Thread.Sleep(1); if (Cancel) { return; } Program.AppendToLogFile("Train panel loaded sucessfully."); } // add exterior section if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { ObjectManager.UnifiedObject[] CarObjects; ObjectManager.UnifiedObject[] BogieObjects; ExtensionsCfgParser.ParseExtensionsConfig(CurrentTrainFolder, CurrentTrainEncoding, out CarObjects, out BogieObjects, TrainManager.Trains[k]); System.Threading.Thread.Sleep(1); if (Cancel) { return; } //Stores the current array index of the bogie object to add //Required as there are two bogies per car, and we're using a simple linear array.... int currentBogieObject = 0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { if (CarObjects[i] == null) { // load default exterior object string file = OpenBveApi.Path.CombineFile(Program.FileSystem.GetDataFolder("Compatibility"), "exterior.csv"); ObjectManager.StaticObject so = ObjectManager.LoadStaticObject(file, System.Text.Encoding.UTF8, ObjectManager.ObjectLoadMode.Normal, false, false, false); if (so == null) { CarObjects[i] = null; } else { double sx = TrainManager.Trains[k].Cars[i].Width; double sy = TrainManager.Trains[k].Cars[i].Height; double sz = TrainManager.Trains[k].Cars[i].Length; CsvB3dObjectParser.ApplyScale(so, sx, sy, sz); CarObjects[i] = so; } } if (CarObjects[i] != null) { // add object int j = TrainManager.Trains[k].Cars[i].CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].CarSections, j + 1); if (CarObjects[i] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (CarObjects[i] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)CarObjects[i]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } //Load bogie objects if (BogieObjects[currentBogieObject] != null) { int j = TrainManager.Trains[k].Cars[i].FrontBogie.CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].FrontBogie.CarSections, j + 1); if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].FrontBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } currentBogieObject++; //Can't think of a better way to do this than two functions...... if (BogieObjects[currentBogieObject] != null) { int j = TrainManager.Trains[k].Cars[i].RearBogie.CarSections.Length; Array.Resize <TrainManager.CarSection>(ref TrainManager.Trains[k].Cars[i].RearBogie.CarSections, j + 1); if (BogieObjects[currentBogieObject] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[1]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0] = new ObjectManager.AnimatedObject(); TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States = new ObjectManager.AnimatedObjectState[1]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Position = new Vector3(0.0, 0.0, 0.0); TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].States[0].Object = s; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].CurrentState = 0; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[0].ObjectIndex = ObjectManager.CreateDynamicObject(); } else if (BogieObjects[currentBogieObject] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)BogieObjects[currentBogieObject]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements = new ObjectManager.AnimatedObject[a.Objects.Length]; for (int h = 0; h < a.Objects.Length; h++) { TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h] = a.Objects[h]; TrainManager.Trains[k].Cars[i].RearBogie.CarSections[j].Elements[h].ObjectIndex = ObjectManager.CreateDynamicObject(); } } } currentBogieObject++; } } // place cars { double z = 0.0; for (int i = 0; i < TrainManager.Trains[k].Cars.Length; i++) { //Front axle track position TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].FrontAxle.Position; //Bogie for front axle TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.FrontAxle.Position; TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].FrontAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].FrontBogie.Length + TrainManager.Trains[k].Cars[i].FrontBogie.RearAxle.Position; //Rear axle track position TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].RearAxle.Position; //Bogie for rear axle TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.FrontAxle.Position; TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Follower.TrackPosition = TrainManager.Trains[k].Cars[i].RearAxle.Follower.TrackPosition - 0.5 * TrainManager.Trains[k].Cars[i].RearBogie.Length + TrainManager.Trains[k].Cars[i].RearBogie.RearAxle.Position; //Beacon reciever (AWS, ATC etc.) TrainManager.Trains[k].Cars[i].BeaconReceiver.TrackPosition = z - 0.5 * TrainManager.Trains[k].Cars[i].Length + TrainManager.Trains[k].Cars[i].BeaconReceiverPosition; z -= TrainManager.Trains[k].Cars[i].Length; if (i < TrainManager.Trains[k].Cars.Length - 1) { z -= 0.5 * (TrainManager.Trains[k].Couplers[i].MinimumDistanceBetweenCars + TrainManager.Trains[k].Couplers[i].MaximumDistanceBetweenCars); } } } // configure ai / timetable if (TrainManager.Trains[k] == TrainManager.PlayerTrain) { TrainManager.Trains[k].TimetableDelta = 0.0; } else if (TrainManager.Trains[k].State != TrainManager.TrainState.Bogus) { TrainManager.Trains[k].AI = new Game.SimpleHumanDriverAI(TrainManager.Trains[k]); TrainManager.Trains[k].TimetableDelta = Game.PrecedingTrainTimeDeltas[k]; TrainManager.Trains[k].Specs.DoorOpenMode = TrainManager.DoorMode.Manual; TrainManager.Trains[k].Specs.DoorCloseMode = TrainManager.DoorMode.Manual; } } TrainProgress = 1.0; // finished created objects System.Threading.Thread.Sleep(1); if (Cancel) { return; } ObjectManager.FinishCreatingObjects(); // update sections if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // load plugin for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i].State != TrainManager.TrainState.Bogus) { if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { if (!PluginManager.LoadCustomPlugin(TrainManager.Trains[i], CurrentTrainFolder, CurrentTrainEncoding)) { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } else { PluginManager.LoadDefaultPlugin(TrainManager.Trains[i], CurrentTrainFolder); } } } }
/// <summary>Attempts to load and parse the current train's panel configuration file.</summary> /// <param name="TrainPath">The absolute on-disk path to the train folder.</param> /// <param name="Encoding">The automatically detected or manually set encoding of the panel configuration file.</param> /// <param name="Train">The base train on which to apply the panel configuration.</param> internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, Train Train) { Train.Cars[Train.DriverCar].CarSections = new CarSection[1]; Train.Cars[Train.DriverCar].CarSections[0] = new CarSection { Elements = new ObjectManager.AnimatedObject[] { }, Overlay = true }; string File = OpenBveApi.Path.CombineFile(TrainPath, "panel.animated"); if (System.IO.File.Exists(File)) { Program.FileSystem.AppendToLogFile("Loading train panel: " + File); if (System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg")) || System.IO.File.Exists(OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"))) { Program.FileSystem.AppendToLogFile("INFO: This train contains both a 2D and a 3D panel. The 3D panel will always take precedence"); } ObjectManager.AnimatedObjectCollection a = AnimatedObjectParser.ReadObject(File, Encoding); if (a != null) { //HACK: If a == null , loading our animated object completely failed (Missing objects?). Fallback to trying the panel2.cfg try { for (int i = 0; i < a.Objects.Length; i++) { a.Objects[i].ObjectIndex = ObjectManager.CreateDynamicObject(); } Train.Cars[Train.DriverCar].CarSections[0].Elements = a.Objects; Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.NotAvailable; World.CameraRestriction = Camera.RestrictionMode.NotAvailable; World.UpdateViewingDistances(); return; } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", "panel.animated"); MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.RestartArguments = " "; Loading.Cancel = true; return; } } Interface.AddMessage(MessageType.Error, false, "The panel.animated file " + File + " failed to load. Falling back to 2D panel."); } var Panel2 = false; try { File = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); if (System.IO.File.Exists(File)) { Program.FileSystem.AppendToLogFile("Loading train panel: " + File); Panel2 = true; Panel2CfgParser.ParsePanel2Config("panel2.cfg", TrainPath, Encoding, Train, Train.DriverCar); Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On; World.CameraRestriction = Camera.RestrictionMode.On; } else { File = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); if (System.IO.File.Exists(File)) { Program.FileSystem.AppendToLogFile("Loading train panel: " + File); PanelCfgParser.ParsePanelConfig(TrainPath, Encoding, Train); Train.Cars[Train.DriverCar].CameraRestrictionMode = Camera.RestrictionMode.On; World.CameraRestriction = Camera.RestrictionMode.On; } else { World.CameraRestriction = Camera.RestrictionMode.NotAvailable; } } } catch { var currentError = Translations.GetInterfaceString("errors_critical_file"); currentError = currentError.Replace("[file]", Panel2 == true ? "panel2.cfg" : "panel.cfg"); MessageBox.Show(currentError, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.RestartArguments = " "; Loading.Cancel = true; } }