• 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
— Mission-based 2D shoot 'em up —
The penultimate mission that we're going to add will involve the player having to protect another vessel. The SS Goodboy is having trouble with her engines, and so Leo has come to the rescue. The enemy will be able to both target, attack, and destroy the SS Goodboy, so the player must stay on their toes when it comes to keeping the ship safe. If the SS Goodboy is destroyed, the mission will be failed.
Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./shooter3-28 to run the code. You will see a window open displaying the intermission's planets screen. Select Pea, then enter the comms section to start the mission. Play the game as normal, defeating the enemies, and ensuring they don't destroy the SS Goodboy. After some time, the SS Goodboy will report their engines are fixed, and the player need only clean up the remaining enemies. You may repeat the mission as often as you like. Remember that you can change the fighter's damage, weapons, output, and health by editing game.c, if you wish to get ahead of things early. Once you're finished, close the window to exit.
Inspecting the code
As promised, these parts are decreasing in length and complexity, thanks to us having already built the foundations upon which this mission is based. We will need to make a few additions, but they are minor compared to everything else, as we'll see.
Right then, starting with structs.h:
We've added in an inner struct to Stage called missionTarget, to hold the mission target's health. `health` is the current health, while maxHealth is the maximum health. Note that `health` is a pointer, so that it always reflects the SS Goodboy's current health.
Now for the SS Goodboy itself. We've created ssGoodboy.c to hold all of the Goodboy's functions. Once more, they will be very familiar. So, let's jump in, starting with initSSGoodboy:
The SS Goodboy has no weapons, uses the evade AI type, and is also on the side of the cats (SIDE_CATS). At the end of the function, we're setting Stage's missionTaraget's `health` to the SS Goodboy's health (as a pointer), and the maxHealth to the Goodboy's maxHealth.
Now for the `tick` function:
Engine position, blah blah blah! Shall we move on?
Again, we're going to skip `draw` and `destroy`, and look at the `die` function:
When the SS Goodboy is destroyed, it will call updateObjective "ssGoodboyDestroyed". We have a conditional objective with this as the target name, meaning that the mission will fail if we lose the SS Goodboy.
That's all we need to do for the SS Goodboy..! It's rather like the Greeble POW ships, except that it's on the side of the player. The SS Goodboy will make an effort to run away from the enemies, just like how the POW ships flee the player.
Our mission introduces a few new script commands: COMPLETE_OBJECTIVE and SET_ENEMY_LIMIT. We just need to head over to script.c, and update doScript to support these:
Starting with COMPLETE_OBJECTIVE. This command will instantly complete an objective, identified by id. We're reading the argument for the command (the `id` of the objective) into a strParam, and then calling a new function named completeObjective, passing over the id. We'll see this function in a moment.
SET_ENEMY_LIMIT is another simple command to handle. As the name implies, this sets the enemy limit in the stage. We're reading the value and setting Stage's totalEnemies to it. In the case of our mission, totalEnemies is -1 (unlimited), and we'll be setting it to 5, so that only 5 more enemies will be spawned on the stage from here on out.
That's all we need to do for our scripting updates. Let's look at the new completeObjective function, in objectives.c:
As expected, we're looping through all our objectives, searching for an objective that isn't yet complete (currentValue less than targetValue), and whose `id` matches the `id` passed into the function. Once found, we'll set the currentValue to the targetValue, and print the Objective Complete message. Simples.
We're very nearly done..! All that remains is to head over to stage.c, to make the updates to display the mission target's health bar.
Starting with drawHUD:
We're calling a new function, drawMissionTargetHealthBar:
We're first testing if stage.missionTarget.health is not NULL (remember, it's a pointer!), and then preparing to draw the health bar. Unlike our player's health and shield bars, the mission target's health bar is always the same length (MISSION_TARGET_HEALTH_BAR_SIZE, defined as 75), but the associated value will be displayed as a percentage. In other words, we'll always draw MISSION_TARGET_HEALTH_BAR_SIZE blocks, but adjust the brightness of the blocks if they are within the target's health percentage.
We calculate the percent of the missionTarget's `health`, multiply that by MISSION_TARGET_HEALTH_BAR_SIZE to discover how many bright blocks to draw, then use a for-loop to render the blocks. As with the player's health bar, we'll render the blocks in red, yellow, and green, based on their threshold. You will have observed as the SS Goodboy takes damage that the number of bright blocks decreases.
One thing to keep in mind is that, unlike the player's health bar, we're presenting the target's health bar from right to left. Green blocks on the left, red on the right. So, our x position is decreasing from its starting point, to move left.
Another mission done! We've now only got one mission left to do, and we're nearly finished! The next mission is the big one - the boss fight. The Greebles aren't impressed with Leo's antics in the new KIT-E fighter, and have deployed a frigate to deal with him. The player must now prepare themselves to face off against The Gravlax!
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: