Vertical Shmup - Part 9 - The Aliens Fight Back!
This is really starting to come together, we have an easily extensible framework for creating alien movement patterns, but they are a bit toothless at the moment as they cant fire weapons of their own, so let's remedy that!
First we'll design an Alien Missile Sprite:
I'm going to use Sprite Type 3 for the Alien Missiles, but before we start coding there's something we're going to need to consider. And that's MPAGD's collision detection - since all sprites are 16 px x 16px - but our Alien Missile only occupies 3pixels wide - this might lead to some pretty mean collisions which visually dpnt appear to be a missile colliding with the player's spaceship - but will be registered by the engine as a collision, so we'll need to include some code that will help check whether the graphical part of the sprite has actually collided with the spaceship.
Also, we need to consider how the missiles travel, do they just go straight down?, should they home in on the player? or should they veer to the left or right dependent on the Alien's direction of travel when they launched the missile.
This latter approach is my favourite, and, since all of the Alien Directions are stored in the DIRECTION variable, it should be pretty easy to code the direction of the missile, so if an alien is travelling right when they released the missile then the missile should veer to the right, and so on.
Other things we need to consider include:
When should a missile be fired?
What is the probability of a missile being fired?
Should all Aliens fire at the same rate?
Should all Aliens fire missiles or only some?
How high up the screen should an alien be before they are allowed to fire missiles? (we might want to avoid them firing if they are just a few pixels above the player as that would be unfair.
We've built quite a flexible framework so far - so I'm thinking we could extend the flexibility further to allow us to vary the missile firing capabilities of each alien attack wave, that will help us make the attack waves progressively harder.
OK, so, on with the coding, first, lets extend our Spawner code to include some new variables for controlling the Aliens weapon firing capabilities, open up your Spawner event (Sprite Type 7, and add the highlighted code:
EVENT SPRITETYPE7 ; SPAWNER ; ============ IF SETTINGA = 0 ; first time we run this LET JUMPSPEED = 0 ; reset the enemies spawned counter LET SETTINGB = 0 ; reset the timer RESTORE ; start from the beginning of the data table REPEAT L ; get the data for this level READ AIRBORNE ; get the spawner movement READ X ; get starting x position READ Y ; get starting y position READ J ; get the enemy sprite type READ K ; get the enemy image READ E ; how many enemies in this level? READ G ; delay between releasing each enemy READ P ; points for a kill READ C ; colour READ W ; weapon READ H ; fire weapon probability READ Q ; min height to release weapons ENDREPEAT LET D = E ; how many kills to get bonus? LET F = E ; enemies remaining ADD 1 TO SETTINGA ; prevent this running again ENDIF IF SETTINGB = G ; has timer reached the delay point? IF JUMPSPEED < E ; are there still enemies left to spawn? IF S < 9 ; are there 8 or less enemies already on screen? SPAWN J K ; spawn an enemy ADD 1 TO JUMPSPEED ; increment the number spawned SPAWNED ; switch to the enemy sprite LET DIRECTION = 0 ; set its initial direction to 0 ENDSPRITE ; retrun to the spawner ENDIF ELSE ; all enemies in this level have been spawned IF F = 0 ; all enemies are off screen ADD 1 TO L ; get ready for next level IF L = 7 ; have we reached the end of the levels? LET L = 1 ; yes, start again ENDIF LET SETTINGA = 0 ; get data for next level ENDIF ENDIF LET SETTINGB = 0 ; reset the timer ENDIF ADD 1 TO SETTINGB ; increment the timer DATA 0 132 8 5 2 8 8 5 67 1 10 100 DATA 0 100 8 5 2 8 8 10 68 1 10 100 DATA 0 232 16 5 5 8 6 15 69 1 8 100 DATA 0 8 16 5 5 8 6 15 70 1 8 100 DATA 0 232 146 5 2 16 6 20 71 1 4 100 DATA 0 8 146 5 2 16 6 20 66 1 4 100
All we've added here is three new variables that we will read in from the data table:
W (Weapon) - 0 will mean they can't fire missiles, 1 means they can
H(Probability of firing a weapon) the higher the value the less chance they will actually fire a missile
Q (firing height) this is the Y value below which (i.e. higher up the screen since Spectrum Y co-ordinates start at the top of the screen ) an alien must be if they are allowed to fire missiles..
I've added in some values for each of the 6 currently defined attack waves in the Data table at the bottom.
Next, we're going to need to add a couple of lines of code to our player event that will Store the players X & Y coordinate values as we're going to need to use these in the Aliens event to calculate how far, horizontally, the alien is from the player, open up your player event and add these lines at the bottom of your script:
LET M = X ; store player X in M LET N = Y ; store player Y in N
Next, we'll add some code to the Alien event (Sprite Type 5), open yours up and add/update the highlighted code:
EVENT SPRITETYPE5 ; FLEXI ALIENS ; ===================== SPRITEINK C ; colour the alien with C value IF SETTINGA = 0 ; Alien first appears RESTORE ; Restore the data table LET RND = L ; Store the Level number in RND ADD 50 TO RND ; and increase it by 50 WHILE DIRECTION <> RND ; While DIRECTION doesnt = RND READ DIRECTION ; Read value from DATA table READ SETTINGB ; Also read second value ENDWHILE ; Keep going until we find the value in RND LET SETTINGA = 1 ; When we find it change SA to 1 ENDIF IF SETTINGA = 1 ; Ready to get direction & reptitions READ DIRECTION ; Read the next movement into DIRECTION READ SETTINGB ; Read the repetitions into SettingB LET SETTINGA = 2 ; ENDIF IF COLLISION 1 ; if hit by a torpedo OTHER ; switch to torpedo LET DIRECTION = 99 ; run the explosion ENDSPRITE ; return to this Alien LET TYPE = 6 ; Switch it to explosion type LET IMAGE = 4 ; Explosion image LET FRAME = 0 ; first frame LET DIRECTION = 0 ; tell explosion its an alien exploding ENDIF IF COLLISION 0 ; if enemy and player collide LET TYPE = 6 ; switch it to the explosion type LET IMAGE = 4 ; and the explosion image LET FRAME = 0 ; reset it to first frame LET DIRECTION = 0 ; this is an alen exploding OTHER LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ENDIF IF DIRECTION = 0 ; Move N SPRITEUP SPRITEUP ENDIF IF DIRECTION = 1 ; Move NNE SPRITERIGHT SPRITEUP SPRITEUP ENDIF IF DIRECTION = 2 ; Move NE SPRITERIGHT SPRITERIGHT SPRITEUP SPRITEUP ENDIF IF DIRECTION = 3 ; Move ENE SPRITERIGHT SPRITERIGHT SPRITEUP ENDIF IF DIRECTION = 4 ; Move E SPRITERIGHT SPRITERIGHT ENDIF IF DIRECTION = 5 ; Move ESE SPRITERIGHT SPRITERIGHT SPRITEDOWN ENDIF IF DIRECTION = 6 ; MOVE SE SPRITERIGHT SPRITERIGHT SPRITEDOWN SPRITEDOWN ENDIF IF DIRECTION = 7 ; MOVE SSE SPRITEDOWN SPRITEDOWN SPRITERIGHT ENDIF IF DIRECTION = 8 ; MOVE S SPRITEDOWN SPRITEDOWN ENDIF IF DIRECTION = 9 ; MOVE SSW SPRITEDOWN SPRITEDOWN SPRITELEFT ENDIF IF DIRECTION = 10 ; MOVE SW SPRITEDOWN SPRITEDOWN SPRITELEFT SPRITELEFT ENDIF IF DIRECTION = 11 ; MOVE WSW SPRITEDOWN SPRITELEFT SPRITELEFT ENDIF IF DIRECTION = 12 ; MOVE W SPRITELEFT SPRITELEFT ENDIF IF DIRECTION = 13 ; MOVE WNW SPRITELEFT SPRITELEFT SPRITEUP ENDIF IF DIRECTION = 14 ; MOVE NW SPRITELEFT SPRITELEFT SPRITEUP SPRITEUP ENDIF IF DIRECTION = 15 ; MOVE NNW SPRITELEFT SPRITEUP SPRITEUP ENDIF ; BOUNDARY CHECKING ; =================== IF X <= LEFTEDGE ; if alien reaches the left edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter SUBTRACT 1 FROM F ; decrement the all aliens in wave counter ENDIF IF X >= RIGHTEDGE ; if alien reaches the right edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter SUBTRACT 1 FROM F ; decrement the all aliens in wave counter ENDIF IF Y >= BOTTOMEDGE ; if alien reaches the right edge REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter SUBTRACT 1 FROM F ; decrement the all aliens in wave counter ENDIF ; SHALL WE SHOOT A MISSILE? ; =========================== IF Y < Q ; is alien above minimum height for releasing missiles? IF W > 0 ; yes, can alien release missiles? IF X < M ; yes, is Alien to the right of player? SUBTRACT X FROM M ; how far to the right of player is alien? IF M <= 8 ; 8 or less pixels? LET AIRBORNE = 1 ; run the chance of firing routine ENDIF ADD X TO M ; reset M ELSE IF M < X ; is alien to the left of player? SUBTRACT M FROM X ; yes, how far to the left IF X < = 8 ; 8 pixels or less? LET AIRBORNE = 1; yes, run the chance of firing routine ENDIF ADD M TO X ; reset X ELSE ; enemy must be directly above player LET AIRBORNE = 1 ; run the chance of firing routine ENDIF ENDIF ENDIF ENDIF IF AIRBORNE = 1 ; Chance of firing routine IF S < 9 ; are there 8 or less sprites on screen? GETRANDOM H ; yes, get a random number from 0 to the aliens missile probability # IF RND = 1 ; is it 1? LET RND = DIRECTION ; yes, store aliens direction in RND SPAWN 3 6 ; spawn a missile SPAWNED ; switch to the spawned missile LET DIRECTION = RND ; set its direction to RND ENDSPRITE ; switch back to this alien ENDIF ENDIF LET AIRBORNE = 0 ; end the chance of firing routine ENDIF SUBTRACT 1 FROM SETTINGB ; decrement the repetitions IF SETTINGB = 0 ; has alien moved all repetitions? LET SETTINGA = 1 ; get the next direction & # of repetitions ENDIF DATA 51 0 8 20 7 8 6 8 5 8 4 16 DATA 52 0 8 20 9 8 10 8 11 8 12 16 DATA 53 0 12 48 11 2 10 2 9 2 8 2 7 2 6 2 5 2 4 40 5 2 6 2 7 2 8 2 9 2 10 40 DATA 54 0 4 48 5 2 6 2 7 2 8 2 9 2 10 2 11 2 12 40 11 2 10 2 9 2 8 2 7 2 6 40 DATA 55 0 13 26 14 4 15 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 4 10 40 DATA 56 0 3 26 1 4 0 4 15 4 14 4 13 4 12 4 11 4 10 4 9 4 8 4 7 4 6 40
Read through the comments in the highlighted code and hopefully you'll understand what we are doing:
Check to see if the ALien is with 8 pixels to the left and x pixels to the right of the player
if they are, theres a random chance that a missile will be fired (provided they are also above the minimum vertical distance as set in variable Q) AND if there arent already too many sprites on screen
The probability of a missile being fired is 1 in H (where H is defined in the attack wave data table in the spawner event.
And if all these tests are passed spawn a missle (type 3, Image 6)
Then set the DIRECTION value of the spawned missile to be the same as the DIRECTION the alien was travelling in when they fired the missile.
Now all we need to do is write the missile code, so open up Sprite Type 3 and paste in this:
EVENT SPRITETYPE3 ; ENEMY MISSILE SPRITEINK 71 ; bright white SPRITEDOWN ; go down fast (3 times SPRITEDOWN SPRITEDOWN IF DIRECTION = 0 ; was alien going down when fired? ELSE ; if so, do nothing IF DIRECTION < 8 ; was alien going right? SPRITERIGHT ; make missile veer right ELSE IF DIRECTION = 8 ; was alien going straight up? ELSE ; do nothing SPRITELEFT ; alien was going left, veer missile left ENDIF ENDIF ENDIF IF COLLISION PLAYER ; has missile collided with player? IF X > M ; is missile to the right of player? SUBTRACT M FROM X ; find out how far to the right IF RND <= 10 ; 10 px or less? its a hit! OTHER ; switch to the player LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ; switch back to this event ENDIF ADD M TO X ; reset X ELSE ; not to the right IF X < M ; is missile to the left? SUBTRACT X FROM M ; yes, how far to left? IF M <= 8 ; 8 or less pixels? OTHER ;switch to the player LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ; switch back to this event ENDIF ADD X TO M ; reset M ELSE ; must have been a direct hit OTHER ; switch to the player LET DIRECTION = 99 ; trigger the player explosion ENDSPRITE ;switch back to this event ENDIF ENDIF ENDIF IF Y >= 188 ; has missile reached bottom of the screen REMOVE ; remove it SUBTRACT 1 FROM S ; decrement the sprite counter ENDIF
So, we check for collisions with the player and then calculate how much of a direct hit it is, effectively creating a quick hit box, you can adjust these values to suit your own game and graphics.
One final thing to update, notice when we register a hit on the player we send an instruction to the player sprite to set its direction to 99, so we'll need to add a clause to our player event as follows ,open up your player event and add:
IF DIRECTION = 99 LET TYPE = 6 ; switch it to the explosion type LET IMAGE = 4 ; and the explosion image LET FRAME = 0 ; reset it to first frame LET DIRECTION = 1 ; this is the player exploding ENDIF
I've done this to save memory, since there may be multiple places in the code where we explode the player (notice I replaced the type, image, frame & direction settings in the Alien/Player collision in the ALien event (sprite type 5) earlier. Every byte counts!
OK, that should be everything we need to give the Aliens their firepower...time for a test!
That looks OK to me for now, we can always refine it later as the game progresses.
Next we'll do a bit of housekeeping and tidy some stuff up.
NEXT: PART 10 - A SPOT OF HOUSEKEEPING