/// <summary>Creates a new static background, using the default 0.8s fade-in time</summary> /// <param name="Texture">The texture to apply</param> /// <param name="Repetition">The number of times the texture should be repeated around the viewing frustrum</param> /// <param name="KeepAspectRatio">Whether the aspect ratio of the texture should be preseved</param> internal StaticBackground(Textures.Texture Texture, double Repetition, bool KeepAspectRatio) { this.Texture = Texture; this.Repetition = Repetition; this.KeepAspectRatio = KeepAspectRatio; this.TransitionTime = 0.8; this.Time = -1; }
/// <summary>Creates a new static background</summary> /// <param name="Texture">The texture to apply</param> /// <param name="Repetition">The number of times the texture should be repeated around the viewing frustrum</param> /// <param name="KeepAspectRatio">Whether the aspect ratio of the texture should be preseved</param> /// <param name="transitionTime">The time taken in seconds for the fade-in transition to occur</param> /// <param name="Mode">The transition mode</param> internal StaticBackground(Textures.Texture Texture, double Repetition, bool KeepAspectRatio, double transitionTime, BackgroundTransitionMode Mode) { this.Texture = Texture; this.Repetition = Repetition; this.KeepAspectRatio = KeepAspectRatio; this.TransitionTime = transitionTime; this.Time = -1; this.Mode = Mode; }
// parse panel config internal static void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { // read lines System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "panel.cfg"); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); } } // initialize double FullWidth = 480, FullHeight = 440, SemiHeight = 240; double AspectRatio = FullWidth / FullHeight; double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance; WorldWidth = WorldHeight * AspectRatio; } World.CameraRestrictionBottomLeft = new Vector3(-0.5 * WorldWidth, -0.5 * WorldHeight, EyeDistance); World.CameraRestrictionTopRight = new Vector3(0.5 * WorldWidth, 0.5 * WorldHeight, EyeDistance); double WorldLeft = Train.Cars[Train.DriverCar].DriverX - 0.5 * WorldWidth; double WorldTop = Train.Cars[Train.DriverCar].DriverY + 0.5 * WorldHeight; double WorldZ = Train.Cars[Train.DriverCar].DriverZ; const double UpDownAngleConstant = -0.191986217719376; double PanelYaw = 0.0; double PanelPitch = UpDownAngleConstant; string PanelBackground = OpenBveApi.Path.CombineFile(TrainPath, "panel.bmp"); // parse lines for panel and view for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // panel case "panel": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "background": if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; PanelBackground = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelBackground)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelBackground + "could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; } } i++; } i--; break; // view case "view": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "yaw": { double yaw = 0.0; if (Value.Length > 0 && !Interface.TryParseDoubleVb6(Value, out yaw)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); yaw = 0.0; } PanelYaw = Math.Atan(yaw); } break; case "pitch": { double pitch = 0.0; if (Value.Length > 0 && !Interface.TryParseDoubleVb6(Value, out pitch)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); pitch = 0.0; } PanelPitch = Math.Atan(pitch) + UpDownAngleConstant; } break; } } i++; } i--; break; } } } } Train.Cars[Train.DriverCar].DriverYaw = PanelYaw; Train.Cars[Train.DriverCar].DriverPitch = PanelPitch; // panel { if (!System.IO.File.Exists(PanelBackground)) { Interface.AddMessage(Interface.MessageType.Error, true, "The panel image could not be found in " + FileName); } else { Textures.Texture t; Textures.RegisterTexture(PanelBackground, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; SemiHeight = FullHeight - h; CreateElement(Train, 0, SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } } // parse lines for rest double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Lines[i].Length != 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // pressuregauge case "pressuregauge": case "pressuremeter": case "pressureindicator": case "圧力計": { int Type = 0; Color32[] NeedleColor = new Color32[] { new Color32(0, 0, 0, 255), new Color32(0, 0, 0, 255) }; int[] NeedleType = new int[] { 0, 0 }; double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null, Cover = null; double Angle = 0.785398163397449, Minimum = 0.0, Maximum = 1000.0; double UnitFactor = 1000.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "type": case "形態": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Type is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } else if (Type != 0 & Type != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Type must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } break; case "lowerneedle": case "lowerhand": case "下針": case "upperneedle": case "upperhand": case "上針": int k = Key.ToLowerInvariant() == "lowerneedle" | Key.ToLowerInvariant() == "lowerhand" | Key == "下針" ? 0 : 1; if (Arguments.Length >= 1 && Arguments[0].Length > 0) { switch (Arguments[0].ToLowerInvariant()) { case "bc": case "ブレーキシリンダ": NeedleType[k] = 1; break; case "sap": case "直通管": NeedleType[k] = 2; break; case "bp": case "ブレーキ管": case "制動管": NeedleType[k] = 3; break; case "er": case "釣り合い空気溜め": case "釣り合い空気ダメ": case "つりあい空気溜め": case "ツリアイ空気ダメ": NeedleType[k] = 4; break; case "mr": case "元空気溜め": case "元空気ダメ": NeedleType[k] = 5; break; default: { int a; if (!Interface.TryParseIntVb6(Arguments[0], out a)) { Interface.AddMessage(Interface.MessageType.Error, false, "Subject is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); a = 0; } NeedleType[k] = a; } break; } } int r = 0, g = 0, b = 0; if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + Key + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 4 && Arguments[3].Length > 0 && !Interface.TryParseIntVb6(Arguments[3], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + Key + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } NeedleColor[k] = new Color32((byte)r, (byte)g, (byte)b, 255); break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 1.0; } break; case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "cover": case "ふた": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Cover = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Cover)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Cover + "could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Cover = null; } } break; case "unit": case "単位": if (Arguments.Length >= 1 && Arguments[0].Length > 0) { string a = Arguments[0].ToLowerInvariant(); int Unit = 0; if (a == "kpa") { Unit = 0; } else if (a == "kgf/cm2" | a == "kgf/cm^2" | a == "kg/cm2" | a == "kg/cm^2") { Unit = 1; } else if (!Interface.TryParseIntVb6(Arguments[0], out Unit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } else if (Unit != 0 & Unit != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } if (Unit == 1) { UnitFactor = 98066.5; } else { UnitFactor = 1000.0; } } break; case "maximum": case "最大": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "PressureValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Maximum = 1000.0; } break; case "minimum": case "最小": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "PressureValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Minimum = 0.0; } break; case "angle": case "角度": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Angle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Angle = 0.785398163397449; } else { Angle *= 0.0174532925199433; } break; } } i++; } i--; // units Minimum *= UnitFactor; Maximum *= UnitFactor; // background if (Background != null) { Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } // cover if (Cover != null) { Textures.Texture t; Textures.RegisterTexture(Cover, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Type == 0) { // needles for (int k = 0; k < 2; k++) { if (NeedleType[k] != 0) { string Folder = Program.FileSystem.GetDataFolder("Compatibility"); string File = OpenBveApi.Path.CombineFile(Folder, k == 0 ? "needle_pressuregauge_lower.png" : "needle_pressuregauge_upper.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - (double)(4 + k) * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, NeedleColor[k], false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); double c0 = (Angle * (Maximum - Minimum) - 2.0 * Minimum * Math.PI) / (Maximum - Minimum) + Math.PI; double c1 = 2.0 * (Math.PI - Angle) / (Maximum - Minimum); string Variable = "0"; switch (NeedleType[k]) { case 1: Variable = "brakecylinder"; break; case 2: Variable = "straightairpipe"; break; case 3: Variable = "brakepipe"; break; case 4: Variable = "equalizingreservoir"; break; case 5: Variable = "mainreservoir"; break; } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(Variable + " " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } } else if (Type == 1) { // leds if (NeedleType[1] != 0) { int j = CreateElement(Train, CenterX - Radius, CenterY + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, null, NeedleColor[1], false); double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, NeedleColor[1]); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = true; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = Angle - 2.0 * Math.PI; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = 2.0 * Math.PI - Angle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; double c0 = (Angle * (Maximum - Minimum) - 2.0 * Minimum * Math.PI) / (Maximum - Minimum); double c1 = 2.0 * (Math.PI - Angle) / (Maximum - Minimum); string Variable; switch (NeedleType[1]) { case 1: Variable = "brakecylinder"; break; case 2: Variable = "straightairpipe"; break; case 3: Variable = "brakepipe"; break; case 4: Variable = "equalizingreservoir"; break; case 5: Variable = "mainreservoir"; break; default: Variable = "0"; break; } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(Variable + " " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } } break; // speedometer case "speedometer": case "speedindicator": case "速度計": { int Type = 0; Color32 Needle = new Color32(255, 255, 255, 255); bool NeedleOverridden = false; double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null, Cover = null, Atc = null; double Angle = 1.0471975511966, Maximum = 33.3333333333333, AtcRadius = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "type": case "形態": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Type)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } else if (Type != 0 & Type != 1) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be either 0 or 1 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Type = 0; } break; case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "needle": case "hand": case "針": { int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 255; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 255; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 255; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } Needle = new Color32((byte)r, (byte)g, (byte)b, 255); NeedleOverridden = true; } break; case "cover": case "ふた": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; Cover = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Cover)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Cover + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Cover = null; } break; case "atc": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; Atc = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Atc)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Atc + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Atc = null; } break; case "atcradius": case "atc半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out AtcRadius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); AtcRadius = 0.0; } break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 0.0; } break; case "angle": case "角度": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Angle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Angle = 1.0471975511966; } else { Angle *= 0.0174532925199433; } break; case "maximum": case "最大": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "SpeedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Maximum = 33.3333333333333; } else { Maximum *= 0.277777777777778; } break; } } i++; } i--; if (Background != null) { // background/led Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Cover != null) { // cover Textures.Texture t; Textures.RegisterTexture(Cover, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } if (Atc != null) { // atc int w, h; Program.CurrentHost.QueryTextureDimensions(Atc, out w, out h); if (w > 0 & h > 0) { int n = w / h; int k = -1; for (int j = 0; j < n; j++) { double s; switch (j) { case 1: s = 0.0; break; case 2: s = 15.0; break; case 3: s = 25.0; break; case 4: s = 45.0; break; case 5: s = 55.0; break; case 6: s = 65.0; break; case 7: s = 75.0; break; case 8: s = 90.0; break; case 9: s = 100.0; break; case 10: s = 110.0; break; case 11: s = 120.0; break; default: s = -1.0; break; } s *= 0.277777777777778; double a; if (s >= 0.0) { a = 2.0 * s * (Math.PI - Angle) / Maximum + Angle + Math.PI; } else { a = Math.PI; } double x = CenterX - 0.5 * h + Math.Sin(a) * AtcRadius; double y = CenterY - 0.5 * h - Math.Cos(a) * AtcRadius + SemiHeight; Textures.Texture t; Textures.RegisterTexture(Atc, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(j * h, 0, h, h), Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); if (j == 0) { k = CreateElement(Train, x, y, (double)h, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, x, y, (double)h, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("271 pluginstate"); } } if (Type == 0) { // needle string Folder = Program.FileSystem.GetDataFolder("Compatibility"); string File = OpenBveApi.Path.CombineFile(Folder, "needle_speedometer.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); double c0 = Angle + Math.PI; double c1 = 2.0 * (Math.PI - Angle) / Maximum; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } else if (Type == 1) { // led if (!NeedleOverridden) Needle = new Color32(0, 0, 0, 255); int j = CreateElement(Train, CenterX - Radius, CenterY + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, null, Needle, false); double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, Needle); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = true; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = Angle - 2.0 * Math.PI; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = 2.0 * Math.PI - Angle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; double c0 = Angle; double c1 = 2.0 * (Math.PI - Angle) / Maximum; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + c1.ToString(Culture) + " " + c0.ToString(Culture) + " fma"); } } break; // digitalindicator case "digitalindicator": { string Number = null; double CornerX = 0.0, CornerY = 0.0; int Width = 0, Height = 0; double UnitFactor = 3.6; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "number": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Number = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Number)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Number + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Number = null; } } break; case "corner": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; case "size": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out Height)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Height = 0; } break; case "unit": if (Arguments.Length >= 1 && Arguments[0].Length > 0) { string a = Arguments[0].ToLowerInvariant(); int Unit = 0; if (a == "km/h") { Unit = 0; } else if (a == "mph") { Unit = 1; } else if (a == "m/s") { Unit = 2; } else if (!Interface.TryParseIntVb6(Arguments[0], out Unit)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } else if (Unit < 0 | Unit > 2) { Interface.AddMessage(Interface.MessageType.Error, false, "Value must be between 0 and 2 in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Unit = 0; } if (Unit == 1) { UnitFactor = 2.2369362920544; } else if (Unit == 2) { UnitFactor = 1.0; } else { UnitFactor = 3.6; } } break; } } i++; } i--; if (Number == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Number is required to be specified in " + Section + " in " + FileName); } if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Height <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is required to be specified in " + Section + " in " + FileName); } if (Number != null & Width > 0 & Height > 0) { int w, h; Program.CurrentHost.QueryTextureDimensions(Number, out w, out h); if (w > 0 & h > 0) { //Generate an error message rather than crashing if the clip region is invalid if (Width > w) { Width = w; Interface.AddMessage(Interface.MessageType.Warning, false, "Clip region width was greater than the texture width " + Section + " in " + FileName); } if (Height > h) { Height = h; Interface.AddMessage(Interface.MessageType.Warning, false, "Clip region height was greater than the texture height " + Section + " in " + FileName); } int n = h / Height; Textures.Texture[] t = new Textures.Texture[n]; for (int j = 0; j < n; j++) { Textures.RegisterTexture(Number, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(w - Width, j * Height, Width, Height), Color24.Blue), out t[j]); //TextureManager.UseTexture(t[j], TextureManager.UseMode.Normal); } { // hundreds int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 100 >= <> 100 quotient 10 mod 10 ?"); } { // tens int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX + (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX + (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 10 >= <> 10 quotient 10 mod 10 ?"); } { // ones int k = -1; for (int j = 0; j < n; j++) { if (j == 0) { k = CreateElement(Train, CornerX + 2.0 * (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX + 2.0 * (double)Width, CornerY + SemiHeight, (double)Width, (double)Height, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 7.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t[j], new Color32(255, 255, 255, 255), true); } } Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("speedometer abs " + UnitFactor.ToString(Culture) + " * floor 10 mod"); } } } } break; // pilotlamp case "pilotlamp": case "知らせ灯": { double CornerX = 0.0, CornerY = 0.0; string TurnOn = null, TurnOff = null; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "turnon": case "点灯": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { TurnOn = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(TurnOn)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + TurnOn + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); TurnOn = null; } } break; case "turnoff": case "消灯": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { TurnOff = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(TurnOff)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + TurnOff + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); TurnOff = null; } } break; case "corner": case "左上": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; } } i++; } i--; if (TurnOn != null & TurnOff != null) { Textures.Texture t0, t1; Textures.RegisterTexture(TurnOn, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t0); Textures.RegisterTexture(TurnOff, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t1); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t0, Textures.OpenGlTextureWrapMode.ClampClamp); Textures.LoadTexture(t1, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t0.Width; double h = (double)t0.Height; int j = CreateElement(Train, CornerX, CornerY + SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 2.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t0, new Color32(255, 255, 255, 255), false); w = (double)t1.Width; h = (double)t1.Height; CreateElement(Train, CornerX, CornerY + SemiHeight, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 2.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t1, new Color32(255, 255, 255, 255), true); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("doors 0 !="); } } break; // watch case "watch": case "時計": { Color32 Needle = new Color32(0, 0, 0, 255); double CenterX = 0.0, CenterY = 0.0, Radius = 16.0; string Background = null; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "background": case "背景": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Background = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Background)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName" + Background + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Background = null; } } break; case "needle": case "hand": case "針": { int r = 0, g = 0, b = 0; if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = 0; } else if (r < 0 | r > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "RedValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); r = r < 0 ? 0 : 255; } if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseIntVb6(Arguments[1], out g)) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = 0; } else if (g < 0 | g > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "GreenValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); g = g < 0 ? 0 : 255; } if (Arguments.Length >= 3 && Arguments[2].Length > 0 && !Interface.TryParseIntVb6(Arguments[2], out b)) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = 0; } else if (b < 0 | b > 255) { Interface.AddMessage(Interface.MessageType.Error, false, "BlueValue is required to be within the range from 0 to 255 in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); b = b < 0 ? 0 : 255; } Needle = new Color32((byte)r, (byte)g, (byte)b, 255); } break; case "center": case "中心": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CenterY = 0.0; } break; case "radius": case "半径": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; } } i++; } i--; if (Background != null) { Textures.Texture t; Textures.RegisterTexture(Background, new OpenBveApi.Textures.TextureParameters(null, Color24.Blue), out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; CreateElement(Train, CenterX - 0.5 * w, CenterY + SemiHeight - 0.5 * h, w, h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 3.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } string Folder = Program.FileSystem.GetDataFolder("Compatibility"); { // hour string File = OpenBveApi.Path.CombineFile(Folder, "needle_hour.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 4.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time 0.000277777777777778 * floor 0.523598775598298 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } { // minute string File = OpenBveApi.Path.CombineFile(Folder, "needle_minute.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 5.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time 0.0166666666666667 * floor 0.10471975511966 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } { // second string File = OpenBveApi.Path.CombineFile(Folder, "needle_second.png"); Textures.Texture t; Textures.RegisterTexture(File, out t); OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(t, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)t.Width; double h = (double)t.Height; int j = CreateElement(Train, CenterX - Radius * w / h, CenterY + SemiHeight - Radius, 2.0 * Radius * w / h, 2.0 * Radius, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - 6.0 * StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, Needle, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("time floor 0.10471975511966 *"); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(20.0, 0.4); } } break; // brakeindicator case "brakeindicator": { double CornerX = 0.0, CornerY = 0.0; string Image = null; int Width = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); string[] Arguments = GetArguments(Value); switch (Key.ToLowerInvariant()) { case "image": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { Image = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(Image)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + Image + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Image = null; } } break; case "corner": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[0], out CornerX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerX = 0.0; } else if (Arguments.Length >= 2 && Arguments[1].Length > 0 && !Interface.TryParseDoubleVb6(Arguments[1], out CornerY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); CornerY = 0.0; } break; case "width": if (Arguments.Length >= 1 && Arguments[0].Length > 0 && !Interface.TryParseIntVb6(Arguments[0], out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 1; } else if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is expected to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Width = 1; } break; } } i++; } i--; if (Image == null) { Interface.AddMessage(Interface.MessageType.Error, false, "Image is required to be specified in " + Section + " in " + FileName); } if (Width <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Image != null & Width > 0) { int w, h; Program.CurrentHost.QueryTextureDimensions(Image, out w, out h); if (w > 0 & h > 0) { int n = w / Width; int k = -1; for (int j = 0; j < n; j++) { Textures.Texture t; OpenBveApi.Textures.TextureClipRegion clip = new OpenBveApi.Textures.TextureClipRegion(j * Width, 0, Width, h); Textures.RegisterTexture(Image, new OpenBveApi.Textures.TextureParameters(clip, Color24.Blue), out t); //TextureManager.UseTexture(t, TextureManager.UseMode.Normal); if (j == 0) { k = CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), false); } else { CreateElement(Train, CornerX, CornerY + SemiHeight, (double)Width, (double)h, FullWidth, FullHeight, WorldLeft, WorldTop, WorldWidth, WorldHeight, WorldZ + EyeDistance - StackDistance, Train.Cars[Train.DriverCar].DriverX, Train.Cars[Train.DriverCar].DriverY, Train.Cars[Train.DriverCar].DriverZ, t, new Color32(255, 255, 255, 255), true); } } if (Train.Cars[Train.DriverCar].Specs.BrakeType == TrainManager.CarBrakeType.AutomaticAirBrake) { int maxpow = Train.Specs.MaximumPowerNotch; int em = maxpow + 3; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " brakeNotch 0 > " + maxpow.ToString(Culture) + " BrakeNotch + " + maxpow.ToString(Culture) + " powerNotch - ? ?"); } else { if (Train.Specs.HasHoldBrake) { int em = Train.Specs.MaximumPowerNotch + 2 + Train.Specs.MaximumBrakeNotch; int maxpow = Train.Specs.MaximumPowerNotch; int maxpowp1 = maxpow + 1; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " holdBrake " + maxpowp1.ToString(Culture) + " brakeNotch 0 > brakeNotch " + maxpowp1.ToString(Culture) + " + " + maxpow.ToString(Culture) + " powerNotch - ? ? ?"); } else { int em = Train.Specs.MaximumPowerNotch + 1 + Train.Specs.MaximumBrakeNotch; int maxpow = Train.Specs.MaximumPowerNotch; Train.Cars[Train.DriverCar].CarSections[0].Elements[k].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("emergencyBrake " + em.ToString(Culture) + " brakeNotch 0 > brakeNotch " + maxpow.ToString(Culture) + " + " + maxpow.ToString(Culture) + " powerNotch - ? ?"); } } } } } break; } } } } }
// --- constructors --- /// <summary>Creates a new table of characters.</summary> /// <param name="font">The font.</param> /// <param name="offset">The offset from codepoint U+0000.</param> internal OpenGlFontTable(Font font, int offset) { /* * Measure characters. * */ Size[] physicalSizes = new Size[256]; Size[] typographicSizes = new Size[256]; Bitmap bitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(bitmap); graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; for (int i = 0; i < 256; i++) { SizeF physicalSize = graphics.MeasureString(char.ConvertFromUtf32(offset + i), font, int.MaxValue, StringFormat.GenericDefault); SizeF typographicSize = graphics.MeasureString(char.ConvertFromUtf32(offset + i), font, int.MaxValue, StringFormat.GenericTypographic); physicalSizes[i] = new Size((int)Math.Ceiling(physicalSize.Width), (int)Math.Ceiling(physicalSize.Height)); typographicSizes[i] = new Size((int)Math.Ceiling(typographicSize.Width == 0.0f ? physicalSize.Width : typographicSize.Width), (int)Math.Ceiling(typographicSize.Height == 0.0f ? physicalSize.Height : typographicSize.Height)); } /* * Find suitable bitmap dimensions. * */ const int width = 256; const int border = 1; int x = border; int y = border; int lineHeight = 0; for (int i = 0; i < 256; i++) { if (x + physicalSizes[i].Width + border > width) { x = border; y += lineHeight; lineHeight = 0; } else { x += physicalSizes[i].Width + 2 * border; } if (physicalSizes[i].Height + border > lineHeight) { lineHeight = physicalSizes[i].Height + 2 * border; } } y += lineHeight; int height = (int)RoundToPowerOfTwo((uint)y); graphics.Dispose(); bitmap.Dispose(); /* * Draw character to bitmap. * */ bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); graphics = Graphics.FromImage(bitmap); graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; graphics.Clear(Color.Black); x = border; y = border; lineHeight = 0; this.Characters = new OpenGlFontChar[256]; for (int i = 0; i < 256; i++) { if (x + physicalSizes[i].Width + border > width) { x = border; y += lineHeight; lineHeight = 0; } graphics.DrawString(char.ConvertFromUtf32(offset + i), font, Brushes.White, new PointF(x, y)); float x0 = (float)(x - border) / (float)width; float x1 = (float)(x + physicalSizes[i].Width + border) / (float)width; float y0 = (float)(y - border) / (float)height; float y1 = (float)(y + physicalSizes[i].Height + border) / (float)height; this.Characters[i] = new OpenGlFontChar(new RectangleF(x0, y0, x1 - x0, y1 - y0), new Size(physicalSizes[i].Width + 2 * border, physicalSizes[i].Height + 2 * border), typographicSizes[i]); x += physicalSizes[i].Width + 2 * border; if (physicalSizes[i].Height + border > lineHeight) { lineHeight = physicalSizes[i].Height + 2 * border; } } graphics.Dispose(); this.Texture = Textures.RegisterTexture(bitmap); }
/******************** PRIVATE METHODS *********************/ // // SET STATE // /// <summary>Sets the state, intializing any required resource.</summary> /// <param name="newState">The new state to set to.</param> private void setState(state newState) { switch (newState) { case state.map: if (mapImage == null) { mapImage = new Textures.Texture(Game.RouteInformation.RouteMap); mapSize = Game.RouteInformation.RouteMap.Size; } break; case state.gradient: if (gradientImage == null) { gradientImage = new Textures.Texture(Game.RouteInformation.GradientProfile); gradientSize = Game.RouteInformation.GradientProfile.Size; } break; } currentState = newState; }
internal Background(Textures.Texture Texture, int Repetition, bool KeepAspectRatio) { this.Texture = Texture; this.Repetition = Repetition; this.KeepAspectRatio = KeepAspectRatio; }
// load all textures /// <summary>Loads all signal or glow textures.</summary> /// <param name="BaseFile">The base file.</param> /// <param name="IsGlowTexture">Whether to load glow textures. If false, black is the transparent color. If true, the texture is edited according to the CSV route documentation.</param> /// <returns>All textures matching the base file.</returns> private static Textures.Texture[] LoadAllTextures(string BaseFile, bool IsGlowTexture) { string Folder = System.IO.Path.GetDirectoryName(BaseFile); if (!System.IO.Directory.Exists(Folder)) { return new Textures.Texture[] { }; } string Name = System.IO.Path.GetFileNameWithoutExtension(BaseFile); Textures.Texture[] Textures = new Textures.Texture[] { }; string[] Files = System.IO.Directory.GetFiles(Folder); for (int i = 0; i < Files.Length; i++) { string a = System.IO.Path.GetFileNameWithoutExtension(Files[i]); if (a.StartsWith(Name, StringComparison.OrdinalIgnoreCase)) { if (a.Length > Name.Length) { string b = a.Substring(Name.Length).TrimStart(); int j; if (int.TryParse(b, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out j)) { if (j >= 0) { string c = System.IO.Path.GetExtension(Files[i]); switch (c.ToLowerInvariant()) { case ".bmp": case ".gif": case ".jpg": case ".jpeg": case ".png": case ".tif": case ".tiff": if (j >= Textures.Length) { int n = Textures.Length; Array.Resize<Textures.Texture>(ref Textures, j + 1); for (int k = n; k < j; k++) { Textures[k] = null; } } if (IsGlowTexture) { OpenBveApi.Textures.Texture texture; if (Program.CurrentHost.LoadTexture(Files[i], null, out texture)) { if (texture.BitsPerPixel == 32) { byte[] bytes = texture.Bytes; InvertLightness(bytes); texture = new OpenBveApi.Textures.Texture(texture.Width, texture.Height, 32, bytes); } Textures[j] = OpenBve.Textures.RegisterTexture(texture); } } else { OpenBve.Textures.RegisterTexture(Files[i], new OpenBveApi.Textures.TextureParameters(null, Color24.Black), out Textures[j]); } break; } } } } } } return Textures; }
// parse panel config internal static void ParsePanel2Config(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { // read lines System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); } } // initialize double DriverX = Train.Cars[Train.DriverCar].DriverX; double DriverY = Train.Cars[Train.DriverCar].DriverY; double DriverZ = Train.Cars[Train.DriverCar].DriverZ; double PanelResolution = 1024.0; double PanelLeft = 0.0, PanelRight = 1024.0; double PanelTop = 0.0, PanelBottom = 1024.0; double PanelCenterX = 0.0, PanelCenterY = 512.0; double PanelOriginX = 0.0, PanelOriginY = 512.0; double PanelBitmapWidth = 1024.0, PanelBitmapHeight = 1024.0; string PanelDaytimeImage = null; string PanelNighttimeImage = null; Color24 PanelTransparentColor = new Color24(0, 0, 255); // parse lines for panel for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // panel case "this": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "resolution": double pr = 0.0; if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out pr)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (pr > 100) { PanelResolution = pr; } else { //Parsing very low numbers (Probable typos) for the panel resolution causes some very funky graphical bugs //Cap the minimum panel resolution at 100px wide (BVE1 panels are 480px wide, so this is probably a safe minimum) Interface.AddMessage(Interface.MessageType.Error, false, "A panel resolution of less than 10px was given at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "left": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelLeft)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line" + (i + 1).ToString(Culture) + " in " + FileName); } break; case "right": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelRight)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "top": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelTop)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "bottom": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelBottom)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { PanelDaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelDaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelDaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); PanelDaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { PanelNighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelNighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelNighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); PanelNighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out PanelTransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "center": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out PanelCenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out PanelCenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } case "origin": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out PanelOriginX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out PanelOriginY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } } i++; } i--; break; } } } } { // camera restriction double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / World.AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance / World.AspectRatio; WorldWidth = WorldHeight * World.AspectRatio; } double x0 = (PanelLeft - PanelCenterX) / PanelResolution; double x1 = (PanelRight - PanelCenterX) / PanelResolution; double y0 = (PanelCenterY - PanelBottom) / PanelResolution * World.AspectRatio; double y1 = (PanelCenterY - PanelTop) / PanelResolution * World.AspectRatio; World.CameraRestrictionBottomLeft = new Vector3(x0 * WorldWidth, y0 * WorldHeight, EyeDistance); World.CameraRestrictionTopRight = new Vector3(x1 * WorldWidth, y1 * WorldHeight, EyeDistance); Train.Cars[Train.DriverCar].DriverYaw = Math.Atan((PanelCenterX - PanelOriginX) * WorldWidth / PanelResolution); Train.Cars[Train.DriverCar].DriverPitch = Math.Atan((PanelOriginY - PanelCenterY) * WorldWidth / PanelResolution); } // create panel if (PanelDaytimeImage != null) { if (!System.IO.File.Exists(PanelDaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "The daytime panel bitmap could not be found in " + FileName); PanelDaytimeImage = null; } else { Textures.Texture tday; Textures.RegisterTexture(PanelDaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(PanelTransparentColor.R, PanelTransparentColor.G, PanelTransparentColor.B)), out tday); Textures.Texture tnight = null; if (PanelNighttimeImage != null) { if (!System.IO.File.Exists(PanelNighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "The nighttime panel bitmap could not be found in " + FileName); PanelNighttimeImage = null; } else { Textures.RegisterTexture(PanelNighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(PanelTransparentColor.R, PanelTransparentColor.G, PanelTransparentColor.B)), out tnight); } } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); PanelBitmapWidth = (double)tday.Width; PanelBitmapHeight = (double)tday.Height; CreateElement(Train, 0.0, 0.0, PanelBitmapWidth, PanelBitmapHeight, 0.5, 0.5, 0.0, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); } } // parse lines for rest double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // pilotlamp case "pilotlamp": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color24 TransparentColor = new Color24(0, 0, 255); int Layer = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); int w = tday.Width; int h = tday.Height; int j = CreateElement(Train, LocationX, LocationY, w, h, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f + " 1 == --"); } } break; // needle case "needle": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color32 Color = new Color32(255, 255, 255, 255); Color24 TransparentColor = new Color24(0, 0, 255); double OriginX = -1.0, OriginY = -1.0; bool OriginDefined = false; double Layer = 0.0, Radius = 0.0; double InitialAngle = -2.0943951023932, LastAngle = 2.0943951023932; double Minimum = 0.0, Maximum = 1000.0; double NaturalFrequency = -1.0, DampingRatio = -1.0; bool Backstop = false; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterX is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterY is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; case "radius": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is expected to be non-zero in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "color": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out Color)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "origin": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out OriginX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out OriginY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); OriginX = -OriginX; } OriginDefined = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; case "initialangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out InitialAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "lastangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out LastAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "naturalfreq": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out NaturalFrequency)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (NaturalFrequency < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NaturalFrequency = -NaturalFrequency; } break; case "dampingratio": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out DampingRatio)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (DampingRatio < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DampingRatio = -DampingRatio; } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "backstop": if (Value.Length != 0 && Value.ToLowerInvariant() == "true" || Value == "1") { Backstop = true; } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)tday.Width; double h = (double)tday.Height; if (!OriginDefined) { OriginX = 0.5 * w; OriginY = 0.5 * h; } double ox = OriginX / w; double oy = OriginY / h; double n = Radius == 0.0 | OriginY == 0.0 ? 1.0 : Radius / OriginY; double nx = n * w; double ny = n * h; int j = CreateElement(Train, LocationX - ox * nx, LocationY - oy * ny, nx, ny, ox, oy, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, Color, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); string f; switch (Subject.ToLowerInvariant()) { case "hour": f = "0.000277777777777778 time * floor"; break; case "min": f = "0.0166666666666667 time * floor"; break; case "sec": f = "time floor"; break; default: f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); break; } //Convert angles from degrees to radians InitialAngle *= 0.0174532925199433; LastAngle *= 0.0174532925199433; double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); f += " " + a1.ToString(Culture) + " * " + a0.ToString(Culture) + " +"; if (NaturalFrequency >= 0.0 & DampingRatio >= 0.0) { Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(NaturalFrequency, DampingRatio); } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); if (Backstop) { Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction.Minimum = InitialAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction.Maximum = LastAngle; } } } break; case "lineargauge": { string Subject = "true"; int Width = 0; Vector2 Direction = new Vector2(1,0); double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; double Minimum = 0.0, Maximum = 0.0; Color24 TransparentColor = new Color24(0, 0, 255); int Layer = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "width": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "direction": { string[] s = Value.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 LinearGauge Direction 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 LinearGauge Direction at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Direction = new Vector2(x, y); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in LinearGauge Direction at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); int w = tday.Width; int h = tday.Height; int j = CreateElement(Train, LocationX, LocationY, w, h, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); if (Maximum < Minimum) { Interface.AddMessage(Interface.MessageType.Error, false, "Maximum value must be greater than minimum value " + Section + " in " + FileName); break; } string tf = GetInfixFunction(Train, Subject, Minimum, Maximum, Width, tday.Width, Section + " in " + FileName); if (tf != String.Empty) { Train.Cars[Train.DriverCar].CarSections[0].Elements[j].TextureShiftXDirection = Direction; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(tf); } } } break; // digitalnumber case "digitalnumber": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color24 TransparentColor = new Color24(0, 0, 255); double Layer = 0.0; int Interval = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "interval": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Interval)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Interval <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } if (Interval <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Interval is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null & Interval > 0) { int wday, hday; Program.CurrentHost.QueryTextureDimensions(DaytimeImage, out wday, out hday); if (wday > 0 & hday > 0) { int nday = hday / Interval; Textures.Texture[] tday = new Textures.Texture[nday]; Textures.Texture[] tnight; for (int k = 0; k < nday; k++) { Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(0, k * Interval, wday, Interval), new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday[k]); } if (NighttimeImage != null) { int wnight, hnight; Program.CurrentHost.QueryTextureDimensions(NighttimeImage, out wnight, out hnight); int nnight = hnight / Interval; if (nnight > nday) nnight = nday; tnight = new Textures.Texture[nday]; for (int k = 0; k < nnight; k++) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(0, k * Interval, wday, Interval), new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight[k]); } for (int k = nnight; k < nday; k++) { tnight[k] = null; } } else { tnight = new Textures.Texture[nday]; for (int k = 0; k < nday; k++) { tnight[k] = null; } } int j = -1; for (int k = 0; k < tday.Length; k++) { int l = CreateElement(Train, LocationX, LocationY, (double)wday, (double)Interval, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday[k], tnight[k], new Color32(255, 255, 255, 255), k != 0); if (k == 0) j = l; } string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); } } } break; // digitalgauge case "digitalgauge": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; Color32 Color = new Color32(0, 0, 0, 255); double Radius = 0.0; int Layer = 0; double InitialAngle = -2.0943951023932, LastAngle = 2.0943951023932; double Minimum = 0.0, Maximum = 1000.0; double Step = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterX is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterY is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "radius": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is expected to be non-zero in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; case "color": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out Color)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "initialangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out InitialAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { InitialAngle *= 0.0174532925199433; } break; case "lastangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out LastAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { LastAngle *= 0.0174532925199433; } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "step": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Step)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Radius is required to be non-zero in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (Minimum == Maximum) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum and Maximum must not be equal in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 0.0; } if (Math.Abs(InitialAngle - LastAngle) > 6.28318531) { Interface.AddMessage(Interface.MessageType.Warning, false, "The absolute difference between InitialAngle and LastAngle exceeds 360 degrees in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (Radius != 0.0) { // create element int j = CreateElement(Train, LocationX - Radius, LocationY - Radius, 2.0 * Radius, 2.0 * Radius, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, null, null, Color, false); InitialAngle = InitialAngle + Math.PI; LastAngle = LastAngle + Math.PI; double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, Color); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = InitialAngle <= LastAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = InitialAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = LastAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); if (Step == 1.0) { f += " floor"; } else if (Step != 0.0) { string s = (1.0 / Step).ToString(Culture); string t = Step.ToString(Culture); f += " " + s + " * floor " + t + " *"; } f += " " + a1.ToString(Culture) + " " + a0.ToString(Culture) + " fma"; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); } else { Interface.AddMessage(Interface.MessageType.Error, false, "Radius is required to be specified in " + Section + " in " + FileName); } } break; // timetable case "timetable": { double LocationX = 0.0, LocationY = 0.0; double Width = 0.0, Height = 0.0; //We read the transparent color for the timetable from the config file, but it is never used //TODO: Fix or depreciate?? Color24 TransparentColor = new Color24(0, 0, 255); double Layer = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "width": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Width <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is required to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "height": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Height)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Height <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is required to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; // create element if (Width <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Height <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is required to be specified in " + Section + " in " + FileName); } if (Width > 0.0 & Height > 0.0) { int j = CreateElement(Train, LocationX, LocationY, Width, Height, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, null, null, new Color32(255, 255, 255, 255), false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("timetable"); Timetable.AddObjectForCustomTimetable(Train.Cars[Train.DriverCar].CarSections[0].Elements[j]); } } break; } } } } }
private static void RenderCube(Vector3 Position, Vector3 Direction, Vector3 Up, Vector3 Side, double Size, double CameraX, double CameraY, double CameraZ, Textures.Texture TextureIndex) { Vector3[] v = new Vector3[8]; v[0] = new Vector3(Size, Size, -Size); v[1] = new Vector3(Size, -Size, -Size); v[2] = new Vector3(-Size, -Size, -Size); v[3] = new Vector3(-Size, Size, -Size); v[4] = new Vector3(Size, Size, Size); v[5] = new Vector3(Size, -Size, Size); v[6] = new Vector3(-Size, -Size, Size); v[7] = new Vector3(-Size, Size, Size); for (int i = 0; i < 8; i++) { World.Rotate(ref v[i].X, ref v[i].Y, ref v[i].Z, Direction.X, Direction.Y, Direction.Z, Up.X, Up.Y, Up.Z, Side.X, Side.Y, Side.Z); v[i].X += Position.X - CameraX; v[i].Y += Position.Y - CameraY; v[i].Z += Position.Z - CameraZ; } int[][] Faces = new int[6][]; Faces[0] = new int[] { 0, 1, 2, 3 }; Faces[1] = new int[] { 0, 4, 5, 1 }; Faces[2] = new int[] { 0, 3, 7, 4 }; Faces[3] = new int[] { 6, 5, 4, 7 }; Faces[4] = new int[] { 6, 7, 3, 2 }; Faces[5] = new int[] { 6, 2, 1, 5 }; if (TextureIndex == null || !Textures.LoadTexture(TextureIndex, Textures.OpenGlTextureWrapMode.ClampClamp)) { if (TexturingEnabled) { GL.Disable(EnableCap.Texture2D); TexturingEnabled = false; } for (int i = 0; i < 6; i++) { GL.Begin(PrimitiveType.Quads); GL.Color3(1.0, 1.0, 1.0); for (int j = 0; j < 4; j++) { GL.Vertex3(v[Faces[i][j]].X, v[Faces[i][j]].Y, v[Faces[i][j]].Z); } GL.End(); } return; } else { TexturingEnabled = true; GL.Enable(EnableCap.Texture2D); } GL.BindTexture(TextureTarget.Texture2D, TextureIndex.OpenGlTextures[(int)Textures.OpenGlTextureWrapMode.ClampClamp].Name); Vector2[][] t = new Vector2[6][]; t[0] = new Vector2[] { new Vector2(1.0, 0.0), new Vector2(1.0, 1.0), new Vector2(0.0, 1.0), new Vector2(0.0, 0.0) }; t[1] = new Vector2[] { new Vector2(0.0, 0.0), new Vector2(1.0, 0.0), new Vector2(1.0, 1.0), new Vector2(0.0, 1.0) }; t[2] = new Vector2[] { new Vector2(1.0, 1.0), new Vector2(0.0, 1.0), new Vector2(0.0, 0.0), new Vector2(1.0, 0.0) }; t[3] = new Vector2[] { new Vector2(1.0, 1.0), new Vector2(0.0, 1.0), new Vector2(0.0, 0.0), new Vector2(1.0, 0.0) }; t[4] = new Vector2[] { new Vector2(0.0, 1.0), new Vector2(0.0, 0.0), new Vector2(1.0, 0.0), new Vector2(1.0, 1.0) }; t[5] = new Vector2[] { new Vector2(0.0, 1.0), new Vector2(0.0, 0.0), new Vector2(1.0, 0.0), new Vector2(1.0, 1.0) }; for (int i = 0; i < 6; i++) { GL.Begin(PrimitiveType.Quads); GL.Color3(1.0, 1.0, 1.0); for (int j = 0; j < 4; j++) { GL.TexCoord2(t[i][j].X, t[i][j].Y); GL.Vertex3(v[Faces[i][j]].X, v[Faces[i][j]].Y, v[Faces[i][j]].Z); } GL.End(); } }
internal MarkerStartEvent(double trackPositionDelta, Textures.Texture texture) { this.TrackPositionDelta = trackPositionDelta; this.DontTriggerAnymore = false; this.Texture = texture; }
//Parses an XML background definition public static BackgroundManager.BackgroundHandle ReadBackgroundXML(string fileName) { List <BackgroundManager.StaticBackground> Backgrounds = new List <BackgroundManager.StaticBackground>(); //The current XML file to load XmlDocument currentXML = new XmlDocument(); //Load the object's XML file currentXML.Load(fileName); string Path = System.IO.Path.GetDirectoryName(fileName); double[] UnitOfLength = { 1.0 }; //Check for null if (currentXML.DocumentElement != null) { XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/Background"); //Check this file actually contains OpenBVE light definition nodes if (DocumentNodes != null) { foreach (XmlNode n in DocumentNodes) { if (n.HasChildNodes) { double DisplayTime = -1; //The time to transition between backgrounds in seconds double TransitionTime = 0.8; //The texture to use (if static) Textures.Texture t = null; //The object to use (if object based) ObjectManager.StaticObject o = null; //The transition mode between backgrounds BackgroundManager.BackgroundTransitionMode mode = BackgroundManager.BackgroundTransitionMode.FadeIn; //The number of times the texture is repeated around the viewing frustrum (if appropriate) double repetitions = 6; foreach (XmlNode c in n.ChildNodes) { string[] Arguments = c.InnerText.Split(','); switch (c.Name.ToLowerInvariant()) { case "mode": switch (c.InnerText.ToLowerInvariant()) { case "fadein": mode = BackgroundManager.BackgroundTransitionMode.FadeIn; break; case "fadeout": mode = BackgroundManager.BackgroundTransitionMode.FadeOut; break; case "none": mode = BackgroundManager.BackgroundTransitionMode.None; break; default: Interface.AddMessage(Interface.MessageType.Error, true, c.InnerText + "is not a valid background fade mode in file " + fileName); break; } break; case "object": string f = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(fileName), c.InnerText); if (!System.IO.File.Exists(f)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + f + " not found in file " + fileName); } else { ObjectManager.UnifiedObject b = ObjectManager.LoadObject(f, System.Text.Encoding.Default, ObjectManager.ObjectLoadMode.Normal, false, false, false); o = (ObjectManager.StaticObject)b; } break; case "repetitions": if (!NumberFormats.TryParseDoubleVb6(Arguments[0], UnitOfLength, out repetitions)) { Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not parse to a valid number of repetitions in " + fileName); } break; case "texture": var file = OpenBveApi.Path.CombineFile(Path, c.InnerText); if (!System.IO.File.Exists(file)) { Interface.AddMessage(Interface.MessageType.Error, false, "The background texture file " + c.InnerText + " does not exist in " + fileName); } else { Textures.RegisterTexture(file, out t); } break; case "time": if (!Interface.TryParseTime(Arguments[0].Trim(), out DisplayTime)) { Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " does not parse to a valid time in file " + fileName); } break; case "transitiontime": if (!NumberFormats.TryParseDoubleVb6(Arguments[0], UnitOfLength, out TransitionTime)) { Interface.AddMessage(Interface.MessageType.Error, false, c.InnerText + " is not a valid background transition time in " + fileName); } break; } } //Create background if texture is not null if (t != null && o == null) { Backgrounds.Add(new BackgroundManager.StaticBackground(t, repetitions, false, TransitionTime, mode, DisplayTime)); } if (t == null && o != null) { //All other parameters are ignored if an object has been defined //TODO: Error message stating they have been ignored return(new BackgroundManager.BackgroundObject(o)); } } } if (Backgrounds.Count == 1) { return(Backgrounds[0]); } if (Backgrounds.Count > 1) { //Sort list- Not worried about when they start or end, so use simple LINQ Backgrounds = Backgrounds.OrderBy(o => o.Time).ToList(); //If more than 2 backgrounds, convert to array and return a new dynamic background return(new BackgroundManager.DynamicBackground(Backgrounds.ToArray())); } } } //We couldn't find any valid XML, so return false throw new InvalidDataException(); }
/// <summary>Loads all BVE4 signal or glow textures (Non animated file)</summary> /// <param name="BaseFile">The base file.</param> /// <param name="IsGlowTexture">Whether to load glow textures. If false, black is the transparent color. If true, the texture is edited according to the CSV route documentation.</param> /// <returns>All textures matching the base file.</returns> private static Textures.Texture[] LoadAllTextures(string BaseFile, bool IsGlowTexture) { string Folder = System.IO.Path.GetDirectoryName(BaseFile); if (Folder != null && !System.IO.Directory.Exists(Folder)) { return(new Textures.Texture[] { }); } string Name = System.IO.Path.GetFileNameWithoutExtension(BaseFile); Textures.Texture[] Textures = new Textures.Texture[] { }; if (Folder == null) { return(Textures); } string[] Files = System.IO.Directory.GetFiles(Folder); for (int i = 0; i < Files.Length; i++) { string a = System.IO.Path.GetFileNameWithoutExtension(Files[i]); if (a == null) { return(Textures); } if (a.StartsWith(Name, StringComparison.OrdinalIgnoreCase)) { if (a.Length > Name.Length) { string b = a.Substring(Name.Length).TrimStart(); int j; if (int.TryParse(b, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out j)) { if (j >= 0) { string c = System.IO.Path.GetExtension(Files[i]); if (c == null) { return(Textures); } switch (c.ToLowerInvariant()) { case ".bmp": case ".gif": case ".jpg": case ".jpeg": case ".png": case ".tif": case ".tiff": if (j >= Textures.Length) { int n = Textures.Length; Array.Resize <Textures.Texture>(ref Textures, j + 1); for (int k = n; k < j; k++) { Textures[k] = null; } } if (IsGlowTexture) { OpenBveApi.Textures.Texture texture; if (Program.CurrentHost.LoadTexture(Files[i], null, out texture)) { if (texture.BitsPerPixel == 32) { byte[] bytes = texture.Bytes; InvertLightness(bytes); texture = new OpenBveApi.Textures.Texture(texture.Width, texture.Height, 32, bytes); } Textures[j] = OpenBve.Textures.RegisterTexture(texture); } } else { OpenBve.Textures.RegisterTexture(Files[i], new OpenBveApi.Textures.TextureParameters(null, Color24.Black), out Textures[j]); } break; } } } } } } return(Textures); }
// render data private static void RenderData(ref Table Table) { // prepare timetable int w = 384, h = 192; int offsetx = 0; int actualheight = h; float descriptionwidth = 256; float descriptionheight = 16; float stationnamewidth = 16; for (int k = 0; k < 2; k++) { Bitmap b = new Bitmap(w, h); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.Transparent); g.FillRectangle(Brushes.White, new RectangleF(offsetx, 0, w, actualheight)); Font f = new Font(FontFamily.GenericSansSerif, 13.0f, GraphicsUnit.Pixel); Font fs = new Font(FontFamily.GenericSansSerif, 11.0f, GraphicsUnit.Pixel); Font fss = new Font(FontFamily.GenericSansSerif, 9.0f, GraphicsUnit.Pixel); // draw timetable string t; SizeF s; // description float x0 = offsetx + 8; float y0 = 8; if (k == 1) { t = DefaultTimetableDescription; g.DrawString(t, f, Brushes.Black, new RectangleF(x0, 6, descriptionwidth, descriptionheight + 8)); y0 += descriptionheight + 2; } // highest speed t = Interface.GetInterfaceString("timetable_highestspeed"); s = g.MeasureString(t, fs); g.DrawString(t, fs, Brushes.Black, x0, y0); float y0a = y0 + s.Height + 2; float x1 = x0 + s.Width + 4; for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; t = Table.Tracks[i].Speed; g.DrawString(t, f, Brushes.Black, x0, y); s = g.MeasureString(t, f); float x = x0 + s.Width + 4; if (x > x1) { x1 = x; } } g.DrawLine(Pens.LightGray, new PointF(x1 - 2, 4 + descriptionheight), new PointF(x1 - 2, y0a + 18 * Table.Tracks.Length - 1)); // driving time t = Interface.GetInterfaceString("timetable_drivingtime"); s = g.MeasureString(t, fs); g.DrawString(t, fs, Brushes.Black, x1, y0); float x2 = x1 + s.Width + 4; for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; if (Table.Tracks[i].Time.Hour.Length != 0) { t = Table.Tracks[i].Time.Hour; g.DrawString(t, fss, Brushes.Black, x1, y + 2); } else { t = "0"; } s = g.MeasureString(t, fss, 9999, StringFormat.GenericTypographic); float x = x1 + s.Width - 1; if (Table.Tracks[i].Time.Minute.Length != 0) { t = Table.Tracks[i].Time.Minute; g.DrawString(t, fs, Brushes.Black, x, y + 2); } else { t = "00:"; } s = g.MeasureString(t, fs, 9999, StringFormat.GenericTypographic); x += s.Width + 1; t = Table.Tracks[i].Time.Second; g.DrawString(t, fss, Brushes.Black, x, y + 2); s = g.MeasureString(t, fss, 9999, StringFormat.GenericTypographic); x += s.Width + 8; if (x > x2) { x2 = x; } } for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; g.DrawLine(Pens.LightGray, new PointF(offsetx + 4, y - 1), new PointF(x2 - 2, y - 1)); } g.DrawLine(Pens.LightGray, new PointF(x2 - 2, 4 + descriptionheight), new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1)); // station name float y2 = y0; t = Interface.GetInterfaceString("timetable_stationname"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x2, y2); float x3 = x2 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; g.DrawLine(Pens.LightGray, new PointF(x2 - 2, y - 1), new PointF(w - 4, y - 1)); t = Table.Stations[i].Name; if (Table.Stations[i].NameJapanese & Table.Stations[i].Name.Length > 1) { float[] sizes = new float[t.Length]; float totalsize = 0.0f; for (int j = 0; j < t.Length; j++) { sizes[j] = g.MeasureString(new string(t[j], 1), f, 9999, StringFormat.GenericTypographic).Width; totalsize += sizes[j]; } float space = (stationnamewidth - totalsize) / (float)(t.Length - 1); float x = 0.0f; for (int j = 0; j < t.Length; j++) { g.DrawString(new string(t[j], 1), f, Brushes.Black, x2 + x, y); x += sizes[j] + space; } } else { g.DrawString(t, f, Brushes.Black, x2, y); } s = g.MeasureString(t, f); { float x = x2 + s.Width + 4; if (x > x3) { x3 = x; } } } g.DrawLine(Pens.LightGray, new PointF(x3 - 2, 4 + descriptionheight), new PointF(x3 - 2, y0 + 18 * (Table.Stations.Length + 1))); if (k == 0) { stationnamewidth = x3 - x2 - 6; } // arrival time t = Interface.GetInterfaceString("timetable_arrivaltime"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x3, y2); float x4 = x3 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; if (Table.Stations[i].Pass) { t = "00"; s = g.MeasureString(t, fs); float x = x3 + s.Width; t = " ↓"; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += +s.Width + 4; if (x > x4) { x4 = x; } } else { if (Table.Stations[i].Arrival.Hour.Length != 0) { t = Table.Stations[i].Arrival.Hour; g.DrawString(t, fs, Brushes.Black, x3, y); } else { t = "00"; } s = g.MeasureString(t, fs); float x = x3 + s.Width; if (Table.Stations[i].Arrival.Minute.Length != 0 & Table.Stations[i].Arrival.Second.Length != 0) { t = Table.Stations[i].Arrival.Minute + ":" + Table.Stations[i].Arrival.Second; } else { t = ""; } g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += s.Width + 4; if (x > x4) { x4 = x; } } } g.DrawLine(Pens.LightGray, new PointF(x4 - 2, 4 + descriptionheight), new PointF(x4 - 2, y0 + 18 * (Table.Stations.Length + 1))); // departure time t = Interface.GetInterfaceString("timetable_departuretime"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x4, y2); float x5 = x4 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; if (Table.Stations[i].Terminal) { t = "00"; s = g.MeasureString(t, fs); float x = x4 + s.Width; const float c0 = 4; const float c1 = 32; g.DrawLine(Pens.Black, new PointF(x + c0, y + 6), new PointF(x + c1, y + 6)); g.DrawLine(Pens.Black, new PointF(x + c0, y + 10), new PointF(x + c1, y + 10)); x += c1 + 4; if (x > x5) { x5 = x; } } else { if (Table.Stations[i].Departure.Hour.Length != 0) { t = Table.Stations[i].Departure.Hour; g.DrawString(t, fs, Brushes.Black, x4, y); } else { t = "00"; } s = g.MeasureString(t, fs); float x = x4 + s.Width; if (Table.Stations[i].Departure.Minute.Length != 0 & Table.Stations[i].Departure.Second.Length != 0) { t = Table.Stations[i].Departure.Minute + ":" + Table.Stations[i].Departure.Second; } else { t = ""; } g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += s.Width + 4; if (x > x5) { x5 = x; } } } for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; g.DrawLine(Pens.LightGray, new PointF(x2 - 2, y - 1), new PointF(w - 4, y - 1)); } // border if (k == 1) { g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(offsetx + 4, y0a + 18 * Table.Tracks.Length - 1)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, y0a + 18 * Table.Tracks.Length - 1), new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(w - 4, 4)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4 + descriptionheight), new PointF(w - 4, 4 + descriptionheight)); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0 + 18 * (Table.Stations.Length + 1)), new PointF(w - 4, y0 + 18 * (Table.Stations.Length + 1))); g.DrawLine(Pens.Black, new PointF(w - 4, 4), new PointF(w - 4, y0 + 18 * (Table.Stations.Length + 1))); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1), new PointF(x2 - 2, y0 + 18 * (Table.Stations.Length + 1))); } // measure w = (int)Math.Ceiling((double)(x5 + 1)); h = (int)Math.Ceiling((double)(y0 + 18 * (Table.Stations.Length + 1) + 4)); // description if (k == 0) { t = DefaultTimetableDescription; s = g.MeasureString(t, f, w - 16); descriptionwidth = s.Width; descriptionheight = s.Height + 2; h += (int)Math.Ceiling((double)s.Height) + 4; } // finish if (k == 0) { // measures int nw = Textures.RoundUpToPowerOfTwo(w); offsetx = nw - w; w = nw; actualheight = h; h = Textures.RoundUpToPowerOfTwo(h); } else { // create texture g.Dispose(); DefaultTimetableTexture = Textures.RegisterTexture(b); } } }
// render data private static void RenderData(ref Table Table) { // prepare timetable int w = 384, h = 192; int offsetx = 0; int actualheight = h; float descriptionwidth = 256; float descriptionheight = 16; float stationnamewidth = 16; for (int k = 0; k < 2; k++) { Bitmap b = new Bitmap(w, h); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.Transparent); g.FillRectangle(Brushes.White, new RectangleF(offsetx, 0, w, actualheight)); Font f = new Font(FontFamily.GenericSansSerif, 13.0f, GraphicsUnit.Pixel); Font fs = new Font(FontFamily.GenericSansSerif, 11.0f, GraphicsUnit.Pixel); Font fss = new Font(FontFamily.GenericSansSerif, 9.0f, GraphicsUnit.Pixel); // draw timetable string t; SizeF s; // description float x0 = offsetx + 8; float y0 = 8; if (k == 1) { t = DefaultTimetableDescription; g.DrawString(t, f, Brushes.Black, new RectangleF(x0, 6, descriptionwidth, descriptionheight + 8)); y0 += descriptionheight + 2; } // highest speed t = Interface.GetInterfaceString("timetable_highestspeed"); s = g.MeasureString(t, fs); g.DrawString(t, fs, Brushes.Black, x0, y0); float y0a = y0 + s.Height + 2; float x1 = x0 + s.Width + 4; for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; t = Table.Tracks[i].Speed; g.DrawString(t, f, Brushes.Black, x0, y); s = g.MeasureString(t, f); float x = x0 + s.Width + 4; if (x > x1) x1 = x; } g.DrawLine(Pens.LightGray, new PointF(x1 - 2, 4 + descriptionheight), new PointF(x1 - 2, y0a + 18 * Table.Tracks.Length - 1)); // driving time t = Interface.GetInterfaceString("timetable_drivingtime"); s = g.MeasureString(t, fs); g.DrawString(t, fs, Brushes.Black, x1, y0); float x2 = x1 + s.Width + 4; for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; if (Table.Tracks[i].Time.Hour.Length != 0) { t = Table.Tracks[i].Time.Hour; g.DrawString(t, fss, Brushes.Black, x1, y + 2); } else { t = "0"; } s = g.MeasureString(t, fss, 9999, StringFormat.GenericTypographic); float x = x1 + s.Width - 1; if (Table.Tracks[i].Time.Minute.Length != 0) { t = Table.Tracks[i].Time.Minute; g.DrawString(t, fs, Brushes.Black, x, y + 2); } else { t = "00:"; } s = g.MeasureString(t, fs, 9999, StringFormat.GenericTypographic); x += s.Width + 1; t = Table.Tracks[i].Time.Second; g.DrawString(t, fss, Brushes.Black, x, y + 2); s = g.MeasureString(t, fss, 9999, StringFormat.GenericTypographic); x += s.Width + 8; if (x > x2) x2 = x; } for (int i = 0; i < Table.Tracks.Length; i++) { float y = y0a + 18 * i; g.DrawLine(Pens.LightGray, new PointF(offsetx + 4, y - 1), new PointF(x2 - 2, y - 1)); } g.DrawLine(Pens.LightGray, new PointF(x2 - 2, 4 + descriptionheight), new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1)); // station name float y2 = y0; t = Interface.GetInterfaceString("timetable_stationname"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x2, y2); float x3 = x2 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; g.DrawLine(Pens.LightGray, new PointF(x2 - 2, y - 1), new PointF(w - 4, y - 1)); t = Table.Stations[i].Name; if (Table.Stations[i].NameJapanese & Table.Stations[i].Name.Length > 1) { float[] sizes = new float[t.Length]; float totalsize = 0.0f; for (int j = 0; j < t.Length; j++) { sizes[j] = g.MeasureString(new string(t[j], 1), f, 9999, StringFormat.GenericTypographic).Width; totalsize += sizes[j]; } float space = (stationnamewidth - totalsize) / (float)(t.Length - 1); float x = 0.0f; for (int j = 0; j < t.Length; j++) { g.DrawString(new string(t[j], 1), f, Brushes.Black, x2 + x, y); x += sizes[j] + space; } } else { g.DrawString(t, f, Brushes.Black, x2, y); } s = g.MeasureString(t, f); { float x = x2 + s.Width + 4; if (x > x3) x3 = x; } } g.DrawLine(Pens.LightGray, new PointF(x3 - 2, 4 + descriptionheight), new PointF(x3 - 2, y0 + 18 * (Table.Stations.Length + 1))); if (k == 0) { stationnamewidth = x3 - x2 - 6; } // arrival time t = Interface.GetInterfaceString("timetable_arrivaltime"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x3, y2); float x4 = x3 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; if (Table.Stations[i].Pass) { t = "00"; s = g.MeasureString(t, fs); float x = x3 + s.Width; t = " ↓"; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += +s.Width + 4; if (x > x4) x4 = x; } else { if (Table.Stations[i].Arrival.Hour.Length != 0) { t = Table.Stations[i].Arrival.Hour; g.DrawString(t, fs, Brushes.Black, x3, y); } else { t = "00"; } s = g.MeasureString(t, fs); float x = x3 + s.Width; if (Table.Stations[i].Arrival.Minute.Length != 0 & Table.Stations[i].Arrival.Second.Length != 0) { t = Table.Stations[i].Arrival.Minute + ":" + Table.Stations[i].Arrival.Second; } else t = ""; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += s.Width + 4; if (x > x4) x4 = x; } } g.DrawLine(Pens.LightGray, new PointF(x4 - 2, 4 + descriptionheight), new PointF(x4 - 2, y0 + 18 * (Table.Stations.Length + 1))); // departure time t = Interface.GetInterfaceString("timetable_departuretime"); s = g.MeasureString(t, f); g.DrawString(t, f, Brushes.Black, x4, y2); float x5 = x4 + s.Width + 4; for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; if (Table.Stations[i].Terminal) { t = "00"; s = g.MeasureString(t, fs); float x = x4 + s.Width; const float c0 = 4; const float c1 = 32; g.DrawLine(Pens.Black, new PointF(x + c0, y + 6), new PointF(x + c1, y + 6)); g.DrawLine(Pens.Black, new PointF(x + c0, y + 10), new PointF(x + c1, y + 10)); x += c1 + 4; if (x > x5) x5 = x; } else { if (Table.Stations[i].Departure.Hour.Length != 0) { t = Table.Stations[i].Departure.Hour; g.DrawString(t, fs, Brushes.Black, x4, y); } else { t = "00"; } s = g.MeasureString(t, fs); float x = x4 + s.Width; if (Table.Stations[i].Departure.Minute.Length != 0 & Table.Stations[i].Departure.Second.Length != 0) { t = Table.Stations[i].Departure.Minute + ":" + Table.Stations[i].Departure.Second; } else t = ""; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); x += s.Width + 4; if (x > x5) x5 = x; } } for (int i = 0; i < Table.Stations.Length; i++) { float y = y0 + 18 * (i + 1) + 2; g.DrawLine(Pens.LightGray, new PointF(x2 - 2, y - 1), new PointF(w - 4, y - 1)); } // border if (k == 1) { g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(offsetx + 4, y0a + 18 * Table.Tracks.Length - 1)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, y0a + 18 * Table.Tracks.Length - 1), new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(w - 4, 4)); g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4 + descriptionheight), new PointF(w - 4, 4 + descriptionheight)); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0 + 18 * (Table.Stations.Length + 1)), new PointF(w - 4, y0 + 18 * (Table.Stations.Length + 1))); g.DrawLine(Pens.Black, new PointF(w - 4, 4), new PointF(w - 4, y0 + 18 * (Table.Stations.Length + 1))); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0a + 18 * Table.Tracks.Length - 1), new PointF(x2 - 2, y0 + 18 * (Table.Stations.Length + 1))); } // measure w = (int)Math.Ceiling((double)(x5 + 1)); h = (int)Math.Ceiling((double)(y0 + 18 * (Table.Stations.Length + 1) + 4)); // description if (k == 0) { t = DefaultTimetableDescription; s = g.MeasureString(t, f, w - 16); descriptionwidth = s.Width; descriptionheight = s.Height + 2; h += (int)Math.Ceiling((double)s.Height) + 4; } // finish if (k == 0) { // measures int nw = Textures.RoundUpToPowerOfTwo(w); offsetx = nw - w; w = nw; actualheight = h; h = Textures.RoundUpToPowerOfTwo(h); } else { // create texture g.Dispose(); DefaultTimetableTexture = Textures.RegisterTexture(b); } } }
// ================================ internal static void Reset(bool ResetLogs) { // track manager TrackManager.CurrentTrack = new TrackManager.Track(); // train manager TrainManager.Trains = new TrainManager.Train[] { }; // game Debug.ClearMessages(); CurrentInterface = InterfaceType.Normal; RouteComment = ""; RouteImage = ""; RouteAccelerationDueToGravity = 9.80665; RouteRailGauge = 1.435; RouteInitialAirPressure = 101325.0; RouteInitialAirTemperature = 293.15; RouteInitialElevation = 0.0; RouteSeaLevelAirPressure = 101325.0; RouteSeaLevelAirTemperature = 293.15; Stations = new Station[] { }; Sections = new Section[] { }; BufferTrackPositions = new double[] { }; Messages = new Message[] { }; MarkerTextures = new Textures.Texture[] { }; PointsOfInterest = new PointOfInterest[] { }; PrecedingTrainTimeDeltas = new double[] { }; PrecedingTrainSpeedLimit = double.PositiveInfinity; BogusPretrainInstructions = new BogusPretrainInstruction[] { }; TrainName = ""; TrainStart = TrainStartMode.EmergencyBrakesNoAts; NoFogStart = (float)Math.Max(1.33333333333333 * Options.Current.ViewingDistance, 800.0); NoFogEnd = (float)Math.Max(2.66666666666667 * Options.Current.ViewingDistance, 1600.0); PreviousFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 0.0); CurrentFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 0.5); NextFog = new Fog(NoFogStart, NoFogEnd, new Color24(128, 128, 128), 1.0); InfoTotalTriangles = 0; InfoTotalTriangleStrip = 0; InfoTotalQuads = 0; InfoTotalQuadStrip = 0; InfoTotalPolygon = 0; InfoStaticOpaqueFaceCount = 0; if (ResetLogs) { LogRouteName = ""; LogTrainName = ""; LogDateTime = DateTime.Now; CurrentScore = new Score(); ScoreMessages = new ScoreMessage[] { }; ScoreLogs = new ScoreLog[64]; ScoreLogCount = 0; BlackBoxEntries = new BlackBoxEntry[256]; BlackBoxEntryCount = 0; BlackBoxNextUpdate = 0.0; } // renderer Renderer.Reset(); }
// update custom timetable internal static void UpdateCustomTimetable(Textures.Texture daytime, Textures.Texture nighttime) { for (int i = 0; i < CustomObjectsUsed; i++) { for (int j = 0; j < CustomObjects[i].States.Length; j++) { for (int k = 0; k < CustomObjects[i].States[j].Object.Mesh.Materials.Length; k++) { if (daytime != null) { CustomObjects[i].States[j].Object.Mesh.Materials[k].DaytimeTexture = daytime; } if (nighttime != null) { CustomObjects[i].States[j].Object.Mesh.Materials[k].NighttimeTexture = nighttime; } } } } if (daytime != null) { CurrentCustomTimetableDaytimeTexture = daytime; } if (nighttime != null) { CurrentCustomTimetableNighttimeTexture = nighttime; } if (CurrentCustomTimetableDaytimeTexture != null | CurrentCustomTimetableNighttimeTexture != null) { CustomTimetableAvailable = true; } else { CustomTimetableAvailable = false; } }
public static bool ReadMarkerXML(string fileName, ref CsvRwRouteParser.Marker marker) { //The current XML file to load XmlDocument currentXML = new XmlDocument(); //Load the marker's XML file currentXML.Load(fileName); string Path = System.IO.Path.GetDirectoryName(fileName); if (currentXML.DocumentElement != null) { bool iM = false; XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/TextMarker"); if (DocumentNodes == null || DocumentNodes.Count == 0) { DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/ImageMarker"); iM = true; } if (DocumentNodes == null || DocumentNodes.Count == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "No marker nodes defined in XML file " + fileName); return(false); } marker = new CsvRwRouteParser.Marker(); foreach (XmlNode n in DocumentNodes) { if (n.HasChildNodes) { bool EarlyDefined = false, LateDefined = false; string EarlyText = null, Text = null, LateText = null; string[] Trains = null; Textures.Texture EarlyTexture = null, Texture = null, LateTexture = null; double EarlyTime = 0.0, LateTime = 0.0, TimeOut = Double.PositiveInfinity, EndingPosition = Double.PositiveInfinity; OpenBveApi.Colors.MessageColor EarlyColor = MessageColor.White, OnTimeColor = MessageColor.White, LateColor = MessageColor.White; foreach (XmlNode c in n.ChildNodes) { switch (c.Name.ToLowerInvariant()) { case "early": if (!c.HasChildNodes) { Interface.AddMessage(Interface.MessageType.Error, false, "No paramaters defined for the early message in " + fileName); } foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "text": EarlyText = cc.InnerText; break; case "texture": var f = OpenBveApi.Path.CombineFile(Path, cc.InnerText); if (System.IO.File.Exists(f)) { if (!Textures.RegisterTexture(f, out EarlyTexture)) { Interface.AddMessage(Interface.MessageType.Error, false, "Loading MessageEarlyTexture " + f + " failed."); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "MessageEarlyTexture " + f + " does not exist."); } break; case "time": if (!Interface.TryParseTime(cc.InnerText, out EarlyTime)) { Interface.AddMessage(Interface.MessageType.Error, false, "Early message time invalid in " + fileName); } EarlyDefined = true; break; case "color": EarlyColor = ParseColor(cc.InnerText, fileName); break; } } break; case "ontime": if (!c.HasChildNodes) { Interface.AddMessage(Interface.MessageType.Error, false, "No paramaters defined for the on-time message in " + fileName); } foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "text": Text = cc.InnerText; break; case "texture": var f = OpenBveApi.Path.CombineFile(Path, cc.InnerText); if (System.IO.File.Exists(f)) { if (!Textures.RegisterTexture(f, out Texture)) { Interface.AddMessage(Interface.MessageType.Error, false, "Loading MessageTexture " + f + " failed."); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "MessageTexture " + f + " does not exist."); } break; case "color": OnTimeColor = ParseColor(cc.InnerText, fileName); break; } } break; case "late": if (!c.HasChildNodes) { Interface.AddMessage(Interface.MessageType.Error, false, "No paramaters defined for the late message in " + fileName); } foreach (XmlNode cc in c.ChildNodes) { switch (cc.Name.ToLowerInvariant()) { case "text": LateText = cc.InnerText; break; case "texture": var f = OpenBveApi.Path.CombineFile(Path, cc.InnerText); if (System.IO.File.Exists(f)) { if (!Textures.RegisterTexture(f, out LateTexture)) { Interface.AddMessage(Interface.MessageType.Error, false, "Loading MessageLateTexture " + f + " failed."); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "MessageLateTexture " + f + " does not exist."); } break; case "time": if (!Interface.TryParseTime(cc.InnerText, out LateTime)) { Interface.AddMessage(Interface.MessageType.Error, false, "Early message time invalid in " + fileName); } LateDefined = true; break; case "color": LateColor = ParseColor(cc.InnerText, fileName); break; } } break; case "timeout": if (!NumberFormats.TryParseDouble(c.InnerText, new[] { 1.0 }, out TimeOut)) { Interface.AddMessage(Interface.MessageType.Error, false, "Marker timeout invalid in " + fileName); } break; case "distance": if (!NumberFormats.TryParseDouble(c.InnerText, new[] { 1.0 }, out EndingPosition)) { Interface.AddMessage(Interface.MessageType.Error, false, "Marker distance invalid in " + fileName); } break; case "trains": Trains = c.InnerText.Split(';'); break; } } //Check this marker is valid if (TimeOut == Double.PositiveInfinity && EndingPosition == Double.PositiveInfinity) { Interface.AddMessage(Interface.MessageType.Error, false, "No marker timeout or distance defined in marker XML " + fileName); return(false); } if (EndingPosition != Double.PositiveInfinity) { marker.EndingPosition = EndingPosition; } MessageManager.TextureMessage t = new MessageManager.TextureMessage(); MessageManager.GeneralMessage m = new MessageManager.GeneralMessage(); //Add variants if (EarlyDefined) { if (iM) { if (EarlyTexture != null) { t.MessageEarlyTime = EarlyTime; t.MessageEarlyTexture = EarlyTexture; } else { Interface.AddMessage(Interface.MessageType.Warning, false, "An early time was defined, but no message was specified in MarkerXML " + fileName); } } else { if (EarlyText != null) { m.MessageEarlyTime = EarlyTime; m.MessageEarlyText = EarlyText; m.MessageColor = EarlyColor; } else { Interface.AddMessage(Interface.MessageType.Warning, false, "An early time was defined, but no message was specified in MarkerXML " + fileName); } } } if (LateDefined) { if (iM) { if (LateTexture != null) { t.MessageLateTime = LateTime; t.MessageLateTexture = LateTexture; } else { Interface.AddMessage(Interface.MessageType.Warning, false, "A late time was defined, but no message was specified in MarkerXML " + fileName); } } else { if (LateText != null) { m.MessageLateTime = LateTime; m.MessageLateText = LateText; m.MessageLateColor = LateColor; } else { Interface.AddMessage(Interface.MessageType.Warning, false, "An early time was defined, but no message was specified in MarkerXML " + fileName); } } } //Final on-time message if (iM) { t.Trains = Trains; if (Texture != null) { t.MessageOnTimeTexture = Texture; } } else { m.Trains = Trains; if (Text != null) { m.MessageOnTimeText = Text; m.Color = OnTimeColor; } } if (iM) { marker.Message = t; } else { marker.Message = m; } } } } return(true); }