; MarsJump.asm
;			by Maddy, allegedly
;			modified from Airjump and Airspin by HammerBrother, found here: https://www.smwcentral.net/?p=section&a=details&id=14759

; Created for 'Life on Mars' in Super "Sonic Saves the World" World
; The player may jump one additional time in mid-air (this can be a regular or spin jump)
; After the air jump is used, the player's palette will flash
; Touching the ground or bouncing on something refreshes this ability

; Feel free to reuse this code however you want. HammerBrother should be credited. Crediting me is appreciated too :)

; Usage: This is intended to be used as an UberASM library file, called from many level files
;	     It could also be used in gamemode 14 to apply to your entire game

; ------------------------------ 
; CONFIG OPTIONS
; ------------------------------ 

; Normal jump vertical speed
!NJumpYSpeed = $B0

; Spin jump vertical speed
!SJumpYSpeed = $B6	

; amount of frames after bouncing on something and refilling your airjump where the airjump is disabled
; this allows the player to perform regrabs without accidentally airjumping
!cooldown = $0A

; if 1, flash the player palette when the float timer is low
; this requires another patch (Manual Player Palette Update Patch, by imamelia, https://www.smwcentral.net/?p=section&a=details&id=4517)
; I'm using a special version of this patch with modifications by DtotheFourth, so I've disabled this option by default
; if you know what you're doing, you should be able to get it working though!
!set_palette = 0

; ------------------------------
; FreeRAM ADDRESSES
; make sure these don't overlap with RAM already in use by other ASM in these levels
; ------------------------------

; whether you currently can double jump
; -> 1 byte
!RAM_djump = $58

; a cooldown timer
; while active you cannot double jump even if you otherwise would be able to
; -> 1 byte
!RAM_cooldown = $87

; the previous frame's y-speed or 0, whichever is smaller
; we use this to detect bounces
; -> 1 byte
!RAM_last = $5C	

; when this is set, the palette will be uploaded
; This must match the address in Manual Player Palette Update Patch
; -> 1 byte
!RAM_PalUpdateFlag = $7FB200 

; a pointer to the player palette to upload
; This must match the address in Manual Player Palette Update Patch
; -> 2 bytes
!RAM_PlayerPalPtr  = $7FB202

; ------------------------------
; DATA
; ------------------------------

if !set_palette
	; dthe palette to flash while the airjump is exhausted
	Palette_Flash:
		dw $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF, $FFFF
endif

; ------------------------------ 
; GAME LOGIC CODE
; ------------------------------ 

init:	
	LDA #$01
	STA !RAM_djump
	STZ !RAM_cooldown
	STZ !RAM_last
	RTL

main:

	; flash palette
	if !set_palette	
	; {
		.Palette
		LDA !RAM_djump
		BNE +
		LDA $71
		BNE +
		LDA $14
		AND #%00000100
		BEQ +
		
		LDA #$01
		STA !RAM_PalUpdateFlag		
		LDA #Palette_Flash>>16
		STA !RAM_PlayerPalPtr+2
		REP #$20
		LDA.w #Palette_Flash
		STA !RAM_PlayerPalPtr
		SEP #$20	
		+
	; }
	endif
	
	; try air jumping
	; {
		.AirJump
		..Checks
		LDA $77				;\If mario is on ground, restore jumps
		AND.b #%00000100		;|
		BNE ..RestoreJumps		;/
		LDA $74				;>If net/vine climbing...
		ORA $75				;>Or water
		BNE ..RestoreJumps		;>Then restore jumps
		LDA !RAM_cooldown	; Cooldown timer after a bounce
		BNE ..Return
		LDA $71				;\Don't jump when doing such actions such as dying.
		BNE ..Return			;/
		LDA $1407+!addr			;>If cape flying
		ORA $75				;>and/or swimming
		BNE ..Return			;>Then return.
		LDA $16				;\If pressing jump, confirm
		BMI ...ContinueChecking		;/
		LDA $18				;\If not pressing spinjump nor jump, return
		BPL ..Return			;/

		...ContinueChecking
			LDA !RAM_djump	;\If no jumps left, return.
			BEQ ..Return			;/

		..JumpConfirm
			STZ !RAM_djump	;\Consume jump
		
		..TypeOfJump
		LDA $16				;\Go to normal jump if B is pressed.
		BMI ...NormalJump		;/
		LDA $18				;\Go to spinjump if A is pressed.
		BPL ..Return			;/

		...Spinjump
		LDA #$01			;\Switch to spinjump
		STA $140D+!addr			;/
		LDA #!SJumpYSpeed		;\Spinjump up
		STA $7D				;/
		STA !RAM_last
		STZ !RAM_cooldown
		LDA #$04			;\SFX
		STA $1DFC+!addr			;/
		BRA ..BothJumps

		...NormalJump
		STZ $140D+!addr			;>Switch to normal jump
		LDA #!NJumpYSpeed		;\Jump up
		STA $7D				;/
		STA !RAM_last
		STZ !RAM_cooldown
		LDA #$01			;\SFX
		STA $1DFA+!addr			;/

		..BothJumps
		JSR SUB_SMOKE
		LDA #$01			; Update camera vertical scroll
		STA $1404
		LDA #$0B			;\Jump pose (replaces long jump pose)
		STA $72				;/	
		RTL
		
		..RestoreJumps
		LDA #$01
		STA !RAM_djump

		..Return
	; }
	
	; update the cooldown timer
	; {
		LDA !RAM_cooldown
		BEQ +
		DEC !RAM_cooldown
		+
	; }
	
	; check if y-speed less than previous, if so you bounced.
	; then update the previous y-speed var
	; {	
		; check for refill
		LDA $7D
		BPL +
		CMP !RAM_last
		BCS +
		LDA #$01
		STA !RAM_djump
		LDA #!cooldown
		STA !RAM_cooldown
		+
		
		; update var
		LDA $7D
		BPL +
		CMP !RAM_last
		BCS ++
		STA !RAM_last
		BRA ++
		+
		LDA #$FF
		STA !RAM_last
		++
	; }
	RTL
	
SUB_SMOKE:          LDY #$03                ; \ find a free slot to display effect
FINDFREE:           LDA $17C0,y             ;  |
                    BEQ FOUNDONE            ;  |
                    DEY                     ;  |
                    BPL FINDFREE            ;  |
                    RTS                     ; / return if no slots open

FOUNDONE:           LDA #$01                ; \ set effect graphic to smoke graphic
                    STA $17C0,y             ; /
                    LDA #$14                ; \ set time to show smoke
                    STA $17CC,y             ; /
                    LDA $96               ; \ smoke y position = generator y position
					ADC #$10
                    STA $17C4,y             ; /
                    LDA $94               ; \ load generator x position and store it for later
                    STA $17C8,y             ; /
                    RTS