Archipelago/worlds/lufia2ac/basepatch/basepatch.asm

1327 lines
38 KiB
NASM

lorom
org $DFFFFD ; expand ROM to 3MB
DB "EOF"
org $80FFD8 ; expand SRAM to 32KB
DB $05 ; overwrites DB $03
org $80809A ; patch copy protection
CMP $710000 ; overwrites CMP $702000
org $8080A6 ; patch copy protection
CMP $710000 ; overwrites CMP $702000
org $8AEAA3 ; skip gruberik intro dialogue
DB $1C,$86,$03 ; L2SASM JMP $8AE784+$0386
org $8AEC82 ; skip gruberik save dialogue
DB $1C,$93,$01 ; L2SASM JMP $8AEB1C+$0193
org $8AECFE ; skip gruberik abandon dialogue
DB $1C,$32,$02 ; L2SASM JMP $8AEB1C+$0232
org $8AF4E1 ; skip gruberik selan dialogue
DB $1C,$D8,$09 ; L2SASM JMP $8AEB1C+$09D8
org $8AF528 ; skip gruberik guy dialogue
DB $1C,$1E,$0A ; L2SASM JMP $8AEB1C+$0A1E
org $8AF55F ; skip gruberik arty dialogue
DB $1C,$67,$0A ; L2SASM JMP $8AEB1C+$0A67
org $8AF5B2 ; skip gruberik tia dialogue
DB $1C,$C3,$0A ; L2SASM JMP $8AEB1C+$0AC3
org $8AF61A ; skip gruberik dekar dialogue
DB $1C,$23,$0B ; L2SASM JMP $8AEB1C+$0B23
org $8AF681 ; skip gruberik lexis dialogue
DB $1C,$85,$0B ; L2SASM JMP $8AEB1C+$0B85
org $8EA349 ; skip ancient cave entrance dialogue
DB $1C,$B0,$01 ; L2SASM JMP $8EA1AD+$01B0
org $8EA384 ; reset architect mode, skip ancient cave exit dialogue
DB $1B,$E1,$1C,$2B,$02 ; clear flag $E1, L2SASM JMP $8EA1AD+$022B
org $8EA565 ; skip ancient cave leaving dialogue
DB $1C,$E9,$03 ; L2SASM JMP $8EA1AD+$03E9
org $8EA653 ; skip master intro dialogue
DB $1C,$0F,$01 ; L2SASM JMP $8EA5FA+$010F
org $8EA721 ; skip master fight dialogue
DB $1C,$45,$01 ; L2SASM JMP $8EA5FA+$0145
org $8EA74B ; skip master victory dialogue
DB $1C,$AC,$01 ; L2SASM JMP $8EA5FA+$01AC
org $8EA7AA ; skip master key dialogue and animation
DB $1C,$EE,$01 ; L2SASM JMP $8EA5FA+$01EE
org $8EA7F4 ; skip master goodbye dialogue
DB $1C,$05,$02 ; L2SASM JMP $8EA5FA+$0205
org $8EA807 ; skip master not fight dialogue
DB $1C,$18,$02 ; L2SASM JMP $8EA5FA+$0218
org $94AC45 ; connect ancient cave exit stairs to gruberik entrance
DB $67,$09,$18,$68
org $948DE1 ; connect gruberik west border to ancient cave entrance
DB $07,$08,$14,$F0
org $948DEA ; connect gruberik south border to ancient cave entrance
DB $07,$08,$14,$F0
org $948DF3 ; connect gruberik north border to ancient cave entrance
DB $07,$08,$14,$F0
; archipelago item
org $96F9AD ; properties
DB $00,$00,$00,$E4,$00,$00,$00,$00,$00,$00,$00,$00,$00
org $9EDD60 ; name
DB "AP item " ; overwrites "Key30 "
org $9FA900 ; sprite
incbin "ap_logo/ap_logo.bin"
warnpc $9FA980
; sold out item
org $96F9BA ; properties
DB $00,$00,$00,$10,$00,$00,$00,$00,$00,$00,$00,$00,$00
org $9EDD6C ; name
DB "SOLD OUT " ; overwrites "Crown "
org $D08000 ; signature, start of expanded data area
DB "ArchipelagoLufia"
org $D09800 ; start of expanded code area
; initialize
pushpc
org $808046
; DB=$80, x=1, m=1
JSL Init ; overwrites JSL $809037
pullpc
Init:
; check signature
LDX.b #$0F
-: LDA $D08000,X
CMP $F02000,X
BNE +
DEX
BPL -
BRA ++
; set up DMA to clear expanded SRAM
+: STZ $211C ; force multiplication results (MPYx) to zero
REP #$10
LDA.b #$80
STA $4300 ; transfer B-bus to A-bus, with A-bus increment
LDA.b #$34
STA $4301 ; B-bus source register $2134 (MPYL)
LDX.w #$2000
STX $4302 ; A-bus destination address $F02000 (SRAM)
LDA.b #$F0
STA $4304
LDX.w #$6000
STX $4305 ; transfer 24kB
LDA.b #$01
STA $420B ; start DMA channel 1
; sign expanded SRAM
PHB
TDC
LDA.b #$3F
LDX.w #$8000
LDY.w #$2000
MVN $F0,$D0 ; copy 64B from $D08000 to $F02000
PLB
++: SEP #$30
JSL $809037 ; (overwritten instruction)
RTL
; transmit checks from chests
pushpc
org $8EC1EB
JML TX ; overwrites JSL $83F559
pullpc
TX:
JSL $83F559 ; (overwritten instruction) chest opening animation
REP #$20
LDA $7FD4EF ; read chest item ID
BIT.w #$0200 ; test for iris item flag
BEQ +
JSR ReportLocationCheck
SEP #$20
JML $8EC2DC ; skip item get process; consider chest emptied
+: BIT.w #$4200 ; test for blue chest flag
BEQ +
LDA $F02048 ; load total blue chests checked
CMP $D08010 ; compare against max AP item number
BPL +
LDA $F02040 ; load check counter
INC ; increment check counter
STA $F02040 ; store check counter
SEP #$20
JML $8EC2DC ; skip item get process; consider chest emptied
+: SEP #$20
JML $8EC1EF ; continue item get process
; transmit checks from script events
pushpc
org $80A435
; DB=$8E, x=0, m=1
JML ScriptTX ; overwrites STA $7FD4F1
pullpc
ScriptTX:
STA $7FD4F1 ; (overwritten instruction)
LDA $05AC ; load map number
CMP.b #$F1 ; check if ancient cave final floor
BNE +
REP #$20
LDA $7FD4EF ; read script item id
CMP.w #$01C2 ; test for ancient key
BNE +
JSR ReportLocationCheck
SEP #$20
JML $80A47F ; skip item get process
+: SEP #$20
JML $80A439 ; continue item get process
ReportLocationCheck:
PHA ; remember item id
LDA $F0204A ; load other locations count
INC ; increment check counter
STA $F0204A ; store other locations count
DEC
ASL
TAX
PLA
STA $F02060,X ; store item id in checked locations list
RTS
; report event flag based goal completion
Goal:
TDC
LDA $0797 ; load EV flags $C8-$CF (iris sword, iris shield, ..., iris pot)
TAX
LDA $0798 ; load EV flags $D0-$D7 (iris tiara, boss, others...)
TAY
AND.b #$02 ; test boss victory
LSR
STA $F02031 ; report boss victory goal
TYA
AND.b #$01 ; test iris tiara
ADC $97B418,X ; test remaining iris items via predefined lookup table for number of bits set in a byte
CMP $D08017 ; compare with number of treasures required
BMI +
LDA.b #$01
STA $F02032 ; report iris treasures goal
AND $F02031
STA $F02033 ; report boss victory + iris treasures goal
+: RTS
; receive items
RX:
REP #$20
LDA $F02802 ; load snes side received items processed counter
CMP $F02800 ; compare with client side received items counter
BPL +
INC
STA $F02802 ; increase received items processed counter
ASL
TAX
LDA $F02802,X ; load received item ID
BRA ++
+: LDA $F02046 ; load snes side found AP items processed counter
CMP $F02044 ; compare with client side found AP items counter
BPL +
LDA $F02044
STA $F02046 ; increase AP items processed counter
LDA.w #$01CA ; load "AP item" ID
++: STA $7FD4EF ; store it as a "chest"
JSR SpecialItemGet
SEP #$20
JSL $8EC1EF ; call chest opening routine (but without chest opening animation)
STZ $A7 ; cleanup
JSL $83AB4F ; cleanup
+: SEP #$20
RTS
SpecialItemGet:
BPL + ; spells have high bit set
JSR LearnSpell
+: BIT.w #$0200 ; iris items
BEQ +
SEC
SBC.w #$039C
ASL
TAX
LDA $8ED8C3,X ; load predefined bitmask with a single bit set
ORA $0797
STA $0797 ; set iris item EV flag ($C8-$D0)
BRA ++
+: CMP.w #$01C2 ; ancient key
BNE +
LDA.w #$0008
ORA $0796
STA $0796 ; set ancient key EV flag ($C3)
LDA.w #$0200
ORA $0797
STA $0797 ; set boss item EV flag ($D1)
BRA ++
+: CMP.w #$01BF ; capsule monster items range from $01B8 to $01BE
BPL ++
SBC.w #$01B1 ; party member items range from $01B2 to $01B7
BMI ++
ASL
TAX
LDA $8ED8C7,X ; load predefined bitmask with a single bit set
ORA $F02018 ; set unlock bit for party member/capsule monster
STA $F02018
++: RTS
LearnSpell:
STA $0A0B
SEP #$20
LDA.b #$06
-: PHA
JSL $82FD3D ; teach spell in $0A0B to character determined by A
PLA
DEC
BPL -
REP #$20
LDA $0A0B
RTS
; use items
pushpc
org $82AE6F
; DB=$83, x=0, m=1
JSL SpecialItemUse ; overwrites JSL $81EFDF
org $8EFD2E ; unused region at the end of bank $8E
DB $1E,$0B,$01,$2B,$01,$1A,$02,$00 ; add selan
DB $1E,$0B,$01,$2B,$02,$1A,$03,$00 ; add guy
DB $1E,$0B,$01,$2B,$03,$1A,$04,$00 ; add arty
DB $1E,$0B,$01,$2B,$05,$1A,$05,$00 ; add dekar
DB $1E,$0B,$01,$2B,$04,$1A,$06,$00 ; add tia
DB $1E,$0B,$01,$2B,$06,$1A,$07,$00 ; add lexis
DB $1F,$0B,$01,$2C,$01,$1B,$02,$00 ; remove selan
DB $1F,$0B,$01,$2C,$02,$1B,$03,$00 ; remove guy
DB $1F,$0B,$01,$2C,$03,$1B,$04,$00 ; remove arty
DB $1F,$0B,$01,$2C,$05,$1B,$05,$00 ; remove dekar
DB $1F,$0B,$01,$2C,$04,$1B,$06,$00 ; remove tia
DB $1F,$0B,$01,$2C,$06,$1B,$07,$00 ; remove lexis
pullpc
SpecialItemUse:
JSL $81EFDF ; (overwritten instruction)
REP #$20
LDA $0A06 ; get ID of item being used
CMP.w #$01B8
BPL +
SBC.w #$01B1 ; party member items range from $01B2 to $01B7
BMI +
ASL
TAX
ASL
ASL
ADC.w #$FD2E
STA $09B7 ; set pointer to L2SASM join script
SEP #$20
LDA $8ED8C7,X ; load predefined bitmask with a single bit set
BIT $077E ; check against EV flags $02 to $07 (party member flags)
BEQ ++
LDA.b #$30 ; character already present; modify pointer to point to L2SASM leave script
ADC $09B7
STA $09B7
BRA +++
++: LDA $07A9 ; character not present; load EV register $0B (party counter)
CMP.b #$03
BPL + ; abort if party full
+++ LDA.b #$8E
STA $09B9
PHK
PEA ++
PEA $8DD8
JML $83BB76 ; initialize parser variables
++: NOP
JSL $809CB8 ; call L2SASM parser
TSX
INX #13
TXS
JML $82A45E ; leave menu
+: SEP #$20
RTL
; main loop
pushpc
org $83BC16
; DB=$83, x=0, m=1
JSL MainLoop ; overwrites LDA $09A7 : BIT.b #$01
NOP
pullpc
MainLoop:
JSR RX
JSR Goal
JSR Unlocks
LDA $09A7 ; (overwritten instruction)
BIT.b #$01 ; (overwritten instruction)
RTL
Unlocks:
LDA $F02018 ; load party member unlocks from SRAM
STA $0780 ; transfer to flags (WRAM)
LDA $F02019 ; load capsule monster unlocks from SRAM
TAY
LDX.w #$0000
-: TYA
LSR
TAY
BCC +
LDA $82C33C
CMP $11BB,X
BMI +++
BRA ++
+: LDA.b #$00
++: STA $11BB,X ; unlock/lock capsule monster #X
+++ INX
CPX.w #$0007
BNE -
LDA $F02019
TAY
BNE +
LDA.b #$FF
STA $0A7F ; lock capsule menu
BRA ++
+: LDA.b #$07
STA $0A7F ; unlock capsule menu
LDA $F02019
BIT.b #$80 ; track whether one-time setup has been done before
BNE ++
ORA.b #$80
STA $F02019
CMP.b #$FF
BEQ ++ ; all capsule monsters available; don't overwrite starting capsule
LDX.w #$FFFF
TYA
-: LSR
INX
BCC -
TXA
STA $11A3 ; activate first unlocked capsule monster
STA $7FB5FB
STA $F02016
JSL $82C2FD ; run setup routine for capsule monsters
++: RTS
; lock party members
pushpc
org $8AEC3E
DB $15,$C4,$A4,$01 ; L2SASM JMP $8AEB1C+$01A4 if flag $C4 set
org $8AECC0
DB $6C,$65,$00,$FA ; (overwritten instruction)
DB $15,$12,$AE,$01,$2E,$66 ; remove selan if flag $12 clear
DB $15,$13,$B4,$01,$2E,$67 ; remove guy if flag $13 clear
DB $15,$14,$BA,$01,$2E,$68 ; remove arty if flag $14 clear
DB $15,$15,$C0,$01,$2E,$6A ; remove dekar if flag $15 clear
DB $15,$16,$C6,$01,$2E,$69 ; remove tia if flag $16 clear
DB $15,$17,$CC,$01,$2E,$6B ; remove lexis if flag $17 clear
DB $00
pullpc
; party member items (IDs $01B2 - $01B7)
pushpc
org $96F875 ; properties
DB $40,$00,$00,$E9,$64,$00,$00,$00,$00,$00,$00,$00,$00
DB $40,$00,$00,$E0,$64,$00,$00,$00,$00,$00,$00,$00,$00
DB $40,$00,$00,$EB,$64,$00,$00,$00,$00,$00,$00,$00,$00
DB $40,$00,$00,$ED,$64,$00,$00,$00,$00,$00,$00,$00,$00
DB $40,$00,$00,$E8,$64,$00,$00,$00,$00,$00,$00,$00,$00
DB $40,$00,$00,$EF,$64,$00,$00,$00,$00,$00,$00,$00,$00
org $979EC6 ; descriptions
DB "Parcelyte commander. " : DB $00
DB "A guy named Guy. " : DB $00
DB "(Or was it Artea?) " : DB $00
DB "Strongest warrior. " : DB $00
DB "Elcid shopkeeper. " : DB $00
DB "Great inventor." : DB $00
org $97FDAC ; remove from scenario item list
DW $0000,$0000,$0000,$0000,$0000,$0000
org $9EDC40 ; names
DB "Selan " ; overwrites "Wind key "
DB "Guy " ; overwrites "Cloud key "
DB "Arty " ; overwrites "Light key "
DB "Dekar " ; overwrites "Sword key "
DB "Tia " ; overwrites "Tree key "
DB "Lexis " ; overwrites "Flower key "
pullpc
; capsule monster items (IDs $01B8 - $01BE)
pushpc
org $96F8C3 ; properties
DB $00,$00,$00,$EE,$12,$00,$00,$00,$00,$00,$00,$00,$00
DB $00,$00,$00,$EE,$12,$00,$00,$00,$00,$00,$00,$00,$00
DB $00,$00,$00,$EE,$12,$00,$00,$00,$00,$00,$00,$00,$00
DB $00,$00,$00,$EE,$12,$00,$00,$00,$00,$00,$00,$00,$00
DB $00,$00,$00,$EE,$12,$00,$00,$00,$00,$00,$00,$00,$00
DB $00,$00,$00,$EE,$12,$00,$00,$00,$00,$00,$00,$00,$00
DB $00,$00,$00,$EE,$12,$00,$00,$00,$00,$00,$00,$00,$00
org $979F47 ; descriptions
DB "NEUTRAL " : DB $00
DB "LIGHT " : DB $00
DB "WIND " : DB $00
DB "WATER " : DB $00
DB "DARK " : DB $00
DB "SOIL " : DB $00
DB "FIRE " : DB $00
org $9EDC88 ; names
DB "JELZE " ; overwrites "Magma key "
DB "FLASH " ; overwrites "Heart key "
DB "GUSTO " ; overwrites "Ghost key "
DB "ZEPPY " ; overwrites "Trial key "
DB "DARBI " ; overwrites "Dankirk key "
DB "SULLY " ; overwrites "Basement key"
DB "BLAZE " ; overwrites "Narcysus key"
pullpc
; allow inactive characters to gain exp
pushpc
org $81DADD
; DB=$81, x=0, m=1
NOP ; overwrites BNE $81DAE2 : JMP $DBED
JML HandleActiveExp
AwardExp:
; isolate exp distribution into a subroutine, to be reused for both active party members and inactive characters
org $81DAE9
NOP #2 ; overwrites JMP $DBBD
RTL
org $81DB42
NOP #2 ; overwrites JMP $DBBD
RTL
org $81DD11
; DB=$81, x=0, m=1
JSL HandleInactiveExp ; overwrites LDA $0A8A : CLC
pullpc
HandleActiveExp:
BNE + ; (overwritten instruction; modified) check if statblock not empty
JML $81DBED ; (overwritten instruction; modified) abort
+: JSL AwardExp ; award exp (X=statblock pointer, Y=position in battle order, $00=position in menu order)
JML $81DBBD ; (overwritten instruction; modified) continue to next level text
HandleInactiveExp:
LDA $F0201B ; load inactive exp gain rate
BEQ + ; zero gain; skip everything
CMP.b #$64
BCS ++ ; full gain
LSR $1607
ROR $1606 ; half gain
ROR $1605
++: LDY.w #$0000 ; start looping through all characters
-: TDC
TYA
LDX.w #$0003 ; start looping through active party
--: CMP $0A7B,X
BEQ ++ ; skip if character in active party
DEX
BPL -- ; continue looping through active party
STA $153D ; inactive character detected; overwrite character index of 1st slot in party battle order
ASL
TAX
REP #$20
LDA $859EBA,X ; convert character index to statblock pointer
SEP #$20
TAX
PHY ; stash character loop index
LDY $0A80
PHY ; stash 1st (in menu order) party member statblock pointer
STX $0A80 ; overwrite 1st (in menu order) party member statblock pointer
LDY.w #$0000 ; set to use 1st position (in battle order)
STY $00 ; set to use 1st position (in menu order)
JSL AwardExp ; award exp (X=statblock pointer, Y=position in battle order, $00=position in menu order)
PLY ; restore 1st (in menu order) party member statblock pointer
STY $0A80
PLY ; restore character loop index
++: INY
CPY.w #$0007
BCC - ; continue looping through all characters
+: LDA $0A8A ; (overwritten instruction) load current gold
CLC ; (overwritten instruction)
RTL
; receive death link
pushpc
org $83BC91
; DB=$83, x=0, m=1
JSL DeathLinkRX ; overwrites LDA $7FD0AE
pullpc
DeathLinkRX:
LDA $F0203F ; check death link trigger
BEQ +
TDC
STA $F0203F ; reset death link trigger
LDA $F0203D ; check death link enabled
BEQ +
LDA.b #$04
STA $0BBC ; kill maxim
STA $0C7A ; kill selan
STA $0D38 ; kill guy
STA $0DF6 ; kill arty
STA $0EB4 ; kill tia
STA $0F72 ; kill dekar
STA $1030 ; kill lexis
LDA.b #$FE
STA $7FF8A3 ; select normal enemy battle
LDA.b #$82
STA $7FF8A4 ; select a formation containing only demise
JSL $8383EB ; force battle
+: LDA $7FD0AE ; (overwritten instruction)
RTL
DeathLinkTX:
LDA $F0203D ; check death link enabled
BEQ +
LDA $7FF8A4 ; load formation number
CMP.b #$82 ; did we die from a death link?
BEQ +
STA $004202
LDA.b #$0A
STA $004203 ; multiply by 10 to get formation offset
TDC
NOP
LDA $004216
TAX
LDA $7FF756,X ; read first monster in formation
INC
STA $F0203E ; send death link by monster id + 1
+: RTL
; clear receiving counters when starting new game; force "GIFT" mode
pushpc
org $83AD83
; DB=$83, x=0, m=1
JSL ClearRX ; overwrites BIT #$02 : BEQ $83ADAB
pullpc
ClearRX:
REP #$20
TDC
STA $F02800 ; clear received count
STA $F02802 ; clear processed count
SEP #$20
; absence of the overwritten instructions automatically leads to "GIFT" mode code path
RTL
; store receiving counters when saving game
pushpc
org $82EB61
; DB=$8A, x=0, m=1
JSL SaveRX ; overwrites JSL $8090C9
pullpc
SaveRX:
JSL $8090C9 ; (overwritten instruction) write save slot A to SRAM
SEP #$10
REP #$20
ASL
ASL
TAX
LDA $F02800 ;
STA $F027E0,X ; save received count
LDA $F02802 ;
STA $F027E2,X ; save processed count
SEP #$20
REP #$10
RTL
; restore receiving counters when loading game
pushpc
org $82EAD5
; DB=$83, x=0, m=1
JSL LoadRX ; overwrites JSL $809099
pullpc
LoadRX:
JSL $809099 ; (overwritten instruction) load save slot A from SRAM
SEP #$10
REP #$20
ASL
ASL
TAX
LDA $F027E0,X ;
STA $F02800 ; restore received count
LDA $F027E2,X ;
STA $F02802 ; restore processed count
SEP #$20
REP #$10
RTL
; keep inventory after defeat
pushpc
org $848B9C
; DB=$7E, x=0, m=1
NOP #5 ; overwrites LDA.b #$FF : STA $7FE759 : JSR $8888
JSL DeathLinkTX
pullpc
; set initial floor number
pushpc
org $8487A9
JSL InitialFloor ; overwrites TDC : STA $7FE696
NOP
pullpc
InitialFloor:
LDA $D08015 ; read initial floor number
STA $7FE696 ; (overwritten instruction)
TDC ; (overwritten instruction)
RTL
; report final floor goal completion
pushpc
org $839E87
JSL FinalFloor ; overwrites STA $0005B0
pullpc
FinalFloor:
STA $0005B0 ; (overwritten instruction)
LDA.b #$01
STA $F02034 ; report final floor goal
RTL
; start with Providence
pushpc
org $8488BB
; DB=$84, x=0, m=0
JSL Providence ; overwrites LDX.w #$1402 : STX $0A8D
NOP #2
pullpc
Providence:
LDX.w #$1402 ; (overwritten instruction)
STX $0A8D ; (overwritten instruction) add Potion x10
LDX.w #$022D
STX $0A8F ; add Providence
RTL
; start inventory
pushpc
org $848901
; DB=$84, x=0, m=1
JSL StartInventory ; overwrites JSL $81ED35
pullpc
StartInventory:
JSL $81ED35 ; (overwritten instruction)
REP #$20
LDA $F02802 ; number of items to process
DEC
BMI ++ ; skip if empty
ASL
TAX
-: LDA $F02804,X ; item ID
BPL + ; spells have high bit set
PHX
JSR LearnSpell
PLX
+: BIT.w #$C200 ; ignore spells, blue chest items, and iris items
BNE +
PHX
STA $09CF ; specify item ID
TDC
INC
STA $09CD ; specify quantity as 1
JSL $82E80C ; add item to inventory
REP #$20
PLX
+: DEX
DEX
BPL -
++: SEP #$20
RTL
; architect mode
pushpc
org $8EA1E7
base = $8EA1AD ; ancient cave entrance script base
DB $15,$E1 : DW .locked-base ; L2SASM JMP .locked if flag $E1 set
DB $08,"Did you like the layout",$03, \
"of the last cave? I can",$03, \
"lock it down and prevent",$03, \
"the cave from changing.",$01
DB $08,"Do you want to lock",$03, \
"the cave layout?",$01
DB $10,$02 : DW .cancel-base,.lock-base ; setup 2 choices: .cancel and .lock
DB $08,"Cancel",$0F,"LOCK IT DOWN!",$0B
.cancel:
DB $4C,$54,$00 ; play sound $54, END
.lock:
DB $5A,$05,$03,$7F,$37,$28,$56,$4C,$6B,$1A,$E1 ; shake, delay $28 f, stop shake, play sound $6B, set flag $E1
.locked:
DB $08,"It's locked down.",$00
warnpc $8EA344
org $839018
; DB=$83, x=0, m=1
JSL ArchitectMode ; overwrites LDA.b #$7E : PHA : PLB
pullpc
ArchitectMode:
; check current mode
LDA $079A
BIT.b #$02
BEQ + ; go to write mode if flag $E1 (i.e., bit $02 in $079A) not set
; read mode (replaying the locked down layout)
JSR ArchitectBlockAddress
LDA $F00000,X ; check if current block is marked as filled
BEQ + ; go to write mode if block unused
TDC
LDA.b #$36
LDY.w #$0521
INX
MVN $7E,$F0 ; restore 55 RNG values from $F00000,X to $7E0521
INX
LDA $F00000,X
STA $0559 ; restore current RNG index from $F00000,X to $7E0559
BRA ++
; write mode (recording the layout)
+: JSR ArchitectClearBlocks
JSR ArchitectBlockAddress
LDA $7FE696
STA $F00000,X ; mark block as used
TDC
LDA.b #$36
LDX.w #$0521
INY
MVN $F0,$7E ; backup 55 RNG values from $7E0521 to $F00000,Y
INY
LDA $7E0559
STA $0000,Y ; backup current RNG index from $7E0559 to $F00000,Y
LDA.b #$7E ; (overwritten instruction) set DB=$7E
PHA ; (overwritten instruction)
PLB ; (overwritten instruction)
++: RTL
ArchitectClearBlocks:
LDA $7FE696 ; read next floor number
CMP $D08015 ; compare initial floor number
BEQ +
BRL ++ ; skip if not initial floor
+: LDA.b #$F0
PHA
PLB
!floor = 1
while !floor < 99 ; mark all blocks as unused
STZ !floor*$40+$6000
!floor #= !floor+1
endwhile
++: RTS
ArchitectBlockAddress:
; calculate target SRAM address
TDC
LDA $7FE696 ; read next floor number
REP #$20
ASL #6
ADC.w #$6000 ; target SRAM address = next_floor * $40 + $6000
TAX
TAY
SEP #$20
RTS
; for architect mode: make red chest behavior for iris treasure replacements independent of current inventory
; by ensuring the same number of RNG calls, no matter if you have the iris item already or not
; (done by prefilling *all* chests first and potentially overwriting one of them with an iris item afterwards,
; instead of checking the iris item first and then potentially filling *one fewer* regular chest)
pushpc
org $8390C9
; DB=$96, x=0, m=1
NOP ; overwrites LDY.w #$0000
BRA + ; go to regular red chest generation
-: ; iris treasure handling happens below
org $839114
; DB=$7F, x=0, m=1
NOP #36 ; overwrites all of providence handling
LDA.b #$83 ; (overwritten instruction from org $8391E9) set DB=$83 for floor layout generation
PHA ; (overwritten instruction from org $8391E9)
PLB ; (overwritten instruction from org $8391E9)
BRL ++ ; go to end
+: LDY.w #$0000 ; (overwritten instruction from org $8390C9) initialize chest index
; red chests are filled below
org $8391E9
; DB=$7F, x=0, m=1
NOP ; overwrites LDA.b #$83 : PHA : PLB
BRL - ; go to iris treasure handling
++: ; floor layout generation happens below
pullpc
; for architect mode: make red chest behavior for spell replacements independent of currently learned spells
; by ensuring the same number of RNG calls, no matter if you have the spell already or not
pushpc
org $8391A6
; DB=$7F, x=0, m=1
JSL SpellRNG ; overwrites LDA.b #$80 : STA $E747,Y
NOP
pullpc
SpellRNG:
LDA.b #$80 ; (overwritten instruction) mark chest item as spell
STA $E747,Y ; (overwritten instruction)
JSL $8082C7 ;
JSL $8082C7 ; advance RNG twice
RTL
; shops
pushpc
org $83B442
; DB=$83, x=1, m=1
JSL Shop ; overwrites STA $7FD0BF
pullpc
Shop:
STA $7FD0BF ; (overwritten instruction)
LDY $05AC ; load map number
CPY.b #$F0 ; check if ancient cave
BCC +
LDA $05B4 ; check if going to ancient cave entrance
BEQ +
LDA $7FE696 ; load next to next floor number
DEC
CPY.b #$F1 ; check if going to final floor
BCS ++ ; skip a decrement because next floor number is not incremented on final floor
DEC
++: CMP $D08015 ; check if past initial floor
BCC +
STA $4204 ; WRDIVL; dividend = floor number
STZ $4205 ; WRDIVH
TAX
LDA $D0801A
STA $4206 ; WRDIVB; divisor = shop_interval
STA $211C ; M7B; second factor = shop_interval
JSL $8082C7 ; advance RNG (while waiting for division to complete)
LDY $4216 ; RDMPYL; skip if remainder (i.e., floor number mod shop_interval) is not 0
BNE +
STA $211B
STZ $211B ; M7A; first factor = random number from 0 to 255
TXA
CLC
SBC $2135 ; MPYM; calculate (floor number) - (random number from 0 to shop_interval-1) - 1
STA $30 ; set shop id
STZ $05A8 ; initialize variable for sold out item tracking
STZ $05A9
PHB
PHP
JML $80A33A ; open shop menu (eventually causes return by reaching existing PLP : PLB : RTL at $809DB0)
+: RTL
; shop item select
pushpc
org $82DF50
; DB=$83, x=0, m=1
JML ShopItemSelected ; overwrites JSR $8B08 : CMP.b #$01
pullpc
ShopItemSelected:
LDA $1548 ; check inventory free space
BEQ +
JSR LoadShopSlotAsFlag
BIT $05A8 ; test item not already sold
BNE +
JML $82DF79 ; skip quantity selection and go directly to buy/equip
+: JML $82DF80 ; abort and go back to item selection
; track bought shop items
pushpc
org $82E084
; DB=$83, x=0, m=1
JSL ShopBuy ; overwrites LDA.b #$05 : LDX.w #$0007
NOP
org $82E10E
; DB=$83, x=0, m=1
JSL ShopEquip ; overwrites SEP #$10 : LDX $14DC
NOP
pullpc
ShopBuy:
JSR LoadShopSlotAsFlag
TSB $05A8 ; mark item as sold
LDA.b #$05 ; (overwritten instruction)
LDX.w #$0007 ; (overwritten instruction)
RTL
ShopEquip:
JSR LoadShopSlotAsFlag
TSB $05A8 ; mark item as sold
SEP #$10 ; (overwritten instruction)
LDX $14DC ; (overwritten instruction)
RTL
LoadShopSlotAsFlag:
TDC
LDA $14EC ; load currently selected shop slot number
ASL
TAX
LDA $8ED8C3,X ; load predefined bitmask with a single bit set
RTS
; mark bought items as sold out
pushpc
org $8285EA
; DB=$83, x=0, m=0
JSL SoldOut ; overwrites LDA [$FC],Y : AND #$01FF
NOP
pullpc
SoldOut:
LDA $8ED8C3,X ; load predefined bitmask with a single bit set
BIT $05A8 ; test sold items
BEQ +
LDA.w #$01CB ; load sold out item id
BRA ++
+: LDA [$FC],Y ; (overwritten instruction)
AND #$01FF ; (overwritten instruction)
++: RTL
; increase variety of red chest gear after B9
pushpc
org $839176
; DB=$7F, x=0, m=1
CLC ; {carry clear = disable this feature, carry set = enable this feature}
JSL RedChestGear ; overwrites LDX.w #$1000 : LDA $60
org $83917D
; DB=$7F, x=0, m=1
JSL RunEquipmentRNG ; overwrites LSR : JSR $9E11
pullpc
RedChestGear:
BCC +
REP #$20 ; support more than 127 items
+: LDX.w #$1000 ; (overwritten instruction)
LDA $60 ; (overwritten instruction)
RTL
RunEquipmentRNG:
BCS +
SEP #$20
PHK
PEA ++
PEA $8DD8
LSR
JML $839E11
+: LSR ; (overwritten instruction) divide by 2 (translates max item offset to max item number)
SEP #$20 ; (the max item number fits in 8bits since there are always fewer than 256 eligible items)
STA $004202 ; run RNG: fill WRMPYA multiplicand register with max item number
JSL $8082C7 ; run RNG: load 8bit accumulator with 1st random number from PRNG
STA $004203 ; run RNG: fill WRMPYB multiplier register with 1st random number and start multiplication
NOP
REP #$20
LDA $004216 ; run RNG: read RDMPYL+H multiplication result
STA $E746,Y ; save it for later
SEP #$20
JSL $8082C7 ; run RNG: load 8bit accumulator with 2nd random number from PRNG
STA $004203 ; run RNG: fill WRMPYB multiplier register with 2nd random number and start multiplication
CLC
TDC
LDA $004217 ; run RNG: read RDMPYH multiplication result
REP #$20
ADC $E746,Y
AND.w #$FF00
XBA
ASL ; multiply by 2 (translates selected item number to selected item offset)
++: TAX ; store result in 16bit X register
RTL
; relocate capsule cravings table
pushpc
org $82C55A
LDA $D09200,X ; overwrites LDA $95FF16,X
org $82C55F
LDA $D09202,X ; overwrites LDA $95FF18,X
org $82C572
LDA $D09200,X ; overwrites LDA $95FF16,X
pullpc
; set capsule monster starting xp
pushpc
org $82C313
; DB=$84, x=0, m=1
JSL CapsuleStartingXp ; overwrites LDX.w #$0000 : LDA.b #$00 : STA $7FF1AA,X : INX : CPX.w #$0015 : BNE $82C318
NOP #11
pullpc
CapsuleStartingXp:
PHB
REP #$20
LDA $D08012
STA $7FF1AA ; store low word of starting XP for first capsule monster
SEP #$20
LDA $D08014
STA $7FF1AC ; store highest byte of starting XP for first capsule monster
TDC
LDA.b #$11
LDX.w #$F1AA
LDY.w #$F1AD
MVN $7F,$7F ; pattern fill the remaining six capsule monster slots
PLB
RTL
; set starting capsule monster
pushpc
org $82C36A
; DB=$83, x=0, m=1
JSL StartingCapsule ; overwrites STZ $11A3 : LDA.b #$01
NOP
pullpc
StartingCapsule:
LDA $F02016 ; read starting capsule monster id
STA $11A3
LDA.b #$01 ; (overwritten instruction)
RTL
; enter ancient cave as if coming from the world map
pushpc
org $83B773
; DB=$7E, x=0, m=1
JSL CaveEntrance ; overwrites LDA $05AC : STA $05B4
NOP #2
pullpc
CaveEntrance:
LDA $05AC ; (overwritten instruction)
CMP.b #$68
BNE + ; when leaving gruberik, act as if leaving world map
TDC
+: STA $05B4 ; (overwritten instruction)
RTL
; enable run button
; directional input item crash fix
pushpc
org $83FC6C
REP #$10 ; overwrites BEQ $83FC8A : LDA.b #$80
LDA.b #$40
pullpc
; mid-turn death fix
pushpc
org $85B544
JSL MidTurnDeathFix ; overwrites JSL $85CCCE
pullpc
MidTurnDeathFix:
JSL $85CCCE ; (overwritten instruction) clear shared battle registers after attack
LDY.w #$000F ; offset to status effect byte
LDA ($BE),Y ; offset to stat block of attacker
BIT.b #$04 ; check death
BEQ +
TSX ; attacker died; abort script
INX #3
TXS
JML $85B476
+: RTL ; attacker still alive; continue script
; poison death fix
pushpc
org $818959
JSL PoisonDeathFix ; overwrites JSL $859DD4
pullpc
PoisonDeathFix:
JSL $859DD4 ; (overwritten instruction)
JSL $8593B7
RTL
; single-node room fix
pushpc
org $839C64
; DB=$7F, x=0, m=1
BNE + ; overwrites BNE $17
org $839C7B
; DB=$7F, x=0, m=1
JMP $9BE7 ; overwrites BRA $22 : LDX.w #$00FF
+: TDC
TAX
org $839C99
; DB=$7F, x=0, m=1
INX ; overwrites DEX : CPX.w #$0010 : BCS $E1
CPX.w #$0100
BCC $E1
pullpc
; door stairs fix
pushpc
org $839453
; DB=$7F, x=0, m=1
JSL DoorStairsFix ; overwrites JSR $9B18 : JSR $9D11
NOP #2
pullpc
DoorStairsFix:
CLC
LDY.w #$0000
--: LDX.w #$00FF ; loop through floor layout starting from the bottom right
-: LDA $EA00,X ; read node contents
BEQ + ; always skip empty nodes
BCC ++ ; 1st pass: skip all blocked nodes (would cause door stairs or rare stairs)
LDA $E9F0,X ; 2nd pass: skip only if the one above is also blocked (would cause door stairs)
++: BMI +
INY ; count usable nodes
+: DEX
BPL -
TYA
BNE ++ ; all nodes blocked?
SEC ; set up 2nd, less restrictive pass
BRA --
++: JSL $8082C7 ; advance RNG
STA $00211B
TDC
STA $00211B ; M7A; first factor = random number from 0 to 255
TYA
STA $00211C ; M7B; second factor = number of possible stair positions
LDA $002135 ; MPYM; calculate random number from 0 to number of possible stair positions - 1
TAY
LDX.w #$00FF ; loop through floor layout starting from the bottom right
-: LDA $EA00,X ; read node contents
BEQ + ; always skip empty nodes
BCC ++ ; if 1st pass was sufficient: skip all blocked nodes (prevent door stairs and rare stairs)
LDA $E9F0,X ; if 2nd pass was needed: skip only if the one above is also blocked (prevent door stairs)
++: BMI +
DEY ; count down to locate the (Y+1)th usable node
BMI ++
+: DEX
BPL -
++: TXA ; return selected stair node coordinate
RTL
; equipment text fix
pushpc
org $81F2E3
; DB=$9E, x=0, m=1
NOP #2 ; overwrites BPL $81F2D6
pullpc
; music menu fix
pushpc
org $82BF44
; DB=$83, x=0, m=1
BNE $12 ; overwrites BNE $06
pullpc
; logo skip
pushpc
org $80929A
; DB=$80, x=0, m=1
LDA.b #$00 ; overwrites LDA.b #$80
pullpc
; intro skip
pushpc
org $8080CF
; DB=$80, x=1, m=1
JML $8383BD ; overwrites JML $808281
pullpc
; SRAM map
; $F02000 16 signature
; $F02010 2 blue chest count
; $F02012 3 capsule starting xp
; $F02015 1 initial floor
; $F02016 1 starting capsule
; $F02017 1 iris treasures required
; $F02018 1 party members available
; $F02019 1 capsule monsters available
; $F0201A 1 shop interval
; $F0201B 1 inactive exp gain rate
; $F02030 1 selected goal
; $F02031 1 goal completion: boss
; $F02032 1 goal completion: iris_treasure_hunt
; $F02033 1 goal completion: master_iris_treasure_hunt
; $F02034 1 goal completion: final_floor
; $F0203D 1 death link enabled
; $F0203E 1 death link sent (monster id + 1)
; $F0203F 1 death link received
; $F02040 2 check counter for this save file (snes_blue_chests_checked)
; $F02042 2 RESERVED
; $F02044 2 check counter (client_ap_items_found)
; $F02046 2 check counter (snes_ap_items_found)
; $F02048 2 check counter for the slot (total_blue_chests_checked)
; $F0204A 2 check counter for this save file (snes_other_locations_checked)
; $F02050 16 coop uuid
; $F02060 var list of checked locations
; $F027E0 16 saved RX counters
; $F02800 2 received counter
; $F02802 2 processed counter
; $F02804 var list of received items
; $F06000 var architect mode RNG state backups