From f1bff7842827ae50464810c9778b71fcb67a6897 Mon Sep 17 00:00:00 2001 From: Paul Robson Date: Tue, 30 Jan 2024 21:22:34 +0000 Subject: [PATCH] Invaders build --- basic/code/games/invaders.bas | Bin 0 -> 4807 bytes basic/code/games/invaders.bsc | 251 +++++++++++++++++++++++++ basic/code/games/invaders.gfx | Bin 0 -> 2176 bytes basic/storage/invaders.bas | Bin 0 -> 4807 bytes basic/storage/invaders.bsc | 251 +++++++++++++++++++++++++ basic/storage/invaders.gfx | Bin 1920 -> 2176 bytes emulator/storage/invaders.bas | Bin 0 -> 4807 bytes emulator/storage/invaders.bsc | 251 +++++++++++++++++++++++++ emulator/storage/invaders.gfx | Bin 0 -> 2176 bytes firmware/common/include/data/prompt.h | 2 +- firmware/common/include/data/sfxdata.h | 6 +- 11 files changed, 757 insertions(+), 4 deletions(-) create mode 100644 basic/code/games/invaders.bas create mode 100644 basic/code/games/invaders.bsc create mode 100644 basic/code/games/invaders.gfx create mode 100644 basic/storage/invaders.bas create mode 100644 basic/storage/invaders.bsc create mode 100644 emulator/storage/invaders.bas create mode 100644 emulator/storage/invaders.bsc create mode 100644 emulator/storage/invaders.gfx diff --git a/basic/code/games/invaders.bas b/basic/code/games/invaders.bas new file mode 100644 index 0000000000000000000000000000000000000000..b91d439de5dd2a9f0f83138ef0ce004fa7111fe2 GIT binary patch literal 4807 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 literal 0 HcmV?d00001 diff --git a/basic/code/games/invaders.bsc b/basic/code/games/invaders.bsc new file mode 100644 index 00000000..0fb6918c --- /dev/null +++ b/basic/code/games/invaders.bsc @@ -0,0 +1,251 @@ +' +' *** Space Invaders *** +' +cls:gload "invaders.gfx" +call Initialise() + +call ResetMap():call RedrawGame():call ResetBullets():call RepaintInvaders() +repeat + call PlayLevel() +until lives = 0 +end +' +' Play/Continue the current level +' +proc PlayLevel() + dead = false + tMoveInvaders = 0:tMovePlayer = 0:tMoveBullets = 0:tCheckFire = 0 + tFireCheckTime = 100 + repeat + if event(tMoveInvaders,moveRate) then call MoveInvaders():call RepaintInvaders():sprite 10 hide + if event(tMovePlayer,3) then call MovePlayer() + if event(tMoveBullets,3) + if yPBullet > -20 then call MovePlayerBullet() + call MoveInvaderBullets() + endif + if event(tCheckFire,tFireCheckTime) then tFireCheckTime = rand(70)+20:call CheckInvaderFire() + until dead|remaining = 0 + if remaining = 0 then call ResetMap() + if dead then lives = lives - 1 + + if lives > 0 + call Delay(200) + call RedrawGame():call ResetBullets():call RepaintInvaders() + endif + + endproc +' +' The invaders are actually a tilemap, so create it. +' +proc Initialise() + invBulletCount = 2 + invMap = alloc(13 * 5 + 3) + poke invMap,1:poke invMap+1,13:poke invMap+2,6 + tilemap invMap,8,0 + dim colAlive(11),xBullet(invBulletCount),yBullet(invBulletCount) + score = 0:highScore = 1000:lives = 3:level = 0 + xPlayer = 160 +endproc +' +' Redraw the score +' +proc DrawScore() + text right$("000000"+str$(score),6) ink 7 dim 1 solid to 62,10 + if score = 0 | score = highScore then text right$("000000"+str$(highScore),6) ink 7 dim 1 to 142,10 +endproc +' +' Initialise the invaders map and counts +' +proc ResetMap() + local a,c,i,x,y + for y = 0 to 4 + a = y * 13 + invMap + 3:c = (y+1) \ 2 + poke a,$F8:poke a+12,$F8 + for i = 1 to 11:poke a+i,c:next + next + xLeft = 160 - 13 * 8:yTop = 28+level*6:xDirection = 4 + for i = 1 to 11:colAlive(i) = $1F:next + remaining = 5*11:moveRate = 3+2*remaining + level = (level+1) % 10 + call ResetColumns() + call AnimationFlipCode() +endproc +' +' Redraw the main game screen +' +proc RedrawGame() + local i + cls:sprite clear:text "000000" solid ink 7 dim 1 to 222,10 + text "SCORE<1>" to 56,0:text "SCORE<2>" to 216,0:text "HI-SCORE" to 136,0 + call DrawScore():call DrawLives() + for i = 1 to 3:call DrawShield(i*80,180):next + sprite 0 image $85 to xPlayer,230 +endproc +' +' Move Player +' +proc MovePlayer() + local fire,dx,dy + fire = joypad(dx,dy) + xPlayer = max(0,min(319,xPlayer+dx*6)) + sprite 0 to xPlayer,230 + if yPBullet < 0 then if fire then xPBullet = xPlayer:yPBullet = 230-8:sfx 0,22 +endproc +' +' Draw lives +' +proc DrawLives() + local i + text str$(lives) solid dim 1 ink 2 to 8,230 + if lives > 1 then for i = 1 to lives-1:image $85 to i*16+4,228:next +endproc +' +' Draw the shield +' +proc DrawShield(x,y) + local i,w,h:w = 50:h = 30 + rect x-w\2,y ink 2 solid to x+w\2,y+h + ellipse ink 0 from x-h\3,y+h-h\3 to x+h\3,y+h+h\3 + for i = 0 to w\4 + line x-w\2+i,y-1 to x-w\2,y+i + line x+w\2-i,y-1 to x+w\2,y+i + next +endproc +' +' Move all the invaders +' +proc MoveInvaders() + sys Animate6502 + xLeft = xLeft + xDirection + if xLeft + rightColumn * 16 > 303 | xLeft < 16-16 * leftColumn + xDirection = -xDirection + xLeft = max(min(xLeft,303-rightColumn*16),0) + rect ink 0 solid from 0,yTop to 319,yTop+8 + yTop = yTop + 8:if yTop > 170 then dead = true + if sfxPitch = 100:sfxPitch = 200:else:sfxPitch = 100:endif + sound 0 clear:sound 0,sfxPitch,8 + endif +endproc +' +' Repaint the tile map +' +proc RepaintInvaders() + tiledraw from xLeft,yTop to xLeft+191,yTop+80 +endproc +' +' Reset Bullets +' +proc ResetBullets() + sprite 9 hide:xPBullet = 0:yPBullet = -99 + local i + for i = 1 to invBulletCount:yBullet(i) = -1:sprite i hide:next +endproc +' +' Move Player Bullet +' +proc MovePlayerBullet() + yPBullet = yPBullet - 4 + if yPBullet > 180 then if point(xPBullet,yPBullet) = 2 then call BreakShield(xPBullet,yPBullet+3):yPBullet = -99 + if yPBullet > xTop & yPBullet < yTop+80 + local row,col,pos + row = (yPBullet - yTop) >> 4 + col = (xPBullet - xLeft + 4) >> 4 + pos = (xPBullet - xLeft + 4) & 15 + if col >= 1 & col <= 11 & pos <= 12 + mask = 1 << (row) + if (colAlive(col) & mask) <> 0 + colAlive(col) = colAlive(col)-mask + if colAlive(col) = 0 then call ResetColumns() + sprite 10 image $86 to xLeft+col*16,yTop+row*16+8 + call DeleteAlien(col,row) + score = score + (6-row)\2*10:call DrawScore() + yPBullet = -99 + endif + endif + endif + sprite 9 image $80+((yPBullet >> 2) & 3) to xPBullet,yPBullet +endproc +' +' Hit shields +' +proc BreakShield(x,y) + ellipse from x-5,y-5 ink 0 solid to x+5,y+5 +endproc +' +' Delete Alien +' +proc DeleteAlien(x,y) + poke invMap+3+x+y*13,$F8 + remaining = remaining - 1 + call RepaintInvaders() + sfx 0,21 + endproc +' +' Delay n cs +' +proc Delay(n) + n = time()+n + repeat:until time() > n +endproc +' +' Check if invaders should fire. +' +proc CheckInvaderFire() + currentCheck = currentCheck + 1:if currentCheck > invBulletCount then currentCheck = 1 + if remaining > 0 & yBullet(currentCheck) < 0 + local col,row,n + repeat:col = rand(11)+1:until colAlive(col) <> 0 + n = colAlive(col) >> 1:row = 0 + while n <> 0:row = row + 1:n = n >> 1:wend + xBullet(currentCheck) = xLeft + col * 16 + yBullet(currentCheck) = yTop + row * 16 + sfx 0,23 + endif +endproc +' +' Move invader bullets +' +proc MoveInvaderBullets() + local i + for i = 1 to invBulletCount + if yBullet(i) >= 0 + yBullet(i) = yBullet(i)+4 + if point(xBullet(i),yBullet(i)) = 2 then call BreakShield(xBullet(i),yBullet(i)):yBullet(i) = -99 + if yBullet(i) > 235 then yBullet(i) = -99 + if yBullet(i) > 224 then if abs(xBullet(i)-xPlayer) < 6 then dead = true:sfx 0,19 + sprite i image ($80+(yBullet(i) >> 2) & 3) to xBullet(i),yBullet(i) + endif + next +endproc +' +' Reset left/right column +' +proc ResetColumns() + if remaining > 0 + leftColumn = 1:rightColumn = 11 + while colAlive(leftColumn) = 0:leftColumn = leftColumn+1:wend + while colAlive(rightColumn) = 0:rightColumn = rightColumn-1:wend + endif +endproc +' +' Animation flip +' +proc AnimationFlipCode() +Animate6502 = alloc(64) + for pass = 0 to 1 + p = Animate6502:o = pass * 3 + ldx #3 + .animloop + lda invMap,x + bmi animxskip + cmp #$F0 + bcs animxskip + eor #4 + sta invMap,x + .animxskip + inx + cpx #3+13*5 + bne animloop + rts + next +endproc \ No newline at end of file diff --git a/basic/code/games/invaders.gfx b/basic/code/games/invaders.gfx new file mode 100644 index 0000000000000000000000000000000000000000..6710553fa2709d98941d37cd309f77d3c5bf9d8c GIT binary patch literal 2176 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 literal 0 HcmV?d00001 diff --git a/basic/storage/invaders.bsc b/basic/storage/invaders.bsc new file mode 100644 index 00000000..0fb6918c --- /dev/null +++ b/basic/storage/invaders.bsc @@ -0,0 +1,251 @@ +' +' *** Space Invaders *** +' +cls:gload "invaders.gfx" +call Initialise() + +call ResetMap():call RedrawGame():call ResetBullets():call RepaintInvaders() +repeat + call PlayLevel() +until lives = 0 +end +' +' Play/Continue the current level +' +proc PlayLevel() + dead = false + tMoveInvaders = 0:tMovePlayer = 0:tMoveBullets = 0:tCheckFire = 0 + tFireCheckTime = 100 + repeat + if event(tMoveInvaders,moveRate) then call MoveInvaders():call RepaintInvaders():sprite 10 hide + if event(tMovePlayer,3) then call MovePlayer() + if event(tMoveBullets,3) + if yPBullet > -20 then call MovePlayerBullet() + call MoveInvaderBullets() + endif + if event(tCheckFire,tFireCheckTime) then tFireCheckTime = rand(70)+20:call CheckInvaderFire() + until dead|remaining = 0 + if remaining = 0 then call ResetMap() + if dead then lives = lives - 1 + + if lives > 0 + call Delay(200) + call RedrawGame():call ResetBullets():call RepaintInvaders() + endif + + endproc +' +' The invaders are actually a tilemap, so create it. +' +proc Initialise() + invBulletCount = 2 + invMap = alloc(13 * 5 + 3) + poke invMap,1:poke invMap+1,13:poke invMap+2,6 + tilemap invMap,8,0 + dim colAlive(11),xBullet(invBulletCount),yBullet(invBulletCount) + score = 0:highScore = 1000:lives = 3:level = 0 + xPlayer = 160 +endproc +' +' Redraw the score +' +proc DrawScore() + text right$("000000"+str$(score),6) ink 7 dim 1 solid to 62,10 + if score = 0 | score = highScore then text right$("000000"+str$(highScore),6) ink 7 dim 1 to 142,10 +endproc +' +' Initialise the invaders map and counts +' +proc ResetMap() + local a,c,i,x,y + for y = 0 to 4 + a = y * 13 + invMap + 3:c = (y+1) \ 2 + poke a,$F8:poke a+12,$F8 + for i = 1 to 11:poke a+i,c:next + next + xLeft = 160 - 13 * 8:yTop = 28+level*6:xDirection = 4 + for i = 1 to 11:colAlive(i) = $1F:next + remaining = 5*11:moveRate = 3+2*remaining + level = (level+1) % 10 + call ResetColumns() + call AnimationFlipCode() +endproc +' +' Redraw the main game screen +' +proc RedrawGame() + local i + cls:sprite clear:text "000000" solid ink 7 dim 1 to 222,10 + text "SCORE<1>" to 56,0:text "SCORE<2>" to 216,0:text "HI-SCORE" to 136,0 + call DrawScore():call DrawLives() + for i = 1 to 3:call DrawShield(i*80,180):next + sprite 0 image $85 to xPlayer,230 +endproc +' +' Move Player +' +proc MovePlayer() + local fire,dx,dy + fire = joypad(dx,dy) + xPlayer = max(0,min(319,xPlayer+dx*6)) + sprite 0 to xPlayer,230 + if yPBullet < 0 then if fire then xPBullet = xPlayer:yPBullet = 230-8:sfx 0,22 +endproc +' +' Draw lives +' +proc DrawLives() + local i + text str$(lives) solid dim 1 ink 2 to 8,230 + if lives > 1 then for i = 1 to lives-1:image $85 to i*16+4,228:next +endproc +' +' Draw the shield +' +proc DrawShield(x,y) + local i,w,h:w = 50:h = 30 + rect x-w\2,y ink 2 solid to x+w\2,y+h + ellipse ink 0 from x-h\3,y+h-h\3 to x+h\3,y+h+h\3 + for i = 0 to w\4 + line x-w\2+i,y-1 to x-w\2,y+i + line x+w\2-i,y-1 to x+w\2,y+i + next +endproc +' +' Move all the invaders +' +proc MoveInvaders() + sys Animate6502 + xLeft = xLeft + xDirection + if xLeft + rightColumn * 16 > 303 | xLeft < 16-16 * leftColumn + xDirection = -xDirection + xLeft = max(min(xLeft,303-rightColumn*16),0) + rect ink 0 solid from 0,yTop to 319,yTop+8 + yTop = yTop + 8:if yTop > 170 then dead = true + if sfxPitch = 100:sfxPitch = 200:else:sfxPitch = 100:endif + sound 0 clear:sound 0,sfxPitch,8 + endif +endproc +' +' Repaint the tile map +' +proc RepaintInvaders() + tiledraw from xLeft,yTop to xLeft+191,yTop+80 +endproc +' +' Reset Bullets +' +proc ResetBullets() + sprite 9 hide:xPBullet = 0:yPBullet = -99 + local i + for i = 1 to invBulletCount:yBullet(i) = -1:sprite i hide:next +endproc +' +' Move Player Bullet +' +proc MovePlayerBullet() + yPBullet = yPBullet - 4 + if yPBullet > 180 then if point(xPBullet,yPBullet) = 2 then call BreakShield(xPBullet,yPBullet+3):yPBullet = -99 + if yPBullet > xTop & yPBullet < yTop+80 + local row,col,pos + row = (yPBullet - yTop) >> 4 + col = (xPBullet - xLeft + 4) >> 4 + pos = (xPBullet - xLeft + 4) & 15 + if col >= 1 & col <= 11 & pos <= 12 + mask = 1 << (row) + if (colAlive(col) & mask) <> 0 + colAlive(col) = colAlive(col)-mask + if colAlive(col) = 0 then call ResetColumns() + sprite 10 image $86 to xLeft+col*16,yTop+row*16+8 + call DeleteAlien(col,row) + score = score + (6-row)\2*10:call DrawScore() + yPBullet = -99 + endif + endif + endif + sprite 9 image $80+((yPBullet >> 2) & 3) to xPBullet,yPBullet +endproc +' +' Hit shields +' +proc BreakShield(x,y) + ellipse from x-5,y-5 ink 0 solid to x+5,y+5 +endproc +' +' Delete Alien +' +proc DeleteAlien(x,y) + poke invMap+3+x+y*13,$F8 + remaining = remaining - 1 + call RepaintInvaders() + sfx 0,21 + endproc +' +' Delay n cs +' +proc Delay(n) + n = time()+n + repeat:until time() > n +endproc +' +' Check if invaders should fire. +' +proc CheckInvaderFire() + currentCheck = currentCheck + 1:if currentCheck > invBulletCount then currentCheck = 1 + if remaining > 0 & yBullet(currentCheck) < 0 + local col,row,n + repeat:col = rand(11)+1:until colAlive(col) <> 0 + n = colAlive(col) >> 1:row = 0 + while n <> 0:row = row + 1:n = n >> 1:wend + xBullet(currentCheck) = xLeft + col * 16 + yBullet(currentCheck) = yTop + row * 16 + sfx 0,23 + endif +endproc +' +' Move invader bullets +' +proc MoveInvaderBullets() + local i + for i = 1 to invBulletCount + if yBullet(i) >= 0 + yBullet(i) = yBullet(i)+4 + if point(xBullet(i),yBullet(i)) = 2 then call BreakShield(xBullet(i),yBullet(i)):yBullet(i) = -99 + if yBullet(i) > 235 then yBullet(i) = -99 + if yBullet(i) > 224 then if abs(xBullet(i)-xPlayer) < 6 then dead = true:sfx 0,19 + sprite i image ($80+(yBullet(i) >> 2) & 3) to xBullet(i),yBullet(i) + endif + next +endproc +' +' Reset left/right column +' +proc ResetColumns() + if remaining > 0 + leftColumn = 1:rightColumn = 11 + while colAlive(leftColumn) = 0:leftColumn = leftColumn+1:wend + while colAlive(rightColumn) = 0:rightColumn = rightColumn-1:wend + endif +endproc +' +' Animation flip +' +proc AnimationFlipCode() +Animate6502 = alloc(64) + for pass = 0 to 1 + p = Animate6502:o = pass * 3 + ldx #3 + .animloop + lda invMap,x + bmi animxskip + cmp #$F0 + bcs animxskip + eor #4 + sta invMap,x + .animxskip + inx + cpx #3+13*5 + bne animloop + rts + next +endproc \ No newline at end of file diff --git a/basic/storage/invaders.gfx b/basic/storage/invaders.gfx index 44362c53750f69adbdfd69e5b82351bce169e923..6710553fa2709d98941d37cd309f77d3c5bf9d8c 100644 GIT binary patch literal 2176 zcmd^9L2kq#41~R;$MAzyUYHyFz&yJ%V_a3cYJ-yWR42sPjK>(GgEKqm%6fegMPf7}0we_#9{_kj&N z4M*5->l55FkT>3=yb&-%K7on)4(ol{*97$(-0?7vYf`}Ycj8Ht`6 z9!^f%U?L2M2M_3w7CL9}nf|C}Mw(|`e^6L(&xasa{>TLKNPK|IN4n_)O`+dMeO^RC zGWFr;`h!(tOHe+yQ)pvlgSrMnx;N*nz7^o>0Pdl9gg1x06U&?WLsuiv@}2%KOW#_? zTK2pa!c0`QQ@*(Uc{BPGp7BNbKM`L046~bNa=1c<3x+K8a3+j!Cfs48F3PGvY{WZT NWp_XLV9U5|c>(KOohbkS diff --git a/emulator/storage/invaders.bas b/emulator/storage/invaders.bas new file mode 100644 index 0000000000000000000000000000000000000000..b91d439de5dd2a9f0f83138ef0ce004fa7111fe2 GIT binary patch literal 4807 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 literal 0 HcmV?d00001 diff --git a/emulator/storage/invaders.bsc b/emulator/storage/invaders.bsc new file mode 100644 index 00000000..0fb6918c --- /dev/null +++ b/emulator/storage/invaders.bsc @@ -0,0 +1,251 @@ +' +' *** Space Invaders *** +' +cls:gload "invaders.gfx" +call Initialise() + +call ResetMap():call RedrawGame():call ResetBullets():call RepaintInvaders() +repeat + call PlayLevel() +until lives = 0 +end +' +' Play/Continue the current level +' +proc PlayLevel() + dead = false + tMoveInvaders = 0:tMovePlayer = 0:tMoveBullets = 0:tCheckFire = 0 + tFireCheckTime = 100 + repeat + if event(tMoveInvaders,moveRate) then call MoveInvaders():call RepaintInvaders():sprite 10 hide + if event(tMovePlayer,3) then call MovePlayer() + if event(tMoveBullets,3) + if yPBullet > -20 then call MovePlayerBullet() + call MoveInvaderBullets() + endif + if event(tCheckFire,tFireCheckTime) then tFireCheckTime = rand(70)+20:call CheckInvaderFire() + until dead|remaining = 0 + if remaining = 0 then call ResetMap() + if dead then lives = lives - 1 + + if lives > 0 + call Delay(200) + call RedrawGame():call ResetBullets():call RepaintInvaders() + endif + + endproc +' +' The invaders are actually a tilemap, so create it. +' +proc Initialise() + invBulletCount = 2 + invMap = alloc(13 * 5 + 3) + poke invMap,1:poke invMap+1,13:poke invMap+2,6 + tilemap invMap,8,0 + dim colAlive(11),xBullet(invBulletCount),yBullet(invBulletCount) + score = 0:highScore = 1000:lives = 3:level = 0 + xPlayer = 160 +endproc +' +' Redraw the score +' +proc DrawScore() + text right$("000000"+str$(score),6) ink 7 dim 1 solid to 62,10 + if score = 0 | score = highScore then text right$("000000"+str$(highScore),6) ink 7 dim 1 to 142,10 +endproc +' +' Initialise the invaders map and counts +' +proc ResetMap() + local a,c,i,x,y + for y = 0 to 4 + a = y * 13 + invMap + 3:c = (y+1) \ 2 + poke a,$F8:poke a+12,$F8 + for i = 1 to 11:poke a+i,c:next + next + xLeft = 160 - 13 * 8:yTop = 28+level*6:xDirection = 4 + for i = 1 to 11:colAlive(i) = $1F:next + remaining = 5*11:moveRate = 3+2*remaining + level = (level+1) % 10 + call ResetColumns() + call AnimationFlipCode() +endproc +' +' Redraw the main game screen +' +proc RedrawGame() + local i + cls:sprite clear:text "000000" solid ink 7 dim 1 to 222,10 + text "SCORE<1>" to 56,0:text "SCORE<2>" to 216,0:text "HI-SCORE" to 136,0 + call DrawScore():call DrawLives() + for i = 1 to 3:call DrawShield(i*80,180):next + sprite 0 image $85 to xPlayer,230 +endproc +' +' Move Player +' +proc MovePlayer() + local fire,dx,dy + fire = joypad(dx,dy) + xPlayer = max(0,min(319,xPlayer+dx*6)) + sprite 0 to xPlayer,230 + if yPBullet < 0 then if fire then xPBullet = xPlayer:yPBullet = 230-8:sfx 0,22 +endproc +' +' Draw lives +' +proc DrawLives() + local i + text str$(lives) solid dim 1 ink 2 to 8,230 + if lives > 1 then for i = 1 to lives-1:image $85 to i*16+4,228:next +endproc +' +' Draw the shield +' +proc DrawShield(x,y) + local i,w,h:w = 50:h = 30 + rect x-w\2,y ink 2 solid to x+w\2,y+h + ellipse ink 0 from x-h\3,y+h-h\3 to x+h\3,y+h+h\3 + for i = 0 to w\4 + line x-w\2+i,y-1 to x-w\2,y+i + line x+w\2-i,y-1 to x+w\2,y+i + next +endproc +' +' Move all the invaders +' +proc MoveInvaders() + sys Animate6502 + xLeft = xLeft + xDirection + if xLeft + rightColumn * 16 > 303 | xLeft < 16-16 * leftColumn + xDirection = -xDirection + xLeft = max(min(xLeft,303-rightColumn*16),0) + rect ink 0 solid from 0,yTop to 319,yTop+8 + yTop = yTop + 8:if yTop > 170 then dead = true + if sfxPitch = 100:sfxPitch = 200:else:sfxPitch = 100:endif + sound 0 clear:sound 0,sfxPitch,8 + endif +endproc +' +' Repaint the tile map +' +proc RepaintInvaders() + tiledraw from xLeft,yTop to xLeft+191,yTop+80 +endproc +' +' Reset Bullets +' +proc ResetBullets() + sprite 9 hide:xPBullet = 0:yPBullet = -99 + local i + for i = 1 to invBulletCount:yBullet(i) = -1:sprite i hide:next +endproc +' +' Move Player Bullet +' +proc MovePlayerBullet() + yPBullet = yPBullet - 4 + if yPBullet > 180 then if point(xPBullet,yPBullet) = 2 then call BreakShield(xPBullet,yPBullet+3):yPBullet = -99 + if yPBullet > xTop & yPBullet < yTop+80 + local row,col,pos + row = (yPBullet - yTop) >> 4 + col = (xPBullet - xLeft + 4) >> 4 + pos = (xPBullet - xLeft + 4) & 15 + if col >= 1 & col <= 11 & pos <= 12 + mask = 1 << (row) + if (colAlive(col) & mask) <> 0 + colAlive(col) = colAlive(col)-mask + if colAlive(col) = 0 then call ResetColumns() + sprite 10 image $86 to xLeft+col*16,yTop+row*16+8 + call DeleteAlien(col,row) + score = score + (6-row)\2*10:call DrawScore() + yPBullet = -99 + endif + endif + endif + sprite 9 image $80+((yPBullet >> 2) & 3) to xPBullet,yPBullet +endproc +' +' Hit shields +' +proc BreakShield(x,y) + ellipse from x-5,y-5 ink 0 solid to x+5,y+5 +endproc +' +' Delete Alien +' +proc DeleteAlien(x,y) + poke invMap+3+x+y*13,$F8 + remaining = remaining - 1 + call RepaintInvaders() + sfx 0,21 + endproc +' +' Delay n cs +' +proc Delay(n) + n = time()+n + repeat:until time() > n +endproc +' +' Check if invaders should fire. +' +proc CheckInvaderFire() + currentCheck = currentCheck + 1:if currentCheck > invBulletCount then currentCheck = 1 + if remaining > 0 & yBullet(currentCheck) < 0 + local col,row,n + repeat:col = rand(11)+1:until colAlive(col) <> 0 + n = colAlive(col) >> 1:row = 0 + while n <> 0:row = row + 1:n = n >> 1:wend + xBullet(currentCheck) = xLeft + col * 16 + yBullet(currentCheck) = yTop + row * 16 + sfx 0,23 + endif +endproc +' +' Move invader bullets +' +proc MoveInvaderBullets() + local i + for i = 1 to invBulletCount + if yBullet(i) >= 0 + yBullet(i) = yBullet(i)+4 + if point(xBullet(i),yBullet(i)) = 2 then call BreakShield(xBullet(i),yBullet(i)):yBullet(i) = -99 + if yBullet(i) > 235 then yBullet(i) = -99 + if yBullet(i) > 224 then if abs(xBullet(i)-xPlayer) < 6 then dead = true:sfx 0,19 + sprite i image ($80+(yBullet(i) >> 2) & 3) to xBullet(i),yBullet(i) + endif + next +endproc +' +' Reset left/right column +' +proc ResetColumns() + if remaining > 0 + leftColumn = 1:rightColumn = 11 + while colAlive(leftColumn) = 0:leftColumn = leftColumn+1:wend + while colAlive(rightColumn) = 0:rightColumn = rightColumn-1:wend + endif +endproc +' +' Animation flip +' +proc AnimationFlipCode() +Animate6502 = alloc(64) + for pass = 0 to 1 + p = Animate6502:o = pass * 3 + ldx #3 + .animloop + lda invMap,x + bmi animxskip + cmp #$F0 + bcs animxskip + eor #4 + sta invMap,x + .animxskip + inx + cpx #3+13*5 + bne animloop + rts + next +endproc \ No newline at end of file diff --git a/emulator/storage/invaders.gfx b/emulator/storage/invaders.gfx new file mode 100644 index 0000000000000000000000000000000000000000..6710553fa2709d98941d37cd309f77d3c5bf9d8c GIT binary patch literal 2176 zcmd^9L2kq#41~R;$MAzyUYHyFz&yJ%V_a3cYJ-yWR42sPjK>(GgEKqm%6fegMPf7}0we_#9{_kj&N z4M*5->l55FkT>3=yb&-%K7on)4(ol{*97$(