Now that we have the freedom to attack, you might be thinking it would be nice if there were more options. In this lesson, we will add code so that the enemy opponent can actually play cards and attack us too. This will give a greater variety of move options, and add a bit of strategy to the game, all while giving a great boost to the fun factor. In my opinion, this is where it actually starts feeling like a real game!
In this lesson we will pick up where we left off, and finish a working implementation of attacking. This will include creating a new game action, implementing it through a system, and providing a viewer so a user of our game can see it all happen. Along the way I will also do a bit of refactoring to make things more flexible.
The only way to win a match is to reduce your opponent’s hit points to zero. We currently support fatigue damage as one path toward this goal, but that is merely a battle of attrition. If we were to implement the ability for minions and heroes to attack each other, the level of strategy and fun would increase dramatically. We will lay some foundational work toward this end goal, and will create a working demo that highlights minions which are attack ready.
If there are no constraints on summoning minions, then this game would be almost entirely luck based. Whoever drew the strongest card first and played it would almost certainly win. Doesn’t sound fun to me. Let’s fix that by constraining the ability to play cards by their mana cost.
Now that we can play cards, let’s add a reason to do so. Our player’s deck is currently filled with nothing but minions, so you might be able to guess what this lesson is going to be all about. After playing a card that happens to be a minion, we will “summon” the minion. Instead of going straight to the graveyard, it will go onto the player’s battlefield, ready for combat.
You can take turns drawing cards from your deck and putting them in your hand… but at the moment all that leads to is a hand full of cards. Sounds like we need a way to start playing cards from our hand toward some other purpose. In this lesson we will provide the ability to play a card, and also enable the local player to control which of the cards they wish to play by interacting with what is presented on screen.
In the last lesson, we implemented the Fatigue stat – caused by attempting to draw cards from an empty deck. However, although we could watch the stat change, it had no real effect on the game itself. In this lesson, we will add a reaction to fatigue which applies damage to the player’s hero by the fatigue amount. As a result, we have the means to determine when a game will end – whenever a hero’s hit points have dropped to zero.
In the previous lesson, I mentioned two cases where drawing cards should not be allowed: when your deck is empty, and when your hand is full. In many cases an exception to a game action could result in the complete cancellation of the action, but what if the action could be partially performed? For example, imagine that you needed to draw three cards, and only had two in your deck. Rather than cancel the entire action, you should still draw the two cards that were available, and then apply fatigue for the third draw attempt only. Let’s try it out.
One of the most powerful features of the Action system is the way it can chain together actions with reactions into a long sequence of events. At the moment we have only implemented a single action – changing turns, but in a game like Hearthstone, this action will always have at least one reaction. When a player starts their turn, they are supposed to draw a card. Let’s go ahead and implement the draw a card feature as a reaction to the action for changing turns.
In the previous lesson, we implemented the systems and models necessary to change turns. However, outside of watching a checkmark appear in a unit test, there was no visible evidence of anything occuring. Having a “view” to represent the data is an important step of the process, but is something that really can be anything you want, and therefore should be separated as much as possible. The final result could be 2D or 3D. It doesn’t have to be animated, though it definitely could be, and might even include stuff like flashy particles, who knows? The important thing is that a user can understand and interact with the game.