b451f83174
Captures the pre-Godot-port state of the codebase. This is the rollback anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md). All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
49 lines
1.7 KiB
C#
49 lines
1.7 KiB
C#
using Theriapolis.Core.Util;
|
|
|
|
namespace Theriapolis.Core.Rules.Combat;
|
|
|
|
/// <summary>
|
|
/// Tactical-tile line-of-sight via Bresenham. The caller supplies a
|
|
/// "blocked at (x, y)?" predicate so this helper stays free of a hard
|
|
/// dependency on TacticalChunk / WorldState — Phase 5 M4 tests use a flat
|
|
/// arena (always-clear); M5 plugs in the live tactical-tile sampler.
|
|
/// </summary>
|
|
public static class LineOfSight
|
|
{
|
|
/// <summary>
|
|
/// True if a straight line from <paramref name="from"/> to
|
|
/// <paramref name="to"/> traverses only un-blocked tiles. Endpoints
|
|
/// themselves are NOT consulted — only the intermediate tiles.
|
|
/// </summary>
|
|
public static bool HasLine(Vec2 from, Vec2 to, System.Func<int, int, bool> isBlockedAt)
|
|
{
|
|
int x0 = (int)System.Math.Floor(from.X);
|
|
int y0 = (int)System.Math.Floor(from.Y);
|
|
int x1 = (int)System.Math.Floor(to.X);
|
|
int y1 = (int)System.Math.Floor(to.Y);
|
|
|
|
int dx = System.Math.Abs(x1 - x0);
|
|
int dy = System.Math.Abs(y1 - y0);
|
|
int sx = x0 < x1 ? 1 : -1;
|
|
int sy = y0 < y1 ? 1 : -1;
|
|
int err = dx - dy;
|
|
|
|
int x = x0, y = y0;
|
|
while (true)
|
|
{
|
|
// Skip the endpoint itself
|
|
if (!(x == x0 && y == y0) && !(x == x1 && y == y1))
|
|
{
|
|
if (isBlockedAt(x, y)) return false;
|
|
}
|
|
if (x == x1 && y == y1) return true;
|
|
int e2 = 2 * err;
|
|
if (e2 > -dy) { err -= dy; x += sx; }
|
|
if (e2 < dx) { err += dx; y += sy; }
|
|
}
|
|
}
|
|
|
|
/// <summary>Convenience: always-clear arena. Used by combat-duel and most M4 tests.</summary>
|
|
public static readonly System.Func<int, int, bool> AlwaysClear = (_, _) => false;
|
|
}
|