2018-09-27 - Card Thief
Due to a recent discussion on twitter on “secret things in video games that are there but no one notices” i’ve been encouraged to write about Card Thief’s hidden secret: Card dealing is not random at all.
what's something you added to your game that almost nobody will notice?
— Jan Willem Nijman (@jwaaaap) September 26, 2018
In Card Thief the dealing of cards is a big part of the overall gameplay. I assume you are familiar with the game already but here is a quick rundown of it’s idea: 8 cards and 1 player card are dealt in a 3×3 grid from a deck of about 45 cards. Each time the players remove cards in their turns the empty spots are filled up with cards from the deck until the deck is depleted and the game ends.
Since the deck is made out of different type of cards, good and bad for the player, it’s very crucial which cards are dealt, since a too difficult deal can end a game quick, a too easy deal can make the game very shallow. Early on in Card Thief’s development i’ve noticed a few things that made games more tense/interesting when it comes to the amount of specific cards on the board. This is why i decided to implement a card dealing logic that always maintains a certain level of difficulty but gives the players a little help in extra tough situations.
Disclaimer: I’m a self taught programmer and my code often is very amateur. So this is not a lesson in coding but a lesson in using your limited resources in a smart way.
The amount of cards that are dealt per round can vary from 1-8 after a full board clear. In my logic i always go through the same order of cards Enemy, Sneak & Hide, Torches, Treasures, Obstacles. This means when after 2 cards that have been added to the next deal, i only added 2 Enemy cards i won’t check for any other types.
When dealing a specifc card type i always check if this type is present in the deck. If not i will try to deal a similar type. Specifically for hide cards if there are none left i will first try to deal a sneak card if no sneak cards are left i will deal a random card from the deck. This is true for all other types as well.
A special case is the end game where the deck is empty but the player still can continue to play. I will briefly show what happens after a deck is empty later. Here’s the run down of the dealing logic if the deck still has cards left.
At first i’m checking the current state of the board by simply counting the amount of each card type.
Then i roll the amount of maximum possible enemies that can be dealt. I roll a number from 1-100 to get a percentage chance. 4 guards can be dealt with a chance of 5%, 2 Guards with a chance of 10% and 3 with a chance of 85%. Then i check if the amount of enemies on board is smaller then the maximum possible chance. If it is i add another roll to it and will deal an enemy card with the chance of 85%. The other 15% will deal a completely random card from the deck.
Sneak & Hide cards
At first i check if the amount of sneak cards on board is smaller than 1. If it is, i check if the player has at least played 3 turns. If he has not i always add 1 sneak card. Next i check if the player’s current stealth is smaller than 5. If it is and the amount of hide cards is smaller than 1 i always deal a hide card (or fallback to a sneak card). This is a nice trick because a player that is low on stealth values hide cards way more than a player at full health.
If the player’s stealth is greater than 5 i roll and deal a sneak card at a chance of 70% and a treasure card 30% of the time. This adds a nice bit of tension since the player can play treasure cards without spending stealth, but can’t recover stealth either.
I always try to deal at least 2 Torches. Torches are pretty crucial when it comes to difficulty of a board and from internal play testing 2 torches where a good amount to create enough tension each round. I also consider warden cards as torches.
Next i check if the amount of obstacle cards (doors or taps) is bigger than 0. If not i always add 1 treasure card.
If there are no obstacle cards on the board i will roll and with a 39% chance i deal an obstacle card, with a 51% chance i deal a treasure card, and with 10% chance i deal a random card from the deck.
Empty deck state
The empty deck state is a special case in Card Thief since the game can continue even after all cards from a deck are played. Early on i decided it would be cool to let players push their luck and continue to pick pocket guards for more score even after the sneak and hide resources would be depleted. I have a special dealing logic in place that ensures only enemy and torch cards can be dealt after finishing the deck. For the enemies i define a special enemy pool based on the current heist/game mode. The first level can only deal guards, the 2nd adds wardens and wolfs, the 3rd add owls and the 4th adds Overseers. From this pool i draw cards when dealing more enemies. Besides that i always enforce 2 torches on board.
That’s pretty much it. It’s way more complicated than i imagined, since i haven’t opened the code for the dealing logic in more than a year. I hope this helps to understand my approach to card dealing in Card Thief a bit and will give you a glimpse of what crucial parts a game can hide from it’s players.