; Kirby Boss
; HuFlungDu
; This is just a boss I created for a hack I made and converted to be usable
; in normal mario hacks in an attemt to expand the available boss pool here
; Assembles with xkas, so use Romi's sprite tool.
; For once I was too lazy to comment my code. Sorry.
; Thanks to TLMB for most of the kirby graphics.
; Extra bit: I put it in there, but it doesn' work for some reason and I'm too lazy
; to figure out why. It should use the secret exit if the bit is set though...
; No credit necessary
!Action = $1510
!FrameNum = $1570
!FrameTimer = $163E
!DontDo = $151C
!WalkTimer = $1540
!HP = $C2
!HPCountDown = $1528
!Flashing = $1558
!FlashTimer = $2F ;How long Kirby flashes and is invincible after being hit
!TotalHP = $10 ;How much HP kirby has
!HPTaken = $02 ;How much HP he takes when hit
!FireSpriteNum = $07 ;The sprite number you inserted his projectile as
print "INIT ",pc
JSR SUB_HORZ_POS
TYA
STA $157C,x
STZ !FrameNum,x
LDA #$01
STA !DontDo,x
LDA #$03
STA !Action,x
LDA #$30
STA !FrameTimer,x
LDA #!TotalHP
STA !HP,x
RTL
print "MAIN ",pc
PHB
PHK
PLB
JSR SPRITE_ROUTINE ;Jump to the routine to keep organized
PLB
RTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SPRITE_ROUTINE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
speed:
db $10,$F0
Yflip:
db $00,$00,$00,$01,$01,$01,$01,$00
Xflip:
db $01,$01,$01,$01,$00,$00,$00,$00
t_BOUNCE:
db $E4,$1C ;x speed to give mario when he stomps the sprite
SPRITE_ROUTINE:
LDA $9D
BEQ +
.JumpReturn
JMP Return
+
LDA $14C8,x
CMP #$08
BNE .JumpReturn
LDA !HPCountDown,x
BEQ +
DEC !HPCountDown,x
DEC !HP,x
+
LDA !HP,x
BNE +
LDA !Action,x
CMP #$0A
BCS +
LDA #$0A
STA !Action,x
LDA #$20
STA !FrameTimer,x
+
LDA !Action,x
CMP #$0A
BCS +
JSL $019138
+
JSR SUB_OFF_SCREEN_X0
LDA !Action,x
CMP #$0A
BCS .NoContact
JSL $01A7DC ; \ check for mario/sprite contact
BCC .NoContact ; / do nothing if no contact
.Contact
JSR SUB_VERT_POS ; \
LDA $0E ; | if mario isn't above sprite, and there's vertical contact...
CMP #$E6 ; | ... sprite wins
BPL .SpriteWins ; /
LDA $7D ; \ if mario speed is upward, return
BMI .NoContact ; /
LDA #!FlashTimer ; \ prevent the Double-Edge glitch from happening
STA !Flashing,x ; /
LDA #!HPTaken
STA !HPCountDown,x
LDA $15 ; Save controller state
PHA
ORA #$C0 ; Set holding X/Y and A/B
STA $15
JSL $01AA33 ; set mario speed
PLA
STA $15
JSR SUB_HORZ_POS
TYA
EOR #$01
TAY
LDA t_BOUNCE,y ; | bounce away horizontally
STA $7B ; /
JSL $01AB99 ; display contact graphic
LDA #$20
STA $1DF9
JMP .NoContact
.SpriteWins
LDA $154C,x ; \ if disable interaction set...
ORA $15D0,x ; | ...or sprite being eaten...
BNE .NoContact ; / ...return
LDA $1490 ; \ Branch if Mario has a star
BNE .NoContact ; /
JSL $00F5B7 ; damage Mario
.NoContact
LDA !Action,x
ASL
TAY
REP #$20
LDA ActionPointers,y
PHA
SEP #$20
RTS
Dieing:
LDA #$09
STA !FrameNum,x
LDA !FrameTimer,x
BNE +
LDA #$C0
STA $AA,x
LDA #$0B
STA !Action,x
LDA #$23 ; \ sound effect
STA $1DF9 ; /
+
JMP Return
Spinning:
LDA !FrameNum,x
CMP #$0A
BCS +
LDA #$0A
STA !FrameNum,x
+
LDA $13
BIT #$03
BNE +
INC !FrameNum,x
LDA !FrameNum,x
CMP #$12
BCC +
LDA #$0A
STA !FrameNum,x
+
LDA !FrameNum,x
SEC
SBC #$0A
PHX
TAX
LDA Yflip,x
XBA
LDA Xflip,x
PLX
STA $157C,x
XBA
STA $1534,x
STZ $B6,x
LDA #$88
STA $1686,x
JSL $01802A
JMP Return
Walkleft:
LDA #$E0
STA $B6,x
LDA #$01
STA $157C,x
JMP BothWRL
Walkright:
LDA #$20
STA $B6,x
LDA #$00
STA $157C,x
BothWRL:
LDA !FrameNum,x
BNE +
LDA #$01
STA !FrameNum,x
+
LDA !WalkTimer,x
BNE +
LDA #$08
STA !WalkTimer,x
INC !FrameNum,x
LDA !FrameNum,x
CMP #$05
BCC +
LDA #$01
STA !FrameNum,x
+
JSL $01802A
LDA !Action,x
BEQ ++
LDA $E4,x
CMP #$10
BCS +
LDA #$10
STA $E4,x
LDA #$02
STA !Action,x
+
BRA +
++
LDA $E4,x
CMP #$E0
BCC +
LDA #$E0
STA $E4,x
LDA #$03
STA !Action,x
+
LDA #$30
STA !FrameTimer,x
;JMP Return
;RTS ;End the routine
Return:
LDA !Flashing,x
LSR
BIT #$01
BNE +
JSR SUB_GFX
+
RTS
Waitingright:
STZ $157C,x
LDA #$00
STA !FrameNum,x
LDA !FrameTimer,x
BNE +
PHX
JSR GetRand
TAX
LDA RightActions,x
PLX
STA !Action,x
LDA #$10
STA !FrameTimer,x
+
JMP Return
Waitingleft:
LDA #$01
STA $157C,x
LDA #$00
STA !FrameNum,x
LDA !FrameTimer,x
BNE +
PHX
JSR GetRand
TAX
LDA LeftActions,x
PLX
STA !Action,x
LDA #$10
STA !FrameTimer,x
+
JMP Return
Jumpingright:
LDA #$10
STA $B6,x
LDA #$06
STA !FrameNum,x
STZ $157C,x
STZ !DontDo,x
LDA $E4,x
CMP #$E0
BCC +
LDA #$02
STA !DontDo,x
LDA #$E0
STA $E4,x
+
JMP BothJLR
Jumpingleft:
LDA #$F0
STA $B6,x
LDA #$06
STA !FrameNum,x
LDA #$01
STA $157C,x
STZ !DontDo,x
LDA $E4,x
CMP #$10
BCS +
LDA #$01
STA !DontDo,x
LDA #$10
STA $E4,x
+
BothJLR:
LDA $1588,x
BIT #$04
BEQ +
LDA !DontDo,x
BEQ ++
CMP #$01
BNE +++
LDA #$02
STA !Action,x
LDA #$30
STA !FrameTimer,x
STZ !DontDo,x
BRA ++
+++
LDA #$03
STA !Action,x
LDA #$30
STA !FrameTimer,x
STZ !DontDo,x
++
LDA !WalkTimer,x
BNE ++
LDA #$10
STA !WalkTimer,x
++
STZ $B6,x
LDA #$08
STA !FrameNum,x
+
LDA !WalkTimer,x
CMP #$01
BNE +
LDA #$C0
STA $AA,x
LDA #$01
STA $1DFA
+
JSL $01802A
JMP Return
;RTS
Kickright:
LDA #$40
STA $B6,x
STZ $157C,x
LDA $E4,x
CMP #$E0
BCC +
LDA #$E0
STA $E4,x
LDA #$03
STA !Action,x
LDA #$30
STA !FrameTimer,x
+
JMP BothKLR
Kickleft:
LDA #$C0
STA $B6,x
LDA #$01
STA $157C,x
LDA $E4,x
CMP #$10
BCS +
LDA #$10
STA $E4,x
LDA #$02
STA !Action,x
LDA #$30
STA !FrameTimer,x
+
BothKLR:
LDA #$07
STA !FrameNum,x
JSL $01802A
JMP Return
Fireright:
STZ $157C,x
LDA #$50
STA $0D
LDA #$08
STA $0E
LDA !FrameTimer,x
BNE +
LDA #$02
STA !Action,x
LDA #$30
STA !FrameTimer,x
+
JMP BothFRL
Fireleft:
LDA #$01
STA $157C,x
LDA #$B0
STA $0D
LDA #$F8
STA $0E
LDA !FrameTimer,x
BNE +
LDA #$03
STA !Action,x
LDA #$30
STA !FrameTimer,x
+
BothFRL:
LDA #$05
STA !FrameNum,x
LDA !FrameTimer,x
CMP #$06
BNE +
LDA #$01
STA $07
LDA #!FireSpriteNum
STA $06
LDA $E4,x
CLC
ADC $0E
STA $08
LDA $14E0,x
STA $09
LDA $D8,x
SEC
SBC #$05
STA $0A
LDA $14D4,x
STA $0B
LDA $157C,x
STA $0C
JSR SpawnCust
LDA #$06
STA $1DFC
+
JMP Return
ActionPointers:
dw Walkright-1
dw Walkleft-1
dw Waitingright-1
dw Waitingleft-1
dw Jumpingright-1
dw Jumpingleft-1
dw Kickright-1
dw Kickleft-1
dw Fireright-1
dw Fireleft-1
dw Dieing-1
dw Spinning-1
RightActions:
db $00,$02,$04,$06,$08
LeftActions:
db $01,$03,$05,$07,$09
GetRand:
LDA $7F9C40 ;This is an address used to store your seed value
STA $4202 ;\Multiplication
LDA $94 ;|This could be a static number or could be generated
;|By another routine, your choice
STA $4203 ;/
NOP #4 ;Wait for it to finish
REP #$20 ;\Get the result back out and
LDA $4216 ;|add in your "b" value
CLC ;|
ADC $13 ;/(This works same as !Multiplicand earlier, but it's 16bit)
STA $4204 ;\Set up division
SEP #$20 ;|(Used as MOD in this case)
LDA #$05 ;|This is the value you want to do MOD with
STA $4206 ;/Can also be static or generated
NOP #8 ;Wait for it to finish
LDA $4216 ;\Get back the low byte of remainder
STA $7F9C40 ;/And make it your new seed
RTS
SpawnCust:
JSL $02A9DE
BMI Return1
PHX
TYX
LDA $07
STA $14C8,x
LDA $06
STA $7FAB9E,x
JSL $07F7D2
JSL $0187A7
LDA #$08
STA $7FAB10,x
LDA #$0E
STA $163E,x
;Store it to Mario's position.
LDA $08
STA $E4,x
LDA $09
STA $14E0,x
LDA $0A
STA $D8,x
LDA $0B
STA $14D4,x
LDA $0C
STA $157C,x
LDA $0D
STA $B6,x
PLX
Return1:
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GRAPHICS ROUTINE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TilePointers:
dw Tile1,Tile2,Tile3,Tile4,Tile5,Tile6,Tile7,Tile8,Tile9,Tile10
dw Tile11,Tile12,Tile13,Tile14,Tile15,Tile16,Tile17,Tile18
Tile1:
db $00,$02,$20,$22
Tile2:
db $04,$06,$24,$26
Tile3:
db $08,$0A,$28,$2A
Tile4:
db $0C,$0E,$2C,$2E
Tile5:
db $08,$0A,$28,$2A
Tile6:
db $40,$42,$60,$62
Tile7:
db $44,$46,$64,$66
Tile8:
db $48,$4A,$68,$6A
Tile9:
db $4C,$4E,$6C,$6E
Tile10:
db $88,$8A,$A8,$AA
Tile11:
db $8C,$8E,$AC,$AE
Tile12:
db $C0,$C2,$E0,$E2
Tile13:
db $C4,$C6,$E4,$E6
Tile14:
db $C0,$C2,$E0,$E2
Tile15:
db $8C,$8E,$AC,$AE
Tile16:
db $C0,$C2,$E0,$E2
Tile17:
db $C4,$C6,$E4,$E6
Tile18:
db $C0,$C2,$E0,$E2
YOff:
db $F0,$F0,$00,$00
XOff:
db $08,$F8,$08,$F8
SUB_GFX: JSR GET_DRAW_INFO ;Get all the drawing info, duh
LDA $157C,x
STA $06
LDA $1534,x
STA $07
PHX
LDA !FrameNum,x
ASL
TAX
REP #$20
LDA TilePointers,x
STA $04
SEP #$20
LDX #$03
loop:
LDA $00
PHX
LDX $06
BNE +
PLX
CLC
ADC XOff,x
BRA ++
+
PLX
SEC
SBC XOff,x
++
STA $0300,y
LDA $01
PHX
LDX $07
BNE +
PLX
CLC
ADC YOff,x
BRA ++
+
PLX
SEC
SBC YOff,x
SEC
SBC #$08
++
STA $0301,y
;PHX
PHY
TXY
LDA ($04),y
PLY
STA $0302,y
PHX
LDX $15E9
LDA $15F6,x
ORA $64
LDX $06
BNE +
ORA #$40
+
LDX $07
BEQ +
ORA #$80
+
PLX
STA $0303,y
INY
INY
INY
INY
;DEC $05
DEX
BPL loop
PLX
LDA #$03
LDY #$02 ;|they were all 16x16 tiles
JSL $01B7B3 ;/and then draw em
EndIt:
RTS
SUB_HORZ_POS: LDY #$00
LDA $94
SEC
SBC $E4,x
STA $0F
LDA $95
SBC $14E0,x
BPL SPR_L16
INY
SPR_L16:
RTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GET_DRAW_INFO
; This is a helper for the graphics routine. It sets off screen flags, and sets up
; variables. It will return with the following:
;
; Y = index to sprite OAM ($300)
; $00 = sprite x position relative to screen boarder
; $01 = sprite y position relative to screen boarder
;
; It is adapted from the subroutine at $03B760
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SPR_T1: db $0C,$1C
SPR_T2: db $01,$02
GET_DRAW_INFO: STZ $186C,x ; reset sprite offscreen flag, vertical
STZ $15A0,x ; reset sprite offscreen flag, horizontal
LDA $E4,x ; \
CMP $1A ; | set horizontal offscreen if necessary
LDA $14E0,x ; |
SBC $1B ; |
BEQ ON_SCREEN_X ; |
INC $15A0,x ; /
ON_SCREEN_X: LDA $14E0,x ; \
XBA ; |
LDA $E4,x ; |
REP #$20 ; |
SEC ; |
SBC $1A ; | mark sprite invalid if far enough off screen
CLC ; |
ADC.w #$0040 ; |
CMP.w #$0180 ; |
SEP #$20 ; |
ROL A ; |
AND #$01 ; |
STA $15C4,x ; /
BNE INVALID ;
LDY #$00 ; \ set up loop:
LDA $1662,x ; |
AND #$20 ; | if not smushed (1662 & 0x20), go through loop twice
BEQ ON_SCREEN_LOOP ; | else, go through loop once
INY ; /
ON_SCREEN_LOOP: LDA $D8,x ; \
CLC ; | set vertical offscreen if necessary
ADC SPR_T1,y ; |
PHP ; |
CMP $1C ; | (vert screen boundry)
ROL $00 ; |
PLP ; |
LDA $14D4,x ; |
ADC #$00 ; |
LSR $00 ; |
SBC $1D ; |
BEQ ON_SCREEN_Y ; |
LDA $186C,x ; | (vert offscreen)
ORA SPR_T2,y ; |
STA $186C,x ; |
ON_SCREEN_Y: DEY ; |
BPL ON_SCREEN_LOOP ; /
LDY $15EA,x ; get offset to sprite OAM
LDA $E4,x ; \
SEC ; |
SBC $1A ; | $00 = sprite x position relative to screen boarder
STA $00 ; /
LDA $D8,x ; \
SEC ; |
SBC $1C ; | $01 = sprite y position relative to screen boarder
STA $01 ; /
RTS ; return
INVALID: PLA ; \ return from *main gfx routine* subroutine...
PLA ; | ...(not just this subroutine)
RTS ; /
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SUB_OFF_SCREEN
; This subroutine deals with sprites that have moved off screen
; It is adapted from the subroutine at $01AC0D
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SPR_T12: db $40,$B0
SPR_T13: db $01,$FF
SPR_T14: db $30,$C0,$A0,$C0,$A0,$F0,$60,$90 ;bank 1 sizes
db $30,$C0,$A0,$80,$A0,$40,$60,$B0 ;bank 3 sizes
SPR_T15: db $01,$FF,$01,$FF,$01,$FF,$01,$FF ;bank 1 sizes
db $01,$FF,$01,$FF,$01,$00,$01,$FF ;bank 3 sizes
SUB_OFF_SCREEN_X1: LDA #$02 ; \ entry point of routine determines value of $03
BRA STORE_03 ; | (table entry to use on horizontal levels)
SUB_OFF_SCREEN_X2: LDA #$04 ; |
BRA STORE_03 ; |
SUB_OFF_SCREEN_X3: LDA #$06 ; |
BRA STORE_03 ; |
SUB_OFF_SCREEN_X4: LDA #$08 ; |
BRA STORE_03 ; |
SUB_OFF_SCREEN_X5: LDA #$0A ; |
BRA STORE_03 ; |
SUB_OFF_SCREEN_X6: LDA #$0C ; |
BRA STORE_03 ; |
SUB_OFF_SCREEN_X7: LDA #$0E ; |
STORE_03: STA $03 ; |
BRA START_SUB ; |
SUB_OFF_SCREEN_X0: STZ $03 ; /
START_SUB: JSR SUB_IS_OFF_SCREEN ; \ if sprite is not off screen, return
BEQ RETURN_35 ; /
LDA $5B ; \ goto VERTICAL_LEVEL if vertical level
AND #$01 ; |
BNE VERTICAL_LEVEL ; /
LDA $D8,x ; \
CLC ; |
ADC #$50 ; | if the sprite has gone off the bottom of the level...
LDA $14D4,x ; | (if adding 0x50 to the sprite y position would make the high byte >= 2)
ADC #$00 ; |
CMP #$02 ; |
BPL ERASE_SPRITE ; / ...erase the sprite
LDA $167A,x ; \ if "process offscreen" flag is set, return
AND #$04 ; |
BNE RETURN_35 ; /
LDA $13
AND #$01
ORA $03
STA $01
TAY
LDA $1A
CLC
ADC SPR_T14,y
ROL $00
CMP $E4,x
PHP
LDA $1B
LSR $00
ADC SPR_T15,y
PLP
SBC $14E0,x
STA $00
LSR $01
BCC SPR_L31
EOR #$80
STA $00
SPR_L31: LDA $00
BPL RETURN_35
ERASE_SPRITE: LDA $14C8,x ; \ if sprite status < 8, permanently erase sprite
CMP #$08 ; |
BCC KILL_SPRITE ; /
LDY $161A,x
CPY #$FF
BEQ KILL_SPRITE
LDA #$00
STA $1938,y
KILL_SPRITE: STZ $14C8,x ; erase sprite
LDA $7FAB10,x ; \ set secret exit if the extra bit is set
BIT #$04 ; |
BEQ + ; /
LDA #$01 ; \ set secret exit
STA $141C ; /
+
INC $13C6 ; prevent Mario from walking at the level end
LDA #$FF ; \ set goal
STA $1493 ; /
LDA #$0B ; \ set ending music
STA $1DFB ; /
RETURN_35: RTS ; return
VERTICAL_LEVEL: LDA $167A,x ; \ if "process offscreen" flag is set, return
AND #$04 ; |
BNE RETURN_35 ; /
LDA $13 ; \
LSR A ; |
BCS RETURN_35 ; /
LDA $E4,x ; \
CMP #$00 ; | if the sprite has gone off the side of the level...
LDA $14E0,x ; |
SBC #$00 ; |
CMP #$02 ; |
BCS ERASE_SPRITE ; / ...erase the sprite
LDA $13
LSR A
AND #$01
STA $01
TAY
LDA $1C
CLC
ADC SPR_T12,y
ROL $00
CMP $D8,x
PHP
LDA.w $001D
LSR $00
ADC SPR_T13,y
PLP
SBC $14D4,x
STA $00
LDY $01
BEQ SPR_L38
EOR #$80
STA $00
SPR_L38:
LDA $00
BPL RETURN_35
BMI ERASE_SPRITE
SUB_IS_OFF_SCREEN: LDA $15A0,x ; \ if sprite is on screen, accumulator = 0
ORA $186C,x ; |
RTS ; / return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SUB_VERT_POS
; This routine determines if Mario is above or below the sprite. It sets the Y register
; to the direction such that the sprite would face Mario
; It is ripped from $03B829
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SUB_VERT_POS:
LDY #$00
LDA $96
SEC
SBC $D8,x
STA $0F
LDA $97
SBC $14D4,x
BPL SPR_L11
INY
SPR_L11:
RTS