Archipelago/worlds/ladx/LADXR/patches/bank3e.py

231 lines
7.1 KiB
Python

import os
import binascii
from ..assembler import ASM
from ..utils import formatText
import pkgutil
def hasBank3E(rom):
return rom.banks[0x3E][0] != 0x00
def generate_name(l, i):
if i < len(l):
name = l[i]
else:
name = f"player {i}"
name = name[:16]
assert(len(name) <= 16)
return 'db "' + name + '"' + ', $ff' * (17 - len(name)) + '\n'
# Bank $3E is used for large chunks of custom code.
# Mainly for new chest and dropped items handling.
def addBank3E(rom, seed, player_id, player_name_list):
# No default text for getting the bow, so use an unused slot.
rom.texts[0x89] = formatText("Found the {BOW}!")
rom.texts[0xD9] = formatText("Found the {BOOMERANG}!") # owl text slot reuse
rom.texts[0xBE] = rom.texts[0x111] # owl text slot reuse to get the master skull message in the first dialog group
rom.texts[0xC8] = formatText("Found {BOWWOW}! Which monster put him in a chest? He is a good boi, and waits for you at the Swamp.")
rom.texts[0xC9] = 0xC0A0 # Custom message slot
rom.texts[0xCA] = formatText("Found {ARROWS_10}!")
rom.texts[0xCB] = formatText("Found a {SINGLE_ARROW}... joy?")
# Create a trampoline to bank 0x3E in bank 0x00.
# There is very little room in bank 0, so we set this up as a single trampoline for multiple possible usages.
# the A register is preserved and can directly be used as a jumptable in page 3E.
# Trampoline at rst 8
# the A register is preserved and can directly be used as a jumptable in page 3E.
rom.patch(0, 0x0008, "0000000000000000000000000000", ASM("""
ld h, a
ld a, [$DBAF]
push af
ld a, $3E
call $080C ; switch bank
ld a, h
jp $4000
"""), fill_nop=True)
# Special trampoline to jump to the damage-entity code, we use this from bowwow to damage instead of eat.
rom.patch(0x00, 0x0018, "000000000000000000000000000000", ASM("""
ld a, $03
ld [$2100], a
call $71C0
ld a, [$DBAF]
ld [$2100], a
ret
"""))
def get_asm(name):
return pkgutil.get_data(__name__, "bank3e.asm/" + name).decode().replace("\r", "")
rom.patch(0x3E, 0x0000, 0x2F00, ASM("""
call MainJumpTable
pop af
jp $080C ; switch bank and return to normal code.
MainJumpTable:
rst 0 ; JUMP TABLE
dw MainLoop ; 0
dw RenderChestItem ; 1
dw GiveItemFromChest ; 2
dw ItemMessage ; 3
dw RenderDroppedKey ; 4
dw RenderHeartPiece ; 5
dw GiveItemFromChestMultiworld ; 6
dw CheckIfLoadBowWow ; 7
dw BowwowEat ; 8
dw HandleOwlStatue ; 9
dw ItemMessageMultiworld ; A
dw GiveItemAndMessageForRoom ; B
dw RenderItemForRoom ; C
dw StartGameMarinMessage ; D
dw GiveItemAndMessageForRoomMultiworld ; E
dw RenderOwlStatueItem ; F
dw UpdateInventoryMenu ; 10
dw LocalOnlyItemAndMessage ; 11
StartGameMarinMessage:
; Injection to reset our frame counter
call $27D0 ; Enable SRAM
ld hl, $B000
xor a
ldi [hl], a ;subsecond counter
ld a, $08 ;(We set the counter to 8 seconds, as it takes 8 seconds before link wakes up and marin talks to him)
ldi [hl], a ;second counter
xor a
ldi [hl], a ;minute counter
ldi [hl], a ;hour counter
ld hl, $B010
ld a, $01 ;tarin's gift gets skipped for some reason, so inflate count by 1
ldi [hl], a ;check counter low
xor a
ldi [hl], a ;check counter high
; Show the normal message
ld a, $01
jp $2385
TradeSequenceItemData:
; tile attributes
db $0D, $0A, $0D, $0D, $0E, $0E, $0D, $0D, $0D, $0E, $09, $0A, $0A, $0D
; tile index
db $1A, $B0, $B4, $B8, $BC, $C0, $C4, $C8, $CC, $D0, $D4, $D8, $DC, $E0
UpdateInventoryMenu:
ld a, [wTradeSequenceItem]
ld hl, wTradeSequenceItem2
or [hl]
ret z
ld hl, TradeSequenceItemData
ld a, [$C109]
ld e, a
ld d, $00
add hl, de
; Check if we need to increase the counter
ldh a, [$E7] ; frame counter
and $0F
jr nz, .noInc
ld a, e
inc a
cp 14
jr nz, .noWrap
xor a
.noWrap:
ld [$C109], a
.noInc:
; Check if we have the item
ld b, e
inc b
ld a, $01
ld de, wTradeSequenceItem
.shiftLoop:
dec b
jr z, .shiftLoopDone
sla a
jr nz, .shiftLoop
; switching to second byte
ld de, wTradeSequenceItem2
ld a, $01
jr .shiftLoop
.shiftLoopDone:
ld b, a
ld a, [de]
and b
ret z ; skip this item
ld b, [hl]
push hl
; Write the tile attribute data
ld a, $01
ldh [$4F], a
ld hl, $9C6E
call WriteToVRAM
inc hl
call WriteToVRAM
ld de, $001F
add hl, de
call WriteToVRAM
inc hl
call WriteToVRAM
; Write the tile data
xor a
ldh [$4F], a
pop hl
ld de, 14
add hl, de
ld b, [hl]
ld hl, $9C6E
call WriteToVRAM
inc b
inc b
inc hl
call WriteToVRAM
ld de, $001F
add hl, de
dec b
call WriteToVRAM
inc hl
inc b
inc b
call WriteToVRAM
ret
WriteToVRAM:
ldh a, [$41]
and $02
jr nz, WriteToVRAM
ld [hl], b
ret
LocalOnlyItemAndMessage:
call GiveItemFromChest
call ItemMessage
ret
""" + get_asm("multiworld.asm")
+ get_asm("link.asm")
+ get_asm("chest.asm")
+ get_asm("bowwow.asm")
+ get_asm("message.asm")
+ get_asm("itemnames.asm")
+ "".join(generate_name(["The Server"] + player_name_list, i ) for i in range(100)) # allocate
+ 'db "another world", $ff\n'
+ get_asm("owl.asm"), 0x4000), fill_nop=True)
# 3E:3300-3616: Multiworld flags per room (for both chests and dropped keys)
# 3E:3800-3B16: DroppedKey item types
# 3E:3B16-3E2C: Owl statue or trade quest items
# Put 20 rupees in all owls by default.
rom.patch(0x3E, 0x3B16, "00" * 0x316, "1C" * 0x316)
# Prevent the photo album from crashing due to serial interrupts
rom.patch(0x28, 0x00D2, ASM("ld a, $09"), ASM("ld a, $01"))