public Ets2Item FindItem(ulong uid) { if (uid == 0) { return(null); } var pos = Stream.IndexesOfUlong(BitConverter.GetBytes(uid)); foreach (var off in pos) { var offs = off - 4; var type = BitConverter.ToUInt32(Stream, offs); if (type < 0x40 && type != 0) { var item = new Ets2Item(uid, this, offs); if (item.Valid) { return(item); } } } return(null); }
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 void Parse(bool skipMultiSectors) { Loading = true; ParseRoadLookFiles(); // Load all LUTs LoadLUT(); ItemSearchRequests = new List <Ets2ItemSearchRequest>(); Sectors = SectorFiles.Select(x => new Ets2Sector(this, x)).ToList(); // 2-stage process so we can validate node UID's at item stage ThreadPool.SetMaxThreads(1, 1); Parallel.ForEach(Sectors, (sec) => sec.ParseNodes()); Parallel.ForEach(Sectors, (sec) => sec.ParseItems()); Loading = false; // Some nodes may refer to items in other sectors. // We can search all sectors for those, but this is relatively slow process. if (!skipMultiSectors) { // Now find all that were not found Console.WriteLine(ItemSearchRequests.Count + " were not found; attempting to search them through all sectors"); foreach (var req in ItemSearchRequests) { Ets2Item item = Sectors.Select(sec => sec.FindItem(req.ItemUID)).FirstOrDefault(tmp => tmp != null); if (item == null) { Console.WriteLine("Still couldn't find node " + req.ItemUID.ToString("X16")); } else { if (req.IsBackward) { item.Apply(req.Node); req.Node.BackwardItem = item; } if (req.IsForward) { item.Apply(req.Node); req.Node.ForwardItem = item; } if (item.StartNode == null && item.StartNodeUID != null) { Ets2Node startNode; if (Nodes.TryGetValue(item.StartNodeUID, out startNode)) { item.Apply(startNode); } } if (item.EndNode == null && item.EndNodeUID != null) { Ets2Node endNode; if (Nodes.TryGetValue(item.EndNodeUID, out endNode)) { item.Apply(endNode); } } Console.Write("."); } } } // Navigation cache BuildNavigationCache(); // Lookup all cities Cities = Items.Values.Where(x => x.Type == Ets2ItemType.City).GroupBy(x => x.City).Select(x => x.FirstOrDefault()).ToDictionary(x => x.City, x => x); Console.WriteLine(Items.Values.Count(x => x.Type == Ets2ItemType.Building) + " buildings were found"); Console.WriteLine(Items.Values.Count(x => x.Type == Ets2ItemType.Road) + " roads were found"); Console.WriteLine(Items.Values.Count(x => x.Type == Ets2ItemType.Prefab) + " prefabs were found"); Console.WriteLine(Items.Values.Count(x => x.Type == Ets2ItemType.Prefab && x.Prefab != null && x.Prefab.Curves.Any()) + " road prefabs were found"); Console.WriteLine(Items.Values.Count(x => x.Type == Ets2ItemType.Service) + " service points were found"); Console.WriteLine(Items.Values.Count(x => x.Type == Ets2ItemType.Company) + " companies were found"); Console.WriteLine(Items.Values.Count(x => x.Type == Ets2ItemType.City) + " cities were found"); }
public Ets2NavigationSegment(Ets2Item prefab) { Type = Ets2NavigationSegmentType.Prefab; Prefab = prefab; }
private void FindRoute(object state) { Loading = true; Dictionary <ulong, Tuple <float, Ets2Item> > nodeMap = new Dictionary <ulong, Tuple <float, Ets2Item> >(); List <Ets2Item> nodesToWalk = new List <Ets2Item>(); // Fill node map foreach (var node in Mapper.Items.Values.Where(x => x.Type == Ets2ItemType.Prefab && x.HideUI == false)) { nodesToWalk.Add(node); nodeMap.Add(node.ItemUID, new Tuple <float, Ets2Item>(float.MaxValue, null)); } // Walk first node (START) if (nodeMap.ContainsKey(Start.ItemUID) == false) { // Nope return; } if (nodeMap.ContainsKey(End.ItemUID) == false) { // Nope return; } // <Weight, LastItem> nodeMap[Start.ItemUID] = new Tuple <float, Ets2Item>(0, null); while (nodesToWalk.Any()) { float distanceWalked = float.MaxValue; Ets2Item toWalk = null; foreach (var node in nodesToWalk) { var dTmp = nodeMap[node.ItemUID].Item1; if (dTmp != float.MaxValue && distanceWalked > dTmp) { distanceWalked = dTmp; toWalk = node; } } if (toWalk == null) { break; } nodesToWalk.Remove(toWalk); var currentWeight = nodeMap[toWalk.ItemUID].Item1; // Iterate all destination nodes from this node foreach (var jump in toWalk.Navigation) { var newWeight = jump.Value.Item1 + currentWeight; var newNode = jump.Key; if (nodeMap.ContainsKey(newNode.ItemUID) && nodeMap[newNode.ItemUID].Item1 > newWeight) { nodeMap[newNode.ItemUID] = new Tuple <float, Ets2Item>(newWeight, toWalk); } // add route with weight + previous node } } List <Ets2Item> routeNodes = new List <Ets2Item>(); List <Ets2Item> routeRoads = new List <Ets2Item>(); List <Ets2NavigationSegment> segments = new List <Ets2NavigationSegment>(); var goingViaNode = (ulong)0; var route = End; while (route != null) { // we add this prefab to the route list routeNodes.Add(route); var prefabSeg = new Ets2NavigationSegment(route); segments.Add(prefabSeg); // find the next prefab in the route description var gotoNew = nodeMap[route.ItemUID].Item2; if (gotoNew == null) { break; } // get a path from the current prefab to the new one var path = route.Navigation[gotoNew]; var roadPath = path.Item3; // add the path to road list routeRoads.AddRange(roadPath); segments.Add(new Ets2NavigationSegment(roadPath, prefabSeg)); // Set the new prefab as route route = gotoNew; } routeNodes.Add(Start); segments.Reverse(); Roads = routeRoads; Prefabs = routeNodes.Select(x => new Tuple <Ets2Item, int, int>(x, 0, 0)).ToList(); Loading = false; // TODO: find routes on prefabs // The following code is a start to that, but not finished yet. // It divides the route into segments, that describe the path(s) on a road or prefab to get from A to B. // Find entry/exit of start/end segment var foundDst = float.MaxValue; var foundNode = default(Ets2Node); // Find the closest road to startpoint foreach (var node in segments[0].Prefab.NodesList) { var dst = node.Value.Point.DistanceTo(From); if (foundDst > dst) { foundDst = dst; foundNode = node.Value; } } // We found the node; find the road that exists at this point segments[0].Entry = foundNode; foundDst = float.MaxValue; foundNode = default(Ets2Node); // Find the closest road to startpoint foreach (var node in segments[segments.Count - 1].Prefab.NodesList) { var dst = node.Value.Point.DistanceTo(To); if (foundDst > dst) { foundDst = dst; foundNode = node.Value; } } // We found the node; find the road that exists at this point segments[segments.Count - 1].Exit = foundNode; // Iterate all segments for (int seg = 0; seg < segments.Count; seg++) { // Generate prefab routes if (segments[seg].Type == Ets2NavigationSegmentType.Prefab) { var prevRoad = seg > 0 ? segments[seg - 1] : null; var nextRoad = seg + 1 < segments.Count ? segments[seg + 1] : null; // Link segments together if (prevRoad != null) { segments[seg].Entry = prevRoad.Entry; } if (nextRoad != null) { segments[seg].Exit = nextRoad.Exit; } segments[seg].GenerateOptions(prevRoad, nextRoad); } // Generate lane data if (segments[seg].Type == Ets2NavigationSegmentType.Road) { var prefFab = seg > 0 ? segments[seg - 1] : null; var nextFab = seg + 1 < segments.Count ? segments[seg + 1] : null; segments[seg].GenerateOptions(prefFab, nextFab); } } //for (int seg = 1; seg < segments.Count - 1; seg++) for (int seg = 0; seg < segments.Count; seg++) { // Validate routes if (segments[seg].Type == Ets2NavigationSegmentType.Prefab) { var prevRoad = seg > 0 ? segments[seg - 1] : null; var nextRoad = seg + 1 < segments.Count ? segments[seg + 1] : null; foreach (var opt in segments[seg].Options) { if (prevRoad == null) { // start point; validate if it is close to our start node if (opt.Points.FirstOrDefault().DistanceTo(segments[seg].Entry.Point) < 10) { opt.EntryLane = 0; // yep; sure } } else { var entryPoint = opt.Points.FirstOrDefault(); foreach (var roadOpt in prevRoad.Options) { if (roadOpt.Points.Any(x => x.CloseTo(entryPoint))) { // We've got a match ! :D opt.EntryLane = roadOpt.ExitLane; roadOpt.Valid = roadOpt.EntryLane >= 0 && roadOpt.ExitLane >= 0; } } } if (nextRoad == null) { // last point. if (opt.Points.FirstOrDefault().DistanceTo(segments[seg].Exit.Point) < 10) { opt.ExitLane = 0; // yep; sure } } else { var exitPoint = opt.Points.LastOrDefault(); foreach (var roadOpt in nextRoad.Options) { if (roadOpt.Points.Any(x => x.CloseTo(exitPoint))) { // We've got a match ! :D opt.ExitLane = roadOpt.EntryLane; roadOpt.Valid = roadOpt.EntryLane >= 0 && roadOpt.ExitLane >= 0; } } } opt.Valid = opt.EntryLane >= 0 && opt.ExitLane >= 0; } } // Generate prefab routes if (segments[seg].Type == Ets2NavigationSegmentType.Road && false) { var nextPrefab = segments[seg + 1]; var prevPrefab = segments[seg - 1]; if (nextPrefab.Type != Ets2NavigationSegmentType.Prefab || prevPrefab.Type != Ets2NavigationSegmentType.Prefab) { continue; } // Deduct road options by matching entry/exits for (int solI = 0; solI < segments[seg].Options.Count; solI++) { // Find if there is any route in prefab that matches our entry lane var okStart = prevPrefab.Options.Any(x => segments[seg].MatchEntry(solI, prevPrefab)); var okEnd = nextPrefab.Options.Any(x => segments[seg].MatchExit(solI, nextPrefab)); // This road has a valid end & Start if (okStart && okEnd) { segments[seg].Options[solI].Valid = true; } } } } // There is probably only 1 valid solution for entry/exit //if (segments[0].Options.Any()) segments[0].Options[0].Valid = true; //if (segments[segments.Count - 1].Options.Any()) segments[segments.Count - 1].Options[0].Valid = true; Segments = segments; }