Files
dockipelago/worlds/ladx/LADXR/patches/core.py
T

743 lines
24 KiB
Python

from ..assembler import ASM
from ..entranceInfo import ENTRANCE_INFO
from ..roomEditor import RoomEditor, ObjectWarp, ObjectHorizontal
from ..backgroundEditor import BackgroundEditor
from .. import utils
def bugfixWrittingWrongRoomStatus(rom):
# The normal rom contains a pretty nasty bug where door closing triggers in D7/D8 can effect doors in
# dungeons D1-D6. This fix should prevent this.
rom.patch(0x02, 0x1D21, 0x1D3C, ASM("call $5B9F"), fill_nop=True)
def fixEggDeathClearingItems(rom):
rom.patch(0x01, 0x1E79, ASM("cp $0A"), ASM("cp $08"))
def fixWrongWarp(rom):
rom.patch(0x00, 0x18CE, ASM("cp $04"), ASM("cp $03"))
re = RoomEditor(rom, 0x2b)
for x in range(10):
re.removeObject(x, 7)
re.objects.append(ObjectHorizontal(0, 7, 0x2C, 10))
while len(re.getWarps()) < 4:
re.objects.append(ObjectWarp(1, 3, 0x7a, 80, 124))
re.store(rom)
def bugfixBossroomTopPush(rom):
rom.patch(0x14, 0x14D9, ASM("""
ldh a, [$99]
dec a
ldh [$99], a
"""), ASM("""
jp $7F80
"""), fill_nop=True)
rom.patch(0x14, 0x3F80, "00" * 0x80, ASM("""
ldh a, [$99]
cp $50
jr nc, up
down:
inc a
ldh [$99], a
jp $54DE
up:
dec a
ldh [$99], a
jp $54DE
"""), fill_nop=True)
def bugfixPowderBagSprite(rom):
rom.patch(0x03, 0x2055, "8E16", "0E1E")
def easyColorDungeonAccess(rom):
re = RoomEditor(rom, 0x312)
re.entities = [(3, 1, 246), (6, 1, 247)]
re.store(rom)
def removeGhost(rom):
## Ghost patch
# Do not have the ghost follow you after dungeon 4
rom.patch(0x03, 0x1E1B, ASM("LD [$DB79], A"), "", fill_nop=True)
def alwaysAllowSecretBook(rom):
rom.patch(0x15, 0x3F23, ASM("ld a, [$DB0E]\ncp $0E"), ASM("xor a\ncp $00"), fill_nop=True)
rom.patch(0x15, 0x3F2A, 0x3F30, "", fill_nop=True)
def cleanup(rom):
# Remove unused rooms to make some space in the rom
re = RoomEditor(rom, 0x2C4)
re.objects = []
re.entities = []
re.store(rom, 0x2C4)
re.store(rom, 0x2D4)
re.store(rom, 0x277)
re.store(rom, 0x278)
re.store(rom, 0x279)
re.store(rom, 0x1ED)
re.store(rom, 0x1FC) # Beta room
rom.texts[0x02B] = b'' # unused text
def disablePhotoPrint(rom):
rom.patch(0x28, 0x07CC, ASM("ldh [$01], a\nldh [$02], a"), "", fill_nop=True) # do not reset the serial link
rom.patch(0x28, 0x0483, ASM("ld a, $13"), ASM("jr $EA", 0x4483)) # Do not print on A press, but jump to cancel
rom.patch(0x28, 0x0492, ASM("ld hl, $4439"), ASM("ret"), fill_nop=True) # Do not show the print/cancel overlay
def fixMarinFollower(rom):
# Allow opening of D0 with marin
rom.patch(0x02, 0x3402, ASM("ld a, [$DB73]"), ASM("xor a"), fill_nop=True)
# Instead of uselessly checking for sidescroller rooms for follower spawns, check for color dungeon instead
rom.patch(0x01, 0x1FCB, 0x1FD3, ASM("cp $FF\nret z"), fill_nop=True)
# Do not load marin graphics in color dungeon
rom.patch(0x00, 0x2EA6, 0x2EB0, ASM("cp $FF\njp $2ED3"), fill_nop=True)
# Fix marin on taltal bridge causing a lockup if you have marin with you
# This changes the location where the index to the marin entity is stored from it's normal location
# To the memory normal reserved for progress on the egg maze (which is reset to 0 on a warp)
rom.patch(0x18, 0x1EF7, ASM("ld [$C50F], a"), ASM("ld [$C5AA], a"))
rom.patch(0x18, 0x2126, ASM("ld a, [$C50F]"), ASM("ld a, [$C5AA]"))
rom.patch(0x18, 0x2139, ASM("ld a, [$C50F]"), ASM("ld a, [$C5AA]"))
rom.patch(0x18, 0x214F, ASM("ld a, [$C50F]"), ASM("ld a, [$C5AA]"))
rom.patch(0x18, 0x2166, ASM("ld a, [$C50F]"), ASM("ld a, [$C5AA]"))
def quickswap(rom, button):
rom.patch(0x00, 0x1094, ASM("jr c, $49"), ASM("jr nz, $49")) # prevent agressive key repeat
rom.patch(0x00, 0x10BC, # Patch the open minimap code to swap the your items instead
ASM("xor a\nld [$C16B], a\nld [$C16C], a\nld [$DB96], a\nld a, $07\nld [$DB95], a"), ASM("""
ld a, [$DB%02X]
ld e, a
ld a, [$DB%02X]
ld [$DB%02X], a
ld a, e
ld [$DB%02X], a
ret
""" % (button, button + 2, button, button + 2)))
def injectMainLoop(rom):
rom.patch(0x00, 0x0346, ASM("""
ldh a, [$FE]
and a
jr z, $08
"""), ASM("""
; Call the mainloop handler
xor a
rst 8
"""), fill_nop=True)
def warpHome(rom):
# Patch the S&Q menu to allow 3 options
rom.patch(0x01, 0x012A, 0x0150, ASM("""
ld hl, $C13F
call $6BA8 ; make sound on keypress
ldh a, [$CC] ; load joystick status
and $04 ; if up
jr z, noUp
dec [hl]
noUp:
ldh a, [$CC] ; load joystick status
and $08 ; if down
jr z, noDown
inc [hl]
noDown:
ld a, [hl]
cp $ff
jr nz, noWrapUp
ld a, $02
noWrapUp:
cp $03
jr nz, noWrapDown
xor a
noWrapDown:
ld [hl], a
jp $7E02
"""), fill_nop=True)
rom.patch(0x01, 0x3E02, 0x3E20, ASM("""
swap a
add a, $48
ld hl, $C018
ldi [hl], a
ld a, $24
ldi [hl], a
ld a, $BE
ldi [hl], a
ld [hl], $00
ret
"""), fill_nop=True)
rom.patch(0x01, 0x00B7, ASM("""
ld a, [$C13F]
cp $01
jr z, $3B
"""), ASM("""
ld a, [$C13F]
jp $7E20
"""), fill_nop=True)
re = RoomEditor(rom, 0x2a3)
warp = re.getWarps()[0]
type = 0x00
map = 0x00
room = warp.room
x = warp.target_x
y = warp.target_y
one_way = [
'd0',
'd1',
'd3',
'd4',
'd6',
'd8',
'animal_cave',
'right_fairy',
'rooster_grave',
'prairie_left_cave2',
'prairie_left_fairy',
'armos_fairy',
'boomerang_cave',
'madbatter_taltal',
'forest_madbatter',
]
one_way = {ENTRANCE_INFO[x].room for x in one_way}
if warp.room in one_way:
# we're starting at a one way exit room
# warp indoors to avoid soft locks
type = 0x01
map = 0x10
room = 0xa3
x = 0x50
y = 0x7f
rom.patch(0x01, 0x3E20, 0x3E6B, ASM("""
; First, handle save & quit
cp $01
jp z, $40F9
and a
jp z, $40BE ; return to normal "return to game" handling
ld a, [$C509] ; Check if we have an item in the shop
and a
jp nz, $40BE ; return to normal "return to game" handling
ld a, $0B
ld [$DB95], a
call $0C7D
; Replace warp0 tile data, and put link on that tile.
ld a, $%02x ; Type
ld [$D401], a
ld a, $%02x ; Map
ld [$D402], a
ld a, $%02x ; Room
ld [$D403], a
ld a, $%02x ; X
ld [$D404], a
ld a, $%02x ; Y
ld [$D405], a
ldh a, [$98]
swap a
and $0F
ld e, a
ldh a, [$99]
sub $08
and $F0
or e
ld [$D416], a
ld a, $07
ld [$DB96], a
ret
jp $40BE ; return to normal "return to game" handling
""" % (type, map, room, x, y)), fill_nop=True)
# Patch the RAM clear not to delete our custom dialog when we screen transition
# This is kind of horrible as it relies on bank 1 being loaded, lol
rom.patch(0x01, 0x042C, "C629", "6B7E")
rom.patch(0x01, 0x3E6B, 0x3E7B, ASM("""
ld bc, $A0
call $29DC
ld bc, $1200
ld hl, $C100
call $29DF
ret
"""), fill_nop=True)
# Patch the S&Q screen to have 3 options.
be = BackgroundEditor(rom, 0x0D)
for n in range(2, 18):
be.tiles[0x99C0 + n] = be.tiles[0x9980 + n]
be.tiles[0x99A0 + n] = be.tiles[0x9960 + n]
be.tiles[0x9980 + n] = be.tiles[0x9940 + n]
be.tiles[0x9960 + n] = be.tiles[0x98e0 + n]
be.tiles[0x9960 + 10] = 0xCE
be.tiles[0x9960 + 11] = 0xCF
be.tiles[0x9960 + 12] = 0xC4
be.tiles[0x9960 + 13] = 0x7F
be.tiles[0x9960 + 14] = 0x7F
be.store(rom)
sprite_data = [
0b00000000,
0b01000100,
0b01000101,
0b01000101,
0b01111101,
0b01000101,
0b01000101,
0b01000100,
0b00000000,
0b11100100,
0b00010110,
0b00010101,
0b00010100,
0b00010100,
0b00010100,
0b11100100,
]
for n in range(32):
rom.banks[0x0F][0x08E0 + n] = sprite_data[n // 2]
def addFrameCounter(rom, check_count):
# Patch marin giving the start the game to jump to a custom handler
rom.patch(0x05, 0x1299, ASM("ld a, $01\ncall $2385"), ASM("push hl\nld a, $0D\nrst 8\npop hl"), fill_nop=True)
# Add code that needs to be called every frame to tick our ingame time counter.
rom.patch(0x00, 0x0091, "00" * (0x100 - 0x91), ASM("""
ld a, [$DB95] ;Get the gameplay type
dec a ; and if it was 1
ret z ; we are at the credits and the counter should stop.
; Check if the timer expired
ld hl, $FF0F
bit 2, [hl]
ret z
res 2, [hl]
; Increase the "subsecond" counter, and continue if it "overflows"
call $27D0 ; Enable SRAM
ld hl, $B000
ld a, [hl]
inc a
cp $20
ld [hl], a
ret nz
xor a
ldi [hl], a
; Increase the seconds counter/minutes/hours counter
increaseSecMinHours:
ld a, [hl]
inc a
daa
ld [hl], a
cp $60
ret nz
xor a
ldi [hl], a
jr increaseSecMinHours
"""), fill_nop=True)
# Replace a cgb check with the call to our counter code.
rom.patch(0x00, 0x0367, ASM("ld a, $0C\ncall $0B0B"), ASM("call $0091\nld a, $2C"))
# Do not switch to 8x8 sprite mode
rom.patch(0x17, 0x2E9E, ASM("res 2, [hl]"), "", fill_nop=True)
# We need to completely reorder link sitting on the raft to work with 16x8 sprites.
sprites = rom.banks[0x38][0x1600:0x1800]
sprites[0x1F0:0x200] = b'\x00' * 16
for index, position in enumerate(
(0, 0x1F,
1, 0x1F, 2, 0x1F,
7, 8,
3, 9, 4, 10, 5, 11, 6, 12,
3, 13, 4, 14, 5, 15, 6, 16,
3, 17, 4, 18, 5, 19, 6, 20,
)):
rom.banks[0x38][0x1600+index*0x10:0x1610+index*0x10] = sprites[position*0x10:0x10+position*0x10]
rom.patch(0x27, 0x376E, 0x3776, "00046601", fill_nop=True)
rom.patch(0x27, 0x384E, ASM("ld c, $08"), ASM("ld c, $04"))
rom.patch(0x27, 0x3776, 0x3826,
"FA046002"
"0208640402006204"
"0A106E030A086C030A006A030AF86803"
"FA046002"
"0208640402006204"
"0A1076030A0874030A0072030AF87003"
"FA046002"
"0208640402006204"
"0A107E030A087C030A007A030AF87803"
, fill_nop=True)
rom.patch(0x27, 0x382E, ASM("ld a, $6C"), ASM("ld a, $80")) # OAM start position
rom.patch(0x27, 0x384E, ASM("ld c, $08"), ASM("ld c, $04")) # Amount of overlay OAM data
rom.patch(0x27, 0x3826, 0x382E, ASM("dw $7776, $7792, $77AE, $7792")) # pointers to animation
rom.patch(0x27, 0x3846, ASM("ld c, $2C"), ASM("ld c, $1C")) # Amount of OAM data
# TODO: fix flying windfish
# Upper line of credits roll into "TIME"
rom.patch(0x17, 0x069D, 0x0713, ASM("""
ld hl, OAMData
ld de, $C000 ; OAM Buffer
ld bc, $0048
call $2914
ret
OAMData:
db $20, $18, $34, $00 ;T
db $20, $20, $20, $00 ;I
db $20, $28, $28, $00 ;M
db $20, $30, $18, $00 ;E
db $20, $70, $16, $00 ;D
db $20, $78, $18, $00 ;E
db $20, $80, $10, $00 ;A
db $20, $88, $34, $00 ;T
db $20, $90, $1E, $00 ;H
db $50, $18, $14, $00 ;C
db $50, $20, $1E, $00 ;H
db $50, $28, $18, $00 ;E
db $50, $30, $14, $00 ;C
db $50, $38, $24, $00 ;K
db $50, $40, $32, $00 ;S
db $68, $38, $%02x, $00 ;0
db $68, $40, $%02x, $00 ;0
db $68, $48, $%02x, $00 ;0
""" % ((((check_count // 100) % 10) * 2) | 0x40, (((check_count // 10) % 10) * 2) | 0x40, ((check_count % 10) * 2) | 0x40), 0x469D), fill_nop=True)
# Lower line of credits roll into XX XX XX
rom.patch(0x17, 0x0784, 0x082D, ASM("""
ld hl, OAMData
ld de, $C048 ; OAM Buffer
ld bc, $0038
call $2914
call $27D0 ; Enable SRAM
ld hl, $C04A
ld a, [$B003] ; hours
call updateOAM
ld a, [$B002] ; minutes
call updateOAM
ld a, [$B001] ; seconds
call updateOAM
ld a, [$DB58] ; death count high
call updateOAM
ld a, [$DB57] ; death count low
call updateOAM
ld a, [$B011] ; check count high
call updateOAM
ld a, [$B010] ; check count low
call updateOAM
ret
updateOAM:
ld de, $0004
ld b, a
swap a
and $0F
add a, a
or $40
ld [hl], a
add hl, de
ld a, b
and $0F
add a, a
or $40
ld [hl], a
add hl, de
ret
OAMData:
db $38, $18, $40, $00 ;0 (10 hours)
db $38, $20, $40, $00 ;0 (1 hours)
db $38, $30, $40, $00 ;0 (10 minutes)
db $38, $38, $40, $00 ;0 (1 minutes)
db $38, $48, $40, $00 ;0 (10 seconds)
db $38, $50, $40, $00 ;0 (1 seconds)
db $00, $00, $40, $00 ;0 (1000 death)
db $38, $80, $40, $00 ;0 (100 death)
db $38, $88, $40, $00 ;0 (10 death)
db $38, $90, $40, $00 ;0 (1 death)
; checks
db $00, $00, $40, $00 ;0
db $68, $18, $40, $00 ;0
db $68, $20, $40, $00 ;0
db $68, $28, $40, $00 ;0
""", 0x4784), fill_nop=True)
# Grab the "mostly" complete A-Z font
sprites = rom.banks[0x38][0x1100:0x1400]
for index, position in enumerate((
0x10, 0x20, # A
0x11, 0x21, # B
0x12, 0x12 | 0x100, # C
0x13, 0x23, # D
0x14, 0x24, # E
0x14, 0x25, # F
0x12, 0x22, # G
0x20 | 0x100, 0x26, # H
0x17, 0x17 | 0x100, # I
0x28, 0x28, # J
0x19, 0x29, # K
0x06, 0x07, # L
0x1A, 0x2A, # M
0x1B, 0x2B, # N
0x00, 0x00, # O?
0x00, 0x00, # P?
#0x00, 0x00, # Q?
0x11, 0x18, # R
0x1C, 0x2C, # S
0x1D, 0x2D, # T
0x26, 0x10, # U
0x00, 0x00, # V?
0x1E, 0x2E, # W
#0x00, 0x00, # X?
#0x00, 0x00, # Y?
0x27, 0x27, # Z
)):
sprite = sprites[(position&0xFF)*0x10:0x10+(position&0xFF)*0x10]
if position & 0x100:
for n in range(4):
sprite[n * 2], sprite[14 - n * 2] = sprite[14 - n * 2], sprite[n * 2]
sprite[n * 2 + 1], sprite[15 - n * 2] = sprite[15 - n * 2], sprite[n * 2 + 1]
rom.banks[0x38][0x1100+index*0x10:0x1110+index*0x10] = sprite
# Number graphics change for the end
tile_graphics = """
........ ........ ........ ........ ........ ........ ........ ........ ........ ........
.111111. ..1111.. .111111. .111111. ..11111. 11111111 .111111. 11111111 .111111. .111111.
11333311 .11331.. 11333311 11333311 .113331. 13333331 11333311 13333331 11333311 11333311
13311331 113331.. 13311331 13311331 1133331. 13311111 13311331 11111331 13311331 13311331
13311331 133331.. 13311331 11111331 1331331. 1331.... 13311331 ...11331 13311331 13311331
13311331 133331.. 11111331 ....1331 1331331. 1331.... 13311111 ...13311 13311331 13311331
13311331 111331.. ...13311 .1111331 1331331. 1331111. 1331.... ..11331. 13311331 13311331
13311331 ..1331.. ..11331. .1333331 13313311 13333311 1331111. ..13311. 11333311 11333331
13311331 ..1331.. ..13311. .1111331 13333331 13311331 13333311 .11331.. 13311331 .1111331
13311331 ..1331.. .11331.. ....1331 11113311 11111331 13311331 .13311.. 13311331 ....1331
13311331 ..1331.. .13311.. ....1331 ...1331. ....1331 13311331 11331... 13311331 ....1331
13311331 ..1331.. 11331... 11111331 ...1331. 11111331 13311331 13311... 13311331 11111331
13311331 ..1331.. 13311111 13311331 ...1331. 13311331 13311331 1331.... 13311331 13311331
11333311 ..1331.. 13333331 11333311 ...1331. 11333311 11333311 1331.... 11333311 11333311
.111111. ..1111.. 11111111 .111111. ...1111. .111111. .111111. 1111.... .111111. .111111.
........ ........ ........ ........ ........ ........ ........ ........ ........ ........
""".strip()
for n in range(10):
gfx_high = "\n".join([line.split(" ")[n] for line in tile_graphics.split("\n")[:8]])
gfx_low = "\n".join([line.split(" ")[n] for line in tile_graphics.split("\n")[8:]])
rom.banks[0x38][0x1400+n*0x20:0x1410+n*0x20] = utils.createTileData(gfx_high)
rom.banks[0x38][0x1410+n*0x20:0x1420+n*0x20] = utils.createTileData(gfx_low)
def addWarpImprovements(rom, extra_warps):
# Patch in a warp icon
tile = utils.createTileData( \
"""11111111
10000000
10200320
10323200
10033300
10023230
10230020
10000000""", key="0231")
MINIMAP_BASE = 0x3800
# This is replacing a junk tile never used on the minimap
rom.banks[0x2C][MINIMAP_BASE + len(tile) * 0x65 : MINIMAP_BASE + len(tile) * 0x66] = tile
# Allow using ENTITY_WARP for finding which map sections are warps
# Interesting - 3CA0 should be free, but something has pushed all the code forward a byte
rom.patch(0x02, 0x3CA1, None, ASM("""
ld e, $0F
ld d, $00
warp_search_loop:
; Warp search loop
ld hl, $C3A0
add hl, de ; $5FE1: $19
ld a, [hl] ; $5FE2: $7E
cp $61 ; ENTITY_WARP
jr nz, search_continue ; if it's not a warp, check the next one
ld hl, $C280
add hl, de
ld a, [hl]
and a
jr z, search_continue ; if this is despawned, check the next one
found:
jp $511B ; found
search_continue:
dec e
ld a, e
cp $FF
jr nz, warp_search_loop
not_found:
jp $512B
"""))
# Insert redirect to above code
rom.patch(0x02, 0x1109, ASM("""
ldh a, [$F6]
cp 1
"""), ASM("""
jp $7CA1
nop
"""))
# Leaves some extra bytes behind, if we need more space in 0x02
# On warp hole, open map instead
rom.patch(0x19, 0x1DB9, None, ASM("""
ld a, 7 ; Set GAMEPLAY_MAP
ld [$DB95], a
ld a, 0 ; reset subtype
ld [$DB96], a
ld a, 1 ; Set flag for using teleport
ld [$FFDD], a
ret
"""), fill_nop=True)
# Patch over some instructions that decided if we are in debug mode holding some
# buttons with instead checking for FFDD (why FFDD? It appears to be never used anywhere, so we repurpose it for "is in teleport mode")
rom.banks[0x01][0x17B8] = 0xDD
rom.banks[0x01][0x17B9] = 0xFF
rom.banks[0x01][0x17FD] = 0xDD
rom.banks[0x01][0x17FE] = 0xFF
# If in warp mode, don't allow manual exit
rom.patch(0x01, 0x1800, "20021E60", ASM("jp nz, $5818"), fill_nop=True)
# Allow warp with just B
rom.banks[0x01][0x17C0] = 0x20
# Allow cursor to move over black squares
# This allows warping to undiscovered areas - a fine cheat, but needs a check for wOverworldRoomStatus in the warp code
CHEAT_WARP_ANYWHERE = False
if CHEAT_WARP_ANYWHERE:
rom.patch(0x01, 0x1AE8, None, ASM("jp $5AF5"))
# This disables the arrows around the selection bubble
#rom.patch(0x01, 0x1B6F, None, ASM("ret"), fill_nop=True)
# Fix lag when moving the cursor
# One option - just disable the delay code
#rom.patch(0x01, 0x1A76, 0x1A76+3, ASM("xor a"), fill_nop=True)
#rom.banks[0x01][0x1A7C] = 0
# Another option - just remove the animation
rom.banks[0x01][0x1B20] = 0
rom.banks[0x01][0x1B3B] = 0
# Patch the icon for all teleports
all_warps = [0x01, 0x95, 0x2C, 0xEC]
if extra_warps:
# mamu
all_warps.append(0x45)
# Tweak the flute location
rom.banks[0x14][0x0E95] += 0x10
rom.banks[0x14][0x0EA3] += 0x01
mamu_pond = RoomEditor(rom, 0x45)
# Remove some tall grass so we can add a warp instead
mamu_pond.changeObject(1, 6, 0xE8)
mamu_pond.moveObject(1, 6, 3, 5)
mamu_pond.addEntity(3, 5, 0x61)
mamu_pond.store(rom)
# eagle
all_warps.append(0x0F)
room = RoomEditor(rom, 0x0F)
# Move one cliff edge and change it into a pit
room.changeObject(7, 6, 0xE8)
room.moveObject(7, 6, 6, 4)
# Add the warp
room.addEntity(6, 4, 0x61)
# move the two corners
room.moveObject(6, 7, 7, 7)
room.moveObject(6, 6, 7, 6)
for object in room.objects:
# Extend the lower wall
if ((object.x == 0 and object.y == 7)
# Extend the lower floor
or (object.x == 0 and object.y == 6)):
room.overlay[object.x + object.count + object.y * 10] = object.type_id
object.count += 1
room.store(rom)
for warp in all_warps:
# Set icon
rom.banks[0x20][0x168B + warp] = 0x55
# Set text
if not rom.banks[0x01][0x1959 + warp]:
rom.banks[0x01][0x1959 + warp] = 0x42
# Set palette
# rom.banks[0x20][0x178B + 0x95] = 0x1
# Setup [?!] icon on map and associated text
rom.banks[0x01][0x1909 + 0x42] = 0x2B
rom.texts[0x02B] = utils.formatText('Warp')
# call warp function (why not just jmp?!)
rom.patch(0x01, 0x17C3, None, ASM("""
call $7E7B
ret
"""))
# Build a switch statement by hand
warp_jump = "".join(f"cp ${hex(warp)[2:]}\njr z, success\n" for warp in all_warps)
rom.patch(0x01, 0x3E7B, None, ASM(f"""
TeleportHandler:
ld a, [$DBB4] ; Load the current selected tile
; TODO: check if actually revealed so we can have free movement
; Check cursor against different tiles to see if we are selecting a warp
{warp_jump}
jr exit
success:
ld a, $0B
ld [$DB95], a ; Gameplay type
xor a
ld [$D401], a ; wWarp0MapCategory
ldh [$DD], a ; unset teleport flag(!!!)
ld [$D402], a ; wWarp0Map
ld a, [$DBB4] ; wDBB4
ld [$D403], a ; wWarp0Room
ld a, $68
ld [$D404], a ; wWarp0DestinationX
ldh [$98], a ; LinkPositionY
ld [$D475], a
ld a, $70
ld [$D405], a ; wWarp0DestinationY
ldh [$99], a ; LinkPositionX
ld a, $66
ld [$D416], a ; wWarp0PositionTileIndex
ld a, $07
ld [$DB96], a ; wGameplaySubtype
ldh a, [$A2]
ld [$DBC8], a
call $0C83 ; ApplyMapFadeOutTransition
xor a ; $5DF3: $AF
ld [$C167], a ; $5DF4: $EA $67 $C1
exit:
ret
"""))