[转载][原创]一步一步用C#编写三国杀(三):设计流程 – 忘却之都 – 博客园.
前面已经说了牌堆的设计,那么现在就正式进入流程,满足我们在(一)中所说的需求。
由于在(二)中已经说了要维护扩展,因此对于之前定义的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();
}
/// 初始化新的<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();
}
}
/// 表示游戏牌牌堆。
/// </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(“游戏结束!“);
}
/// 开始游戏。
/// </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++;
} private bool IsGameEnds()
{
return players.Any(p => p.IsDead); // 如果选择到IsDead的Player,游戏结束
}
{
if (currentToken == players.Length – 1)
currentToken = 0;
else
currentToken++;
players[currentToken].HasToken
= true;} private bool IsGameEnds()
{
return players.Any(p => p.IsDead); // 如果选择到IsDead的Player,游戏结束
}
注意,流程的设计中,由于玩家是在游戏逻辑边界内的,因此采用了主动的做法来进行设计。这样对于程序来说,我只要去考虑玩家的摸牌、出牌和弃牌所引起的各种变化即可。