Protect your castle in the style of Atari’s pioneering Rampart. Mark shows you how, in the latest edition of Wireframe magazine, available now.
As the eighties drew to a close, a new strategy genre emerged. The tower defence game involved building walls or barriers around your castle to prevent an enemy destroying it. One notable early entry in the genre was Rampart, released by Atari. The game took in two phases. In the building phase, the player filled holes in the walls around their fort and placed cannons. In the second phase, the player’s fort would be attacked from the sea by an armada of ships, and the player could fire back with their cannon. This cycle would continue until the player could no longer fill the gaps in the walls or they’d destroyed all the attacking ships.
For our Pygame Zero example, we’ll look at two tower defence mechanics: the wall building system and then the check to see if the keep has been enclosed by walls. To start coding, we’ll need a background – in this case, a shoreline section of land. We then need to define an invisible grid which will hold the details of where the walls are positioned. If we say that each wall segment is 40×40 pixels, on an 800×600 pixel window we can define a grid that is 20×15 squares in a two-dimensional list. In this list, we’ll start with all the squares set to 0 and then if we add a wall section, switch that square to 1. To define where we can and can’t build, we can load in a little black-and-white image and read pixels from that to set squares which are non-buildable as 3.
Our fort will take up four squares, so we set the top-left square of the fort’s position as 2 and the other three squares as 3. This will stop the player building walls over the fort. We track the mouse movement with the
on_mouse_move() function and when we have the mouse x and y coordinates, we draw a single section of wall under the mouse pointer locked to the invisible grid. When we get an
on_mouse_down() event, we check that the square we’re over is a 0, and if it is, we switch that to a 1.
draw() function, if we draw a single wall section wherever there’s a 1 in our grid, we’ll see flat squares where the walls are. What we want, though, is for all the walls to join up with battlements around the outside. For this, we need to inspect the squares around each wall piece and change the image we use depending on how it’s connected to other wall pieces. We look at the squares above, to the right, below, and to the left, and make a string with zeroes if there’s no wall there, and ones if there is. This means we’ll get the string ‘0000’ if no walls connect, ‘1000’ if there’s a wall above, ‘0101’ if there are walls on the left and right of our current wall, and so on. This is then used to load the wall image – for example, ‘wall0101’.
In the second part of our example, we’ll detect whether the fort has been surrounded by walls. There are various ways to do this, but we’re going to use a ‘flood fill’ routine to test this. First, we make a copy of our grid list to test with. Then, starting at the fort location on our grid, we recursively test all the neighbouring squares. If we hit a wall, then we close that branch of the loop. This creates a sort of cascade of tests going outwards until either the side of the map is reached or we run out of squares to test because walls have been hit by all the code branches. If the edge of the map is reached, the wall has holes in it; otherwise, the fort is enclosed.
Those are our wall-building and test routines. To complete the tower defence-style game, you’ll need to add some cannons and ships to fight, but we’ll leave that part for you to have fun with.
Get your copy of Wireframe #70
And if you prefer your magazines in digital form, you can also download Wireframe issue 70 as a free PDF!