rem Dead Worlds v1.0 rem Owen Cooper March 2022 - o_cooper@yahoo.com rem Use the DEad World Investigator probe-bot (DEWI) to explore the empty caverns of six dead worlds in the rem desolate ZX82 galaxy. Harvest the three Tiptonite crystals that once powered dead civilizations and escape the caverns. rem Find the three crystals in each world, return to the start room and hit the exit warp to finish game. Do it rem as fast as you can, the only score is your time, try and set personal best i.e. time attack game like Barnstorming. rem Old School game mapping on graph paper will help you. rem Walls kill you. rem Worlds are 8x6 screens with wraparound on all four edges - so 288 screens in 4k with space left over. rem Control has some cheap momentum and this gets more pronounced on later worlds. rem There are six worlds, selectable by moving joystick U/D on the start screen (internally these are numbered 0-5) rem Press Fire to start, and to restart after a Game Over. rem World 1 - ABYSSIUM rem World 2 - BRIMSTONE rem World 3 - LILACIUM rem World 4 - DUNEBALL rem World 5 - RAINGOD rem World 6 - CHROMIUM rem r,c - room row, room col for player rem w - world no 0-5 rem q - player1 colour (exit and crystals) rem l,i,u,d - player momentum count in 4 cardinal directions, using i for right as r already in use rem a,b,d - room numbers for crystals. Set to 255 when collected rem e - momentum increaser for later levels rem p - start room number so also exit room number rem t - used for score timer rem z - current room number of player rem y - used for current room type number onscreen rem g - sfx countdown timer rem h - cheap debounce timer rem x - counter used for colour strobing rem j - another counter for colour strobing rem draw a title screen playfield: XX..XXX..XX..XX................. X.X.....X..X.X.X................ X.X.XXX.XXXX.X.X................ X.X.....X..X.X.X................ XX..XXX.X..X.XX................. ................................ XX..X...XX..XXX.XXX.XXX.XXX..... X.X.X..X..X.X.X......X..X....... X.X.X..XXXX.X.X.XXX..X..XXX..... XX..X..X..X.X.X......X....X..... X...XX.X..X.X.X.XXX..X..XXX..... end x = 0:j=0 _firstStart drawscreen COLUBK = 0:COLUPF=x:COLUP0=0 j=j+1:if j=5 then j=0:x=x+1:rem slow the colour fade to avoid it being unpleasantly flickery if x = 255 then x=0 : rem needed? Does bAtari just roll overflowing integers back to 0? rem x=x+1 if !joy0fire then goto _firstStart _startScreen AUDV0=0 COLUBK = 0:COLUPF=0 const scorefade=1:scorecolor=$9C:score=0: const font = retroputer gosub _spriteLeft player0x=78:player0y =50:w=0 foo99 drawscreen if joy0fire then goto foo99 : rem debouncing gosub _digitOne: player1x=78:player1y=66:w=0 foo100 h=h+1 if h=255 then h=1 drawscreen COLUP0 = 156: COLUP1 = 30 rem allow player to make DEWI look left/right on world select screen. No real reason just a little Easter Egg if joy0left then gosub _spriteLeft if joy0right then gosub _spriteRight if joy0up || joy0down then goto _selGame foo101 drawscreen COLUP0 = 156: COLUP1 = 30 if joy0fire then goto _startProg goto foo100 _selGame if h<15 then goto foo101 : rem cheap debounce timer h=1 if joy0up then w=w + 1: if w = 6 then w = 0 if joy0down then w=w-1:if w=-1 then w=5 if w = 0 then gosub _digitOne if w = 1 then gosub _digitTwo if w = 2 then gosub _digitThree if w = 3 then gosub _digitFour if w = 4 then gosub _digitFive if w = 5 then gosub _digitSix goto foo101 _startProg x = 0:q=30:l = 0 : i = 0: u = 0: d = 0 : t = 0 goto _levelDefs _afterLevelDefsCall rem where do we start this world? goto _startRoom _afterStartRoomCall player0x = 78: player0y = 50 : player1x = -60:player1y =-60 rem find out which room tile to draw and playfield it gosub _drawRoom rem different colours per world goto _roomColours _afterRoomColoursCall _mainloop drawscreen rem increase score (i.e. timer) every 60 loops t = t + 1: if t=60 then score=score+1:t=0 COLUP0 = 156:COLUP1=q rem joysticks control + momentum rem move by 2px boundary on joystick, build up a momentum count at same time. rem if off stick and momentum exists, move by 1px boundary and reduce the momentum by 1 if joy0left then l=l+1: gosub _spriteLeft:if l>(24+e) then l=(24+e) if joy0left then player0x = player0x - 2 if !joy0left && l>0 then player0x = player0x -1:l=l-1 if joy0right then i=i+1: gosub _spriteRight:if i>(24+e) then i=(24+e) if joy0right then player0x = player0x + 2 if !joy0right && i>0 then player0x = player0x +1: i=i-1 if joy0up then u=u+1:if u>(24+e) then u = (24+e) if joy0up then player0y = player0y -2 if !joy0up && u>0 then player0y = player0y -1:u=u-1 if joy0down then d=d+1:if d>(24+e) then d=(24+e) if joy0down then player0y = player0y + 2 if !joy0down && d>0 then player0y = player0y + 1:d=d-1 rem check for player crashes into the landscape if collision(playfield,player0) then goto _playerCrash rem check for player reaches the exit - player1 in room type0 is always exit if collision(player0,player1) && y = 0 then goto _endScreen rem check for player collects a collectible - player1 in room type7 is always crystal but no need to check room rem as cannot be anuything else here due to above check if collision(player0,player1) then gosub _getCrystal rem check for player hitting a room exit if player0y < 8 then gosub _goNorth:gosub _drawRoom:gosub _drawCrystal:gosub _drawExit if player0y >87 then gosub _goSouth:gosub _drawRoom:gosub _drawCrystal:gosub _drawExit if player0x > 140 then gosub _goEast:gosub _drawRoom:gosub _drawCrystal:gosub _drawExit if player0x < 16 then gosub _goWest:gosub _drawRoom:gosub _drawCrystal:gosub _drawExit rem if generic sfx timer going then decrease, kill audio channel 0 when it hits zero if g>0 then g=g-1:if g= 0 then AUDV0=0 goto _mainloop _playerCrash rem player failed screen gosub _deadPlayer g = 150 : rem audio timer also used to lower volume so explosion SFX fades out (rather crudely) AUDC0 = 8 : AUDV0 = 8 : AUDF0 = 21 _foo2 drawscreen COLUP0=q:COLUP1=q rem volume fader if g>0 then g=g-1:if g=0 then AUDV0=0:player0x=-20:player0y=-20 if g=113 then AUDV0=6 if g=75 then AUDV0=4:gosub _deadPlayer2 if g=37 then AUDV0=2 if !joy0fire then goto _foo2 goto _startScreen _endScreen rem Remove player sprite, strobe score until fire button pressed g = 200 : rem audio timer for warping out noise AUDC0 = 19 : AUDV0 = 8 : AUDF0 = 13 _foo1 player0x = -60:player0y =-60 : rem remove player sprite drawscreen x = x + 1 scorecolor = x rem volume fader if g>0 then g=g-1:if g=0 then AUDV0=0 if g=175 then AUDV0=7 if g=150 then AUDV0=6 if g=125 then AUDV0=5 if g=100 then AUDV0=4 if g=75 then AUDV0=3 if g=50 then AUDV0=2 if g=25 then AUDV0=1 if !joy0fire then goto _foo1 goto _startScreen _goNorth player0y = 87 if r = 0 then r=5:return r=r-1:return _goSouth player0y = 8 if r= 5 then r=0:return r=r+1:return _goEast player0x =16 if c=7 then c=0:return c=c+1:return _goWest player0x =140 if c=0 then c=7:return c=c-1:return _drawRoom rem find out tile number from the levelDefs z = r*8 z = z + c if w = 0 then y = world0[z} if w = 1 then y = world1[z} if w = 2 then y = world2[z} if w = 3 then y = world3[z} if w = 4 then y = world4[z} if w = 5 then y = world5[z} on y gosub room0 room1 room2 room3 room4 room5 room6 room7 return _drawExit rem bail if not at start room if z <> p then return rem draw exit but only if all three crystals are collected (they have their room number changed to 255 when collected) if a=255 && b=255 && f=255 then player1x = 78:player1y=50: goto _spriteExit _afterDrawExit return _drawCrystal rem bail if not a room type 7. Otherwise check if a crystal is still in this room if y <7 then return if z=a then goto _drawCrystal2 if z=b then goto _drawCrystal2 if z=f then goto _drawCrystal2 _afterDrawCrystal2 return _drawCrystal2 rem draw a crystal in the centre of the room on the central ledge/plinth goto _spriteDiamond _afterSpriteDiamond player1x = 77:player1y=40 goto _afterDrawCrystal2 _getCrystal rem take player1 off screen, set the location flag accordingly (255) player1x = -20:player1y=-20 if z=a then a=255 if z=b then b=255 if z=f then f=255 g = 75 : rem set the audio timer, mainloop will handle decreasing it and killing audio channel 0 when expired AUDC0 = 19 : AUDV0 = 8 : AUDF0 = 13 return rem each world is 48 digits with the room type number, going left to right, top to bottom _levelDefs data world0 5,5,6,0,3,4,0,6,5,6,3,7,1,1,5,3,4,2,4,1,3,3,6,5,0,0,3,4,4,5,2,3,7,4,3,2,6,3,3,4,0,5,1,2,3,7,5,1 end data world1 3,0,2,6,5,6,1,2,4,4,0,6,5,4,7,0,0,1,2,0,3,2,3,6,3,4,3,5,6,7,2,1,6,6,2,6,1,6,0,3,1,4,3,3,5,7,4,6 end data world2 0,1,3,0,4,7,6,5,1,7,1,5,2,1,5,4,3,6,3,5,2,1,4,1,1,3,0,5,2,0,1,4,7,0,3,1,2,5,4,6,4,4,2,4,2,0,4,4 end data world3 0,6,2,5,2,4,2,5,3,5,0,4,4,0,2,6,0,2,7,1,4,5,2,5,1,2,6,1,7,6,2,1,2,1,0,0,0,5,4,7,2,5,1,1,1,6,3,0 end data world4 4,6,4,6,3,1,1,2,2,5,3,5,5,4,5,5,5,0,4,1,1,1,1,7,3,4,0,6,4,0,2,2,7,3,6,1,0,4,1,4,3,0,3,2,3,7,1,1 end data world5 5,7,0,0,3,1,2,7,6,1,5,0,5,3,2,1,4,5,3,5,6,0,7,0,3,2,0,1,6,3,1,1,1,3,3,3,1,3,4,0,6,4,3,0,6,2,1,4 end goto _afterLevelDefsCall _startRoom rem hardcoded starting room numbers by world. e is added to momentum (later worlds are more skiddy) if w=0 then r=0:c=3:e=0:a=11:b=32:f=45 if w=1 then r=0:c=1:e=2:a=14:b=29:f=45 if w=2 then r=3:c=5:e=2:a=5:b=9:f=32 if w=3 then r=1:c=2:e=4:a=18:b=28:f=39 if w=4 then r=2:c=1:e=4:a=23:b=32:f=45 if w=5 then r=5:c=3:e=6:a=1:b=7:f=22 rem calc start number from r,c - this takes 14 bytes, it's 4 per world for hardcoding (p=3 etc.) so smaller this way p = r*8 p = p + c goto _afterStartRoomCall _roomColours if w = 0 then COLUPF = 134 : rem blue if w = 1 then COLUPF = 70 : rem red if w = 2 then COLUPF = 86 : rem purple if w = 3 then COLUPF = 252 : rem yellow if w = 4 then COLUPF = 182 : rem green if w = 5 then COLUPF = 12 : rem silver goto _afterRoomColoursCall rem DEWI with eyes looking to right _spriteRight player0: %01011010 %00111100 %01101010 %01101010 %11111111 %11111111 %01111110 %00111100 end return rem DEWI with eyes looking to left _spriteLeft player0: %01011010 %00111100 %01010110 %01010110 %11111111 %11111111 %01111110 %00111100 end return rem Tiptonite crystal _spriteDiamond player1: %0001000 %0011100 %0111110 %1011111 %0101110 %0010100 %0001000 end goto _afterSpriteDiamond rem the exit warp _spriteExit player1: %11111111 %10000001 %10111101 %10100101 %10100101 %10111101 %10000001 %11111111 end goto _afterDrawExit rem "pop" type explosion, lifted from Einvader _deadPlayer player0: %10001001 %01000010 %00100100 %10000000 %00000001 %00100100 %01000010 %10010001 end return rem second frame of explosion, just some dust left over _deadPlayer2 player0: %00001000 %00000000 %00000000 %10000000 %00000001 %00000000 %00000000 %00010000 end return rem room playfields. A world's start room will always be a room type 0. There are always 3 x room type 7 and the rem crystals live here, one in each. World's start room becomes world's exit room once all 3 are collected room0 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XX............................XX X..............................X ................................ ................................ ................................ ................................ ................................ X..............................X XX............................XX XXXXXXXXXXXX........XXXXXXXXXXXX end return room1 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX ................................ ................................ ................................ ................................ ...........XXXXXXXXXX........... XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX end return room2 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX .........XXX........XXX......... ..........XX........XX.......... ..........XX........XX.......... ..........XX........XX.......... .........XXX........XXX......... XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX end return room3 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX ...........XXXXXXXXXX........... ................................ ................................ ................................ ................................ XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX end return room4 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX ....................X........... ....................X........... ....................X........... ....................X........... ....................X........... XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX end return room5 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX ...........X.................... ...........X.................... ...........X.................... ...........X.................... ...........X.................... XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX end return room6 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX ...........XXXXXXXXXX........... ................................ ................................ ................................ ...........XXXXXXXXXX........... XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX XXXXXXXXXXXX........XXXXXXXXXXXX end return room7 playfield: XXXXXXXXXXXX........XXXXXXXXXXXX XXXX........................XXXX XX............................XX ................................ ................................ ..............XXXX.............. ...............XX............... ................................ XX............................XX XXXX........................XXXX XXXXXXXXXXXX........XXXXXXXXXXXX end return rem sprite definitions for digits 1-6 for world select functionality. These are lifted from the retroputer font rem as that keeps the digits consistent with the timer display even if using 8x8 digits is a bit of a memory hog _digitOne player1: %00111000 %00111000 %00111000 %00111000 %00011000 %00011000 %00011000 %00011000 end return _digitTwo player1: %01111110 %01100000 %01100000 %01100000 %00111110 %00000010 %01000010 %01111110 end return _digitThree player1: %01111110 %01000110 %00000110 %00000110 %00111110 %00000010 %01000010 %01111110 end return _digitFour player1: %00001100 %00001100 %00001100 %01111110 %01000100 %01000100 %01000100 %00000100 end return _digitFive player1: %01111110 %01000110 %00000110 %00000110 %01111100 %01000000 %01000000 %01111110 end return _digitSix player1: %01111110 %01000110 %01000110 %01000110 %01111100 %01000000 %01000010 %01111110 end return