invent 8bit

UFO: Alien Massacre

UFO defense but not really

UFO Alien Massacre is a little SCADS game I wrote, which appeared on Fred 53. It's a little shooter, where you control a UFO over a planet and must destroy the other UFOs without hitting the ground bases.

You can only drop bombs, while other UFOs can fire missiles sideways & bases launch missiles upwards, meaning you have precisely the wrong weapon for your task. Fun!

From memory I thought this game worked differently: you were the attacker and must destroy all ground bases and other UFOs. I suspect this might have been how it started, but I later flipped it to make it more challenging. It's confusing since the player sprite is red (looks bad) and the other UFOs are blue -- but so are the ground bases.

There's a nice twist that when you get hit your UFO "malfunctions" for a few seconds, careering out of control (taking momentum from the missile that hits you) and being harder to steer. The gradual acceleration/deceleration of the ship goes some way to hide the SCADs slowness.

Gameplay screenshot, dropping bombs. Gameplay screenshot, dropping bombs.

Malfunction counter showing in top right. Malfunction counter showing in top right.

Gameplay screenshot, dropping bombs. Post game review score & rank

It's a simple game, but fairly playable -- the description in FRED is shown below.

This month, in slot 'D' we have a SCADs game (!) by Martin Fitzpatrick. It's called 'UFO' and is a bit of a shoot-em-up. It took me a few goes to work out what I was supposed to do, but I think this is right. You're supposed to shoot the things that are flying in the air, not the groundbases. Although the ground bases can harm you, they also harm the UFOs.

You are a little ship (I've double-checked that bit for spelling mistakes!). You and your groundbases must shoot the UFOs. If you get shot, you lose control of flight and firing temporarily.

Too many shots, and it's that nasty little Game Over. Complete the levels and you go up a rank or two. When you complete all the levels (which, if I can do it can't be too hard), you're given an assessment of your progress. It takes a little to get into, but when you do, it's very playable.

Source code

Main game

basic
   10 GO TO 30
   20 ~FILE "draw1"
   30 LET score1=0,level=1,lives=5,rnk=1,rank=0,sht=0,fir=0,bas=0,crsh=0,energy=6
   40 ~INIT
   50 IF level=1 THEN GO SUB begin
   60 ~INKBLACK
   70 LET axs=0,ays=0,sx=0,sy=0,c=0,miss=0,hit=0,sp=0,hp=-1,con=0,onscr=0,l=0,re=0,mal=0,msy=0,msx=0
   80 DIM dead(63): FOR a=1 TO 63: LET dead(a)=5: NEXT a
   90 ~LINK 0,1
  100 FOR a=5 TO 8:~LINK a,14: NEXT a
  110 FOR a=9 TO 12:~LINK a,15: NEXT a
  120 FOR a=13 TO 16:~LINK a,16: NEXT a
  130 FOR a=17 TO 20:~LINK a,17: NEXT a
  140 FOR a=21 TO 24:~LINK a,18: NEXT a
  150 FOR a=25 TO 28:~LINK a,19: NEXT a
  160 FOR a=29 TO 34:~LINK a,31:~ANIMATE a,2: NEXT a
  170 FOR a=35 TO 40:~LINK a,33:~ANIMATE a,3: NEXT a
  180 FOR a=41 TO 46:~LINK a,35:~ANIMATE a,4: NEXT a
  190 ~AUTOSOUND 0,0,3:~AUTOSOUND 0,7,3
  200 FOR a=5 TO 31:~AUTOSOUND a,0,4: NEXT a
  210 ~MISSRANGE 50,63
  220 ~MISSOUND 1
  230 ~MISSILE 0,255,2,50,53,128,255,64:~AMMO 0,255
  240 FOR a=5 TO 46:~MISSILE a,255,10,54,63,128,255,64:~AMMO a,255: NEXT a
  250 ~YEDGE 0,2,2
  260 ~XEDGE 0,1,1
  270 FOR a=29 TO 46:~XEDGE a,1,1:~YEDGE a,0,0: NEXT a
  280 ~ROOM 0
  290 DIM sp$(24)
  300 FOR a=0 TO 240 STEP 16+(26-level)
  310 LET b=1+ RND (23)
  320 IF sp$(b)=" " THEN ~SPUT b+4,a,32,128: LET sp$(b)=" STEP ": ELSE GO TO 310
  330 NEXT a
  340 ~SPUT 0,122,100,128
  350 DIM ons(15)
  360 FOR a=1 TO level+1
  370 REM LET l=l+1: IF l>=level+1 THEN FOR a=1 TO 15: IF ons(a)<>0 THEN NEXT a: ELSE GO TO 341
  380 LET b=1+ RND (14)
  390 IF a >= level+1 THEN GO TO 440
  400 IF ons(b)=0 THEN LET ons(b)=1: ELSE GO TO 380
  410 ~SPUT b+29, RND (240),32+ RND (158),128:~SPEED b+29, RND (5)- RND (5),0
  420 LET onscr=onscr-1
  430 NEXT a
  440 GO SUB disp
  450 IF level=16 THEN GO TO ending
  460 ~PNUMA 0,180,2,48,level
  470 ~COLOUR
  480 DO
  490 IF onscr=0 THEN LET level=level+1: GO TO 40
  500 LET r=5+ RND (41):~FIRE r
  510 LET r=5+ RND (41):~FIRE r
  520 LET r=29+ RND (17):~SPEED r,( RND (dead(r))- RND (dead(r))), RND (dead(r))- RND (dead(r))
  530 FOR b=1 TO 2
  540 ~MOVEALL:~VIEW
  550 ON b: GO SUB control
  560 ~MISSHIT  LENGTH (0,miss): IF miss <> 255 THEN GO SUB misshit
  570 NEXT b
  580 IF sp=0  AND lives>0 THEN ~HIT 0, LENGTH (0,hit): IF hit <> 0 THEN ~NEXTHIT  LENGTH (0,hit): GO SUB hitit
  590 IF lives=0 THEN ~TEXTA 82,100,"GAME OVER":~VIEW: PAUSE : GO SUB rating
  600 LET hp=hp-1
  610 IF hp <> 0 THEN LOOP
  620 IF sp-1=0 THEN ~SPUT 0,122,100,128: LET axs=0,ays=0,sx=0,sy=0: LET sp=sp-1,energy=6,re=0,mal=0
  630 GO SUB disp
  640 LET sp=0
  650 LOOP
  660 LABEL control
  670 IF re>0 THEN IF RND (re)>2 THEN GO TO 730
  680 ~GETINP 1, LENGTH (0,c)
  690 IF c=1  OR c=2  OR c=8 THEN LET ays=ays+((energy-1)/5)
  700 IF c=5  OR c=6  OR c=4 THEN LET ays=ays-1
  710 IF c=7  OR c=6  OR c=8 THEN LET axs=axs-1
  720 IF c=3  OR c=2  OR c=4 THEN LET axs=axs+1
  730 IF sx<axs THEN LET sx=sx+1
  740 IF sx>axs THEN LET sx=sx-1
  750 IF sy<ays THEN LET sy=sy+1
  760 IF sy>ays THEN LET sy=sy-1
  770 IF axs>5 THEN LET axs=5
  780 IF axs<-5 THEN LET axs=-5
  790 IF ays>5 THEN LET ays=5
  800 IF ays<-5 THEN LET ays=-5
  810 LET ays=ays-0.125
  820 ~SPEED 0,sx,sy
  830 IF c>32  AND c<255  AND RND (re)<2 THEN ~FIRE  0: LET ays=ays+0.12,fir=fir+1
  840 IF re>1  AND mal=1 THEN ~TEXTA 242,183,"M"
  850 IF re=0  OR mal=0 THEN ~TEXTA 242,183," "
  860 IF re>0 THEN LET mal= NOT mal: ELSE LET mal=1
  870 ~PNUMA 240,175,2,48,re
  880 LET re=re-1: IF re<0 THEN LET re=0
  890 RETURN
  900 LABEL misshit
  910 IF miss=0 THEN GO TO mainmiss
  920 LET dead(miss)=dead(miss)-1
  930 ~FIRE miss:~SCOM 4
  940 IF miss>28 THEN ~SPEED miss,( RND (dead(miss))- RND (dead(miss))),( RND (dead(miss))- RND (dead(miss)))
  950 IF dead(miss)=0 THEN ~TOGGLE miss:~TOGGLE miss:~ANIMOFF miss,64,9,1:~SCOM 4: IF miss<29 THEN LET score1=score1-100: LET rank=rank-10: IF score1<0 THEN LET score1=0,bas=bas+1
  960 IF dead(miss)=0  AND miss>28 THEN LET onscr=onscr+1
  970 LET who= PEEK (147456+(14592)+(64*miss)+51)
  980 IF miss>28  AND who>49  AND who<55 THEN LET score1=score1+10,rank=rank+2,sht=sht+1
  990 IF miss<29  AND who>49  AND who<55 THEN LET score1=score1-10,rank=rank-2: IF score1<0 THEN LET score1=0
 1000 GO SUB disp
 1010 RETURN
 1020 LABEL hitit
 1030 ~ANIMOFF 0,64,9,1
 1040 ~ANIMOFF hit,64,9,1
 1050 ~SCOM 1
 1060 LET lives=lives-1,bas=bas+1: IF hit>28 THEN LET onscr=onscr+1,crsh=crsh+1
 1070 IF hit<29 THEN LET score1=score1-25: ELSE LET score1=score1+5,rank=rank+1,bas=bas+1
 1080 IF score1<0 THEN LET score1=0
 1090 LET sp=1
 1100 LET hp=6
 1110 IF lives=0 THEN LET sp=0
 1120 GO SUB disp
 1130 RETURN
 1140 LABEL mainmiss
 1150 LET who= PEEK (147456+(14592)+51)
 1160 LET msy= PEEK ((147456+14592)+(64*who)+12)
 1170 LET msx= PEEK ((147456+14592)+(64*who)+11)
 1180 IF msx>15 THEN LET msx=-(255-msx)
 1190 IF msy>15 THEN LET msy=-(255-msy)
 1200 LET sx=sx+msx: LET sy=sy-msy
 1210 LET re=re+15
 1220 LET energy=energy-1
 1230 IF energy=0 THEN LET crsh=crsh+1: GO TO 1260
 1240 GO SUB disp
 1250 RETURN
 1260 ~ANIMOFF miss,64,9,1
 1270 LET sp=sp+miss+1
 1280 LET lives=lives-1
 1290 IF lives=0 THEN LET sp=0
 1300 GO SUB disp
 1310 LET hp=6
 1320 RETURN
 1330 LABEL disp
 1340 IF rnk<1 THEN LET rnk=1
 1350 IF rank>9 THEN LET rnk=rnk+1,rank=rank-10
 1360 ~PNUMA 94,158,5,48,score1
 1370 IF energy>0 THEN ~PNUMA 140,158,1,48,energy-1
 1380 ~PNUMA 152,158,1,48,lives
 1390 ~TEXTA 88,170,"RANK - "+ STRS rnk
 1400 RETURN
 1410 LABEL ending
 1420 ~TEXTA 72,100,"WELL DONE"
 1430 ~SCOM 2: DO
 1440 PAUSE 2:~COLOUR
 1450 ~MOVEALL:~VIEW
 1460 LOOP UNTIL INKEY$ <> ""
 1470 GO SUB rating
 1480 RUN
 1490 LABEL begin
 1500 ~INIT:~ROOM 0:~COLOUR
 1510 ~TEXTA 30,100,"METROPOLIS SOFTWARE"
 1520 ~SCOM 2:~VIEW: PAUSE 250:~CTEXT
 1530 ~TEXTA 80,100,"PRESENTS"
 1540 ~SCOM 2:~VIEW: PAUSE 250:~CTEXT
 1550 ~TEXTA 20,100,"UFO - ALIEN MASSACRE!"
 1560 ~SCOM 2:~VIEW: PAUSE
 1570 ~TEXTA 24,50,"BY MARTIN FITZPATRICK"
 1580 ~VIEW: PAUSE 100:~CTEXT
 1590 RETURN
 1600 LABEL rating
 1610 ~INKBLACK
 1620 ~MAINSCR:~FULLCLEAR:~CTEXT:~COPYSCREEN
 1630 ~TEXTA 10,170,"BULLETS USED "
 1640 ~TEXTA 10,160,"ENEMIES SHOT "
 1641 ~TEXTA 10,150,"ENEMIES RAMMED "
 1650 ~TEXTA 10,140,"BASES DESTROYED "
 1651 ~TEXTA 10,130,"LEVEL "
 1652 ~TEXTA 10,120,"RANK "
 1653 ~TEXTA 10,110,"SCORE "
 1660 ~PNUMA 200,170,2,48,fir
 1665 ~PNUMA 200,160,2,48,sht
 1670 ~PNUMA 200,150,2,48,crsh
 1671 ~PNUMA 200,140,2,48,bas
 1672 ~PNUMA 200,130,2,48,level
 1673 ~PNUMA 200,120,2,48,rnk
 1674 ~PNUMA 200,110,2,48,score1
 1680 LET rat= INT (((level*10)+(rnk*10)+((lives+score1)*10)+crsh))
 1685 LET rat=rat- INT (10*(fir/(sht+1))+(bas))
 1690 IF rat<0 THEN LET rat=0
 1700 ~TEXTA 60,50,"RATING "+" - "
 1710 ~PNUMA 160,50,3,48,rat
 1720 ~VIEW:~COLOUR: PAUSE 0
 1730 RUN
 1740 RETURN

Loader

basic
    1 ON ERROR err
    5 SCREEN 1: FOR a=2 TO 15: CLOSE SCREEN a: NEXT a
   10 REM BASIC INTERPRETOR, GLENCO SOFTWARE 1992
   20 CLEAR 65535: OPEN TO 3: LET MCODE=&14000: LET SCR2=&23000: MODE 4: DEF KEYCODE 193,"MODE 3:CLS#": DEF KEYCODE 192,"~:": LET A= UDG "~": FOR I=A TO A+7: READ B: POKE I,B: NEXT I: DATA 62,62,62,62,62,62,62,0: IF PEEK (&511F)=192 THEN GO TO MEM512: ELSE GO TO MEM256
   30 LABEL {20}{1}MEM512{20}{1}{20}{0}
   40 LET FLAG=0: LET SCR1=&83000: LET ms=30: FOR I=&5103 TO &511C: IF PEEK (I) <> 0 THEN LET FLAG=99
   50 NEXT I: FOR I=&5108 TO &511C: POKE I,&F0: NEXT I: GO TO NEXT_PART
   60 LABEL {20}{1}MEM256{20}{0}
   70 LET FLAG=0: LET SCR1=&43000: LET ms=14: FOR I=&5103 TO &510C: IF PEEK (I) <> 0 THEN LET FLAG=99
   80 NEXT I: IF FLAG=99 THEN GO TO MEM_ERROR: ELSE FOR I=&5108 TO &510C: POKE I,&F0: NEXT I: GO TO NEXT_PART
  110 LABEL {20}{1}NEXT_PART{20}{0}
  120 OPEN SCREEN 2,4: POKE &5103,&F0,&F0,&F0: PEN 1: CSIZE 8,16: CSIZE 8,8: LOAD "SCAD.S1"  CODE MCODE: POKE MCODE+3,MS: LOAD "SCAD.S2"  CODE SCR1: LOAD "SCAD.S2"  CODE SCR2: CALL MCODE
  130 ~INIT
  140 LOAD "UFO"
  150 DEF PROC err
  160 ON ERROR STOP
  170 ON ERROR err
  180 GO TO lino
  190 END PROC
Tiny games for your BBC micro:bit.

To support developers in [[ countryRegion ]] I give a [[ localizedDiscount[couponCode] ]]% discount on all books and courses.

[[ activeDiscount.description ]] I'm giving a [[ activeDiscount.discount ]]% discount on all books and courses.