Chessistics/Scripts/Presentation/EventAnimator.cs
Samuel Bouchet e6eaae43ab Initial commit: Chessistics prototype v0.3
Black box sim engine (commands in, events out) with 3 piece types
(Rook, Bishop, Knight), cargo transfer system with social status
priority, collision detection, and victory/defeat conditions.

57 tests covering rules, simulation, loading, and solvability.
Godot 4 presentation layer scaffolding.
2026-04-10 14:58:03 +02:00

169 lines
5.7 KiB
C#

using Godot;
using System;
using System.Collections.Generic;
using Chessistics.Engine.Events;
using Chessistics.Engine.Model;
using Chessistics.Scripts.Board;
using Chessistics.Scripts.Pieces;
using Chessistics.Scripts.UI;
namespace Chessistics.Scripts.Presentation;
public partial class EventAnimator : Node
{
private BoardView _boardView = null!;
private ObjectivePanel _objectivePanel = null!;
private ControlBar _controlBar = null!;
private MetricsOverlay _metricsOverlay = null!;
private readonly Dictionary<int, PieceView> _pieceViews = new();
private readonly Dictionary<int, TrajectView> _trajectViews = new();
private bool _animating;
public bool IsAnimating => _animating;
[Signal]
public delegate void TurnAnimationCompletedEventHandler();
[Signal]
public delegate void VictoryReachedEventHandler();
[Signal]
public delegate void CollisionOccurredEventHandler();
public void Initialize(BoardView boardView, ObjectivePanel objectivePanel,
ControlBar controlBar, MetricsOverlay metricsOverlay)
{
_boardView = boardView;
_objectivePanel = objectivePanel;
_controlBar = controlBar;
_metricsOverlay = metricsOverlay;
}
public void RegisterPiece(int pieceId, PieceView pieceView, TrajectView trajectView)
{
_pieceViews[pieceId] = pieceView;
_trajectViews[pieceId] = trajectView;
}
public void UnregisterPiece(int pieceId)
{
if (_pieceViews.TryGetValue(pieceId, out var pv))
{
pv.QueueFree();
_pieceViews.Remove(pieceId);
}
if (_trajectViews.TryGetValue(pieceId, out var tv))
{
tv.QueueFree();
_trajectViews.Remove(pieceId);
}
}
public void ProcessEvents(IReadOnlyList<IWorldEvent> events)
{
_animating = true;
var tween = CreateTween();
tween.SetParallel(false);
foreach (var evt in events)
{
switch (evt)
{
case TurnStartedEvent ts:
tween.TweenCallback(Callable.From(() => _controlBar.UpdateTurn(ts.TurnNumber)));
break;
case PieceMovedEvent moved:
if (_pieceViews.TryGetValue(moved.PieceId, out var pv))
{
var target = _boardView.CoordsToPixel(moved.To);
var piece = pv;
var kind = piece.Kind;
float duration = kind == PieceKind.Knight ? 0.4f : 0.3f;
tween.TweenProperty(piece, "position", target, duration);
}
break;
case CollisionDetectedEvent collision:
tween.TweenCallback(Callable.From(() =>
{
FlashPiece(collision.PieceIdA);
FlashPiece(collision.PieceIdB);
EmitSignal(SignalName.CollisionOccurred);
}));
break;
case CargoTransferredEvent transfer:
tween.TweenCallback(Callable.From(() =>
{
if (transfer.GivingPieceId != null && _pieceViews.ContainsKey(transfer.GivingPieceId.Value))
_pieceViews[transfer.GivingPieceId.Value].SetCargo(null);
if (transfer.ReceivingPieceId != null && _pieceViews.ContainsKey(transfer.ReceivingPieceId.Value))
_pieceViews[transfer.ReceivingPieceId.Value].SetCargo(transfer.Type);
}));
tween.TweenInterval(0.15f);
break;
case CargoProducedEvent:
break; // visual pulse could go here
case DemandProgressEvent progress:
tween.TweenCallback(Callable.From(() =>
_objectivePanel.UpdateProgress(progress.DemandCell, progress.Name, progress.Current, progress.Required)));
break;
case VictoryEvent victory:
tween.TweenCallback(Callable.From(() =>
{
_metricsOverlay.ShowMetrics(victory.Metrics);
EmitSignal(SignalName.VictoryReached);
}));
break;
case DeadlineExpiredEvent:
tween.TweenCallback(Callable.From(() =>
EmitSignal(SignalName.CollisionOccurred))); // reuse for pause
break;
case TurnEndedEvent:
break;
}
}
tween.TweenCallback(Callable.From(() =>
{
_animating = false;
EmitSignal(SignalName.TurnAnimationCompleted);
}));
}
private void FlashPiece(int pieceId)
{
if (!_pieceViews.TryGetValue(pieceId, out var pv)) return;
var tween = pv.CreateTween();
tween.TweenProperty(pv, "modulate", new Color(1, 0.2f, 0.2f), 0.1f);
tween.TweenProperty(pv, "modulate", Colors.White, 0.1f);
tween.SetLoops(3);
}
public void ResetPiecePositions(BoardSnapshot snapshot)
{
foreach (var ps in snapshot.Pieces)
{
if (_pieceViews.TryGetValue(ps.Id, out var pv))
{
pv.Position = _boardView.CoordsToPixel(ps.StartCell);
pv.SetCargo(null);
}
}
}
public void ClearAll()
{
foreach (var pv in _pieceViews.Values)
pv.QueueFree();
foreach (var tv in _trajectViews.Values)
tv.QueueFree();
_pieceViews.Clear();
_trajectViews.Clear();
}
}