241 lines
7.0 KiB
C#
241 lines
7.0 KiB
C#
using Microsoft.Xna.Framework;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Subsurface
|
|
{
|
|
class PathNode
|
|
{
|
|
private WayPoint wayPoint;
|
|
|
|
private int wayPointID;
|
|
|
|
public int state;
|
|
|
|
public PathNode Parent;
|
|
|
|
private Vector2 position;
|
|
|
|
public float F,G,H;
|
|
|
|
public List<PathNode> connections;
|
|
public float[] distances;
|
|
|
|
public WayPoint Waypoint
|
|
{
|
|
get { return wayPoint; }
|
|
}
|
|
|
|
public Vector2 Position
|
|
{
|
|
get {return position;}
|
|
}
|
|
|
|
public PathNode(WayPoint wayPoint)
|
|
{
|
|
this.wayPoint = wayPoint;
|
|
this.position = wayPoint.SimPosition;
|
|
wayPointID = wayPoint.ID;
|
|
|
|
connections = new List<PathNode>();
|
|
}
|
|
|
|
public static List<PathNode> GenerateNodes(List<WayPoint> wayPoints)
|
|
{
|
|
var nodes = new Dictionary<int, PathNode>();
|
|
foreach (WayPoint wayPoint in wayPoints)
|
|
{
|
|
nodes.Add(wayPoint.ID, new PathNode(wayPoint));
|
|
}
|
|
|
|
foreach (KeyValuePair<int,PathNode> node in nodes)
|
|
{
|
|
foreach (MapEntity linked in node.Value.wayPoint.linkedTo)
|
|
{
|
|
PathNode connectedNode = null;
|
|
nodes.TryGetValue(linked.ID, out connectedNode);
|
|
if (connectedNode == null) continue;
|
|
|
|
node.Value.connections.Add(connectedNode);
|
|
}
|
|
}
|
|
|
|
var nodeList = nodes.Values.ToList();
|
|
foreach (PathNode node in nodeList)
|
|
{
|
|
node.distances = new float[node.connections.Count];
|
|
for (int i = 0; i< node.distances.Length; i++)
|
|
{
|
|
node.distances[i] = Vector2.Distance(node.position, node.connections[i].position);
|
|
}
|
|
}
|
|
return nodeList;
|
|
}
|
|
|
|
}
|
|
|
|
class PathFinder
|
|
{
|
|
List<PathNode> nodes;
|
|
|
|
private bool insideSubmarine;
|
|
|
|
public PathFinder(List<WayPoint> wayPoints, bool insideSubmarine = false)
|
|
{
|
|
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => w.MoveWithLevel != insideSubmarine));
|
|
|
|
this.insideSubmarine = insideSubmarine;
|
|
}
|
|
|
|
public SteeringPath FindPath(Vector2 start, Vector2 end)
|
|
{
|
|
float closestDist = 0.0f;
|
|
PathNode startNode = null;
|
|
foreach (PathNode node in nodes)
|
|
{
|
|
float dist = Vector2.Distance(start,node.Position);
|
|
if (dist<closestDist || startNode==null)
|
|
{
|
|
closestDist = dist;
|
|
startNode = node;
|
|
}
|
|
}
|
|
|
|
closestDist = 0.0f;
|
|
PathNode endNode = null;
|
|
foreach (PathNode node in nodes)
|
|
{
|
|
float dist = Vector2.Distance(end, node.Position);
|
|
if (dist < closestDist || endNode == null)
|
|
{
|
|
closestDist = dist;
|
|
endNode = node;
|
|
}
|
|
}
|
|
|
|
if (startNode == null || endNode == null)
|
|
{
|
|
DebugConsole.ThrowError("Pathfinding error, couldn't find pathnodes");
|
|
return null;
|
|
}
|
|
|
|
return FindPath(startNode,endNode);
|
|
}
|
|
|
|
public SteeringPath FindPath(WayPoint start, WayPoint end)
|
|
{
|
|
PathNode startNode=null, endNode=null;
|
|
foreach (PathNode node in nodes)
|
|
{
|
|
if (node.Waypoint == start)
|
|
{
|
|
startNode = node;
|
|
if (endNode != null) break;
|
|
}
|
|
if (node.Waypoint == end)
|
|
{
|
|
endNode = node;
|
|
if (startNode != null) break;
|
|
}
|
|
|
|
if (startNode==null || endNode==null)
|
|
{
|
|
DebugConsole.ThrowError("Pathfinding error, couldn't find matching pathnodes to waypoints");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return FindPath(startNode, endNode);
|
|
}
|
|
|
|
private SteeringPath FindPath(PathNode start, PathNode end)
|
|
{
|
|
foreach (PathNode node in nodes)
|
|
{
|
|
node.state = 0;
|
|
node.F = 0.0f;
|
|
node.G = 0.0f;
|
|
node.H = 0.0f;
|
|
}
|
|
|
|
start.state = 1;
|
|
while (true)
|
|
{
|
|
|
|
PathNode currNode = null;
|
|
float dist = 10000.0f;
|
|
foreach (PathNode node in nodes)
|
|
{
|
|
if (node.state != 1) continue;
|
|
if (node.F < dist)
|
|
{
|
|
dist = node.F;
|
|
currNode = node;
|
|
}
|
|
}
|
|
|
|
if (currNode == null || currNode == end) break;
|
|
|
|
currNode.state = 2;
|
|
|
|
for (int i = 0; i < currNode.connections.Count; i++)
|
|
{
|
|
PathNode nextNode = currNode.connections[i];
|
|
|
|
//a node that hasn't been searched yet
|
|
if (nextNode.state==0)
|
|
{
|
|
nextNode.H = Vector2.Distance(nextNode.Position,end.Position);
|
|
nextNode.G = currNode.G + currNode.distances[i];
|
|
nextNode.F = nextNode.G + nextNode.H;
|
|
nextNode.Parent = currNode;
|
|
nextNode.state = 1;
|
|
}
|
|
//node that has been searched
|
|
else if (nextNode.state==1)
|
|
{
|
|
float tempG = currNode.G + currNode.distances[i];
|
|
//only use if this new route is better than the
|
|
//route the node was a part of
|
|
if (tempG < nextNode.G)
|
|
{
|
|
nextNode.G = tempG;
|
|
nextNode.F = nextNode.G + nextNode.H;
|
|
nextNode.Parent = currNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (end.state==0)
|
|
{
|
|
//path not found
|
|
return new SteeringPath();
|
|
}
|
|
|
|
SteeringPath path = new SteeringPath();
|
|
List<WayPoint> finalPath = new List<WayPoint>();
|
|
|
|
PathNode pathNode = end;
|
|
while (pathNode != start && pathNode != null)
|
|
{
|
|
finalPath.Add(pathNode.Waypoint);
|
|
|
|
pathNode = pathNode.Parent;
|
|
}
|
|
|
|
finalPath.Reverse();
|
|
|
|
foreach (WayPoint wayPoint in finalPath)
|
|
{
|
|
path.AddNode(wayPoint);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|