1049 lines
26 KiB
Plaintext
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 |