;BomberMan 0.03 on july-december 1998 By Eric Piel #include CRASH82.INC .db "BomberMan",6,"MultiPlayer",0 GameTimer = OP1 ;66 bytes for all OPX ;SubGameTimer = GameTimer + 1 ; PlayerWorking = GameTimer + 2 PlayersState = GameTimer + 3 ;Each set bit signifies BBM is dead BBM1Y = GameTimer + 4 BBM1X = BBM1Y + 1 BBM1Dir = BBM1Y + 2 BBM1BombTimer = BBM1Y + 3 ;NmbOfBombPosible ;NmbOfBombCurrent BBM1BombY = BBM1Y + 5 ;Expantion BBM2Y = BBM1Y + 7 BBM2X = BBM2Y + 1 BBM2Dir = BBM2Y + 2 BBM2BombTimer = BBM2Y + 3 BBM2BombY = BBM2Y + 5 BATStart = TEXT_MEM ;Bomb Allocation Table BombY = 0 BombX = 1 BombTimer = 2 BombBBMNumb = 3 LvlBuf = TEXT_MEM2 ; res 3,(IY+$05) ;;;Black on White ld hl,GRAPH_MEM ld de,OP1 ld bc,66 ldir ;reset all variable call CR_GRBCopy ;Fast clear ld bc,96 ld de,LvlBuf ld hl,Lvl1 ldir ld de,0 ;bc ld hl,LvlBuf DrwLvl: ld a,(hl) push de push hl cp 80h jr z,DrwDBlock cp 90h jr z,DrwUBlock cp 50h jr nz,NextPieceLvl DrwMask: ld hl,SprMask jr DrwPieceLvl DrwDBlock: ld hl,SprDBlock jr DrwPieceLvl DrwUBlock: ld hl,SprUBlock DrwPieceLvl: ld a,d call QuickAlignedDrw NextPieceLvl: pop hl pop de ld a,e inc a cp 12 jr nz,NoAddD ld a,d add a,8 cp 64 jr z,FinDrwLvl ld d,a xor a NoAddD: ld e,a inc hl jr DrwLvl FinDrwLvl: xor a ld e,a ld hl,SprBBM1 call DRWSPR ld hl,(88 * 100h)+56 ld (BBM2Y),hl ld a,h ld e,l ld hl,SprBBM2 call DRWSPR call CR_GRBCopy DebBoucleJeu: ld ix,BBM1Dir ld hl,PlayerWorking ld (hl),%00000001 call TestKey call DeplacBBM call BombTime ld ix,BBM2Dir ld hl,PlayerWorking ld (hl),%00000010 call TestKey call DeplacBBM call BombTime halt ;BBM3 halt ;BBM4 ld a,(PlayersState) or a jr nz,EndKill ExitTestKey: ld a,%10111111 out ($01),a ; Mask out Y= Mode in A,($01) bit 4,a ret z AddTime: ld hl,(GameTimer) ;ld hl,(SubGameTimer) inc hl ld (GameTimer),hl ld a,l or h ;if h=0 and l=0 : (l or h)=0 jr z,EndTime ContTime: jr DebBoucleJeu EndTime: ld hl,$0403 ld (CURSOR_POS),hl ld hl,SZEndTime ROM_CALL(D_ZT_STR) KeyEndGame: call CR_KHAND cp K_YEDIT jr nz,KeyEndGame ret EndKill: ld hl,$0403 ld (CURSOR_POS),hl ld hl,SZEndKill ROM_CALL(D_ZT_STR) ld hl,$0704 ;; ld (CURSOR_POS),hl ld a,3 ld hl,(PlayersState) ;just l sub l ld l,a ld h,0 ROM_CALL(D_HL_DECI) ld hl,$0404 ;; ld (CURSOR_POS),hl ld hl,SZPlayer ROM_CALL(D_ZT_STR) jr KeyEndGame ;ix=BBMDir BombBBM: ;;must add the 10h block in the level ld a,(ix) ;dpl? or a ret nz ld a,(ix + 1) ;BBMBombTimer or a ret nz ld (ix + 1),0B0h ;FFh ld e,(ix - 2) ;BBMPos ld a,(ix - 1) ld (ix + 3),e ;BBMBombPos ld (ix + 4),a push af push de ld h,a ld l,e call GetAddressInLvl ld a,(PlayerWorking) or 10h ld (hl),a pop de pop af ld hl,SprBomb call DRWSPR call CR_GRBCopy ret ExpendBomb: ld a,40h + 28 ;scroll the screen to simulate explosion out (P_LCD_CON),a ld l,(ix + 3) ;BBMBombPos ;to destroy the bomb itself ld h,(ix + 4) ;A bug : the bomb will rexplose infinitly (avoid by \/?) ;;;;; push hl call GetAddressInLvl ld (hl),40h pop hl ;;;;; call SetFireInSquare ; ld a,40h - 12 + 64 ;scroll the screen to simulate explosion ; out ($10),a ;Should be a routine ld l,(ix + 3) ;BBMBombPos ld h,(ix + 4) ld b,2 ExpdBb1Up: ld a,l or a jr z,ExpdBb1DownIni ;Out of the screen sub 8 ld l,a push bc push hl call SetFireInSquare pop hl pop bc or a jr z,ExpdBb1DownIni djnz ExpdBb1Up ExpdBb1DownIni: ; ld a,40h + 4 ;scroll the screen to simulate explosion ; out ($10),a ld l,(ix + 3) ;BBMBombPos ;ld hl,(BBM1BombY) ld h,(ix + 4) ld b,2 ExpdBb1Down: ld a,l cp 56 jr z,ExpdBb1LeftIni ;Out of the screen add a,8 ld l,a push bc push hl call SetFireInSquare pop hl pop bc or a jr z,ExpdBb1LeftIni djnz ExpdBb1Down ExpdBb1LeftIni: ; ld a,40h - 4 +64 ;scroll the screen to simulate explosion ; out ($10),a ld l,(ix + 3) ;BBMBombPos ;ld hl,(BBM1BombY) ld h,(ix + 4) ld b,2 ExpdBb1Left: ld a,h or a jr z,ExpdBb1RightIni ;Out of the screen sub 8 ld h,a push bc push hl call SetFireInSquare pop hl pop bc or a jr z,ExpdBb1RightIni djnz ExpdBb1Left ExpdBb1RightIni: ; ld a,40h +2 ;scroll the screen to simulate explosion ; out ($10),a ld l,(ix + 3) ;BBMBombPos ;ld hl,(BBM1BombY) ld h,(ix + 4) ld b,2 ExpdBb1Right: ld a,h cp 88 jr z,ExpdBb1End ;Out of the screen add a,8 ld h,a push bc push hl call SetFireInSquare pop hl pop bc or a jr z,ExpdBb1End djnz ExpdBb1Right ExpdBb1End: call CR_GRBCopy ld a,40h ;reset the screen as it was before the explosion out (P_LCD_CON),a ret ;IN: hl=XY in Graph ;RETURN: a is null if the line of bombing must stop(collision) SetFireInSquare: ;should be a routine ld de,(BBM1Y) ld a,d add a,4 ;check the middle of BBM and %11111000 ;at about 4 ld d,a ld a,e add a,4 and %11111000 ld e,a call CP_HL_DE jr z,BBM1Dead ld de,(BBM2Y) ld a,d add a,4 ;check the middle of BBM and %11111000 ;at about 4 ld d,a ld a,e add a,4 and %11111000 ld e,a call CP_HL_DE jr z,BBM2Dead ;A bug will occure when BBM is moving: If not dead but in square a bit of BBM will be erase. Possible correction : Erase all sprites before drawing fire and redraw them after. ;avoid by \/? push hl call GetAddressInLvl ld a,(hl) pop de ;ld de,hl cp 80h ;DestroyableBlock jr z,EraseBlock cp 90h ;UndestroyableBlock jr z,LineFinished and 0F0h cp 20h jr z,AlreadyFire cp 10h ;The other bomb jr z,ExpendOtherBomb ld a,(PlayerWorking) or 20h ;should be a routine ld (hl),a ld hl,SprFire ld a,d call DRWSPR ;don't need to save ix because it is aligned ld a,1 ;needed? ret AlreadyFire: ld a,(PlayerWorking) or 20h ;should be a routine ld (hl),a ld a,1 ;;a is 20h ret EraseBlock: ld a,(PlayerWorking) or 20h ;should be a routine ld (hl),a ld hl,SprFire ld a,e ld e,d srl e srl e srl e call QuickAlignedDrw LineFinished: xor a ret BBM1Dead: ld a,%00000001 jr BBMDeadEnd BBM2Dead: ld a,%00000010 BBMDeadEnd: ld (PlayersState),a ret ExpendOtherBomb: ld a,(PlayerWorking) push af ;push bc push de ;push hl push ix rrca jr nc,ExpOthBb1 ;;must be for both players ld ix,BBM2Dir ld a,2 ld (PlayerWorking),a call ExpendBomb jr FinExpOthBb ExpOthBb1: ld ix,BBM1Dir ld a,1 ld (PlayerWorking),a call ExpendBomb FinExpOthBb: ;;A Bug : The bomb is sometimes still drawn when we explode it : it is not synchrone ld a,(ix + 1) bit 3,a ;set=> blank (if a<72) jr z,RemoveSprBomb cp 72 jr c,DontNeedToRmvBmb RemoveSprBomb: call BombTFlash+3 DontNeedToRmvBmb: ld (ix + 1),8 ;The BombTime pop ix ;pop hl pop de ;pop bc pop af ld (PlayerWorking),a ld a,1 ;needed? ret EndOfFire: ld hl,LvlBuf ld b,96 ;ld bc,96 ld a,(PlayerWorking) ;should be the bomb number or 20h EOFrRepeatBlock: cp (hl) ;;cpir jr nz,EOFrNextBlock ;jp po,EOFrEnd ;BC=0 EOFrDelBlock: push af ;;ld c,a? push bc push hl ;dec hl ld (hl),40h call ConvLBToGM ld de,SprFire call ALIGN+1 ;address of sprite in de and to write in hl;DRWSPR ;don't need to save ix because it is aligned pop hl pop bc pop af EOFrNextBlock: inc hl djnz EOFrRepeatBlock EOFrEnd: call CR_GRBCopy ret BombTime: ld a,(ix + 1) or a ret z dec a ld (ix + 1),a ;BBMBombTimer ld a,(ix + 1) cp 72 ret nc cp 8 jp z,ExpendBomb ;should be a call ;ret cp 0 jp z,EndOfFire ;should be a call ;ret BombTFlash: and %00000111 ret nz ld hl,SprBomb ld e,(ix + 3) ;BBM1BombY ld a,(ix + 4) ;BBM1BombX call DRWSPR call CR_GRBCopy ret ;ix=BBMDir TestKey: ld a,(PlayerWorking ) ;ld a, BBM1Dir & 0ffh ;LSB of BBM1Dir ;ld de,BBM1Dir ;ld a,e rra ;cp ixl jr nc,CallPK2;jr nz,CallPK2 call PushedKeyBBM1 ;prefer jr jr NextPushed CallPK2: call PushedKeyBBM2 NextPushed: push af bit 6,a call z,BombBBM pop af or (ix) ;Compatible with the current dpl ? rrca jr nc,DownBBM rrca jr nc,LeftBBM rrca jr nc,RightBBM rrca ret c ;jr c,NextTestKey UpBBM: ld a,(ix - 2) or a ret z ;jr z,NextTestKey ld b,%00001110 ;Mask for Down ld de,-8 jr FinTestKey DownBBM: ld a,(ix - 2) cp 56 ret z ;jr z,NextTestKey ld b,%00000111 ;Mask for Up ld de,8 jr FinTestKey LeftBBM: ld a,(ix - 1) or a ret z ;jr z,NextTestKey ld b,%10001011 ;Mask for Right ld de,-800h jr FinTestKey RightBBM: ld a,(ix - 1) cp 88 ret z ;jr z,NextTestKey ld b,%10001101 ;Mask for Left ld de,800h FinTestKey: call IsCasePossible ld (ix),b ret ;ix=BBMDir b=FutureDpl de=mvt ;returns b as new Dpl IsCasePossible: ld a,(ix) ;Dpl? or a ret nz ;It has been possible so it's possible ld l,(ix - 2) ld h,(ix - 1) NextIP: add hl,de call GetAddressInLvl ld a,(hl) cp 80h jr z,NotPossible cp 90h jr z,NotPossible ;ret nz ;jr nz,RetNotPossible and 0F0h cp 10h jr z,NotPossible cp 20h ;A fire! ret nz KillBBMIP: push ix ld e,(ix - 2) ld a,(ix - 1) call DRWSPR pop ix ld a,(PlayerWorking) ld hl,PlayersState or (hl) ld (hl),a NotPossible: ld b,0 RetNotPossible: ret PushedKeyBBM1: ;arrows + clear ld a,0ffh ; reset keyport ;Thank you Harper Maddox (from Chicken) out (1),a ; necessary code ld a,%11111100 out ($01),a ; Mask out Arrows/Clear in a,($01) ret PushedKeyBBM2: ;Sto>:right 4:Left 0:down 2:up + '.' ld a,0ffh ; reset keyport ;Thank you Harper Maddox (from Chicken) out (1),a ; necessary code ld a,%11110111 out ($01),a ; Mask out '.'/2 in a,($01) bit 0,a jr nz,NotPK2Bomb set 0,a res 6,a NotPK2Bomb: bit 1,a jr nz,PK22ndTest set 1,a res 2,a PK22ndTest: ld b,a ld a,0ffh ; reset keyport ;Thank you Harper Maddox (from Chicken) out (1),a ; necessary code ld a,%11001111 out (1),a ; Mask out 0/sto>/4 in a,(1) bit 2,a jr nz,PK2End set 2,a res 3,a PK2End: and b ret ;ix = BBMDir DeplacBBM: ld a,(ix) ;dpl? or a jr nz,IsMoving halt ;It must slow down to compensate ret IsMoving: push af ld hl,SprBBM1 ;V ld a, BBM1Dir & 0ffh ;;PlayersWorking ;LSB of BBM1Dir ;ld de,BBM1Dir ;ld a,e cp ixl jr z,SetHLSpr1Dpl ld hl,SprBBM2 SetHLSpr1Dpl: ; rla ; jr nc,DelSprBBM1V ; ld de,8 ; add hl,de ;hl=SprBBM1H ;DelSprBBM1V: ld e,(ix - 2) ;ld de,(BBM1Y) ;delete spr ld a,(ix - 1) push hl push ix call DRWSPR pop ix pop hl pop af ;restore a rrca jr nc,UpDplBBM rrca jr nc,RightDplBBM rrca jr nc,LeftDplBBM DownDplBBM: inc (ix - 2) ; inc (ix - 2) ld a,(ix - 2) push af ld e,a ld a,(ix - 1) jr FinDplBBM UpDplBBM: dec (ix - 2) ;inc (hl) ; dec (ix - 2) ;inc (hl) ld a,(ix - 2) push af ld e,a ld a,(ix - 1) jr FinDplBBM RightDplBBM: inc (ix - 1) ; inc (ix - 1) ld a,(ix - 1) push af ld e,(ix - 2) jr FinDplBBM LeftDplBBM: dec (ix - 1) ; dec (ix - 1) ld a,(ix - 1) push af ld e,(ix - 2) FinDplBBM: push ix call DRWSPR call CR_GRBCopy pop ix pop af and %00000111 ;modulo 8 ret nz ;jr nz,NextDeplac ld (ix),a ;ld (BBM1Dir),a ret ;hl=XY in Graph GetAddressInLvl: ld d,0 ld a,l srl l add a,l ;a=12Y/8 ld l,a ld a,h ;a=X/8 rra ;carry is 0 by add a,l srl a srl a add a,l ld e,a ld hl,LvlBuf add hl,de ret ;hl points in lvlBuf ;return hl pointing in GraphMem ConvLBToGM: ld de,LvlBuf or a ;reset carry flag sbc hl,de ld a,12 call DIV_HL_A ld h,0 ld d,h ld e,l add hl,hl add hl,de add hl,hl add hl,hl add hl,hl add hl,hl add hl,hl ;hl*96 ld de,GRAPH_MEM add hl,de ld d,0 ld e,a add hl,de ret ;a=Yg e=Xg/8 hl=Sprite Address ;inspired by the Sprite draw routine v1.0 coded by Hannes Edfeldt in 1997 QuickAlignedDrw: push hl ld d,0 ld h,d ld l,a sla l ;Y*2=2Y add a,l ;2Y+1=3Y ld l,a add hl,hl ;3Y*2=6Y ;can be >256 add hl,hl ;6Y*2=12Y add hl,de ld de,GRAPH_MEM add hl,de pop de ld b,8 QADrwLoop: ld a,(de) ld (hl),a inc de push bc ld bc,12 add hl,bc pop bc djnz QADrwLoop ret ; Sprite draw routine v1.0 coded by Hannes Edfeldt in 1997 ; With these routine you can draw non-aligned sprites. ; Feel free to use these routines in your own productions as long as you give ; me some credit. ; This file should of course be viewed in a DOS texteditor ;) ; Hannes Edfeldt -+- movax@algonet.se -+- http://www.algonet.se/~movax ;мллллллллллллп DRWSPR пллллллллллллллллллллллллллллллллллллллллллллллллллллллл ;кФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФП ;Г Draw 8x8 sprite ў a=x, e=y, hl=sprite address XOR !!! Г ;РФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФй DRWSPR: push hl ; Save sprite address ;лллл Calculate the address in graphbuf лллл ld hl,0 ; Do y*12 ld d,0 add hl,de add hl,de add hl,de add hl,hl add hl,hl ld d,0 ; Do x/8 ld e,a srl e srl e srl e add hl,de ld de,GRAPH_MEM add hl,de ; Add address to graphbuf ld b,00000111b ; Get the remainder of x/8 and b cp 0 ;or a;; Is this sprite aligned to 8*n,y? jp z,ALIGN ;лллл Non aligned sprite blit starts here лллл pop ix ; ix->sprite ld d,a ; d=how many bits to shift each line ld e,8 ; Line loop LILOP: ld b,(ix+0) ; Get sprite data ld c,0 ; Shift loop push de SHLOP: srl b rr c dec d jp nz,SHLOP pop de ld a,b ; Write line to graphbuf XOR (hl) ld (hl),a inc hl ld a,c XOR (hl) ld (hl),a ld bc,11 ; Calculate next line address add hl,bc inc ix ; Inc spritepointer dec e jp nz,LILOP ; Next line jp DONE1 ;;ret ;лллл Aligned sprite blit starts here лллл ALIGN: ; Blit an aligned sprite to graphbuf pop de ; de->sprite ld b,8 ALOP1: ld a,(de) XOR (hl) ; xor=erase/blit ld (hl),a inc de push bc ld bc,12 add hl,bc pop bc djnz ALOP1 DONE1: ret ;мллллллллллллм DRWSPR мллллллллллллллллллллллллллллллллллллллллллллллллллллллл SZEndTime: .db "DRAW GAME",0CEh,0 SZEndKill: .db "VICTORY!!",0 SZPlayer: .db "Player",0 SprBBM4: .db %00111100 .db %01011010 .db %10011001 .db %11111111 .db %11111111 .db %10011001 .db %01011010 .db %00111100 SprBBM3: .db %00111100 .db %01000010 .db %10100101 .db %10011001 .db %10011001 .db %10100101 .db %01000010 .db %00111100 SprBBM1: .db %00111100 .db %01000010 .db %10011001 .db %10111101 .db %10111101 .db %10011001 .db %01000010 .db %00111100 SprBBM2: .db %00111100 .db %01100110 .db %11100111 .db %10000001 .db %10000001 .db %11100111 .db %01100110 .db %00111100 ;SprBlank: ;perhaps in ROM ; .db %00000000 ; .db %00000000 ; .db %00000000 ; .db %00000000 ; .db %00000000 ; .db %00000000 ; .db %00000000 ; .db %00000000 SprBomb: .db %01111000 .db %00010000 .db %00111000 .db %01001100 .db %01010100 .db %01101100 .db %00111000 .db %00000000 ;SprDBlock2: ; .db %11001100 ; .db %11001100 ; .db %00110011 ; .db %00110011 ; .db %11001100 ; .db %11001100 ; .db %00110011 ; .db %00110011 SprDBlock: .db %11111111 .db %00001000 .db %00011000 .db %11111011 .db %11111111 .db %10000000 .db %10000001 .db %10111111 SprUBlock: .db %11111111 .db %10000001 .db %10101011 .db %10010111 .db %10101011 .db %10010111 .db %10111111 .db %11111111 SprMask: .db %10101010 .db %00000001 .db %10000000 .db %00000001 .db %10000000 .db %00000001 .db %10000000 .db %01010101 SprFire: .db %00100010 .db %11000110 .db %01101000 .db %00011010 .db %10000011 .db %00010110 .db %11001100 .db %01000100 ;Code Lvl: ;01h -> 08h : BBM ;10h -> 1Fh : Bombs ;20h -> 2Fh : fire ;40h : Blank ;50h : Mask ;80h : Destroyable blocks (LSB=Bonus) ;90h : Undestroyable block ; Lvl1: ; 1 2 3 4 5 6 7 8 9 10 11 12 .db 40h,40h,80h,40h,40h,40h,80h,40h,40h,80h,40h,40h .db 40h,90h,80h,90h,40h,90h,80h,40h,90h,80h,90h,40h .db 40h,80h,80h,80h,80h,80h,80h,80h,40h,80h,80h,40h .db 80h,90h,40h,90h,80h,40h,90h,80h,80h,40h,90h,80h .db 80h,90h,40h,80h,80h,90h,40h,80h,90h,40h,90h,80h .db 40h,80h,80h,40h,80h,80h,80h,80h,80h,80h,80h,40h .db 40h,90h,80h,90h,40h,80h,90h,40h,90h,80h,90h,40h .db 40h,40h,80h,40h,40h,80h,40h,40h,40h,80h,40h,40h LvlTst: ; 1 2 3 4 5 6 7 8 9 10 11 12 ; .db 00h,40h,40h,40h,40h,40h,40h,40h,40h,40h,40h,40h ; .db 40h,90h,80h,90h,40h,90h,40h,90h,40h,90h,40h,90h ; .db 40h,40h,80h,40h,40h,40h,40h,40h,40h,40h,40h,40h ; .db 40h,90h,80h,90h,40h,90h,40h,90h,40h,90h,40h,90h ; .db 40h,80h,80h,80h,80h,40h,40h,80h,40h,40h,40h,40h ; .db 40h,90h,80h,90h,80h,90h,40h,90h,40h,90h,40h,90h ; .db 40h,80h,80h,40h,40h,80h,80h,80h,40h,40h,40h,40h ; .db 80h,90h,40h,90h,40h,90h,40h,90h,40h,90h,40h,40h .end