| | | 1 | | using FixedMathSharp; |
| | | 2 | | using System; |
| | | 3 | | |
| | | 4 | | namespace Trailblazer.Pathing; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// Internal waypoint guide produced from a staged transition-aware route. |
| | | 8 | | /// </summary> |
| | | 9 | | internal sealed class HybridGuide : IWaypointGuide |
| | | 10 | | { |
| | | 11 | | /// <summary> |
| | | 12 | | /// The active waypoints for this guide, which may be generated from either A* or flow field segments depending on t |
| | | 13 | | /// This allows the guide to provide consistent waypoint-based navigation regardless of the underlying pathfinding s |
| | | 14 | | /// </summary> |
| | | 15 | | public AStarWaypoint[] ActiveWaypoints { get; private set; } = Array.Empty<AStarWaypoint>(); |
| | | 16 | | |
| | | 17 | | /// <summary> |
| | | 18 | | /// Returns the index of the current waypoint being pursued. |
| | | 19 | | /// This is used to track progression through the waypoints of the current stage in the plan, and it allows the guid |
| | | 20 | | /// The index is updated as the agent reaches each waypoint, and it helps ensure that the guide provides directions |
| | | 21 | | /// </summary> |
| | | 22 | | public int CurrentWaypointIndex { get; private set; } |
| | | 23 | | |
| | | 24 | | /// <summary> |
| | | 25 | | /// Tracks the last waypoint index that was used to provide a fallback direction. |
| | | 26 | | /// This helps ensure that fallback directions are provided in a forward progression along the path, rather than rep |
| | | 27 | | /// By updating this index each time a fallback direction is provided, the guide can offer more dynamic and contextu |
| | | 28 | | /// </summary> |
| | | 29 | | private int _lastTriedIndex; |
| | | 30 | | |
| | | 31 | | /// <summary> |
| | | 32 | | /// Initializes the guide with the given waypoints for the current stage of the plan. |
| | | 33 | | /// </summary> |
| | | 34 | | /// <param name="waypoints">The waypoints to initialize the guide with.</param> |
| | | 35 | | /// <returns>True if the guide was successfully initialized; otherwise, false.</returns> |
| | | 36 | | public bool Initialize(AStarWaypoint[] waypoints) |
| | | 37 | | { |
| | 8 | 38 | | if (waypoints == null || waypoints.Length == 0) |
| | 2 | 39 | | return false; |
| | | 40 | | |
| | 6 | 41 | | ActiveWaypoints = waypoints; |
| | 6 | 42 | | CurrentWaypointIndex = waypoints.Length > 1 ? 1 : 0; |
| | 6 | 43 | | _lastTriedIndex = CurrentWaypointIndex; |
| | 6 | 44 | | return true; |
| | | 45 | | } |
| | | 46 | | |
| | | 47 | | /// <inheritdoc/> |
| | | 48 | | public int GetIndex(Vector3d from) |
| | | 49 | | { |
| | 2 | 50 | | Fixed64 minDistSq = Fixed64.MAX_VALUE; |
| | 2 | 51 | | int bestIndex = -1; |
| | 12 | 52 | | for (int i = 0; i < ActiveWaypoints.Length; i++) |
| | | 53 | | { |
| | 5 | 54 | | Fixed64 distSq = (from - ActiveWaypoints[i].Position).SqrMagnitude; |
| | 5 | 55 | | if (distSq < minDistSq) |
| | | 56 | | { |
| | 5 | 57 | | minDistSq = distSq; |
| | 5 | 58 | | bestIndex = i; |
| | | 59 | | } |
| | | 60 | | |
| | 5 | 61 | | if (minDistSq <= Fixed64.Epsilon) |
| | | 62 | | break; |
| | | 63 | | } |
| | | 64 | | |
| | 2 | 65 | | return bestIndex; |
| | | 66 | | } |
| | | 67 | | |
| | | 68 | | /// <inheritdoc/> |
| | 5 | 69 | | public void AdvanceWaypoint() => CurrentWaypointIndex++; |
| | | 70 | | |
| | | 71 | | /// <inheritdoc/> |
| | | 72 | | public bool TryGetMovementDirection(Vector3d origin, out Vector3d direction) |
| | | 73 | | { |
| | 2 | 74 | | direction = Vector3d.Zero; |
| | | 75 | | |
| | 2 | 76 | | if (ActiveWaypoints == null || ActiveWaypoints.Length == 0) |
| | 1 | 77 | | return false; |
| | | 78 | | |
| | 1 | 79 | | int closestIndex = GetIndex(origin); |
| | 1 | 80 | | if (closestIndex == -1) |
| | 0 | 81 | | return false; |
| | | 82 | | |
| | 1 | 83 | | direction = (ActiveWaypoints[closestIndex].Position - origin).Normalize(); |
| | 1 | 84 | | return true; |
| | | 85 | | } |
| | | 86 | | |
| | | 87 | | /// <inheritdoc/> |
| | | 88 | | public Vector3d GetCurrentWaypointDirection(Vector3d origin) |
| | | 89 | | { |
| | 4 | 90 | | if (ActiveWaypoints == null |
| | 4 | 91 | | || CurrentWaypointIndex < 0 |
| | 4 | 92 | | || CurrentWaypointIndex >= ActiveWaypoints.Length) |
| | | 93 | | { |
| | 2 | 94 | | return Vector3d.Zero; |
| | | 95 | | } |
| | | 96 | | |
| | 2 | 97 | | Vector3d waypoint = ActiveWaypoints[CurrentWaypointIndex].Position; |
| | 2 | 98 | | if (waypoint == Vector3d.Zero) |
| | 1 | 99 | | return Vector3d.Zero; |
| | | 100 | | |
| | 1 | 101 | | return (waypoint - origin).Normal; |
| | | 102 | | } |
| | | 103 | | |
| | | 104 | | /// <inheritdoc/> |
| | | 105 | | public bool TryGetFallbackDirection(Vector3d from, out Vector3d fallbackDirection) |
| | | 106 | | { |
| | 2 | 107 | | fallbackDirection = Vector3d.Zero; |
| | | 108 | | |
| | 2 | 109 | | if (ActiveWaypoints == null || ActiveWaypoints.Length == 0) |
| | 1 | 110 | | return false; |
| | | 111 | | |
| | 1 | 112 | | int searchStart = FixedMath.Clamp(_lastTriedIndex, 0, ActiveWaypoints.Length - 1); |
| | 1 | 113 | | Fixed64 minDistSq = Fixed64.MAX_VALUE; |
| | 1 | 114 | | int bestIndex = -1; |
| | | 115 | | |
| | 6 | 116 | | for (int i = searchStart; i < ActiveWaypoints.Length; i++) |
| | | 117 | | { |
| | 2 | 118 | | Fixed64 distSq = (from - ActiveWaypoints[i].Position).SqrMagnitude; |
| | 2 | 119 | | if (distSq < minDistSq) |
| | | 120 | | { |
| | 2 | 121 | | minDistSq = distSq; |
| | 2 | 122 | | bestIndex = i; |
| | | 123 | | } |
| | | 124 | | } |
| | | 125 | | |
| | 1 | 126 | | if (bestIndex < 0) |
| | 0 | 127 | | return false; |
| | | 128 | | |
| | 1 | 129 | | fallbackDirection = (ActiveWaypoints[bestIndex].Position - from).Normal; |
| | 1 | 130 | | _lastTriedIndex = bestIndex; |
| | 1 | 131 | | return true; |
| | | 132 | | } |
| | | 133 | | |
| | | 134 | | /// <summary> |
| | | 135 | | /// Attempts to get the waypoint at the specified index. |
| | | 136 | | /// This can be used to retrieve specific waypoints for debugging, visualization, or advanced navigation logic that |
| | | 137 | | /// By providing a method to access waypoints by index, the guide allows for greater flexibility and control over ho |
| | | 138 | | /// </summary> |
| | | 139 | | /// <param name="index">The index of the waypoint to retrieve.</param> |
| | | 140 | | /// <param name="waypoint">When this method returns, contains the waypoint at the specified index, if the index is v |
| | | 141 | | /// <returns>True if the waypoint was successfully retrieved; otherwise, false.</returns> |
| | | 142 | | public bool TryGetWaypointAt(int index, out AStarWaypoint waypoint) |
| | | 143 | | { |
| | 3 | 144 | | if (ActiveWaypoints == null || index < 0 || index >= ActiveWaypoints.Length) |
| | | 145 | | { |
| | 1 | 146 | | waypoint = default; |
| | 1 | 147 | | return false; |
| | | 148 | | } |
| | | 149 | | |
| | 2 | 150 | | waypoint = ActiveWaypoints[index]; |
| | 2 | 151 | | return true; |
| | | 152 | | } |
| | | 153 | | } |