;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SMB3 Buster Beetle
; Coded by SMWEdit
;
; Uses first extra bit: NO
;
; This sprite will pick up block 12E or any block set to act like 12E
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

incsrc subroutinedefs_trasm.asm

		SPRITE_NUM = $56		; set this to the sprite # of the custom throw block

		HORZ_RANGE = $0080		; max X distance of mario for throwing
		VERT_RANGE = $0030		; max Y distance downward of mario for throwing

		TILE1 = $08			; run frame 1
		TILE2 = $0C			; run frame 2
		TILE3 = $0E			; holding frame

;; don't change these:

		COUNTER = $1504
		ACTSTATUS = $1528
		TILEINDEX = $1602
		LIFTTIMER = $163E
		PAUSETIMER = $1540

		LIFTTIME = $10
		HOLDTHROWTIME = $0C
		PAUSEMOVETIME = $14

		TMP1 = $00

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; INIT and MAIN JSL targets
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		dcb "INIT"
		JSL SubHorizPos
		TYA
		STA $157C,x
		RTL		

		dcb "MAIN"
		PHB
		PHK
		PLB
		JSR SPRITE_ROUTINE
		PLB
		RTL


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SPRITE_ROUTINE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SPEED_TABLE	dcb $10,$F0		; speeds (right, left)

THROWXSPEEDS	dcb $30,$C0		; throw speeds (right, left)
		THROWYSPEED = $C0	; how fast upward speed is

PICKUP_INDEXES	dcb $02,$02,$02,$02,$02,$02,$02,$02
		dcb $00,$00,$00,$00,$00,$00,$00,$00

RETURN1		RTS

SPRITE_ROUTINE	JSR SUB_GFX
		LDA $14C8,x		; \  return if
		CMP #$08		;  | sprite status
		BNE RETURN1		; /  is not 8
		LDA $9D			; \ return if
		BNE RETURN1		; / sprites locked
		LDA $15D0,x		; \ return if
		BNE RETURN1		; / being eaten
		JSL SubOffscreenX0	; only process sprite while on screen             

		STZ $B6,x		; default: not moving X

		LDA PAUSETIMER,x	; \  if movement is
		BEQ NOTPAUSED		;  | paused, then
		JMP RETURNING		; /  ignore all code
NOTPAUSED

		LDA ACTSTATUS,x		; \  which action
		BEQ RUNNING		;  | is being performed
		JMP PICKING_UP		; /  right now?

RUNNING
		LDA $1686,x		; \  change direction
		AND #%11101111		;  | if touched
		STA $1686,x		; /
		LDY $157C,x		; \  set x speed
		LDA SPEED_TABLE,y	;  | depending on
		STA $B6,x		; /  direction
		LDA COUNTER,x		; \
		AND #%00000100		;  | set tile index
		LSR A			;  | according to
		LSR A			;  | a frame counter
		STA TILEINDEX,x		;  |
		INC COUNTER,x		; /
		JSL $81802A		; update position based on speed values, also obj interaction
		LDA $1588,x		; \
		AND #%00000011		;  | not hitting wall? then skip
		BEQ NOT_HIT_SIDE	; /
		LDA $1860		; \
		CMP #$2E		;  | throw blk? (or blk set to act like one)
		BNE NO_PICK_UP		;  | if not, then
		LDA $1862		;  | skip next code
		CMP #$01		;  |
		BNE NO_PICK_UP		; /
		LDA #$02		; \
		STA $9C			;  | destroy blk
		JSL $80BEB0		; /
		LDA #LIFTTIME		; \ lifting timer
		STA LIFTTIMER,x		; / for block
		STZ COUNTER,x		; zero counter for next action
		LDA #$01		; \ set status to
		STA ACTSTATUS,x		; / picking up block
		BRA NOT_HIT_SIDE	; skip dir flip code
NO_PICK_UP
		LDA $157C,x		; \  not picking blk
		EOR #%00000001		;  | up, so hitting a
		STA $157C,x		; /  wall flips dir
NOT_HIT_SIDE
		JMP RETURNING		; end of running code, return

PICKING_UP
		LDA $1686,x		; \  don't change
		ORA #%00010000		;  | direction if
		STA $1686,x		; /  touched
		JSL $81802A		; update position based on speeds, with gravity
		LDY LIFTTIMER,x		; \  set tile index
		LDA PICKUP_INDEXES,y	;  | according to how
		STA TILEINDEX,x		; /  far into lifting
		LDA LIFTTIMER,x		; \ skip to end unless
		BNE RETURNING		; / picking up is done
		JSL SubHorizPos	; \  must face
		TYA			;  | Mario if
		STA $157C,x		; /  ready to throw
		LDA COUNTER,x		; \ counter != 0 means
		BNE BEGINTHROW		; / throwing already started
		LDA $E4,x		; \
		CMP $1A			;  | if slightly
		LDA $14E0,x		;  | off screen (H)
		SBC $1B			;  | then throw
		BNE BEGINTHROW		; /
		LDY #$00		; \
		LDA $E4,x		;  | check H
		STA TMP1		;  | range
		LDA $14E0,x		;  |
		STA TMP1+1		;  |
		PHP			;  |
		REP #%00100000		;  |
		LDA $94			;  |
		SEC			;  |
		SBC TMP1		;  |
		BPL HRESULTISPLUS	;  |
		EOR.w #$FFFF		;  |
		INC A			;  |
HRESULTISPLUS	CMP.w #HORZ_RANGE	;  |
		BCC HRANGE_OK		;  |
		LDY #$01		;  |
HRANGE_OK	PLP			;  |
		CPY #$00		;  |
		BNE RETURNING		; /
		LDY #$00		; \
		LDA $D8,x		;  | check V
		STA TMP1		;  | range
		LDA $14D4,x		;  |
		STA TMP1+1		;  |
		PHP			;  |
		REP #%00100000		;  |
		LDA $96			;  |
		SEC			;  |
		SBC TMP1		;  |
		BMI VRANGE_OK		;  |
		CMP.w #VERT_RANGE	;  |
		BCC VRANGE_OK		;  |
		LDY #$01		;  |
VRANGE_OK	PLP			;  |
		CPY #$00		;  |
		BNE RETURNING		; /
BEGINTHROW	INC COUNTER,x		; increment counter to get closer to throwing
		LDA COUNTER,x		; \  if throw pause time
		CMP #HOLDTHROWTIME	;  | has not expired, then
		BCC RETURNING		; /  don't throw the block
		JSR THROW		; throw the block
		STZ ACTSTATUS,x		; status back to running
		STZ COUNTER,x		; reset counter
		STZ TILEINDEX,x		; tile index = 0 for pause
		LDA #PAUSEMOVETIME	; \ set timer for
		STA PAUSETIMER,x	; / movement pause

RETURNING
		JSL $818032		; interact with other sprites
		JSL $81A7DC		; check for mario/sprite contact       
RETURN		RTS

THROW		JSL $82A9DE		; \ get slot or else
		BMI ENDTHROW		; / end if no slots
		LDA #$01		; \ set sprite status
		STA $14C8,y		; / as a new sprite
		PHX			; back up X
		TYX			; new sprite index to X
		LDA #SPRITE_NUM		; \ store custom
		STA $7FAB9E,x		; / sprite number
		JSL $87F7D2		; reset sprite properties
		JSL $8187A7		; get table values for custom sprite
		LDA #$08		; \ mark as a
		STA $7FAB10,x		; / custom sprite
		PLX			; load backed up X
		LDA $E4,x		; \
		STA $E4,y		;  | set position
		LDA $14E0,x		;  | of block to
		STA $14E0,y		;  | be thrown
		LDA $D8,x		;  |
		SEC			;  |
		SBC #$0E		;  |
		STA $D8,y		;  |
		LDA $14D4,x		;  |
		SBC #$00		;  |
		STA $14D4,y		; /
		LDA #$60		;
		JSR CODE_01BF6A		;
		LDA $01			; \
		STA $B6,y		; /
		LDA $00			; \
		STA $AA,y		; / speed
ENDTHROW	RTS

CODE_01BF6A:		STA $01
			PHX					;\ preserve sprite indexes of Magikoopa and magic
			PHY					;/
			JSR CODE_01AD42				; $0E = vertical distance to Mario
			STY $02					; $02 = vertical direction to Mario
			LDA $0E					;\ $0C = vertical distance to Mario, positive
			BPL CODE_01BF7C				; |
			EOR #$FF				; |
			CLC					; |
			ADC #$01				; |
CODE_01BF7C:		STA $0C					;/
			JSL SubHorizPos			; $0F = horizontal distance to Mario
			STY $03					; $03 = horizontal direction to Mario
			LDA $0F					;\ $0D = horizontal distance to Mario, positive
			BPL CODE_01BF8C				; |
			EOR #$FF				; |
			CLC					; |
			ADC #$01				; |
CODE_01BF8C:		STA $0D					;/
			LDY #$00
			LDA $0D					;\ if vertical distance less than horizontal distance,
			CMP $0C					; |
			BCS CODE_01BF9F				;/ branch
			INY					; set y register
			PHA					;\ switch $0C and $0D
			LDA $0C					; |
			STA $0D					; |
			PLA					; |
			STA $0C					;/
CODE_01BF9F:		LDA #$00				;\ zero out $00 and $0B
			STA $0B					; | ...what's wrong with STZ?
			STA $00					;/
			LDX $01					;\ divide $0C by $0D?
CODE_01BFA7:		LDA $0B					; |\ if $0C + loop counter is less than $0D,
			CLC					; | |
			ADC $0C					; | |
			CMP $0D					; | |
			BCC CODE_01BFB4				; |/ branch
			SBC $0D					; | else, subtract $0D
			INC $00					; | and increase $00
CODE_01BFB4:		STA $0B					; |
			DEX					; |\ if still cycles left to run,
			BNE CODE_01BFA7				;/ / go to start of loop
			TYA					;\ if $0C and $0D was not switched,
			BEQ CODE_01BFC6				;/ branch
			LDA $00					;\ else, switch $00 and $01
			PHA					; |
			LDA $01					; |
			STA $00					; |
			PLA					; |
			STA $01					;/
CODE_01BFC6:		LDA $00					;\ if horizontal distance was inverted,
			LDY $02					; | invert $00
			BEQ CODE_01BFD3				; |
			EOR #$FF				; |
			CLC					; |
			ADC #$01				; |

			STA $00					;/
CODE_01BFD3:		LDA $01					;\ if vertical distance was inverted,
			LDY $03					; | invert $01
			BEQ CODE_01BFE0				; |
			EOR #$FF				; |
			CLC					; |
			ADC #$01				; |
			STA $01					;/
CODE_01BFE0:		PLY					;\ retrieve Magikoopa and magic sprite indexes
			PLX					;/
			RTS					; return

CODE_01AD42:		LDY #$00
			LDA $D3
			SEC
			SBC $D8,x
			STA $0E
			LDA $D4
			SBC $14D4,x
			BPL Return01AD53
			INY
Return01AD53:		RTS					; return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GRAPHICS ROUTINE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		TILESDRAWN = $04
		COORDINDEX = $05

XCOORDS		dcb $00,$00,$00,$00,$00,$00,$00,$00
		dcb $00,$03,$05,$08,$0A,$0C,$0D,$0E
		dcb $02

YCOORDS		dcb $F2,$F2,$F2,$F2,$F2,$F2,$F2,$F2
		dcb $F6,$F7,$F8,$F9,$FB,$FC,$FD,$FF
		dcb $F3

TILES		dcb TILE1,TILE2,TILE3

SUB_GFX		JSL GetDrawInfo

		STZ TILESDRAWN		; zero tiles drawn so far

		LDA $157C,x		; \ direction into
		STA $02			; / scratch RAM

		LDA $14C8,x		; \  vertical flip
		SEC			;  | based on whether or
		SBC #$02		;  | not sprite is dying.
		STA $03			; /  dying means flip

		LDA $00			; \ beetle X
		STA $0300,y		; / position

		LDA $01			; \
		PHX			;  | beetle Y
		LDX $03			;  | position,
		BNE NOTDEAD		;  | subtract 10
		SEC			;  | if dying
		SBC #$10		;  |
NOTDEAD		PLX			;  |
		STA $0301,y		; /

		PHY			; \
		LDY TILEINDEX,x		;  | beetle tile based on table
		LDA TILES,y		;  | entry corresponding to the
		PLY			;  | index set in the main code
		STA $0302,y		; /

		LDA $15F6,x		; sprite properties
		PHX			; \
		LDX $02			;  | flip if
		BNE NOFLIPX1		;  | sprite is
		ORA #%01000000		;  | flipped
NOFLIPX1	;PLX			; /
		;PHX			; \
		LDX $03			;  | flip V if
		BNE NOFLIPY1		;  | sprite is
		ORA #%10000000		;  | dying
NOFLIPY1	PLX			; /
		ORA $64			; add in priority bits
		STA $0303,y             ; set properties

		INC TILESDRAWN		; indicate a tile was drawn

		LDA ACTSTATUS,x		; \ if status is zero, only other status than
		BEQ FINALIZEGFX		; / block lifting/throwing, don't show block

		LDA LIFTTIMER,x		; \ default coordinate index
		STA COORDINDEX		; / is amt left in lift time
		LDA COUNTER,x		; \
		CMP #$04		;  | if counter for
		BCC NO_OVERRIDE_INDEX	;  | throw wait time
		CMP #$08		;  | elapsed is 4-7,
		BCS NO_OVERRIDE_INDEX	;  | then use slightly
		LDA #LIFTTIME		;  | shifted block
		STA COORDINDEX		; /
NO_OVERRIDE_INDEX

		INY			; \
		INY			;  | for next tile, get
		INY			;  | next OAM tile index
		INY			; /

		PHY			; \
		LDY COORDINDEX		;  | get X
		LDA XCOORDS,y		;  | coordinate
		LDY $02			;  | for block
		BEQ NOFLIPXC		;  | and flip
		EOR #%11111111		;  | if sprite
		INC A			;  | is flipped
NOFLIPXC	PLY			; /
		CLC			; \ then add it to
		ADC $00			; / tile position
		STA $0300,y		; and store the X position 

		PHY			; \
		LDY COORDINDEX		;  | get Y
		LDA YCOORDS,y		;  | coordinate
		LDY $03			;  | for block
		BNE NOFLIPYC		;  | and flip
		EOR #%11111111		;  | and subtract
		INC A			;  | #$10 if
		SEC			;  | sprite is
		SBC #$10		;  | dying
NOFLIPYC	PLY			; /
		CLC			; \ then add it to
		ADC $01			; / tile position
		STA $0301,y		; and store the Y position 

		LDA #$40		; \ set block
		STA $0302,y		; / tile number

		LDA $13			; \
		AND #%00000111		;  | cycle through palettes
		ASL A			; /
		PHX			; \	NOTE: you can cut out the block flipping code, I just thought it might look better with it because the beetle in SMB3 has it
		LDX $02			;  | flip if
		BNE NOFLIPX2		;  | sprite is
		ORA #%01000000		;  | flipped
NOFLIPX2	;PLX			; /
		;PHX			; \
		LDX $03			;  | flip V if
		BNE NOFLIPY2		;  | sprite is
		ORA #%10000000		;  | dying
NOFLIPY2	PLX			; /
		ORA $64                 ; add in the priority bits from the level settings
		STA $0303,y             ; set properties

		INC TILESDRAWN		; indicate a tile was drawn

FINALIZEGFX
		LDY #$02		; #$02 means the tiles are 16x16
		LDA TILESDRAWN		; \ A = tiles-1
		DEC A			; /
		JSL $81B7B3		; don't draw if offscreen, set tile size
		RTS


