;
;  dirt
;
;  Created by Quinn Dunki on 12/29/19
;

MAXPARTICLES = 64

.macro PARTICLEPTR_XY
	txa		; Pointer to particle structure from index
	asl
	asl
	asl
	asl
	tay
.endmacro


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; createDirtExplosion
;
; Simulate a bunch of dirt flying everywhere
;
; PARAML0 = X pos of circle left edge
; PARAML1 = Y pos of circle center
; PARAML2 = Radius (16 bits)
;
; Trashes SCRATCHL,SCRATCHL2,CACHEDATA
;
createDirtExplosion:
	SAVE_AXY

	lda #1
	sta dirtExplosionActive

	lda #3			; Radius hard-coded for now
	sta PARAML2
	asl
	sta CACHEDATA	; Cache diameter

	ldx #0
	ldy #0
createDirtExplosionLoop:
	jsr createDirtExplosionColumn
	inc PARAML0
	inc PARAML0
	iny
	cpy CACHEDATA
	bne createDirtExplosionLoop

	RESTORE_AXY
	rts


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; createDirtExplosionColumn
;
; Create one column of a starting dirt explosion circle
; X = Particle index to start filling from
; Y = Local X index of column
; PARAML0 = X pos of column
; PARAML1 = Y pos of circle center
; PARAML2 = Radius (16 bits)
;
; X=> Last particle index we filled in +1
; Trashse SCRATCHL,SCRATCHL2
;
createDirtExplosionColumn:
	SAVE_AY
	lda PARAML2
	pha

	stx SCRATCHL2 ; Cache particle index

	phy				; Look up circle table for our radius
	lda PARAML2
	asl
	tay
	lda circleTable,y
	sta SCRATCHL

	; Find starting Y from circle table
	pla
	asl
	tay
	lda (SCRATCHL),y
	tax			; Row counter
	pha
	eor #$ffff
	inc
	sta PARAML2	; Row counter end

	pla
	clc
	adc PARAML1
	sta SCRATCHL ; Current Y position
	
createDirtExplosionColumnLoop:
	phx				; Find pointer to next particle to fill out
	ldx SCRATCHL2
	PARTICLEPTR_XY
	plx

	; X position - Always the same
	lda PARAML0
	sta dirtParticles+DP_POSX,y
	asl		; Convert to 12.4
	asl
	asl
	asl
	sta dirtParticles+DP_PRECISEX,y

	; Y position - Iterates from computed cache
	lda SCRATCHL
	sta dirtParticles+DP_POSY,y
	asl		; Convert to 12.4
	asl
	asl
	asl
	sta dirtParticles+DP_PRECISEY,y

	; X velocity. Need half to be negative. I feel like there
	; should be a clever branchless way to do this, but I couldn't
	; come up with it.
	jsr random
	lda RANDOM
	bmi createDirtExplosionColumnLoopNegX
	and #$00ff
	bra createDirtExplosionColumnLoopNowY

createDirtExplosionColumnLoopNegX:
	ora #$ff00

createDirtExplosionColumnLoopNowY:
	sta dirtParticles+DP_VX,y

	; Y velocity
	jsr random
	lda RANDOM
	and #$00ff
	ora #$0100
	sta dirtParticles+DP_VY,y

	; Advance to next particle
	inc SCRATCHL2
	inc SCRATCHL
	inx
	cpx PARAML2
	bmi createDirtExplosionColumnLoop

	; Gather return values
	ldx SCRATCHL2

	pla
	sta PARAML2
	RESTORE_AY
	rts


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; renderDirtExplosion
;
; Update and render the current dirt explosion
;
renderDirtExplosion:
	SAVE_AXY

	lda dirtExplosionActive
	beq renderDirtExplosionDone

	ldx #0
	stz dirtExplosionActive		; Assume we're done unless a particle says otherwise

renderDirtExplosionLoop:
	PARTICLEPTR_XY
	jsr updateDirtParticle
	inx			; Advance array pointer to next particle
	cpx #MAXPARTICLES
	bne renderDirtExplosionLoop

renderDirtExplosionDone:
	RESTORE_AXY
	rts


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; updateDirtParticle
;
; Y = Offset to particle in array
; Update and render the current dirt explosion
;
; Trashes SCRATCHL,PARAML0

updateDirtParticle:
	SAVE_AXY

	lda dirtParticles+DP_POSX,y
	bpl updateDirtParticleContinue
	jmp updateDirtParticleDone	; Particle is dead

updateDirtParticleContinue:
	; Erase old position
	lda #dirtParticles		; Calculate pointer to struct
	sta PARAML0
	clc
	tya
	adc PARAML0
	sta PARAML0
	jsr vramPtr
	lda #00
	sta SHADOWVRAMBANK,x

	; Integrate gravity over velocity
	lda dirtParticles+DP_VY,y
	clc
	adc #GRAVITY
	sta dirtParticles+DP_VY,y

	; Integrate X velocity over position
	lda dirtParticles+DP_VX,y
	; Convert 8.8 to 12.4
	cmp #$8000
	ror
	cmp #$8000
	ror
	cmp #$8000
	ror
	cmp #$8000
	ror
	clc
	adc dirtParticles+DP_PRECISEX,y
	sta dirtParticles+DP_PRECISEX,y

	; Convert to integer for rendering
	lsr
	lsr
	lsr
	lsr
	sta dirtParticles+DP_POSX,y

	; Integrate Y velocity over position
	lda dirtParticles+DP_VY,y
	; Convert 8.8 to 12.4
	cmp #$8000
	ror
	cmp #$8000
	ror
	cmp #$8000
	ror
	cmp #$8000
	ror
	clc
	adc dirtParticles+DP_PRECISEY,y
	sta dirtParticles+DP_PRECISEY,y

	; Convert to integer for rendering
	lsr
	lsr
	lsr
	lsr
	sta dirtParticles+DP_POSY,y

	; Check new position
	jsr vramPtr		; PARAML0 still holds struct pointer
	cpx #$ffff
	beq updateDirtParticleKill			; Offscreen, so we're done
	lda SHADOWVRAMBANK,x
	beq updateDirtParticleStillAlive	; All sky, so carry on
;	cmp #$11
;	bne updateDirtParticleKill			; Not dirt, so we're done
	lda dirtParticles+DP_VY,y
	bmi updateDirtParticleKill			; +Y velocity on dirt, so we're done
	
updateDirtParticleStillAlive:
	; Draw new position
	lda #$33
	sta SHADOWVRAMBANK,x

	; Let everyone know we did work
	lda #1
	sta dirtExplosionActive
	
updateDirtParticleDone:
	RESTORE_AXY
updateDirtParticleDoneRTS:
	rts

updateDirtParticleKill:
	lda #-1
	sta dirtParticles+DP_POSX,y
	bra updateDirtParticleDone

dirtExplosionActive:
	.word 0

dirtParticles:
.repeat MAXPARTICLES
	.word -1	; Pos X in pixels
	.word 100	; Pos Y in pixels
	.word $a00	; Pos X (12.4 fixed point)
	.word $640	; Pos Y (12.4 fixed point)
	.word $ff00	; Velocity X (8.8 fixed point)
	.word $100	; Velocity Y (8.8 fixed point)
	.word 0,0	; Pad to 16 bytes
.endrepeat


DP_POSX = 0		; Byte offsets into dirtParticles data structure
DP_POSY = 2
DP_PRECISEX = 4
DP_PRECISEY = 6
DP_VX = 8
DP_VY = 10
