;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Multi-Midway Points 1.0, by imamelia
;;
;; Have you ever been disappointed that Super Mario World allows
;; only one midway point per overworld level, and it has to be in
;; the same level, *and* it can't go beyond screen 0F, AND the player's
;; XY position within the screen has to be precisely identical to that of
;; the normal level entrance? Well, with this patch, those restrictions are
;; removed.  You can have up to 255 checkpoints in each level (overworld
;; level; it would be silly to have them in sublevels), and moreover, you
;; can set them to any level number, X position, and Y position you want!
;; In fact, the patch even has an option for changing the level data pointer
;; on the fly if you want, so you can make Chocolate Island 2-styled levels.
;; You might want to read the readme, though.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

header
lorom

!Freespace = $15C000

!OWLevelNum = $13BF			; RAM address for the overworld level number (the reason this is a define is because of the Extra Overworld Level RAM patch)

!CheckpointRAM = $7FA040		; 96 bytes of free RAM that will be used for the checkpoints, 1 byte per overworld level

!AltPtrIndex = $7FA110			; you can use this to change the level data pointer in sublevels
						; set bit 15 to enable it, and set bits 0-8 to the level number
!AltPtrXPos = $7FA112			; same here, but with the player's X position instead
!AltPtrYPos = $7FA114			; same here, but with the player's Y position instead


org $00802A		; initialize the checkpoint table at game load
JSL ClearAllCheckpoints	;

org $05D8BC		; part of the routine that sets the level number
JML MultiMidway1		;

org $05DAD7		; part of the routine that sets the player's XY position (and action)
JML MultiMidway2		;

org $00976C		;
JML GameOverClear	; clear all checkpoints on "Game Over"
NOP #2			;

org $05CBFF		; part of the level end scorecard routine
JML LevelEndClear		; clear the checkpoint flags for the current level at level end
NOP #4			;

org !Freespace		;

reset bytes

db "STAR"
dw CodeEnd-CodeStart-$01
dw CodeEnd-CodeStart-$01^$FFFF

CodeStart:

;------------------------------------------------
; clear all checkpoints at game load/reset
;------------------------------------------------

ClearAllCheckpoints:

STA $7F8000

InitCheckpoints:

PHP					; preserve processor flags
REP #$20				; 16-bit A
SEP #$10				;

LDA.w #$0000			;
STA !AltPtrIndex			; clear all
STA !AltPtrXPos			; alternate
STA !AltPtrYPos			; pointers

LDX #$5E				; $60 bytes to clear, so load 5E
.Loop
STA !CheckpointRAM,x		; clear 2 bytes at a time
DEX					; decrement our index twice,
DEX					; since we're in 16-bit mode
BPL .Loop				; if our index is still positive, loop

PLP					; pull back processor flags
RTL					;

;------------------------------------------------
; part of the code that sets the level number to load
;------------------------------------------------

MultiMidway1:

PHB
PHK
PLB
PHP
SEP #$30
JSR MultiMidwayMainRt1
PLP
PLB
REP #$30
LDA $0E
STA $010B				; mirror the level number change for levelnum.asm (can easily cause bugs otherwise)
ASL
CLC
ADC $0E
TAY

;--------
;;;; Code used by smwc-coins
;--------

incsrc CoinDefines.asm

PHA
PHY
PHX
PHP
SEP #$30
LDA $13BF
CMP.l !TempCounter
BEQ Coincheck
CMP #$01
BEQ Coincheck
JSL InitCheckpoints
LDA #$00
STA.l !TempCounter+1
STA.l !TempCounter+2

Coincheck:
LDA.l !TempCounter+1
LSR #3
ORA.l !TempCounter+1
STA.l !TempCounter+1

LDA $13BF
STA.l !TempCounter
AND #$07
TAX
LDA bitTable,x
STA.l !TempCounter+3

LDA $13BF
LSR #3
TAX

LDA !ramspace,x
AND.l !TempCounter+3
BEQ Sec

LDA.l !TempCounter+1
ORA #$01
STA.l !TempCounter+1

LDA.l !TempCounter+2
ORA #$01
STA.l !TempCounter+2

Sec:
LDA.l !ramspace+$0B,x
AND.l !TempCounter+3
BEQ Thi

LDA.l !TempCounter+1
ORA #$02
STA.l !TempCounter+1

LDA.l !TempCounter+2
ORA #$02
STA.l !TempCounter+2

Thi:
LDA.l !ramspace+$16,x
AND !TempCounter+3
BEQ EndCoins

LDA.l !TempCounter+1
ORA #$04
STA.l !TempCounter+1

LDA.l !TempCounter+2
ORA #$04
STA.l !TempCounter+2

BRA EndCoins

bitTable:
db $01, $02, $04, $08, $10, $20, $40, $80
;------
;;;;
;------

EndCoins:
PLP
PLX
PLY
PLA

JML $05D8C0

;------------------------------------------------
; part of the code that sets the player's XY position and action on level load
;------------------------------------------------

MultiMidway2:

PHB
PHK
PLB
PHP
SEP #$30
JSR MultiMidwayMainRt2
PLP
PLB

LDA $141A				; restoring hijacked code...
BEQ .JumpBack2			;
JML $85DADC			;
.JumpBack2				;
JML $85DAEB			;

;------------------------------------------------
; most of the main code for the multi-midway routines (much of it is shared)
;------------------------------------------------

NoCheckpoints:

REP #$20			; 16-bit A
LDA !AltPtrIndex		; check the alternate level pointer
BPL ReturnMM		;
LDA !AltPtrIndex		; load it again...
AND #$01FF		; we need only the lower 9 bits
BRA SetLevelNum		; use an alternate pointer index for the level number

NoCheckpoints2:		;

REP #$20			; 16-bit A
LDA !AltPtrIndex		; check the alternate level pointer
BMI AltPosition		;
ReturnMM:			;
RTS

AltPosition:			;
LDA !AltPtrXPos		; set the player's X position
STA $94			;
LDA !AltPtrYPos		; set the player's Y position
STA $96			;
RTS

MultiMidwayMainRt1:

LDA $0100			; check the game mode
CMP #$11			; if we are not in a level fade-in sequence...
BNE ReturnMM		; return

LDA $141A			; if we are in a sublevel...
BNE NoCheckpoints	; skip the checkpoint pointer switch

PHX				;
LDX !OWLevelNum		; get the overworld level number into X
LDA !CheckpointRAM,x	; load a byte from the checkpoint table
PLX
CMP #$00			;
BEQ NoCheckpoints	;

JSR GetIndex		; get the level number index

LDA ($06),y			; load the level number
SetLevelNum:		;
STA $0E			; set the new level number and return
RTS				; no need to REP #$30, since m and x are already clear

MultiMidwayMainRt2:	;

LDA $141A			; if we are in a sublevel...
BNE NoCheckpoints2	; skip the checkpoint pointer switch

PHX				;
LDX !OWLevelNum		; get the overworld level number into X
LDA !CheckpointRAM,x	; load a byte from the checkpoint table
PLX
CMP #$00			;
BEQ NoCheckpoints2	;

JSR GetIndex		;

INY #2			;
LDA ($06),y			; get the extra flags
STA $00			;
SEP #$20			;
AND #$07			; bits 0-2 = player action
STA $192A			;
LDA $00			;
AND #$08			; bit 3 = slippery level flag
BEQ .NoSlip			;
LDA #$80			;
TSB $192A			;
.NoSlip			;
LDA $00			;
AND #$10			; bit 4 = water level flag
BEQ .NoWater		;
LDA #$40			;
TSB $192A			;
.NoWater			;
REP #$20			;
INY #2			;
LDA ($06),y			; get the player's X position
STA $94			; set the player's X position
INY #2			;
LDA ($06),y			; get the player's Y position
STA $96			; set the player's Y position
INY #2			;
LDA ($06),y			; get the Layer 1 X position
STA $1A			; set the Layer 1 X position
INY #2			;
LDA ($06),y			; get the Layer 1 Y position
STA $1C			; set the Layer 1 Y position
INY #2			;
LDA ($06),y			; get the Layer 2 X position
STA $1E			; set the Layer 2 X position
INY #2			;
LDA ($06),y			; get the Layer 2 Y position
STA $20			; set the Layer 2 Y position

RTS

;------------------------------------------------
; clear all checkpoints on Game Over
;------------------------------------------------

GameOverClear:

JSL InitCheckpoints	; no duh
STZ $0DC1			;
LDA $0DB4			; hijacked code
JML $809772		;

;------------------------------------------------
; clear the current level's checkpoints at level end
;------------------------------------------------

LevelEndClear:		; clear the current level's checkpoint flags at level end

PHX				;
LDX !OWLevelNum		;
LDA #$00			;
STA !CheckpointRAM,x	;
PLX				;

PHB				; restore hijacked code:
LDA #$05			; preserve data bank
PHA				; and set the data bank to 05
PLB				;
PEA $B298			; "PLB : RTL" in bank 05
JML $05CC07|$800000	; jump to the original routine (we can't just JSL, since it ends in RTS)

;------------------------------------------------
; subroutine for getting an index to a particular checkpoint table
;------------------------------------------------

GetIndex:			; when this subroutine returns, ($06),y will point to the table of bytes associated with a particular checkpoint

REP #$20			; 16-bit A
LDA !OWLevelNum		; load the overworld level number
AND.w #$00FF		; wipe the high byte and multiply by 2
ASL				; since the pointers are 16-bit
TAY				;
LDA CheckPtrs,y		; get a pointer for a particular level
STA $06			; store this to scratch RAM
SEP #$20			; set A back to 8-bit
PHX				;
LDX !OWLevelNum		; get the overworld level number into X
LDA !CheckpointRAM,x	; load a byte from the checkpoint table
PLX				;
REP #$30			; 16-bit AXY
AND.w #$00FF		; wipe the high byte again
BEQ .Return			; if no checkpoints have been reached, return
DEC				; decrement once
ASL #4			; there are 16 bytes of data per checkpoint
TAY				;
RTS				;

.Return			;
PLA				;
RTS				;


;------------------------------------------------
; pointers and data tables for the checkpoints
;------------------------------------------------

print "Location of multi-midway tables: $",pc

incsrc multimidwaytables.asm

print "Freespace used: ",bytes," bytes."
print "Next address: $",pc

CodeEnd:
