;---------------------------------------------------------------------------- ;Trapped.z80 - ; written by Justin Shaler, inspired by Block Dude ; ; TODO: add more levels, test ; OPTIMIZATIONS: pass_cont (use shadow regs) ; ; 1/21/05 - uploaded to ticalc ; 1/19/05 - completed testing (played through the entire game with no errors); began writing readme ; 1/18/05 - added two levels and began final testing ; 9/21/04 - fixed bug of extra "pop" when stepping up into a door ; 9/18/04 - added another level and changed delay routine to use halt to conserve battery life ; 9/06/04 - added another level - not sure if it's beatable ; 9/03/04 - added levels, made screen for when you win the game ; 8/30/04 - added 2 new levels, changed some of the old levels because of the new 25 tile height ; 8/29/04 - Fixed ladder bugs, added a level with a bunch of ladders ; 8/28/04 - Finished ladders, with a few bugs ; 8/27/04 - Began to do ladders: huge changes needed ; - New Keys: [x,t,0,n] is used to look around the screen ; Hold Alpha + Up/Down to go up or down ladders ; 2nd still does secondary action ; Up/down by themselves still work the same, unless already climbing a ladder ; 8/26/04 - finished ingame menu, made the level height 25, finished level compression ; 8/25/04 - password and level number display at the beginning of the level ; made metal walls and doors indesctuctible ; 8/24/04 - debugged the password routine, made a working door sprite to advance to the next level ; 8/22/04 - completed the main menu, the password entry ; 8/20/04 - made an opening screen that looked horrible, started (hopefully) a better one ; 8/19/04 - got just about everything working ; - finished push block, bomb, goldbomb routines ; - finished picking up routine ; - finished ability to advance to the next level ; - added delays to everything so you don't move too fast ; 8/18/04 - finished setting down routine ; - finished player falling routine ; - finished falling routine for other objects (unless it doesn't work for a large stack of blocks when one is blown up ; -- i don't know since i haven't implemented bombs yet) ; 8/17/04 - finished climbing routine, started routine to set down blocks ; - also did some restructuring of code (added getLevelByte) ; - IDEA: It might be better if I used saferam instead of variables in code, changed ; 8/16/04 - started simple left/right movement and climbing ; 8/13/04 - finished implementing alpha scrolling, started on player movement ; 8/12/04 - debugged displayMap - now it works! ; 8/11/04 - wrote most of displayMap ; 8/10/04 - decided to try making this game (my first) ; - IDEA: similar to Block Dude from puzzPack, but with bombs, spikes, ; and push-only blocks. ; - Wrote loadLevel, calcMapStart ;---------------------------------------------------------------------------- .nolist #define TI83P #include "ion.inc" #include "keyval.inc" ;#define BETA #define LADDERS_ON ; ladder functionality takes up about 500 bytes, but i don't ever plan on turning them off... ; so, why DID i make them able to be disabled??? who knows... ; used to draw large font anywhere #define FracDrawLFont 2 #define FontFlags 50 ; bcall to draw a rectangle filled with white #define drawRectClear $4D8C #define Highest_Level 13 ; the number of playable levels ; for key<->ascii #define CHARTABLE_LENGTH 36 ;map size: 26 wide x 25 high #define mapWidth 26 #define x_max 25 #define mapHeight 25 #define y_max 24 ;#define levelDataSize 2 + (mapHeight * mapWidth / 2) ;bytes/level (decompressed) ;tiles index values: #define blank 0 #define wall 1 #define metalwall 2 #define block 3 #define pushblock 4 #define bomb 5 #define goldbomb 6 #define spikes 7 #define door 8 #define ladder 9 #define block_ladder $A #define pushblock_ladder $B #define bomb_ladder $C #define goldbomb_ladder $D ; playerFlags bit values: #define bFacingRight 7 #define bCarryingBlock 6 #define bCarryingBomb 5 #define bCarryingGBomb 4 #define bScrolling 3 #define bHitSpikes 2 #define bTemp2 1 #define bTemp1 0 ; moreFlags bit values #define bmClimbingLadder 7 #define delayLength $0C ; a few RAM variables #define currentLevelNumber saferam3 + 1 #define passChar currentLevelNumber ; in the password routine, this is how many characters have been entered (0 - 5) #define playerX saferam3 + 2 #define playerY saferam3 + 3 #define playerFlags saferam3 + 4 ; %(facing right?)(carrying block?)(carrying bomb?)(carrying goldbomb?)(scrolling w/ alpha)(dead?)(temp2 drawMap: original temp)(temp drawMap: is a half-tile?) #define mapstartX saferam3 + 5 ; it represents the starting X tile, not byte! #define mapstartY saferam3 + 6 #define safeword saferam3 + 7 ; use these two bytes for temp storage #define screenX saferam3 + 7 ; used as the position on-screen of the tile being drawn in drawMap #define screenY saferam3 + 8 #define menuOption saferam3 + 9 ; the currently selected menu option (not preserved) #define curPassword saferam3 + 10 ; 5 bytes long, next available is saferam3 + 15 #define moreFlags saferam3 + 15 #define curLevel saferam1 ; location in memory where the current level is stored .list #ifdef TI83P .org progstart-2 .db $BB,$6D #else .org progstart #endif ret ; don't let ti-os run the program jr nc, start title: .db "Trapped! v1.0" #ifdef BETA .db " - beta" #endif .db 0 start: runMainMenu: xor A ld (menuOption), A ld (playerFlags), A ld (moreFlags), A ld A, Highest_Level + 1 ; load the title level call loadLevel call calcMapStart call redrawMap call delay ; a little delay for looks call drawMenu call drawLines call ionfastcopy ; now get the keypresses menu_key: ; see if 2nd or del was pressed ld A, $FF out (1), A ; reset key port ld A, KeyRow_Top out (1), A in A, (1) cp k2nd ; 2nd selects menu option jp z, menuSelect cp kDel ; del quits ret z cp kMode ret z ; mode quits ld A, $FF out (1), A ; reset key port ld A, KeyRow_5 out (1), A in A, (1) cp kEnter ; enter selects menu option jp z, menuSelect ld A, $FF ; reset key port out (1), A ld A, KeyRow_Pad out (1), A ; arrow keys? in A, (1) cp kUp call z, menuUp cp kDown call z, menuDown jr menu_key ; nothing pressed that we should do anything with menuDown: ld A, (menuOption) cp 2 jr z, setMenu0 inc A menuSaveDrawRet: ld (menuOption), A call drawLines call delay ret menuUp: ld A, (menuOption) or A jr z, setMenu2 dec A jr menuSaveDrawRet menuSelect: ld A, (menuOption) or A jp z, newGame ; 0 = new game cp 1 ; 1 = password entry jr z, enterPass ret ; 2 = quit setMenu0: xor A ld (menuOption), A jr menuSaveDrawRet setMenu2: ld A, 2 ld (menuOption), A jr menuSaveDrawRet enterPass: call redrawMap call drawPasswordBox call ionfastcopy set FracDrawLFont, (IY + FontFlags) ; draw the large font anywhere (y = 35, x = 41 ) xor A ld (passChar), A ; we've entered no characters yet dec A ; now A = $FF out (1), A ; reset key port ld HL, $2020 ld A, $20 ld (curPassword), HL ; clear the 5 bytes to spaces ($20) ld (curPassword + 2), HL ld (curPassword + 4), A pass_key: call waitKey cp GDel jr z, pass_bksp ; del removes the last letter cp GClear jr z, enterPass ; clear restarts the password entry cp GMode jr z, pass_done cp GEnter jr z, pass_lookup ; convert to ascii = charTable(getCSC() - $0B) add A, -$0B ; A = getCSC() - $0B cp CHARTABLE_LENGTH + 1 jr nc, pass_key ld IX, charTable ld D, 0 ld E, A add IX, DE ; IX -> ascii ld A, (IX) ; A = ascii ; it must be a letter ('A' <= A <= 'Z') cp 'A' jr c, pass_key cp 'Z' + 1 jr nc, pass_key ; so now we have a valid ascii character in A ld B, A ; save A in B ld A, (passChar) ; have we already entered 5 letters? cp 5 jr nc, pass_key inc A ld (passChar), A ; increment the number of letters entered dec A ld HL, curPassword ld D, 0 ld E, A add HL, DE ld A, B ld (HL), A ; save the letter to the password buffer call drawPass jr pass_key pass_done: res FracDrawLFont, (IY + FontFlags) jp runMainMenu pass_bksp: ld A, (passChar) or A jr z, pass_key dec A ld (passChar), A ld HL, curPassword ld D, 0 ld E, A add HL, DE ld A, $20 ld (HL), A call drawPass jr pass_key drawPass: ; displays the currently entered password (5 characters max) ld HL, $242A ld (PenCol), HL ld B, 5 ; show 5 characters ld HL, curPassword ; HL -> password bcall(_vputsn) ret pass_lookup: ld A, (passChar) cp 5 ; we should have 5 letters entered before searching jp nz, pass_key ld B, 0 ; B is the current password level for searching - 1 pass_check: ld IX, passwords ; IX is the beginning of the current password = passwords + (5 * B) ld DE, 5 ld A, B ; save the level number or A jr z, pass_cont pass_mult: add IX, DE djnz pass_mult pass_cont: ld B, A ; restore the level number ld HL, (curPassword) ld C, 0 ; C is the number of letters that have matched - once one fails, it is set to 0 - when C = 5, the password was found ld A, (IX) ; it's too bad that i can't use something like cpir for this, since cpir is opposite what i need (stops when it matches) cp L jr nz, next_pass ld A, (IX + 1) cp H jr nz, next_pass ld HL, (curPassword + 2) ld A, (IX + 2) cp L jr nz, next_pass ld A, (IX + 3) cp H jr nz, next_pass ld HL, (curPassword + 4) ld A, (IX + 4) cp L jr nz, next_pass ; it matches ld A, B ; B is (the level number that matched) - 1 inc A jr runLevel next_pass: inc B ld A, B cp Highest_Level + 1 jr nc, newGame ; password wasn't found jr pass_check newGame: ld A, 1 ; load level 1 runLevel: res FracDrawLFont, (IY + FontFlags) ; in case it was set by password functions call loadLevel xor A ld (moreFlags), A ; make it so he's not climbing call calcMapStart call redrawMap call startLevel or A ; A = 0 if we should go to the main menu jp z, runMainMenu cp 1 ; A = 1 if we should go to the next level jr z, nextLevel ; error rst 10h\dec B = "D7 05" should be "28 9F 1C", i think $9F16 is the problem cp 2 ; A = 2 if the player hit spikes or chose to restart the level jr z, restartLevel ret nextLevel: call delay call delay ld A, (currentLevelNumber) cp Highest_Level jr z, game_won inc A jr runLevel restartLevel: call delay call delay ld A, (currentLevelNumber) jr runLevel ; ---- game_won ---- ; this loads the final level (HighestLevel + 2) ; then it runs without user interaction according to the following rules: ; 1) calculate map start and redraw the map ; 2) if the tile to the facing direction is a wall, switch the facing direction using smc (note: INC A = $3C and DEC A = $3D) ; 3) move in the facing direction and down one ; 4) if the current row is 18 (zero-based), then set the current row to be 8 (row == playerY) ; 5) repeat all steps until enter is pressed, then go to the main menu game_won: ld A, Highest_Level + 2 call loadLevel game_won_loop: ; rule (1) call calcMapStart call redrawMap call delay ; a little delay for looks ; rule (2) ld HL, (playerY) ld A, (playerX) game_won_facing1: ; for SMC, changes to dec A or inc A inc A ; we start facing to the right call getTile cp wall ; we are facing into a wall jr z, game_won_switchFacing game_won_forward: ; we return to here after switching the facing direction ld A, (playerX) game_won_facing2: inc A ld (playerX), A game_won_fall: ; rule 4 ld A, (playerY) inc A cp 18 jr z, game_won_setY8 ld (playerY), A game_won_cont2: ld A, $FF out (1), A ; reset key port ld A, KeyRow_5 out (1), A in A, (1) cp kEnter jp z, game_won_showWinMessage ; show the winning message jr game_won_loop game_won_setY8: ld A, 8 ld (playerY), A jr game_won_cont2 game_won_switchFacing: ld A, (game_won_facing1) cp $3C ; "inc A" jr z, game_won_setLeft ; jump to set everything to "dec A" dec A ; now A = $3C = "inc A" ld (game_won_facing1), A ld (game_won_facing2), A ld A, (playerFlags) set bFacingRight, A ld (playerFlags), A jr game_won_forward game_won_setLeft: inc A ; now A = $3D = "dec A" ld (game_won_facing1), A ld (game_won_facing2), A ld A, (playerFlags) res bFacingRight, A ld (playerFlags), A jr game_won_forward game_won_showWinMessage: bcall(_clrLCDFull) set FracDrawLFont, (IY + FontFlags) ld HL, $1313 ; write "You Won!!!: " + # at 19, 19 ld (PenCol), HL ld HL, text_gamewon bcall(_vputs) game_won_sWM_loop: ld A, 0 out (1), A ld A, KeyRow_Top out (1), A in A, (1) cp k2nd jr nz, game_won_sWM_loop res FracDrawLFont, (IY + FontFlags) jp runMainMenu ; --- drawPasswordBox --- ; Draws the password box sprite ; (10 bytes wide, 21 px high ; x = 12, y = 29) drawPasswordBox: ld A, 12 ld L, 29 ld BC, $150A ld IX, pass_box call ionLargeSprite ret ; --- drawMenu --- ; Draw the menu (2 large sprites) drawMenu: ; draw the two menu sprites ld A, 48 ld L, 0 ld BC, $3806 ; 6 by 56 ld IX, menu_choices call ionLargeSprite ld A, 8 ld L, 33 ld BC, $0E05 ld IX, menu_author call ionLargeSprite ret ; --- drawLines --- ;Line Locations: ;Wow, it's annoying that _iline takes 0 to be the bottom row, not the top... ;(for all, bottom line is top line + 9) ;New Game: ;left = 53, right = 91 ;top line = 44 ;Password: ;left = 54, right = 90 ;top line = 34 ;Exit ;left = 64, right = 80 ;top line = 24 drawLines: ; draw the currently selected lines ld A, (menuOption) or A jr z, drawMenu0 ; draw the lines on new game ( i don't CALL them because i want to return all the way out of drawLineswith them) cp 1 jr z, drawMenu1 ; draw the lines on password jr drawMenu2 ; draw the lines on exit clearMenu0: ld H, 0 jr menu0 drawMenu0: ld H, 1 menu0: ld BC, $352C ; B = x, C = y begin ld DE, $5B2C ; D = x, E = y end bcall(_iline) ; preserves all ld C, $2C - $09 ld E, $2C - $09 ; switch to the bottom line bcall(_iline) ld A, H or A ret z ; return if this was the clearing function call clearMenu1 call clearMenu2 ret clearMenu1: ld H, 0 jr menu1 drawMenu1: ld H, 1 menu1: ld BC, $3622 ; B = x, C = y begin ld DE, $5A22 ; D = x, E = y end bcall(_iline) ; preserves all ld C, $22 - $09 ld E, $22 - $09 ; switch to the bottom line bcall(_iline) ld A, H or A ret z ; return if this was the clearing function call clearMenu0 call clearMenu2 ret clearMenu2: ld H, 0 jr menu2 drawMenu2: ld H, 1 menu2: ld BC, $4018 ; B = x, C = y begin ld DE, $5018 ; D = x, E = y end bcall(_iline) ; preserves all ld C, $18 - $09 ld E, $18 - $09 ; switch to the bottom line bcall(_iline) ld A, H or A ret z ; return if this was the clearing function call clearMenu0 call clearMenu1 ret ; returns the scan code of a key pressed (not 0) ; better for battery life that a plain getCSC loop waitKey: bcall(_getCSC) ; wait for a keypress ei halt or A jr z, waitKey ret ; -- redrawMap -- ; Clears the graph buffer, calls drawMap, and calls ionfastcopy redrawMap: ld hl,gbuf ld de,gbuf+1 ld bc,767 ld (hl),0 ldir ; clear the graph buffer (only used once, so inline) call drawMap call ionfastcopy ret ; -- calcMapStart -- ; Determines the viewable section (12x8) of the tilemap (26x25) based on the player's position ; uses: (playerX), (playerY) ; affects: (mapstartX), (mapstartY) ; destroys: AF ; ; Rules for determining mapstart: ; 1) Have the player within 1 tile of the center, and show 5 tiles above the player ; (the routine exits if these conditions are met) ; 2) the x-offset must be between 0 and 13 (25-12) so that the view is entirely on the map ; 3) the y-offset must be between 0 and 17 calcMapStart: ; mapstartX should be playerX - 6 if 6 <= playerX < 20 ld A, (playerX) cp 20 jr nc, cms_setX14 sub 6 jr c, cms_setX0 ld (mapstartX), A jr cms_setY cms_setX0: xor A ld (mapstartX), A jr cms_setY cms_setX14: ld A, 14 ld (mapstartX), A cms_setY: ; mapstartY will be 0 , if playerY <= 5 ; will be playerY - 5 , if 6 <= playerY <= 21 ; will be 17 , if playerY >= 22 ld A, (playerY) cp 6 jr c, cms_setY0 cp 22 jr nc, cms_setY17 add A, -5 ld (mapstartY), A ret cms_setY0: xor A ld (mapstartY), A ret cms_setY17: ld A, 17 ld (mapstartY), A ret ; ----- drawMap ------ ; Copies the visible portion of the map to the graph buffer. Mapstart must be set to a valid value ; Uses: (playerX), (playerY), (playerFlags), (mapstartX), (mapstartY) ; Affects: gbuf, 2 low bits of (playerFlags) ; Destroys: HL, AF, BC, DE, IX drawMap: ld HL, 0 ld (screenX), HL ; start drawing at the UL corner of the screen ld A, (mapstartX) ld HL, (mapstartY) call getLevelByte ; get our drawing ptr = levels + 2 + (mapstartY * 13) + mapstartX / 2 bit 0, A jr nz, dm_halfByte res 1, A ; bit 1 will be used when the row changes ld (playerFlags), A jr dm_getTileIndexFromMap dm_halfByte: set 1, A ; bit 1 will be used when the row changes ld (playerFlags), A dm_getTileIndexFromMap: ; HL must be a pointer to the current byte of the tilemap ld A, (playerFlags) bit 0, A ; at a half-step? jr Z, dm_getHighNibble ; no ; yes, so ignore the high nibble ld A, (HL) and $0F jr dm_drawTileA dm_getHighNibble: ; here, I get the high nibble, so the range is $0 to $F, instead of $00 to $FF ld A, (HL) srl A srl A srl A srl A dm_drawTileA: ; draw the tile of index A at (screenX, screenY) ; tile start = tiles + (A * 8) push HL ; save the map pointer sla A sla A sla A ; A = A * 8 ld IX, tiles ld E, A ld D, 0 add IX, DE ; IX is now a correct pointer to the tile ld A, (screenX) ld HL, (screenY) ld B, 8 call ionputsprite ; now, advance the screen position pop HL ; restore the map pointer ; check if we've reached the end of the row ld A, (screenX) cp 8 * 11 ; maximum value jr Z, dm_nextRow ; first, advance the screen X coordinate add A, 8 ld (screenX), A ld A, (playerFlags) ; if we haven't done the half-step, advance to it - otherwise, advance to the next byte bit 0, A jr Z, dm_nextHalfStep inc HL ; go to the next byte res 0, A ; we won't be at a half step ld (playerFlags), A jr dm_getTileIndexFromMap ; -- loop to the next tile over dm_nextHalfStep: set 0, A ld (playerFlags), A jr dm_getTileIndexFromMap ; -- loop to the next tile over dm_nextRow: ; check if we're done drawing all map tiles ld A, (screenY) cp 8 * 7 jr Z, dm_drawPlayer add A, 8 ; increment the row value ld (screenY), A ; set the column counters to their init positions ; these include screenX, HL, and the half-step flag bit xor A ld (screenX), A ld E, 7 ld D, 0 add HL, DE ; add 7 if we started at a half-step, i will add (a total) of 8 if we started at a whole step ld A, (playerFlags) bit 1, A jr NZ, dm_nr_setBit0 res 0, A ld (playerFlags), A inc HL jr dm_getTileIndexFromMap dm_nr_setBit0: set 0, A ld (playerFlags), A ;inc HL ; add 7 + 1 = 8 in total here jr dm_getTileIndexFromMap dm_drawPlayer: ; draw the player and what he's holding #ifdef LADDERS_ON ld A, (moreFlags) bit bmClimbingLadder, A jr z, dm_drawPlayerCont ld IX, tPlayerClimbingLadder jr dm_continue2 dm_drawPlayerCont: #endif ; first, figure out whether he is facing left or right, and load the right tile into IX ld A, (playerFlags) bit bFacingRight, A jr nz, dm_drawPlayerRight #ifdef LADDERS_ON ld A, (playerX) ld HL, (playerY) call getTile cp ladder ; if we're in front of a ladder, use the ladder tile jr nz, dm_leftcont ld IX, tPlayerLeft_Ladder jr dm_continue2 dm_leftcont: #endif ld IX, tPlayerleft jr dm_continue2 dm_drawPlayerRight: #ifdef LADDERS_ON ld A, (playerX) ld HL, (playerY) call getTile cp ladder ; if we're in front of a ladder, use the ladder tile jr nz, dm_rightcont ld IX, tPlayerRight_Ladder jr dm_continue2 dm_rightcont: #endif ld IX, tPlayerright dm_continue2: ; figure out the correct screen position of the player in x = A, y = L ld A, (mapstartY) ld B, A ld A, (playerY) sub B ; A = playerY - mapstartY cp 8 ; shouldn't show the player if he's below the screen (or above, in which case A would be negative > 8) ret nc add A, A ; multiply by 8 add A, A add A, A ld (screenY), A ld L, A ; L = ycoord ld A, (mapstartX) ld B, A ld A, (playerX) sub B ; A = playerX - mapstartX cp 12 ; shouldn't show the player if he's off the screen (similarly, or to the left) ret nc add A, A ; multiply by 8 add A, A add A, A ; A = xcoord ld (screenX), A ld B, 8 ; B = sprite size call ionputsprite #ifdef LADDERS_ON ld A, (playerX) ld HL, (playerY) call getTile cp ladder jr nz, dm_cont3 ld IX, tLadder ; xor the ladder tile to clear it ld A, (screenX) ld HL, (screenY) ld B, 8 call ionputsprite dm_cont3: #endif ; now, draw what he's carrying ld A, (playerFlags) ; first, check if there's nothing being carried and %01110000 ret z ; carrying nothing, so we're done #ifdef LADDERS_ON ld A, (playerX) ld HL, (playerY) dec L call getTile cp ladder jr nz, dm_cont4 ld IX, tLadder ; xor the ladder tile to clear it call dm_drawCarried dm_cont4: #endif ld A, (playerFlags) ; ok, he's carrying something - let's figure out what it is sla A sla A jr C, dm_carryingBlock ; carrying a block? sla A jr C, dm_carryingBomb ; a bomb? sla A jr dm_carryingGoldBomb ; a gold bomb, then dm_carryingBlock: #ifdef LADDERS_ON ld A, (playerX) ld HL, (playerY) dec L call getTile cp ladder jr nz, dm_blockcont ld IX, tBlock_Ladder jr dm_drawCarried dm_blockcont: #endif ld IX, tBlock jr dm_drawCarried dm_carryingBomb: #ifdef LADDERS_ON ld A, (playerX) ld HL, (playerY) dec L call getTile cp ladder jr nz, dm_bombcont ld IX, tBomb_Ladder jr dm_drawCarried dm_bombcont: #endif ld IX, tBomb jr dm_drawCarried dm_carryingGoldBomb: #ifdef LADDERS_ON ld A, (playerX) ld HL, (playerY) dec L call getTile cp ladder jr nz, dm_gbombcont ld IX, tGoldBomb_Ladder jr dm_drawCarried dm_gbombcont: #endif ld IX, tGoldbomb dm_drawCarried: ld B, 8 ld A, (screenY) sub 8 ; it should be one tile above the player ret c ld L, A ld A, (screenX) call ionputsprite ; $A228 - for debugging ret ; hey, we're done drawing the map ; --- getTile --- ; Returns the tile at the map location x = A, y = L ; Inputs: A, L ; Outputs: A ; Destroys: all except IX getTile: call getLevelByte bit 0, A jr z, gt_whole ld A, (HL) and $0F ret gt_whole: ld A, (HL) srl A srl A srl A srl A ret ; --- setTile --- ; Sets the map location (x, y) = (A, L) to the tile value in H ; Inputs: A = x pos, L = y pos, H = tile value (must be less than or equal to $0F) setTile: ld B, H push BC ; save the tile value from destruction by getLevelByte call getLevelByte pop BC ; now B is the tile number bit 0, A ld A, (HL) ; A is the current tilemap byte jr z, st_whole ; z from bit 0, A ; now we're going to set the low nibble to the tile value and $F0 ; clear the low nibble or B ; set the low nibble ld (HL), A ; set it in the map ret st_whole: ; here we set the high nibble sla B sla B sla B sla B ; shift B so that the tile value is in the high nibble and $0F ; clear the high nibble or B ; set the high nibble to the tile value ld (HL), A ret ; --- playerFall --- ; This routine causes the player to fall down until the tile beneath him is not blank. ; Also, if the final tile beneath him is spikes, bit 3, (playerFlags) is set - meaning that he is dead ; The routine also displays him falling by drawing the map after each iteration ; Inputs: None ; Outputs: None ; Destroys: all except IX playerFall: ld HL, (playerY) ; check if he's at the bottom of the screen - this doesn't cause death, he just doesn't fall ld A, L cp y_max ret z ; return if he's at the bottom #ifdef LADDERS_ON ; check if he's on top of a ladder tile, then don't fall (if he's not carrying anything) ld A, (playerFlags) and %01110000 jr nz, pf_cont ; carrying something, continue ld A, (playerX) call getTile cp ladder jr nz, pf_cont ; if we're not on a ladder, then continue ld HL, (playerY) ld A, (playerX) inc L call getTile ; if the tile beneath him is blank (i.e. he's hanging in the air on the ladder, set him to be climbing) or A ret nz ld A, (moreFlags) set bmClimbingLadder, A ld (moreFlags), A ret pf_cont: #endif ld HL, (playerY) ld A, (playerX) inc L ; now check if the tile beneath him is blank or a ladder ; (A, L) = (playerX, playerY + 1) call getTile cp door jp z, pop_pop_nextLevel #ifdef LADDERS_ON cp ladder jr z, pf_cont2 #endif or A ; if A is not zero, we can check for spikes jr nz, pf_spikeCheck pf_cont2: ld A, (playerY) inc A ld (playerY), A call calcMapStart call redrawMap jr playerFall ; continue falling pf_spikeCheck: cp spikes ret nz ; if it's not spikes, we're done ld A, (playerFlags) set bHitSpikes, A ; this is only reset at the beginning of the level ld (playerFlags), A ret ; --- tileFall --- ; This routine causes the tile with the inputted coordinates to fall, and then causes the tile above it to fall ; It doesn't draw the changes, so call redrawMap afterward ; Inputs: A = tile x pos, L = tile y pos ; Outputs: None ; Destroys: all except IX tileFall: ld H, A ld (safeword), HL ; save the x and y coordinates (safeword -> y, saveword + 1 -> x): used for when the current tile is falling push HL ; also save it to the stack, for when the tile above needs to fall, if necessary ; now check if the tile itself is a block, pushblock, bomb, or goldbomb (their tile values are contiguous in that order) ; or anything from block_ladder to goldbomb_ladder ld A, (safeword + 1) call getTile cp block jp c, popret #ifdef LADDERS_ON cp goldbomb_ladder + 1 jp nc, popret cp block_ladder jr c, tf_checkgb jr tileFall_ok tf_checkgb: #endif cp goldbomb + 1 jp nc, popret tileFall_ok: ; at this point, the tile is a valid moveable tile - check if it should fall ld (saferam3), A; first save its value same_tileFall: ld HL, (safeword) ld A, L ; check if the tile is at the bottom of the screen cp y_max jr z, next_tileFall ; return if it's at the bottom inc L ld A, (safeword + 1) call getTile ; check if the tile beneath it is blank or a ladder #ifdef LADDERS_ON cp ladder jr z, tf_cont #endif or A jr nz, next_tileFall ; if it is done falling, check the one that was above it tf_cont: #ifdef LADDERS_ON ; check if the tile itself is in front of a ladder ld HL, (safeword) ld A, H call getTile cp block_ladder jr c, tf_setAboveBlank jr tileFall_ladder2 tf_setAboveBlank: #endif ld HL, (safeword) ld A, (safeword + 1) ld H, blank tileFall_ok2: call setTile ; set the tile to a blank one ld HL, (safeword) ; restore the old tile location inc L ; go to the next lower tile location ld (safeword), HL ; save the new location ld A, (saferam3) ; restore the tile value #ifdef LADDERS_ON cp goldbomb + 1 call nc, ladderToNonladder ; changes tiles from ladder to non-ladder, if needed cp goldbomb + 1 call c, nonLadderToLadder ; changes tiles from non-ladder to ladder, if needed ld A, (saferam3) #endif ld H, A ld A, (safeword + 1) call setTile ; set the lower tile to the falling tile jr same_tileFall #ifdef LADDERS_ON tileFall_ladder2: ld HL, (safeword) ld A, (safeword + 1) ld H, ladder jr tileFall_ok2 #endif ; subtracts 7 from A if the tile at (x,y) = (H, L) is blank #ifdef LADDERS_ON ladderToNonladder: ld A, H call getTile ld HL, (safeword) or A ret nz ld A, (saferam3) sub 7 ld (saferam3), A ret ; adds 7 to A if the tile at (x,y) = (H, L) is a ladder nonLadderToLadder: ld A, H call getTile ld HL, (safeword) cp ladder ret nz ld A, (saferam3) add A, 7 ld (saferam3), A ret #endif next_tileFall: ; ok, the last tile was done falling, see if there was another above the old one pop HL ; get the saved coordinates ld A, L or A ret z ; if the old tile was at the top of the map, there was nothing above it, so return dec L ; go to the position above the old tile, L = y coord ld A, H ; now A = x coord call tileFall ; this is a recursive call, but it can only go a maximum of less than mapHeight, since that's the most tiles in a column ret ; we're done popret: pop HL ret ; -- getLevelByte -- ; Get the byte offset in the level based on x and y ; Inputs: A = x pos, L = y pos ; Outputs: HL = byte value = curLevel + 2 + (y * 13) + (x / 2), A = (playerFlags) ; Affects: If A is odd, bit 0 of playerFlags is set ; Destroys: AF, BC, DE, HL getLevelByte: sra A ; A = x / 2 ld C, A ; BC = x / 2 ld B, 0 jr C, glb_tileHalf ; the carry flag is still from sra A ld A, (playerFlags) res 0, A ld (playerFlags), A glb_continue: ld H, 0 ld E, L ; multiply y by 13 --- ld D, H add HL, HL ; HL = y * 2 add HL, DE ; HL = y * 3 add HL, HL ; HL = y * 6 add HL, HL ; HL = y * 12 add HL, DE ; HL = y * 13 add HL, BC ; HL = (y * 13) + (x / 2) inc HL inc HL ; HL = 2 + (y * 13) + (x / 2) ld DE, curLevel add HL, DE ; HL = levels + 2 + (y * 13) + (x / 2) ret glb_tileHalf: ld A, (playerFlags) set 0, A ld (playerFlags), A jr glb_continue ; -- Num2Dec ---- ; Input: L = number to convert, DE = location of ASCII string ; Output: ASCII string at (DE) ; This routine was written by Milos Bazelides and can be found (as of 8/24/04) at http://zilog.sh.cvut.cz/~baze/misc/z80bits.html ; It was modified by me to write spaces instead of initial zeros and to only use L (1 byte instead of 2) Num2Dec: ld H, 0 ld bc,-100 call Num1 ld c,-10 call Num1 ld c,b Num1: ld a,'0'-1 Num2: inc a add hl,bc jr c,Num2 sbc hl,bc ld (de),a inc de ret ; --- HLtoLevelNumAscii --- ; HL will point to a null-terminated ascii string of the current level number HLtoLevelNumAscii: ld HL, (currentLevelNumber) ld H, 0 ld DE, curPassword ; use the bytes at curPassword to temporarily store the ascii-converted level number call Num2Dec ld HL, curPassword + 3 xor A ld (HL), A ; make the null terminator ; now remove the zeros ld HL, curPassword ; increment HL until we get to a character other than '0' (30 decimal) lvlascii_loop: ld A, (HL) cp '0' jr nz, lvlascii_checkEnd inc HL jr lvlascii_loop lvlascii_checkEnd: ; we need to make sure that zero displays as "0" and not blank cp 0 ;(null) ret nz ; it wasn't a null, so we're good dec HL ret ; --- HL to current password --- ; HL will point to a 5-character, fixed-length (not null-terminated) string that is the current password HLtoPass: ld HL, passwords ld A, (currentLevelNumber) ; now get the password = passwords + 5*(currentLevelNumber - 1) dec A or A ret z ld B, A ld DE, 5 sl_loop: add HL, DE djnz sl_loop ret ; -- startLevel --- ; Runs the main game loop - getting key input, taking actions, and exiting when ; the use chooses to quit or advances to the next level. ; Outputs: A = 1 if level was completed, A = 0 if the user chose to go to the main menu, A = 2 if the level should be restarted startLevel: ; display the password box ; 16,16 to 79, 39 set FracDrawLFont, (IY + FontFlags) ; allow large text to be drawn with _v... ld HL, $1010 ld DE, $274F bcall(drawRectClear) ld HL, $1313 ; write "Level: " + # at 19, 19 ld (PenCol), HL ld HL, text_level bcall(_vputs) call HLtoLevelNumAscii bcall(_vputs) ld HL, $1D13 ld (PenCol), HL ld HL, text_pass ; write "Pass: " at 19,29 bcall(_vputs) call HLtoPass ld B, 5 ; write 5 characters bcall(_vputsn) ld A, $FF out (1), A ; reset key port (so we don't pass by the level box) call waitKey res FracDrawLFont, (IY + FontFlags) call redrawMap keyin: ld C, 1 ; see if 2nd, mode, or del was pressed ld A, $FF out (C), A ; reset key ld A, KeyRow_Top out (C), A in A, (C) cp k2nd ; 2nd is the secondary action (bomb, push) jp z, actionButton cp kMode ; mode goes to the ingame menu jp z, modePressed ld A, $FF ; reset key port out (C), A ld A, KeyRow_1 out (C), A in B, (C) bit 7, B ; bit 7 is 0 when alpha is held down jp z, alphaHeld ld A, $FF ; reset key port out (C), A ld A, KeyRow_2 out (C), A in B, (C) bit 7, B ; bit 7 is 0 when X,t,O,n is held down jr z, xHeld ld A, (playerFlags) bit 3, A ; if we were scrolling, but stopped, then reset the mapstart jr z, keyin_continue res 3, A ld (playerFlags), A call calcMapStart call redrawMap ld C, 1 keyin_continue: ld A, $FF ; reset key port out (C), A ld A, KeyRow_Pad out (C), A ; now look for the arrow keys (by themselves, since I have already handled alpha scrolling) in A, (C) cp kUp call z, upPressed cp kDown call z, downPressed cp kLeft call z, leftPressed cp kRight call z, rightPressed jr keyin ; nothing pressed that we should do anything with ; - xHeld checks whether an arrow key is pressed, and scrolls the view if so xHeld: ld A, (playerFlags) set 3, A ; set the scrolling flag ld (playerFlags), A ld A, $FF ; reset the key port out (C), A ld A, KeyRow_Pad out (C), A in B, (C) bit 0, B jr z, xDown bit 1, B jr z, xLeft bit 2, B jr z, xRight bit 3, B jr z, xUp x_continue: call redrawMap jp keyin xDown: call scrollDown bit 1, B jp z, xLeft bit 2, B jp z, xRight jr x_continue xLeft: call scrollLeft bit 3, B jp z, xUp jr x_continue xRight: call scrollRight bit 3, B jp z, xUp jr x_continue xUp: call scrollUp jr x_continue scrollDown: ld A, (mapstartY) cp 17 ret z inc A ld (mapstartY), A ret scrollLeft: ld A, (playerFlags) res bFacingRight, A ld (playerFlags), A ld A, (mapstartX) or A ret z dec A ld (mapstartX), A ret scrollRight: ld A, (playerFlags) set bFacingRight, A ld (playerFlags), A ld A, (mapstartX) cp 14 ret z inc A ld (mapstartX), A ret scrollUp: ld A, (mapstartY) or A ret z dec A ld (mapstartY), A ret ; -- If alpha is held, check if up or down is held to climb ladders alphaHeld: ld A, (playerFlags) and %01110000 ; no climbing ladders if we're carrying something jp nz, keyin ; check if we're on a ladder tile ld HL, (playerY) ld A, (playerX) call getTile cp ladder jr nz, not_climbing_ladder ld A, (moreFlags) set bmClimbingLadder, A ld (moreFlags), A ld C, 1 ld A, $FF ; reset key port out (C), A ld A, KeyRow_Pad out (C), A ; now look for the arrow keys (by themselves, since I have already handled alpha scrolling) in A, (C) cp kUp call z, upPressed cp kDown call z, downPressed jp keyin not_climbing_ladder: ld A, (moreFlags) res bmClimbingLadder, A ld (moreFlags), A jp keyin ; --- If Mode is pressed, show the in-game menu --- ; Gives the user the option to restart the level or return to the main menu ; Keys: ; 2nd: select option ; Mode: return to game modePressed: xor A ld (menuOption), A ; i'm using this byte for the cursor pos, like in the main menu call drawIngameMenu igmenu_key: ld A, $FF out (1), A ; reset key port ld A, KeyRow_Top out (1), A in A, (1) cp k2nd jp z, igmenuSelect cp kMode ; mode returns to game jr z, igmenuBack ld A, $FF out (1), A ; reset key port ld A, KeyRow_5 out (1), A in A, (1) cp kEnter jp z, igmenuSelect ld A, $FF ; reset key port out (1), A ld A, KeyRow_Pad out (1), A ; arrow keys? in A, (1) cp kUp jr z, igmenuUp cp kDown jr z, igmenuDown jr igmenu_key ; nothing pressed that we should do anything with igmenuSelect: res FracDrawLFont, (IY + FontFlags) ld A, (menuOption) or A jr z, igmenuRestartLevel cp 1 jr z, igMainMenu jr igmenuBack igmenuBack: res FracDrawLFont, (IY + FontFlags) call redrawMap call delay jp keyin igmenuRestartLevel: call delay ld A, 2 ; A = 2 will make runLevel restart the level ret ; to runLevel igMainMenu: call delay xor A ; A = 0 will go to the main menu ret igmenuDown: ld A, (menuOption) cp 2 jr z, setigMenu0 inc A jr igmenuSaveDrawRet igmenuUp: ld A, (menuOption) or A jr z, setigMenu2 dec A igmenuSaveDrawRet: ld (menuOption), A call drawCursor call delay call delay jp igmenu_key setigMenu0: xor A jr igmenuSaveDrawRet setigMenu2: ld A, 2 jr igmenuSaveDrawRet ; --- drawIngameMenu --- ; The in-game menu is as follows (all coordinates with 0,0 as UL corner): ; A box from (8,8) to (87, 56) filled with white ; Level display: "Level 12" at (11,11) ; Password display: "Pass:ABCDE" at (11,19) ; Horizontal line from (11,28) to (84,28) ; "Restart" at 19,31 ; "Main Menu" at 19,39 ; "Back" at 19,47 ; ASCII $05 (right arrow cursor) at (13, 31+8*cursor), where 0<=cursor<=2 is the selected item drawIngameMenu: set FracDrawLFont, (IY + FontFlags) ; allow large text to be drawn with _v... ld HL, $0808 ld DE, $3857 ; = 87,56 bcall(drawRectClear) ; display the box ld HL, $0B0B ; write "Level: " + # at 11,11 ld (PenCol), HL ld HL, text_level bcall(_vputs) call HLtoLevelNumAscii bcall(_vputs) ld HL, $130B ld (PenCol), HL ld HL, text_pass ; write "Pass: " at 11,19 bcall(_vputs) call HLtoPass ld B, 5 ; write 5 character password bcall(_vputsn) ld H, 1 ; draw the horizontal line (y coordinates are backward) ld BC, $0B23 ; B = x, C = y begin ld DE, $5423 ; D = x, E = y end bcall(_iline) ld HL, $1F13 ; write "Restart" ld (PenCol), HL ld HL, text_restart bcall(_vputs) ld HL, $2713 ; write "Main Menu" ld (PenCol), HL ld HL, text_mainmenu bcall(_vputs) ld HL, $2F13 ; write "Back" ld (PenCol), HL ld HL, text_back bcall(_vputs) drawCursor: ; clear all cursors ld B, 3 ld HL, $1F0B ; (13, 31) clearCursor_loop: ld A, ' ' ld (PenCol), HL bcall(_vputmap) ld DE, $0800 add HL, DE ; add H, 8 djnz clearCursor_loop ; draw the cursor at (13, 31+8*cursor) ld A, (menuOption) sla A sla A sla A ; A = 8*cursor ( A = 0, 8, or 16 ) add A, 31 ; A = 31 + 8*cursor ld L, $0B ld H, A ld (PenCol), HL ld A, $05 ; ASCII $05 = right arrow cursor bcall(_vputmap) ; writes the character in A to pencol, penrow ret ; --- actionButton --- ; Performs the secondary action on the tile in front ; If a bomb is in front, it is blown up, and if something is on the other side of it, that is blown up ; then the tiles above the bomb and the destroyed tile fall ; If a pushblock is in front, and nothing is in front of it, it is moved forward 1 space, and then falls, if needed. actionButton: jp action_checkEdge action_cont: call set_facing12 ld A, (safeword + 1) ld HL, (playerY) ; (A, L) = (playerX + facing, playerY) call getTile cp bomb jr z, action_bomb cp goldbomb jr z, action_goldbomb cp pushblock jr z, action_pushblock #ifdef LADDERS_ON cp bomb_ladder jr z, action_bomb cp goldbomb_ladder jr z, action_goldbomb cp pushblock_ladder jr z, action_pushblock #endif jp keyin ; the tile in front isn't a bomb or pushblock, so we're done action_bomb: #ifdef LADDERS_ON cp bomb_ladder jr nz, bomb_reg ld B, ladder jr bomb_blowUp bomb_reg: #endif ld B, blank bomb_blowUp: ld A, (safeword + 1) ld HL, (playerY) ; first, destroy the bomb ld H, B call setTile action_goldbomb: ld A, (safeword) cp x_max + 1 ; A must be between 0 and x_max, i.e. A must be less than or equal to x_max jr nc, bomb_fallAbove ld HL, (playerY) call getTile cp metalwall ; don't blow up metal walls jr z, bomb_fallAbove cp door ; or doors jr z, bomb_fallAbove ld A, (safeword) ld HL, (playerY) ld H, blank call setTile ; set the tile on the other side of the bomb to a blank ld A, (playerY) or A jp z,action_done ; if we're at the top of the screen, nothing can fall dec A ld L, A ld A, (safeword) ; (A, L) = (playerX + 2*facing, playerY - 1) call tileFall ; make the stack over whatever was destroyed fall bomb_fallAbove: ld A, (playerY) or A jr z, action_done ; if we're at the top of the screen, nothing can fall call set_facing12 ; restore safeword after tileFall ld A, (safeword + 1) ld HL, (playerY) dec L ; (A, L) = (playerX + facing, playerY - 1) call tileFall jr action_done action_pushblock: ; check if (safeword) is a valid x coordinate ld A, (safeword) ; A = (playerX) + 2*facing cp x_max + 1 ; A must be between 0 and x_max, i.e. A must be less than or equal to x_max jp nc, keyin ; if the pushblock is at the edge, we can't push it ld A, (safeword) ld HL, (playerY) ; (A, L) = (playerX + 2*facing, playerY) call getTile ; check if the tile in front the pushblock is not blank and not a ladder #ifdef LADDERS_ON cp ladder jr z, pblock_cont1 #endif or A jp nz, keyin ; return if we can't push it pblock_cont1: ld A, (safeword) ld HL, (playerY) #ifdef LADDERS_ON call getTile ; check if it was a ladder cp ladder ld A, (safeword) ld HL, (playerY) jr nz, pblock_reg ld H, pushblock_ladder jr pblock_set pblock_reg: #endif ld H, pushblock pblock_set: call setTile ; set the pushblock to the new position ld A, (safeword + 1) ld HL, (playerY) #ifdef LADDERS_ON call getTile ; check if it was a pushblock, or a pushblock_ladder cp pushblock_ladder ld A, (safeword + 1) ld HL, (playerY) jr nz, pblock_oldblank ld H, ladder jr pblock_setold pblock_oldblank: #endif ld H, blank pblock_setold: call setTile ; remove the pushblock from it's old position ld A, (safeword) ld HL, (playerY) call tileFall ; make the pushblock fall call set_facing12 ; restore anything that was destroyed by tileFall ld A, (playerY) ; now we will make any stack of blocks that were above it fall or A ; first, check that it wasn't at the top jr z, action_done dec A ld L, A ld A, (safeword + 1) ; (A, L) = (playerX + facing, playerY - 1) call tileFall ; make any stack fall action_done: call redrawMap call delay call delay jp keyin ; checks if we're facing toward an edge action_checkEdge: ld A, (playerFlags) bit bFacingRight, A ld A, (playerX) jr z, aCheckEdgeL cp x_max ; are we at the right edge? ret z ; return if we are jp action_cont aCheckEdgeL: or A jp z, keyin ; return if we're at the left edge jp action_cont ; -- set_facing12 ; Sets playerX + facing to safeword + 1 and playerX + 2*facing to safeword set_facing12: ld A, (playerX) ld B, A ; B = (playerX) call setPlayerXFacing ; now A = (safeword + 1) = (playerX) + facing sub B ; A = facing ld B, A ; B = facing ld A, (playerX) add A, B add A, B ; A = (playerX) + 2*facing ld (safeword), A ret ; -- check if the tile to the player's left is a blank - if so, then move there leftPressed: ld A, (playerFlags) ; set the facing bit to the left res bFacingRight, A ld (playerFlags), A and %01110000 jr nz, left_checkCarrying ; restrict movement if carrying thing would hit something left_cont: ld A, (playerX) ;or A ; these are from before I used a door ;jr z, pop_NextLevel ; if we're at the left edge, then go to the next level or A ret z ; return if we're at the left edge (don't move left) ld HL, (playerY) dec A call getTile cp door jr z, pop_NextLevel #ifdef LADDERS_ON cp ladder jr z, left_cont2 #endif or A ; 0 = blank jr nz, dirpressed_1 left_cont2: ld A, (playerX) dec A ld (playerX), A dirpressed_1: #ifdef LADDERS_ON ld A, (moreFlags) res bmClimbingLadder, A ld (moreFlags), A #endif call playerFall call calcMapStart call redrawMap call delay ld A, (playerFlags) bit bHitSpikes, A jr nz, dead_spikes ret left_checkCarrying: ; check if (playerX - 1, playerY - 1) is blank ld A, (playerX) ld HL, (playerY) dec A ; i know that if we're at the left edge, we will get meaningless data here, but when we return we check the left edge situation dec L call getTile #ifdef LADDERS_ON cp ladder jr z, left_cont #endif or A jr z, left_cont ; it's blank, so keep trying to move call redrawMap ; draw the facing change, if happened ret dead_spikes: pop HL ; since a direction pressed was called, I wan't to return directly to the main game loop - not the key loop ld A, 2 ; since A = 2 if the player died ret ; this returns from startLevel, not leftPressed pop_pop_NextLevel: pop HL ; used in playerFall pop_NextLevel: pop HL ; since leftPressed was called, I wan't to return directly to the main game loop - not the key loop ld A, 1 ; since A = 1 if the level was completed ret ; this returns from startLevel, not leftPressed rightPressed: ld A, (playerFlags) ; set the facing bit to the right set bFacingRight, A ld (playerFlags), A and %01110000 jr nz, right_checkCarrying ; restrict movement if carrying thing would hit something right_cont: ld A, (playerX) cp x_max ret z ld HL, (playerY) inc A call getTile cp door jr z, pop_NextLevel #ifdef LADDERS_ON cp ladder jr z, right_cont2 #endif or A ; 0 = blank jr nz, dirpressed_1 right_cont2: ld A, (playerX) inc A ld (playerX), A jr dirpressed_1 right_checkCarrying: ; check if (playerX + 1, playerY - 1) is blank ld A, (playerX) ld HL, (playerY) inc A ; i know that if we're at the right edge, we will get meaningless data here, but when we return we check the right edge situation dec L call getTile #ifdef LADDERS_ON cp ladder jr z, right_cont #endif or A jr z, right_cont ; it's blank, so keep trying to move call redrawMap ; draw the facing change, if happened ret ; -- climb, according to these rules -- ; 1) the tile in front must NOT be blank (playerX + facing, playerY) and it must not be a door ; 2) the tile above must be blank (playerX, playerY - 1) or a ladder ; 3) the tile above and in front must be blank (playerX + facing, playerY - 1) or a ladder or a door ; If carrying something, also: ; 4) 2 tiles above must be blank (playerX, playerY - 2) or a ladder ; 5) 1 tile in front and 2 tiles up must be blank (playerX + facing, playerY - 2) or a ladder upPressed: ; check rule (2) ld A, (playerY) ; there must be at least one tile above the player (i.e. playerY >= 1) cp 1 ret c ; return if there's no available tile dec A; now check whether it's blank ld L, A ld A, (playerX) ; now (A, L) = (playerX, playerY - 1) call getTile ;cp door ;ret z #ifdef LADDERS_ON cp ladder jr z, up_climbingCheck #endif or A ret nz ; return if it's not blank ; if we're already climbing a ladder, just move up #ifdef LADDERS_ON up_climbingCheck: ld A, (moreFlags) bit bmClimbingLadder, A jr z, up_rule4 ld A, (playerX) ld HL, (playerY) dec L ; check if the tile above is a ladder, if not, do nothing call getTile cp ladder ret nz ld A, (playerY) dec A ld (playerY), A call calcMapStart call redrawMap call delay ret up_rule4: #endif ; check rule (4), if needed ld A, (playerFlags) ; check if carrying something and %01110000 jr z, up_checkFacingRules ld A, (playerY) ; there must be 2+ tiles above the player (i.e. playerY >= 2) cp 2 ret c ; return if we're too close to the top sub 2 ld L, A ld A, (playerX) ; now (A, L) = (playerX, playerY - 2) call getTile #ifdef LADDERS_ON cp ladder jr z, up_checkFacingRules #endif or A ret nz ; return if it's not blank up_checkFacingRules: call setPlayerXFacing ; safeword + 1 -> playerX + facing = A ;ld A, (safeword + 1) ;this is set during setPlayerXFacing cp x_max + 1 ; if playerX + facing is off either edge, then quit (negative will loop to $FF) ret nc ; now check rule (1) ld HL, (playerY) ; now (A, L) = (playerX + 1, playerY) call getTile or A ret z ; this one is different: return if it IS blank #ifdef LADDERS_ON cp ladder ret z ; similarly, return if it IS a ladder #endif ; check rule (3) ld A, (safeword+1) ld HL, (playerY) dec L ; now (A, L) = (playerX + 1, playerY - 1) call getTile #ifdef LADDERS_ON cp ladder jr z, up_rule5 #endif cp door jp z, pop_nextLevel or A ret nz ; return if it isn't blank up_rule5: ; check rule (5), if needed ld A, (playerFlags) ; check if carrying something and %01110000 jr z, up_move ld A, (playerY) sub 2 ld L, A ld A, (safeword+1) ; now (A, L) = (playerX + 1, playerY - 2) call getTile or A jr z, up_move ; if it's blank, all rules are satisfied, so move cp ladder jr z, up_move ; or if it's a ladder ret ; it wasn't blank, so return ; actually do the movement up_move: ld A, (safeword + 1) ld HL, (playerX) ; L = playerX, H = playerY dec H ld L, A ; L = playerX + facing ld (playerX), HL ; save the new position jp dirpressed_1 ; -- press down to pick up a block, bomb, or gold bomb - or to set down what is carried --- ; Is something carried? ; Yes: ; a1) Check if he's on an edge ; a1) Check if the spot in front and above him is open (playerX + facing, playerY - 1) or a ladder ; a2) If so, stop carrying the object, and add it to the map at (playerX + facing, playerY - 1) ; a3) Call a routine to make the object fall ; No: ; b1) If a carryable object is in front of him ((playerX + facing, playerY) is a block, bomb, or gold bomb) ; b2) And if the spot above him is open (playerX, playerY - 1) ; b3) And if there isn't something above the object ((playerX + facing, playerY - 1) is blank) ; b4) Then remove the object from the map and set the appropriate carrying bit of playerFlags downPressed: ; if we're already climbing a ladder, just move down #ifdef LADDERS_ON ld A, (moreFlags) bit bmClimbingLadder, A jr z, down_cont ld A, (playerY) cp y_max jr z, down_cont inc A ld L, A ld A, (playerX) ; check if the tile below is a ladder or a blank call getTile cp ladder jr z, down_ladderCont or A jr nz, down_cont ld A, (moreFlags) ; here, a blank was below, so stop climbing and fall res bmClimbingLadder, A ld (moreFlags), A down_ladderCont: ld A, (playerY) inc A ld (playerY), A call playerFall call calcMapStart call redrawMap ld A, (playerFlags) bit bHitSpikes, A jp nz, dead_spikes call delay ret down_cont: #endif ld A, (playerFlags) ; check if something is carried and %01110000 jp z, down_pickUp ; now something is carried, so let's see if we can set it down ld A, (playerFlags) bit bFacingRight, A jr nz, set_downR ; here, we are facing left ld A, (playerX) or A ret z ; do nothing if we're at the left edge dec A ld HL, (playerY) dec L ; now (A, L) = (playerX - 1, playerY - 1) call getTile #ifdef LADDERS_ON cp ladder jr z, down_contL1 #endif or A ret nz ; return if the tile above and left is not blank down_contL1: ld A, (playerX) dec A set_down_LorR: ld HL, (playerY) ; H = playerFlags dec L ; so (A, L) = (playerX + facing, playerY - 1) before I branch off ld (safeword), HL ld (safeword + 1), A ; now safeword -> playerY - 1, playerX + facing : this is so that i can call tileFall with the coordinates bit bCarryingBlock, H jr nz, set_down_block bit bCarryingBomb, H jr nz, set_down_bomb ld H, goldbomb ld A, (playerFlags) res bCarryingGBomb, A jr set_down_finish set_down_block: ld H, block ld A, (playerFlags) res bCarryingBlock, A jr set_down_finish set_down_bomb: ld H, bomb ld A, (playerFlags) res bCarryingBomb, A jr set_down_finish set_downR: ; here, we are facing right ld A, (playerX) cp x_max ret z ; do nothing if we're at the right edge inc A ld HL, (playerY) dec L ; now (A, L) = (playerX - 1, playerY - 1) call getTile #ifdef LADDERS_ON cp ladder jr z, down_contR1 #endif or A ret nz ; return if the tile above and left is not blank down_contR1: ld A, (playerX) inc A jp set_down_LorR set_down_finish: ld (playerFlags), A ; H is now the tile to set (unless there's a ladder, then add 7) #ifdef LADDERS_ON push HL ld HL, (safeword) ld A, H call getTile pop HL cp ladder jr nz, set_down_fin_cont ld DE, $0700 add HL, DE set_down_fin_cont: #endif ld A, (safeword) ld L, A ld A, (safeword + 1) call setTile ld HL, (safeword) ld A, H call tileFall call redrawMap call delay call delay ret ; here, we will try to pick something up, if possible ; b1) If a carryable object is in front of him ((playerX + facing, playerY) is a block, bomb, or gold bomb) ; b2) And if the spot above him is open (playerX, playerY - 1) or a ladder ; b3) And if there isn't something above the object ((playerX + facing, playerY - 1) is blank or a ladder) ; b4) Then remove the object from the map (possibly make it a ladder) and set the appropriate carrying bit of playerFlags down_pickUp: ; check rule (b2) ld HL, (playerY) dec L ld A, (playerX) call getTile #ifdef LADDERS_ON cp ladder jr z, pickup_cont #endif or A ret nz ; return if the tile above him is not blank pickup_cont: call setplayerXfacing ; this will save playerX + facing to safeword + 1 ; check rule (b3) ld HL, (playerY) dec L call getTile #ifdef LADDERS_ON cp ladder jr z, pickup_cont2 #endif or A ret nz ; return if the tile above the object isn't blank pickup_cont2: ; check rule (b1) ld HL, (playerY) ld A, (safeword + 1) ; now (A, L) = (playerX + facing, playerY) call getTile cp block jr z, down_pickUp_block ; pick up a block cp bomb jr z, down_pickUp_bomb ; pick up a bomb cp goldbomb #ifdef LADDERS_ON jr z, down_pickUp_gbomb cp block_ladder jr z, down_pickUp_block ; pick up a block cp bomb_ladder jr z, down_pickUp_bomb ; pick up a bomb cp goldbomb_ladder #endif ret nz ; return if it isn't any of the six (or 3, w/o ladders) down_pickUp_gbomb: ld A, (playerFlags) set bCarryingGBomb, A ; now we're carrying a gold bomb ld (playerFlags), A down_pickUp_removeFront: #ifdef LADDERS_ON ld HL, (playerY) ; check if the tile in front of him had a ladder background ld A, (safeword + 1) call getTile cp block_ladder jr c, down_setblank ld HL, (playerY) ld A, (safeword + 1) ld H, ladder jr down_setFinal down_setblank: #endif ld HL, (playerY) ld A, (safeword + 1) ld H, blank down_setFinal: call setTile ; set the tile in front to a blank or ladder #ifdef LADDERS_ON ld A, (moreFlags) res bmClimbingLadder, A ; we can't be climbing if we're carrying something ld (moreFlags), A call playerFall #endif call redrawMap call delay call delay ; delay extra long ret down_pickUp_block: ld A, (playerFlags) set bCarryingBlock, A ld (playerFlags), A jr down_pickUp_removeFront down_pickUp_bomb: ld A, (playerFlags) set bCarryingBomb, A ld (playerFlags), A jr down_pickUp_removeFront ; --- set_playerXfacing --- ; Loads playerX + facing to safeword + 1 setplayerXfacing: ld A, (playerFlags) bit bFacingRight, A ld A, (playerX) jr z, spx_left inc A spx_cont: ; now A = playerX + facing ld (safeword + 1), A ret spx_left: dec A jr spx_cont ; --- delay --- ; Waits a little (change delayLength to tweak) delay: ei ld b, delayLength delay_loop: halt djnz delay_loop ret ; Compressed level data format: ; - if the high order nibble of a byte is D, then the low order nibble of that byte ; represents the number of $11 bytes in a row (1 to 16) ; (i.e. $DA should be decompressed as $11, $11, $11, $11, $11, $11, $11, $11, $11, $11) ; - if the byte begins with E, then the low nibble represents the number of $00 tiles in a row MINUS 16 (17 to 32) ; (i.e. $E2 ==> $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ($00 19 times)) ; - if the byte begins with F, then the low nibble represents the number of $00 tiles in a row (1 to 16) ; (i.e. $F3 ==> $00, $00, $00, $00) ; ; Note that this means that $00 and $11 will never appear in a tile map. Therefore, $00 is the first byte of all levels ; so that I can find them (since they aren't a constant size, now) ; -- loadLevel -- ; sets the player position, player flags, and the current level number. ; Decompresses the level and copies it to curLevel ; input: A = level number to load (A >= 1) loadLevel: ld HL, levelData + 1 ; begin at the first level ld (currentLevelNumber), A ; save the level number dec A ld E, A ; E = how many levels we need to go past ll_searchAgain: ; find the level in memory ld A, E or A jr z, levelFound ld A, $00 ; search for the beginning of each level ld BC, tiles - levelData ; never start searching in the tile sprites cpir ; now HL = the location of $00 + 1 dec E jr ll_searchAgain levelFound: ; loop though each byte and copy it to the position in curLevel if it doesn't begin with D, E, or F ; HL -> current byte in the compressed level ; DE -> current byte in the uncompressed level ld DE, curLevel ll_nextByte: ld A, (HL) or A ; if the current byte is $00, we have reached the next level, so we're done (if the level is the last one, this works since (blank)=0) jr z, ll_setPlayerPos and $F0 ; test the high nibble cp $D0 jr z, ll_decompressWall cp $E0 jr z, ll_decompressLongBlank cp $F0 jr z, ll_decompressBlank ; here, it wasn't a compressed tile, so just copy it over ldi ; this decrements BC, but I don't care about that since i'm not using BC jr ll_nextByte ll_decompressWall: ld A, (HL) and $0F ; now look at how many we need to copy inc A ; minumum of 1 ld B, A ld A, $11 jr ll_decompress ll_decompressLongBlank: ld A, (HL) and $0F add A, $11 ; minimum of 17 ld B, A ld A, $00 ; fill with blanks jr ll_decompress ll_decompressBlank: ld A, (HL) and $0F inc A ; minimum of 1 ld B, A ld A, $00 ; fill with blanks jr ll_decompress ; inputs: A = byte to fill, B = length to fill, DE = location to begin fill ; outputs: HL = HL + 1, DE = DE + B ll_decompress: ld (DE), A inc DE djnz ll_decompress inc HL jr ll_nextByte ll_setPlayerPos: ld HL, curLevel dec (HL) ; since 1-based ld A, (HL) ; A = playerX ld (playerX), A inc HL dec (HL) ; since 1-based ld A, (HL) ; A = playerY & facing bit ld B, A and %01111111 ; ignore the highest bit (the facing bit) ld (playerY), A ld A, B ; now set the high-order bit of playerFlags to the facing bit and %10000000 ld (playerFlags), A ; note: this sets all other flag bits to zero, which is ok because this is the beginning of the level ret ; ascii = charTable(getCSC() - $0B) ; length = 37 bytes charTable: .db "WRMH",0 .db 0,0,0,"VQLG",0,0 .db ".ZUPKFC",0, .db " YTOJEB",0,0 .db "XSNIDA" text_level: .db "Level ",0 text_pass: .db "Pass:",0 text_restart: .db "Restart",0 text_mainmenu: .db "Main Menu", 0 text_back: .db "Back", 0 text_gamewon .db "You Won!!!", 0 passwords: ; 5 bytes / level, but I don't think it's worth compressing ; Sorry, but for the purposes of this release I am not including the full ; passwords. .db "JXXXX" ; 1 .db "BXXXX" ; 2 .db "KXXXX" ; contributed-1 .db "PXXXX" ; 3 .db "HXXXX" ; contributed-2 .db "RXXXX" ; 4 .db "OXXXX" ; 5 .db "UXXXX" ; 6 .db "HXXXX" ; 7 .db "IXXXX" ; 8 .db "YXXXX" ; 9 .db "AXXXX" ; 10 .db "HXXXY" ; 11 ;----------------------- ;level tilemaps: ;max size: 26 wide x 12 high ;tiles: ; 0 = blank ; 1 = wall ; 2 = metal wall ; 3 = block ; 4 = push block ; 5 = bomb ; 6 = gold bomb ; 7 = spikes ; 8 = door ; 9 = ladder ; --- In active level---- ; A = block_ladder ; B = bomb_ladder ; C = goldbomb_ladder ; D = pushblock_ladder ; ; ; ---- In compressed levels ---- ; D = wall signal ; E = blank signal (long run) ; F = blank signal (short run) ; ; Compressed level data format: ; - if the high order nibble of a byte is D, then the low order nibble of that byte ; represents the number of $11 bytes in a row (up to 16) ; (i.e. $DA should be decompressed as $11, $11, $11, $11, $11, $11, $11, $11, $11, $11) ; - if the byte begins with E, then the low nibble represents the number of $00 tiles in a row MINUS 16 ; (i.e. $E2 ==> $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ($00 18 times)) ; - if the byte begins with F, then the low nibble represents the number of $00 tiles in a row ; (i.e. $F3 ==> $00, $00, $00) ; ; Note that this means that $00 and $11 will never appear in a tile map, since $F0 = $00 and $D0 = $11. ; Therefore, $00 is the first byte of all levels so that I can find them (since they aren't a constant size) ; However, $11 can appear as the x-pos or y-pos. $00 cannot because I made the minimum be 1 for each. ; ;----------------------- levelData: ; level 1 .db $00; Level marker .db 25, 11; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $EF, $E9, $D0, $10,$F8, $D1, $F0, $01,$D0, $F6, $01,$F2, $01,$F0, $D0, $F0, $01,$10 .db $F1, $01,$D0, $01,$10,$F0, $01,$F1, $D0, $10,$02 .db $F1, $10,$01,$F0, $D0, $F0, $01,$F3, $02 .db $D1, $F0, $01,$F0, $01,$10,$01,$50,$F0, $10,$F0, $02 .db $10,$F0, $80,$F2, $10,$01,$50,$03,$10,$30,$02 .db $DB, $12 .db $DB, $12 .db $EF, $EF, $EF, $EF, $EB ; level 2 .db $00; Level marker .db 24, 24; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $EF, $EF, $EF, $FC, $01,$D4, $10,$F0, $01,$D3, $10,$F4, $01,$F0, $10,$FA, $10 .db $10,$FA, $01 .db $10,$FA, $01 .db $10,$FA, $01 .db $10,$FA, $01 .db $10,$F1, $01,$D0, $F1, $08,$F3, $01 .db $10,$F1, $09,$01,$D2, $17,$77,$77,$77,$71 .db $13,$33,$15,$09,$01,$DA, $19,$01,$D1, $F1, $D0, $F1, $D0, $10,$F1, $09,$F0, $D0, $F5, $01 .db $10,$F1, $09,$F0, $10,$F4, $90,$01 .db $10,$77,$77,$79,$F0, $10,$F0, $01,$90,$F0, $01,$90,$01 .db $17,$D1, $19,$F0, $13,$F0, $01,$90,$F0, $31,$90,$01 .db $D2, $10,$F0, $15,$50,$01,$90,$F0, $31,$90,$01 .db $DC, ; contributed level 2 .db $00; Level marker .db 24, 19; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $EF, $E9, $80,$99,$99,$99,$90,$F6, $02,$20,$99,$99,$99,$90,$F6, $D0, $20,$F2, $90,$F5, $01,$D0, $20,$F2, $90,$F5, $D1, $F3, $40,$40,$90 .db $F2, $01,$D1, $F2, $02,$22,$22,$90 .db $F2, $D2, $33,$F3, $09,$90 .db $F1, $01,$D3, $10,$F2, $99,$90 .db $30,$F9, $99,$F0, $30,$F9, $90,$F0, $D1, $10,$F5, $40,$90,$90,$F0, $D2, $F4, $02,$22,$90,$90,$F0, $D2, $10,$F5, $99,$90,$F0, $D3, $F5, $99,$90,$F0, $D3, $10,$F4, $09,$F1, $D4, $F4, $09,$D6, $10,$F3, $09,$F1, $D5, $F3, $09,$F1, $D5, $10,$F4, $03 .db $D6, $F1, $D1, $10,$D0, $77,$77,$77,$77,$77,$77,$77,$77,$77,$77,$77,$77,$77 ; level 3 .db $00; Level marker .db 3, 129; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $D0, $F0, $10,$F6, $10,$F0, $01 .db $D0, $10,$10,$F6, $14,$F0, $01 .db $10,$10,$F0, $50,$F2, $D3, $10,$01 .db $10,$10,$05,$50,$07,$50,$01,$10,$F2, $10,$01 .db $10,$D2, $10,$D1, $F1, $D1, $10,$01 .db $10,$F0, $01,$F0, $10,$10,$F2, $10,$F1, $01 .db $D0, $10,$01,$D0, $10,$D3, $10,$F1, $01 .db $10,$10,$F9, $01 .db $10,$10,$F2, $04,$F3, $03,$F0, $01 .db $10,$20,$F1, $01,$D0, $10,$F2, $03,$30,$01 .db $18,$20,$04,$F0, $01,$F0, $10,$F0, $40,$03,$03,$D2, $21,$DA, $22,$21,$DF, $D7, $EF, $EF, $EF, $EF, $FE, ; contributed level 1 .db $00; Level marker .db 25, 24; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $E0, $04,$04,$90,$F9, $01,$D0, $90,$F0, $51,$09,$30,$F4, $01,$F0, $01,$90,$01,$D0, $09,$33,$F1, $50,$F1, $D0, $F0, $41,$90,$D0, $F0, $79,$D0, $19,$F0, $55,$F0, $01,$D0, $01,$D1, $10,$F2, $09,$F0, $D3, $01,$D1, $10,$F2, $09,$09 .db $77,$77,$77,$77,$77,$77,$77,$77,$77,$77,$77,$D0, $19 .db $FB, $09 .db $99,$99,$99,$99,$99,$99,$99,$F4, $09 .db $99,$99,$99,$99,$99,$99,$99,$F4, $09 .db $90,$FA, $09 .db $90,$FA, $39 .db $90,$F8, $09,$03,$39 .db $90,$F2, $10,$F4, $09,$D0, $19 .db $90,$F2, $D0, $F4, $09,$F1, $90,$F2, $D0, $10,$F3, $09,$F1, $10,$F2, $20,$D0, $F3, $09,$F1, $D0, $F2, $20,$01,$10,$F2, $09,$F1, $D0, $F2, $20,$F0, $D0, $F2, $09,$F0, $30 .db $01,$D0, $10,$F0, $25,$F1, $10,$F1, $09,$F0, $10 .db $81,$D1, $F0, $25,$50,$F0, $10,$F1, $09,$D0, $10 .db $D2, $03,$21,$D2, $10,$30,$09,$F1, $D3, $10,$F3, $30,$09,$F1, $77,$77,$77,$77,$77,$77,$77,$77,$77,$D3, ; level 4 .db $00; Level marker .db 26, 4; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $E0, $01,$D3, $F0, $01,$D1, $F1, $D3, $F0, $01,$D5, $01,$10,$F7, $10,$F0, $D1, $F8, $10,$D1, $10,$F5, $D2, $10,$10 .db $D0, $10,$F8, $10,$10 .db $D0, $F9, $10,$D0, $01,$33,$F0, $43,$30,$F0, $D0, $10,$F0, $01,$F1, $02 .db $01,$D0, $10,$D1, $F0, $D0, $13,$30,$01,$F0, $05,$02 .db $01,$D0, $17,$D1, $F0, $D1, $13,$01,$F0, $33,$02 .db $01,$D3, $77,$D6, $01,$DB, $01,$DB, $F2, $01,$F3, $D0, $F1, $01,$01 .db $F2, $01,$10,$F0, $D1, $10,$F2, $D0, $10,$F2, $D1, $10,$10,$10,$F0, $01,$F0, $01 .db $D0, $F5, $10,$10,$F0, $02,$F0, $01 .db $01,$05,$30,$F3, $10,$10,$30,$02,$F0, $01 .db $01,$55,$30,$F3, $10,$13,$33,$32,$F0, $01 .db $01,$D6, $22,$22,$22,$F0, $01 .db $F9, $01,$F0, $01 .db $F9, $01,$F0, $01 .db $F9, $01,$F0, $81 .db $DC, ; level 5 .db $00; Level marker .db 2, 143; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $F2, $D3, $F7, $01,$F3, $10,$F6, $20,$04,$50,$F1, $01,$D0, $10,$F3, $01,$20,$01,$D0, $F3, $01,$10,$F2, $10,$20,$F0, $01,$10,$F3, $01,$10,$F0, $01,$F0, $20,$F0, $01,$D2, $F2, $01,$10 .db $10,$F2, $01,$F0, $D0, $F0, $10,$F2, $01 .db $10,$F0, $21,$70,$71,$F0, $10,$F0, $01,$F2, $01 .db $10,$F2, $01,$05,$10,$55,$F0, $10,$F1, $01 .db $22,$22,$F0, $57,$01,$55,$10,$55,$50,$01,$F1, $01 .db $10,$02,$22,$21,$D5, $10,$09,$01 .db $10,$F1, $21,$D6, $19,$01 .db $10,$F1, $01,$10,$F5, $09,$01 .db $10,$F9, $09,$01 .db $10,$30,$F8, $09,$01 .db $D0, $10,$F8, $09,$01 .db $D0, $10,$F8, $09,$01 .db $D0, $10,$F5, $50,$F1, $09,$01 .db $D0, $10,$F4, $55,$50,$F1, $09,$01 .db $D0, $10,$F0, $30,$F0, $03,$02,$22,$20,$F1, $09,$01 .db $D0, $10,$03,$30,$F0, $01,$02,$F0, $20,$F1, $09,$01 .db $D0, $10,$D0, $10,$01,$10,$01,$80,$10,$F1, $09,$01 .db $D0, $10,$F3, $71,$17,$17,$77,$F0, $09,$01 .db $D0, $17,$77,$77,$77,$77,$DF, $D3, ; level 6 .db $00; Level marker .db 25, 24; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $F5, $D4, $F6, $01,$10,$F1, $31,$F0, $D0, $F5, $D0, $F2, $51,$F1, $10 .db $F4, $10,$F1, $05,$51,$F1, $01 .db $F3, $01,$10,$F1, $55,$31,$90,$F0, $01 .db $F3, $D0, $F1, $D2, $90,$F0, $01 .db $F2, $01,$19,$01,$D0, $10,$F0, $01,$90,$F0, $01 .db $F0, $D0, $F0, $01,$09,$D0, $F2, $01,$91,$D1, $01,$F0, $10,$01,$09,$10,$F2, $01,$90,$F0, $01 .db $10,$F0, $10,$01,$09,$10,$F2, $01,$90,$F0, $01 .db $19,$09,$01,$01,$09,$10,$F2, $01,$90,$F0, $01 .db $19,$19,$F0, $D0, $09,$10,$F0, $90,$F0, $01,$90,$F0, $01 .db $19,$19,$F0, $01,$09,$D0, $10,$90,$F0, $01,$90,$F0, $01 .db $19,$19,$F0, $01,$09,$F0, $01,$90,$F0, $01,$90,$F0, $01 .db $19,$19,$F1, $19,$F0, $01,$90,$F0, $01,$90,$F0, $01 .db $19,$19,$F1, $D0, $F0, $01,$D2, $F1, $01 .db $19,$10,$F1, $01,$10,$F2, $22,$F1, $01 .db $19,$01,$10,$F1, $D0, $F3, $D1, $10 .db $10,$F0, $01,$F1, $01,$30,$F5, $10,$F0, $01,$10,$F1, $D1, $F2, $01,$F0, $10,$F0, $04,$D0, $F2, $01,$50,$F2, $D0, $10,$F0, $55,$10,$10,$F2, $22,$22,$F1, $01 .db $10,$50,$45,$10,$02,$F4, $20,$F0, $01 .db $10,$40,$55,$18,$02,$F4, $25,$F0, $01 .db $DB, $10 ; level 7 .db $00; Level marker .db 5, 131; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $10,$F2, $10,$F2, $10,$F1, $01,$F0, $10,$06,$F1, $10,$F2, $10,$F1, $01,$F0, $10,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$01,$F0, $DA, $01,$F0, $10,$F6, $01,$F2, $10 .db $10,$01,$D0, $10,$F1, $01,$D0, $01,$F2, $01 .db $D1, $F0, $D0, $10,$01,$D0, $01,$D0, $F2, $01 .db $10,$F2, $D1, $F5, $01 .db $10,$FA, $01 .db $10,$F0, $10,$F4, $01,$D1, $01,$D0, $15,$F0, $10,$F0, $04,$F1, $44,$01,$90,$F1, $01 .db $D0, $01,$D0, $07,$D0, $10,$D2, $91,$D0, $01,$01 .db $D0, $71,$D0, $71,$D0, $17,$D2, $91,$D0, $01,$01 .db $D8, $90,$F1, $01 .db $D6, $F0, $01,$90,$F1, $01 .db $D0, $F6, $01,$90,$07,$70,$01 .db $D0, $F6, $01,$97,$71,$17,$01 .db $D3, $10,$F2, $01,$01,$D1, $01 .db $10,$F2, $D0, $F2, $01,$99,$99,$19,$91 .db $10,$F2, $01,$D0, $F1, $01,$F0, $09,$09,$91 .db $13,$F1, $10,$F0, $01,$D0, $F0, $01,$77,$77,$77,$01 .db $13,$30,$F0, $D0, $F1, $01,$D2, $19,$99,$01 .db $13,$33,$F0, $D0, $10,$F2, $09,$99,$99,$F0, $01 .db $18,$63,$30,$D1, $F2, $07,$77,$77,$77,$71 .db $DC, ; new 3 (level 7.5) .db $00; Level marker .db 2, 130; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $D9, $F2, $10,$91,$F3, $01,$F0, $01,$91,$F2, $D0, $91,$55,$55,$90,$F0, $01,$D1, $91,$F2, $19,$91,$D1, $91,$F1, $01,$D0, $91,$F2, $19,$D0, $10, .db $F0, $91,$F2, $D0, $91,$F2, $19,$10,$19,$F0, $91,$F2, $01,$91,$F2, $19,$D0, $19,$D1, $F2, $01,$91,$F2, $19,$09,$19,$15,$55,$50,$F0, $D1, $91,$F2, .db $19,$09,$19,$15,$55,$55,$01,$D1, $91,$F2, $D0, $19,$09,$D5, $91,$F2, $19,$09,$09,$D1, $F1, $01,$D0, $91,$F2, $19,$09,$19,$D1, $10,$F1, $D0, $91, .db $F2, $19,$D4, $F1, $01,$91,$F2, $19,$09,$99,$99,$99,$D3, $91,$F2, $19,$F2, $09,$10,$09,$F1, $91,$F2, $D0, $10,$F1, $09,$10,$09,$F1, $91,$F0, $D0, $10 .db $F0, $D2, $19,$D0, $19,$D3, $10,$10 .db $D3, $19,$D0, $19,$F3, $10,$10 .db $10,$F2, $09,$D1, $F3, $10,$10 .db $10,$F2, $09,$D1, $F3, $18,$10 .db $10,$06,$F2, $D6, $10 .db $10,$04,$F0, $35,$01,$D6, $10 .db $DB, $10 .db $DB, $17 .db $DC, ; level 8 .db $00; Level marker .db 5, 142; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $01,$D8, $F2, $10,$F8, $D0, $F1, $19,$F9, $D0, $10 .db $19,$D5, $F4, $01 .db $19,$F5, $D1, $F2, $01 .db $19,$F3, $50,$F2, $D1, $01,$01 .db $19,$F3, $D0, $10,$F4, $D0, $19,$F1, $30,$F7, $01 .db $19,$F0, $01,$10,$F7, $01 .db $19,$F1, $09,$F1, $30,$F4, $01 .db $19,$F1, $09,$F0, $05,$43,$F4, $01 .db $19,$01,$D0, $19,$D2, $10,$F3, $01 .db $19,$01,$F0, $09,$F7, $81 .db $19,$01,$F0, $09,$F5, $D0, $10,$D0, $19,$01,$10,$09,$09,$F3, $09,$F1, $01 .db $19,$01,$D0, $19,$09,$D3, $19,$F1, $01 .db $19,$01,$10,$09,$09,$03,$30,$F1, $09,$F0, $01,$D0, $19,$02,$17,$77,$09,$03,$33,$F1, $09,$F1, $01 .db $19,$02,$D1, $09,$03,$53,$F1, $09,$F1, $31 .db $19,$02,$D1, $F0, $03,$33,$F1, $09,$F0, $03,$31 .db $19,$02,$D0, $20,$F0, $D2, $10,$09,$03,$33,$31 .db $19,$02,$D0, $20,$F3, $D0, $19,$D2, $19,$F0, $04,$40,$F4, $09,$F1, $01 .db $D0, $10,$D1, $17,$77,$77,$77,$77,$77,$77,$77,$71 .db $D0, $17,$DA ; level 9 .db $00; Level marker .db 5, 134; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $10,$FA, $01 .db $10,$F8, $09,$F0, $01 .db $13,$F8, $09,$F0, $01 .db $13,$30,$F4, $77,$F1, $09,$F0, $01 .db $13,$30,$F0, $01,$03,$F0, $07,$D0, $70,$F0, $09,$F0, $01 .db $13,$33,$F0, $01,$33,$07,$71,$D0, $17,$74,$09,$F0, $02 .db $D6, $09,$D1, $29,$F0, $02 .db $10,$10,$10,$D0, $F0, $10,$F0, $09,$10,$F0, $29,$F0, $02 .db $10,$10,$10,$01,$F0, $10,$F0, $09,$10,$F0, $19,$33,$62 .db $10,$10,$F0, $01,$F2, $09,$10,$F0, $22,$22,$22 .db $10,$10,$F0, $01,$F1, $77,$77,$16,$F1, $02,$01 .db $10,$F1, $01,$F1, $D1, $13,$30,$F0, $02,$01 .db $10,$F1, $01,$F1, $10,$09,$03,$30,$F0, $02,$01 .db $13,$04,$04,$F0, $40,$40,$10,$09,$22,$22,$22,$22,$01 .db $D2, $10,$D1, $10,$09,$F1, $01,$F0, $01 .db $10,$F5, $09,$F1, $01,$F0, $01 .db $10,$F3, $01,$09,$09,$F1, $01,$F0, $01 .db $10,$F3, $02,$19,$09,$F1, $02,$22,$21 .db $10,$F3, $32,$19,$09,$F1, $01,$F0, $21 .db $10,$F2, $03,$32,$19,$09,$F1, $01,$08,$21 .db $10,$F2, $03,$32,$19,$F2, $22,$22,$22 .db $13,$F0, $30,$30,$03,$12,$10,$F2, $21,$F0, $01 .db $D0, $30,$30,$30,$33,$12,$10,$F2, $21,$F0, $01 .db $D0, $10,$30,$40,$41,$12,$10,$F1, $33,$21,$F0, $01 .db $D4, $22,$21,$D1, $12,$21,$D1 ; New level - #10 - gigantic, scary, beatable? .db $00; Level marker .db 3, 130; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $01,$D1, $81,$15,$55,$55,$50,$F3, $28 .db $01,$04,$01,$D0, $15,$55,$55,$50,$F0, $07,$77,$77,$D2, $F0, $15,$55,$55,$55,$55,$05,$01,$D2, .db $F2, $15,$55,$55,$55,$55,$55,$05,$55,$55,$D0, $F0, $D1, $15,$55,$55,$55,$55,$55,$05,$55,$55,$D0, $F1, $01,$D2, .db $15,$55,$55,$95,$55,$55,$D1, $01,$01,$55,$55,$50,$05,$55,$55,$95,$55,$55,$D0, $F0, $71,$01,$55,$55,$59,$05,$55,$55,$95,$D2, .db $10,$D0, $01,$D1, $19,$05,$55,$55,$95,$55,$51,$D0, $F1, $01,$F1, $09,$05,$55,$55,$55,$55,$51,$D0, $09,$04,$01,$F1, $09,$05,$55,$55, .db $55,$55,$51,$D0, $19,$D0, $12,$D1, $09,$D6, $19,$F0, $82,$F0, $10,$09,$10,$90,$F0, $13,$20,$23,$10 .db $19,$F0, $12,$09,$10,$09,$F0, $90,$F0, $13,$28,$23,$10 .db $19,$F1, $09,$17,$71,$D0, $90,$01,$13,$21,$23,$10 .db $10,$F1, $09,$D1, $04,$90,$01,$33,$33,$33,$10 .db $10,$F1, $09,$F1, $04,$90,$01,$33,$33,$33,$10 .db $10,$90,$F0, $09,$F1, $04,$F0, $D3, $10 .db $10,$90,$F0, $09,$F1, $01,$10,$01,$33,$33,$33,$10 .db $10,$90,$F5, $01,$33,$33,$33,$10 .db $10,$93,$F5, $01,$33,$33,$33,$10 .db $F0, $91,$D0, $10,$F3, $01,$13,$33,$33,$10 .db $F0, $01,$F0, $D0, $60,$51,$77,$77,$71,$17,$77,$77,$17 .db $77,$71,$F0, $01,$66,$61,$D8, $F0, $01,$D1, $F6, ; Title level .db $00; Level marker .db 5, 2; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $D1, $01,$F9, $15,$F0, $01,$F9, $D2, $F9, $10,$FB, $10,$FB, $10,$FB, $10,$FB, $D5, $EF, $EF, $EF, $EF, $EF, $EF, $EF, $F3 ; Game won level - back and forth falling with spikes .db $00; Level marker .db 8, 129; player xpos, ypos (note: the highest bit of ypos is 1 if he is facing right) .db $D2, $F0, $01,$D3, $10,$F2, $D2, $F0, $01,$D3, $10,$F2, $D2, $F0, $01,$D3, $10,$F2, $D2, $F0, $01,$D3, $10,$F2, $D2, $F0, $01,$D3, .db $10,$F2, $D2, $70,$F0, $D3, $10,$F2, $D2, $17,$F0, $01,$D2, $10,$F2, $D3, $70,$F0, $D2, $10,$F2, $D3, $17,$F0, $D2, $10,$F2, $D3, $10,$F0, .db $D2, $10,$F2, $D3, $F0, $07,$D2, $10,$F2, $D2, $10,$F0, $71,$D2, $10,$F2, $D2, $F0, $07,$D3, $10,$F2, $D2, $F0, $71,$D3, $10,$F2, $D2, $F0, .db $01,$D3, $10,$F2, $D2, $70,$F0, $D3, $10,$F2, $D2, $17,$F0, $01,$D2, $10,$F2, $D3, $70,$F0, $D2, $10,$F2, $D3, $17,$F0, $D2, $10,$F2, $D3, .db $10,$F0, $D2, $10,$F2, $D3, $F0, $07,$D2, $10,$F2, $D2, $10,$F0, $71,$D2, $10,$F2, $D2, $F0, $07,$D3, $10,$F2, $D2, $F0, $71,$D3, $10,$F2, .db $D2, $F0, $01,$D3, $10,$F2, ;--------------------------------- ; Tiles: 8x8 = 8 bytes each ; ; A few have labels, some don't ; The ones that don't are only drawn as part of the tilemap ;--------------------------------- tiles: ;(each tile is 8x8 = 8 bytes) ; blank = 0 .db $00, $00, $00, $00, $00, $00, $00, $00 ; wall = 1 .db %11111111 .db %00010001 .db %11111111 .db %01000100 .db %11111111 .db %00010001 .db %11111111 .db %01000100 ;metal wall = 2 .db %11111111 .db %10100101 .db %11111111 .db %10111101 .db %10111101 .db %11111111 .db %10100101 .db %11111111 tBlock: ; block = 3 .db %11111111 .db %10000001 .db %10000001 .db %10000001 .db %10000001 .db %10000001 .db %10000001 .db %11111111 ; pushblock = 4 .db %11111111 .db %10000001 .db %10111001 .db %10100101 .db %10111001 .db %10100001 .db %10000001 .db %11111111 tBomb: ; bomb = 5 .db %00001110 .db %00010000 .db %00111100 .db %01110110 .db %11111011 .db %11111111 .db %01111110 .db %00111100 tGoldbomb: ; goldbomb = 6 .db %01110000 .db %00001000 .db %00111100 .db %01000010 .db %11111111 .db %10000001 .db %01000010 .db %00111100 ; spikes = 7 .db %00000000 .db %00100000 .db %00100010 .db %10100010 .db %10101010 .db %10101010 .db %11111111 .db %11111111 ; door = 8 .db %00111100 .db %01000010 .db %01000010 .db %01000010 .db %01001010 .db %01001010 .db %01000010 .db %01000010 tLadder: ; ladder = 9 .db %01000010 .db %01111110 .db %01000010 .db %01111110 .db %01000010 .db %01111110 .db %01000010 .db %01111110 ; the following tiles are actually the ladder picture combined with the item ; it's not the most elegant solution, but I don't really care...it's only a few dozen extra bytes (of data, and some convoluted code...) tBlock_Ladder: ; block + ladder = A .db %11111111 .db %11111111 .db %11000011 .db %11111111 .db %11000011 .db %11111111 .db %11111111 .db %11111111 tPushBlock_Ladder: ; pushblock + ladder = B .db %11111111 .db %11000011 .db %11111011 .db %11100111 .db %11111011 .db %11100011 .db %11000011 .db %11111111 tBomb_Ladder: ; bomb + ladder = C .db %01001110 .db %01010010 .db %01111110 .db %01110110 .db %11111011 .db %11111111 .db %01111110 .db %01111110 tGoldBomb_Ladder: ; goldbomb + ladder = D .db %01110010 .db %01001010 .db %01111110 .db %01000010 .db %11111111 .db %10000001 .db %01000010 .db %01111110 ; the following tiles are never part of the tilemap tPlayerleft: .db %00011100 .db %01111110 .db %00010010 .db %00100010 .db %00010100 .db %00101010 .db %00001000 .db %00010100 tPlayerright: .db %00111000 .db %01111110 .db %01001000 .db %01000100 .db %00101000 .db %01010100 .db %00010000 .db %00101000 tPlayerClimbingLadder: .db %01011110 .db %01111110 .db %01100010 .db %01100010 .db %01010110 .db %01111110 .db %01001010 .db %01111110 tPlayerLeft_Ladder: .db %01011110 .db %01111110 .db %01010010 .db %01100010 .db %01010110 .db %01111110 .db %01001010 .db %01111110 tPlayerRight_Ladder: .db %01111010 .db %01111110 .db %01001010 .db %01000110 .db %01101010 .db %01111110 .db %01010010 .db %01111110 ; I could save about 150 bytes by compressing these, but I don't want to... ;"By Justin Shaler" ;5 bytes wide, 14 px high ;x = 8, y = 33 menu_author: .db $18,$00,$80,$20,$00,$14,$00,$80,$24,$00,$19,$40,$A9,$70,$C0,$15 .db $40,$AA,$24,$A0,$15,$42,$A9,$24,$A0,$18,$C1,$1A,$12,$A0,$00,$40 .db $00,$00,$00,$00,$80,$00,$00,$00,$00,$06,$81,$80,$00,$00,$08,$80 .db $80,$00,$00,$04,$C6,$8D,$60,$00,$02,$AA,$95,$80,$00,$02,$AA,$99 .db $00,$00,$0C,$A6,$4D,$00 ;6 bytes wide, 56 px high ;x = 48, y = 0 menu_choices: .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$1F,$FF,$FF,$FF .db $FF,$F8,$20,$00,$00,$00,$00,$04,$20,$00,$00,$00,$00,$04,$20,$00 .db $00,$00,$00,$04,$2F,$C0,$00,$00,$02,$24,$2F,$C0,$00,$00,$02,$74 .db $23,$00,$00,$00,$02,$74,$23,$2C,$EE,$71,$8E,$74,$23,$31,$29,$4A .db $52,$24,$23,$21,$29,$4B,$D2,$24,$23,$21,$29,$4A,$12,$04,$23,$20 .db $EE,$71,$CE,$24,$20,$00,$08,$40,$00,$04,$20,$00,$08,$40,$00,$04 .db $1F,$FF,$FF,$FF,$FF,$F8,$20,$00,$00,$00,$00,$04,$40,$00,$00,$00 .db $00,$02,$40,$00,$00,$00,$00,$02,$40,$00,$00,$00,$00,$02,$43,$20 .db $00,$70,$00,$02,$43,$20,$00,$88,$00,$02,$42,$A4,$A8,$83,$3C,$42 .db $42,$AA,$A8,$98,$AA,$A2,$42,$68,$50,$8A,$AA,$82,$42,$66,$50,$79 .db $AA,$62,$40,$00,$00,$00,$00,$02,$40,$00,$00,$00,$00,$02,$40,$00 .db $00,$00,$00,$02,$40,$00,$00,$00,$00,$02,$41,$C0,$00,$00,$00,$42 .db $41,$20,$00,$00,$00,$42,$41,$2C,$66,$A9,$99,$C2,$41,$C2,$88,$AA .db $52,$42,$41,$0A,$22,$52,$52,$42,$41,$06,$CC,$51,$91,$C2,$40,$00 .db $00,$00,$00,$02,$40,$00,$00,$00,$00,$02,$40,$00,$00,$00,$00,$02 .db $40,$00,$00,$00,$00,$02,$40,$00,$3C,$10,$00,$02,$40,$00,$20,$04 .db $00,$02,$40,$00,$39,$56,$00,$02,$40,$00,$20,$94,$00,$02,$40,$00 .db $20,$94,$00,$02,$40,$00,$3D,$52,$00,$02,$40,$00,$00,$00,$00,$02 .db $40,$00,$00,$00,$00,$02,$40,$00,$00,$00,$00,$02,$40,$00,$00,$00 .db $00,$02,$20,$00,$00,$00,$00,$04,$1F,$FF,$FF,$FF,$FF,$F8,$00,$00 .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; 10 bytes wide, 21 px high ; x = 12, y = 29 pass_box: .db $7F,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FC,$80,$00,$00,$00,$00,$00 .db $00,$00,$00,$02,$80,$00,$00,$00,$00,$00,$00,$00,$00,$02,$80,$00 .db $00,$00,$00,$00,$00,$00,$00,$02,$80,$00,$00,$00,$00,$00,$00,$00 .db $00,$02,$80,$00,$00,$00,$00,$00,$00,$00,$00,$02,$80,$00,$00,$00 .db $00,$00,$00,$00,$00,$02,$9F,$00,$00,$00,$00,$00,$00,$00,$00,$02 .db $90,$80,$00,$00,$00,$00,$00,$00,$00,$02,$90,$98,$E7,$00,$00,$00 .db $00,$00,$00,$02,$9F,$05,$08,$40,$00,$00,$00,$00,$00,$02,$90,$1C .db $C6,$00,$00,$00,$00,$00,$00,$02,$90,$24,$21,$00,$00,$00,$00,$00 .db $00,$02,$90,$1D,$CE,$40,$00,$00,$00,$00,$00,$02,$80,$00,$00,$00 .db $00,$00,$00,$00,$00,$02,$80,$00,$00,$00,$00,$00,$00,$00,$00,$02 .db $80,$00,$00,$00,$00,$00,$00,$00,$00,$02,$80,$00,$00,$00,$00,$00 .db $00,$00,$00,$02,$80,$00,$00,$00,$00,$00,$00,$00,$00,$02,$80,$00 .db $00,$00,$00,$00,$00,$00,$00,$02,$7F,$FF,$FF,$FF,$FF,$FF,$FF,$FF .db $FF,$FC .end .end