;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Hopping Snift
;; Exactly like MikeyK's version, but hops a little bit. It has 
;; most of the same settings. 
;; The extra bit determines if it should face Mario.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	UpdateSpritePos = $01802A  
	SprSprInteract = $018032   
	MarioSprInteract = $01A7DC
	GetSpriteClippingA = $03B69F
	CheckForContact = $03B72B
	DisplayContactGfx = $01AB99 
	FinishOAMWrite = $01B7B3
	ShowSpinJumpStars = $07FC3B
	GivePoints = $02ACE5
	HurtMario = $00F5B7
	
	ExtraProperty1 = $7FAB28
	ExtraProperty2 = $7FAB34
	
	RAM_ThrowTimer = $1504
	RAM_SpriteDir = $157C
	RAM_SprTurnTimer = $15AC

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; sprite init JSL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        dcb "INIT"
	LDA $167A,x
	STA $1528,x
	LDA #$40
	STA $C2,x
	LDA #$01		
	STA $151C,x

        JSR SubHorzPos
        TYA
        STA $157C,x

        TXA
        AND #$03
        ASL A
        ASL A
        ASL A
        STA RAM_ThrowTimer,x
                    
        RTL                 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; sprite code JSL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        dcb "MAIN"                                    
        PHB                  
        PHK                  
        PLB                  
	CMP #09
	BCS HandleStunnd
        JSR SpriteMainSub    
        PLB                  
        RTL

HandleStunnd:
	LDA $167A,x		; Set to interact with Mario
	AND #$7F
	STA $167A,x
	
	LDY $15EA,X		; Replace Goomba's tile
	PHX
	LDX Tilemap
	LDA $302,Y
	CMP #$A8
	BEQ SetTile
	LDX Tilemap+1
SetTile:
	TXA
 	STA $302,Y
	PLX	
	PLB
	RTL
	
DecrementTimers:
	LDA RAM_ThrowTimer,x
	BEQ DoneDec
	DEC RAM_ThrowTimer,x
DoneDec:
	RTS
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; sprite main code 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        SPRITE_GRAVITY = $20
        TIME_TO_SHOW = $20      ;time to display the boomerang before it is thrown
        TIME_TILL_THROW = $2F
Return:
	RTS
SpriteMainSub:
	JSR SubGfx
        LDA $9D                 ; \ if sprites locked, return
        BNE Return              ; /
	LDA $14C8,x
	CMP #$08
	BNE Return
	JSR MaybeFaceMario
        JSR SubOffScreen	; handle off screen situation
	INC $1570,x

	LDA RAM_ThrowTimer,x             
        CMP #TIME_TO_SHOW  
	BCS WalkingState

ThrowingState:
	JSR DecrementTimers	
	LDA RAM_ThrowTimer,x    
        CMP #TIME_TO_SHOW       
        BCS NO_THROW            
        LDA $14                 
        LSR A                   
        CLC                     
        ADC $15E9               
        AND #$01                
	INC A
	INC A
        STA $1602,x  

	LDA RAM_ThrowTimer,x    ; \ if time until throw = 0
        BNE NO_TIME_SET         ;  |
        LDA #TIME_TILL_THROW    ;  | reset the timer
        STA RAM_ThrowTimer,x    ; /
NO_TIME_SET:
	CMP #$01                ; \ call the ball routine if the timer is 
        BNE NO_THROW            ;  | about to tun out
        JSR SUB_BALL_THROW      ; /
NO_THROW:


	
WalkingState:	
        LDA $14                 ; Set walking frame based on frame counter
        LSR A                   
        LSR A                   
        LSR A                   
        CLC                    
        ADC $15E9              
        AND #$01               
        STA $1602,x                   
	
	JSR Hop		; Jump to custom code

	JSL UpdateSpritePos     ; Update position based on speed values

	LDA $1588,x             ; if on the ground, reset the turn counter
        AND #$04
        BEQ NotOnGround
	JSR DecrementTimers	
	STZ $AA,x
	STZ $151C,x		; Reset turning flag (used if sprite stays on ledges)
NotOnGround:	
	
 	
SharedCode:	
	LDA $1528,x
	STA $167A,x
	
        JSL SprSprInteract	; Interact with other sprites
	JSL MarioSprInteract	; Check for mario/sprite contact (carry set = contact)
        BCC Return1             ; return if no contact
	
        JSR SubVertPos          ; \
        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 Return1             ; /

        LDA ExtraProperty1,x 	; Check property byte to see if sprite can be spin jumped
        AND #$10
        BEQ SpinKillDisabled    
        LDA $140D               ; Branch if mario is spin jumping
        BNE SpinKill
SpinKillDisabled:	
	LDA $187A
	BNE RideSprite
	LDA ExtraProperty1,x
	AND #$20
	BEQ RideSprite
	BIT $16		        ; Don't pick up sprite if not pressing button
        BVC RideSprite
	LDA #$0B		; Sprite status = Carried
	STA $14C8,x
	LDA #$FF		; Set time until recovery
	STA $1540,x
	RTS

RideSprite:	
	LDA #$01                ; \ set "on sprite" flag
        STA $1471               ; /
        LDA #$06                ; Disable interactions for a few frames
        STA $154C,x             
        STZ $7D                 ; Y speed = 0
        LDA #$E1                ; \
        LDY $187A               ;  | mario's y position += E1 or D1 depending if on yoshi
        BEQ NO_YOSHI            ;  |
        LDA #$D1                ;  |
NO_YOSHI:
	CLC                     ;  |
        ADC $D8,x               ;  |
        STA $96                 ;  |
        LDA $14D4,x             ;  |
        ADC #$FF                ;  |
        STA $97                 ; /
        LDY #$00                ; \ 
        LDA $1491               ;  | $1491 == 01 or FF, depending on direction
        BPL LABEL9              ;  | set mario's new x position
        DEY                     ;  |
LABEL9:
	CLC                     ;  |
        ADC $94                 ;  |
        STA $94                 ;  |
        TYA                     ;  |
        ADC $95                 ;  |
        STA $95                 ; /
        RTS                     

SpriteWins:
	LDA $154C,x             ; \ if disable interaction set...
        ORA $15D0,x             ;  |   ...or sprite being eaten...
        BNE Return1             ; /   ...return
        LDA $1490               ; Branch if Mario has a star
        BNE MarioHasStar        
        JSL HurtMario	
Return1:
	RTS                    

SpinKill:
	JSR SUB_STOMP_PTS       ; give mario points
	LDA #$F8	        ; Set Mario Y speed
	STA $7D
        JSL DisplayContactGfx   ; display contact graphic
        LDA #$04                ; \ status = 4 (being killed by spin jump)
        STA $14C8,x             ; /   
        LDA #$1F                ; \ set spin jump animation timer
        STA $1540,x             ; /
        JSL ShowSpinJumpStars
        LDA #$08                ; \ play sound effect
        STA $1DF9               ; /
        RTS                     ; return

MarioHasStar:
        LDA #$02                ; \ status = 2 (being killed by star)
        STA $14C8,x             ; /
        LDA #$D0                ; \ set y speed
        STA $AA,x               ; /
        JSR SubHorzPos          ; get new direction
        LDA KILLED_X_SPEED,y    ; \ set x speed based on direction
        STA $B6,x               ; /
        INC $18D2               ; increment number consecutive enemies killed
        LDA $18D2               ; \
        CMP #$08                ;  | if consecutive enemies stomped >= 8, reset to 8
        BCC NoReset             ;  |
        LDA #$08                ;  |
        STA $18D2               ; /   
NoReset:
	JSL GivePoints
        LDY $18D2               ; \ 
        CPY #$08                ;  | if consecutive enemies stomped < 8 ...
        BCS NO_SOUND2           ;  |
        LDA STAR_SOUNDS,y       ;  |    ... play sound effect
        STA $1DF9               ; /
NO_SOUND2:
	RTS                     ; final return

KILLED_X_SPEED:
	dcb $F0,$10
Hop:
        LDA #$40		; If timer isn't 40..
        CMP $C2,x		; 
        BNE IncreaseHop	; Increase it.
        LDA $1588,x		; Don't jump if already on ground.
        AND #$04		; 
        BEQ RETURN3	; 
        LDA #$D8		; Set jump height.
        STA $AA,x		; Store it too. 
        STZ $C2,x		; Reset timer.
        RTS		; Return
IncreaseHop:
        INC $C2,x		; Increase timer.
        RETURN3
        RTS		; Return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; sprite graphics routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

TILEMAP
        dcb $22,$24,$22,$24	; Walking
	dcb $C0,$C0,$C0,$C0	; Turning
X_OFFSET
        dcb $00,$00,$00,$01	; Walking
	dcb $00,$00,$00,$01	; Turning

SubGfx:	
        JSL $20CA4F         ; sets y = OAM offset
        LDA $157C,x             ; \ $02 = direction
        STA $02                 ; / 

        LDA $1602,x
        STA $03                 
        PHX                     

        LDA $14C8,x		; If killed...
        CMP #$02
	BNE NotKilled
        STZ $03			
        LDA $15F6,x		;    ...flip vertically
        ORA #$80
        STA $15F6,x
	BRA DrawSprite
NotKilled:
	
	LDA ExtraProperty2,x	; If turning frame enambled...
	AND #$01
	BEQ NotTurning	
	LDA RAM_SprTurnTimer,x	;    ...and turning...
	BEQ NotTurning
	INC $03			;    ...set turning frame
	INC $03
	INC $03
	INC $03
NotTurning:

DrawSprite:
        PHX
        LDX $03
        LDA $00                 ; \ tile x position = sprite x location ($00)
        CLC
        ADC X_OFFSET,x
        STA $0300,y             ; /

	LDA $01                 ; \ tile y position = sprite y location ($01)
        STA $0301,y             ; /

        LDA TILEMAP,x           ;  |
        STA $0302,y             ; /
	PLX

	LDA $15F6,x             ; tile properties xyppccct, format
        LDX $02                 ; \ if direction == 0...
        BNE NO_FLIP             ;  |
        ORA #$40                ; /    ...flip tile
NO_FLIP:	
        ORA $64                 ; add in tile priority of level
        STA $0303,y             ; store tile properties

        PLX                     ; pull, X = sprite index
        LDY #$02                ; \ 460 = 2 (all 16x16 tiles)
        LDA #$00                ;  | A = (number of tiles drawn - 1)
        JSL $01B7B3             ; / don't draw if offscreen
        RTS                     ; return
                    

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ball routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

X_OFFSET            dcb $08,$FC
X_OFFSET2           dcb $00,$FF
X_SPEED_BALL        dcb $16,$EA

SUB_BALL_THROW      LDY #$E8                
                    LDA $157C,x             
                    BNE LABEL9c             
                    LDY #$18                
LABEL9c             STY $00                 
                    LDY #$07                
LABEL8              LDA $170B,y             
                    BEQ LABEL7              
                    DEY                     
                    BPL LABEL8              
                    RTS                     
LABEL7              LDA #$0D                
                    STA $170B,y             

                    PHY
                    LDA $157C,x
                    TAY
                    LDA $E4,x               
                    CLC
                    ADC X_OFFSET,y
                    PLY
                    STA $171F,y             

                    PHY
                    LDA $157C,x
                    TAY
                    LDA $14E0,x             
                    ADC X_OFFSET2,y
                    PLY
                    STA $1733,y             

                    LDA $D8,x               
                    CLC
                    ADC #$0F
                    STA $1715,y             
                    LDA $14D4,x             
                    ADC #$00
                    STA $1729,y             

                    LDA #$D0                
                    STA $173D,y             
                    PHY
                    LDA $157C,x
                    TAY
                    LDA X_SPEED_BALL,y
                    PLY
                    STA $1747,y             
                    BRA LABEL10             
LABEL6              RTS                     
LABEL10             LDA $1715,y             
                    ADC #$F7                
                    STA $1715,y             
                    BCC LABEL11c            
                    LDA $1729,y             
                    ADC #$FF                
                    STA $1729,y             
LABEL11c            RTS                     
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
MaybeFaceMario:
	LDA $7FAB10,x	; Face Mario if extra bit is set
	AND #$04
	BEQ Return4	
	JSR SubHorzPos         	; Face Mario
	TYA                       
	STA RAM_SpriteDir,X
Return4:	
	RTS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; routines below can be shared by all sprites.  they are ripped from original
; SMW and poorly documented
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SubVertPos:
	LDY #$00
        LDA $96
        SEC
        SBC $D8,x
        STA $0F
        LDA $97
        SBC $14D4,x
        BPL VertIncY
        INY
VertIncY:
        RTS   

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This routine determines which side of the sprite Mario is on.  It sets the Y register
; to the direction such that the sprite would face Mario
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SubHorzPos:
	LDY #$00
	LDA $94
	SEC
	SBC $E4,x
	STA $0F
	LDA $95
	SBC $14E0,x
	BPL HorzIncY
	INY 
HorzIncY:
	RTS 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; points routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

STAR_SOUNDS         dcb $00,$13,$14,$15,$16,$17,$18,$19
             
SUB_STOMP_PTS:
	PHY                      
        LDA $1697               ; \
        CLC                     ;  | 
        ADC $1626,x             ; / some enemies give higher pts/1ups quicker??
        INC $1697               ; increase consecutive enemies stomped
        TAY                     ;
        INY                     ;
        CPY #$08                ; \ if consecutive enemies stomped >= 8 ...
        BCS NO_SOUND            ; /    ... don't play sound 
        LDA STAR_SOUNDS,y       ; \ play sound effect
        STA $1DF9               ; /   
NO_SOUND:
	TYA                     ; \
        CMP #$08                ;  | if consecutive enemies stomped >= 8, reset to 8
        BCC NO_RESET            ;  |
        LDA #$08                ; /
NO_RESET:
	JSL GivePoints
        PLY                     
        RTS                     ; return



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SUB_OFF_SCREEN
; This subroutine deals with sprites that have moved off screen
; It is adapted from the subroutine at $01AC0D
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
DATA_01AC0D:
	dcb $40,$B0
DATA_01AC0F:
	dcb $01,$FF
DATA_01AC11:
        dcb $30,$C0
DATA_01AC19:
        dcb $01,$FF

SubOffScreen:
	JSR IsSprOnScreen       ; \ if sprite is not off screen, return                                       
        BEQ Return01ACA4        ; /                                                                           
        LDA $5B                 ; \  vertical level                                    
        AND #$01                ;  |                                                                           
        BNE VerticalLevel       ; /                                                                           
        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 OffScrEraseSprite   ; /    ...erase the sprite                                                    
        LDA $167A,X             ; \ if "process offscreen" flag is set, return                                
        AND #$04                ;  |                                                                           
        BNE Return01ACA4        ; /                                                                           
        LDA $13                   
        AND #$01                
        STA $01                   
        TAY                       
        LDA $1A                   
        CLC                       
        ADC DATA_01AC11,Y       
        ROL $00                   
        CMP $E4,X                 
        PHP                       
        LDA $1B                   
        LSR $00                   
        ADC DATA_01AC19,Y       
        PLP                       
        SBC $14E0,X             
        STA $00                   
        LSR $01                   
        BCC ADDR_01AC7C           
        EOR #$80                
        STA $00                   
ADDR_01AC7C:
        LDA $00                   
        BPL Return01ACA4          
OffScrEraseSprite:
	LDA $14C8,X             ; \ If sprite status < 8, permanently erase sprite 
        CMP #$08                ;  | 
        BCC OffScrKillSprite    ; / 
        LDY $161A,X             
        CPY #$FF                
        BEQ OffScrKillSprite      
        LDA #$00                
        STA $1938,Y             
OffScrKillSprite:
	STZ $14C8,X             ; Erase sprite 
Return01ACA4:
	RTS                       

VerticalLevel:
	LDA $167A,X             ; \ If "process offscreen" flag is set, return                
        AND #$04                ;  |                                                           
        BNE Return01ACA4        ; /                                                           
        LDA $13                 ; \                                                           
        LSR                     ;  |                                                           
        BCS Return01ACA4        ; /                                                           
        LDA $E4,X               ; \                                                           
        CMP #$00                ;  | If the sprite has gone off the side of the level...      
        LDA $14E0,X             ;  |                                                          
        SBC #$00                ;  |                                                          
        CMP #$02                ;  |                                                          
        BCS OffScrEraseSprite   ; /  ...erase the sprite      
        LDA $13                   
        LSR                       
        AND #$01                
        STA $01                   
        TAY                       
	LDA $1C                   
        CLC                       
        ADC DATA_01AC0D,Y       
        ROL $00                   
        CMP $D8,X                 
        PHP                       
        LDA $001D               
        LSR $00                   
        ADC DATA_01AC0F,Y       
        PLP                       
        SBC $14D4,X             
        STA $00                   
        LDY $01                   
        BEQ ADDR_01ACF3           
        EOR #$80                
        STA $00                   
ADDR_01ACF3:
        LDA $00                   
        BPL Return01ACA4          
        BMI OffScrEraseSprite  

IsSprOnScreen:
	LDA $15A0,X             ; \ A = Current sprite is offscreen 
        ORA $186C,X             ; /  
        RTS                     ; Return 
		
