using System; using UnityEngine; /// /// 象棋对局总管理(普通类,由 ChineseChessMainPanel 持有并统一控制) /// 负责:开局、回合、走棋校验与应用、将军/胜负判断、悔棋、网络同步数据封装 /// /// 联网方案:帧同步——走法与规则判断全在前端本地跑, /// 走子数据通过 SyncGameData 透传给对方(后端不解析),后端只负责胜负结算 /// public class ChessGameManager { /// 棋盘数据管理 public ChessBoardManager board = new ChessBoardManager(); /// 规则管理 public ChessRulesManager rules = new ChessRulesManager(); /// 悔棋记录管理 public ChessMoveRecorder recorder = new ChessMoveRecorder(); /// 本地玩家阵营 public ChessCamp myCamp; /// 当前轮到哪方走(红先黑后) public ChessCamp currentTurn; /// 已走的半回合数(同时作为帧序号) public int moveCount; /// 对局是否已结束 public bool isGameOver; /// 胜方 public ChessCamp winnerCamp; /// 走棋成功(本地或网络同步),参数含完整走子数据 public event Action OnMoveApplied; /// 悔棋完成,参数为回退的步数 public event Action OnUndoApplied; /// 对局结束(本地规则判定),参数为胜方 public event Action OnGameOver; /// 回合切换 public event Action OnTurnChanged; /// 现在是否轮到本地玩家 public bool IsMyTurn => !isGameOver && currentTurn == myCamp; /// 开一局新对局 public void StartGame(ChessCamp myCamp) { this.myCamp = myCamp; board.InitDefaultBoard(); recorder.Clear(); moveCount = 0; isGameOver = false; winnerCamp = ChessCamp.None; currentTurn = ChessCamp.Red; // 红先 OnTurnChanged?.Invoke(currentTurn); } /// /// 本地玩家尝试走棋(完整校验:回合、归属、规则、送将),失败返回 false /// public bool TryLocalMove(Vector2Int from, Vector2Int to) { if (isGameOver || currentTurn != myCamp) return false; var piece = board.GetPiece(from); if (piece == null || piece.camp != myCamp) return false; if (!rules.GetLegalMoves(board, piece).Contains(to)) return false; ApplyMoveInternal(piece, to, true); return true; } /// /// 应用网络同步过来的走棋(对方走的)。带帧序号校验,重复/乱序帧直接丢弃 /// public bool ApplyRemoteMove(ChessMoveData move) { if (isGameOver || move == null) return false; // 帧序号校验 if (move.seq < moveCount) return false; // 重复帧 if (move.seq > moveCount) { Debug.LogWarning($"[Chess] 丢帧:收到 seq={move.seq},本地 moveCount={moveCount}"); return false; // 乱序帧 } var from = new Vector2Int(move.fromX, move.fromY); var to = new Vector2Int(move.toX, move.toY); var piece = board.GetPiece(from); if (piece == null || piece.camp == myCamp || piece.camp != currentTurn) return false; if (!rules.GetLegalMoves(board, piece).Contains(to)) return false; ApplyMoveInternal(piece, to, false); return true; } /// 应用一步棋(数据层),并触发相关事件 private void ApplyMoveInternal(ChessPieceData piece, Vector2Int to, bool isLocal) { Vector2Int from = piece.pos; ChessPieceData captured = board.ApplyMove(piece, to); moveCount++; recorder.Push(new ChessMoveRecord { piece = piece, from = from, to = to, captured = captured }); ChessCamp moverCamp = piece.camp; currentTurn = moverCamp == ChessCamp.Red ? ChessCamp.Black : ChessCamp.Red; // 胜负与将军判断(纯前端本地判定) bool check = false; bool gameOver = false; if (captured != null && captured.pieceType == ChessPieceType.Jiang) { gameOver = true; // 将/帅被吃 } else { check = rules.IsInCheck(board, currentTurn); if (!rules.HasAnyLegalMove(board, currentTurn)) { gameOver = true; // 对方无任何合法走法 → 将死或困毙 } } var args = new ChessMoveEventArgs { move = new ChessMoveData { seq = moveCount - 1, fromX = from.x, fromY = from.y, toX = to.x, toY = to.y }, movePieceId = piece.id, capturedPieceId = captured != null ? captured.id : -1, isLocal = isLocal, isCheck = check, moverCamp = moverCamp }; OnMoveApplied?.Invoke(args); if (gameOver) SetGameOver(moverCamp); else OnTurnChanged?.Invoke(currentTurn); } /// /// 悔棋:回退最近一步(一个半回合,双方确认后各调一次,两端回退同一手) /// public bool UndoOneStep() { var r = recorder.PopLast(); if (r == null) return false; board.UndoMove(r.piece, r.from, r.captured); currentTurn = r.piece.camp; moveCount--; isGameOver = false; winnerCamp = ChessCamp.None; OnUndoApplied?.Invoke(1); OnTurnChanged?.Invoke(currentTurn); return true; } private void SetGameOver(ChessCamp winner) { isGameOver = true; winnerCamp = winner; OnGameOver?.Invoke(winner); } }