selectPiece method
Implementation
Future<void> selectPiece(int pieceId) async {
if (_state.phase != LudoTurnPhase.awaitingPieceSelection) {
throw StateError('Roll the dice first.');
}
if (_isAnimating) return;
final move = _state.legalMoves.firstWhere(
(m) => m.pieceId == pieceId,
orElse: () => throw ArgumentError('Piece $pieceId is not a legal move.'),
);
final piece = _state.pieces.firstWhere((p) => p.id == pieceId);
final steps = move.toPosition - move.fromPosition;
// ── step-by-step animation ────────────────────────────────────
_isAnimating = true;
_animatingPiece = piece;
for (var step = 1; step <= steps; step++) {
_animatingStep = piece.trackPosition + step;
_animatingPiece = piece.copyWith(trackPosition: _animatingStep!);
notifyListeners();
// await _audio.playPieceMove();
await Future.delayed(stepAnimationDuration);
}
_animatingPiece = null;
_animatingStep = null;
_isAnimating = false;
// ── commit to engine ──────────────────────────────────────────
final result = _engine.move(_state, pieceId);
_state = result.state.copyWith(lastMovedPiece: result.movedPiece);
onPieceMoved?.call(result.movedPiece, result.fromPosition, result.toPosition);
for (final captured in result.capturedPieces) {
// _audio.playCapture();
onPieceCaptured?.call(captured, result.movedPiece);
}
if (result.playerWon) {
// _audio.playWin();
final place = _state.winners.indexOf(result.movedPiece.playerIndex) + 1;
onPlayerWon?.call(result.movedPiece.playerIndex, place);
} else if (result.movedPiece.isFinished) {
// _audio.playPieceHome();
}
// Teams mode: fire team-won callback
if (result.teamWon) {
final myTeam = teamOf(result.movedPiece.playerIndex, _teams);
if (myTeam != null) onTeamWon?.call(myTeam);
}
if (result.gameFinished) {
// _audio.playGameOver();
onGameFinished?.call(List.unmodifiable(_state.winners));
} else if (result.turnPassed) {
_state = _state.copyWith(clearLastMovedPiece: true);
onTurnChanged?.call(_state.currentPlayerIndex);
}
notifyListeners();
}