Files
5.24BuYu/Assets/MyGame/aa/UI/Game/ChineseChess/ChessGame/ChessGameManager.cs
T
2026-09-13 01:40:32 +08:00

171 lines
5.9 KiB
C#

using System;
using UnityEngine;
/// <summary>
/// 象棋对局总管理(普通类,由 ChineseChessMainPanel 持有并统一控制)
/// 负责:开局、回合、走棋校验与应用、将军/胜负判断、悔棋、网络同步数据封装
///
/// 联网方案:帧同步——走法与规则判断全在前端本地跑,
/// 走子数据通过 SyncGameData 透传给对方(后端不解析),后端只负责胜负结算
/// </summary>
public class ChessGameManager
{
/// <summary>棋盘数据管理</summary>
public ChessBoardManager board = new ChessBoardManager();
/// <summary>规则管理</summary>
public ChessRulesManager rules = new ChessRulesManager();
/// <summary>悔棋记录管理</summary>
public ChessMoveRecorder recorder = new ChessMoveRecorder();
/// <summary>本地玩家阵营</summary>
public ChessCamp myCamp;
/// <summary>当前轮到哪方走(红先黑后)</summary>
public ChessCamp currentTurn;
/// <summary>已走的半回合数(同时作为帧序号)</summary>
public int moveCount;
/// <summary>对局是否已结束</summary>
public bool isGameOver;
/// <summary>胜方</summary>
public ChessCamp winnerCamp;
/// <summary>走棋成功(本地或网络同步),参数含完整走子数据</summary>
public event Action<ChessMoveEventArgs> OnMoveApplied;
/// <summary>悔棋完成,参数为回退的步数</summary>
public event Action<int> OnUndoApplied;
/// <summary>对局结束(本地规则判定),参数为胜方</summary>
public event Action<ChessCamp> OnGameOver;
/// <summary>回合切换</summary>
public event Action<ChessCamp> OnTurnChanged;
/// <summary>现在是否轮到本地玩家</summary>
public bool IsMyTurn => !isGameOver && currentTurn == myCamp;
/// <summary>开一局新对局</summary>
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);
}
/// <summary>
/// 本地玩家尝试走棋(完整校验:回合、归属、规则、送将),失败返回 false
/// </summary>
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;
}
/// <summary>
/// 应用网络同步过来的走棋(对方走的)。带帧序号校验,重复/乱序帧直接丢弃
/// </summary>
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;
}
/// <summary>应用一步棋(数据层),并触发相关事件</summary>
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);
}
/// <summary>
/// 悔棋:回退最近一步(一个半回合,双方确认后各调一次,两端回退同一手)
/// </summary>
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);
}
}