/// <summary> /// Navigate from X/Y to X/Y coordinates /// </summary> /// <param name="from"></param> /// <param name="to"></param> public Ets2NavigationRoute NavigateTo(Ets2Point from, Ets2Point to) { var start = FindClosestRoadPrefab(from); var end = FindClosestRoadPrefab(to); Console.WriteLine("Navigating from " + start.ItemUID.ToString("X16") + " to " + end.ItemUID.ToString("X16")); // Look up pre-fab closest by these 2 points return new Ets2NavigationRoute(start,end, from, to, this); }
public Ets2Item FindClosestRoadPrefab(Ets2Point location) { // Find road or prefab closest by var closestPrefab = Items.Values.Where(x => x.HideUI==false && x.Type == Ets2ItemType.Prefab && x.Prefab != null && x.Prefab.Curves.Any()) .OrderBy(x => Math.Sqrt(Math.Pow(location.X - x.PrefabNode.X, 2) + Math.Pow(location.Z - x.PrefabNode.Z, 2))) .FirstOrDefault(); return closestPrefab; }
/// <summary> /// Navigate from X/Y to X/Y coordinates /// </summary> /// <param name="from"></param> /// <param name="to"></param> public Ets2NavigationRoute NavigateTo(Ets2Point from, Ets2Point to) { var start = FindClosestRoadPrefab(from); var end = FindClosestRoadPrefab(to); Console.WriteLine("Navigating from " + start.ItemUID.ToString("X16") + " to " + end.ItemUID.ToString("X16")); // Look up pre-fab closest by these 2 points return(new Ets2NavigationRoute(start, end, from, to, this)); }
public Ets2Item FindClosestRoadPrefab(Ets2Point location) { // Find road or prefab closest by var closestPrefab = Items.Values.Where(x => x.HideUI == false && x.Type == Ets2ItemType.Prefab && x.Prefab != null && x.Prefab.Curves.Any()) .OrderBy(x => Math.Sqrt(Math.Pow(location.X - x.PrefabNode.X, 2) + Math.Pow(location.Z - x.PrefabNode.Z, 2))) .FirstOrDefault(); return(closestPrefab); }
public float DistanceTo(Ets2Point pt) { if (pt == null) return float.MaxValue; var dx = pt.X - X; var dy = pt.Z - Z; var dst = (float) Math.Sqrt(dx * dx + dy * dy); return dst; }
/// <summary> /// Navigate to city from X/Y point /// </summary> /// <param name="from"></param> /// <param name="city"></param> public Ets2NavigationRoute NavigateTo(Ets2Point from, string city) { if (Cities.ContainsKey(city) == false) return null; var cityPoint = Cities[city].StartNode.Point; var start = FindClosestRoadPrefab(from); var end = FindClosestRoadPrefab(cityPoint); return new Ets2NavigationRoute(start, end, from, null, this); }
public Ets2NavigationRoute(Ets2Item start, Ets2Item end, Ets2Point from, Ets2Point to, Ets2Mapper mapper) { Start = start; End = end; From = from; To = to; Mapper = mapper; if (Start != End) { ThreadPool.QueueUserWorkItem(FindRoute); } }
public float DistanceTo(Ets2Point pt) { if (pt == null) { return(float.MaxValue); } var dx = pt.X - X; var dy = pt.Z - Z; var dst = (float)Math.Sqrt(dx * dx + dy * dy); return(dst); }
/// <summary> /// Navigate to city from X/Y point /// </summary> /// <param name="from"></param> /// <param name="city"></param> public Ets2NavigationRoute NavigateTo(Ets2Point from, string city) { if (Cities.ContainsKey(city) == false) { return(null); } var cityPoint = Cities[city].StartNode.Point; var start = FindClosestRoadPrefab(from); var end = FindClosestRoadPrefab(cityPoint); return(new Ets2NavigationRoute(start, end, from, null, this)); }
public void GenerateRoadPolygon(int steps) { if (RoadPolygons == null) { RoadPolygons = new Ets2Point[0]; } if (RoadPolygons != null && RoadPolygons.Count() == steps) { return; } if (StartNode == null || EndNode == null) { return; } if (Type != Ets2ItemType.Road) { return; } var ps = new Ets2Point[steps]; var sx = StartNode.X; var ex = EndNode.X; var sz = StartNode.Z; var ez = EndNode.Z; var radius = (float)Math.Sqrt((sx - ex) * (sx - ex) + (sz - ez) * (sz - ez)); var tangentSX = (float)Math.Cos(-StartNode.Yaw) * radius; var tangentEX = (float)Math.Cos(-EndNode.Yaw) * radius; var tangentSZ = (float)Math.Sin(-StartNode.Yaw) * radius; var tangentEZ = (float)Math.Sin(-EndNode.Yaw) * radius; for (int k = 0; k < steps; k++) { var s = (float)k / (float)(steps - 1); var x = (float)Ets2CurveHelper.Hermite(s, sx, ex, tangentSX, tangentEX); var z = (float)Ets2CurveHelper.Hermite(s, sz, ez, tangentSZ, tangentEZ); ps[k] = new Ets2Point(x, 0, z, 0); } RoadPolygons = ps; }
public IEnumerable <Ets2Point> GeneratePolygonForRoute(Ets2PrefabRoute route, Ets2Node node, int nodeOr) { List <Ets2Point> p = new List <Ets2Point>(); if (route == null || route.Route == null) { return(p); } /* * yaw -= this.Nodes[nodeOr].Yaw; * yaw += Math.PI/2; */ var xOr = node.X; var yOr = node.Z; var yaw = node.Yaw - this.Nodes[nodeOr].Yaw + Math.PI / 2; foreach (var curve in route.Route) { var srx = curve.StartX - this.Nodes[nodeOr].X; var erx = curve.EndX - this.Nodes[nodeOr].X; var srz = curve.StartZ - this.Nodes[nodeOr].Z; var erz = curve.EndZ - this.Nodes[nodeOr].Z; var sr = (float)Math.Sqrt(srx * srx + srz * srz); var er = (float)Math.Sqrt(erx * erx + erz * erz); var ans = yaw - Math.Atan2(srz, srx); var ane = yaw - Math.Atan2(erz, erx); var sx = xOr - sr * (float)Math.Sin(ans); var ex = xOr - er * (float)Math.Sin(ane); var sz = yOr - sr * (float)Math.Cos(ans); var ez = yOr - er * (float)Math.Cos(ane); // TODO: Temporary linear interpolation // TODO: Interpolate heading & Y value var ps = new Ets2Point[2]; ps[0] = new Ets2Point(sx, node.Y, sz, (float)ans); ps[1] = new Ets2Point(ex, node.Y, ez, (float)ane); p.AddRange(ps); } return(p); }
/// <summary> /// Generate road curves for a specific lane. The curve is generated with [steps] /// nodes and positioned left or right from the road's middle point. /// Additionally, each extra lane is shifted 4.5 game units outward. /// </summary> /// <param name="steps"></param> /// <param name="leftlane"></param> /// <param name="lane"></param> /// <returns></returns> public IEnumerable <Ets2Point> GenerateRoadCurve(int steps, bool leftlane, int lane) { var ps = new Ets2Point[steps]; var sx = StartNode.X; var ex = EndNode.X; var sz = StartNode.Z; var ez = EndNode.Z; if (steps == 2) { sx += (float)Math.Sin(-StartNode.Yaw) * (leftlane ? -1 : 1) * (RoadLook.Offset + (0.5f + lane) * 4.5f); sz += (float)Math.Cos(-StartNode.Yaw) * (leftlane ? -1 : 1) * (RoadLook.Offset + (0.5f + lane) * 4.5f); ex += (float)Math.Sin(-EndNode.Yaw) * (leftlane ? -1 : 1) * (RoadLook.Offset + (0.5f + lane) * 4.5f); ez += (float)Math.Cos(-EndNode.Yaw) * (leftlane ? -1 : 1) * (RoadLook.Offset + (0.5f + lane) * 4.5f); ps[0] = new Ets2Point(sx, 0, sz, StartNode.Yaw); ps[1] = new Ets2Point(ex, 0, ez, EndNode.Yaw); return(ps); } var radius = (float)Math.Sqrt((sx - ex) * (sx - ex) + (sz - ez) * (sz - ez)); var tangentSX = (float)Math.Cos(-StartNode.Yaw) * radius; var tangentEX = (float)Math.Cos(-EndNode.Yaw) * radius; var tangentSZ = (float)Math.Sin(-StartNode.Yaw) * radius; var tangentEZ = (float)Math.Sin(-EndNode.Yaw) * radius; for (int k = 0; k < steps; k++) { var s = (float)k / (float)(steps - 1); var x = (float)Ets2CurveHelper.Hermite(s, sx, ex, tangentSX, tangentEX); var z = (float)Ets2CurveHelper.Hermite(s, sz, ez, tangentSZ, tangentEZ); var tx = (float)Ets2CurveHelper.HermiteTangent(s, sx, ex, tangentSX, tangentEX); var ty = (float)Ets2CurveHelper.HermiteTangent(s, sz, ez, tangentSZ, tangentEZ); var yaw = (float)Math.Atan2(ty, tx); x += (float)Math.Sin(-yaw) * (leftlane ? -1 : 1) * (RoadLook.Offset + (0.5f + lane) * 4.5f); z += (float)Math.Cos(-yaw) * (leftlane ? -1 : 1) * (RoadLook.Offset + (0.5f + lane) * 4.5f); ps[k] = new Ets2Point(x, 0, z, yaw); } return(ps); }
public IEnumerable<Ets2Point> GeneratePolygonForRoute(Ets2PrefabRoute route, Ets2Node node, int nodeOr) { List<Ets2Point> p = new List<Ets2Point>(); if (route == null || route.Route == null) return p; /* yaw -= this.Nodes[nodeOr].Yaw; yaw += Math.PI/2; */ var xOr = node.X; var yOr = node.Z; var yaw = node.Yaw - this.Nodes[nodeOr].Yaw + Math.PI / 2; foreach (var curve in route.Route) { var srx = curve.StartX - this.Nodes[nodeOr].X; var erx = curve.EndX - this.Nodes[nodeOr].X; var srz = curve.StartZ - this.Nodes[nodeOr].Z; var erz = curve.EndZ - this.Nodes[nodeOr].Z; var sr = (float) Math.Sqrt(srx*srx + srz*srz); var er = (float) Math.Sqrt(erx*erx + erz*erz); var ans = yaw - Math.Atan2(srz, srx); var ane = yaw - Math.Atan2(erz, erx); var sx = xOr - sr*(float) Math.Sin(ans); var ex = xOr - er*(float) Math.Sin(ane); var sz = yOr - sr*(float) Math.Cos(ans); var ez = yOr - er*(float) Math.Cos(ane); // TODO: Temporary linear interpolation // TODO: Interpolate heading & Y value var ps = new Ets2Point[2]; ps[0] = new Ets2Point(sx, node.Y, sz,(float) ans); ps[1] = new Ets2Point(ex, node.Y, ez, (float)ane); p.AddRange(ps); } return p; }
/// <summary> /// Navigate to city company from X/Y point /// </summary> /// <param name="from"></param> /// <param name="city"></param> /// <param name="company"></param> public Ets2NavigationRoute NavigateTo(Ets2Point from, string city, string company) { throw new NotImplementedException(); }
public bool CloseTo(Ets2Point pt) { return DistanceTo(pt) <= 2f; }
public bool CloseTo(Ets2Point pt) { return(DistanceTo(pt) <= 2f); }
public IEnumerable <IEnumerable <Ets2Point> > GeneratePolygonCurves(Ets2Node node, int nodeOr) { var ks = new List <IEnumerable <Ets2Point> >(); var steps = 16; if (nodeOr >= this.Nodes.Count) { nodeOr = 0; } if (Nodes.Any() == false) { return(ks); } var xOr = node.X; var yOr = node.Z; var yaw = node.Yaw - this.Nodes[nodeOr].Yaw + Math.PI / 2; foreach (var curve in Curves) { var ps = new Ets2Point[steps]; var srx = curve.StartX - this.Nodes[nodeOr].X; var erx = curve.EndX - this.Nodes[nodeOr].X; var srz = curve.StartZ - this.Nodes[nodeOr].Z; var erz = curve.EndZ - this.Nodes[nodeOr].Z; var sr = (float)Math.Sqrt(srx * srx + srz * srz); var er = (float)Math.Sqrt(erx * erx + erz * erz); var ans = yaw - Math.Atan2(srz, srx); var ane = yaw - Math.Atan2(erz, erx); var sx = xOr - sr * (float)Math.Sin(ans); var ex = xOr - er * (float)Math.Sin(ane); var sz = yOr - sr * (float)Math.Cos(ans); var ez = yOr - er * (float)Math.Cos(ane); // TODO: Temporary linear interpolation // TODO: Interpolate heading & Y value ps = new Ets2Point[2]; ps[0] = new Ets2Point(sx, 0, sz, 0); ps[1] = new Ets2Point(ex, 0, ez, 0); ks.Add(ps); /* * var tangentSX = (float)Math.Cos(ans) * curve.Length; * var tangentEX = (float)Math.Cos(ane) * curve.Length; * var tangentSY = (float)Math.Sin(ans) * curve.Length; * var tangentEY = (float)Math.Sin(ane) * curve.Length; * * for (int k = 0; k < steps; k++) * { * var s = (float)k / (float)steps; * var x = (float)Ets2CurveHelper.Hermite(s, sx, ex, tangentSX, tangentEX); * var z = (float)Ets2CurveHelper.Hermite(s, sz, ez, tangentSY, tangentEY); * ps[k] = new Ets2Point(x, 0, z, 0); * } * * ks.Add(ps); */ } return(ks); }
public IEnumerable<IEnumerable<Ets2Point>> GeneratePolygonCurves(Ets2Node node, int nodeOr) { var ks = new List<IEnumerable<Ets2Point>>(); var steps = 16; if (nodeOr >= this.Nodes.Count) nodeOr = 0; if (Nodes.Any() == false) return ks; var xOr = node.X; var yOr = node.Z; var yaw = node.Yaw - this.Nodes[nodeOr].Yaw + Math.PI/2; foreach (var curve in Curves) { var ps = new Ets2Point[steps]; var srx = curve.StartX - this.Nodes[nodeOr].X; var erx = curve.EndX - this.Nodes[nodeOr].X; var srz = curve.StartZ - this.Nodes[nodeOr].Z; var erz = curve.EndZ - this.Nodes[nodeOr].Z; var sr = (float) Math.Sqrt(srx * srx + srz * srz); var er = (float)Math.Sqrt(erx * erx + erz * erz); var ans = yaw - Math.Atan2(srz,srx); var ane = yaw - Math.Atan2(erz, erx); var sx = xOr - sr * (float)Math.Sin(ans); var ex = xOr - er * (float)Math.Sin(ane); var sz = yOr - sr * (float)Math.Cos(ans); var ez = yOr - er * (float)Math.Cos(ane); // TODO: Temporary linear interpolation // TODO: Interpolate heading & Y value ps = new Ets2Point[2]; ps[0] = new Ets2Point(sx, 0, sz, 0); ps[1] = new Ets2Point(ex, 0, ez, 0); ks.Add(ps); /* var tangentSX = (float)Math.Cos(ans) * curve.Length; var tangentEX = (float)Math.Cos(ane) * curve.Length; var tangentSY = (float)Math.Sin(ans) * curve.Length; var tangentEY = (float)Math.Sin(ane) * curve.Length; for (int k = 0; k < steps; k++) { var s = (float)k / (float)steps; var x = (float)Ets2CurveHelper.Hermite(s, sx, ex, tangentSX, tangentEX); var z = (float)Ets2CurveHelper.Hermite(s, sz, ez, tangentSY, tangentEY); ps[k] = new Ets2Point(x, 0, z, 0); } ks.Add(ps); */ } return ks; }
public void Render(Graphics g, Rectangle clip, float baseScale, Ets2Point point) { _clip = clip; g.FillRectangle(Palette.Background, new Rectangle(0,0,clip.X+clip.Width, clip.Y+clip.Height)); g.SmoothingMode = baseScale < 1000 ? SmoothingMode.AntiAlias : SmoothingMode.HighSpeed; g.InterpolationMode = InterpolationMode.NearestNeighbor; g.PixelOffsetMode = PixelOffsetMode.None; var errorFont = new Font("Arial", 10.0f, FontStyle.Bold); if (Map == null) { g.DrawString("Map object not initialized", errorFont, Palette.Error, 5, 5); return; } if (Palette == null) { g.DrawString("Palette object not initialized", errorFont, Palette.Error, 5, 5); return; } if (Map.Nodes == null) { g.DrawString("Map has not started parsing", errorFont, Palette.Error, 5, 5); return; } if (Map.Loading) { g.DrawString("Map has not completed parsing", errorFont, Palette.Error, 5, 5); return; } if (!Map.Nodes.Any()) { g.DrawString("Map has no data", errorFont, Palette.Error, 5, 5); return; } centerX = point.X; centerZ = point.Z; if (clip.Width > clip.Height) { totalX = baseScale; totalZ = (int)(baseScale * (float)clip.Height / clip.Width); } else { totalZ = baseScale; totalX = (int)(baseScale * (float)clip.Width / clip.Height); } var startX = clip.X + centerX - totalX; var endX = clip.X + centerX + totalX; var startZ = clip.Y + centerZ - totalZ; var endZ = clip.Y + centerZ + totalZ; var scaleX = clip.Width / (endX - startX); var scaleZ = clip.Height / (endZ - startZ); if (float.IsInfinity(scaleX) || float.IsNaN(scaleX)) scaleX = clip.Width; if (float.IsInfinity(scaleZ) || float.IsNaN(scaleZ)) scaleZ = clip.Height; var nodesNearby = Map.Nodes.Values.Where( x => x.X >= startX - 1500 && x.X <= endX + 1500 && x.Z >= startZ - 1500 && x.Z <= endZ + 1500); var itemsNearby = nodesNearby.SelectMany(x => x.GetItems()).Where(x => x.HideUI == false).ToList(); var prefabs = itemsNearby.Where(x => x.Type == Ets2ItemType.Prefab); var nodesToFollow = prefabs.SelectMany(x => x.NodesList.Values).Distinct(); // Gather all prefabs, and issue a drawing command foreach (var node in nodesToFollow) { if (node == null) continue; // Nodes from prefab are always like: // Prefab = Forward // Road=backward var road = node.ForwardItem != null && node.ForwardItem.Type == Ets2ItemType.Prefab ? node.BackwardItem : node.ForwardItem; var roadStart = road; var fw = node.ForwardItem != null && node.ForwardItem.Type == Ets2ItemType.Road; if (road == null) { // DEAD END continue; } var roadChain = new List<Ets2Item>(); // Start drawing at start road if (fw) { do { roadChain.Add(road); road = road.EndNode == null ? null : road.EndNode.ForwardItem; } while (road != null && road.Type == Ets2ItemType.Road); } else { do { roadChain.Add(road); road = road.StartNode == null ? null : road.StartNode.BackwardItem; } while (road != null && road.Type == Ets2ItemType.Road); } if (!fw) roadChain.Reverse(); foreach (var n in roadChain.Where(x => x.HideUI == false)) { n.GenerateRoadPolygon(64); } // Generate drawing parameters var isHighway = roadStart != null && roadStart.RoadLook != null && roadStart.RoadLook.IsHighway; var isExpress = roadStart != null && roadStart.RoadLook != null && roadStart.RoadLook.IsExpress; var isLocal = roadStart != null && roadStart.RoadLook != null && roadStart.RoadLook.IsLocal; var roadWidth = (roadStart.RoadLook != null ? roadStart.RoadLook.GetTotalWidth() : 10.0f) * scaleX; var roadInGps = Route != null && !Route.Loading && Route.Roads != null && Route.Roads.Contains(roadStart); var pen = default(Pen); if (isHighway) { pen = new Pen(roadInGps ? Palette.HighwayGPS : Palette.Highway, roadWidth); } else if (isExpress) { pen = new Pen(roadInGps ? Palette.ExpressGPS : Palette.Express, roadWidth); } else if (isLocal) { pen = new Pen(roadInGps ? Palette.LocalGPS : Palette.Local, roadWidth); } else { pen = new Pen(Palette.Error, roadWidth); } var roadPoly = roadChain.Where(x => x.HideUI == false) .SelectMany(x => x.RoadPolygons) .Select(x => new PointF((x.X - startX) * scaleX, (x.Z - startZ) * scaleZ)); if (roadPoly.Any()) { g.DrawLines(pen, roadPoly.ToArray()); } } // Cities? var cityFont = new Font("Arial", 10.0f); foreach (var cities in itemsNearby.Where(x => x.Type == Ets2ItemType.City && x.StartNode != null)) { var ctX = cities.StartNode.X; var ctZ = cities.StartNode.Z; var mapX = (ctX - startX) * scaleX; var mapY = (ctZ - startZ) * scaleZ; // g.DrawString(cities.City, cityFont, Brushes.White, mapX, mapY); } // Draw all prefab curves foreach (var prefabItem in prefabs.Where(x => x.Prefab != null && x.HideUI == false).Distinct()) { var inGps = Route != null && Route.Prefabs != null && Route.Prefabs.Any(x => x.Item1 == prefabItem); if (prefabItem.Prefab.Company != null) { // TODO: Draw companies } else { var originNode = prefabItem.NodesList.FirstOrDefault().Value; if (originNode != null) { foreach ( var poly in prefabItem.Prefab.GeneratePolygonCurves(originNode, prefabItem.Origin)) { var offsetPoly = poly.Select(x => new PointF((x.X - startX) * scaleX, (x.Z - startZ) * scaleZ)).ToArray(); var p = new Pen(inGps?Palette.PrefabGPS : Palette.Prefab, 1.0f); g.DrawLines(p, offsetPoly); } } } } var rotation = point.Heading; var truckLength = 10; g.DrawLine(new Pen(Brushes.Cyan, 5.0f), scaleX * totalX + (float)Math.Sin(rotation) * truckLength * 2, scaleZ * totalZ + (float)Math.Cos(rotation) * truckLength * 2, scaleX * totalX, scaleZ * totalZ); }
public void Render(Graphics g, Rectangle clip, float baseScale, Ets2Point point) { _clip = clip; g.FillRectangle(Palette.Background, new Rectangle(0, 0, clip.X + clip.Width, clip.Y + clip.Height)); g.SmoothingMode = baseScale < 1000 ? SmoothingMode.AntiAlias : SmoothingMode.HighSpeed; g.InterpolationMode = InterpolationMode.NearestNeighbor; g.PixelOffsetMode = PixelOffsetMode.None; var errorFont = new Font("Arial", 10.0f, FontStyle.Bold); if (Map == null) { g.DrawString("Map object not initialized", errorFont, Palette.Error, 5, 5); return; } if (Palette == null) { g.DrawString("Palette object not initialized", errorFont, Palette.Error, 5, 5); return; } if (Map.Nodes == null) { g.DrawString("Map has not started parsing", errorFont, Palette.Error, 5, 5); return; } if (Map.Loading) { g.DrawString("Map has not completed parsing", errorFont, Palette.Error, 5, 5); return; } if (!Map.Nodes.Any()) { g.DrawString("Map has no data", errorFont, Palette.Error, 5, 5); return; } centerX = point.X; centerZ = point.Z; if (clip.Width > clip.Height) { totalX = baseScale; totalZ = (int)(baseScale * (float)clip.Height / clip.Width); } else { totalZ = baseScale; totalX = (int)(baseScale * (float)clip.Width / clip.Height); } var startX = clip.X + centerX - totalX; var endX = clip.X + centerX + totalX; var startZ = clip.Y + centerZ - totalZ; var endZ = clip.Y + centerZ + totalZ; var scaleX = clip.Width / (endX - startX); var scaleZ = clip.Height / (endZ - startZ); if (float.IsInfinity(scaleX) || float.IsNaN(scaleX)) { scaleX = clip.Width; } if (float.IsInfinity(scaleZ) || float.IsNaN(scaleZ)) { scaleZ = clip.Height; } var nodesNearby = Map.Nodes.Values.Where( x => x.X >= startX - 1500 && x.X <= endX + 1500 && x.Z >= startZ - 1500 && x.Z <= endZ + 1500); var itemsNearby = nodesNearby.SelectMany(x => x.GetItems()).Where(x => x.HideUI == false).ToList(); var prefabs = itemsNearby.Where(x => x.Type == Ets2ItemType.Prefab); var nodesToFollow = prefabs.SelectMany(x => x.NodesList.Values).Distinct(); // Gather all prefabs, and issue a drawing command foreach (var node in nodesToFollow) { if (node == null) { continue; } // Nodes from prefab are always like: // Prefab = Forward // Road=backward var road = node.ForwardItem?.Type == Ets2ItemType.Prefab ? node.BackwardItem : node.ForwardItem; var roadStart = road; var fw = node.ForwardItem?.Type == Ets2ItemType.Road; if (road == null) { // DEAD END continue; } var roadChain = new List <Ets2Item>(); // Start drawing at start road if (fw) { do { roadChain.Add(road); road = road.EndNode?.ForwardItem; } while (road != null && road.Type == Ets2ItemType.Road); } else { do { roadChain.Add(road); road = road.StartNode?.BackwardItem; } while (road != null && road.Type == Ets2ItemType.Road); } if (!fw) { roadChain.Reverse(); } foreach (var n in roadChain.Where(x => x.HideUI == false)) { n.GenerateRoadPolygon(64); } // Generate drawing parameters var isHighway = roadStart?.RoadLook?.IsHighway == true; var isExpress = roadStart?.RoadLook?.IsExpress == true; var isLocal = roadStart?.RoadLook?.IsLocal == true; var isNoVehicles = roadStart?.RoadLook?.IsNoVehicles == true; var roadWidth = (roadStart?.RoadLook?.GetTotalWidth() ?? 10.0f) * scaleX; var roadInGps = Route != null && !Route.Loading && Route.Roads != null && Route.Roads.Contains(roadStart); var pen = default(Pen); if (isHighway) { pen = new Pen(roadInGps ? Palette.HighwayGPS : Palette.Highway, roadWidth); } else if (isExpress) { pen = new Pen(roadInGps ? Palette.ExpressGPS : Palette.Express, roadWidth); } else if (isLocal) { pen = new Pen(roadInGps ? Palette.LocalGPS : Palette.Local, roadWidth); } else if (isNoVehicles) { pen = new Pen(Palette.NoVehicles, roadWidth); } else { pen = new Pen(Palette.Error, roadWidth); } var roadPoly = roadChain.Where(x => x.HideUI == false) .SelectMany(x => x.RoadPolygons) .Select(x => new PointF((x.X - startX) * scaleX, (x.Z - startZ) * scaleZ)); if (roadPoly.Any()) { g.DrawLines(pen, roadPoly.ToArray()); } } // Cities? var cityFont = new Font("Arial", 10.0f); foreach (var cities in itemsNearby.Where(x => x.Type == Ets2ItemType.City && x.StartNode != null && !x.HideUI)) { var ctX = cities.StartNode.X; var ctZ = cities.StartNode.Z; var mapX = (ctX - startX) * scaleX; var mapY = (ctZ - startZ) * scaleZ; // g.DrawString(cities.City, cityFont, Brushes.White, mapX, mapY); } // Draw all prefab curves foreach (var prefabItem in prefabs.Where(x => x.Prefab != null && x.HideUI == false).Distinct()) { var inGps = Route != null && Route.Prefabs != null && Route.Prefabs.Any(x => x.Item1 == prefabItem); if (prefabItem.Prefab.Company != null) { // TODO: Draw companies } else { var originNode = prefabItem.NodesList.FirstOrDefault().Value; if (originNode != null) { foreach ( var poly in prefabItem.Prefab.GeneratePolygonCurves(originNode, prefabItem.Origin)) { var offsetPoly = poly.Select(x => new PointF((x.X - startX) * scaleX, (x.Z - startZ) * scaleZ)).ToArray(); var p = new Pen(inGps?Palette.PrefabGPS : Palette.Prefab, 1.0f); g.DrawLines(p, offsetPoly); } } } } var rotation = point.Heading; var truckLength = 10; g.DrawLine(new Pen(Brushes.Cyan, 5.0f), scaleX * totalX + (float)Math.Sin(rotation) * truckLength * 2, scaleZ * totalZ + (float)Math.Cos(rotation) * truckLength * 2, scaleX * totalX, scaleZ * totalZ); }