Chessistics/Scripts/Board/BoardView.cs
Samuel Bouchet a7280b1a5a Overhaul turn mechanics, collision destruction, and visual animations
- New turn order: produce -> transfer -> move -> collision resolution
- Collisions now destroy weaker pieces (status > level > mutual destruction)
  instead of halting the simulation. SimPhase.Collision removed.
- Add piece Level property (all level 1 in proto, prepared for future)
- Production fires every turn (interval concept removed), buffer = Amount
  (default 1, future 2-4), leftovers overwritten each turn
- Transfer tiebreaker: status > level > clockwise direction (alternating
  even/odd turns in y-up coords), replaces distance-to-production
- Demands always accept matching cargo even when already satisfied
- TurnNumber added to all turn events for animation grouping
- Simultaneous animations: produce flash, cargo slide, parallel piece moves
- Camera centering fix + middle-click pan
- GDD updated with new rules + lore section added

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 21:44:12 +02:00

98 lines
2.3 KiB
C#

using Godot;
using System.Collections.Generic;
using Chessistics.Engine.Model;
namespace Chessistics.Scripts.Board;
public partial class BoardView : Node2D
{
public const int CellSize = 80;
private readonly Dictionary<Coords, CellView> _cells = new();
private int _width;
private int _height;
public void BuildBoard(LevelDef level)
{
// Clear existing children
foreach (var child in GetChildren())
child.QueueFree();
_cells.Clear();
_width = level.Width;
_height = level.Height;
var boardState = BoardState.FromLevel(level);
for (int col = 0; col < level.Width; col++)
{
for (int row = 0; row < level.Height; row++)
{
var coords = new Coords(col, row);
var cellView = new CellView();
cellView.Setup(coords, boardState.GetCell(coords), CellSize);
AddChild(cellView);
_cells[coords] = cellView;
}
}
// Label productions and demands
foreach (var prod in level.Productions)
{
if (_cells.TryGetValue(prod.Position, out var cell))
cell.SetLabel(prod.Name);
}
foreach (var demand in level.Demands)
{
if (_cells.TryGetValue(demand.Position, out var cell))
cell.SetLabel(demand.Name);
}
}
public Coords? PixelToCoords(Vector2 localPos)
{
int col = Mathf.FloorToInt(localPos.X / CellSize);
// Cell at row R has top-left Y = -R*CellSize, extending downward.
// floor(-Y/Size) != -floor(Y/Size) for non-integers, so use the latter.
int row = -Mathf.FloorToInt(localPos.Y / CellSize);
var coords = new Coords(col, row);
return coords.IsOnBoard(_width, _height) ? coords : null;
}
public Vector2 CoordsToPixel(Coords coords)
{
return new Vector2(
coords.Col * CellSize + CellSize / 2f,
-coords.Row * CellSize + CellSize / 2f
);
}
public CellView? GetCellView(Coords coords)
=> _cells.GetValueOrDefault(coords);
public void SetHoverCell(Coords? coords)
{
foreach (var cell in _cells.Values)
cell.SetHover(false);
if (coords != null && _cells.TryGetValue(coords.Value, out var cellView))
cellView.SetHover(true);
}
public void ClearHighlights()
{
foreach (var cell in _cells.Values)
cell.SetHighlight(false);
}
public void HighlightCells(IEnumerable<Coords> cells, Color color)
{
foreach (var coords in cells)
{
if (_cells.TryGetValue(coords, out var cellView))
cellView.SetHighlightColor(color);
}
}
}