mirror of
https://github.com/dschmenk/PLASMA.git
synced 2024-06-01 03:41:34 +00:00
639 lines
17 KiB
Plaintext
639 lines
17 KiB
Plaintext
//
|
|
// Ziggy Stardust and the Spiders from Mars
|
|
//
|
|
include "inc/cmdsys.plh"
|
|
include "inc/hgrlib.plh"
|
|
include "inc/hgrsprite.plh"
|
|
include "inc/joybuzz.plh"
|
|
sysflags reshgr1 // Reserve HGR page 1
|
|
|
|
const NUMSTARS = 32
|
|
const SHIP_SPRTNUM = 0
|
|
const MISSLE_SPRTNUM = 1
|
|
const WEB_SPRTBASE = 2
|
|
const SPIDER_SPRTEBASE = 8
|
|
const LASER_RECHARGE = 20
|
|
const MISSLE_RELOAD = 10
|
|
const ATTACK_SPEED = 12
|
|
const SWARM_LIMIT = 52
|
|
const FASTMOVE_LIMIT = 120
|
|
const WEB_LIMIT = 152
|
|
const YSHIP = 180
|
|
const LASER_BUZZ = 20
|
|
const ATTACK_BUZZ = 5
|
|
const WEB1_BUZZ = 10
|
|
const WEB2_BUZZ = 12
|
|
const WEB3_BUZZ = 14
|
|
const SPLAT_BUZZ = 40
|
|
|
|
var sprtEgg1, sprtEgg2, sprtEgg3, sprtEgg
|
|
var sprtSpider1, sprtSpider2, sprtSpider3, sprtSpider
|
|
var sprtSplat1, sprtSplat2
|
|
var sprtWeb1, sprtWeb2, sprtWeb3
|
|
var sprtShip, sprtMissle, sprtDummy, heapstart
|
|
var[NUMSTARS] xstar
|
|
byte[NUMSTARS] ystar
|
|
byte curstar, buzz
|
|
|
|
//
|
|
// Apple II speaker tone generator routine
|
|
//
|
|
//asm spkrTone(pitch, duration)#0
|
|
byte _spkrTone[]
|
|
byte = $86, $E5 // STX ESP
|
|
byte = $B4, $C0 // LDY ESTKH,X
|
|
byte = $B5, $D0 // LDA ESTKL,X
|
|
byte = $F0, $01 // BEQ +1
|
|
byte = $C8 // INY
|
|
byte = $85, $08 // STA DSTL
|
|
byte = $84, $09 // STY DSTH
|
|
byte = $B4, $C1 // LDY ESTKL+1,X
|
|
byte = $B5, $D1 // LDA ESTKL+1,X
|
|
byte = $F0, $01 // BEQ +1
|
|
byte = $C8 // INY
|
|
byte = $85, $E7 // STA TMPL
|
|
byte = $84, $E8 // STY TMPH
|
|
byte = $AA // TAX
|
|
byte = $A9, $FF // LDA #$FF
|
|
byte = $08 // PHP
|
|
byte = $78 // SEI
|
|
byte = $EA // NOP
|
|
byte = $EA // NOP
|
|
byte = $B0, $00 // BCS +0
|
|
byte = $38 // SEC
|
|
byte = $CA // DEX
|
|
byte = $D0, $18 // BNE +18
|
|
byte = $88 // DEY
|
|
byte = $D0, $17 // BNE +17
|
|
byte = $2C, $30, $C0 // BIT $C030
|
|
byte = $A6, $E7 // LDX TMPL
|
|
byte = $A4, $E8 // LDY TMPH
|
|
byte = $E9, $01 // SBC #$01
|
|
byte = $B0, $EA // BCS -EA
|
|
byte = $C6, $08 // DEC DSTL
|
|
byte = $D0, $EA // BNE -EA
|
|
byte = $C6, $09 // DEC DSTH
|
|
byte = $D0, $E6 // BNE -E6
|
|
byte = $F0, $07 // BEQ +8
|
|
byte = $EA // NOP
|
|
byte = $EA // NOP
|
|
byte = $EA, $EA // BIT $C000 (nope)
|
|
byte = $EA // NOP
|
|
byte = $D0, $EB // BNE -EA
|
|
byte = $28 // PLP
|
|
byte = $A6, $E5 // LDX ESP
|
|
byte = $E8 // INX
|
|
byte = $E8 // INX
|
|
byte = $60 // RTS
|
|
|
|
def game(spdrcnt)#1
|
|
var[8] xspdr, yspdr, ixspdr, iyspdr, hatch
|
|
var xship, xmissle, leftxlaser, rightxlaser, attack
|
|
byte[8] life, death
|
|
byte lasercharging, missleloading, misslecnt, ymissle
|
|
byte leftylaser, rightylaser, lefthit, righthit, sprtnum[], width
|
|
byte spdrnum, k, xdelta, ydelta, btn0, btn1, webstate
|
|
|
|
xship = 140
|
|
ymissle = 255 // Not active value
|
|
misslecnt = 6
|
|
missleloading = FALSE
|
|
lasercharging = FALSE
|
|
//
|
|
// Initialize the spiders (first is special)
|
|
//
|
|
xspdr[0] = 100
|
|
ixspdr[0] = 1
|
|
yspdr[0] = 60
|
|
iyspdr[0] = -1
|
|
hatch[0] = 1
|
|
life[0] = 3
|
|
death[0] = 0
|
|
attack = -1
|
|
webstate = 0
|
|
//
|
|
// Initialize spiders
|
|
//
|
|
spdrnum = spdrcnt - 1
|
|
for k = 1 to spdrnum
|
|
spriteAdd(k + SPIDER_SPRTEBASE, spriteDup(sprtEgg3))
|
|
xspdr[k] = ((xspdr[k - 1] ^ 37) + 97) & 255
|
|
ixspdr[k] = -ixspdr[k - 1]
|
|
yspdr[k] = ((yspdr[k - 1] ^ 29) + 53) & 63
|
|
iyspdr[k] = -iyspdr[k - 1]
|
|
hatch[k] = k / 2 + 1//hatch[k - 1] + 1
|
|
life[k] = 3
|
|
death[k] = 0
|
|
next
|
|
//
|
|
// Initialize missle display
|
|
//
|
|
hgrColor(3)
|
|
hgrRect(0, YSHIP+3, 279, 191)
|
|
for k = 0 to misslecnt-1
|
|
spritePos(k * 16 + 160, 184, sprtMissle)
|
|
spriteDrawXor(sprtMissle)
|
|
next
|
|
hgrColor(1)
|
|
hgrRect(4, YSHIP+6, LASER_RECHARGE * 4 + 4, 188)
|
|
//
|
|
// Main game loop
|
|
//
|
|
repeat
|
|
//
|
|
// Move living spiders
|
|
//
|
|
for k = 0 to spdrnum
|
|
if life[k]
|
|
yspdr[k] = yspdr[k] + iyspdr[k]
|
|
if yspdr[k] < -5
|
|
//
|
|
// Above screen
|
|
//
|
|
iyspdr[k] = 1
|
|
if hatch[k]
|
|
hatch[k]--
|
|
if hatch[k] == 0
|
|
//
|
|
// Hatch this egg into a spider
|
|
//
|
|
life[k] = 3 // Reset life
|
|
spriteAdd(k + SPIDER_SPRTEBASE, spriteDup(sprtSpider3))
|
|
fin
|
|
fin
|
|
fin
|
|
if yspdr[k] > SWARM_LIMIT
|
|
//
|
|
// Attack or not?
|
|
//
|
|
if hatch[k] == 0 and attack < 0
|
|
attack = k
|
|
iyspdr[k] = ATTACK_SPEED
|
|
fin
|
|
if attack == k
|
|
//
|
|
// Attacking
|
|
//
|
|
if yspdr[k] > WEB_LIMIT
|
|
//
|
|
// Iterate through web sequence
|
|
//
|
|
when webstate
|
|
is 10
|
|
iyspdr[k] = -8 // Retreat
|
|
yspdr[k] = WEB_LIMIT
|
|
spriteDel(2)
|
|
spriteDel(3)
|
|
spriteDel(4)
|
|
webstate = 0
|
|
break
|
|
is 0
|
|
iyspdr[k] = 0
|
|
spriteAdd(2, sprtWeb1)
|
|
spritePosIndex(xspdr[k], WEB_LIMIT+7, 2)
|
|
buzz = WEB1_BUZZ
|
|
webstate = 1
|
|
break
|
|
is 1
|
|
spriteAdd(3, sprtWeb2)
|
|
spritePosIndex(xspdr[k], WEB_LIMIT+15, 3)
|
|
buzz = WEB2_BUZZ
|
|
webstate = 2
|
|
break
|
|
is 2
|
|
spriteAdd(4, sprtWeb3)
|
|
spritePosIndex(xspdr[k], WEB_LIMIT+22, 4)
|
|
webstate = 3
|
|
otherwise
|
|
//
|
|
// States 3..9 just repeat this
|
|
//
|
|
if xship >= xspdr[k] - 17 and xship <= xspdr[k] + 17
|
|
//
|
|
// Capture ship
|
|
//
|
|
return FALSE
|
|
fin
|
|
buzz = WEB3_BUZZ
|
|
webstate++
|
|
wend
|
|
else
|
|
if iyspdr[k] > 0
|
|
//
|
|
// Move towards player during attack, but not too fast
|
|
//
|
|
xdelta = yspdr[k] > FASTMOVE_LIMIT ?? 2 :: 4
|
|
xspdr[k] = xspdr[k] + (xship - xspdr[k]) / xdelta
|
|
buzz = ATTACK_BUZZ
|
|
else
|
|
//
|
|
// Retreat at a progressively faster rate
|
|
//
|
|
if ixspdr[k] > 0
|
|
xspdr[k] = xspdr[k] + (WEB_LIMIT - yspdr[k]) / 8
|
|
else
|
|
xspdr[k] = xspdr[k] - (WEB_LIMIT - yspdr[k]) / 8
|
|
fin
|
|
fin
|
|
fin
|
|
else
|
|
//
|
|
// Just reverse direction towards top of screen
|
|
//
|
|
iyspdr[k] = -1
|
|
fin
|
|
else
|
|
if attack == k
|
|
//
|
|
// Retreated back to swarm zone so stop attack
|
|
//
|
|
attack = -1
|
|
iyspdr[k] = -1
|
|
fin
|
|
//
|
|
// Swarm movement
|
|
//
|
|
if ixspdr[k] > 0
|
|
xspdr[k] = xspdr[k] + yspdr[k] / 8
|
|
else
|
|
xspdr[k] = xspdr[k] - yspdr[k] / 8
|
|
fin
|
|
if xspdr[k] < -5 or xspdr[k] > 284
|
|
if xspdr[k] < -5
|
|
xspdr[k] = -5
|
|
ixspdr[k] = 1
|
|
else
|
|
xspdr[k] = 284
|
|
ixspdr[k] = -1
|
|
fin
|
|
fin
|
|
fin
|
|
spritePosIndex(xspdr[k], yspdr[k], k+SPIDER_SPRTEBASE)
|
|
elsif death[k]
|
|
//
|
|
// Splat sequence
|
|
//
|
|
death[k]--
|
|
if death[k]
|
|
spriteAdd(k+SPIDER_SPRTEBASE, spriteDup(sprtSplat2))
|
|
spritePosIndex(xspdr[k], yspdr[k], k+SPIDER_SPRTEBASE)
|
|
else
|
|
spriteAdd(k+SPIDER_SPRTEBASE, spriteDup(sprtDummy))
|
|
spritePosIndex(0, 0, k+SPIDER_SPRTEBASE)
|
|
spdrcnt--
|
|
if not spdrcnt
|
|
return TRUE
|
|
fin
|
|
buzz = SPLAT_BUZZ
|
|
fin
|
|
fin
|
|
next
|
|
//
|
|
// Move ship
|
|
//
|
|
xdelta, ydelta, btn0, btn1 = joypos(buzz)
|
|
buzz = spdrcnt * 4
|
|
xship = xship + ((xdelta + 4) / 8 - 5)
|
|
if xship < 0; xship = 0; fin
|
|
if xship > 279; xship = 279; fin
|
|
spritePosIndex(xship, YSHIP, SHIP_SPRTNUM)
|
|
//
|
|
// Missle
|
|
//
|
|
if misslecnt and missleloading
|
|
missleloading--
|
|
fin
|
|
if btn1 and not missleloading and ymissle == 255
|
|
//
|
|
// Fire missle
|
|
//
|
|
xmissle = xship
|
|
ymissle = YSHIP-15
|
|
missleloading = MISSLE_RELOAD
|
|
misslecnt--
|
|
spritePos(misslecnt * 16 + 160, 184, sprtMissle) // Erase from armory
|
|
spriteDrawXor(sprtMissle)
|
|
spriteAdd(1, sprtMissle)
|
|
fin
|
|
if ymissle <> 255
|
|
if ymissle < 8
|
|
//
|
|
// Missle off top of screen
|
|
//
|
|
spriteDel(1)
|
|
ymissle = 255
|
|
else
|
|
ymissle = ymissle - 8
|
|
spritePosIndex(xmissle, ymissle, MISSLE_SPRTNUM)
|
|
for k = 0 to spdrnum
|
|
if life[k]
|
|
//
|
|
// Hit test will all living spiders
|
|
//
|
|
if ymissle >= yspdr[k] - 15 and ymissle <= yspdr[k] + 5
|
|
if hatch[k]
|
|
width = 7 // Egg width
|
|
else
|
|
width = 10 // Spider width
|
|
fin
|
|
if xmissle >= xspdr[k] - width and xmissle <= xspdr[k] + width
|
|
//
|
|
// Hit & kill spider
|
|
//
|
|
life[k] = 0
|
|
death[k] = 2
|
|
spriteAdd(k+SPIDER_SPRTEBASE, spriteDup(sprtSplat1))
|
|
spritePosIndex(xspdr[k], yspdr[k], k+SPIDER_SPRTEBASE)
|
|
if attack == k // Was it attacking?
|
|
attack = -1
|
|
spriteDel(2)
|
|
spriteDel(3)
|
|
spriteDel(4)
|
|
webstate = 0
|
|
fin
|
|
//
|
|
// Remove missle
|
|
//
|
|
spriteDel(MISSLE_SPRTNUM)
|
|
ymissle = 255
|
|
fin
|
|
fin
|
|
fin
|
|
next
|
|
buzz = ymissle/4
|
|
fin
|
|
fin
|
|
//
|
|
// Update sprites
|
|
//
|
|
spriteDrawXorList()
|
|
//
|
|
// Lasers
|
|
//
|
|
if lasercharging
|
|
lasercharging--
|
|
hgrColor(1)
|
|
hgrRect(81 - lasercharging*4, YSHIP+6, 84 - lasercharging*4, 188)
|
|
fin
|
|
if btn0 and not lasercharging
|
|
//
|
|
// Fire laser
|
|
//
|
|
hgrColor(0)
|
|
hgrRect(4, YSHIP+6, LASER_RECHARGE * 4 + 4, 188)
|
|
lasercharging = LASER_RECHARGE
|
|
leftxlaser = xship - 7
|
|
rightxlaser = xship + 7
|
|
leftylaser = 0
|
|
rightylaser = 0
|
|
lefthit = 255
|
|
righthit = 255
|
|
for k = 0 to spdrnum
|
|
if life[k]
|
|
//
|
|
// Hit test will all living spiders
|
|
//
|
|
if hatch[k]
|
|
width = 7 // Egg width
|
|
else
|
|
width = 10 // Spider width
|
|
fin
|
|
if leftxlaser >= xspdr[k]-width and leftxlaser <= xspdr[k]+width
|
|
if yspdr[k] + 5 > leftylaser
|
|
lefthit = k
|
|
leftylaser = yspdr[k] + 5
|
|
fin
|
|
fin
|
|
if rightxlaser >= xspdr[k]-width and rightxlaser <= xspdr[k]+width
|
|
if yspdr[k] + 5 > rightylaser
|
|
righthit = k
|
|
rightylaser = yspdr[k] + 5
|
|
fin
|
|
fin
|
|
fin
|
|
next
|
|
//
|
|
// Draw Laser beams
|
|
//
|
|
hgrColor(3)
|
|
if leftxlaser >= 0
|
|
hgrXorVLin(leftylaser, YSHIP-8, leftxlaser)
|
|
hgrXorVLin(leftylaser, YSHIP-8, leftxlaser+1)
|
|
fin
|
|
if rightxlaser < 280
|
|
hgrXorVLin(rightylaser, YSHIP-8, rightxlaser)
|
|
hgrXorVLin(rightylaser, YSHIP-8, rightxlaser-1)
|
|
fin
|
|
buzz = LASER_BUZZ
|
|
(@_spkrTone)(15, 1)#0
|
|
if lefthit <> 255
|
|
sprtnum = lefthit + SPIDER_SPRTEBASE
|
|
//
|
|
// Hit
|
|
//
|
|
life[lefthit]--
|
|
if not life[lefthit]
|
|
//
|
|
// Kill spider
|
|
//
|
|
death[lefthit] = 2
|
|
spriteAdd(sprtnum, spriteDup(sprtSplat1))
|
|
spritePosIndex(xspdr[lefthit], yspdr[lefthit], sprtnum)
|
|
if attack == lefthit // Was it attacking?
|
|
spriteDel(2)
|
|
spriteDel(3)
|
|
spriteDel(4)
|
|
webstate = 0
|
|
attack = -1
|
|
fin
|
|
else
|
|
//
|
|
// Dim color to show life status
|
|
//
|
|
if hatch[lefthit]
|
|
sprtEgg = life[lefthit] == 2 ?? sprtEgg2 :: sprtEgg1
|
|
spriteAdd(sprtnum, spriteDup(sprtEgg))
|
|
else
|
|
sprtSpider = life[lefthit] == 2 ?? sprtSpider2 :: sprtSpider1
|
|
spriteAdd(sprtnum, spriteDup(sprtSpider))
|
|
fin
|
|
spritePosIndex(xspdr[lefthit], yspdr[lefthit], sprtnum)
|
|
fin
|
|
fin
|
|
if righthit <> 255 and life[righthit]
|
|
sprtnum = righthit + SPIDER_SPRTEBASE
|
|
//
|
|
// Hit
|
|
//
|
|
life[righthit]--
|
|
if not life[righthit]
|
|
//
|
|
// Kill spider
|
|
//
|
|
death[righthit] = 2
|
|
spriteAdd(sprtnum, spriteDup(sprtSplat1))
|
|
spritePosIndex(xspdr[righthit], yspdr[righthit], sprtnum)
|
|
if attack == righthit // Was it attacking?
|
|
spriteDel(2)
|
|
spriteDel(3)
|
|
spriteDel(4)
|
|
webstate = 0
|
|
attack = -1
|
|
fin
|
|
else
|
|
//
|
|
// Dim color to show life status
|
|
//
|
|
if hatch[righthit]
|
|
sprtEgg = life[righthit] == 2 ?? sprtEgg2 :: sprtEgg1
|
|
spriteAdd(sprtnum, spriteDup(sprtEgg))
|
|
else
|
|
sprtSpider = life[righthit] == 2 ?? sprtSpider2 :: sprtSpider1
|
|
spriteAdd(sprtnum, spriteDup(sprtSpider))
|
|
fin
|
|
spritePosIndex(xspdr[righthit], yspdr[righthit], sprtnum)
|
|
fin
|
|
fin
|
|
//
|
|
// Undraw lasers
|
|
//
|
|
hgrColor(3)
|
|
if leftxlaser >= 0
|
|
hgrXorVLin(leftylaser, YSHIP-8, leftxlaser)
|
|
hgrXorVLin(leftylaser, YSHIP-8, leftxlaser+1)
|
|
fin
|
|
if rightxlaser < 280
|
|
hgrXorVLin(rightylaser, YSHIP-8, rightxlaser)
|
|
hgrXorVLin(rightylaser, YSHIP-8, rightxlaser-1)
|
|
fin
|
|
fin
|
|
//
|
|
// Update stars
|
|
//
|
|
hgrColor(3)
|
|
hgrXorPlot(xstar[curstar], ystar[curstar])
|
|
ystar[curstar]++
|
|
if ystar[curstar] > YSHIP
|
|
ystar[curstar] = 0
|
|
fin
|
|
hgrXorPlot(xstar[curstar], ystar[curstar])
|
|
curstar++
|
|
if curstar >= NUMSTARS; curstar = 0; fin
|
|
until ^$C000 == $9B // ESCape key pressed
|
|
getc
|
|
return FALSE
|
|
end
|
|
//
|
|
// Initialization
|
|
//
|
|
puts("Loading");
|
|
putc('.')
|
|
sprtShip = spriteRead("SHIP")
|
|
if not sprtShip
|
|
cmdsys:modexec("SFMSPRT")
|
|
sprtShip = spriteRead("SHIP")
|
|
if not sprtShip
|
|
puts("\nError: Unable to compile sprites.\n")
|
|
return -1
|
|
fin
|
|
fin
|
|
putc('.')
|
|
sprtMissle = spriteRead("MISSLE")
|
|
putc('.')
|
|
sprtEgg3 = spriteRead("EGG3")
|
|
putc('.')
|
|
sprtEgg2 = spriteRead("EGG2")
|
|
putc('.')
|
|
sprtEgg1 = spriteRead("EGG1")
|
|
putc('.')
|
|
sprtSpider3= spriteRead("SPIDER3")
|
|
putc('.')
|
|
sprtSpider2= spriteRead("SPIDER2")
|
|
putc('.')
|
|
sprtSpider1= spriteRead("SPIDER1")
|
|
putc('.')
|
|
sprtWeb1 = spriteRead("WEB1")
|
|
putc('.')
|
|
sprtWeb2 = spriteRead("WEB2")
|
|
putc('.')
|
|
sprtWeb3 = spriteRead("WEB3")
|
|
putc('.')
|
|
sprtSplat1 = spriteRead("SPLAT1")
|
|
putc('.')
|
|
sprtSplat2 = spriteRead("SPLAT2")
|
|
putc('.')
|
|
sprtDummy = spriteRead("DUMMY")
|
|
//
|
|
// Heat up JIT compiler
|
|
//
|
|
for curstar = 0 to 50
|
|
putc('.')
|
|
spriteAdd(0, sprtDummy)
|
|
spritePosIndex(0, 0, 0)
|
|
spriteDrawXorList()
|
|
next
|
|
heapstart = heapmark()
|
|
//puts("\nBeginning available memory: "); puti(heapavail()); putln
|
|
repeat
|
|
hgrMode(hgrPage1)
|
|
spriteDrawBuf(hgrDrawBuf(hgrPage1)) // Single buffered
|
|
spriteAdd(SHIP_SPRTNUM, sprtShip)
|
|
spriteAdd(SPIDER_SPRTEBASE, sprtEgg3)
|
|
//
|
|
// Init stars
|
|
//
|
|
xstar[0] = 99
|
|
ystar[0] = 10
|
|
hgrColor(3)
|
|
hgrXorPlot(xstar[0], ystar[0])
|
|
for curstar = 1 to NUMSTARS-1
|
|
xstar[curstar] = (xstar[curstar - 1] + 97 - curstar) % 280
|
|
ystar[curstar] = (ystar[curstar - 1] + 19 + curstar) % YSHIP
|
|
hgrXorPlot(xstar[curstar], ystar[curstar])
|
|
next
|
|
curstar = 0
|
|
if game(8)
|
|
//
|
|
// Won
|
|
//
|
|
spriteDrawXorList()
|
|
(@_spkrTone)(100, 20)#0
|
|
(@_spkrTone)(75, 20)#0
|
|
(@_spkrTone)(15, 20)#0
|
|
(@_spkrTone)(25, 100)#0
|
|
puts("\nZiggy Stardust has saved Earth!\n")
|
|
else
|
|
//
|
|
// Lost
|
|
//
|
|
spriteDrawXorList()
|
|
(@_spkrTone)(100, 50)#0
|
|
spriteDrawXor(sprtShip)
|
|
(@_spkrTone)(150, 50)#0
|
|
spriteDrawXor(sprtShip)
|
|
(@_spkrTone)(100, 50)#0
|
|
spriteDrawXor(sprtShip)
|
|
(@_spkrTone)(150, 50)#0
|
|
puts("\nEarth has been overrun")
|
|
puts("\nby the Spiders from Mars\n")
|
|
fin
|
|
hgrMode(hgrOFF)
|
|
//hgrDrawBuf(hgrPage1)
|
|
//
|
|
// Clear sprite lists
|
|
//
|
|
for curstar = 0 to 15
|
|
spriteDel(curstar)
|
|
next
|
|
spriteDrawXorList()
|
|
spriteDrawXorList()
|
|
//
|
|
// Reset heap
|
|
//
|
|
heaprelease(heapstart)
|
|
//puts("\nAvailable memory: "); puti(heapavail()); putln
|
|
puts("Play again (Y/N)?")
|
|
until toupper(getc) <> 'Y'
|
|
putln
|
|
done
|