Learn how to create the turn-based combat system found in games like Pokémon, Final Fantasy, and Undertale. Raspberry Pi’s Rik Cross shows you how.
In the late 1970s, high school student Richard Garriott made a little game called Akalabeth. Programmed in Applesoft BASIC, it helped set the template for the role-playing genre on computers. Even today, turn-based combat is still a common sight in games, with this autumn’s Pokémon Sword and Shield revolving around a battle system which sees opponents take turns to plan and execute attacks or defensive moves.
The turn-based combat system in this article is text-only, and works by allowing players to choose to defend against or attack their opponent in turn. The battle ends when only one player has some health remaining.
Each Player
taking part in the battle is added to the static players
list as it’s created. Players have a name
, a health
value (initially set to 100
) and a Boolean defending
value (initially set to False
) to indicate whether a player is using their shield. Players also have an inputmethod
attribute, which is the function used for getting player input for making various choices in the game. This function is passed to the object when created, and means that we can have human players that give their input through the keyboard, as well as computer players that make choices (in our case simply by making a random choice between the available options).
A base Action
class specifies an action owner
and an opponent
, as well as an execute()
method which has no effect on the game. Subclasses of the base class override this execute() method to specify the effect the action has on the owner
and/or the opponent
of the action. As a basic example, two actions have been created: Defend
, which sets the owner’s defending
attribute to True
, and Attack
, which sets the owner’s defending
attribute to False
, and lowers the opponent’s health
by a random amount depending on whether or not they are defending.
Players take turns to choose a single action to perform in the battle, starting with the human ‘Hero’ player. The choose_action()
method is used to decide what to do next (in this case either attack or defend), as well as an opponent if the player has chosen to attack. A player can only be selected as an opponent if they have a health
value greater than 0, and are therefore still in the game. This choose_action()
method returns an Action
, which is then executed using its execute()
method. A few time.sleep()
commands have also been thrown in here to ramp up the suspense!
After each player has had their turn, a check is done to make sure that at least two players still have a health
value greater than 0, and therefore that the battle can continue. If so, the static get_next_player()
method finds the next player still in the game to take their turn in the battle, otherwise, the game ends and the winner is announced.
Our example battle can be easily extended in lots of interesting ways. The AI for choosing an action could also be made more sophisticated, by looking at opponents’ health
or defending
attributes before choosing an action. You could also give each action a ‘cost’, and give players a number of action ‘points’ per turn. Chosen actions would be added to a list, until all of the points have been used. These actions would then be executed one after the other, before moving on to the next player’s turn.
You can read more features like this one in Wireframe issue 28, available now at Tesco, WHSmith, all good independent UK newsagents, and the Raspberry Pi Store, Cambridge.
Or you can buy Wireframe directly from Raspberry Pi Press — delivery is available worldwide. And if you’d like a handy digital version of the magazine, you can also download issue 28 for free in PDF format.
Make sure to follow Wireframe on Twitter and Facebook for updates and exclusive offers and giveaways. Subscribe on the Wireframe website to save up to 49% compared to newsstand pricing!
Website: LINK