340 lines
8.5 KiB
C#
340 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using HG;
|
|
using RoR2.Navigation;
|
|
using UnityEngine;
|
|
|
|
namespace RoR2;
|
|
|
|
public class PathFollower
|
|
{
|
|
public class Drawer : IDisposable
|
|
{
|
|
private PathFollower _target;
|
|
|
|
private WireMeshBuilder wireMeshBuilder;
|
|
|
|
private DebugOverlay.MeshDrawer passDistanceDrawer;
|
|
|
|
private DebugOverlay.MeshDrawer pathDrawer;
|
|
|
|
private List<Path.Waypoint> waypointCache;
|
|
|
|
private int cachedCurrentWaypoint;
|
|
|
|
public PathFollower target
|
|
{
|
|
get
|
|
{
|
|
return _target;
|
|
}
|
|
set
|
|
{
|
|
if (_target != value)
|
|
{
|
|
_target = value;
|
|
pathDrawer.enabled = _target != null;
|
|
passDistanceDrawer.enabled &= _target != null;
|
|
waypointCache.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
public Drawer(PathFollower target)
|
|
{
|
|
cachedCurrentWaypoint = -1;
|
|
waypointCache = new List<Path.Waypoint>();
|
|
wireMeshBuilder = new WireMeshBuilder();
|
|
passDistanceDrawer = DebugOverlay.GetMeshDrawer();
|
|
pathDrawer = DebugOverlay.GetMeshDrawer();
|
|
int num = 32;
|
|
float sliceSize = 360f / (float)num;
|
|
Vector3 halfForward = Vector3.forward * 0.5f;
|
|
Vector3 p = GetCirclePosition(-1);
|
|
for (int j = 0; j < num; j++)
|
|
{
|
|
Vector3 vector = GetCirclePosition(j);
|
|
wireMeshBuilder.AddLine(p, Color.yellow, vector, Color.yellow);
|
|
p = vector;
|
|
}
|
|
passDistanceDrawer.hasMeshOwnership = true;
|
|
passDistanceDrawer.mesh = wireMeshBuilder.GenerateMesh();
|
|
wireMeshBuilder.Clear();
|
|
pathDrawer.mesh = new Mesh();
|
|
RoR2Application.onFixedUpdate += FixedUpdate;
|
|
this.target = target;
|
|
Vector3 GetCirclePosition(int i)
|
|
{
|
|
return Quaternion.AngleAxis((float)i * sliceSize, Vector3.up) * halfForward;
|
|
}
|
|
}
|
|
|
|
private void SetWaypoints(List<Path.Waypoint> newWaypoints, int newCurrentWaypoint)
|
|
{
|
|
if (!ListUtils.SequenceEquals(waypointCache, newWaypoints) || !cachedCurrentWaypoint.Equals(newCurrentWaypoint))
|
|
{
|
|
waypointCache.Clear();
|
|
waypointCache.AddRange(newWaypoints);
|
|
cachedCurrentWaypoint = newCurrentWaypoint;
|
|
RebuildPathMesh();
|
|
}
|
|
}
|
|
|
|
private void RebuildPathMesh()
|
|
{
|
|
List<Path.Waypoint> waypoints = target.waypoints;
|
|
Color nodeColor;
|
|
Color traversedPathColor;
|
|
Color currentPathColor;
|
|
Color untraversedPathColor;
|
|
Color drawPathColor;
|
|
if (waypoints.Count > 1)
|
|
{
|
|
nodeColor = new Color(1f, 0.5f, 0.5f, 0.25f);
|
|
traversedPathColor = new Color(0.5f, 0.5f, 0.5f, 0.25f);
|
|
currentPathColor = new Color(1f, 0.5f, 0.5f, 1f);
|
|
untraversedPathColor = new Color(0.5f, 0f, 0f, 0.25f);
|
|
target.nodeGraph.GetNodePosition(waypoints[0].nodeIndex, out var position2);
|
|
drawPathColor = untraversedPathColor;
|
|
DrawNode(position2);
|
|
for (int j = 1; j < waypoints.Count; j++)
|
|
{
|
|
UpdateDrawPathColor(j);
|
|
target.nodeGraph.GetNodePosition(waypoints[j].nodeIndex, out var position3);
|
|
wireMeshBuilder.AddLine(position2, drawPathColor, position3, drawPathColor);
|
|
DrawNode(position3);
|
|
position2 = position3;
|
|
}
|
|
}
|
|
wireMeshBuilder.GenerateMesh(pathDrawer.mesh);
|
|
wireMeshBuilder.Clear();
|
|
void DrawNode(Vector3 position)
|
|
{
|
|
wireMeshBuilder.AddLine(position - Vector3.up, nodeColor, position + Vector3.up, nodeColor);
|
|
}
|
|
void UpdateDrawPathColor(int i)
|
|
{
|
|
if (cachedCurrentWaypoint == i)
|
|
{
|
|
drawPathColor = currentPathColor;
|
|
}
|
|
else if (cachedCurrentWaypoint < i)
|
|
{
|
|
drawPathColor = untraversedPathColor;
|
|
}
|
|
else
|
|
{
|
|
drawPathColor = traversedPathColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (target != null)
|
|
{
|
|
passDistanceDrawer.enabled = target.currentWaypoint < target.waypoints.Count;
|
|
if (passDistanceDrawer.enabled)
|
|
{
|
|
target.nodeGraph.GetNodePosition(target.waypoints[target.currentWaypoint].nodeIndex, out var position);
|
|
passDistanceDrawer.transform.position = position;
|
|
passDistanceDrawer.transform.localScale = Vector3.one * target.debugPassDistance;
|
|
}
|
|
SetWaypoints(target.waypoints, target.currentWaypoint);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
target = null;
|
|
RoR2Application.onFixedUpdate -= FixedUpdate;
|
|
pathDrawer.Dispose();
|
|
passDistanceDrawer.Dispose();
|
|
wireMeshBuilder.Dispose();
|
|
}
|
|
}
|
|
|
|
private Vector3 currentPosition;
|
|
|
|
private const float waypointPassDistance = 2f;
|
|
|
|
private const float waypointPassYTolerance = 2f;
|
|
|
|
private List<Path.Waypoint> waypoints = new List<Path.Waypoint>();
|
|
|
|
private int currentWaypoint;
|
|
|
|
private NodeGraph.NodeIndex previousNode = NodeGraph.NodeIndex.invalid;
|
|
|
|
private float debugPassDistance;
|
|
|
|
public NodeGraph nodeGraph { get; private set; }
|
|
|
|
public NodeGraph.NodeIndex nextNode { get; private set; } = NodeGraph.NodeIndex.invalid;
|
|
|
|
|
|
public bool isFinished => currentWaypoint >= waypoints.Count;
|
|
|
|
public bool isOnJumpLink => false;
|
|
|
|
public bool hasPassedFirstWaypoint => currentWaypoint > 0;
|
|
|
|
public bool nextWaypointNeedsJump
|
|
{
|
|
get
|
|
{
|
|
if (waypoints.Count > 0 && currentWaypoint < waypoints.Count)
|
|
{
|
|
return waypoints[currentWaypoint].minJumpHeight > 0f;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static float DistanceXZ(Vector3 a, Vector3 b)
|
|
{
|
|
a.y = 0f;
|
|
b.y = 0f;
|
|
return Vector3.Distance(a, b);
|
|
}
|
|
|
|
public float CalculateJumpVelocityNeededToReachNextWaypoint(float moveSpeed)
|
|
{
|
|
if (!nextWaypointNeedsJump)
|
|
{
|
|
return 0f;
|
|
}
|
|
Vector3 a = currentPosition;
|
|
if (!GetNextNodePosition(out var nextPosition))
|
|
{
|
|
return 0f;
|
|
}
|
|
return Trajectory.CalculateInitialYSpeed(Trajectory.CalculateGroundTravelTime(moveSpeed, DistanceXZ(a, nextPosition)), nextPosition.y - a.y);
|
|
}
|
|
|
|
public void UpdatePosition(Vector3 newPosition)
|
|
{
|
|
Vector3 vector = currentPosition;
|
|
currentPosition = newPosition;
|
|
if (GetNextNodePosition(out var nextPosition))
|
|
{
|
|
Vector3 vector2 = nextPosition - currentPosition;
|
|
Vector3 vector3 = vector2;
|
|
vector3.y = 0f;
|
|
float num = 2f;
|
|
Vector3 inNormal = currentPosition - vector;
|
|
Plane plane = new Plane(inNormal, nextPosition);
|
|
if (plane.GetSide(currentPosition) != plane.GetSide(vector))
|
|
{
|
|
num += 2f;
|
|
}
|
|
if (waypoints.Count > currentWaypoint + 1 && waypoints[currentWaypoint + 1].minJumpHeight > 0f)
|
|
{
|
|
num = 0.5f;
|
|
}
|
|
debugPassDistance = num;
|
|
if (num * num >= vector3.sqrMagnitude && Mathf.Abs(vector2.y) <= 2f)
|
|
{
|
|
SetWaypoint(currentWaypoint + 1);
|
|
}
|
|
}
|
|
_ = nextNode != NodeGraph.NodeIndex.invalid;
|
|
}
|
|
|
|
private void SetWaypoint(int newWaypoint)
|
|
{
|
|
currentWaypoint = Math.Min(newWaypoint, waypoints.Count);
|
|
if (currentWaypoint == waypoints.Count)
|
|
{
|
|
nextNode = NodeGraph.NodeIndex.invalid;
|
|
previousNode = NodeGraph.NodeIndex.invalid;
|
|
}
|
|
else
|
|
{
|
|
nextNode = waypoints[currentWaypoint].nodeIndex;
|
|
previousNode = ((currentWaypoint > 0) ? waypoints[currentWaypoint - 1].nodeIndex : NodeGraph.NodeIndex.invalid);
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
nodeGraph = null;
|
|
nextNode = NodeGraph.NodeIndex.invalid;
|
|
previousNode = NodeGraph.NodeIndex.invalid;
|
|
waypoints.Clear();
|
|
currentWaypoint = 0;
|
|
}
|
|
|
|
public void SetPath(Path newPath)
|
|
{
|
|
if (nodeGraph != newPath.nodeGraph)
|
|
{
|
|
Reset();
|
|
nodeGraph = newPath.nodeGraph;
|
|
}
|
|
waypoints.Clear();
|
|
newPath.WriteWaypointsToList(waypoints);
|
|
currentWaypoint = 0;
|
|
for (int i = 1; i < waypoints.Count; i++)
|
|
{
|
|
if (waypoints[i].nodeIndex == nextNode && waypoints[i - 1].nodeIndex == previousNode)
|
|
{
|
|
currentWaypoint = i;
|
|
break;
|
|
}
|
|
}
|
|
SetWaypoint(currentWaypoint);
|
|
}
|
|
|
|
private void GetPreviousAndNextNodePositions(out Vector3? previousPosition, out Vector3? nextPosition)
|
|
{
|
|
previousPosition = null;
|
|
nextPosition = null;
|
|
if (!(nodeGraph == null))
|
|
{
|
|
if (nodeGraph.GetNodePosition(previousNode, out var position))
|
|
{
|
|
previousPosition = position;
|
|
}
|
|
if (nodeGraph.GetNodePosition(nextNode, out position))
|
|
{
|
|
nextPosition = position;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool GetNextNodePosition(out Vector3 nextPosition)
|
|
{
|
|
if (nodeGraph != null && nextNode != NodeGraph.NodeIndex.invalid && nodeGraph.GetNodePosition(nextNode, out nextPosition))
|
|
{
|
|
return true;
|
|
}
|
|
nextPosition = currentPosition;
|
|
return false;
|
|
}
|
|
|
|
public Vector3? GetNextPosition()
|
|
{
|
|
if (GetNextNodePosition(out var nextPosition))
|
|
{
|
|
return nextPosition;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void DebugDrawPath(Color color, float duration)
|
|
{
|
|
for (int i = 1; i < waypoints.Count; i++)
|
|
{
|
|
nodeGraph.GetNodePosition(waypoints[i].nodeIndex, out var position);
|
|
nodeGraph.GetNodePosition(waypoints[i - 1].nodeIndex, out var position2);
|
|
Debug.DrawLine(position, position2, color, duration);
|
|
}
|
|
for (int j = 0; j < waypoints.Count; j++)
|
|
{
|
|
nodeGraph.GetNodePosition(waypoints[j].nodeIndex, out var position3);
|
|
Debug.DrawLine(position3 + Vector3.up, position3 - Vector3.up, color, duration);
|
|
}
|
|
}
|
|
}
|