#program basicInvaders
#autoline 10,10
#autostart
; NextBASIC Invaders example game
;
RUN AT 2
PROC main()
;
DEFPROC playGame()
REPEAT 
SPRITE MOVE 
LET %k=% SPRITE OVER (65,0 TO 35,7): IF %k THEN PROC kill(%k&127)
IF % SPRITE OVER (64,68 TO 71,6,7) THEN PROC die()
IF % SPRITE AT (a(t),1)=(h+(t*20)+8) THEN PROC moveDown(): WHILE %(h+(b*20))<240
LET %m=%(( IN i(0)&k(0)=v(0))-( IN i(1)&k(1)=v(1)))<<1: IF %(p+m)<304 THEN LET %p=%p+m: SPRITE %64,%p
IF %( IN i(2)&k(2)=v(2)) AND ( SPRITE 65=0) THEN PROC shoot()
IF %(( RND 100>85) AND NOT ( SPRITE 68 AND SPRITE 69 AND SPRITE 70 AND SPRITE 71)) THEN PROC zap()
IF %(e<10) AND (e<(d+(n/5))) AND (SPRITE AT(a(b),5)=0) THEN PROC speedUp()
IF %(( RND 50000>49950) AND ( SPRITE 35=0)) THEN PROC saucer()
REPEAT UNTIL %a=0
PRINT AT 0,11; FLASH 1; INK 6;"GAME OVER!" 
FOR %i=1 TO 20
LET octave=% RND 9
LET p$="O"+ STR$ octave+"M56X16384UW0c"
PLAY p$,p$,p$
NEXT %i
PRINT AT 2,5; INVERSE 1;"Press any key for menu"
NEXT #0 TO key
ENDPROC 
;
DEFPROC updateMovement()
LOCAL %y,%o
REM Update x limits for each anchor sprite
FOR %y=0 TO 4
IF %e(y) THEN SPRITE CONTINUE %a(y),%32+(20*(l(y)-l)) TO %272-(20*(r-l(y)))
NEXT %y
ENDPROC 
;
DEFPROC shoot()
REM Start shot from player position
SPRITE 65,%p,240,1,1
SPRITE CONTINUE 65,%p STOP ,0 TO 240 STEP -4 RUN ,1, BIN 11
PLAY "M56X2048W0U1c","X2048U1c","X2048U1c"
ENDPROC 
;
DEFPROC zap()
LOCAL %i,x,y,o
REM Find an unused alien missile sprite
LET %i=67
REPEAT 
LET %i=%i+1
REPEAT UNTIL % SPRITE i=0
REM Choose a column
LET %x=%l+ RND (r-l+1): LET %y=%b
REM Find row of lowest invader in column
REPEAT : WHILE % NOT (((l(y)<x) AND SPRITE (a(y)+x)) OR ((l(y)=x) AND SPRITE (a(y))))
LET %y=%y-1
REPEAT UNTIL %(y=-1) 
IF %y=-1 THEN ENDPROC 
REM Start missile in middle of invader
LET %o=% SPRITE AT (a(y),1)+8
SPRITE %i,% SPRITE AT (a(y),0)+((x-l(y))*20),%o,%2+ RND 2,1
SPRITE CONTINUE %i, STOP ,%o TO 255 STEP 2 RUN ,2 TO 3, BIN 11
ENDPROC 
;
DEFPROC score(%s)
LOCAL %i
FOR %i=1 TO 5
SPRITE %i+80,%312-(i*8),8,%(s MOD 10)+14,1
LET %s=%s/10
NEXT %i
SPRITE %80,312,8,14,1
ENDPROC 
;
DEFPROC kill(%k)
LOCAL %y,%x
SPRITE 65,,,,0
REM Special handling for saucer
IF %k=35 THEN PROC killSaucer(): ENDPROC 
LET %n=%n+1
REM Find grid position of victim
LET %y=%k/7
IF %k=a(y) THEN LET %x=%l(y): ELSE LET %x=%k-a(y)
REM Find pixel position & pattern of anchor
LET %u=% SPRITE AT (a(y),0): LET %v=% SPRITE AT (a(y),1): LET %w=% SPRITE AT (a(y),2)
LET %z=%z+(5-y)
PROC score(%z)
REM Set sprite 3 to explosion at victim location
SPRITE 66,%u+(x-l(y)*20),%v,24,1
SPRITE CONTINUE 66, STOP , STOP ,24 TO 27, BIN 1100000,3
PLAY "O0M56X6144UW0c","O0X6144Uc","O0X6144Uc"
REM Disable victim's sprite
IF %l(y)=r(y) THEN PROC removeRow(%y):ENDPROC 
IF %l(y)=x THEN PROC removeLeft(%y,%u,%v,%w):ENDPROC 
SPRITE % SGN {-k},,,,0
IF %r(y)=x THEN PROC removeRight(%y)
ENDPROC 
;
DEFPROC saucer()
REM Start saucer from left to right
SPRITE 35,0,8,28,1
SPRITE CONTINUE 35,0 TO 319 STEP 1 RUN , STOP ,28, BIN 11,% RND 2
ENDPROC 
;
DEFPROC killSaucer()
REM Change saucer into explosion
SPRITE 35,,,24
SPRITE CONTINUE 35, STOP , STOP ,24 TO 27, BIN 1100000,3
LET %z=%z+10
PROC score(%z)
PLAY "O6M56X8192UW0c","O6X8192Uc","O6X8192Uc"
ENDPROC 
;
DEFPROC removeLeft(%y,%u,%v,%w)
LOCAL %x
REM Find grid position of new anchor
LET %x=%l(y)+1
REPEAT 
WHILE % SPRITE (a(y)+x)=0
LET %x=%x+1
REPEAT UNTIL 0
REM Set anchor to new position
SPRITE %a(y),%u+((x-l(y))*20),%v,%w,1
REM Disable the one we promoted to anchor
SPRITE % SGN {-(a(y)+x)},,,,0
LET %l(y)=%x
REM Update offsets of remaining sprites
FOR %i=%x+1 TO 6
SPRITE % SGN {-(a(y)+i)},%(i-x)*20
NEXT %i
REM To prevent flicker when anchor changes, perform updates now,
REM but waiting until the scanline after the row being changed
SPRITE MOVE INT %(v+16)&255
PROC findLeft()
ENDPROC 
;
DEFPROC findLeft()
LOCAL %i
LET %l=99
FOR %i=0 TO 4
IF %(e(i) AND (l(i)<l)) THEN LET %l=%l(i)
NEXT %i
PROC updateMovement()
ENDPROC 
;
DEFPROC removeRight(%y)
LOCAL %x
LET %x=6
REPEAT 
WHILE % SPRITE (a(y)+x)=0
LET %x=%x-1
REPEAT UNTIL 0
IF %x=0 THEN LET %r(y)=%l(y): ELSE LET %r(y)=%x
PROC findRight()
ENDPROC 
;
DEFPROC findRight()
LOCAL %i
LET %r=0
FOR %i=0 TO 4
IF %(e(i) AND (r(i)>r)) THEN LET %r=%r(i)
NEXT %i
PROC updateMovement()
ENDPROC 
;
DEFPROC removeRow(%y)
LOCAL %i
SPRITE %a(y),,,,0
LET %e(y)=0
PROC findLeft()
PROC findRight()
REM Find top/bottom enabled rows
LET %t=99: LET %b=0
FOR %i=0 TO 4
IF %(e(i) AND (i<t)) THEN LET %t=%i
IF %(e(i) AND (i>b)) THEN LET %b=%i
NEXT %i
IF %t<99 THEN ENDPROC 
FOR %i=1 TO 16: SPRITE MOVE : NEXT %i
BEEP .5,10
REM Increase difficulty for next sheet
IF %d<10 THEN LET %d=%d+1
PROC initSheet()
ENDPROC 
;
DEFPROC moveDown()
LOCAL %y
LET %h=%h+8
REM Update y limits for each anchor
FOR %y=0 TO 4
IF %e(y) THEN SPRITE CONTINUE %a(y),,%h+(y*20) TO %h+(y*20)+8 STEP 8
NEXT %y
ENDPROC 
;
DEFPROC setSpeed(%i)
LET %e=%i
REM Invader "slowness": 20/18/16/14/12/.../2
LET %s=%2*(10-i)
REM Use "slowness" 1 at maximum speed
IF %i=10 THEN LET %s=1
ENDPROC
;
DEFPROC speedUp()
LOCAL %y
PROC setSpeed(%e+1)
REM Update rate & delay for each anchor
FOR %y=%t TO %b
IF %e(y) THEN SPRITE CONTINUE %a(y),,,,,%5*s,%(5-y)*s
NEXT %y
ENDPROC 
;
DEFPROC die()
LOCAL %i,o,p$,octave
REM Change base into explosion
SPRITE 64,,,24
SPRITE CONTINUE 64, STOP , STOP ,24 TO 27, BIN 1100000
REM Loop through animation
FOR %i=1 TO 5
SPRITE MOVE 
LET octave=% RND 9
LET p$="O"+ STR$ octave+"M56X6144UW0c"
PLAY p$,p$,p$
NEXT %i
REM Reduce lives
LET %a=%a-1
IF %a>0 THEN SPRITE %128-a,,,,0
LET %p=0
IF %a THEN SPRITE 64,%p,,0,1
ENDPROC 
;
DEFPROC initSheet()
LOCAL %y,%x,%i
REM Remove any missiles
FOR %i=68 TO 71
SPRITE %i,,,,0
NEXT %i
REM Remove saucer and player's shot
SPRITE 35,,,,0
SPRITE 65,,,,0
REM Invaders killed
LET %n=0
REM Initialise speed
PROC setSpeed(%d)
REM Initial height of top row
LET %h=32
FOR %y=0 TO 4
REM Set anchor for each row
LET %a(y)=%y*7
SPRITE %a(y),32,%h+(y*20),%4+(y*2),1, BIN 000
FOR %x=1 TO 6
REM Set remaining row sprites
SPRITE % SGN {-(a(y)+x)},%x*20,0,0,1, BIN 110
NEXT %x
SPRITE MOVE : BEEP .2,0 
NEXT %y
FOR %y=0 TO 4
REM Set auto-movement on anchor sprites
SPRITE CONTINUE %a(y), STEP 8 RUN ,%h+(y*20) TO %h+(y*20)+8 STEP 8 STOP ,%4+(y*2) TO %5+(y*2), BIN 01,%5*s,%(5-y)*s
REM Record leftmost/rightmost positions and mark enabled
LET %l(y)=0: LET %r(y)=6: LET %e(y)=1
NEXT %y
REM Record overall left/right/top/bottom positions
LET %l=0: LET %r=6
LET %t=0: LET %b=4
PROC updateMovement()
ENDPROC 
;
DEFPROC initGame()
;
LOCAL %i
CLS 
REM Score
LET %z=0
PROC score(%z)
REM Player
LET %p=0
SPRITE 64,%p,240,0,1
REM Lives
LET %a=3
FOR %i=1 TO %a-1
SPRITE %128-i,%(i-1)*20,0,0,1
NEXT %i
REM Show player & lives
SPRITE MOVE 
PROC initSheet()
ENDPROC 
;
DEFPROC initSprites()
SPRITE CLEAR : PALETTE CLEAR 
LOAD "basicInvaders.spr" BANK 12
SPRITE BANK 12
ENDPROC 
;
DEFPROC main()
LOCAL key
PROC initSprites()
LET ctrls=1
REPEAT 
LAYER CLEAR : SPRITE CLEAR 
REM Use batching mode
SPRITE STOP
PAPER 0: INK 7: BORDER 0: CLS 
SPRITE PRINT 1: SPRITE BORDER 1
POKE 23658,0: REM turn off CAPS LOCK
PRINT AT 0,6; INK 6; INVERSE 1;" NextBASIC Invaders "
PRINT AT 4,0;"Press "; INVERSE 1;"K"; INVERSE 0;" for keys (O, P, SPACE)"
PRINT AT 6,0;"Press "; INVERSE 1;"J"; INVERSE 0;" for Kempston joystick"
PRINT AT 12,0;"Press "; INVERSE 1;"0"; INVERSE 0;" to "; INVERSE 1;"9"; INVERSE 0;" for difficulty"
PRINT AT 13,0;"to start (0=easiest, 9=hardest)"
PROC setInput(ctrls)
REPEAT 
NEXT #0 TO key
IF key= CODE "k" THEN PROC setInput(1): BEEP .5,0
IF key= CODE "j" THEN PROC setInput(2): BEEP .5,0
LET %d=key- CODE "0"
REPEAT UNTIL %d<=9
BEEP .5,%2*d
PROC initGame()
PROC playGame()
REPEAT UNTIL 0
ENDPROC 
;
DEFPROC setInput(l)
LOCAL %i,%n,t$
LET ctrls=l
RESTORE 
FOR %n=1 TO l
READ t$
FOR %i=0 TO 2
READ %i(i),%k(i),%v(i)
NEXT %i
NEXT %n
PRINT AT 8,0;"Controls: "; INVERSE 1;t$
ENDPROC 
;
DATA "KEYBOARD",57342,1,0,57342,2,0,32766,1,0
DATA "JOYSTICK",31,1,1,31,2,2,31,16,16
SAVE "basicInvaders.bas" LINE 0:.bas2txt -s basicInvaders.bas
