• 2D shoot 'em up
SDL2 Santa game tutorial 🎅
SDL2 Shooter 3 tutorial
The Legend of Edgar 1.36
SDL2 map editor tutorial [UPDATED]
TBFTSS: The Pandoran War - Amiga OS4 Port
— A simple turn-based strategy game —
The Blue Ghosts we're now going to add will exhibit a cautious behaviour. Upon spotting a mage, they will choose to either attack or retreat. So, fiercely in the middle when it comes to their decision making.
Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./sdl2TBS18 to run the code. You will see a window open like the one above, showing three wizards in a maze-like map, as well as a number of ghosts. Play the game as normal. Notice how the Blue Ghosts will sometimes attack and sometimes retreat from the mages. Once you're finished, close the window to exit.
Inspecting the code
Adding in our Blue Ghosts is even easier than adding in the Lilac Ghosts. We need to only update two files: ai.c and ghosts.c.
Let's start with ai.c. We've updated doAI:
First of all, our Blue Ghosts will be using the AI_NORMAL profile (that we defined much earlier in this tutorial). We've modified the doNormal function a little, since it now takes a Unit as a parameter. We're passing across the current ghost's Unit into the function.
The doNormal function itself has also been tweaked:
We're setting up two variables called doFallback and doAttack. These, we're passing into our lookForEnemies function (we'll see what changes we've made to this in a moment). These two variables will act as flags to say whether the ghost should retreat from or attack their targets. We first test whether doFallback or doAttack is set, and then take some random actions.
If both the doFallback and doAttack flags are set, we'll randomly decide (50-50 chance) whether we want to attack (via fireBullet). Otherwise, if the doAttack flag is set, there is a 2 in 3 chance that we'll also call fireBullet. If, however, we've decided not to attack, we'll call fallback.
Finally, if neither of these flags are set (there are no enemies visible), we'll call moveRandom, to make our ghost wander the stage.
Next, let's look at the changes we've made to lookForEnemies:
As previously stated, we're now passing in the current ghost's Unit data; this is really just because the function needs it, and we had previously extracted it at the top of our doAI loop, so no need to extract it again. We're also passing in doFallback and doAttack, as references.
We're setting Stage's targetEntity to NULL, and also setting doFallback and doAttack to 0. Our code to look for an enemy to attack remains unchanged from when we first set it up (at the time, using the White Ghost), but after looking for a target, we're then evaluating them.
If Stage's targetEntity is not NULL (we found an enemy to attack), we're going to find out how we should respond. First, we'll test whether we want to set the doFallback flag. We'll check to see how close our enemy is. If the value of closest is 5 or less (meaning the enemy is within 5 squares of the ghost), and a random of 5 is greater than 3, we'll set the doFallback flag. For the doAttack flag, we'll test to see if the enemy is within 10 squares and is also in our attack range (the MapTile's inAttackRange is set).
That's it! That's all we need to do to have our Blue Ghosts decide to either attack or flee from a nearby enemy! We're simply evaluating the enemy we've chosen to attack and making a random decision based on what we see. There are things that we could add here, such as making them more likely to flee if there is more than 1 enemy nearby or also make them more likely to attack if they have a buddy nearby (who is in their line of sight). But right now, we've got a ghost that will keep its distance from the mages and attack at range, which makes them fun to battle.
The last thing we need to do in ai.c is update addAIUnits:
We want to create Blue Ghosts for this part, and so we're passing through "Blue Ghost" to the initEntity function (again, we've added the relevant data to entityFactory.c).
Lastly, let's define the ghost itself. If we move across to ghosts.c, we've added in a new function called initBlueGhost:
We're just setting the ghost's `name`, `texture`, and other attributes. Also, we're giving him a weapon, the slime ball that we were testing with in earlier parts.
Done! That was simple, eh? Things are getting easier and easier. How about we add a more interesting ghost next? Well, our Red Ghost will have some very unique properties, as it will have a different kind of weapon, one that can create slime pools. It will be capable of not only attacking the player, but also targetting the map around them, to make life a little more difficult for our intrepid trio of wizards.
The source code for all parts of this tutorial (including assets) is available for purchase:
It is also available as part of the SDL2 tutorial bundle: