; RNGdaddy.asm
; 		by Maddy, allegedly
 
; Created for the second part of 'Daddy's Castle' in Super "Sonic Saves the World" World
; A custom RNG castle with a rudimentary lives system

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

; Usage: place this file in your UberASM library folder
; Create an asm file in your UberASM level folder for each sublevel involved
; and then, from those level asm files, call the appropriate init and main routines in this library as listed here...

; LEVELS:
;	intro --- the sublevel before the RNG starts (see example_intro_level.asm)
;		The init routine will assign all screen exits in this sublevel automatically, so there's no need to set those in Lunar Magic
;		INIT ROUTINE: RNGdaddy_init_intro
;		MAIN ROUTINE: RNGdaddy_main
;	end --- the sublevel(s) that the routines will take you to after you've completed the challenge (see example_end_level.asm)
;		INIT ROUTINE: RNGdaddy_init_end
;		MAIN ROUTINE: RNGdaddy_main
;	rooms --- the actual challenge rooms that will be randomly ordered (see example_level.asm)
;		The init routine will assign all screen exits in these sublevels automatically, so there's no need to set those in Lunar Magic
;		INIT ROUTINE: RNGdaddy_init
;		MAIN ROUTINE: RNGdaddy_main

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

; the total amount of randomly ordered challenge rooms to play
!total_rooms = $08

; the list of challenge rooms to be randomly ordered
Rooms:
	dw $0190, $0191, $0192, $0193, $0194, $0195, $0196, $0197

; if you fail this many rooms, the last death sends you back to your last midway (I recommend that this be in the intro room)
; until then, a death will keep you going forward to the next random challenge room
!deaths_to_fail = $03

; the sublevel to go to when you win
!complete_room = $0183

; the sublevel to go to if you win with no deaths
!full_clear_room = $0184

; if 1, allow the player to press L+R to warp to a sublevel
!safe_word = 1
!safe_word_destination = $0181

; if set, resets the player's powerup state when they complete a room
!reset_powerup = 1

; if set, UI icons for the rooms will be drawn. See the DRAWING CODE section near the bottom for more relevant variables and tile IDs
!draw_icons = 1

; ------------------------------
; DEBUG OPTIONS
; these aren't recommended for use in your final game, but might be useful for testing
; ------------------------------

; ID of level to test - if not $FF, this level will always be chosen
; use this to test a specific challenge room without having to roll the RNG to find it
; note that this should be the 1-byte index in the Rooms array defined above, not the 2-byte ID of the sublevel itself
!debug_level = $FF

; if 1, pressing select will trigger the exit of the current challenge room as though you completed it
!debug_select_to_win = 0

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

; the amount of rooms played so far, including the current room if it's not the intro room
; -> 1 byte
!RAM_total = $0F5E

; the amount of rooms failed so far
; -> 1 byte
!RAM_deaths = $0F5F

; the current room the player is in as an index in the room list (ie. from 0 to !total_rooms-1)
; if in the intro room, this is $FF
; if in an end room, this is $FE
; -> 1 byte
!RAM_current = $0F60

; the list of results for all of the rooms
; $00 = not yet played, $01 = completed or currently playing, $02 = failed
; -> x bytes (where x = !total_rooms)
!RAM_rooms = $0F61		

; ------------------------------
; INIT CODE
; call the appropriate entry-point for each type of room
; ------------------------------

; code starts here for the end rooms
init_end:
	LDA #$FE
	STA !RAM_current
	RTL

; code starts here for the intro room
init_intro:
	; zero total
	STZ !RAM_total
	STZ !RAM_deaths

	; loop to zero all level flags
	LDA #$00
	LDY #$00
	-
	STA !RAM_rooms,Y
	INY
	CPY #!total_rooms
	BCC -
	
	LDA #$FF
	STA !RAM_current
	
	BRA setup
	
; code starts here for the challenge rooms
init:
	; increment total completions counter
	INC !RAM_total
	
	; mark the current room as completed in the list
	LDY #$00
	REP #$20
	LDA	$010B
	-
	CMP Rooms,Y
	BEQ +
	INY
	INY
	CPY #!total_rooms<<1
	BCS ++
	BRA -
	+
	SEP #$20
	TYA : LSR : TAY
	LDA #$01
	STA !RAM_rooms,Y
	TYA
	STA !RAM_current
	++
	SEP #$20
	
setup:
	; reset powerup
	if !reset_powerup
		STZ $19
	endif

	; if we're finished all rooms, go to the end room
	LDA !RAM_total
	CMP #!total_rooms
	BCC +
	LDA !RAM_deaths
	BEQ ++
	REP #$20
	LDA #!complete_room
	SEP #$20
	JMP set_exits
	++
	REP #$20
	LDA #!full_clear_room
	SEP #$20
	JMP set_exits
	+
	JSL choose_room
	JMP set_exits
	
; ------------------------------
; MAIN CODE
; ------------------------------

main:	
	; paused check
	LDA $13D4
	BNE .paused
	
	; draw icons
	JSL draw_icons
	
	; safe word check
	if !safe_word
		LDA !RAM_current
		CMP #$FE
		BEQ +
		JSL handle_safe_word
		+
	endif

	; select-to-win check
	if !debug_select_to_win
		LDA !RAM_current
		BMI +
		JSL handle_select_to_win
		+
	endif

	.paused

	; death check
	LDA !RAM_current
	BMI +
	LDA $71
	CMP #$09
	BNE +	
	JSL handle_death
	+
	
	RTL
	
handle_death:
	LDA !RAM_total
	CMP #$01
	BEQ .first_room_death	; we dies on our first room, restart!
	INC !RAM_deaths
	LDX !RAM_current
	LDA #$02
	STA !RAM_rooms,X
	LDA !RAM_deaths
	CMP #!deaths_to_fail
	BCS .game_over 			; we're out of tries, restart!
	BRA .not_game_over  	; we have remaining tries, continue!
	
	.game_over
	LDA #$04
	STA $1DF9
	.first_room_death
	STZ !RAM_total
	LDX !RAM_current
	STZ !RAM_deaths
	STZ !RAM_rooms,X
	LDA #$00
	LDY #$00
	-
	STA !RAM_rooms,Y
	INY
	CPY #!total_rooms
	BCC -
	
	JSL choose_room
	JSL set_exits

	.not_game_over
	; if we're in the final room, we need to change the exit to the non-full clear room
	LDA !RAM_total
	CMP #!total_rooms
	BCC +
	REP #$20
	LDA #!complete_room
	SEP #$20
	JSL set_player_exit
	+	
	; pipe vars
	STZ $89
	STZ $88
	; powerup
	STZ $19
	; yoshi vars
	STZ $187A
	STZ $0DC1
	STZ $13C7
	STZ $0DBA
	STZ $18DF	
	JSL destroy_yoshi
	; pose
	LDA #$3E
	STA $13E0
	; state
	LDA #$06
	STA $71
	RTL
	
if !safe_word
handle_safe_word:
	LDA $17
	AND #%00110000
	CMP #%00110000
	BNE +

	; set exit
	REP #$20
	LDA #!safe_word_destination
	SEP #$20
	JSL set_player_exit
	
	; exit
	LDA #$06
	STA $71
	STZ $89
	STZ $88
	STZ $19
	
	; sfx
	LDA #$10
	LDA $1DF9
	
	+
	RTL
endif

if !debug_select_to_win
handle_select_to_win:
	LDA $16
	AND #%00100000
	BEQ +
	; pipe vars
	STZ $89
	STZ $88
	; powerup
	STZ $19
	; yoshi vars
	STZ $187A
	STZ $0DC1
	STZ $13C7
	STZ $0DBA
	STZ $18DF	
	JSL destroy_yoshi
	; pose
	LDA #$3E
	STA $13E0
	; state
	LDA #$06
	STA $71
	RTL
endif

; choose a random room index. Returns the room's list index (1 byte) in Y and its ID (2 bytes) in A 
; if debug_level is set, it always returns that one
choose_room:

	if !debug_level != $FF
		LDA #!debug_level
		ASL
		TAY
		REP #$20
		LDA Rooms,Y
		SEP #$20	
	else
		; get a random room index in the list
		LDA #!total_rooms
		SEC : SBC !RAM_total
		JSL random
		
		; loop to skip completed rooms
		TAX
		LDY #$00
		-
		LDA !RAM_rooms,Y
		BNE +
		CPX #$00
		BEQ ++
		DEX
		+
		INY
		CPY #!total_rooms
		BCC -
		LDY #$00
		++
		
		; Y = room index in list, A = room ID
		TYA
		ASL
		TAY
		REP #$20
		LDA Rooms,Y
		SEP #$20
	endif
	RTL
	
; sets all screen exits to a given sublevel
; A = the sublevel (2 bytes)
set_exits:
	; set exit low bits
	LDY #$00
	-
	STA $19B8,Y
	INY
	CPY #$20
	BCC -
	
	; set exit high bits
	XBA
	AND #%00000001
	ORA #%00000100
	
	LDY #$00
	-
	STA $19D8,Y
	INY
	CPY #$20
	BCC -
	
	RTL
	
; sets the screen exit that the player is currently on to a given sublevel
; A = the sublevel (2 bytes)
set_player_exit:
	LDX $D2
	STA $19B8,X
	XBA
	AND #%00000001
	ORA #%00000100
	STA $19D8,X
	RTL

destroy_yoshi:
	LDX #$0B
	-	
	LDA $14C8,x
	CMP #$08
	BCC +
	LDA $9E,x
	CMP #$35
	BEQ ++
	+
	DEX
	BPL -
	RTL
	++
	
	; destroy the thing in yoshi's mouth
	LDA $18AC	;\ if Yoshi has sprite in mouth.
	BEQ ++	;/
	STZ $18AC	; Reset mouth timer.
	PHY		;
	LDY $160E,x	;
	BMI +		;
	LDA #$00	;\ Sprite of Yoshi in mouth will be deleted.
	STA $14C8,y	;/
	+	
	PLY		;

	++
	STZ $187A
	LDA #$02
	STA $1FE2,x
	STZ $C2,x
	LDA #$03
	STA $1DFA
	STZ $0DC1

	LDA #$04
	STA $14C8,x
	LDA #$1F
	STA $1540,x
	
	RTL
	
; ------------------------------
; DRAWING CODE
; ------------------------------
	
if !draw_icons == 1	

!icon_y = $02		; y-position of all icons
!icon_x_start = $04	; x-position of the first icon
!icon_x_sep = $0A	; x-separation pixels between each icon
!arrow_y1 = $0B		; first y-position of the arrow
!arrow_y2 = $0A		; second y-position of the arrow; the arrow toggles between first and second y-position on a timer

!end_icon_y = $30		; y-position of all icons in end rooms
!end_icon_x_start = $40	; x-position of the first icon in end rooms

; icon tile IDs. All icons are assumed to be on the second sprite page and drawn via OAM
!arrow_tile = $68
!win_icon_tile = $69
!fail_icon_tile = $6A
level_icon_tiles:
	db $64, $65, $66, $67, $74, $75, $76, $77
	
; properties bytes for level icons and the arrow
!icon_props = %00111001
!arrow_props = %00111001
	
macro nexttile()
	?loop:
	LDA $0301|!addr,y
	CMP #$F0
	BEQ ?end
	INY #$4
	CPY #$00
	BNE ?loop
    RTS
	?end:
endmacro

draw_icons:
	LDA !RAM_current
	CMP #$FE
	BNE +
	LDA #!end_icon_x_start
	STA $00
	LDA #!end_icon_y
	STA $01
	BRA ++
	+
	LDA #!icon_x_start
	STA $00
	LDA #!icon_y
	STA $01
	++
	
	LDY #$30
	LDX #$00
	
	; loop
	-
	; draw arrow?
	CPX !RAM_current
	BNE +
	JSL draw_arrow
	+
	
	; tile properties
	%nexttile()	
	LDA $00
	STA $0300,Y	; X
	LDA $01
	STA $0301,Y	; Y
	LDA #!icon_props
	STA $0303,Y ; props
	
	; pick tile
	CPX !RAM_current
	BNE +
	LDA level_icon_tiles,X
	BRA ++
	+	
	LDA !RAM_rooms,X
	BEQ .not_played
	CMP #$01
	BEQ .won
	.failed
	LDA #!fail_icon_tile
	BRA ++
	.won
	LDA #!win_icon_tile
	BRA ++
	.not_played
	LDA level_icon_tiles,X
	++
	STA $0302,Y
	
	; extra bits
	PHY
	TYA
	LSR #2
	TAY
	LDA #$00
	STA $0460,Y
	PLY
		
	; loop condition
	INX
	CPX #!total_rooms
	BEQ _end
	
	; increase x-coord
	LDA $00
	CLC : ADC #!icon_x_sep
	STA $00
	BRA -
	
	_end:
	RTL
	
draw_arrow:
	%nexttile()
	; X
	LDA $00
	STA $0300,Y	
	; Y
	LDA $14
	AND #%00010000
	BEQ +
	LDA #!arrow_y1
	BRA ++
	+
	LDA #!arrow_y2
	++
	STA $0301,Y
	; tile
	LDA #!arrow_tile
	STA $0302,Y
	; props
	LDA #!arrow_props
	STA $0303,Y	
	; extra bits
	PHY
	TYA
	LSR #2
	TAY
	LDA #$00
	STA $0460,Y
	PLY
	
	RTL	
	
endif
	
;============;
;RNG by 1024
; usage: LDA max, returns in A [0-(max-1)]
;============;

; this wasn't in the original RNG code from 1024, but...
; we're adding our global frame timer in here to make the RNG less predictable
; this timer is set up and run by a separate patch by D4, not included in this package
; so I've disabled it in this example code
!rng_adds_timer = 0
!timerSRAM = $700510

random:
	STA $00
	JSL $01ACF9
	
	if !rng_adds_timer
		; add timer
		LDA $010A
		ASL #2
		CLC : ADC #$03
		TAX
		LDA !timerSRAM,X
		CLC : ADC $148D
	else
		LDA $148D
	endif
	
	-
	CMP $00
	BCC +
	BMI ++
	SEC : SBC $00
	BRA -
	++
	CLC : ADC $00
	BRA -
	
	+
	STA $148D
	RTL