原創文章,轉載請保留作者署名!
前面已經說了牌堆的設計,那麼現在就正式進入流程,滿足我們在(一)中所說的需求。
由於在(二)中已經說了要維護擴充,因此對於之前定義的Scene,則需要定義一個所選擇的擴充包,代碼如下:
擴充包
private readonly IPackage[] selectedPackages;
/// <summary>
/// 初始化新的<see cref="Scene"/>類的執行個體。
/// </summary>
/// <param name="packages">所要載入的包。</param>
public Scene(IEnumerable<IPackage> packages)
{
players[currentToken].HasToken = true;
selectedPackages = packages.ToArray();
}
由於玩家是輪動的,因此設定一個令牌,只有持有令牌的玩家才能行動。
private int currentToken;
那麼定義一個Start方法,開始遊戲迴圈。
首先要根據選擇的擴充包產生遊戲牌堆。對於遊戲牌堆,由於我們之前已經定義好了牌堆基類,因此實現就比較簡單了,將擴充包中的遊戲牌載入並洗牌即可。
遊戲牌堆
/// <summary>
/// 表示遊戲牌牌堆。
/// </summary>
public sealed class GameCardHeap : CardHeap<GameCard>
{
/// <summary>
/// 初始化新的<see cref="GameCardHeap"/>類的執行個體。
/// </summary>
/// <param name="packages">所要載入的擴充包。</param>
public GameCardHeap(IEnumerable<IPackage> packages)
{
foreach (var package in packages)
{
((List<GameCard>) Items).AddRange(package.GameCards);
}
Items.Shuffle();
}
}
回頭看我們上面的Start方法,在牌堆建立後,就進入了流程迴圈,還是直接來代碼吧,注釋都寫的蠻清楚的
流程迴圈
/// <summary>
/// 開始遊戲。
/// </summary>
public void Start()
{
Player currentPlayer;
GameCardHeap heap = new GameCardHeap(selectedPackages); // 建立牌堆
while(true)
{
currentPlayer = players[currentToken]; // 設定當前有令牌的玩家
// 摸牌階段
GameCard[] newCards = heap.Pop(2, true);
currentPlayer.Draw(newCards);
// 出牌階段
while(currentPlayer.HasPlayableCard)
{
// NOTE:為了簡單,先實現只殺下家,並只使用殺、閃、桃
int nextToken = currentToken == players.Length - 1 ? 0 : currentToken + 1;
currentPlayer.Play(players[nextToken], currentPlayer.FirstPlayableCard);
if (IsGameEnds())
goto label;
}
// 棄牌階段
// NOTE:為了簡單,先實現只棄從頭開始的牌到當前體力值
int disCardCount = currentPlayer.HandCards.Length - currentPlayer.Hp;
if (disCardCount > 0)
{
GameCard[] removeCards = currentPlayer.HandCards.Take(disCardCount).ToArray();
currentPlayer.Discard(removeCards);
}
// 將令牌給下一個人
GiveTokenToNext();
}
label:
Console.WriteLine("遊戲結束!");
}
這裡使用了一個死迴圈,但在遊戲結束的時候使用goto語句跳出迴圈。所涉及到的一些方法如下:
一些輔助方法
private void GiveTokenToNext()
{
if (currentToken == players.Length - 1)
currentToken = 0;
else
currentToken++;
players[currentToken].HasToken = true;
}
private bool IsGameEnds()
{
return players.Any(p => p.IsDead); // 如果選擇到IsDead的Player,遊戲結束
}
注意,流程的設計中,由於玩家是在遊戲邏輯邊界內的,因此採用了主動的做法來進行設計。這樣對於程式來說,我只要去考慮玩家的摸牌、出牌和棄牌所引起的各種變化即可。