Everything related to chat functionality, and updating the projectile prefab to rotate properly

This commit is contained in:
Matt F 2026-05-15 13:40:13 -07:00
parent d92d00c83f
commit 66f84652dc
14 changed files with 1133 additions and 121 deletions

View file

@ -90,6 +90,13 @@ namespace TD.Core
/// Yields the four cardinal neighbors of the given tile (N, E, S, W).
/// Does NOT check walkability or map bounds — that is the caller's job.
/// </summary>
/// <remarks>
/// This is the "edge-adjacency" primitive — two tiles are 4-connected if and
/// only if they share an edge. Used by bake-time checks that test "is this
/// volume adjacent to that volume" (goal-adjacency, leak-exit adjacency, etc.)
/// where edge-sharing is the semantic — NOT for pathfinding. For pathing use
/// <see cref="GetNeighbors8"/>.
/// </remarks>
public static IEnumerable<Vector2Int> GetNeighbors(Vector2Int tile)
{
yield return new Vector2Int(tile.x, tile.y + 1); // North (+z)
@ -98,6 +105,77 @@ namespace TD.Core
yield return new Vector2Int(tile.x - 1, tile.y); // West (-x)
}
/// <summary>
/// Yields all eight neighbors of the given tile — the four cardinals plus
/// the four diagonals (NE, SE, SW, NW). Cardinals are yielded first so
/// equal-cost ordering favors cardinal moves at tie-breaks. Does NOT check
/// walkability or map bounds.
/// </summary>
/// <remarks>
/// Used by pathfinding (A*, BFS) for 8-connected movement. Callers must
/// check <see cref="IsDiagonal"/> for each yielded neighbor and reject the
/// move when either of the two "shoulder" cardinal tiles is non-walkable
/// (corner-cut prevention) — see <see cref="GetCornerShoulders"/>.
/// </remarks>
public static IEnumerable<Vector2Int> GetNeighbors8(Vector2Int tile)
{
// Cardinals first.
yield return new Vector2Int(tile.x, tile.y + 1); // N
yield return new Vector2Int(tile.x + 1, tile.y); // E
yield return new Vector2Int(tile.x, tile.y - 1); // S
yield return new Vector2Int(tile.x - 1, tile.y); // W
// Diagonals.
yield return new Vector2Int(tile.x + 1, tile.y + 1); // NE
yield return new Vector2Int(tile.x + 1, tile.y - 1); // SE
yield return new Vector2Int(tile.x - 1, tile.y - 1); // SW
yield return new Vector2Int(tile.x - 1, tile.y + 1); // NW
}
/// <summary>
/// True if <paramref name="from"/> → <paramref name="to"/> is a single diagonal
/// step (both X and Y differ by exactly 1). Both tiles assumed to be 8-neighbors.
/// </summary>
public static bool IsDiagonal(Vector2Int from, Vector2Int to)
{
return Mathf.Abs(from.x - to.x) == 1 && Mathf.Abs(from.y - to.y) == 1;
}
/// <summary>
/// For a diagonal step <paramref name="from"/> → <paramref name="to"/>, returns the
/// two cardinal "shoulder" tiles. To prevent corner-cutting (squeezing through a
/// 1-tile diagonal gap between walls), pathfinders should reject the diagonal step
/// if either shoulder is non-walkable.
/// </summary>
public static void GetCornerShoulders(Vector2Int from, Vector2Int to,
out Vector2Int shoulderA, out Vector2Int shoulderB)
{
shoulderA = new Vector2Int(to.x, from.y); // step in X first
shoulderB = new Vector2Int(from.x, to.y); // step in Y first
}
/// <summary>
/// Cost of stepping from <paramref name="from"/> to its 8-neighbor <paramref name="to"/>.
/// Returns 1.0 for cardinal moves, √2 for diagonals. Used by 8-connected A*.
/// </summary>
public static float StepCost(Vector2Int from, Vector2Int to)
{
return IsDiagonal(from, to) ? 1.41421356f : 1f;
}
/// <summary>
/// Octile distance between two tiles. Admissible heuristic for 8-connected A* on
/// a uniform-cost grid (cardinal cost 1, diagonal cost √2). Equivalent to:
/// <c>max(|dx|, |dy|) + (√2 - 1) * min(|dx|, |dy|)</c>.
/// </summary>
public static float OctileDistance(Vector2Int a, Vector2Int b)
{
int dx = Mathf.Abs(a.x - b.x);
int dy = Mathf.Abs(a.y - b.y);
int max = Mathf.Max(dx, dy);
int min = Mathf.Min(dx, dy);
return max + 0.41421356f * min;
}
// ----- Footprint helpers ---------------------------------------------------
//
// Towers occupy a footprint of N x M tiles anchored at a single tile coordinate.