Beispiel #1
0
def upgradeHealthContainers(rom):
    # Reuse 2 unused shop messages for the heart containers.
    rom.texts[0x2A] = formatText("You found a {HEART_CONTAINER}!")
    rom.texts[0x2B] = formatText("You lost a heart!")

    rom.patch(0x03,
              0x19DC,
              ASM("""
        ld   de, $59D8
        call $3BC0
    """),
              ASM("""
        ld   a, $05  ; renderHeartPiece
        rst  8
    """),
              fill_nop=True)
    rom.patch(0x03,
              0x19F0,
              ASM("""
        ld   hl, $DB5B
        inc  [hl]
        ld   hl, $DB93
        ld   [hl], $FF
    """),
              ASM("""
        ld   a, $06 ; giveItemMultiworld
        rst  8
        ld   a, $0A ; messageForItemMultiworld
        rst  8
skip:
    """),
              fill_nop=True)  # add heart->remove heart on heart container
Beispiel #2
0
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)
Beispiel #3
0
def upgradeOverworldOwlStatues(rom):
    # Replace the code that handles signs/owl statues on the overworld
    # This removes a "have marin with you" special case to make some room for our custom owl handling.
    rom.patch(0x00, 0x201A, ASM("""
        cp   $6F
        jr   z, $2B
        cp   $D4
        jr   z, $27
        ld   a, [$DB73]
        and  a
        jr   z, $08
        ld   a, $78
        call $237C
        jp   $20CF
    """), ASM("""
        cp   $D4
        jr   z, $2B
        cp   $6F
        jr   nz, skip

        ld   a, $09
        rst 8
        jp   $20CF
skip:
    """), fill_nop=True)
Beispiel #4
0
def fixSeashell(rom):
    # Do not unload if we have the lvl2 sword.
    rom.patch(0x03,
              0x1FD3,
              ASM("ld a, [$DB4E]\ncp $02\njp nc, $3F8D"),
              "",
              fill_nop=True)
    # Do not unload in the ghost house
    rom.patch(0x03,
              0x1FE8,
              ASM("ldh  a, [$F8]\nand  $40\njp z, $3F8D"),
              "",
              fill_nop=True)

    # Call our special rendering code
    rom.patch(0x03,
              0x1FF2,
              ASM("ld de, $5FD1\ncall $3C77"),
              ASM("ld a, $05\nrst 8"),
              fill_nop=True)

    # Call our special handlers for messages and pickup
    rom.patch(0x03,
              0x2368,
              0x237C,
              ASM("""
        ld   a, $0A  ; showMessageMultiworld
        rst  8
        ld   a, $06  ; giveItemMultiworld
        rst  8
        call $512A
        ret
    """),
              fill_nop=True)
Beispiel #5
0
def flameThrowerShieldRequirement(rom):
    # if you somehow get a lvl3 shield or higher, it no longer works against the flamethrower, easy fix.
    rom.patch(
        0x03,
        0x2EBA,
        ASM("ld a, [$DB44]\ncp $02\nret nz"),  # if not shield level 2
        ASM("ld a, [$DB44]\ncp $02\nret c"))  # if not shield level 2 or higher
Beispiel #6
0
def fixInstruments(rom):
    rom.patch(0x03, 0x1EA9, 0x1EAE, "", fill_nop=True)
    rom.patch(0x03,
              0x1EB9,
              0x1EC8,
              ASM("""
        ; Render sprite
        ld   a, $05
        rst  8
    """),
              fill_nop=True)

    # Patch the message and instrument giving code
    rom.patch(0x03,
              0x1EE3,
              0x1EF6,
              ASM("""
        ; Handle item effect
        ld   a, $06 ; giveItemMultiworld
        rst  8
        
        ;Show message
        ld   a, $0A ; showMessageMultiworld
        rst  8
    """),
              fill_nop=True)

    # Color cycle palette 7 instead of 1
    rom.patch(0x36, 0x30F0, ASM("ld de, $DC5C"), ASM("ld de, $DC84"))
Beispiel #7
0
def upgradeDungeonOwlStatues(rom):
    # Call our custom handler after the check for the stone beak
    rom.patch(0x18,
              0x1EA2,
              ASM("ldh a, [$F7]\ncp $FF\njr nz, $05"),
              ASM("ld a, $09\nrst 8\nret"),
              fill_nop=True)
Beispiel #8
0
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)
Beispiel #9
0
def removeFlashingLights(rom):
    # Remove the switching between two backgrounds at mamu, always show the spotlights.
    rom.patch(0x00,
              0x01EB,
              ASM("ldh a, [$E7]\nrrca\nand $80"),
              ASM("ld a, $80"),
              fill_nop=True)
    # Remove flashing colors from shopkeeper killing you after stealing and the mad batter giving items.
    rom.patch(0x24, 0x3B77, ASM("push bc"), ASM("ret"))
Beispiel #10
0
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)
Beispiel #11
0
    def patch(self, rom, option, *, multiworld=None):
        rom.banks[0x14][self.addr] = CHEST_ITEMS[option]

        if self.room == 0x1B6:
            # Patch the code that gives the nightmare key when you throw the pot at the chest in dungeon 6
            # As this is hardcoded for a specific chest type
            rom.patch(3, 0x145D, ASM("ld a, $19"), ASM("ld a, $%02x" % (CHEST_ITEMS[option])))

        if multiworld is not None:
            rom.banks[0x3E][0x3300 + self.room] = multiworld
Beispiel #12
0
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
Beispiel #13
0
def noMusic(rom):
    rom.patch(0x1B,
              0x001E,
              ASM("ld hl, $D368\nldi a, [hl]"),
              ASM("xor a"),
              fill_nop=True)
    rom.patch(0x1E,
              0x001E,
              ASM("ld hl, $D368\nldi a, [hl]"),
              ASM("xor a"),
              fill_nop=True)
Beispiel #14
0
def forceLinksPalette(rom, index):
    # This forces the link sprite into a specific palette index ignoring the tunic options.
    rom.patch(0,
              0x1D8C,
              ASM("ld a, [$DC0F]\nand a\njr z, $03\ninc a"),
              ASM("ld a, $%02X" % (index)),
              fill_nop=True)
    rom.patch(0,
              0x1DD2,
              ASM("ld a, [$DC0F]\nand a\njr z, $03\ninc a"),
              ASM("ld a, $%02X" % (index)),
              fill_nop=True)
Beispiel #15
0
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)))
Beispiel #16
0
    def patch(self, rom, option, *, multiworld=None):
        assert multiworld is None

        if self.give_bowwow:
            option = BOWWOW
            rom.texts[0xC8] = formatText(b"Got BowWow!")

        if option != SHIELD:
            rom.patch(
                5, 0x0CDA, ASM("ld a, $22"), ASM("ld a, $00")
            )  # do not change links sprite into the one with a shield

        super().patch(rom, option)
Beispiel #17
0
def fixHeartPiece(rom):
    # Patch all locations where the piece of heart is rendered.
    rom.patch(0x03,
              0x1b52,
              ASM("ld de, $5A4D\ncall $3BC0"),
              ASM("ld a, $04\nrst 8"),
              fill_nop=True)  # state 0

    # Write custom code in the first state handler, this overwrites all state handlers
    # Till state 5.
    rom.patch(0x03,
              0x1A74,
              0x1A98,
              ASM("""
        ; Render sprite
        ld   a, $05
        rst  8
    
        ; Handle item effect
        ld   a, $06 ; giveItemMultiworld
        rst  8
        
        ;Show message
        ld   a, $0A ; showMessageMultiworld
        rst  8
        
        ; Switch to state 5
        ld   hl, $C290; stateTable
        add  hl, bc
        ld   [hl], $05
        ret
    """),
              fill_nop=True)
    # Insert a state 5 handler
    rom.patch(0x03,
              0x1A98,
              0x1B17,
              ASM("""
        ; Render sprite
        ld   a, $05
        rst  8

        ld   a, [$C19F] ; dialog state
        and  a
        ret  nz

        call $512A ; mark room as done
        call $3F8D ; unload entity
        ret
    """),
              fill_nop=True)
Beispiel #18
0
def noSwordMusic(rom):
    # Skip no-sword music override
    # Instead of loading the sword level, we put the value 1 in the A register, indicating we have a sword.
    rom.patch(2, 0x0151, ASM("ld a, [$DB4E]"), ASM("ld a, $01"), fill_nop=True)
    rom.patch(2, 0x3AEF, ASM("ld a, [$DB4E]"), ASM("ld a, $01"), fill_nop=True)
    rom.patch(3, 0x0996, ASM("ld a, [$DB4E]"), ASM("ld a, $01"), fill_nop=True)
    rom.patch(3, 0x0B35, ASM("ld a, [$DB44]"), ASM("ld a, $01"), fill_nop=True)
Beispiel #19
0
def allowRaftGameWithoutFlippers(rom):
    # Allow jumping down the waterfall in the raft game without the flippers.
    rom.patch(0x02,
              0x2E8F,
              ASM("ld a, [$DB0C]"),
              ASM("ld a, $01"),
              fill_nop=True)
    # Change the room that goes back up to the raft game from the bottom, so we no longer need flippers
    re = RoomEditor(rom, 0x1F7)
    re.changeObject(3, 2, 0x1B)
    re.changeObject(2, 3, 0x1B)
    re.changeObject(3, 4, 0x1B)
    re.changeObject(4, 5, 0x1B)
    re.changeObject(6, 6, 0x1B)
    re.store(rom)
Beispiel #20
0
def patchOverworldTilesets(rom):
    rom.patch(0x00,
              0x0D5B,
              0x0D79,
              ASM("""
        ; Instead of loading tileset info from a small 8x8 table, load it from a 16x16 table to give
        ; full control.
        ; A=MapRoom
        ld   hl, $2100
        ld   [hl], $3F
        ld   d, $00
        ld   e, a
        ld   hl, $6F00
        add  hl, de
        ldh  a, [$94] ; We need to load the currently loaded tileset in E to compare it
        ld   e, a
        ld   a, [hl]
        ld   hl, $2100
        ld   [hl], $20
    """),
              fill_nop=True)
    # Remove the camera shop exception
    rom.patch(0x00, 0x0D80, 0x0D8B, "", fill_nop=True)

    for x in range(16):
        for y in range(16):
            rom.banks[0x3F][0x2F00 + x +
                            y * 16] = rom.banks[0x20][0x2E73 + (x // 2) +
                                                      (y // 2) * 8]
    rom.banks[0x3F][0x2F07] = rom.banks[0x3F][
        0x2F08]  # Fix the room next to the egg
    # Fix the rooms around the camera shop
    rom.banks[0x3F][0x2F26] = 0x0F
    rom.banks[0x3F][0x2F27] = 0x0F
    rom.banks[0x3F][0x2F36] = 0x0F
Beispiel #21
0
def upgradeTunicFairy(rom):
    rom.texts[0x268] = formatText(b"Welcome, #####. I admire you for coming this far.")
    rom.texts[0x0CC] = formatText(b"Got the Red Tunic! You can change Tunics at the phone booths.")
    rom.texts[0x0CD] = formatText(b"Got the Blue Tunic! You can change Tunics at the phone booths.")

    rom.patch(0x36, 0x111C, 0x1133, ASM("""
        call $3B12
        ld  a, [$DDE1]
        and $10
        jr  z, giveItems
        ld   [hl], $09
        ret

giveItems:
        ld  a, [$DDE1]
        or  $10
        ld  [$DDE1], a
    """), fill_nop=True)
    rom.patch(0x36, 0x1139, 0x1144, ASM("""
        ld  a, [$51BF]
        ldh [$F1], a
        ld  a, $02
        rst 8
        ld  a, $03
        rst 8
    """), fill_nop=True)

    rom.patch(0x36, 0x1162, 0x1192, ASM("""
        ld  a, [$51C0]
        ldh [$F1], a
        ld  a, $02
        rst 8
        ld  a, $03
        rst 8
        call $3B12
        ret
    """), fill_nop=True)

    rom.patch(0x36, 0x119D, 0x11A2, "", fill_nop=True)
    rom.patch(0x36, 0x11B5, 0x11BE, ASM("""
        ; Skip to the end ignoring all the tunic giving animation.
        call $3B12
        ld   [hl], $09
    """), fill_nop=True)

    rom.banks[0x36][0x11BF] = 0x87
    rom.banks[0x36][0x11C0] = 0x88
Beispiel #22
0
    def patch(self, rom, option, *, multiworld=None):
        assert multiworld is None

        if self.give_bowwow:
            option = BOWWOW
            rom.texts[0xC8] = formatText("Got BowWow!")

        if option != SHIELD:
            rom.patch(
                5, 0x0CDA, ASM("ld a, $22"), ASM("ld a, $00")
            )  # do not change links sprite into the one with a shield

        if option in (MAGIC_POWDER, BOMB):
            re = RoomEditor(rom, 0x0A2)
            re.entities.append((1, 3, 0x41))
            re.store(rom)

        super().patch(rom, option)
Beispiel #23
0
 def save(self, filename, *, name=None):
     self.texts.store(self)
     self.entities.store(self)
     self.rooms_overworld_top.store(self)
     self.rooms_overworld_bottom.store(self)
     self.rooms_indoor_a.store(self)
     self.rooms_indoor_b.store(self)
     self.rooms_color_dungeon.store(self)
     leftover_storage = self.room_sprite_data_overworld.store(self)
     self.room_sprite_data_indoor.addStorage(leftover_storage)
     self.patch(
         0x00, 0x0DFA, ASM("ld hl, $763B"),
         ASM("ld hl, $%04x" % (leftover_storage[0]["start"] | 0x4000)))
     self.room_sprite_data_indoor.adjustDataStart(
         leftover_storage[0]["start"])
     self.room_sprite_data_indoor.store(self)
     self.background_tiles.store(self)
     self.background_attributes.store(self)
     super().save(filename, name=name)
Beispiel #24
0
    def patch(self, rom, option, *, multiworld=None):
        super().patch(rom, option, multiworld=multiworld)

        re = RoomEditor(rom, self.room)

        # Make the bird key accessible without the rooster
        re.removeObject(1, 6)
        re.removeObject(2, 6)
        re.removeObject(3, 5)
        re.removeObject(3, 6)
        re.moveObject(1, 5, 2, 6)
        re.moveObject(2, 5, 3, 6)
        re.addEntity(3, 5, 0x9D)
        re.store(rom)
        rom.patch(0x19, 0x0010, "F0007806F008782600007A0600087A26",
                  "F000640FF008642F0000660F0008662F")
        rom.patch(0x19, 0x004F, ASM("cp $01"), ASM("cp $0A"))

        # Do not give the rooster
        rom.patch(0x19, 0x0E9D, ASM("ld [$DB7B], a"), "", fill_nop=True)
Beispiel #25
0
def updateSpriteData(rom):
    # Remove all the special sprite change exceptions.
    rom.patch(0x00, 0x0DAD, 0x0DDB, ASM("jp $0DDB"), fill_nop=True)

    # For each room update the sprite load data based on which entities are in there.
    for room_nr in range(0x316):
        if room_nr == 0x2FF:
            continue
        values = [None, None, None, None]
        if room_nr == 0x00E:  # D7 entrance opening
            values[2] = 0xD6
            values[3] = 0xD7
        if 0x211 <= room_nr <= 0x21E:  # D7 throwing ball thing.
            values[0] = 0x66
        r = RoomEditor(rom, room_nr)
        for obj in r.objects:
            if obj.type_id == 0xC5 and room_nr < 0x100:  # Pushable Gravestone
                values[3] = 0x82
        for x, y, entity in r.entities:
            sprite_data = entityData.SPRITE_DATA[entity]
            if callable(sprite_data):
                sprite_data = sprite_data(r)
            if sprite_data is None:
                continue
            for m in range(0, len(sprite_data), 2):
                idx, value = sprite_data[m:m + 2]
                if values[idx] is None:
                    values[idx] = value
                elif isinstance(values[idx], set) and isinstance(value, set):
                    values[idx] = values[idx].intersection(value)
                    assert len(values[idx]) > 0
                elif isinstance(values[idx], set) and value in values[idx]:
                    values[idx] = value
                elif isinstance(value, set) and values[idx] in value:
                    pass
                elif values[idx] == value:
                    pass
                else:
                    assert False, "%03x: %02x (%s %s)" % (room_nr, entity,
                                                          values[idx], value)

        data = bytearray()
        for v in values:
            if isinstance(v, set):
                v = next(iter(v))
            elif v is None:
                v = 0xff
            data.append(v)

        if room_nr < 0x100:
            rom.room_sprite_data_overworld[room_nr] = data
        else:
            rom.room_sprite_data_indoor[room_nr - 0x100] = data
Beispiel #26
0
def removeBirdKeyHoleDrop(rom):
    # Prevent the cave with the bird key from dropping you in the water
    # (if you do not have flippers this would softlock you)
    rom.patch(
        0x02, 0x1176,
        ASM("""
        ldh a, [$F7]
        cp $0A
        jr nz, $30
    """),
        ASM("""
        nop
        nop
        nop
        nop
        jr $30
    """))
    # Remove the hole that drops you all the way from dungeon7 entrance to the water in the cave
    re = RoomEditor(rom, 0x01E)
    re.removeObject(5, 4)
    re.store(rom)
Beispiel #27
0
def onlyDropBombsWhenHaveBombs(rom):
    rom.patch(0x03, 0x1FC5, ASM("call $608C"), ASM("call $50B2"))
    # We use some of the unused chest code space here to remove the bomb if you do not have bombs in your inventory.
    rom.patch(0x03,
              0x10B2,
              0x112A,
              ASM("""
        ld   e, INV_SIZE
        ld   hl, $DB00
        ld   a, $02
loop:
        cp   [hl]
        jr   z, resume
        dec  e
        inc  hl
        jr   nz, loop
        jp   $3F8D ; unload entity
resume:
        jp   $608C
    """),
              fill_nop=True)
Beispiel #28
0
def updateFinishingMinigame(rom):
    rom.patch(0x04,
              0x26BE,
              0x26DF,
              ASM("""
        ld   a, $0B ; GiveItemAndMessageForRoom
        rst  8
        
        ; Mark selection as stopping minigame, as we are not asking a question.
        ld   a, $01
        ld   [$C177], a
    """),
              fill_nop=True)
Beispiel #29
0
    def patch(self, rom, option, *, multiworld=None):
        if option != SWORD or multiworld is not None:
            # Set the heart piece data
            super().patch(rom, option, multiworld=multiworld)

            # Patch the room to contain a heart piece instead of the sword on the beach
            re = RoomEditor(rom, 0x0F2)
            re.removeEntities(0x31)  # remove sword
            re.addEntity(5, 5, 0x35)  # add heart piece
            re.store(rom)

            # Prevent shield drops from the like-like from turning into swords.
            rom.patch(0x03,
                      0x1B9C,
                      ASM("ld a, [$DB4E]"),
                      ASM("ld a, $01"),
                      fill_nop=True)
            rom.patch(0x03,
                      0x244D,
                      ASM("ld a, [$DB4E]"),
                      ASM("ld a, $01"),
                      fill_nop=True)
Beispiel #30
0
def updateFinishingMinigame(rom):
    rom.patch(0x04, 0x26BE, 0x26DF, ASM("""
        ld   a, $0E ; GiveItemAndMessageForRoomMultiworld
        rst  8
        
        ; Mark selection as stopping minigame, as we are not asking a question.
        ld   a, $01
        ld   [$C177], a
        
        ; Check if we got rupees from the item skip getting rupees from the fish.
        ld   a, [$DB90]
        ld   hl, $DB8F
        or   [hl]
        jp   nz, $66FE
    """), fill_nop=True)