private void RenderEvents() { if (Program.CurrentRoute.Tracks[0].Elements == null) { return; } GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.DepthTest); GL.DepthMask(true); OptionLighting = false; double da = -Camera.BackwardViewingDistance - Camera.ExtraViewingDistance; double db = Camera.ForwardViewingDistance + Camera.ExtraViewingDistance; bool[] sta = new bool[Program.CurrentRoute.Stations.Length]; // events for (int i = 0; i < Program.CurrentRoute.Tracks[0].Elements.Length; i++) { double p = Program.CurrentRoute.Tracks[0].Elements[i].StartingTrackPosition; double d = p - CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { foreach (GeneralEvent e in Program.CurrentRoute.Tracks[0].Elements[i].Events) { double dy, dx = 0.0, dz = 0.0; double s; Texture t; if (e is BrightnessChangeEvent) { s = 0.15; dy = 4.0; t = BrightnessChangeTexture; } else if (e is BackgroundChangeEvent) { s = 0.25; dy = 3.5; t = BackgroundChangeTexture; } else if (e is StationStartEvent) { s = 0.25; dy = 1.6; t = StationStartTexture; StationStartEvent f = (StationStartEvent)e; sta[f.StationIndex] = true; } else if (e is StationEndEvent) { s = 0.25; dy = 1.6; t = StationEndTexture; StationEndEvent f = (StationEndEvent)e; sta[f.StationIndex] = true; } else if (e is LimitChangeEvent) { s = 0.2; dy = 1.1; t = LimitTexture; } else if (e is SectionChangeEvent) { s = 0.2; dy = 0.8; t = SectionTexture; } else if (e is TransponderEvent) { s = 0.15; dy = 0.4; t = TransponderTexture; } else if (e is SoundEvent) { SoundEvent f = (SoundEvent)e; s = 0.2; dx = f.Position.X; dy = f.Position.Y < 0.1 ? 0.1 : f.Position.Y; dz = f.Position.Z; t = f.SoundBuffer == null ? PointSoundTexture : SoundTexture; } else if (e is RailSoundsChangeEvent) { s = 0.2; dy = 0.8; t = RunSoundTexture; } else { s = 0.2; dy = 1.0; t = null; } if (t != null) { TrackFollower f = new TrackFollower(Program.CurrentHost) { TriggerType = EventTriggerType.None, TrackPosition = p }; f.UpdateAbsolute(p + e.TrackPositionDelta, true, false); f.WorldPosition.X += dx * f.WorldSide.X + dy * f.WorldUp.X + dz * f.WorldDirection.X; f.WorldPosition.Y += dx * f.WorldSide.Y + dy * f.WorldUp.Y + dz * f.WorldDirection.Y; f.WorldPosition.Z += dx * f.WorldSide.Z + dy * f.WorldUp.Z + dz * f.WorldDirection.Z; Cube.Draw(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera.AbsolutePosition, t); } } } } // stops for (int i = 0; i < sta.Length; i++) { if (sta[i]) { for (int j = 0; j < Program.CurrentRoute.Stations[i].Stops.Length; j++) { const double dy = 1.4; const double s = 0.2; double p = Program.CurrentRoute.Stations[i].Stops[j].TrackPosition; TrackFollower f = new TrackFollower(Program.CurrentHost) { TriggerType = EventTriggerType.None, TrackPosition = p }; f.UpdateAbsolute(p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; Cube.Draw(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera.AbsolutePosition, StopTexture); } } } // buffers foreach (double p in Program.CurrentRoute.BufferTrackPositions) { double d = p - CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { const double dy = 2.5; const double s = 0.25; TrackFollower f = new TrackFollower(Program.CurrentHost) { TriggerType = EventTriggerType.None, TrackPosition = p }; f.UpdateAbsolute(p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; Cube.Draw(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera.AbsolutePosition, BufferTexture); } } OptionLighting = true; }
// // CREATE ROUTE MAP // /// <summary>Creates and returns the route map as Bitmap.</summary> /// <returns>The route map.</returns> /// <param name="Width">The width of the bitmap to create.</param> /// <param name="Height">The height of the bitmap to create.</param> /// <param name="inGame"><c>true</c> = bitmap for in-game overlay | <c>false</c> = for standard window.</param> internal static Bitmap CreateRouteMap(int Width, int Height, bool inGame) { int n, n0, n1; RouteRange(out n, out n0, out n1); // find dimensions double x0 = double.PositiveInfinity, z0 = double.PositiveInfinity; double x1 = double.NegativeInfinity, z1 = double.NegativeInfinity; for (int i = n0; i <= n1; i++) { double x = Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.X; double z = Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Z; if (x < x0) { x0 = x; } if (x > x1) { x1 = x; } if (z < z0) { z0 = z; } if (z > z1) { z1 = z; } } // avoid 0 or negative height or width if (x0 >= x1 - 1.0) { x0 = x1 - 1.0; } if (z0 >= z1 - 1.0) { z0 = z1 - 1.0; } // remember area occupied so far double xMin, xMax, zMin, zMax; // used to track the bitmap area actually occupied by drawings xMin = x0; xMax = x1; zMin = z1; // bitmap z goes down, while world z goes up zMax = z0; // fit route w/h ratio in image w/h ratio double wrh = (double)Width / (double)Height; if ((x1 - x0) / (z1 - z0) <= wrh) // if route ratio is taller than bitmap ratio { double dx = 0.5 * (z1 - z0) * wrh; // scale (half of) x range as much as (half of) z range double px = 0.5 * (x0 + x1); // x range mid point x0 = px - dx; // centre scaled x range around mid point x1 = px + dx; } else // if route ratio is wider than bitmap ratio { // do the opposite (scale z range on x) double dz = 0.5 * (x1 - x0) / wrh; double pz = 0.5 * (z0 + z1); z0 = pz - dz; z1 = pz + dz; } double ox = LeftPad, oy = TopPad; double w = (double)(Width - (LeftPad + RightPad)); double h = (double)(Height - (TopPad + BottomPad)); // horizontal and vertical scales double xd = w / (x1 - x0); double zd = h / (z1 - z0); // convert bitmap occupied area from world to bitmap coordinates xMin = ox + (xMin - x0) * xd; xMax = ox + (xMax - x0) * xd; zMin = oy + (z0 - zMin) * zd + h; zMax = oy + (z0 - zMax) * zd + h; // create bitmap int mode = inGame ? 1 : 0; Bitmap b = new Bitmap(Width, Height, inGame ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb); System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(mapColors[mode].background); // ROUTE PATH { int start = 0; bool atc = false; n = n1 - n0 + 1; PointF[] p = new PointF[n]; for (int i = 0; i < n; i++) { double x = Program.CurrentRoute.Tracks[0].Elements[i + n0].WorldPosition.X; double z = Program.CurrentRoute.Tracks[0].Elements[i + n0].WorldPosition.Z; x = ox + (x - x0) * xd; z = oy + (z0 - z) * zd + h; p[i] = new PointF((float)x, (float)z); // ATS / ATC // for each track element, look for a StationStartEvent for (int j = 0; j < Program.CurrentRoute.Tracks[0].Elements[i + n0].Events.Length; j++) { if (Program.CurrentRoute.Tracks[0].Elements[i + n0].Events[j] is StationStartEvent) { StationStartEvent e = (StationStartEvent)Program.CurrentRoute.Tracks[0].Elements[i + n0].Events[j]; // if StationStartEvent found, look for a change in ATS/ATC control; // if there is a change, draw all previous track elements // with colour for the previous control state if (Program.CurrentRoute.Stations[e.StationIndex].SafetySystem == SafetySystem.Atc) { if (!atc) { atc = true; if (i - start - 1 > 0) { g.DrawCurve(mapColors[mode].normalMap, p, start, i - start - 1); } start = i; } } else { if (atc) { atc = false; if (i - start - 1 > 0) { g.DrawCurve(mapColors[mode].atcMap, p, start, i - start - 1); } start = i; } } break; } } } // draw all remaining track element not drawn yet DrawSegmentedCurve(g, atc ? mapColors[mode].atcMap : mapColors[mode].normalMap, p, start, n - start - 1); } // STATION ICONS for (int i = n0; i <= n1; i++) { for (int j = 0; j < Program.CurrentRoute.Tracks[0].Elements[i].Events.Length; j++) { if (Program.CurrentRoute.Tracks[0].Elements[i].Events[j] is StationStartEvent) { StationStartEvent e = (StationStartEvent)Program.CurrentRoute.Tracks[0].Elements[i].Events[j]; if (Program.CurrentRoute.Stations[e.StationIndex].Name != string.Empty) { double x = Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.X; double y = Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Z; x = ox + (x - x0) * xd; y = oy + (z0 - y) * zd + h; // station circle RectangleF r = new RectangleF((float)x - StationRadius, (float)y - StationRadius, StationDiameter, StationDiameter); bool q = Program.CurrentRoute.Stations[e.StationIndex].PlayerStops(); g.FillEllipse(q ? mapColors[mode].actStatnFill : mapColors[mode].inactStatnFill, r); g.DrawEllipse(q ? mapColors[mode].actStatnBrdr : mapColors[mode].inactStatnBrdr, r); // adjust bitmap occupied area if (r.Left < xMin) { xMin = r.Left; } if (r.Top < zMin) { zMin = r.Top; } if (r.Right > xMax) { xMax = r.Right; } if (r.Bottom > zMax) { zMax = r.Bottom; } } } } } // STATION NAMES { double wh = w * h; Font f = new Font(FontFamily.GenericSansSerif, wh < 65536.0 ? 9.0f : 10.0f, GraphicsUnit.Pixel); for (int i = n0; i <= n1; i++) { for (int j = 0; j < Program.CurrentRoute.Tracks[0].Elements[i].Events.Length; j++) { if (Program.CurrentRoute.Tracks[0].Elements[i].Events[j] is StationStartEvent) { StationStartEvent e = (StationStartEvent)Program.CurrentRoute.Tracks[0].Elements[i].Events[j]; if (Program.CurrentRoute.Stations[e.StationIndex].Name != string.Empty) { double x = Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.X; double y = Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Z; x = ox + (x - x0) * xd; y = oy + (z0 - y) * zd + h; bool stop = Program.CurrentRoute.Stations[e.StationIndex].PlayerStops(); string t = Program.CurrentRoute.Stations[e.StationIndex].Name; SizeF m = g.MeasureString(t, f, Width, StringFormat.GenericDefault); double sx = Program.CurrentRoute.Tracks[0].Elements[i].WorldSide.X; double sz = Program.CurrentRoute.Tracks[0].Elements[i].WorldSide.Z; double xt, yt; if (Math.Sign(sx) == Math.Sign(sz)) { // descending bool o = (x - ox) * (h - y) <= (w - x) * (y - oy); if (o) { // up-right xt = x + StationTextPad; yt = y - StationTextPad - m.Height; } else { // down-left xt = x - StationTextPad - m.Width; yt = y + StationTextPad; } } else { // ascending bool o = (h - y) * (w - x) <= (x - ox) * (y - oy); if (o) { // up-left xt = x - StationTextPad - m.Width; yt = y - StationTextPad - m.Height; } else { // down-right xt = x + StationTextPad; yt = y + StationTextPad; } } // constrain text within bitmap edges (taking into account also paddings) if (xt < ox) { xt = ox; } else if (xt + m.Width > w) { xt = w - m.Width; } if (yt < oy) { yt = oy; } else if (yt + m.Height > h) { yt = h - m.Height; } RectangleF r = new RectangleF((float)xt - 1.0f, (float)yt - 1.0f, m.Width + 2.0f, m.Height + 2.0f); g.FillRectangle(stop ? mapColors[mode].actNameFill : mapColors[mode].inactNameFill, r.Left, r.Top, r.Width, r.Height); g.DrawRectangle(stop ? mapColors[mode].actNameBrdr : mapColors[mode].inactNameBrdr, r.Left, r.Top, r.Width, r.Height); g.DrawString(t, f, stop ? mapColors[mode].actNameText : mapColors[mode].inactNameText, (float)xt, (float)yt); // adjust bitmap occupied area if (r.Left < xMin) { xMin = r.Left; } if (r.Top < zMin) { zMin = r.Top; } if (r.Right > xMax) { xMax = r.Right; } if (r.Bottom > zMax) { zMax = r.Bottom; } } } } } } // if in-game, trim unused parts of the bitmap if (inGame) { xMin -= LeftPad; xMax += RightPad; zMin -= TopPad; zMax += BottomPad; if (xMin < 0) { xMin = 0; } if (xMax >= Width) { xMax = Width - 1; } if (zMin < 0) { zMin = 0; } if (zMax >= Height) { zMax = Height - 1; } Bitmap nb = new Bitmap((int)(xMax - xMin + 1.0), (int)(zMax - zMin + 1.0)); // round up g = System.Drawing.Graphics.FromImage(nb); g.DrawImage(b, (int)-xMin, (int)-zMin); // round down // set total bitmap world X and Z ranges from bitmap ranges lastRouteMinX = (int)((xMin - ox) / xd + x0); lastRouteMaxX = (int)((xMax - ox) / xd + x0); lastRouteMinZ = (int)(z0 - (zMax - oy - h) / zd); lastRouteMaxZ = (int)(z0 - (zMin - oy - h) / zd); return(nb); } //set total bitmap X and Z ranges lastRouteMinX = (int)(x0 - ox * (x1 - x0) / w); lastRouteMaxX = (int)(x1 + (Width - w - ox) * (x1 - x0) / w); lastRouteMinZ = (int)(z0 - oy * (z1 - z0) / h); lastRouteMaxZ = (int)(z1 + (Height - h - oy) * (z1 - z0) / h); return(b); }
// // CREATE GRADIENT PROFILE // /// <summary>Creates the route gradient profile.</summary> /// <returns>The route gradient profile as Bitmap.</returns> /// <param name="Width">The width of the bitmap to create.</param> /// <param name="Height">The height of the bitmap to create.</param> /// <param name="inGame"><c>true</c> = bitmap for in-game overlay | <c>false</c> = for standard window.</param> internal static Bitmap CreateRouteGradientProfile(int Width, int Height, bool inGame) { if (Program.CurrentRoute.Tracks[0].Elements.Length > 36 && Program.CurrentRoute.Stations.Length == 0) { // If we have track elements, but no stations, show a specific error message, rather // than the more generic one thrown later // NOTE: Will throw the generic error message on routes shorter than 900m with no stations throw new InvalidDataException(Translations.GetInterfaceString("errors_route_corrupt_nostations")); } // Track elements are assumed to be all of the same length, and this length // is used as measure unit, rather than computing the incremental track length // in any 'real world' unit (like m). // HORIZONTAL RANGE: find first and last used element based on stations int n, n0, n1; RouteRange(out n, out n0, out n1); // VERTICAL RANGE double y0 = double.PositiveInfinity, y1 = double.NegativeInfinity; for (int i = n0; i <= n1; i++) { double y = Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Y; if (y < y0) { y0 = y; } if (y > y1) { y1 = y; } } if (y0 >= y1 - 1.0) { y0 = y1 - 1.0; } // allow for some padding around actual data double ox = LeftPad, oy = TopPad; double w = (double)(Width - (LeftPad + RightPad)); double h = (double)(Height - (TopPad + BottomPad + TrackOffsPad)); // horizontal and vertical scale double nd = w / (double)(n1 - n0); double yd = h / (double)(y1 - y0); // set total bitmap track position range; used by in-game profile to place // the current position of the trains; as the train positions are known as track positions, // actual track positions are needed here, rather than indices into the track element array. double minX = Program.CurrentRoute.Tracks[0].Elements[n0].StartingTrackPosition; double maxX = Program.CurrentRoute.Tracks[0].Elements[n1].StartingTrackPosition; double offX = ox * (maxX - minX) / w; lastGradientMinTrack = (int)(minX - offX); lastGradientMaxTrack = (int)(maxX + offX); // BITMAP (in-game display needs transparency) Bitmap b = new Bitmap(Width, Height, inGame ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb); System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; int mode = inGame ? 1 : 0; g.Clear(mapColors[mode].background); // BELOW SEA LEVEL { double y = oy + (h - 0.5 * (double)(-Program.CurrentRoute.Atmosphere.InitialElevation - y0) * yd); double x0 = ox - (double)(0) * nd; double x1 = ox + (double)(n1 - n0) * nd; g.FillRectangle(mapColors[mode].belowSeaFill, (float)x0, (float)y, (float)x1, (float)(oy + h) - (float)y); g.DrawLine(mapColors[mode].belowSeaBrdr, (float)x0, (float)y, (float)x1, (float)y); } // GRADIENT PROFILE { n = n1 - n0 + 1; PointF[] p = new PointF[n + 2]; p[0] = new PointF((float)ox, (float)(oy + h)); for (int i = n0; i <= n1; i++) { double x = ox + (double)(i - n0) * nd; double y = oy + (h - 0.5 * (double)(Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Y - y0) * yd); p[i - n0 + 1] = new PointF((float)x, (float)y); } p[n + 1] = new PointF((float)(ox + (double)(n - 1) * nd), (float)(oy + h)); g.FillPolygon(mapColors[mode].elevFill, p); for (int i = 1; i < n; i++) { g.DrawLine(mapColors[mode].elevBrdr, p[i], p[i + 1]); } g.DrawLine(mapColors[mode].elevBrdr, 0.0f, (float)(oy + h), (float)Width, (float)(oy + h)); } // STATION NAMES { Font f = new Font(FontFamily.GenericSansSerif, 12.0f, GraphicsUnit.Pixel); StringFormat m = new StringFormat(); for (int i = n0; i <= n1; i++) { for (int j = 0; j < Program.CurrentRoute.Tracks[0].Elements[i].Events.Length; j++) { if (Program.CurrentRoute.Tracks[0].Elements[i].Events[j] is StationStartEvent) { StationStartEvent e = (StationStartEvent)Program.CurrentRoute.Tracks[0].Elements[i].Events[j]; if (Program.CurrentRoute.Stations[e.StationIndex].Name != string.Empty) { bool stop = Program.CurrentRoute.Stations[e.StationIndex].PlayerStops(); if (Program.CurrentRoute.Stations[e.StationIndex].Name.IsJapanese()) { m.Alignment = StringAlignment.Near; m.LineAlignment = StringAlignment.Near; double x = ox + (double)(i - n0) * nd; double y = oy + (h - 0.5 * (double)(Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Y - y0) * yd); string t = Program.CurrentRoute.Stations[e.StationIndex].Name; float tx = 0.0f, ty = (float)oy; for (int k = 0; k < t.Length; k++) { SizeF s = g.MeasureString(t.Substring(k, 1), f, 65536, StringFormat.GenericTypographic); if (s.Width > tx) { tx = s.Width; } } for (int k = 0; k < t.Length; k++) { g.DrawString(t.Substring(k, 1), f, stop ? mapColors[mode].actNameText : mapColors[mode].inactNameText, (float)x - 0.5f * tx, ty); SizeF s = g.MeasureString(t.Substring(k, 1), f, 65536, StringFormat.GenericTypographic); ty += s.Height; } g.DrawLine(stop ? mapColors[mode].actNameBrdr : mapColors[mode].inactNameBrdr, new PointF((float)x, ty + 4.0f), new PointF((float)x, (float)y)); } else { m.Alignment = StringAlignment.Far; m.LineAlignment = StringAlignment.Near; double x = ox + (double)(i - n0) * nd; double y = oy + (h - 0.5 * (double)(Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Y - y0) * yd); g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)oy, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(Program.CurrentRoute.Stations[e.StationIndex].Name, f, stop ? mapColors[mode].actNameText : mapColors[mode].inactNameText, new PointF(0.0f, -5.0f), m); g.ResetTransform(); SizeF s = g.MeasureString(Program.CurrentRoute.Stations[e.StationIndex].Name, f); g.DrawLine(stop ? mapColors[mode].actNameBrdr : mapColors[mode].inactNameBrdr, new PointF((float)x, (float)(oy + s.Width + 4)), new PointF((float)x, (float)y)); } } } } } } // ROUTE MARKERS { Font f = new Font(FontFamily.GenericSansSerif, 10.0f, GraphicsUnit.Pixel); Font fs = new Font(FontFamily.GenericSansSerif, 9.0f, GraphicsUnit.Pixel); StringFormat m = new StringFormat { Alignment = StringAlignment.Far, LineAlignment = StringAlignment.Center }; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int k = TrackOffDist * n / Width; if (k == 0) { if (!inGame) { //If k is equal to zero, this generally means that the WithTrack section is missing from our routefile //Adding zero to the loop control variable will also produce an infinite loop, so that's a bad idea too throw new InvalidDataException(Translations.GetInterfaceString("errors_route_corrupt_withtrack")); } /* * A route with a single station can somehow sometimes work OK in preview but not in-game * Whilst the routefile is probably broken don't chuck the exception here as it takes down the loader */ k = 1; } for (int i = n0; i <= n1; i += k) { double x = ox + (double)(i - n0) * nd; double y = (double)(Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Y - y0) * yd; // track offset label if (x < w) { string t = ((int)Math.Round(Program.CurrentRoute.Tracks[0].Elements[i].StartingTrackPosition)).ToString(Culture); g.DrawString(t + "m", f, mapColors[mode].actNameText, (float)x, (float)(oy + h + TrackOffY)); } // route height at track offset (with measure and vertical line) { y = oy + (h - 0.5 * y) + 2.0f; string t = ((int)Math.Round(Program.CurrentRoute.Atmosphere.InitialElevation + Program.CurrentRoute.Tracks[0].Elements[i].WorldPosition.Y)).ToString(Culture); SizeF s = g.MeasureString(t, fs); if (y < oy + h - (double)s.Width - 10.0) { g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)y + 4.0f, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(t + "m", fs, mapColors[mode].actNameText, 0.0f, 0.0f, m); g.ResetTransform(); g.DrawLine(mapColors[mode].inactNameBrdr, (float)x, (float)(y + s.Width + 12.0), (float)x, (float)(oy + h)); } } } } // finalize return(b); }
/// <summary>Collects the timetable data for the current route</summary> internal void CollectData() { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; Stations = new Station[16]; Tracks = new Track[16]; int n = 0; double Limit = -1.0, LastLimit = 6.94444444444444; int LastArrivalHours = -1, LastDepartureHours = -1; double LastTime = -1.0; for (int i = 0; i < Program.CurrentRoute.Tracks[0].Elements.Length; i++) { for (int j = 0; j < Program.CurrentRoute.Tracks[0].Elements[i].Events.Length; j++) { StationStartEvent sse = Program.CurrentRoute.Tracks[0].Elements[i].Events[j] as StationStartEvent; if (sse != null && Program.CurrentRoute.Stations[sse.StationIndex].Name != string.Empty) { if (Limit == -1.0) { Limit = LastLimit; } // update station if (n == Stations.Length) { Array.Resize <Station>(ref Stations, Stations.Length << 1); } Stations[n].Name = Program.CurrentRoute.Stations[sse.StationIndex].Name; Stations[n].NameJapanese = Program.CurrentRoute.Stations[sse.StationIndex].Name.IsJapanese(); Stations[n].Pass = !Program.CurrentRoute.Stations[sse.StationIndex].PlayerStops(); Stations[n].Terminal = Program.CurrentRoute.Stations[sse.StationIndex].Type != StationType.Normal; double x; if (Program.CurrentRoute.Stations[sse.StationIndex].ArrivalTime >= 0.0) { x = Program.CurrentRoute.Stations[sse.StationIndex].ArrivalTime; x -= 86400.0 * Math.Floor(x / 86400.0); int hours = (int)Math.Floor(x / 3600.0); x -= 3600.0 * (double)hours; int minutes = (int)Math.Floor(x / 60.0); x -= 60.0 * (double)minutes; int seconds = (int)Math.Floor(x); Stations[n].Arrival.Hour = hours != LastArrivalHours?hours.ToString("00", Culture) : ""; Stations[n].Arrival.Minute = minutes.ToString("00", Culture); Stations[n].Arrival.Second = seconds.ToString("00", Culture); LastArrivalHours = hours; } else { Stations[n].Arrival.Hour = ""; Stations[n].Arrival.Minute = ""; Stations[n].Arrival.Second = ""; } if (Program.CurrentRoute.Stations[sse.StationIndex].DepartureTime >= 0.0) { x = Program.CurrentRoute.Stations[sse.StationIndex].DepartureTime; x -= 86400.0 * Math.Floor(x / 86400.0); int hours = (int)Math.Floor(x / 3600.0); x -= 3600.0 * (double)hours; int minutes = (int)Math.Floor(x / 60.0); x -= 60.0 * (double)minutes; int seconds = (int)Math.Floor(x); Stations[n].Departure.Hour = hours != LastDepartureHours?hours.ToString("00", Culture) : ""; Stations[n].Departure.Minute = minutes.ToString("00", Culture); Stations[n].Departure.Second = seconds.ToString("00", Culture); LastDepartureHours = hours; } else { Stations[n].Departure.Hour = ""; Stations[n].Departure.Minute = ""; Stations[n].Departure.Second = ""; } // update track if (n >= 1) { int m = n - 1; if (m == Tracks.Length) { Array.Resize <Track>(ref Tracks, Tracks.Length << 1); } // speed x = Math.Round(3.6 * Limit); Tracks[m].Speed = x.ToString(Culture); // time if (LastTime >= 0.0) { if (Program.CurrentRoute.Stations[sse.StationIndex].ArrivalTime >= 0.0) { x = Program.CurrentRoute.Stations[sse.StationIndex].ArrivalTime; } else if (Program.CurrentRoute.Stations[sse.StationIndex].DepartureTime >= 0.0) { x = Program.CurrentRoute.Stations[sse.StationIndex].DepartureTime; } else { x = -1.0; } if (x >= 0.0) { x -= LastTime; int hours = (int)Math.Floor(x / 3600.0); x -= 3600.0 * (double)hours; int minutes = (int)Math.Floor(x / 60.0); x -= 60.0 * (double)minutes; int seconds = (int)Math.Floor(x); Tracks[m].Time.Hour = hours != 0 ? hours.ToString("0", Culture) : ""; Tracks[m].Time.Minute = minutes != 0 ? minutes.ToString("00", Culture) : ""; Tracks[m].Time.Second = seconds != 0 ? seconds.ToString("00", Culture) : ""; } else { Tracks[m].Time.Hour = ""; Tracks[m].Time.Minute = ""; Tracks[m].Time.Second = ""; } } else { Tracks[m].Time.Hour = ""; Tracks[m].Time.Minute = ""; Tracks[m].Time.Second = ""; } } // update last data if (Program.CurrentRoute.Stations[sse.StationIndex].DepartureTime >= 0.0) { LastTime = Program.CurrentRoute.Stations[sse.StationIndex].DepartureTime; } else if (Program.CurrentRoute.Stations[sse.StationIndex].ArrivalTime >= 0.0) { LastTime = Program.CurrentRoute.Stations[sse.StationIndex].ArrivalTime; } else { LastTime = -1.0; } LastLimit = Limit; Limit = -1.0; n++; } if (n >= 1) { LimitChangeEvent lce = Program.CurrentRoute.Tracks[0].Elements[i].Events[j] as LimitChangeEvent; if (lce != null) { if (lce.NextSpeedLimit != double.PositiveInfinity & lce.NextSpeedLimit > Limit) { Limit = lce.NextSpeedLimit; } } } } } Array.Resize <Station>(ref Stations, n); if (n >= 2) { Array.Resize <Track>(ref Tracks, n - 1); } else { Tracks = new Track[] { }; } }