public Tuple <bool, float> AddPrefabPath(TsPrefabItem prefab, TsNode startNode, TsNode endNode) { //Optional - some prefabs, like gas stastions will be completely selected instead of selecting a sigle road //if (prefab.Prefab.PrefabNodes.Count <= 2) { // RoutePrefabs.Add(prefab); // return true; //} var returnValue = new Tuple <bool, float>(false, 0); var s = prefab.GetNearestNode(this, startNode, 0); var e = prefab.GetNearestNode(this, endNode, 1); if (s.id == -1 || e.id == -1) { return(returnValue); } var key = new Tuple <TsPrefabNode, TsPrefabNode>(s, e); if (prefab.Prefab.NavigationRoutes.ContainsKey(key)) { PrefabNav[prefab] = prefab.Prefab.NavigationRoutes[key].Item1; returnValue = new Tuple <bool, float>(true, prefab.Prefab.NavigationRoutes[key].Item2); } return(returnValue); }
public void Parse() { Version = BitConverter.ToInt32(Stream, 0x0); if (Version < 825) { Log.Msg($"{FilePath} version ({Version}) is too low, min. is 825"); return; } var itemCount = BitConverter.ToUInt32(Stream, 0x10); if (itemCount == 0) { _empty = true; } if (_empty) { return; } var lastOffset = 0x14; for (var i = 0; i < itemCount; i++) { var type = (TsItemType)MemoryHelper.ReadUInt32(Stream, lastOffset); if (Version <= 825) { type++; // after version 825 all types were pushed up 1 } switch (type) { case TsItemType.Road: { var item = new TsRoadItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Roads.Add(item); } break; } case TsItemType.Prefab: { var item = new TsPrefabItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Prefabs.Add(item); } break; } case TsItemType.Company: { var item = new TsCompanyItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Companies.Add(item); } break; } case TsItemType.Service: { var item = new TsServiceItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.CutPlane: { var item = new TsCutPlaneItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.City: { var item = new TsCityItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Cities.Add(item); } break; } case TsItemType.MapOverlay: { var item = new TsMapOverlayItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.MapOverlays.Add(item); } break; } case TsItemType.Ferry: { var item = new TsFerryItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.FerryConnections.Add(item); } break; } case TsItemType.Garage: { var item = new TsGarageItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.Trigger: { var item = new TsTriggerItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Triggers.Add(item); } break; } case TsItemType.FuelPump: { var item = new TsFuelPumpItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.RoadSideItem: { var item = new TsRoadSideItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.BusStop: { var item = new TsBusStopItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.TrafficRule: { var item = new TsTrafficRuleItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.TrajectoryItem: { var item = new TsTrajectoryItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.MapArea: { var item = new TsMapAreaItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.MapAreas.Add(item); } break; } default: { Log.Msg($"Unknown Type: {type} in {Path.GetFileName(FilePath)} @ {lastOffset}"); break; } } } var nodeCount = MemoryHelper.ReadInt32(Stream, lastOffset); for (var i = 0; i < nodeCount; i++) { TsNode node = new TsNode(this, lastOffset += 0x04); Mapper.UpdateEdgeCoords(node); if (!Mapper.Nodes.ContainsKey(node.Uid)) { Mapper.Nodes.Add(node.Uid, node); } lastOffset += 0x34; } }
/// <summary> /// Loads navigation inside TsItem objects /// </summary> private void LoadNavigation() { foreach (var prefab in Prefabs) { foreach (var nodeStr in prefab.Nodes) { var node = GetNodeByUid(nodeStr); TsItem road = null; TsNode precnode = node; TsItem precitem = prefab; TsNode nextnode; TsItem nextitem; List <TsItem> roads = new List <TsItem>(); var totalLength = 0.0f; if (node.ForwardItem != null && node.ForwardItem.Type == TsItemType.Road) { road = node.ForwardItem; } else if (node.BackwardItem != null && node.BackwardItem.Type == TsItemType.Road) { road = node.BackwardItem; } if (road != null) { int direction = 0; if (road.EndNodeUid == node.Uid) { direction = 1; } while (road != null && road.Type != TsItemType.Prefab && !road.Hidden) { var length = (float)Math.Sqrt(Math.Pow(GetNodeByUid(road.StartNodeUid).X - GetNodeByUid(road.EndNodeUid).X, 2) + Math.Pow(GetNodeByUid(road.StartNodeUid).Z - GetNodeByUid(road.EndNodeUid).Z, 2)); TsRoadItem roadObj = (TsRoadItem)road; totalLength += length / roadObj.RoadLook.GetWidth(); /*if (roadObj.RoadLook.IsHighway) totalLength += (length / 2) / roadObj.RoadLook.GetWidth(); * else if (roadObj.RoadLook.IsLocal) totalLength += (length / 1.75f) / roadObj.RoadLook.GetWidth(); * else if (roadObj.RoadLook.IsExpress) totalLength += (length / 1.25f) / roadObj.RoadLook.GetWidth(); * else length += length * 2;*/ roads.Add(road); if (GetNodeByUid(road.StartNodeUid) == precnode) { nextnode = GetNodeByUid(road.EndNodeUid); precnode = GetNodeByUid(road.EndNodeUid); } else { nextnode = GetNodeByUid(road.StartNodeUid); precnode = GetNodeByUid(road.StartNodeUid); } if (nextnode.BackwardItem == road || nextnode.BackwardItem == precitem) { nextitem = nextnode.ForwardItem; precitem = nextnode.ForwardItem; } else { nextitem = nextnode.BackwardItem; precitem = nextnode.BackwardItem; } road = nextitem; } if (road != null && !road.Hidden) { TsPrefabItem prevPrefab = (TsPrefabItem)prefab; TsPrefabItem nextPrefab = (TsPrefabItem)road; TsRoadLook look = ((TsRoadItem)roads.LastOrDefault()).RoadLook; if (prevPrefab.Hidden || nextPrefab.Hidden) { continue; } if (prevPrefab.Navigation.ContainsKey(nextPrefab) == false && (look.IsBidirectional() || direction == 0)) { prevPrefab.Navigation.Add(nextPrefab, new Tuple <float, List <TsItem> >(totalLength, roads)); } if (nextPrefab.Navigation.ContainsKey(prevPrefab) == false && (look.IsBidirectional() || direction == 1)) { var reverse = new List <TsItem>(roads); reverse.Reverse(); nextPrefab.Navigation.Add(prevPrefab, new Tuple <float, List <TsItem> >(totalLength, reverse)); } } } else if (node.ForwardItem != null && node.BackwardItem != null) { TsPrefabItem forwardPrefab = (TsPrefabItem)node.ForwardItem; TsPrefabItem backwardPrefab = (TsPrefabItem)node.BackwardItem; if (forwardPrefab.Hidden || backwardPrefab.Hidden) { continue; } if (forwardPrefab.Navigation.ContainsKey(backwardPrefab) == false) { forwardPrefab.Navigation.Add(backwardPrefab, new Tuple <float, List <TsItem> >(0, null)); } if (backwardPrefab.Navigation.ContainsKey(forwardPrefab) == false) { backwardPrefab.Navigation.Add(forwardPrefab, new Tuple <float, List <TsItem> >(0, null)); } } } } Dictionary <ulong, TsPrefabItem> ferryToPrefab = new Dictionary <ulong, TsPrefabItem>(); foreach (var port in FerryConnections) { float min = float.MaxValue; TsPrefabItem closerPrefab = null; foreach (var prefab in Prefabs) { float distance = (float)Math.Sqrt(Math.Pow(port.X - prefab.X, 2) + Math.Pow(port.Z - prefab.Z, 2)); if (distance < min && prefab.Navigation.Count > 1 && !prefab.Hidden) { min = distance; closerPrefab = prefab; } } ferryToPrefab[port.FerryPortId] = closerPrefab; } foreach (var port in FerryConnections) { foreach (var connection in LookupFerryConnection(port.FerryPortId)) { var ports = new List <TsItem>(); ports.Add(FerryPortbyId[connection.StartPortToken]); ports.Add(FerryPortbyId[connection.EndPortToken]); ferryToPrefab[connection.StartPortToken].Navigation.Add(ferryToPrefab[connection.EndPortToken], new Tuple <float, List <TsItem> >(connection.Distance, ports)); ports.Reverse(); ferryToPrefab[connection.EndPortToken].Navigation.Add(ferryToPrefab[connection.StartPortToken], new Tuple <float, List <TsItem> >(connection.Distance, ports)); } } }
/// <summary> /// Calculate path between two prefabs with Dijkstra's shortest path algorithm (Needs improvement with A* Algorithm) /// </summary> private void CalculatePath(TsPrefabItem Start, TsPrefabItem End) { Dictionary <TsPrefabItem, Tuple <float, TsPrefabItem> > nodesToWalk = new Dictionary <TsPrefabItem, Tuple <float, TsPrefabItem> >(); Dictionary <TsPrefabItem, Tuple <float, TsPrefabItem> > walkedNodes = new Dictionary <TsPrefabItem, Tuple <float, TsPrefabItem> >(); foreach (var node in Prefabs) { nodesToWalk.Add(node, new Tuple <float, TsPrefabItem>(float.MaxValue, null)); } if (!nodesToWalk.ContainsKey((TsPrefabItem)Start)) { return; } if (!nodesToWalk.ContainsKey((TsPrefabItem)End)) { return; } nodesToWalk[Start] = new Tuple <float, TsPrefabItem>(0, null); while (nodesToWalk.Count != 0) { float distanceWalked = float.MaxValue; TsPrefabItem toWalk = null; foreach (var node in nodesToWalk) { var dTmp = node.Value.Item1; if (distanceWalked > dTmp) { distanceWalked = dTmp; toWalk = node.Key; } } if (toWalk == null) { break; } walkedNodes[toWalk] = nodesToWalk[toWalk]; nodesToWalk.Remove(toWalk); if (toWalk.Uid == End.Uid) { break; } var currentWeight = walkedNodes[toWalk].Item1; foreach (var jump in toWalk.Navigation) { var newWeight = jump.Value.Item1 + currentWeight; TsPrefabItem newNode = jump.Key; if (walkedNodes[toWalk].Item2 != null) { TsPrefabItem precPrefab = walkedNodes[toWalk].Item2; TsPrefabItem middlePrefab = toWalk; List <TsItem> precRoad = null; while (precRoad == null && precPrefab != null) { precRoad = precPrefab.Navigation[middlePrefab].Item2; middlePrefab = precPrefab; precPrefab = walkedNodes[precPrefab].Item2; } var nextRoad = toWalk.Navigation[newNode].Item2; if (precRoad != null && nextRoad != null && (precRoad.Count != 0 && nextRoad.Count != 0) && precRoad.LastOrDefault() is TsRoadItem && nextRoad[0] is TsRoadItem) { var result = SetInternalRoutePrefab((TsRoadItem)precRoad.LastOrDefault(), (TsRoadItem)nextRoad[0]); if (!result.Item1) { continue; } else { newWeight += result.Item2; } } } if (!walkedNodes.ContainsKey(newNode) && nodesToWalk[newNode].Item1 > newWeight) { nodesToWalk[newNode] = new Tuple <float, TsPrefabItem>(newWeight, toWalk); } } } TsPrefabItem route = End; while (route != null) { TsPrefabItem gotoNew; if (walkedNodes.ContainsKey(route)) { gotoNew = walkedNodes[route].Item2; } else { gotoNew = nodesToWalk[route].Item2; } if (gotoNew == null) { break; } if (gotoNew.Navigation.ContainsKey(route) && gotoNew.Navigation[route].Item2 != null) { if (gotoNew.Navigation[route].Item2.Count == 2 && gotoNew.Navigation[route].Item2[0] is TsFerryItem && gotoNew.Navigation[route].Item2[1] is TsFerryItem) { var startPort = (TsFerryItem)gotoNew.Navigation[route].Item2[0]; var endPort = (TsFerryItem)gotoNew.Navigation[route].Item2[1]; if (!RouteFerryPorts.ContainsKey(startPort)) { RouteFerryPorts.Add(startPort, endPort); } } else { for (int i = gotoNew.Navigation[route].Item2.Count - 1; i >= 0; i--) { RouteRoads.Add((TsRoadItem)gotoNew.Navigation[route].Item2[i]); } } } route = gotoNew; } RouteRoads.Reverse(); }
public void Parse() { Version = BitConverter.ToInt32(Stream, 0x0); // 853 = 1.31+ var itemCount = BitConverter.ToUInt32(Stream, 0x10); if (itemCount == 0) { _empty = true; } if (_empty) { return; } var lastOffset = 0x14; for (var i = 0; i < itemCount; i++) { var type = (TsItemType)BitConverter.ToUInt32(Stream, lastOffset); switch (type) { case TsItemType.Road: { var item = new TsRoadItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Roads.Add(item); } break; } case TsItemType.Prefab: { var item = new TsPrefabItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Prefabs.Add(item); } break; } case TsItemType.Company: { var item = new TsCompanyItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Companies.Add(item); } break; } case TsItemType.Service: { var item = new TsServiceItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.CutPlane: { var item = new TsCutPlaneItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.City: { var item = new TsCityItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.Cities.Add(item); } break; } case TsItemType.MapOverlay: { var item = new TsMapOverlayItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.MapOverlays.Add(item); } break; } case TsItemType.Ferry: { var item = new TsFerryItem(this, lastOffset); lastOffset += item.BlockSize; if (item.Valid) { Mapper.FerryPortbyId.Add(item.FerryPortId, item); } if (item.Valid) { Mapper.FerryConnections.Add(item); } break; } case TsItemType.Garage: { var item = new TsGarageItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.Trigger: { var item = new TsTriggerItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.FuelPump: { var item = new TsFuelPumpItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.RoadSideItem: { var item = new TsRoadSideItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.BusStop: { var item = new TsBusStopItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.TrafficRule: { var item = new TsTrafficRuleItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.TrajectoryItem: { var item = new TsTrajectoryItem(this, lastOffset); lastOffset += item.BlockSize; break; } case TsItemType.MapArea: { var item = new TsMapAreaItem(this, lastOffset); lastOffset += item.BlockSize; break; } default: { Log.Msg($"Unknown Type: {type} in {Path.GetFileName(FilePath)} @ {lastOffset}"); break; } } } var nodeCount = BitConverter.ToInt32(Stream, lastOffset); for (var i = 0; i < nodeCount; i++) { TsNode node = new TsNode(this, lastOffset += 0x04); if (!Mapper.Nodes.ContainsKey(node.Uid)) { Mapper.Nodes.Add(node.Uid, node); } lastOffset += 0x34; } }