feat(grid): Replace real-time CTF with turn-based grid system

Replace continuous free-form movement with discrete 20x50 grid-based
gameplay featuring asymmetric movement mechanics:

- Blue/Red teams with 3 units each
- Zone-based movement: orthogonal (4 dir) in offense, diagonal (8 dir) in defense
- Alternating turns with click-to-select, click-to-move input
- Fog of war: 3-cell Chebyshev vision radius per unit
- Defense speed nerf: skip every 4th move in own zone
- AI opponent that chases flag carriers and advances toward enemy flag
- Collision resolution: defender wins in their zone, lower ID wins in neutral

Implements all 3 phases from the plan:
- Phase 1: Playable grid with hot-seat two-player
- Phase 2: Fog of war + defense speed nerf
- Phase 3: AI opponent

Deleted obsolete files: Flag.cs, Unit.cs, RouteDrawer.cs, SimpleAI.cs, Visibility.cs

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John Lamb
2026-02-07 17:05:44 -06:00
parent 0de174eb1a
commit e4ac24f989
17 changed files with 1658 additions and 1092 deletions

View File

@@ -5,22 +5,23 @@ public class CameraController : MonoBehaviour
{
// Zoom settings
public float minZoom = 5f;
public float maxZoom = 25f;
public float maxZoom = 35f;
public float zoomSpeed = 2f;
public float pinchZoomSpeed = 0.1f;
// Pan settings
public float panSpeed = 1f;
// Map bounds (grid-based)
float mapWidth = ZoneBoundaries.BoardWidth;
float mapHeight = ZoneBoundaries.BoardHeight;
Camera cam;
Vector2 lastPanPosition;
bool isPanning;
float lastPinchDistance;
bool isPinching;
// Track if we started on a unit (don't pan if drawing route)
bool startedOnUnit;
void Start()
{
cam = Camera.main;
@@ -45,7 +46,7 @@ public class CameraController : MonoBehaviour
Zoom(-scroll * zoomSpeed * Time.deltaTime * 10f);
}
// Right-click pan (left-click is for route drawing)
// Right-click pan (left-click is for unit selection)
if (mouse.rightButton.wasPressedThisFrame)
{
lastPanPosition = mouse.position.ReadValue();
@@ -115,32 +116,8 @@ public class CameraController : MonoBehaviour
var primaryTouch = touch.primaryTouch;
Vector2 touchPos = primaryTouch.position.ReadValue();
// Check if touch started on a unit
if (primaryTouch.press.wasPressedThisFrame)
{
Vector2 worldPos = cam.ScreenToWorldPoint(touchPos);
var hit = Physics2D.OverlapPoint(worldPos);
startedOnUnit = hit != null && hit.GetComponent<Unit>() != null;
if (!startedOnUnit)
{
lastPanPosition = touchPos;
isPanning = true;
}
}
else if (primaryTouch.press.wasReleasedThisFrame)
{
isPanning = false;
startedOnUnit = false;
}
// Single finger pan (only if not drawing route)
if (isPanning && !startedOnUnit && primaryTouch.press.isPressed)
{
Vector2 delta = touchPos - lastPanPosition;
Pan(-delta);
lastPanPosition = touchPos;
}
// Two-finger gestures only for pan on touch - single finger is for unit selection
// Don't initiate pan on single touch
}
else
{
@@ -174,10 +151,14 @@ public class CameraController : MonoBehaviour
float halfHeight = cam.orthographicSize;
float halfWidth = halfHeight * cam.aspect;
float minX = -Game.MapWidth / 2f + halfWidth;
float maxX = Game.MapWidth / 2f - halfWidth;
float minY = -Game.MapHeight / 2f + halfHeight;
float maxY = Game.MapHeight / 2f - halfHeight;
float minX = -mapWidth / 2f + halfWidth;
float maxX = mapWidth / 2f - halfWidth;
float minY = -mapHeight / 2f + halfHeight;
float maxY = mapHeight / 2f - halfHeight;
// Handle case where zoom is wider than map
if (minX > maxX) minX = maxX = 0;
if (minY > maxY) minY = maxY = 0;
Vector3 pos = cam.transform.position;
pos.x = Mathf.Clamp(pos.x, minX, maxX);