gigatron/rom/Contrib/at67/gbas/games/Invader/Invader.gbas
2025-01-28 19:17:01 +03:00

1049 lines
26 KiB
Plaintext

_runtimePath_ "../../runtime"
_runtimeStart_ &h7FFF
_arraysStart_ &h7FFF
_codeRomType_ ROMv3
'use this after _runtimeStart_ to specify maximum number of sprites if you have more than 48
_maxNumSprites_ 56
'size of your most complex expression, (temporary variables required)*2, defaults to 8
_tempVarSize_ 4
'free string work area, (better not use any of the string runtime!)
free STRINGWORKAREA
'defines the amount of contiguous RAM needed for sprite stripes, (in this case 15*6 + 1), also min address and search direction
_spriteStripeChunks_ 15, &h3BA0, descending
'frequency and volume LUTS for player bullet shoot sound, (modelled in Desmos)
const PBL_SIZ = 32
const PBF_LUT = &h7FA0
const PBV_LUT = &h7FE0
def word(PBF_LUT, y, 0.0, 1.0, PBL_SIZ) = exp(-3.0*y)*6000.0 + 14000.0
def byte(PBV_LUT, y, 0.0, 1.0, PBL_SIZ) = exp(-5.0*pow(y, 3.0))*32.0
'frequency LUT for saucer sound, (modelled in Desmos)
const SCF_SIZ=12
const SCF_LUT=&h7EA0
'def word(SCF_LUT, y, 0.0, 180.0, SCF_SIZ) = sin(y) * sin(y) * 2121.0 + 2121.0
def word(SCF_LUT, y, 0.0, SCF_SIZ, SCF_SIZ) = (y % (SCF_SIZ/2)) * 300.0 + 3000.0
const IEF_SIZ=3
const IEF_LUT=&h7EC0
'frequency LUT for invader explosion sound, (modelled in Desmos)
def word(IEF_LUT, y, 0.0, IEF_SIZ, IEF_SIZ) = 6000.0*(1 - exp(-0.5*y))
const PEF_LUT=&h7DA0
const PEF_SIZ=32
def word(PEF_LUT, 0.0, PEF_SIZ, PEF_SIZ) = rand(PEF_SIZ) / PEF_SIZ * 300.0
module "InvaderSprites.i"
const LIFE_Y = 4
const LIFE_X = 136
const HIGH_Y = 1
const HIGH_X = 97
const SCORE_Y = 1
const SCORE_X = 31
const SCORE_LEN = 6
const LEVEL_Y = 1
const LEVEL_X = 2
const LEVEL_LEN = 5
const SAUCER_LEN = 5
dim highBCD%(SCORE_LEN - 1) = 0
dim scoreBCD%(SCORE_LEN - 1) = 0
dim pointsBCD%(SCORE_LEN - 1) = 0
dim levelBCD%(LEVEL_LEN - 1) = 0
dim saucerBCD%(SAUCER_LEN - 1) = 0
const ILIST_END = 128
const INVADERS_J = 5
const INVADERS_I = 10
dim invaders(INVADERS_J - 1, INVADERS_I - 1) = {0+1*256, 0+1*256, 0+1*256, 0+1*256, 0+1*256, 0+1*256, 0+1*256, 0+1*256, 0+1*256, ILIST_END+1*256,
0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256,
0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256, 0+2*256,
0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256,
0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256, 0+3*256}
const INVADERS_H = 8
const INVADERS_Y = 10
const INVADERS_X = 12
'first saucer takes 28 seconds, all others take 25 until game over
const SAUCER_DELAY = 28
const SAUCER_YPOS = 9
const SAUCER_XSTART = 0
const SAUCER_XEND = 141
dim saucerx%(1) = SAUCER_XSTART, SAUCER_XEND
dim iypos%(INVADERS_J - 1) = 4*INVADERS_Y, 3*INVADERS_Y, 2*INVADERS_Y, 1*INVADERS_Y, 0*INVADERS_Y
dim ixpos%(INVADERS_I - 1) = 0*INVADERS_X, 1*INVADERS_X, 2*INVADERS_X, 3*INVADERS_X, 4*INVADERS_X, 5*INVADERS_X, 6*INVADERS_X, 7*INVADERS_X, 8*INVADERS_X, 9*INVADERS_X
'struct{invader, xy, prev, next}
dim istruct((INVADERS_J * INVADERS_I) - 1, 3)
dim iaddress(INVADERS_J - 1, INVADERS_I - 1)
const DIVX = &h0400
def byte(DIVX, x, 0.0, 160.0, 160) = floor(x / INVADERS_X)
const DIVY = &h0500
def byte(DIVY, x, 0.0, 120.0, 120) = floor(x / INVADERS_Y)
const IORIGIN_Y = 56
const IORIGIN_X = 1
const INV_SML = 0
const INV_MED = 0
const INV_BIG = 0
const MAX_LIVES = 3
const NUM_BARRIERS = 3
const BARRIER_Y = 90
const BARRIER_X = 20
const BARRIER_OFFSET = 48
const PLAYER_Y = 114
const PLAYER_X = 1
const PLAYER_XEND = 147
const LEVEL_X6 = 24
const IEXPLODE_DELAY = 10
const BEXPLODE_DELAY = 8
const SEXPLODE_DELAY = 15
const NUM_ITYPES = 3
dim irsprites%(NUM_ITYPES - 1) = InvSmlRt, InvMedRt, InvBigRt
dim ilsprites%(NUM_ITYPES - 1) = InvSmlLt, InvMedLt, InvBigLt
dim iscores%(NUM_ITYPES - 1) = 30, 20, 10
const NUM_SSCORES = 16
dim sscores(NUM_SSCORES - 1) = 100, 50, 50, 100, 150, 100, 100, 50, 300, 100, 100, 100, 50, 150, 100, 50
const INVADER_DEAD = &h8000
const NUM_IBULLETS = 3
dim ibxy(NUM_IBULLETS - 1) = 0
def livesLevel, delayLevel, px, py, ixorigin, iyorigin, ii, jj, xflip, iflip, icurrent, ishoot, sevol, timeTicks, pflip, iscore, imarch, endgame, pbxy, pbullet, ipbcount, ibindex, iexplode, itimer, bexplode, btimer, saucerxy, satimer, sefreq, stimer, sexplode, audmarch, audshoot, aviexplode, oldbutton
call initSystem
reset:
call resetLevel
start:
call startLevel
init:
call initVars
repeat
'wait
call waitScanline
call handleInput
call drawPlayerBullet
call drawPlayer
call movePlayer
call checkInvaders
call checkNextLevel
call drawInvaderBullets
call checkInvaderBullets
call drawInvaders
call updateSaucer
call updateScore
call updateAudio
call updateTime
if &(endgame.lo) then goto start
if &(endgame.hi) then goto reset
&forever
proc drawPlayerBullet
'no bullets while invader is asploding
if &(iexplode) then return
'no bullet so exit
if pbullet.hi &&= 0 then return
asm 'setup SYS function
LDWI SYS_VDrawBits_134
STW giga_sysFn
endasm
gosub checkPlayerBullet
asm 'setup SYS params
LDWI 0x3F00
STW giga_sysArg0
LDW _pbxy
STW giga_sysArg4
endasm
if &(pbullet.lo)
asm 'draw bullet
LDI 0xF8
ST giga_sysArg2
SYS 134
endasm
pbxy.hi = pbxy.hi - 2
if pbxy.hi &&< 14
pbullet = 0
asm 'erase bullet if top of screen
LDI 0x00
ST giga_sysArg2
SYS 134
endasm
endif
return
endif
asm 'erase bullet if pbullet.lo = 0
LDI 0x00
ST giga_sysArg2
SYS 134
endasm
pbullet = 0
endproc
proc drawPlayer
if pflip.lo &= 1
sprite FlipX, Player + 1, px, py
else
sprite NoFlip, Player + 0, px, py
endif
endproc
proc handleInput
local button
button = get("BUTTON_STATE") XOR 255
if &(button AND &h01) then pflip = &h0101
if &(button AND &h02) then pflip = &h0100
if (oldbutton AND &h80) &&= 0
if &(button AND &h80)
gosub playerBullet
endif
endif
oldbutton = button
endproc
playerBullet:
if &(pbullet.hi) then return
inc ipbcount.lo
audshoot = &h0100
pbullet = &h0101
if &(pflip.lo)
pbxy = (px + 6) + ((py - 5 + 8) LSL 8)
else
pbxy = (px + 5) + ((py - 5 + 8) LSL 8)
endif
return
proc movePlayer
if &(pflip.hi)
pflip.hi = 0
if &(pflip.lo)
inc px
if px &&> PLAYER_XEND
px = PLAYER_XEND
endif
else
dec px
if px &&< PLAYER_X
px = PLAYER_X
endif
endif
endif
endproc
proc drawInvaders
local x, y, invader
if &(iexplode) then return
'list is empty, waiting for last invader to explode
if icurrent &= 0 then return
'get current linked list entry
invader = deek(icurrent)
x = ixorigin + peek(icurrent + 2)
y = iyorigin - peek(icurrent + 3)
'invaders landed
if y &&>= PLAYER_Y - 7
call gameOver
return
endif
'draw invader, (erase edge for last invader left to right)
if xflip &> 0
if xflip &= 3
if x &> 0
'we can call a proc inside another proc because called proc does not modify local/param slots
call eraseInvaderEdge, x, y
endif
endif
sprite FlipX, irsprites(invader.hi - 1) + ((x LSR 1) AND 1), x, y
else
sprite NoFlip, ilsprites(invader.hi - 1) + ((x LSR 1) AND 1), x, y
endif
'erase previous row invader
if (imarch)
sprite NoFlip, InvBlk, x, y - INVADERS_Y
endif
'horizontal march flip
if x &>= PLAYER_XEND
iflip = -2
elseif x &&<= 1
iflip = 2
if ipbcount.hi &&= (INVADERS_J * INVADERS_I) - 1
iflip = 3
endif
endif
'next entry in linked list
icurrent = deek(icurrent + 6)
'reached end of linked list
if (invader.lo AND ILIST_END) &&= 0 then return
if iflip &&<> xflip
'vertical march
imarch = 1
iyorigin = iyorigin + INVADERS_H
else
'horizontal march
imarch = 0
ixorigin = ixorigin + iflip
endif
xflip = iflip
endproc
psmashBarrier:
asm
LDWI 0x0C00
STW giga_sysArg0 'FGBG colour
LDWI 0xFEFF
ADDW _pbxy
STW giga_sysArg4 'offset
LD giga_rand0
ANDI 0xC0
ST giga_sysArg2
SYS 134 'left damage
INC giga_sysArg4
LDI 0x00
ST giga_sysArg2
SYS 134 'center damage
INC giga_sysArg4
LD giga_rand2
ANDI 0xC0
ST giga_sysArg2
SYS 134 'right damage
endasm
return
proc drawBarriers
local i, x, y
x = BARRIER_X : y = BARRIER_Y
for i=0 to NUM_BARRIERS - 1
sprite NoFlip, Barrier, x, y
x = x + BARRIER_OFFSET
next i
endproc
proc checkInvaders
local i, j, x, y, iaddr, iprev, inext, invader
'erase invader explosion and restart invader march
if &(iexplode)
inc itimer.lo
if itimer.lo &= IEXPLODE_DELAY
sprite NoFlip, InvBlk, iexplode.lo, iexplode.hi
itimer.lo = 0 : iexplode = itimer.lo
endif
return
endif
if pbullet.hi &&= 0 then return
y = iyorigin - (pbxy.hi - (8 + 3))
if y &&< 0 then return
x = pbxy.lo - ixorigin
if x &&< 0 then return
i = peek(DIVX + x)
if i &&>= INVADERS_I then return
j = (INVADERS_J - 1) - peek(DIVY + y)
if j &&>= INVADERS_J then return
if j &&< 0 then return
'invader already erased
iaddr = iaddress(j, i) : invader = deek(iaddr)
if invader.hi &&= 0 then return
'erase invader
poke iaddr + 1, 0
pbullet.lo = 0
iprev = deek(iaddr + 4)
inext = deek(iaddr + 6)
doke iprev + 6, inext
doke inext + 4, iprev
if icurrent &&= iaddr then icurrent = inext
'update score
iscore = invaders(j, i).hi
'invader shoot needs to know which invaders are dead
invaders(j, i) = invaders(j, i) OR INVADER_DEAD
'update linked list end
if &(invader.lo AND ILIST_END) then poke iprev, peek(iprev) OR ILIST_END
call erasePlayerBullet
'explode invader
x = ixorigin + ((i LSL 3) + (i LSL 2)) 'x = ixorigin + i*INVADERS_X
j = (INVADERS_J - 1) - j
y = iyorigin - ((j LSL 3) + j + j) 'y = iyorigin - j*INVADERS_Y;
if xflip &&> 0
sprite NoFlip, IExplode, x, y
else
sprite FlipX, IExplode + 1, x, y
endif
'halt march to display explosion
itimer = 0 : iexplode = x + (y LSL 8)
gosub disableShootSound
'erase previous row invader if invaders just marched vertically
if (imarch)
sprite NoFlip, InvBlk, x, y - INVADERS_Y
endif
inc ipbcount.hi
endproc
proc playerExplode
local bxy, t, f, v, i
for ibindex=0 &to NUM_IBULLETS - 1
bxy = ibxy(ibindex)
if &(bxy) then call eraseInvaderBullet, bxy
ibxy(ibindex) = 0
next ibindex
t = 0 : f = t
for v=63 downto 0
f = deek(PEF_LUT + (t LSL 1))
sound on, 1, f, v, 0
sound on, 2, f, v, 0
sound on, 3, f, v, 0
sound on, 4, f, v, 0
inc t
if t &&= PEF_SIZ then t = 0
sprite NoFlip, PExplode + ((v LSR 2) AND 1), px, py
wait
next v
sound off
endproc
proc drawInvaderBullets
local bxy
if timeTicks.hi &< 3 then return
for ibindex=0 to NUM_IBULLETS - 1
bxy = ibxy(ibindex)
if (bxy)
asm 'setup SYS function
LDWI SYS_VDrawBits_134
STW giga_sysFn
LDWI 0x3F00
STW giga_sysArg0
endasm
asm 'draw bullet
LDW _drawInvaderBullets_bxy
STW giga_sysArg4
LDI 0x1F
ST giga_sysArg2
SYS 134
endasm
inc bxy.hi
if bxy.hi &>= 121
asm 'erase bullet at bottom of screen
LDI 0x00
ST giga_sysArg2
SYS 134
endasm
bxy = 0
elseif peek(bxy + &h0700) &&= &h0C
asm 'erase bullet
LDI 0x00
ST giga_sysArg2
SYS 134
endasm
if bxy.hi &&> PLAYER_Y
ibxy(ibindex) = 0
call playerDied
return
endif
gosub ismashBarrier
bxy = 0
endif
else
call getNextInvaderShot, bxy
endif
ibxy(ibindex) = bxy
next ibindex
endproc
proc checkNextLevel
if ipbcount.hi &&= (INVADERS_J * INVADERS_I)
icurrent = 0 : ishoot = icurrent
if iexplode &&= 0
if aviexplode &&= 0
endgame.lo = 1
inc delayLevel.lo
if delayLevel.hi &&> 1
delayLevel.hi = delayLevel.hi LSR 1
endif
livesLevel.lo = livesLevel.lo + (INVADERS_H/2)
endif
endif
endif
endproc
'bxy is in the same slot as drawInvaderBullets, so it is passed in by reference
'drawInvaderBullets only uses slot 1 which matches our param slot, (bxy), so we are free to use locals
proc getNextInvaderShot, bxy
local i, j, xy
'list is empty, waiting for last invader to explode
if ishoot &&= 0 then return
'next shoot candidate
ishoot = deek(ishoot + 6)
if &(rnd(0) AND delayLevel.hi)
bxy = 0
return
endif
i = peek(ishoot) AND &h0F
j = (peek(ishoot) LSR 4) AND &h07
xy = deek(ishoot + 2)
'invaders checks for clear path before firing, (can fail if player manages to shoot out of order column invaders)
if j &= 4
if (invaders(j, i) AND INVADER_DEAD) &= 0
bxy = ixorigin + xy.lo + 5 + ((iyorigin - xy.hi + 14) LSL 8)
endif
elseif (invaders(j+1, i) AND INVADER_DEAD)
bxy = ixorigin + xy.lo + 5 + ((iyorigin - xy.hi + 14) LSL 8)
endif
endproc
proc checkInvaderBullets
local bxy
if (bexplode)
sprite NoFlip, BExplode, bexplode.lo, bexplode.hi
inc btimer
if btimer &= BEXPLODE_DELAY
sprite NoFlip, BulBlk, bexplode.lo, bexplode.hi
btimer = 0 : bexplode = btimer
endif
return
endif
if &(iexplode) then return
if pbullet.hi &&= 0 then return
if peek(pbxy - &h0100) &= &h3F
for ibindex=0 to NUM_IBULLETS - 1
bxy = ibxy(ibindex)
if (bxy)
if bxy.lo &= pbxy.lo
call eraseInvaderBullet, bxy
ibxy(ibindex) = 0
bexplode = pbxy - (3 + 11*256)
return
endif
endif
next ibindex
endif
endproc
checkPlayerBullet:
if peek(pbxy) &= &h0C
pbullet.lo = 0
gosub psmashBarrier
elseif peek(pbxy) &&= &h03
pbullet.lo = 0
sexplode = 1
endif
return
proc eraseInvaderBullet, xy
asm
LDWI SYS_VDrawBits_134
STW giga_sysFn
LDWI 0x3F00
STW giga_sysArg0
LDW _eraseInvaderBullet_xy
STW giga_sysArg4
LDI 0x00
ST giga_sysArg2
SYS 134
endasm
endproc
proc erasePlayerBullet
asm
LDWI SYS_VDrawBits_134
STW giga_sysFn
LDWI 0x3F00
STW giga_sysArg0
LDW _pbxy
STW giga_sysArg4
LDI 0x00
ST giga_sysArg2
SYS 134
endasm
endproc
ismashBarrier:
asm
LDWI 0x0C00
STW giga_sysArg0 'FGBG colour
LDWI 0x02FF
ADDW _drawInvaderBullets_bxy
STW giga_sysArg4 'offset
LD giga_rand0
ANDI 0x03
ST giga_sysArg2
SYS 134 'left damage
INC giga_sysArg4
LDI 0x00
ST giga_sysArg2
SYS 134 'center damage
INC giga_sysArg4
LD giga_rand2
ANDI 0x03
ST giga_sysArg2
SYS 134 'right damage
endasm
return
proc eraseInvaderEdge, x, y
asm
LDWI SYS_VDrawBits_134
STW giga_sysFn 'setup SYS function
LDWI 0x0000
STW giga_sysArg0 'FGBG colour
LD _eraseInvaderEdge_x
SUBI 1 'x - 1 to account for extra pixel when marching by 3
ST register0
LD _eraseInvaderEdge_y
ADDI 8
ST register0 + 1
LDW register0
STW giga_sysArg4 'addr = x + ((y + 8) LSL 8)
LDI 0x00
ST giga_sysArg2
SYS 134
endasm
endproc
proc gameOver
endgame.hi = 1
call playerExplode
endproc
proc playerDied
livesLevel.hi = livesLevel.hi - 1
call drawLives
call playerExplode
if livesLevel.hi &= 0 then endgame.hi = 1
endproc
proc updateScore
if iscore &&= 0 then return
bcdint @pointsBCD, peek(@iscores + (iscore - 1))
call drawScore
iscore = 0
endproc
proc updateTime
inc timeTicks.lo
if timeTicks.lo &&= 60
timeTicks.lo = 0
inc timeTicks.hi
endif
endproc
proc updateSaucer
local xy, odd, spoints, i, sy
'wait until there is room for the saucer
if iyorigin &= IORIGIN_Y then return
'player hit saucer
if &(sexplode)
pbullet.lo = 0
stimer.lo = saucerxy.lo
spoints = sscores(ipbcount.lo AND (NUM_SSCORES - 1))
bcdint @pointsBCD, spoints
bcdint @saucerBCD, spoints
call drawScore
return
endif
'update at 30Hz
if &(timeTicks.lo AND 1) then return
'saucerxy [odd:1 y:7][x:8]
if timeTicks.hi &&>= SAUCER_DELAY
timeTicks.hi = 3 'set seconds counter so that bullets are updated
odd = ipbcount.lo AND 1
saucerxy = (SAUCER_YPOS*256) + saucerx(odd)
if &(odd) then saucerxy = saucerxy OR &h8000
endif
if (stimer)
if stimer.hi &< SEXPLODE_DELAY
if ((timeTicks.lo LSR 2) AND 1)
sprite NoFlip, SExplode + 0, stimer.lo, SAUCER_YPOS
else
sprite NoFlip, SExplode + 1, stimer.lo, SAUCER_YPOS
endif
endif
inc stimer.hi
if stimer.hi &= SEXPLODE_DELAY
xy = stimer.lo + 3 + (256*(SAUCER_YPOS + 1))
sprite NoFlip, SauBlk, stimer.lo, SAUCER_YPOS
call drawSaucerScore, xy
elseif stimer.hi &&= (SEXPLODE_DELAY + SEXPLODE_DELAY + SEXPLODE_DELAY)
sprite NoFlip, SauBlk, stimer.lo, SAUCER_YPOS
stimer = 0
endif
endif
if saucerxy &&= 0 then return
xy = saucerxy AND &h7FFF
odd = saucerxy AND &h8000
i = (saucerxy.lo LSR 1) AND 3
if i &&= 3 then i = 0
sy = xy.hi + 3
if (odd)
sprite NoFlip, Saucer + 0, xy.lo, xy.hi
sprite NoFlip, SaucerStripLt + (2 - i), xy.lo, sy
saucerxy.lo = saucerxy.lo - 1
if saucerxy.lo &= SAUCER_XSTART
saucerxy = 0
sound off, 4
sprite NoFlip, SauBlk, SAUCER_XSTART, SAUCER_YPOS
endif
else
sprite FlipX, Saucer + 1, xy.lo, xy.hi
sprite FlipX, SaucerStripRt + i, xy.lo, sy
inc saucerxy.lo
if saucerxy.lo &> SAUCER_XEND
saucerxy = 0
sound off, 4
sprite NoFlip, SauBlk, SAUCER_XEND, SAUCER_YPOS
endif
endif
endproc
'overwrite waveform 0 in audio memory, (invader march)
'load wave, ../../res/audio/Invader/IMarch.gtwav, &h0700, 4
dim inotes%(3) = 40, 38, 36, 34
proc updateAudio
local t, n, v, i
set SOUND_TIMER, 255
i = (INVADERS_J * INVADERS_I - 1) - ipbcount.hi + 3
'invader march
if audmarch.lo &= 0
n = get("MIDI_NOTE", peek(@inotes + (audmarch.hi AND 3)))
sound on, 1, n, 63, 3
elseif audmarch.lo &&= min(i - 1, 4)
sound off, 1
endif
if audmarch.lo &>= i
audmarch.lo = 0
inc audmarch.hi
else
inc audmarch.lo
endif
'player shoot
if (audshoot.hi)
n = deek(PBF_LUT + (audshoot.lo LSL 1))
v = peek(PBV_LUT + audshoot.lo)
sound on, 2, n, v, 3
sound on, 3, n, v, 0
inc audshoot.lo
if audshoot.lo &= PBL_SIZ
gosub disableShootSound
endif
endif
'saucer sound and explosion
if (saucerxy OR sefreq)
n = deek(SCF_LUT + (satimer LSL 1)) - sefreq
sound on, 4, n, 32, 2
inc satimer
if satimer &= SCF_SIZ then satimer = 0
if (sexplode)
sefreq = 1
sexplode = 0 : saucerxy = sexplode
endif
if (sefreq)
sound on, 3, n LSR 4, sevol, 0
if satimer &= 0
sefreq = sefreq + 600
sevol = sevol - 15
if sevol &<= 0
sevol = 60
sefreq = 0
sound off, 3
sound off, 4
endif
endif
endif
endif
'invader explosion
if (iexplode OR aviexplode)
v = 63 - aviexplode
sound on, 2, deek(IEF_LUT + (itimer.hi LSL 1)), v, 3
inc itimer.hi
if itimer.hi &= IEF_SIZ then itimer.hi = 0
aviexplode = aviexplode + 2
if aviexplode &>= 64
itimer.hi = 0 : aviexplode = itimer.hi
sound off, 2
endif
endif
endproc
disableShootSound:
audshoot = 0
sound off, 2
sound off, 3
return
proc drawScore
local i, char
bcdadd @pointsBCD, @scoreBCD, SCORE_LEN
char = SCORE_X
for i=0 to SCORE_LEN-1
sprite NoFlip, Digit + peek(@scoreBCD + SCORE_LEN-1 - i), char, SCORE_Y
char = char + 6
next i
'bcdcmp requires bcd addrs to point to msd
if bcdcmp(@scoreBCD+(SCORE_LEN-1), @highBCD+(SCORE_LEN-1), SCORE_LEN) &= 1
bcdcpy @scoreBCD, @highBCD, SCORE_LEN
call drawHigh
endif
endproc
proc drawHigh
local i, char
char = HIGH_X
for i=0 to SCORE_LEN-1
sprite NoFlip, Digit + peek(@highBCD + SCORE_LEN-1 - i), char, HIGH_Y
char = char + 6
next i
endproc
proc drawLevel
local i, char
sprite NoFlip, Level, LEVEL_X, LEVEL_Y
char = LEVEL_X + 6
for i=0 to LEVEL_LEN-4
sprite NoFlip, Digit + peek(@levelBCD + LEVEL_LEN-4 - i), char, LEVEL_Y
char = char + 6
next i
endproc
proc drawSaucerScore, xy
local i, char
char = xy
for i=0 to SAUCER_LEN-3
sprite NoFlip, DigitS + peek(@saucerBCD + SAUCER_LEN-3 - i), char.lo, char.hi
char.lo = char.lo + 4
next i
endproc
proc drawLives
local i, plife
i = 1
plife = LIFE_X
while i &<= livesLevel.hi
sprite NoFlip, PLife, plife, LIFE_Y
plife = plife + 8
inc i
wend
while i &<= MAX_LIVES
sprite NoFlip, PBlk, plife, LIFE_Y
plife = plife + 8
inc i
wend
endproc
proc initInvaders
local i, j, index, iprev, inext, invader
index = 0 : iprev = index : inext = iprev
for j=INVADERS_J - 1 downto 0
for i=0 to INVADERS_I - 1
if index &= 0
iprev = @istruct(INVADERS_J * INVADERS_I - 1, 0)
else
iprev = addr(istruct(index - 1, 0))
endif
if index &= (INVADERS_J * INVADERS_I) - 1
inext = @istruct(0, 0)
else
inext = addr(istruct(index + 1, 0))
endif
iaddress(j, i) = addr(istruct(index, 0))
'invader = [end:1 j:3 i:4][frame:8]
'struct{invader, xy, prev, next}
invader = invaders(j, i) AND &h7FFF
invaders(j, i) = invader
invader.lo = invader.lo OR ((j LSL 4) AND &h70) OR (i AND &h0F)
istruct(index, 0) = invader
istruct(index, 1) = ixpos(i) + (iypos(j) LSL 8)
istruct(index, 2) = iprev
istruct(index, 3) = inext
inc index
next i
next j
for i=0 to NUM_IBULLETS - 1
ibxy(i) = 0
next i
icurrent = @istruct(0, 0)
ishoot = icurrent
endproc
proc initVars
'initialises all variables, (to zero), starting at @timeTicks
init vars @timeTicks
endproc
proc startLevel
sound off
bcdint @pointsBCD, 0
bcdint @saucerBCD, 0
bcdint @levelBCD, delayLevel.lo
ii = 0 : jj = INVADERS_J - 1
iflip = 2 : xflip = iflip
if livesLevel.lo &> LEVEL_X6 then livesLevel.lo = 0
ixorigin = IORIGIN_X : iyorigin = IORIGIN_Y + livesLevel.lo
cls
call drawScore
call drawHigh
call drawLives
call drawLevel
call drawBarriers
call initInvaders
if delayLevel.lo &&>= 100
call easterEgg
else
call uneasterEgg
endif
endproc
proc resetLevel
livesLevel = 3*256 + 0
delayLevel = 31*256 + 0
bcdint @scoreBCD, 0
sevol = 60
px = PLAYER_X : py = PLAYER_Y
call uneasterEgg
endproc
proc initSystem
'audio fix for ROMv5a
poke &h21, peek(&h21) OR 3
mode 2
set FGBG_COLOUR, 0
endproc
proc easterEgg
local vtable, vaddr
vaddr = &h7F
'for vtable=&h0100 to &h0178 step 2
for vtable=&h0100 to &h01EE step 2
poke vtable, vaddr
dec vaddr
next vtable
endproc
proc uneasterEgg
local vtable, vaddr
vaddr = &h08
for vtable=&h0100 to &h01EE step 2
poke vtable, vaddr
inc vaddr
next vtable
endproc
proc waitScanline
'wait
repeat
until &(get("VIDEO_Y") AND 1)
endproc