| | | 1 | | using FixedMathSharp; |
| | | 2 | | using System; |
| | | 3 | | |
| | | 4 | | namespace Trailblazer.Pathing; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// Waypoint guide used for raw voxel volume detours. |
| | | 8 | | /// </summary> |
| | | 9 | | public sealed class VolumeGuide : IWaypointGuide |
| | | 10 | | { |
| | | 11 | | /// <summary> |
| | | 12 | | /// Gets the trail map result for the volume survey, if available. |
| | | 13 | | /// </summary> |
| | | 14 | | public VolumeSurveyResult? TrailMap { get; private set; } |
| | | 15 | | |
| | | 16 | | /// <inheritdoc/> |
| | | 17 | | public int CurrentWaypointIndex { get; private set; } |
| | | 18 | | |
| | | 19 | | private int _lastTriedIndex; |
| | | 20 | | |
| | | 21 | | /// <summary> |
| | | 22 | | /// Gets the collection of currently active waypoints used for pathfinding. |
| | | 23 | | /// </summary> |
| | | 24 | | /// <remarks>Returns an empty array if no trail map is available or if there are no active waypoints.</remarks> |
| | 79 | 25 | | public AStarWaypoint[] ActiveWaypoints => TrailMap?.Waypoints ?? Array.Empty<AStarWaypoint>(); |
| | | 26 | | |
| | | 27 | | /// <summary> |
| | | 28 | | /// Initializes the navigation state using the specified survey result. |
| | | 29 | | /// </summary> |
| | | 30 | | /// <param name="surveyResult">The survey result containing the path and waypoints to initialize navigation. Must ha |
| | | 31 | | /// <returns>true if initialization succeeds and a valid path is present; otherwise, false.</returns> |
| | | 32 | | public bool Initialize(VolumeSurveyResult surveyResult) |
| | | 33 | | { |
| | 294 | 34 | | if (!surveyResult.HasPath) |
| | 2 | 35 | | return false; |
| | | 36 | | |
| | 292 | 37 | | AStarWaypoint[] waypoints = surveyResult.Waypoints!; |
| | 292 | 38 | | TrailMap = surveyResult; |
| | 292 | 39 | | CurrentWaypointIndex = waypoints.Length > 1 ? 1 : 0; |
| | 292 | 40 | | _lastTriedIndex = CurrentWaypointIndex; |
| | 292 | 41 | | return true; |
| | | 42 | | } |
| | | 43 | | |
| | | 44 | | /// <inheritdoc/> |
| | | 45 | | public int GetIndex(Vector3d from) |
| | | 46 | | { |
| | 2 | 47 | | Fixed64 minDistSq = Fixed64.MAX_VALUE; |
| | 2 | 48 | | int bestIndex = -1; |
| | 12 | 49 | | for (int i = 0; i < ActiveWaypoints.Length; i++) |
| | | 50 | | { |
| | 5 | 51 | | Fixed64 distSq = (from - ActiveWaypoints[i].Position).SqrMagnitude; |
| | 5 | 52 | | if (distSq < minDistSq) |
| | | 53 | | { |
| | 5 | 54 | | minDistSq = distSq; |
| | 5 | 55 | | bestIndex = i; |
| | | 56 | | } |
| | | 57 | | |
| | 5 | 58 | | if (minDistSq <= Fixed64.Epsilon) |
| | | 59 | | break; |
| | | 60 | | } |
| | | 61 | | |
| | 2 | 62 | | return bestIndex; |
| | | 63 | | } |
| | | 64 | | |
| | | 65 | | /// <inheritdoc/> |
| | 22 | 66 | | public void AdvanceWaypoint() => CurrentWaypointIndex++; |
| | | 67 | | |
| | | 68 | | /// <inheritdoc/> |
| | | 69 | | public bool TryGetMovementDirection(Vector3d origin, out Vector3d direction) |
| | | 70 | | { |
| | 2 | 71 | | direction = Vector3d.Zero; |
| | | 72 | | |
| | 2 | 73 | | if (TrailMap == null || !TrailMap.HasPath) |
| | 1 | 74 | | return false; |
| | | 75 | | |
| | 1 | 76 | | int closestIndex = GetIndex(origin); |
| | 1 | 77 | | if (closestIndex == -1) |
| | 0 | 78 | | return false; |
| | | 79 | | |
| | 1 | 80 | | direction = (ActiveWaypoints[closestIndex].Position - origin).Normalize(); |
| | 1 | 81 | | return true; |
| | | 82 | | } |
| | | 83 | | |
| | | 84 | | /// <inheritdoc/> |
| | | 85 | | public Vector3d GetCurrentWaypointDirection(Vector3d origin) |
| | | 86 | | { |
| | 15 | 87 | | if (TrailMap == null |
| | 15 | 88 | | || !TrailMap.HasPath |
| | 15 | 89 | | || CurrentWaypointIndex < 0 |
| | 15 | 90 | | || CurrentWaypointIndex >= ActiveWaypoints.Length) |
| | | 91 | | { |
| | 2 | 92 | | return Vector3d.Zero; |
| | | 93 | | } |
| | | 94 | | |
| | 13 | 95 | | Vector3d waypoint = ActiveWaypoints[CurrentWaypointIndex].Position; |
| | 13 | 96 | | if (waypoint == Vector3d.Zero) |
| | 1 | 97 | | return Vector3d.Zero; |
| | | 98 | | |
| | 12 | 99 | | return (waypoint - origin).Normal; |
| | | 100 | | } |
| | | 101 | | |
| | | 102 | | /// <inheritdoc/> |
| | | 103 | | public bool TryGetFallbackDirection(Vector3d from, out Vector3d fallbackDirection) |
| | | 104 | | { |
| | 2 | 105 | | fallbackDirection = Vector3d.Zero; |
| | | 106 | | |
| | 2 | 107 | | if (TrailMap == null || ActiveWaypoints.Length == 0) |
| | 1 | 108 | | return false; |
| | | 109 | | |
| | 1 | 110 | | int searchStart = FixedMath.Clamp(_lastTriedIndex, 0, ActiveWaypoints.Length - 1); |
| | 1 | 111 | | Fixed64 minDistSq = Fixed64.MAX_VALUE; |
| | 1 | 112 | | int bestIndex = -1; |
| | | 113 | | |
| | 6 | 114 | | for (int i = searchStart; i < ActiveWaypoints.Length; i++) |
| | | 115 | | { |
| | 2 | 116 | | Fixed64 distSq = (from - ActiveWaypoints[i].Position).SqrMagnitude; |
| | 2 | 117 | | if (distSq < minDistSq) |
| | | 118 | | { |
| | 2 | 119 | | minDistSq = distSq; |
| | 2 | 120 | | bestIndex = i; |
| | | 121 | | } |
| | | 122 | | } |
| | | 123 | | |
| | 1 | 124 | | if (bestIndex < 0) |
| | 0 | 125 | | return false; |
| | | 126 | | |
| | 1 | 127 | | fallbackDirection = (ActiveWaypoints[bestIndex].Position - from).Normal; |
| | 1 | 128 | | _lastTriedIndex = bestIndex; |
| | 1 | 129 | | return true; |
| | | 130 | | } |
| | | 131 | | |
| | | 132 | | /// <summary> |
| | | 133 | | /// Attempts to retrieve the waypoint at the specified index in the active waypoints collection. |
| | | 134 | | /// </summary> |
| | | 135 | | /// <remarks>Returns false if the trail map is null, does not have a path, or if the index is out of range.</remarks |
| | | 136 | | /// <param name="index">The zero-based index of the waypoint to retrieve. Must be within the bounds of the active wa |
| | | 137 | | /// <param name="waypoint"> |
| | | 138 | | /// When this method returns, contains the waypoint at the specified index if the operation succeeds; otherwise, |
| | | 139 | | /// the default value for <see cref="AStarWaypoint"/>. |
| | | 140 | | /// </param> |
| | | 141 | | /// <returns>true if the waypoint at the specified index was successfully retrieved; otherwise, false.</returns> |
| | | 142 | | public bool TryGetWaypointAt(int index, out AStarWaypoint waypoint) |
| | | 143 | | { |
| | 12 | 144 | | if (TrailMap == null || !TrailMap.HasPath || index < 0 || index >= ActiveWaypoints.Length) |
| | | 145 | | { |
| | 2 | 146 | | waypoint = default; |
| | 2 | 147 | | return false; |
| | | 148 | | } |
| | | 149 | | |
| | 10 | 150 | | waypoint = ActiveWaypoints[index]; |
| | 10 | 151 | | return true; |
| | | 152 | | } |
| | | 153 | | |
| | | 154 | | internal void ResetForReuse() |
| | | 155 | | { |
| | 279 | 156 | | TrailMap = null; |
| | 279 | 157 | | CurrentWaypointIndex = 0; |
| | 279 | 158 | | _lastTriedIndex = 0; |
| | 279 | 159 | | } |
| | | 160 | | } |