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 _pieceViews = new(); private readonly Dictionary _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 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(); } }