Grid/AStarNode.cs

A data class for A* pathfinding nodes. Wraps a Cell, stores parent, movement tag, g/h/f costs, heap index and provides distance, comparison and equality logic for use in a heap-based open set.

Native Interop
🐞 StartPosition and Direction access Parent.Current when Parent can be null on nodes constructed without a parent, which throws NullReferenceException on access.
using System;
using Sandbox;

namespace GridAStar;

public partial class AStarNode : IHeapItem<AStarNode>, IEquatable<AStarNode>
{
	public Cell Current { get; internal set; } = null;
	public AStarNode Parent { get; internal set; } = null;
	public string MovementTag { get; private set; } = string.Empty;
	public Vector3 StartPosition => Parent.Current.Position;
	public Vector3 EndPosition => Current.Position;
	public Vector3 Direction => (EndPosition - StartPosition).Normal;
	public bool Occupied => Current.Occupied;
	public Component OccupyingEntity => Current.OccupyingEntity;
	public CellTags Tags => Current.Tags;
	public float gCost { get; internal set; } = 0f;
	public float hCost { get; internal set; } = 0f;
	public float fCost => gCost + hCost;
	public int HeapIndex { get; set; }

	public AStarNode( Cell cell )
	{
		Current = cell;
	}
	public AStarNode( Cell cell, AStarNode parent )
	{
		Current = cell;
		Parent = parent;
	}
	public AStarNode( Cell cell, string tag )
	{
		Current = cell;
		MovementTag = tag;
	}
	public AStarNode( Cell cell, AStarNode parent, string tag )
	{
		Current = cell;
		Parent = parent;
		MovementTag = tag;
	}

	public float Distance( Cell other ) => Current.Position.Distance( other.Position );
	public float Distance( AStarNode other ) => Current.Position.Distance( other.Current.Position );

	public int CompareTo( AStarNode other )
	{
		var compare = fCost.CompareTo( other.fCost );
		if ( compare == 0 )
			compare = hCost.CompareTo( other.hCost );
		return -compare;
	}

	public override int GetHashCode() => Current.GetHashCode();

	public static bool operator ==( AStarNode a, AStarNode b ) => a?.Equals( b ) ?? false;
	public static bool operator !=( AStarNode a, AStarNode b ) => !a?.Equals( b ) ?? false;

	public override bool Equals( object obj )
	{
		if ( obj is not AStarNode node ) return false;

		if ( node?.Current != Current ) return false;
		if ( node?.Parent?.Current != Parent?.Current ) return false;
		if ( node?.MovementTag != MovementTag ) return false;

		return true;
	}

	public bool Equals( AStarNode other )
	{
		if ( other is not AStarNode ) return false;

		if ( other?.Current != Current ) return false;
		if ( other?.Parent?.Current != Parent?.Current ) return false;
		if ( other?.MovementTag != MovementTag ) return false;

		return true;
	}
}