2022-12-02 21:09:40 +01:00
|
|
|
using System;
|
2023-01-03 17:46:55 +01:00
|
|
|
using System.Linq;
|
2022-12-02 21:09:40 +01:00
|
|
|
using Godot;
|
2023-08-13 21:18:46 +02:00
|
|
|
using GodotComponentTest.utils;
|
2022-12-02 21:09:40 +01:00
|
|
|
|
2023-08-12 15:20:23 +02:00
|
|
|
public class HexCell : IEquatable<HexCell>
|
2022-12-02 21:09:40 +01:00
|
|
|
{
|
2023-08-12 15:20:23 +02:00
|
|
|
public override bool Equals(object obj)
|
|
|
|
{
|
|
|
|
if (ReferenceEquals(null, obj)) return false;
|
|
|
|
if (ReferenceEquals(this, obj)) return true;
|
|
|
|
if (obj.GetType() != this.GetType()) return false;
|
|
|
|
return Equals((HexCell)obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
{
|
|
|
|
return _cubeCoords.GetHashCode();
|
|
|
|
}
|
|
|
|
|
2023-08-13 21:18:46 +02:00
|
|
|
public static readonly Vector2 Size = new(1, Mathf.Sqrt(3) / 2);
|
|
|
|
private const float Width = 2;
|
|
|
|
private static readonly float Height = Mathf.Sqrt(3);
|
2022-12-02 21:09:40 +01:00
|
|
|
|
2023-08-13 21:18:46 +02:00
|
|
|
public static readonly Vector3 DIR_N = new(0, 1, -1);
|
|
|
|
public static readonly Vector3 DIR_NE = new(1, 0, -1);
|
|
|
|
public static readonly Vector3 DIR_SE = new(1, -1, 0);
|
|
|
|
public static readonly Vector3 DIR_S = new(0, -1, 1);
|
|
|
|
public static readonly Vector3 DIR_SW = new(-1, 0, 1);
|
|
|
|
public static readonly Vector3 DIR_NW = new(-1, 1, 0);
|
|
|
|
|
|
|
|
public static readonly Vector3[] NeighborDirections =
|
|
|
|
{
|
|
|
|
DIR_N,
|
|
|
|
DIR_NW,
|
|
|
|
DIR_SW,
|
|
|
|
DIR_S,
|
|
|
|
DIR_SE,
|
|
|
|
DIR_NE
|
|
|
|
};
|
|
|
|
|
|
|
|
private static readonly Vector2 CornerNW = new Vector2(-Width / 4, -Height / 2);
|
|
|
|
private static readonly Vector2 CornerNE = new Vector2(Width / 4, -Height / 2);
|
|
|
|
private static readonly Vector2 CornerE = new Vector2(Width / 2, 0);
|
|
|
|
private static readonly Vector2 CornerSE = new Vector2(Width / 4, Height / 2);
|
|
|
|
private static readonly Vector2 CornerSW = new Vector2(-Width / 4, Height / 2);
|
|
|
|
private static readonly Vector2 CornerW = new Vector2(-Width / 2, 0);
|
|
|
|
private static readonly Vector2 PlaneNormalN = new Vector2(0, 1);
|
|
|
|
|
|
|
|
private static readonly Vector2 PlaneNormalNE =
|
|
|
|
-new Vector2(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30)));
|
|
|
|
|
|
|
|
private static readonly Vector2 PlaneNormalSE =
|
|
|
|
-new Vector2(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30)));
|
|
|
|
|
|
|
|
private static readonly Vector2 PlaneNormalS = new Vector2(0, -1);
|
|
|
|
|
|
|
|
private static readonly Vector2 PlaneNormalSW =
|
|
|
|
new Vector2(Mathf.Cos(Mathf.Deg2Rad(30)), -Mathf.Sin(Mathf.Deg2Rad(30)));
|
|
|
|
|
|
|
|
private static readonly Vector2 PlaneNormalNW =
|
|
|
|
new Vector2(Mathf.Cos(Mathf.Deg2Rad(-30)), -Mathf.Sin(Mathf.Deg2Rad(-30)));
|
|
|
|
|
|
|
|
private static readonly Plane2D[] BoundaryPlanes =
|
|
|
|
{
|
|
|
|
new Plane2D(CornerNE, PlaneNormalN),
|
|
|
|
new Plane2D(CornerNW, PlaneNormalNW),
|
|
|
|
new Plane2D(CornerW, PlaneNormalSW),
|
|
|
|
new Plane2D(CornerSW, PlaneNormalS),
|
|
|
|
new Plane2D(CornerSE, PlaneNormalSE),
|
|
|
|
new Plane2D(CornerE, PlaneNormalNE),
|
|
|
|
};
|
2022-12-28 16:22:53 +01:00
|
|
|
|
|
|
|
public HexCell()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-12-02 21:09:40 +01:00
|
|
|
public HexCell(float cubeX, float cubeY, float cubeZ)
|
|
|
|
{
|
|
|
|
CubeCoords = RoundCoords(new Vector3(cubeX, cubeY, cubeZ));
|
|
|
|
}
|
|
|
|
|
2023-08-12 14:27:30 +02:00
|
|
|
public virtual bool Equals(HexCell other)
|
2023-07-15 21:50:38 +02:00
|
|
|
{
|
2023-08-12 14:27:30 +02:00
|
|
|
if (other == null)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2023-08-13 21:18:46 +02:00
|
|
|
|
2023-08-12 14:27:30 +02:00
|
|
|
return CubeCoords == other.CubeCoords;
|
2023-07-15 21:50:38 +02:00
|
|
|
}
|
2023-08-12 15:20:23 +02:00
|
|
|
|
|
|
|
public static bool operator ==(HexCell cellA, HexCell cellB)
|
|
|
|
{
|
|
|
|
return Equals(cellA, cellB);
|
|
|
|
}
|
2023-08-13 21:18:46 +02:00
|
|
|
|
2023-08-12 15:20:23 +02:00
|
|
|
public static bool operator !=(HexCell cellA, HexCell cellB)
|
|
|
|
{
|
|
|
|
return !(cellA == cellB);
|
|
|
|
}
|
2023-08-13 21:18:46 +02:00
|
|
|
|
2022-12-02 21:09:40 +01:00
|
|
|
public HexCell(Vector3 cubeCoords)
|
|
|
|
{
|
|
|
|
CubeCoords = cubeCoords;
|
|
|
|
}
|
|
|
|
|
|
|
|
public HexCell(float axialX, float axialY)
|
|
|
|
{
|
|
|
|
AxialCoords = new Vector2(axialX, axialY);
|
|
|
|
}
|
|
|
|
|
|
|
|
public HexCell(Vector2 axialCoords)
|
|
|
|
{
|
|
|
|
AxialCoords = axialCoords;
|
|
|
|
}
|
|
|
|
|
|
|
|
public HexCell(HexCell other)
|
|
|
|
{
|
|
|
|
CubeCoords = other.CubeCoords;
|
|
|
|
}
|
|
|
|
|
2023-01-03 17:46:55 +01:00
|
|
|
public static HexCell FromOffsetCoords(Vector2 offsetCoords)
|
|
|
|
{
|
|
|
|
HexCell result = new HexCell();
|
|
|
|
result.OffsetCoords = offsetCoords;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-12-02 21:09:40 +01:00
|
|
|
private Vector3 _cubeCoords;
|
2022-12-28 16:22:53 +01:00
|
|
|
|
2022-12-02 21:09:40 +01:00
|
|
|
public Vector3 CubeCoords
|
|
|
|
{
|
2022-12-28 16:22:53 +01:00
|
|
|
get { return _cubeCoords; }
|
2022-12-02 21:09:40 +01:00
|
|
|
set
|
|
|
|
{
|
|
|
|
if (Mathf.Abs(value.x + value.y + value.z) > 0.0001)
|
|
|
|
{
|
|
|
|
GD.Print("Warning: Invalid cube coordinates for hex (x + y + z != 0): ", value.ToString());
|
|
|
|
}
|
2022-12-28 16:22:53 +01:00
|
|
|
|
2022-12-02 21:09:40 +01:00
|
|
|
_cubeCoords = RoundCoords(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public Vector2 AxialCoords
|
|
|
|
{
|
|
|
|
get => new Vector2(CubeCoords.x, CubeCoords.y);
|
|
|
|
|
2022-12-28 16:22:53 +01:00
|
|
|
set { CubeCoords = AxialToCubeCoords(value); }
|
2022-12-02 21:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public Vector2 OffsetCoords
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
int x = (int)CubeCoords.x;
|
|
|
|
int y = (int)CubeCoords.y;
|
2023-08-13 21:18:46 +02:00
|
|
|
int offY = y + (x - (x & 1)) / 2;
|
|
|
|
return new Vector2(x, offY);
|
2022-12-02 21:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
set
|
|
|
|
{
|
|
|
|
int x = (int)value.x;
|
|
|
|
int y = (int)value.y;
|
2023-08-13 21:18:46 +02:00
|
|
|
int cubeY = y - (x - (x & 1)) / 2;
|
|
|
|
AxialCoords = new Vector2(x, cubeY);
|
2022-12-02 21:09:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public Vector3 AxialToCubeCoords(Vector2 axialCoords)
|
|
|
|
{
|
|
|
|
return new Vector3(axialCoords.x, axialCoords.y, -axialCoords.x - axialCoords.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Vector3 RoundCoords(Vector2 coords)
|
|
|
|
{
|
|
|
|
Vector3 cubeCoords = AxialToCubeCoords(coords);
|
|
|
|
|
|
|
|
return RoundCoords(cubeCoords);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Vector3 RoundCoords(Vector3 cubeCoords)
|
|
|
|
{
|
|
|
|
Vector3 rounded = new Vector3(
|
|
|
|
Mathf.Round(cubeCoords.x),
|
|
|
|
Mathf.Round(cubeCoords.y),
|
|
|
|
Mathf.Round(cubeCoords.z));
|
|
|
|
|
|
|
|
Vector3 diffs = (rounded - cubeCoords).Abs();
|
|
|
|
if (diffs.x > diffs.y && diffs.x > diffs.z)
|
|
|
|
{
|
|
|
|
rounded.x = -rounded.y - rounded.z;
|
|
|
|
}
|
|
|
|
else if (diffs.y > diffs.z)
|
|
|
|
{
|
|
|
|
rounded.y = -rounded.x - rounded.z;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rounded.z = -rounded.x - rounded.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rounded;
|
|
|
|
}
|
|
|
|
|
2023-01-03 17:46:55 +01:00
|
|
|
public HexCell GetAdjacent(Vector3 dir)
|
2022-12-02 21:09:40 +01:00
|
|
|
{
|
|
|
|
return new HexCell(this.CubeCoords + dir);
|
|
|
|
}
|
2023-01-03 17:46:55 +01:00
|
|
|
|
2023-07-15 21:50:38 +02:00
|
|
|
public HexCell[] GetAllAdjacent()
|
|
|
|
{
|
|
|
|
return new[]
|
|
|
|
{
|
2023-08-13 21:18:46 +02:00
|
|
|
GetAdjacent(DIR_NE),
|
|
|
|
GetAdjacent(DIR_SE),
|
|
|
|
GetAdjacent(DIR_S),
|
|
|
|
GetAdjacent(DIR_SW),
|
2023-07-15 21:50:38 +02:00
|
|
|
GetAdjacent(DIR_NW),
|
|
|
|
GetAdjacent(DIR_N)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-03 17:46:55 +01:00
|
|
|
public int DistanceTo(HexCell target)
|
|
|
|
{
|
|
|
|
return (int)(
|
|
|
|
Mathf.Abs(_cubeCoords.x - target.CubeCoords.x)
|
|
|
|
+ Mathf.Abs(_cubeCoords.y - target.CubeCoords.y)
|
|
|
|
+ Mathf.Abs(_cubeCoords.z - target.CubeCoords.z)
|
|
|
|
) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
public HexCell[] LineTo(HexCell target)
|
|
|
|
{
|
|
|
|
HexCell nudgedTarget = new HexCell();
|
|
|
|
nudgedTarget.CubeCoords = target.CubeCoords + new Vector3(1.0e-6f, 2.0e-6f, -3.0e-6f);
|
|
|
|
int steps = DistanceTo(target);
|
|
|
|
|
|
|
|
HexCell[] path = new HexCell[steps + 1];
|
|
|
|
|
|
|
|
foreach (int dist in Enumerable.Range(0, steps))
|
|
|
|
{
|
|
|
|
path[dist] = new HexCell();
|
|
|
|
path[dist].CubeCoords = CubeCoords.LinearInterpolate(nudgedTarget.CubeCoords, (float)dist / steps);
|
|
|
|
}
|
2023-07-15 21:50:38 +02:00
|
|
|
|
2023-01-03 17:46:55 +01:00
|
|
|
path[steps] = target;
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
2023-08-13 21:18:46 +02:00
|
|
|
|
|
|
|
public void QueryClosestCellBoundary(Vector2 pointLocal, Vector2 dir, out int neighbourIndex, out float distance)
|
|
|
|
{
|
|
|
|
distance = Single.PositiveInfinity;
|
|
|
|
neighbourIndex = 0;
|
|
|
|
foreach (int i in Enumerable.Range(0, 6))
|
|
|
|
{
|
|
|
|
if (BoundaryPlanes[i].Normal.Dot(dir) >= Plane2D.DistancePrecision)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
float planeDistance = BoundaryPlanes[i].DistanceToLineSegment(pointLocal, dir);
|
|
|
|
if (planeDistance > Single.NegativeInfinity && planeDistance < distance)
|
|
|
|
{
|
|
|
|
distance = planeDistance;
|
|
|
|
neighbourIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public HexCell NextCellAlongLine(Vector2 pointLocal, Vector2 dir)
|
|
|
|
{
|
|
|
|
int planeIndex;
|
|
|
|
|
|
|
|
QueryClosestCellBoundary(pointLocal, dir, out planeIndex, out _);
|
|
|
|
|
|
|
|
return GetAdjacent(NeighborDirections[planeIndex]);
|
|
|
|
}
|
2022-12-02 21:09:40 +01:00
|
|
|
}
|