231 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			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"))
 | 
