; FullMoonEclipse.asm
;			by Maddy, allegedly

; Created for 'Daddy's Castle III: Full Moon Eclipse' in Super "Sonic Saves the World" World
; The player collects a coin to start the camera autoscroll, a music track begins playing
; From there, the ASM sets the camera speed and spawns sprites to the beat of the music until it ends

; Feel free to reuse this code however you want. Credit is appreciated :)

; Usage: This is intended to be used as an UberASM level file.
;		However, I recognize that it is not reasonable to reuse this ASM file for other purposes
;		I included it only as an example, and because it was a ton of work so I want to show it off!!
;		As such, it is not very well-documented, apologies


; ------------------------------ 
; GENERATED TIMESTAMP DATA
; ------------------------------ 
; These values were generated by a spreadsheet.
; I painstakingly marked the beats I wanted to use in the song, and then defined all the events I wanted to trigger in that spreadsheet.
; The spreadsheet has a script to convert song timestamps to hex frame values, and export all of the data in this format which this code can use.
; This is the reason why it'd be pretty hard for anyone to reuse this code in their own hacks, but if you want to try editing these by hand be my guest.

Scroll_Times:  dw $0000, $01AF, $01C1, $027A, $028C, $02E0, $0346, $0364, $0411, $0423, $0477, $04DD, $0517, $0528, $053A, $0546, $054E, $056C, $05A8, $06F3, $096E, $0C1D, $0ECD, $0ED9, $0EE5, $0EF1, $0EFF, $0F0B, $0F18, $0F24, $0F32, $1000, $1030, $1062, $112E
Scroll_Speeds: dw $00E0, $0200, $00E0, $0200, $00E0, $0020, $0200, $00E0, $0200, $00E0, $0020, $0200, $0100, $0080, $0040, $0020, $FFC0, $FF80, $0200, $0180, $0100, $00C0, $0200, $00C0, $0200, $00C0, $0200, $00C0, $0200, $00C0, $0200, $0280, $0300, $03A0, $0320
!Scroll_Total = $23
Spawn_Times:  dw $01AF, $027A, $02E0, $02F9, $0313, $032C, $0346, $0477, $0491, $04AA, $04C3, $04DD, $050E, $04D5, $0513, $0515, $051E, $05D7, $05F5, $0611, $0628, $0642, $065B, $0674, $068D, $06AA, $06C3, $06D9, $06F3, $070C, $0726, $073F, $0758, $0773, $078C, $07A8, $07C1, $07D9, $07F2, $080B, $0824, $0840, $0859, $0870, $0889, $08A3, $08BD, $08D6, $08D6, $093D, $096E, $09A5, $0A0A, $0A6D, $0AD4, $0B9E, $0BB7, $0BE1, $0C1D, $0C35, $0C43, $0C5D, $0C6B, $0C78, $0C83, $0C9C, $0CAA, $0CC3, $0CD1, $0CD1, $0D29, $0D38, $0D52, $0D67, $0D77, $0D9B, $0D9E, $0DE6, $0DF2, $0DDA, $0DF4, $0E43, $0E5A, $0E66, $0E81, $0EA6, $0EA7, $0EA7, $0ECD, $0ECE, $0ED0, $1194, $11AE, $11C7, $11E0, $11F9, $1213
Spawn_Sprites: db $1C, $1C, $1C, $1C, $1C, $1C, $07, $1C, $1C, $1C, $1C, $9F, $21, $21, $21, $21, $21, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $1C, $07, $07, $9F, $07, $07, $9F, $07, $9F, $9F, $9F, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $21, $9F, $9F, $9F, $9F, $9F, $9F
Spawn_YPositions: dw $0100, $0110, $0170, $0150, $0130, $0110, $00F0, $0160, $0140, $0150, $0130, $0140, $00B0, $00B0, $00B0, $00B0, $00B0, $00E0, $00E0, $00F0, $0100, $0110, $0120, $0130, $0140, $0150, $0160, $0170, $0170, $0160, $0150, $0140, $0130, $0120, $0110, $0100, $00F0, $00E0, $00E0, $00F0, $0100, $0110, $0120, $0130, $0140, $0150, $0160, $0170, $00E0, $00E0, $00E0, $0120, $00E0, $0140, $00F0, $0150, $0130, $0110, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $00B0, $0160, $00D0, $0160, $00D0, $0160, $00D0
Spawn_Sides: db $01, $01, $01, $00, $01, $00, $01, $00, $00, $01, $01, $01, $B0, $A0, $C0, $90, $D0, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $D0, $E0, $C0, $20, $10, $30, $40, $70, $90, $80, $60, $E0, $50, $60, $70, $80, $90, $A0, $A0, $B0, $C0, $70, $F0, $D0, $80, $A0, $70, $90, $B0, $70, $C0, $B0, $D0, $01, $01, $01, $01, $01, $01
!Spawn_Total = $61
Shell_Speeds: db $30, $CF
BG_Pulse_Times:  dw $01AF, $027A, $02E0, $02F9, $0313, $032C, $0346, $0411, $0477, $0491, $04AA, $04C3, $04DD, $050E, $05A8, $05D7, $05F5, $0611, $0628, $0642, $065B, $0674, $068D, $06AA, $06C3, $06D9, $06F3, $070C, $0726, $073F, $0758, $0773, $078C, $07A8, $07C1, $07D9, $07F2, $080B, $0824, $0840, $0859, $0870, $0889, $08A3, $08BD, $08D6, $08D6, $093D, $096E, $09A5, $0A0A, $0A3D, $0A6D, $0AD4, $0B39, $0B9E, $0BB7, $0BE1, $0C0C, $0C70, $0C8A, $0CD5, $0CEE, $0D3A, $0D53, $0D9F, $0DB8, $0E04, $0E1D, $0E68, $0E82, $0ECD, $0EE5, $0EFF, $0F18, $0F37, $0F9C, $1001, $101A, $1033, $104C, $1066, $1098, $10CA, $10FD, $112F, $1194, $11AE, $11C7, $11E0, $11F9, $1213, $122F, $125E
!BG_Pulses_Total = $5E
!Ev_ActivateP = $08E8
!Ev_DeactivateP = $0C34
!Ev_GetCape = $0F63
!Ev_Ending = $125F

macro call_library(i)
	PHB
	LDA.b #<i>>>16
	PHA
	PLB
	JSL <i>
	PLB
endmacro

States: dw st_waitforblock, st_delay, st_scroll, st_no_left, st_end_delay, st_end

!block_delay = $04
!no_left_time = $20
!end_delay = $94

; ------------------------------ 
; FREERAM
; ------------------------------ 

!RAM_state 		= $58
!RAM_Speed		= $0F5E	; speed, 2 bytes	
!RAM_timer 		= $0F60	; timer, 2 bytes
!RAM_scroll		= $0F62	; 1 byte
!RAM_spawn 		= $0F63	; 1 byte
!RAM_pulse 		= $0F71
!RAM_palette_update_flag = $7FB600
!RAM_palette_level = $0F69	; 1 byte
!RAM_palette_timer = $0F70	; 1 byte'

!ARG_id			= $0F65 ; 1 byte
!ARG_side		= $0F66 ; 1 byte
!ARG_ypos		= $0F67 ; 2 bytes

; ------------------------------ 
; PALETTE CHANGE CONFIG
; ------------------------------ 

!palette_fade_time = $04
!palette_level_max = $06

!bg_color = $0000
bg_palette:
	dw $775D, $6E3B, $5A36, $4DD3, $41D0, $398D, $394C, $38E8, $30A7, $2C65, $2442, $2041, $1C20, $1820, $0000
	
planet_palette:
	dw $0000, $6AF2, $6690, $5E6D, $4DCA, $5A2A, $4548, $3967, $2CC4, $24A4, $1842, $0800, $0000, $0000, $0000, $0000
	dw $0000, $6A92, $6630, $5E2D, $4DCA, $5A2A, $4548, $3967, $2CC4, $24A4, $1842, $0800, $0000, $0000, $0000, $0000
	dw $0000, $6A92, $6630, $5E2D, $4DCA, $5A2A, $4548, $3967, $2CC4, $24A4, $1842, $0800, $0000, $0000, $0000, $0000
	dw $0000, $6A52, $6610, $5E0D, $4DCA, $5A2A, $4548, $3967, $2CC4, $24A4, $1842, $0800, $0000, $0000, $0000, $0000
	dw $0000, $6A52, $6610, $5E0D, $4DCA, $5A2A, $4548, $3967, $2CC4, $24A4, $1842, $0800, $0000, $0000, $0000, $0000
	dw $0000, $6A73, $6A92, $5E0D, $4DCA, $5A2A, $4548, $3967, $2CC4, $24A4, $1842, $0800, $0000, $0000, $0000, $0000
	dw $0000, $6A73, $6A92, $5E0D, $4DCA, $5A2A, $4548, $3967, $2CC4, $24A4, $1842, $0800, $0000, $0000, $0000, $0000
	
end_palette_levels:
	db $00, $00, $00, $00, $00, $01, $01, $02, $03, $04, $05, $05, $06, $06, $06, $06, $06, $06, $06, $06, $05, $05, $04, $03, $02, $01, $01, $00, $00, $00
!total_end_palette = $1E

; ------------------------------ 
; CODE
; ------------------------------ 

init:
	%call_library(FinaleAutoscroll_init)	; Constant horizontal/vertical autoscroll UberASM by KevinM
	
	REP #$20
	STZ.w !RAM_timer
	LDA.w Scroll_Speeds
	STA.w !RAM_Speed
	SEP #$20
	
	STZ !RAM_state	
	STZ !RAM_scroll
	STZ !RAM_spawn
	STZ !RAM_pulse
	STZ $0DBF
	
	; set translevel to Daddy's Castle
	LDA #$4D
	STA $13BF
	
	STZ !RAM_palette_level
	LDA #$00
	STA !RAM_palette_update_flag
	
	RTL
	
main:

	LDA !RAM_state
	ASL
	TAX
	JMP (States,X)
	
; waiting for the block to be hit
st_waitforblock:
	LDA $1463	; lock camera right side
	CMP #$02
	BNE +
	STZ $1462
	+	
	LDA $0DBF
	BEQ +
	INC !RAM_state	; goto state 1
	LDA #!block_delay
	STA !RAM_timer
	+
	RTL
	
; delay timer
st_delay:
	LDA #$02		; lock camera
	STA $1463
	STZ $1462	
	LDA $72
	BNE +
	DEC !RAM_timer	; countdown
	BNE +	
	; play music
	LDA #$51
	STA $1DFB
	; get midway
	LDA #$01
	STA $13CE
	; set timer to first in list
	INC !RAM_state
	STZ !RAM_timer
	STZ !RAM_timer+1
	+
	RTL
	
st_scroll:
	%call_library(NoPause_main)				; Disable Pause by dtothefourth (VERY IMPORTANT because pausing desyncs the music)
	%call_library(FinaleAutoscroll_main)	; Constant horizontal/vertical autoscroll UberASM by KevinM
	
	; dead
	LDA $71
	CMP #$09
	BNE +
	LDA #$80
	STA $1DFB
	RTL
	+
	
	; palette update
	LDA !RAM_palette_level
	BEQ +
	DEC !RAM_palette_timer
	BNE +
	DEC !RAM_palette_level
	JSL update_palette
	LDA !RAM_palette_level
	BEQ +
	LDA #!palette_fade_time
	STA !RAM_palette_timer
	+
	
	; increment timer
	REP #$20
	INC.w !RAM_timer
	SEP #$20

	; hard-coded events
	REP #$20
	LDA !RAM_timer
	CMP #!Ev_Ending
	BCC +
	; ending
	SEP #$20
	INC !RAM_state
	STZ $19
	STZ $1407
	STZ $13E1
	LDA #$D0
	STA $7D
	LDA #!no_left_time
	STA !RAM_timer
	JSR SUB_SMOKE
	BRA ++
	+
	CMP #!Ev_GetCape
	BNE +
	; get cape
	SEP #$20
	LDA #$02
	STA $19
	LDA #$0A
	STA $1DF9
	JSR SUB_SMOKE
	BRA ++
	+
	CMP #!Ev_DeactivateP
	BCC +
	; deactivate P
	SEP #$20
	LDA #$00
	STA $7E14AD
	BRA ++
	+
	CMP #!Ev_ActivateP
	; activate P
	SEP #$20
	BCC +
	LDA #$30
	STA $7E14AD
	+
	++
	SEP #$20
	
	; get index in scroll array
	; {
		LDA !RAM_scroll
		CMP #!Scroll_Total-1
		BEQ +
		CLC : ASL
		TAX
		
		; check timer
		REP #$20
		LDA.w !RAM_timer
		CMP.w Scroll_Times+2,X
		SEP #$20
		BCC +
		
		; if past next scroll speed time, go to that speed!
		INC !RAM_scroll
		REP #$20
		LDA.w Scroll_Speeds+2,X
		STA.w !RAM_Speed
		SEP #$20
		+
	; }	
	
	; get index in spawn array
	; {
		-
		LDA !RAM_spawn
		CMP #!Spawn_Total
		BEQ +
		CLC : ASL
		TAX
		
		; check timer
		REP #$20
		LDA.w !RAM_timer
		CMP.w Spawn_Times,X
		SEP #$20
		BCC +
		
		; y
		REP #$20
		LDA.w Spawn_YPositions,X
		STA.w !ARG_ypos
		SEP #$20
		; sprite
		LDA !RAM_spawn
		TAX
		LDA Spawn_Sprites,X
		STA !ARG_id	
		; side
		LDA Spawn_Sides,X
		STA !ARG_side	
		
		; spawn
		JSR spawn_sprite
		
		; increment counter
		INC !RAM_spawn
		BRA -
		+
	; }
	
	; get index in bg pulse array
	; {
		-
		LDA !RAM_pulse
		CMP #!BG_Pulses_Total
		BEQ +
		CLC : ASL
		TAX
		
		; check timer
		REP #$20
		LDA.w !RAM_timer
		CMP.w BG_Pulse_Times,X
		SEP #$20
		BCC +
		
		LDA #$0A
		STA !RAM_palette_timer
		LDA #!palette_level_max
		STA !RAM_palette_level
		JSL update_palette
		
		; increment counter
		INC !RAM_pulse
		BRA -
		+	
	; }
	
	RTL
	
st_no_left:
	%call_library(DisableLeft_main)		; uberASM to disable controller buttons by janklorde 
	
	LDA #$01
	STA $76
	
	DEC !RAM_timer
	BNE +
	INC !RAM_state
	LDA #!end_delay
	STA !RAM_timer
	+
	RTL

st_end_delay:
	%call_library(DisableRun_main)		; uberASM to disable controller buttons by janklorde 
	
	; palette update
	LDA $14
	AND #$01
	BNE +
	LDA !RAM_palette_level
	BEQ +
	DEC !RAM_palette_timer
	BNE +
	DEC !RAM_palette_level
	JSL update_palette
	LDA !RAM_palette_level
	BEQ +
	LDA #!palette_fade_time
	STA !RAM_palette_timer
	+
	
	DEC !RAM_timer
	BNE +
	INC !RAM_state
	LDA #$56
	STA $1DFB
	STZ !RAM_pulse
	+
	RTL
	
st_end:
	%call_library(DisableRun_main)		; uberASM to disable controller buttons by janklorde 
	
	LDA $14
	AND #%00000111
	BNE ++
	INC !RAM_pulse
	LDA !RAM_pulse
	CMP #!total_end_palette
	BCC +
	STZ !RAM_pulse
	+
	LDX !RAM_pulse
	LDA end_palette_levels,X
	STA !RAM_palette_level
	JSL update_palette
	++
	
	RTL
	
; $00 - sprite number
; $01 - side
; $02 - ypos (2 bytes)
spawn_sprite:
	JSL $02A9DE
	BMI end_spawn
	TYX
	
	; sprite id
	LDA !ARG_id	
	STA $009E,X	; id	
	;JSL $0187A7
	JSL $07F7D2
	
	; init based on sprite id
	LDA !ARG_id	
	CMP #$07
	BEQ +
	; non-shell
	LDA #$01	
	STA $14C8,X	; state
	LDA !ARG_side
	STA $7E157C,X	; facing
	BRA ++
	+
	; shell-specific
	LDA #$0A		
	STA $14C8,X	; state
	LDA !ARG_side
	TAY
	LDA Shell_Speeds,Y
	STA $B6,X
	++
	
	; x
	LDA !ARG_side
	BNE +
	REP #$20				; 00 = left side of screen
	LDA $1462
	SEC : SBC.w #$0010
	SEP #$20
	BRA ++
	+
	CMP #$01
	BNE +
	REP #$20				; 01 = right side of screen
	LDA.w $1462
	CLC : ADC.w #$0100
	SEP #$20
	BRA ++
	+
	LDA #$00				; other = custom x-value
	XBA
	LDA !ARG_side
	REP #$20
	CLC : ADC.w $1462
	SEP #$20	
	++
	STA $E4,X				; store x-position
	XBA
	STA $14E0,X
	
	; y
	LDA !ARG_ypos
	STA $00D8,X
	LDA !ARG_ypos+1
	STA $14D4,X
	
	LDA #$00	
	STA $7FAB10,X
	
	end_spawn:
	RTS
	
update_palette:	
	
	; first palette
	; {
		; X = palette slot
		LDX #$82
		LDY #$00
		-
		; get the color into A
		; {
			PHX
			
			CPY !RAM_palette_level
			BEQ .full
			BCC .full
			
			; it's part of the fade-out gradient
			TYA
			SEC : SBC !RAM_palette_level
			CMP #$0F
			BCC +
			LDA #$0E	; <-- if past max, use the final color
			+		
				
			ASL		
			TAX
			REP #$20
			LDA bg_palette,X	
			BRA ++		
			
			; it's one of the fully-bright colors
			.full
			REP #$20
			LDA bg_palette
			++
			
			PLX
		; }
	
		; insert the color into the palette	
		STA $0703,X				
		SEP #$20
		INX : INX
	
		INY
		CPY #$0F
		BCC -
	; }
	
	; second palette
	; {
		; X = palette slot
		LDX #$22
		LDY #$00
		-
		; get the color into A
		; {
			PHX
		
			; all in second palette are in the fade-out gradient
			TYA
			CLC : ADC #$0F
			SEC : SBC !RAM_palette_level
			CMP #$0F
			BCC +
			LDA #$0E	; <-- if past max, use the final color
			+		
			
			ASL		
			TAX
			REP #$20
			LDA bg_palette,X		
			
			PLX
		; }
	
		; insert the color into the palette
		STA $0703,X	
		SEP #$20		
		INX : INX
	
		INY
		CPY #$0F
		BCC -
	; }
	
	; planet palette
	; {
		LDX #$62	; x = dest palette slot
	
		LDA !RAM_palette_level
		ASL #5
		TAY			; y = source palette id
		INY : INY
	
		REP #$20
		-
		LDA planet_palette,Y
		STA $0703,X	
		INY : INY
		INX : INX
		CPX #$78
		BCC -
		SEP #$20
	; }
	
	LDA #$01
	STA !RAM_palette_update_flag
	RTL
	
nmi:
	LDA $71
	BNE +
    LDA !RAM_palette_update_flag
    BEQ +
	LDA #$00
	STA !RAM_palette_update_flag   
    JSR UploadPal
    +
    RTL

UploadPal:
    REP #$20
    LDY #$00                ;\Set Address for CG-RAM Write to 00
    STY $2121               ;/Address for CG-RAM Write
    LDA #$2200              ;\One register write once, to 2122
    STA $4330               ;/
    LDA #$0703|!addr        ;\Address of palette to upload
    STA $4332               ;/ 
    LDY.b #$7E                ;\Bank to grab palette from
    STY $4334               ;/ 
    LDA #$0200              ;\Upload the whole palette, 200 bytes
    STA $4335               ;/
    LDX #$08                ;\Start DMA
    STX $420B                ;/
    SEP #$20      
    RTS
	
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
					CLC : ADC #$08
                    STA $17C4,y             ; /
                    LDA $94               ; \ load generator x position and store it for later
                    STA $17C8,y             ; /
                    RTS