// // 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 = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double z = TrackManager.CurrentTrack.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); Graphics g = 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 = TrackManager.CurrentTrack.Elements[i + n0].WorldPosition.X; double z = TrackManager.CurrentTrack.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 < TrackManager.CurrentTrack.Elements[i + n0].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i + n0].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.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 (Game.Stations[e.StationIndex].SafetySystem == Game.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 < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double y = TrackManager.CurrentTrack.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 = Game.PlayerStopsAtStation(e.StationIndex); 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 < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; x = ox + (x - x0) * xd; y = oy + (z0 - y) * zd + h; RectangleF r = new RectangleF((float)x - StationRadius, (float)y - StationRadius, StationDiameter, StationDiameter); bool stop = Game.PlayerStopsAtStation(e.StationIndex); string t = Game.Stations[e.StationIndex].Name; SizeF m = g.MeasureString(t, f, Width, StringFormat.GenericDefault); double sx = TrackManager.CurrentTrack.Elements[i].WorldSide.X; double sz = TrackManager.CurrentTrack.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; } 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 = 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 (TrackManager.CurrentTrack.Elements.Length > 36 && Game.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 Exception(Interface.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 = TrackManager.CurrentTrack.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 = TrackManager.CurrentTrack.Elements[n0].StartingTrackPosition; double maxX = TrackManager.CurrentTrack.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); Graphics g = 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)(-Game.RouteInitialElevation - 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)(TrackManager.CurrentTrack.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 < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { bool stop = Game.PlayerStopsAtStation(e.StationIndex); if (Interface.IsJapanese(Game.Stations[e.StationIndex].Name)) { m.Alignment = StringAlignment.Near; m.LineAlignment = StringAlignment.Near; double x = ox + (double)(i - n0) * nd; double y = oy + (h - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); string t = Game.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)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)oy, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(Game.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(Game.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 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 Exception(Interface.GetInterfaceString("errors_route_corrupt_withtrack")); } for (int i = n0; i <= n1; i += k) { double x = ox + (double)(i - n0) * nd; double y = (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd; // track offset label if (x < w) { string t = ((int)Math.Round(TrackManager.CurrentTrack.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(Game.RouteInitialElevation + TrackManager.CurrentTrack.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); }
// collect data internal static void CollectData(out Table Table) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; Table.Stations = new Station[16]; Table.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 < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { TrackManager.StationStartEvent sse = TrackManager.CurrentTrack.Elements[i].Events[j] as TrackManager.StationStartEvent; if (sse != null && Game.Stations[sse.StationIndex].Name != string.Empty) { if (Limit == -1.0) { Limit = LastLimit; } // update station if (n == Table.Stations.Length) { Array.Resize <Station>(ref Table.Stations, Table.Stations.Length << 1); } Table.Stations[n].Name = Game.Stations[sse.StationIndex].Name; Table.Stations[n].NameJapanese = Game.Stations[sse.StationIndex].Name.IsJapanese(); Table.Stations[n].Pass = !Game.PlayerStopsAtStation(sse.StationIndex); Table.Stations[n].Terminal = Game.Stations[sse.StationIndex].Type != StationType.Normal; double x; if (Game.Stations[sse.StationIndex].ArrivalTime >= 0.0) { x = Game.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); Table.Stations[n].Arrival.Hour = hours != LastArrivalHours?hours.ToString("00", Culture) : ""; Table.Stations[n].Arrival.Minute = minutes.ToString("00", Culture); Table.Stations[n].Arrival.Second = seconds.ToString("00", Culture); LastArrivalHours = hours; } else { Table.Stations[n].Arrival.Hour = ""; Table.Stations[n].Arrival.Minute = ""; Table.Stations[n].Arrival.Second = ""; } if (Game.Stations[sse.StationIndex].DepartureTime >= 0.0) { x = Game.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); Table.Stations[n].Departure.Hour = hours != LastDepartureHours?hours.ToString("00", Culture) : ""; Table.Stations[n].Departure.Minute = minutes.ToString("00", Culture); Table.Stations[n].Departure.Second = seconds.ToString("00", Culture); LastDepartureHours = hours; } else { Table.Stations[n].Departure.Hour = ""; Table.Stations[n].Departure.Minute = ""; Table.Stations[n].Departure.Second = ""; } // update track if (n >= 1) { int m = n - 1; if (m == Table.Tracks.Length) { Array.Resize <Track>(ref Table.Tracks, Table.Tracks.Length << 1); } // speed x = Math.Round(3.6 * Limit); Table.Tracks[m].Speed = x.ToString(Culture); // time if (LastTime >= 0.0) { if (Game.Stations[sse.StationIndex].ArrivalTime >= 0.0) { x = Game.Stations[sse.StationIndex].ArrivalTime; } else if (Game.Stations[sse.StationIndex].DepartureTime >= 0.0) { x = Game.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); Table.Tracks[m].Time.Hour = hours != 0 ? hours.ToString("0", Culture) : ""; Table.Tracks[m].Time.Minute = minutes != 0 ? minutes.ToString("00", Culture) : ""; Table.Tracks[m].Time.Second = seconds != 0 ? seconds.ToString("00", Culture) : ""; } else { Table.Tracks[m].Time.Hour = ""; Table.Tracks[m].Time.Minute = ""; Table.Tracks[m].Time.Second = ""; } } else { Table.Tracks[m].Time.Hour = ""; Table.Tracks[m].Time.Minute = ""; Table.Tracks[m].Time.Second = ""; } } // update last data if (Game.Stations[sse.StationIndex].DepartureTime >= 0.0) { LastTime = Game.Stations[sse.StationIndex].DepartureTime; } else if (Game.Stations[sse.StationIndex].ArrivalTime >= 0.0) { LastTime = Game.Stations[sse.StationIndex].ArrivalTime; } else { LastTime = -1.0; } LastLimit = Limit; Limit = -1.0; n++; } if (n >= 1) { TrackManager.LimitChangeEvent lce = TrackManager.CurrentTrack.Elements[i].Events[j] as TrackManager.LimitChangeEvent; if (lce != null) { if (lce.NextSpeedLimit != double.PositiveInfinity & lce.NextSpeedLimit > Limit) { Limit = lce.NextSpeedLimit; } } } } } Array.Resize <Station>(ref Table.Stations, n); if (n >= 2) { Array.Resize <Track>(ref Table.Tracks, n - 1); } else { Table.Tracks = new Track[] { }; } }
private void PerformDefault() { if (Train.Derailed) { if (Train.Handles.EmergencyBrake.Driver != true) { Train.ApplyEmergencyBrake(); } return; } // personality double spd = Train.CurrentSpeed; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { if (Train.Station != this.LastStation) { this.LastStation = Train.Station; double time; if (Stations[Train.Station].ArrivalTime >= 0.0) { time = Stations[Train.Station].ArrivalTime - Train.TimetableDelta; } else if (Stations[Train.Station].DepartureTime >= 0.0) { time = Stations[Train.Station].DepartureTime - Train.TimetableDelta; if (time > SecondsSinceMidnight) { time -= Stations[Train.Station].StopTime; if (time > SecondsSinceMidnight) { time = double.MinValue; } } } else { time = double.MinValue; } if (time != double.MinValue) { const double largeThreshold = 30.0; const double largeChangeFactor = 0.0025; const double smallThreshold = 15.0; const double smallChange = 0.05; double diff = SecondsSinceMidnight - time; if (diff < -largeThreshold) { /* The AI is too fast. Decrease the preferred speed. */ this.CurrentSpeedFactor -= largeChangeFactor * (-diff - largeThreshold); if (this.CurrentSpeedFactor < 0.7) { this.CurrentSpeedFactor = 0.7; } } else if (diff > largeThreshold) { /* The AI is too slow. Increase the preferred speed. */ this.CurrentSpeedFactor += largeChangeFactor * (diff - largeThreshold); if (this.CurrentSpeedFactor > 1.1) { this.CurrentSpeedFactor = 1.1; } } else if (Math.Abs(diff) < smallThreshold) { /* The AI is at about the right speed. Change the preferred speed toward the personality default. */ if (this.CurrentSpeedFactor < this.PersonalitySpeedFactor) { this.CurrentSpeedFactor += smallChange; if (this.CurrentSpeedFactor > this.PersonalitySpeedFactor) { this.CurrentSpeedFactor = this.PersonalitySpeedFactor; } } else if (this.CurrentSpeedFactor > this.PersonalitySpeedFactor) { this.CurrentSpeedFactor -= smallChange; if (this.CurrentSpeedFactor < this.PersonalitySpeedFactor) { this.CurrentSpeedFactor = this.PersonalitySpeedFactor; } } } } } } // door states bool doorsopen = false; for (int i = 0; i < Train.Cars.Length; i++) { for (int j = 0; j < Train.Cars[i].Doors.Length; j++) { if (Train.Cars[i].Doors[j].State != 0.0) { doorsopen = true; break; } if (doorsopen) { break; } } } // do the ai Train.Specs.CurrentConstSpeed = false; Train.ApplyHoldBrake(false); int stopIndex = Train.Station >= 0 ? Game.Stations[Train.Station].GetStopIndex(Train.Cars.Length) : -1; if (Train.CurrentSectionLimit == 0.0) { // passing red signal Train.ApplyEmergencyBrake(); Train.ApplyNotch(-1, true, 1, true); CurrentInterval = 0.5; } else if (doorsopen | Train.StationState == TrainManager.TrainStopState.Boarding) { // door opened or boarding at station this.PowerNotchAtWhichWheelSlipIsObserved = Train.Handles.Power.MaximumNotch + 1; if (Train.Station >= 0 && Stations[Train.Station].Type != StationType.Normal && Stations[Train.Station].Type != StationType.RequestStop && Train == TrainManager.PlayerTrain) { // player's terminal station if (Train.Plugin == null || Train.Plugin.LastReverser == -2) { Train.ApplyReverser(0, false); } Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); CurrentInterval = 1.0; } else { CurrentInterval = 1.0; Train.ApplyNotch(-1, true, 0, true); if (Train.Handles.Brake is TrainManager.AirBrakeHandle) { if (Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.CurrentPressure < 0.3 * Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.ServiceMaximumPressure) { Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); } else if (Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.CurrentPressure > 0.9 * Train.Cars[Train.DriverCar].CarBrake.brakeCylinder.EmergencyMaximumPressure) { Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); } else { Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Lap); } } else { int b; if (Math.Abs(spd) < 0.02) { b = (int)Math.Ceiling(0.5 * (double)Train.Handles.Brake.MaximumNotch); CurrentInterval = 0.3; } else { b = Train.Handles.Brake.MaximumNotch; } if (Train.Handles.Brake.Driver < b) { Train.ApplyNotch(0, true, 1, true); } else if (Train.Handles.Brake.Driver > b) { Train.ApplyNotch(0, true, -1, true); } } Train.UnapplyEmergencyBrake(); if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Completed) { // ready for departure - close doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic && Train.Specs.DoorInterlockState == DoorInterlockStates.Unlocked) { TrainManager.CloseTrainDoors(Train, true, true); } } else if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Boarding) { } else { // not at station - close doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic && Train.Specs.DoorInterlockState == DoorInterlockStates.Unlocked) { TrainManager.CloseTrainDoors(Train, true, true); } } } } else if (Train.Station >= 0 && stopIndex >= 0 && Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].BackwardTolerance && (StopsAtStation(Train.Station, Train) & (Stations[Train.Station].OpenLeftDoors | Stations[Train.Station].OpenRightDoors) & Math.Abs(Train.CurrentSpeed) < 0.25 & Train.StationState == TrainManager.TrainStopState.Pending)) { // arrived at station - open doors if (Train.Specs.DoorOpenMode != TrainManager.DoorMode.Automatic && Train.Specs.DoorInterlockState == DoorInterlockStates.Unlocked) { TrainManager.OpenTrainDoors(Train, Stations[Train.Station].OpenLeftDoors, Stations[Train.Station].OpenRightDoors); } CurrentInterval = 1.0; } else if (Train.Station >= 0 && stopIndex >= 0 && Stations[Train.Station].Type != StationType.Normal && Stations[Train.Station].Type != StationType.RequestStop && Train == TrainManager.PlayerTrain && Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].BackwardTolerance && -Train.StationDistanceToStopPoint < Stations[Train.Station].Stops[stopIndex].ForwardTolerance && Math.Abs(Train.CurrentSpeed) < 0.25) { // player's terminal station (not boarding any longer) if (Train.Plugin != null || Train.Plugin.LastReverser == -2) { Train.ApplyReverser(0, false); } Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); CurrentInterval = 10.0; } else { // drive Train.ApplyReverser(1, false); if (Train.Cars[Train.DriverCar].FrontAxle.CurrentWheelSlip | Train.Cars[Train.DriverCar].RearAxle.CurrentWheelSlip) { // react to wheel slip if (Train.Handles.Power.Driver > 1) { this.PowerNotchAtWhichWheelSlipIsObserved = Train.Handles.Power.Driver; Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); this.CurrentInterval = 2.5; return; } } // initialize double acc = Train.Specs.CurrentAverageAcceleration; double lim = PrecedingTrainSpeedLimit * 1.2; if (Train.CurrentRouteLimit < lim) { lim = Train.CurrentRouteLimit; } if (Train.CurrentSectionLimit < lim) { lim = Train.CurrentSectionLimit; } double powerstart, powerend, brakestart; if (double.IsPositiveInfinity(lim)) { powerstart = lim; powerend = lim; brakestart = lim; } else { lim *= this.CurrentSpeedFactor; if (spd < 8.0) { powerstart = 0.75 * lim; powerend = 0.95 * lim; } else { powerstart = lim - 2.5; powerend = lim - 1.5; } if (this.BrakeMode) { brakestart = powerend; } else { brakestart = lim + 0.5; } } double dec = 0.0; double decelerationCruise; /* power below this deceleration, cruise above */ double decelerationStart; /* brake above this deceleration, cruise below */ double decelerationStep; /* the deceleration step per brake notch */ double BrakeDeceleration = Train.Cars[Train.DriverCar].CarBrake.DecelerationAtServiceMaximumPressure(Train.Handles.Brake.Actual, Train.Cars[Train.DriverCar].Specs.CurrentSpeed); for (int i = 0; i < Train.Cars.Length; i++) { if (Train.Cars[i].Specs.IsMotorCar) { if (Train.Cars[Train.DriverCar].Specs.MotorDeceleration != 0 && Train.Cars[Train.DriverCar].Specs.MotorDeceleration < BrakeDeceleration) { BrakeDeceleration = Train.Cars[Train.DriverCar].Specs.MotorDeceleration; } break; } } if (Train.Handles.Brake is TrainManager.AirBrakeHandle | Train.Handles.Brake.MaximumNotch <= 0) { decelerationCruise = 0.3 * BrakeDeceleration; decelerationStart = 0.5 * BrakeDeceleration; decelerationStep = 0.1 * BrakeDeceleration; } else if (Train.Handles.Brake.MaximumNotch <= 2) { decelerationCruise = 0.2 * BrakeDeceleration; decelerationStart = 0.4 * BrakeDeceleration; decelerationStep = 0.5 * BrakeDeceleration; } else { decelerationCruise = 0.2 * BrakeDeceleration; decelerationStart = 0.5 * BrakeDeceleration; decelerationStep = BrakeDeceleration / (double)Train.Handles.Brake.MaximumNotch; } if (this.CurrentSpeedFactor >= 1.0) { decelerationCruise *= 1.25; decelerationStart *= 1.25; decelerationStep *= 1.25; } if (spd > 0.0 & spd > brakestart) { dec = decelerationStep + 0.1 * (spd - brakestart); } bool reduceDecelerationCruiseAndStart = false; // look ahead double lookahead = (Train.Station >= 0 ? 150.0 : 50.0) + (spd * spd) / (2.0 * decelerationCruise); double tp = Train.FrontCarTrackPosition(); double stopDistance = double.MaxValue; { // next station stop int te = Train.Cars[0].FrontAxle.Follower.LastTrackElement; for (int i = te; i < TrackManager.Tracks[0].Elements.Length; i++) { double stp = TrackManager.Tracks[0].Elements[i].StartingTrackPosition; if (tp + lookahead <= stp) { break; } for (int j = 0; j < TrackManager.Tracks[0].Elements[i].Events.Length; j++) { if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationStartEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.None) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > 0.0 & dist < stopDistance) { stopDistance = dist; } } } } } } } { // events int te = Train.Cars[0].FrontAxle.Follower.LastTrackElement; for (int i = te; i < TrackManager.Tracks[0].Elements.Length; i++) { double stp = TrackManager.Tracks[0].Elements[i].StartingTrackPosition; if (tp + lookahead <= stp) { break; } for (int j = 0; j < TrackManager.Tracks[0].Elements[i].Events.Length; j++) { if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.LimitChangeEvent) { // speed limit TrackManager.LimitChangeEvent e = (TrackManager.LimitChangeEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (e.NextSpeedLimit < spd) { double dist = stp + e.TrackPositionDelta - tp; double edec = (spd * spd - e.NextSpeedLimit * e.NextSpeedLimit * this.CurrentSpeedFactor) / (2.0 * dist); if (edec > dec) { dec = edec; } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.SectionChangeEvent) { // section TrackManager.SectionChangeEvent e = (TrackManager.SectionChangeEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (stp + e.TrackPositionDelta > tp) { if (!CurrentRoute.Sections[e.NextSectionIndex].Invisible & CurrentRoute.Sections[e.NextSectionIndex].CurrentAspect >= 0) { double elim = CurrentRoute.Sections[e.NextSectionIndex].Aspects[CurrentRoute.Sections[e.NextSectionIndex].CurrentAspect].Speed * this.CurrentSpeedFactor; if (elim < spd | spd <= 0.0) { double dist = stp + e.TrackPositionDelta - tp; double edec; if (elim == 0.0) { double redstopdist; if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Completed & dist < 120.0) { dist = 1.0; redstopdist = 25.0; } else if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Pending | stopDistance < dist) { redstopdist = 1.0; } else if (spd > 9.72222222222222) { redstopdist = 55.0; } else { redstopdist = 35.0; } if (dist > redstopdist) { edec = (spd * spd) / (2.0 * (dist - redstopdist)); } else { edec = BrakeDeceleration; } if (dist < 100.0) { reduceDecelerationCruiseAndStart = true; } } else { if (dist >= 1.0) { edec = (spd * spd - elim * elim) / (2.0 * dist); } else { edec = 0.0; } } if (edec > dec) { dec = edec; } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationStartEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.None) { // station start if (Train.Station == -1) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationStartEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.Decelerate) { // Brakes the train when passing through a request stop, which is not to be passed at linespeed if (Train.Station == -1) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } if (dist > 25) { var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.StationEndEvent && Train.NextStopSkipped == TrainManager.StopSkipMode.None) { // station end if (Train.Station == -1) { TrackManager.StationEndEvent e = (TrackManager.StationEndEvent)TrackManager.Tracks[0].Elements[i].Events[j]; if (StopsAtStation(e.StationIndex, Train) & Train.LastStation != e.StationIndex) { int s = Game.Stations[e.StationIndex].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[e.StationIndex].Stops[s].TrackPosition - tp; if (dist > -Stations[e.StationIndex].Stops[s].ForwardTolerance) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } } } } } else if (TrackManager.Tracks[0].Elements[i].Events[j] is TrackManager.TrackEndEvent) { // track end if (Train == TrainManager.PlayerTrain) { TrackManager.TrackEndEvent e = (TrackManager.TrackEndEvent)TrackManager.Tracks[0].Elements[i].Events[j]; double dist = stp + e.TrackPositionDelta - tp; double edec; if (dist >= 15.0) { edec = spd * spd / (2.0 * dist); } else { edec = BrakeDeceleration; } if (edec > dec) { dec = edec; } } } } } } // buffers ahead if (Train == TrainManager.PlayerTrain) { for (int i = 0; i < BufferTrackPositions.Length; i++) { double dist = BufferTrackPositions[i] - tp; if (dist > 0.0) { double edec; if (dist >= 10.0) { edec = spd * spd / (2.0 * dist); } else if (dist >= 5.0) { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); this.CurrentInterval = 0.1; return; } else { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); this.CurrentInterval = 10.0; return; } if (edec > dec) { dec = edec; } } } } // trains ahead for (int i = 0; i < TrainManager.Trains.Length; i++) { if (TrainManager.Trains[i] != Train && TrainManager.Trains[i].State == TrainState.Available) { double pos = TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].RearAxle.Follower.TrackPosition - TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].RearAxle.Position - 0.5 * TrainManager.Trains[i].Cars[TrainManager.Trains[i].Cars.Length - 1].Length; double dist = pos - tp; if (dist > -10.0 & dist < lookahead) { const double minDistance = 10.0; const double maxDistance = 100.0; double edec; if (dist > minDistance) { double shift = 0.75 * minDistance + 1.0 * spd; edec = spd * spd / (2.0 * (dist - shift)); } else if (dist > 0.5 * minDistance) { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); this.CurrentInterval = 0.1; return; } else { Train.ApplyNotch(-1, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); Train.ApplyEmergencyBrake(); this.CurrentInterval = 1.0; return; } if (dist < maxDistance) { reduceDecelerationCruiseAndStart = true; } if (edec > dec) { dec = edec; } } } } Train.UnapplyEmergencyBrake(); // current station if (Train.Station >= 0 & Train.StationState == TrainManager.TrainStopState.Pending) { if (StopsAtStation(Train.Station, Train)) { int s = Game.Stations[Train.Station].GetStopIndex(Train.Cars.Length); if (s >= 0) { double dist = Stations[Train.Station].Stops[s].TrackPosition - tp; if (dist > 0.0) { if (dist < 25.0) { reduceDecelerationCruiseAndStart = true; } else if (this.CurrentSpeedFactor < 1.0) { dist -= 5.0; } var edec = spd * spd / (2.0 * dist); if (edec > dec) { dec = edec; } } else { dec = BrakeDeceleration; } } } } // power / brake if (reduceDecelerationCruiseAndStart) { decelerationCruise *= 0.3; decelerationStart *= 0.3; } double brakeModeBrakeThreshold = 0.75 * decelerationStart + 0.25 * decelerationCruise; if (!BrakeMode & dec > decelerationStart | BrakeMode & dec > brakeModeBrakeThreshold | false) { // brake BrakeMode = true; double decdiff = -acc - dec; if (decdiff < -decelerationStep) { // brake start if (Train.Handles.Power.Driver == 0) { Train.ApplyNotch(0, true, 1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Service); } else { Train.ApplyNotch(-1, true, 0, true); } CurrentInterval *= 0.4; if (CurrentInterval < 0.3) { CurrentInterval = 0.3; } } else if (decdiff > decelerationStep) { // brake stop Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); CurrentInterval *= 0.4; if (CurrentInterval < 0.3) { CurrentInterval = 0.3; } } else { // keep brake Train.ApplyNotch(-1, true, 0, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Lap); CurrentInterval *= 1.2; if (CurrentInterval > 1.0) { CurrentInterval = 1.0; } } if (Train.Handles.Power.Driver == 0 & Train.Handles.Brake.Driver == 0) { Train.ApplyHoldBrake(Train.Handles.HasHoldBrake); } if (Train.Handles.Brake is TrainManager.AirBrakeHandle) { CurrentInterval = 0.1; } } else if (dec > decelerationCruise) { // cut power/brake BrakeMode = false; Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); if (Train.Handles.Power.Driver == 0 & Train.Handles.Brake.Driver == 0) { Train.ApplyHoldBrake(Train.Handles.HasHoldBrake); } CurrentInterval *= 0.4; if (CurrentInterval < 0.3) { CurrentInterval = 0.3; } } else { // power BrakeMode = false; double acclim; if (!double.IsInfinity(lim)) { double d = lim - spd; if (d > 0.0) { acclim = 0.1 / (0.1 * d + 1.0) - 0.12; } else { acclim = -1.0; } } else { acclim = -1.0; } if (spd < powerstart) { // power start (under-speed) if (Train.Handles.Brake.Driver == 0) { if (Train.Handles.Power.Driver < this.PowerNotchAtWhichWheelSlipIsObserved - 1) { Train.ApplyNotch(1, true, 0, true); } } else { Train.ApplyNotch(0, true, -1, true); } Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); if (double.IsPositiveInfinity(powerstart)) { CurrentInterval = 0.3 + 0.1 * Train.Handles.Power.Driver; } else { double p = (double)Train.Handles.Power.Driver / (double)Train.Handles.Power.MaximumNotch; CurrentInterval = 0.3 + 15.0 * p / (powerstart - spd + 1.0); } if (CurrentInterval > 1.3) { CurrentInterval = 1.3; } } else if (spd > powerend) { // power end (over-speed) Train.ApplyNotch(-1, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); CurrentInterval *= 0.3; if (CurrentInterval < 0.2) { CurrentInterval = 0.2; } } else if (acc < acclim) { // power start (under-acceleration) if (Train.Handles.Brake.Driver == 0) { if (Train.Handles.Power.Driver < this.PowerNotchAtWhichWheelSlipIsObserved - 1) { if (Train.Handles.Power.Driver == Train.Handles.Power.Actual) { Train.ApplyNotch(1, true, 0, true); } } } else { Train.ApplyNotch(0, true, -1, true); } Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); CurrentInterval = 1.3; } else { // keep power Train.ApplyNotch(0, true, -1, true); Train.ApplyAirBrakeHandle(TrainManager.AirBrakeHandleState.Release); if (Train.Handles.Power.Driver != 0) { Train.Specs.CurrentConstSpeed = Train.Specs.HasConstSpeed; } if (Train.Handles.Power.Driver == 0 & Train.Handles.Brake.Driver == 0) { Train.ApplyHoldBrake(Train.Handles.HasHoldBrake); } CurrentInterval *= 1.1; if (CurrentInterval > 1.5) { CurrentInterval = 1.5; } } } } }
// create route map internal static Bitmap CreateRouteMap(int Width, int Height) { // find first and last used element based on stations int n = TrackManager.CurrentTrack.Elements.Length; int n0 = n - 1; int n1 = 0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { if (i < n0) { n0 = i; } if (i > n1) { n1 = i; } } } } n0 -= 4; n1 += 8; if (n0 < 0) { n0 = 0; } if (n1 >= TrackManager.CurrentTrack.Elements.Length) { n1 = TrackManager.CurrentTrack.Elements.Length - 1; } if (n1 <= n0) { n1 = n0 + 1; } // 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 = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double z = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; if (x < x0) { x0 = x; } if (x > x1) { x1 = x; } if (z < z0) { z0 = z; } if (z > z1) { z1 = z; } } if (x0 >= x1 - 1.0) { x0 = x1 - 1.0; } if (z0 >= z1 - 1.0) { z0 = z1 - 1.0; } double wrh = (double)Width / (double)Height; if ((x1 - x0) / (z1 - z0) <= wrh) { double dx = 0.5 * (z1 - z0) * wrh; double px = 0.5 * (x0 + x1); x0 = px - dx; x1 = px + dx; } else { double dz = 0.5 * (x1 - x0) / wrh; double pz = 0.5 * (z0 + z1); z0 = pz - dz; z1 = pz + dz; } double xd = 1.0 / (x1 - x0); double zd = 1.0 / (z1 - z0); double ox = 8.0, oy = 8.0; double w = (double)(Width - 16); double h = (double)(Height - 16); // create bitmap Bitmap b = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.White); // draw route path { int start = 0; bool atc = false; PointF[] p = new PointF[n]; for (int i = 0; i < n; i++) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double z = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; x = ox + w * (x - x0) * xd; z = oy + h + h * zd * (z0 - z); p[i] = new PointF((float)x, (float)z); // ats/atc for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].SafetySystem == Game.SafetySystem.Atc) { if (!atc) { atc = true; if (i - start - 1 > 0) { g.DrawCurve(Pens.Black, p, start, i - start - 1); } start = i; } } else { if (atc) { atc = false; if (i - start - 1 > 0) { g.DrawCurve(Pens.DarkRed, p, start, i - start - 1); } start = i; } } } } } DrawSegmentedCurve(g, atc ? Pens.DarkRed : Pens.Black, p, start, n - start - 1); } // draw station circles for (int i = 0; i < n; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; x = ox + w * (x - x0) * xd; y = oy + h + h * zd * (z0 - y); // station circle RectangleF r = new RectangleF((float)x - 4.0f, (float)y - 4.0f, 8.0f, 8.0f); bool q = Game.PlayerStopsAtStation(e.StationIndex); g.FillEllipse(q ? Brushes.SkyBlue : Brushes.LightGray, r); g.DrawEllipse(q ? Pens.Black : Pens.Gray, r); } } } } // draw station names { double wh = w * h; Font f = new Font(FontFamily.GenericSansSerif, wh < 65536.0 ? 9.0f : 10.0f, GraphicsUnit.Pixel); for (int i = 0; i < n; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { double x = TrackManager.CurrentTrack.Elements[i].WorldPosition.X; double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Z; x = ox + w * (x - x0) * xd; y = oy + h + h * zd * (z0 - y); RectangleF r = new RectangleF((float)x - 4.0f, (float)y - 4.0f, 8.0f, 8.0f); bool stop = Game.PlayerStopsAtStation(e.StationIndex); string t = Game.Stations[e.StationIndex].Name; SizeF m = g.MeasureString(t, f, Width, StringFormat.GenericDefault); double sx = TrackManager.CurrentTrack.Elements[i].WorldSide.X; double sz = TrackManager.CurrentTrack.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 + 6.0; yt = y - 6.0 - m.Height; } else { // down-left xt = x - 6.0 - m.Width; yt = y + 6.0; } } else { // ascending bool o = (h - y) * (w - x) <= (x - ox) * (y - oy); if (o) { // up-left xt = x - 6.0 - m.Width; yt = y - 6.0 - m.Height; } else { // down-right xt = x + 6.0; yt = y + 6.0; } } 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; } r = new RectangleF((float)xt, (float)yt, m.Width, m.Height); g.FillRectangle(stop ? Brushes.White : Brushes.LightGray, r.Left - 1.0f, r.Top - 1.0f, r.Width + 2.0f, r.Height + 2.0f); g.DrawRectangle(stop ? Pens.Black : Pens.Gray, r.Left - 1.0f, r.Top - 1.0f, r.Width + 2.0f, r.Height + 2.0f); g.DrawString(t, f, stop ? Brushes.Black : Brushes.Gray, (float)xt, (float)yt); } } } } } // finalize return(b); }
// create route gradient profile internal static Bitmap CreateRouteGradientProfile(int Width, int Height) { // find first and last used element based on stations int n = TrackManager.CurrentTrack.Elements.Length; int n0 = n - 1; int n1 = 0; for (int i = 0; i < TrackManager.CurrentTrack.Elements.Length; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { if (i < n0) { n0 = i; } if (i > n1) { n1 = i; } } } } n0 -= 4; n1 += 8; if (n0 < 0) { n0 = 0; } if (n1 >= TrackManager.CurrentTrack.Elements.Length) { n1 = TrackManager.CurrentTrack.Elements.Length - 1; } if (n1 <= n0) { n1 = n0 + 1; } // find dimensions double y0 = double.PositiveInfinity, y1 = double.NegativeInfinity; for (int i = n0; i <= n1; i++) { double y = TrackManager.CurrentTrack.Elements[i].WorldPosition.Y; if (y < y0) { y0 = y; } if (y > y1) { y1 = y; } } if (y0 >= y1 - 1.0) { y0 = y1 - 1.0; } double nd = 1.0 / (double)(n1 - n0); double yd = 1.0 / (double)(y1 - y0); double ox = 8.0, oy = 8.0; double w = (double)(Width - 16); double h = (double)(Height - 32); // create bitmap Bitmap b = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(b); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.White); // draw below sea level { double y = oy + h * (1.0 - 0.5 * (double)(-Game.RouteInitialElevation - y0) * yd); double x0 = ox - w * (double)(n0) * nd; double x1 = ox + w * (double)(n - n0) * nd; g.FillRectangle(Brushes.PaleGoldenrod, (float)x0, (float)y, (float)x1, (float)(oy + h) - (float)y); g.DrawLine(Pens.Gray, (float)x0, (float)y, (float)x1, (float)y); } // draw route path { PointF[] p = new PointF[n + 2]; p[0] = new PointF((float)(ox - w * (double)n0 * nd), (float)(oy + h)); for (int i = 0; i < n; i++) { double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); p[i + 1] = new PointF((float)x, (float)y); } p[n + 1] = new PointF((float)(ox + w * (double)(n - n0 - 1) * nd), (float)(oy + h)); g.FillPolygon(Brushes.Tan, p); for (int i = 1; i < n; i++) { g.DrawLine(Pens.Black, p[i], p[i + 1]); } g.DrawLine(Pens.Black, 0.0f, (float)(oy + h), (float)Width, (float)(oy + h)); } // draw station names { Font f = new Font(FontFamily.GenericSansSerif, 12.0f, GraphicsUnit.Pixel); StringFormat m = new StringFormat(); for (int i = 0; i < n; i++) { for (int j = 0; j < TrackManager.CurrentTrack.Elements[i].Events.Length; j++) { if (TrackManager.CurrentTrack.Elements[i].Events[j] is TrackManager.StationStartEvent) { TrackManager.StationStartEvent e = (TrackManager.StationStartEvent)TrackManager.CurrentTrack.Elements[i].Events[j]; if (Game.Stations[e.StationIndex].Name != string.Empty) { bool stop = Game.PlayerStopsAtStation(e.StationIndex); if (Interface.IsJapanese(Game.Stations[e.StationIndex].Name)) { m.Alignment = StringAlignment.Near; m.LineAlignment = StringAlignment.Near; double x = ox + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); string t = Game.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 ? Brushes.Black : Brushes.LightGray, (float)x - 0.5f * tx, ty); SizeF s = g.MeasureString(t.Substring(k, 1), f, 65536, StringFormat.GenericTypographic); ty += s.Height; } g.DrawLine(stop ? Pens.Gray : Pens.LightGray, 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 + w * (double)(i - n0) * nd; double y = oy + h * (1.0 - 0.5 * (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd); g.RotateTransform(-90.0f); g.TranslateTransform((float)x, (float)oy, System.Drawing.Drawing2D.MatrixOrder.Append); g.DrawString(Game.Stations[e.StationIndex].Name, f, stop ? Brushes.Black : Brushes.LightGray, new PointF(0.0f, -5.0f), m); g.ResetTransform(); SizeF s = g.MeasureString(Game.Stations[e.StationIndex].Name, f); g.DrawLine(stop ? Pens.Gray : Pens.LightGray, new PointF((float)x, (float)(oy + s.Width + 4)), new PointF((float)x, (float)y)); } } } } } } // draw 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(); m.Alignment = StringAlignment.Far; m.LineAlignment = StringAlignment.Center; System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; int k = 48 * n / Width; for (int i = 0; i < n; i += k) { double x = ox + w * (double)(i - n0) * nd; double y = (double)(TrackManager.CurrentTrack.Elements[i].WorldPosition.Y - y0) * yd; if (x < w) { string t = ((int)Math.Round(TrackManager.CurrentTrack.Elements[i].StartingTrackPosition)).ToString(Culture); g.DrawString(t + "m", f, Brushes.Black, (float)x, (float)(oy + h + 6.0)); } { y = oy + h * (1.0 - 0.5 * y) + 2.0f; string t = ((int)Math.Round(Game.RouteInitialElevation + TrackManager.CurrentTrack.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, Brushes.Black, 0.0f, 0.0f, m); g.ResetTransform(); g.DrawLine(Pens.Gray, (float)x, (float)(y + s.Width + 12.0), (float)x, (float)(oy + h)); } } } } // finalize return(b); }
/// <summary>Renders a graphical visualisation of any events within camera range</summary> /// <param name="Camera">The absolute camera position</param> private static void RenderEvents(Vector3 Camera) { if (Interface.CurrentOptions.ShowEvents == false || TrackManager.Tracks[0].Elements == null) { return; } if (!Initialized) { Init(); Initialized = true; } GL.Enable(EnableCap.CullFace); LibRender.Renderer.CullEnabled = true; GL.Enable(EnableCap.DepthTest); GL.DepthMask(true); if (LibRender.Renderer.LightingEnabled) { GL.Disable(EnableCap.Lighting); LibRender.Renderer.LightingEnabled = false; } if (LibRender.Renderer.AlphaTestEnabled) { GL.Disable(EnableCap.AlphaTest); LibRender.Renderer.AlphaTestEnabled = false; } double da = -World.BackwardViewingDistance - World.ExtraViewingDistance; double db = World.ForwardViewingDistance + World.ExtraViewingDistance; bool[] sta = new bool[CurrentRoute.Stations.Length]; // events for (int i = 0; i < TrackManager.Tracks[0].Elements.Length; i++) { double p = TrackManager.Tracks[0].Elements[i].StartingTrackPosition; double d = p - World.CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { for (int j = 0; j < TrackManager.Tracks[0].Elements[i].Events.Length; j++) { dynamic e = TrackManager.Tracks[0].Elements[i].Events[j]; double dy, dx = 0.0, dz = 0.0; double s; Texture t; if (e is TrackManager.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 TrackManager.StationStartEvent) { s = 0.25; dy = 1.6; t = StationStartTexture; TrackManager.StationStartEvent f = (TrackManager.StationStartEvent)e; sta[f.StationIndex] = true; } else if (e is TrackManager.StationEndEvent) { s = 0.25; dy = 1.6; t = StationEndTexture; TrackManager.StationEndEvent f = (TrackManager.StationEndEvent)e; sta[f.StationIndex] = true; } else if (e is TrackManager.LimitChangeEvent) { s = 0.2; dy = 1.1; t = LimitTexture; } else if (e is TrackManager.SectionChangeEvent) { s = 0.2; dy = 0.8; t = SectionTexture; } else if (e is TrackManager.TransponderEvent) { s = 0.15; dy = 0.4; t = TransponderTexture; } else if (e is TrackManager.SoundEvent) { TrackManager.SoundEvent f = (TrackManager.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 = SoundTexture; } else if (e is TrackManager.PointSoundEvent) { s = 0.2; dx = 0; dy = 0.2; dz = 0; t = PointSoundTexture; } else { s = 0.2; dy = 1.0; t = null; } if (t != null) { TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = EventTriggerType.None; f.TrackPosition = p; f.Update(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; LibRender.Renderer.DrawCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera, t); } } } } // stops for (int i = 0; i < sta.Length; i++) { if (sta[i]) { for (int j = 0; j < CurrentRoute.Stations[i].Stops.Length; j++) { const double dy = 1.4; const double s = 0.2; double p = CurrentRoute.Stations[i].Stops[j].TrackPosition; TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = EventTriggerType.None; f.TrackPosition = p; f.Update(p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; LibRender.Renderer.DrawCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera, StopTexture); } } } // buffers for (int i = 0; i < Game.BufferTrackPositions.Length; i++) { double p = Game.BufferTrackPositions[i]; double d = p - World.CameraTrackFollower.TrackPosition; if (d >= da & d <= db) { const double dy = 2.5; const double s = 0.25; TrackManager.TrackFollower f = new TrackManager.TrackFollower(); f.TriggerType = EventTriggerType.None; f.TrackPosition = p; f.Update(p, true, false); f.WorldPosition.X += dy * f.WorldUp.X; f.WorldPosition.Y += dy * f.WorldUp.Y; f.WorldPosition.Z += dy * f.WorldUp.Z; LibRender.Renderer.DrawCube(f.WorldPosition, f.WorldDirection, f.WorldUp, f.WorldSide, s, Camera, BufferTexture); } } }