private void BuildNavigationCache()
{
// The idea of navigation cache is that we calculate distances between nodes
// The nodes we identify as prefabs (cross points etc.)
// Distance between them are the roads
// This way we don't have to walk through each road segment (which can be hundreds or thousands) each time we want to know the node-node length
// This is a reduction of approximately 6x
Dictionary<ulong, Dictionary<ulong, float>> cache = new Dictionary<ulong, Dictionary<ulong, float>>();
foreach (var prefab in Items.Values.Where(x => x.HideUI == false && x.Type == Ets2ItemType.Prefab))
{
foreach (var node in prefab.NodesList.Values)
{
var endNode = default(Ets2Item);
var fw = node.ForwardItem != null && node.ForwardItem.Type == Ets2ItemType.Road;
var road = node.ForwardItem != null && node.ForwardItem.Type == Ets2ItemType.Prefab
? node.BackwardItem
: node.ForwardItem;
var totalLength = 0.0f;
var weight = 0.0f;
List<Ets2Item> roadList = new List<Ets2Item>();
while (road != null)
{
if (road.StartNode == null || road.EndNode == null)
break;
var length =
(float)Math.Sqrt(Math.Pow(road.StartNode.X - road.EndNode.X, 2) +
Math.Pow(road.StartNode.Z - road.EndNode.Z, 2));
var spd = 1;
if (road.RoadLook != null)
{
if (road.RoadLook.IsExpress) spd = 25;
if (road.RoadLook.IsLocal) spd = 45;
if (road.RoadLook.IsHighway) spd = 70;
}
totalLength += length;
weight += length / spd;
roadList.Add(road);
if (fw)
{
road = road.EndNode == null?null: road.EndNode.ForwardItem;
if (road != null && road.Type == Ets2ItemType.Prefab)
{
endNode = road;
break;
}
}
else
{
road = road.StartNode == null ? null : road.StartNode.BackwardItem;
if (road != null && road.Type == Ets2ItemType.Prefab)
{
endNode = road;
break;
}
}
}
if (prefab.ItemUID == 0x002935DED8C0345C)
{
Console.WriteLine(node.NodeUID.ToString("X16") + " following to " + endNode.ItemUID.ToString("X16"));
}
// If there is no end-node found, it is a dead-end road.
if (endNode != null && prefab != endNode)
{
if (prefab.Navigation.ContainsKey(endNode) == false)
{
prefab.Navigation.Add(endNode,
new Tuple<float, float, IEnumerable<Ets2Item>>(weight, totalLength, roadList));
}
if (endNode.Navigation.ContainsKey(prefab) == false)
{
var reversedRoadList = new List<Ets2Item>(roadList);
reversedRoadList.Reverse();
endNode.Navigation.Add(prefab,
new Tuple<float, float, IEnumerable<Ets2Item>>(weight, totalLength, reversedRoadList));
}
}
}
}
}