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 addBetaRoom(rom): re = RoomEditor(rom, 0x1FC) re.objects[-1].target_y -= 0x10 re.store(rom) re = RoomEditor(rom, 0x038) re.changeObject(5, 1, 0xE1) re.removeObject(0, 0) re.removeObject(0, 1) re.removeObject(0, 2) re.removeObject(6, 1) re.objects.append(ObjectVertical(0, 0, 0x38, 3)) re.objects.append(ObjectWarp(1, 0x1F, 0x1FC, 0x50, 0x7C)) re.store(rom) rom.room_sprite_data_indoor[0x0FC] = rom.room_sprite_data_indoor[0x1A1]
def setRaftGoal(rom): rom.texts[0x1A3] = formatText(b"Just sail away.") # Remove the egg and egg event handler. re = RoomEditor(rom, 0x006) for x in range(4, 7): for y in range(0, 4): re.removeObject(x, y) re.objects.append(ObjectHorizontal(4, 1, 0x4d, 3)) re.objects.append(ObjectHorizontal(4, 2, 0x03, 3)) re.objects.append(ObjectHorizontal(4, 3, 0x03, 3)) re.entities = [] re.updateOverlay() re.store(rom) re = RoomEditor(rom, 0x08D) re.objects[6].count = 4 re.objects[7].x += 2 re.objects[7].type_id = 0x2B re.objects[8].x += 2 re.objects[8].count = 2 re.objects[9].x += 1 re.objects[11] = ObjectVertical(7, 5, 0x37, 2) re.objects[12].x -= 1 re.objects[13].x -= 1 re.objects[14].x -= 1 re.objects[14].type_id = 0x34 re.objects[17].x += 3 re.objects[17].count -= 3 re.updateOverlay() re.overlay[7 + 60] = 0x33 re.store(rom) re = RoomEditor(rom, 0x0E9) re.objects[30].count = 1 re.objects[30].x += 2 re.overlay[7 + 70] = 0x0E re.overlay[8 + 70] = 0x0E re.store(rom) re = RoomEditor(rom, 0x0F9) re.objects = [ ObjectHorizontal(4, 0, 0x0E, 6), ObjectVertical(9, 0, 0xCA, 8), ObjectVertical(8, 0, 0x0E, 8), Object(3, 0, 0x38), Object(3, 1, 0x32), ObjectHorizontal(4, 1, 0x2C, 3), Object(7, 1, 0x2D), ObjectVertical(7, 2, 0x38, 5), Object(7, 7, 0x34), ObjectHorizontal(0, 7, 0x2F, 7), ObjectVertical(2, 3, 0xE8, 4), ObjectVertical(3, 2, 0xE8, 5), ObjectVertical(4, 2, 0xE8, 2), ObjectVertical(4, 4, 0x5C, 3), ObjectVertical(5, 2, 0x5C, 5), ObjectVertical(6, 2, 0x5C, 5), Object(6, 4, 0xC6), ObjectWarp(1, 0x1F, 0xF6, 136, 112) ] re.updateOverlay(True) re.entities.append((0, 0, 0x41)) re.store(rom) re = RoomEditor(rom, 0x1F6) re.objects[-1].target_x -= 16 re.store(rom) # Fix the raft graphics (this overrides some unused graphic tiles) rom.banks[0x31][0x21C0:0x2200] = rom.banks[0x2E][0x07C0:0x0800] # Patch the owl entity to run our custom end handling. rom.patch(0x06, 0x27F5, 0x2A77, ASM(""" ld a, [$DB95] cp $0B ret nz ; If map is not fully loaded, return ld a, [$C124] and a ret nz ; Check if we are moving off the bottom of the map ldh a, [$99] cp $7D ret c ; Move link back so it does not move off the map ld a, $7D ldh [$99], a xor a ld e, a ld d, a raftSearchLoop: ld hl, $C280 add hl, de ld a, [hl] and a jr z, .skipEntity ld hl, $C3A0 add hl, de ld a, [hl] cp $6A jr nz, .skipEntity ; Raft found, check if near the bottom of the screen. ld hl, $C210 add hl, de ld a, [hl] cp $70 jr nc, raftOffWorld .skipEntity: inc e ld a, e cp $10 jr nz, raftSearchLoop ret raftOffWorld: ; Switch to the end credits ld a, $01 ld [$DB95], a ld a, $00 ld [$DB96], a ret """), fill_nop=True) # We need to run quickly trough part of the credits, or else it bugs out # Skip the whole windfish part. rom.patch(0x17, 0x0D39, None, ASM("ld a, $18\nld [$D00E], a\nret")) # And skip the zoomed out laying on the log rom.patch(0x17, 0x20ED, None, ASM("ld a, $00")) # Finally skip some waking up on the log. rom.patch(0x17, 0x23BC, None, ASM("jp $4CD9")) rom.patch(0x17, 0x2476, None, ASM("jp $4CD9"))
def getCleanBossRoom(rom, dungeon_nr): re = RoomEditor(rom, BOSS_ROOMS[dungeon_nr][0]) new_objects = [] for obj in re.objects: if isinstance(obj, ObjectWarp): continue if obj.type_id == 0xBE: # Remove staircases continue if obj.type_id == 0x06: # Remove lava continue if obj.type_id == 0x1c: # Change D1 pits into normal pits obj.type_id = 0x01 if obj.type_id == 0x1e: # Change D1 pits into normal pits obj.type_id = 0xaf if obj.type_id == 0x1f: # Change D1 pits into normal pits obj.type_id = 0xb0 if obj.type_id == 0xF5: # Change open doors into closing doors. obj.type_id = 0xF1 new_objects.append(obj) # Make D4 room a valid fighting room by removing most content. if dungeon_nr == 3: new_objects = new_objects[:2] + [ Object(1, 1, 0xAC), Object(8, 1, 0xAC), Object(1, 6, 0xAC), Object(8, 6, 0xAC) ] # D7 has an empty room we use for most bosses, but it needs some adjustments. if dungeon_nr == 6: # Move around the unused and instrument room. rom.banks[0x14][0x03a0 + 6 + 1 * 8] = 0x00 rom.banks[0x14][0x03a0 + 7 + 2 * 8] = 0x2C rom.banks[0x14][0x03a0 + 7 + 3 * 8] = 0x23 rom.banks[0x14][0x03a0 + 6 + 5 * 8] = 0x00 rom.banks[0x14][0x0520 + 7 + 2 * 8] = 0x2C rom.banks[0x14][0x0520 + 7 + 3 * 8] = 0x23 rom.banks[0x14][0x0520 + 6 + 5 * 8] = 0x00 re.floor_object &= 0x0F new_objects += [ Object(4, 0, 0xF0), Object(1, 6, 0xBE), ObjectWarp(1, dungeon_nr, 0x22E, 24, 16) ] # Set the stairs towards the eagle tower top to our new room. r = RoomEditor(rom, 0x22E) r.objects[-1] = ObjectWarp(1, dungeon_nr, re.room, 24, 112) r.store(rom) # Remove the normal door to the instrument room r = RoomEditor(rom, 0x22e) r.removeObject(4, 0) r.store(rom) rom.banks[0x14][0x22e - 0x100] = 0x00 r = RoomEditor(rom, 0x22c) r.changeObject(0, 7, 0x03) r.changeObject(2, 7, 0x03) r.store(rom) re.objects = new_objects re.entities = [] return re
def changeMiniBosses(rom, mapping): # Fix avalaunch not working when entering a room from the left or right rom.patch(0x03, 0x0BE0, ASM(""" ld [hl], $50 ld hl, $C2D0 add hl, bc ld [hl], $00 jp $4B56 """), ASM(""" ld a, [hl] sub $08 ld [hl], a ld hl, $C2D0 add hl, bc ld [hl], b ; b is always zero here ret """), fill_nop=True) # Remove the powder fairy from giant buzz blob rom.patch(0x36, 0x14F7, ASM("jr nz, $05"), ASM("jr $05")) for target, name in mapping.items(): re = RoomEditor(rom, MINIBOSS_ROOMS[target]) re.entities = [e for e in re.entities if e[2] == 0x61] # Only keep warp, if available re.entities += MINIBOSS_ENTITIES[name] if re.room == 0x228 and name != "GRIM_CREEPER": for x in range(3, 7): for y in range(0, 3): re.removeObject(x, y) if name == "CUE_BALL": re.objects += [ Object(3, 3, 0x2c), ObjectHorizontal(4, 3, 0x22, 2), Object(6, 3, 0x2b), Object(3, 4, 0x2a), ObjectHorizontal(4, 4, 0x21, 2), Object(6, 4, 0x29), ] if name == "BLAINO": # BLAINO needs a warp object to hit you to the entrance of the dungeon. if len(re.getWarps()) < 1: # Default to start house. target = (0x10, 0x2A3, 0x50, 0x7c, 0x2A3) if 0x100 <= re.room < 0x11D: #D1 target = (0, 0x117, 80, 80) elif 0x11D <= re.room < 0x140: #D2 target = (1, 0x136, 80, 80) elif 0x140 <= re.room < 0x15D: #D3 target = (2, 0x152, 80, 80) elif 0x15D <= re.room < 0x180: #D4 target = (2, 0x174, 80, 80) elif 0x180 <= re.room < 0x1AC: #D5 target = (2, 0x1A1, 80, 80) elif 0x1B0 <= re.room < 0x1DE: #D6 target = (2, 0x1D4, 80, 80) elif 0x200 <= re.room < 0x22D: #D7 target = (6, 0x20E, 80, 80) elif 0x22D <= re.room < 0x26C: #D8 target = (7, 0x25D, 80, 80) elif re.room >= 0x300: #D0 target = (0xFF, 0x312, 80, 80) elif re.room == 0x2E1: #Moblin cave target = (0x15, 0x2F0, 0x50, 0x7C) re.objects.append(ObjectWarp(1, *target)) if name == "DODONGO": # Remove breaking floor tiles from the room. re.objects = [obj for obj in re.objects if obj.type_id != 0xDF] if name == "ROLLING_BONES" and target == 2: # Make rolling bones pass trough walls so it does not get stuck here. rom.patch(0x03, 0x02F1 + 0x81, "84", "95") re.store(rom)
def changeBosses(rom, mapping): # Fix the color dungeon not properly warping to room 0 with the boss. rom.patch( 0x14, 0x04E0, "0000000000000000" + "0000000000000000" + "0000000000000000" + "0000010000020300" + "0004050607080900" + "00000A0B0C0D0000" + "00000E0F10110000" + "0000121314150000", "FFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFF" + "FF0001FFFF0203FF" + "FF040506070809FF" + "FFFF0A0B0C0DFFFF" + "FFFF0E0F1011FFFF" + "FFFF12131415FFFF") # Fix the genie death not really liking pits/water. rom.patch(0x04, 0x0521, ASM("ld [hl], $81"), ASM("ld [hl], $91")) for dungeon_nr in range(9): target = mapping[dungeon_nr] if target == dungeon_nr: continue if target == 3: # D4 fish boss # If dungeon_nr == 6: use normal eagle door towards fish. if dungeon_nr == 6: # Add the staircase to the boss, and fix the warp back. re = RoomEditor(rom, 0x22E) for obj in re.objects: if isinstance(obj, ObjectWarp): obj.type_id = 2 obj.map_nr = 3 obj.room = 0x1EF obj.target_x = 24 obj.target_y = 16 re.store(rom) re = RoomEditor(rom, 0x1EF) re.objects[-1] = ObjectWarp( 1, dungeon_nr if dungeon_nr < 8 else 0xff, 0x22E, 24, 16) re.store(rom) else: # Set the proper room event flags rom.banks[0x14][BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x2A # Add the staircase to the boss, and fix the warp back. re = getCleanBossRoom(rom, dungeon_nr) re.objects += [ Object(4, 4, 0xBE), ObjectWarp(2, 3, 0x1EF, 24, 16) ] re.store(rom) re = RoomEditor(rom, 0x1EF) re.objects[-1] = ObjectWarp( 1, dungeon_nr if dungeon_nr < 8 else 0xff, BOSS_ROOMS[dungeon_nr][0], 72, 80) re.store(rom) # Patch the fish heart container to open up the right room. rom.patch( 0x03, 0x1A0F, ASM("ld hl, $D966"), ASM("ld hl, $%04x" % (getBossRoomStatusFlagLocation(dungeon_nr)))) # Patch the proper item towards the D4 boss rom.banks[0x3E][0x3800 + 0x01ff] = rom.banks[0x3E][ 0x3800 + BOSS_ROOMS[dungeon_nr][0]] rom.banks[0x3E][0x3300 + 0x01ff] = rom.banks[0x3E][ 0x3300 + BOSS_ROOMS[dungeon_nr][0]] elif target == 6: # Evil eagle rom.banks[0x14][BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x2A # Patch the eagle heart container to open up the right room. rom.patch( 0x03, 0x1A04, ASM("ld hl, $DA2E"), ASM("ld hl, $%04x" % (getBossRoomStatusFlagLocation(dungeon_nr)))) rom.patch( 0x02, 0x1FC8, ASM("cp $06"), ASM("cp $%02x" % (dungeon_nr if dungeon_nr < 8 else 0xff))) # Add the staircase to the boss, and fix the warp back. re = getCleanBossRoom(rom, dungeon_nr) re.objects += [Object(4, 4, 0xBE), ObjectWarp(2, 6, 0x2F8, 72, 80)] re.store(rom) re = RoomEditor(rom, 0x2F8) re.objects[-1] = ObjectWarp(1, dungeon_nr if dungeon_nr < 8 else 0xff, BOSS_ROOMS[dungeon_nr][0], 72, 80) re.store(rom) # Patch the proper item towards the D7 boss rom.banks[0x3E][0x3800 + 0x02E8] = rom.banks[0x3E][ 0x3800 + BOSS_ROOMS[dungeon_nr][0]] rom.banks[0x3E][0x3300 + 0x02E8] = rom.banks[0x3E][ 0x3300 + BOSS_ROOMS[dungeon_nr][0]] else: rom.banks[0x14][BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x21 re = getCleanBossRoom(rom, dungeon_nr) re.entities = [BOSS_ENTITIES[target]] if target == 4: # For slime eel, we need to setup the right wall tiles. rom.banks[0x20][0x2EB3 + BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x06 if target == 5: # Patch facade so he doesn't use the spinning tiles, which is a problem for the sprites. rom.patch(0x04, 0x121D, ASM("cp $14"), ASM("cp $00")) rom.patch(0x04, 0x1226, ASM("cp $04"), ASM("cp $00")) rom.patch(0x04, 0x127F, ASM("cp $14"), ASM("cp $00")) if target == 7: pass # For hot head, add some lava (causes graphical glitches) # re.animation_id = 0x06 # re.objects += [ # ObjectHorizontal(3, 2, 0x06, 4), # ObjectHorizontal(2, 3, 0x06, 6), # ObjectHorizontal(2, 4, 0x06, 6), # ObjectHorizontal(3, 5, 0x06, 4), # ] re.store(rom)
def changeMiniBosses(rom, mapping): # Fix avalaunch not working when entering a room from the left or right rom.patch(0x03, 0x0BE0, ASM(""" ld [hl], $50 ld hl, $C2D0 add hl, bc ld [hl], $00 jp $4B56 """), ASM(""" ld a, [hl] sub $08 ld [hl], a ld hl, $C2D0 add hl, bc ld [hl], b ; b is always zero here ret """), fill_nop=True) for target, name in mapping.items(): re = RoomEditor(rom, MINIBOSS_ROOMS[target]) re.entities = [e for e in re.entities if e[2] == 0x61] # Only keep warp, if available re.entities += MINIBOSS_ENTITIES[name] if re.room == 0x228 and name != "GRIM_CREEPER": for x in range(3, 7): for y in range(0, 3): re.removeObject(x, y) if name == "CUE_BALL": re.objects += [ Object(3, 3, 0x2c), ObjectHorizontal(4, 3, 0x22, 2), Object(6, 3, 0x2b), Object(3, 4, 0x2a), ObjectHorizontal(4, 4, 0x21, 2), Object(6, 4, 0x29), ] if name == "BLAINO": # BLAINO needs a warp object to hit you to the entrance of the dungeon. if len(re.getWarps()) < 1: # Default to start house. target = (0x10, 0x2A3, 0x50, 0x7c, 0x2A3) if 0x100 <= re.room < 0x11D: #D1 target = (0, 0x117, 80, 80) elif 0x11D <= re.room < 0x140: #D2 target = (1, 0x136, 80, 80) elif 0x140 <= re.room < 0x15D: #D3 target = (2, 0x152, 80, 80) elif 0x15D <= re.room < 0x180: #D4 target = (2, 0x174, 80, 80) elif 0x180 <= re.room < 0x1AC: #D5 target = (2, 0x1A1, 80, 80) elif 0x1B0 <= re.room < 0x1DE: #D6 target = (2, 0x1D4, 80, 80) elif 0x200 <= re.room < 0x22D: #D7 target = (6, 0x20E, 80, 80) elif 0x22D <= re.room < 0x26C: #D8 target = (7, 0x25D, 80, 80) elif re.room >= 0x300: #D0 target = (0xFF, 0x312, 80, 80) elif re.room == 0x2E1: #Moblin cave target = (0x15, 0x2F0, 0x50, 0x7C) re.objects.append(ObjectWarp(1, *target)) re.store(rom) sprite_data = entityData.SPRITE_DATA[MINIBOSS_ENTITIES[name][0][2]] for n in range(0, len(sprite_data), 2): rom.room_sprite_data_indoor[re.room - 0x100][sprite_data[n]] = sprite_data[n + 1]
def changeBosses(rom, mapping): for dungeon_nr in range(9): target = mapping[dungeon_nr] if target == dungeon_nr: continue if target == 3: # D4 fish boss # If dungeon_nr == 6: use normal eagle door towards fish. if dungeon_nr == 6: # Add the staircase to the boss, and fix the warp back. re = RoomEditor(rom, 0x22E) for obj in re.objects: if isinstance(obj, ObjectWarp): obj.type_id = 2 obj.map_nr = 3 obj.room = 0x1EF obj.target_x = 24 obj.target_y = 16 re.store(rom) re = RoomEditor(rom, 0x1EF) re.objects[-1] = ObjectWarp( 1, dungeon_nr if dungeon_nr < 8 else 0xff, 0x22E, 72, 80) re.store(rom) else: # Set the proper room event flags rom.banks[0x14][BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x2A # Patch the fish heart container to open up the right room. rom.patch( 0x03, 0x1A0F, ASM("ld hl, $D966"), ASM("ld hl, $%04x" % (0xD800 + BOSS_ROOMS[dungeon_nr][0]))) # Add the staircase to the boss, and fix the warp back. re = getCleanBossRoom(rom, dungeon_nr) re.objects += [ Object(4, 4, 0xBE), ObjectWarp(2, 3, 0x1EF, 24, 16) ] re.store(rom) re = RoomEditor(rom, 0x1EF) re.objects[-1] = ObjectWarp( 1, dungeon_nr if dungeon_nr < 8 else 0xff, BOSS_ROOMS[dungeon_nr][0], 72, 80) re.store(rom) # Patch the proper item towards the D4 boss rom.banks[0x3E][0x3800 + 0x01ff] = rom.banks[0x3E][ 0x3800 + BOSS_ROOMS[dungeon_nr][0]] rom.banks[0x3E][0x3300 + 0x01ff] = rom.banks[0x3E][ 0x3300 + BOSS_ROOMS[dungeon_nr][0]] elif target == 6: # Evil eagle rom.banks[0x14][BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x2A # Patch the eagle heart container to open up the right room. rom.patch( 0x03, 0x1A04, ASM("ld hl, $DA2E"), ASM("ld hl, $%04x" % (0xD800 + BOSS_ROOMS[dungeon_nr][0]))) rom.patch( 0x02, 0x1FC8, ASM("cp $06"), ASM("cp $%02x" % (dungeon_nr if dungeon_nr < 8 else 0xff))) # Add the staircase to the boss, and fix the warp back. re = getCleanBossRoom(rom, dungeon_nr) re.objects += [Object(4, 4, 0xBE), ObjectWarp(2, 6, 0x2F8, 72, 80)] re.store(rom) re = RoomEditor(rom, 0x2F8) re.objects[-1] = ObjectWarp(1, dungeon_nr if dungeon_nr < 8 else 0xff, BOSS_ROOMS[dungeon_nr][0], 72, 80) re.store(rom) # Patch the proper item towards the D7 boss rom.banks[0x3E][0x3800 + 0x0223] = rom.banks[0x3E][ 0x3800 + BOSS_ROOMS[dungeon_nr][0]] rom.banks[0x3E][0x3300 + 0x0223] = rom.banks[0x3E][ 0x3300 + BOSS_ROOMS[dungeon_nr][0]] else: rom.banks[0x14][BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x21 rom.room_sprite_data_indoor[BOSS_ROOMS[dungeon_nr][0] - 0x100] = SPRITE_DATA[target] for room in BOSS_ROOMS[dungeon_nr][1:]: rom.room_sprite_data_indoor[room - 0x100] = b'\xff\xff\xff\xff' re = getCleanBossRoom(rom, dungeon_nr) re.entities = [BOSS_ENTITIES[target]] if target == 4: # For slime eel, we need to setup the right wall tiles. rom.banks[0x20][0x2EB3 + BOSS_ROOMS[dungeon_nr][0] - 0x100] = 0x06 if target == 5: # Patch facade so he doesn't use the spinning tiles, which is a problem for the sprites. rom.patch(0x04, 0x121D, ASM("cp $14"), ASM("cp $00")) rom.patch(0x04, 0x1226, ASM("cp $04"), ASM("cp $00")) rom.patch(0x04, 0x127F, ASM("cp $14"), ASM("cp $00")) if target == 7: pass # For hot head, add some lava (causes graphical glitches) # re.animation_id = 0x06 # re.objects += [ # ObjectHorizontal(3, 2, 0x06, 4), # ObjectHorizontal(2, 3, 0x06, 6), # ObjectHorizontal(2, 4, 0x06, 6), # ObjectHorizontal(3, 5, 0x06, 4), # ] re.store(rom)