diff --git a/worlds/smw/Levels.py b/worlds/smw/Levels.py index e61a312b8d9..91f065b161b 100644 --- a/worlds/smw/Levels.py +++ b/worlds/smw/Levels.py @@ -1,6 +1,48 @@ from .Names import LocationName + +class BowserRoom(): + name: str + exitAddress: int + roomID: int + + def __init__(self, name: str, exitAddress: int, roomID: int): + self.name = name + self.exitAddress = exitAddress + self.roomID = roomID + +full_bowser_rooms = [ + BowserRoom("Hallway 1 - Door 1", 0x3A680, 0x0D), + BowserRoom("Hallway 1 - Door 2", 0x3A684, 0x0D), + BowserRoom("Hallway 1 - Door 3", 0x3A688, 0x0D), + BowserRoom("Hallway 1 - Door 4", 0x3A68C, 0x0D), + BowserRoom("Hallway 2 - Door 1", 0x3A8CB, 0xD0), + BowserRoom("Hallway 2 - Door 2", 0x3A8CF, 0xD0), + BowserRoom("Hallway 2 - Door 3", 0x3A8D3, 0xD0), + BowserRoom("Hallway 2 - Door 4", 0x3A8D7, 0xD0), + + BowserRoom("Room 1", 0x3A705, 0xD4), + BowserRoom("Room 2", 0x3A763, 0xD3), + BowserRoom("Room 3", 0x3A800, 0xD2), + BowserRoom("Room 4", 0x3A83D, 0xD1), + BowserRoom("Room 5", 0x3A932, 0xCF), + BowserRoom("Room 6", 0x3A9E1, 0xCE), + BowserRoom("Room 7", 0x3AA75, 0xCD), + BowserRoom("Room 8", 0x3AAC7, 0xCC), +] + +standard_bowser_rooms = [ + BowserRoom("Room 1", 0x3A705, 0xD4), + BowserRoom("Room 2", 0x3A763, 0xD3), + BowserRoom("Room 3", 0x3A800, 0xD2), + BowserRoom("Room 4", 0x3A83D, 0xD1), + BowserRoom("Room 5", 0x3A932, 0xCF), + BowserRoom("Room 6", 0x3A9E1, 0xCE), + BowserRoom("Room 7", 0x3AA75, 0xCD), + BowserRoom("Room 8", 0x3AAC7, 0xCC), +] + class SMWPath(): thisEndDirection: int otherLevelID: int @@ -30,7 +72,7 @@ def __init__(self, levelName: str, levelIDAddress: int, eventIDValue: int, exit1 #self.progressByte = progressByte # Inferred from EventIDValue: (ID / 8) + $1F02 #self.progressBit = progressBit # Inferred from EventIDValue: 1 << (7 - (ID % 8)) self.exit1Path = exit1Path - self.exit2Path = exit2Path + self.exit2Path = exit2Path level_info_dict = { diff --git a/worlds/smw/Options.py b/worlds/smw/Options.py index 65ecbbd7712..65b82779606 100644 --- a/worlds/smw/Options.py +++ b/worlds/smw/Options.py @@ -58,7 +58,7 @@ class BowserCastleDoors(Choice): Vanilla: Front and Back Doors behave as vanilla Fast: Both doors behave as the Back Door Slow: Both doors behave as the Front Door - "Front Door" requires beating all 8 Rooms + "Front Door" rooms depend on the `bowser_castle_rooms` option "Back Door" only requires going through the dark hallway to Bowser """ display_name = "Bowser Castle Doors" @@ -68,6 +68,24 @@ class BowserCastleDoors(Choice): default = 0 +class BowserCastleRooms(Choice): + """ + How the rooms of Bowser's Castle Front Door behave + Vanilla: You can choose which rooms to enter, as in vanilla + Random Two Room: Two random rooms are chosen + Random Five Room: Five random rooms are chosen + Gauntlet: All eight rooms must be cleared + Labyrinth: Which room leads to Bowser? + """ + display_name = "Bowser Castle Rooms" + option_vanilla = 0 + option_random_two_room = 1 + option_random_five_room = 2 + option_gauntlet = 3 + option_labyrinth = 4 + default = 1 + + class LevelShuffle(Toggle): """ Whether levels are shuffled @@ -220,6 +238,7 @@ class StartingLifeCount(Range): "percentage_of_yoshi_eggs": PercentageOfYoshiEggs, "dragon_coin_checks": DragonCoinChecks, "bowser_castle_doors": BowserCastleDoors, + "bowser_castle_rooms": BowserCastleRooms, "level_shuffle": LevelShuffle, "swap_donut_gh_exits": SwapDonutGhostHouseExits, #"display_sent_item_popups": DisplaySentItemPopups, diff --git a/worlds/smw/Rom.py b/worlds/smw/Rom.py index 4641141c389..1e6dd35bed9 100644 --- a/worlds/smw/Rom.py +++ b/worlds/smw/Rom.py @@ -1,7 +1,7 @@ import Utils from worlds.Files import APDeltaPatch from .Aesthetics import generate_shuffled_header_data -from .Levels import level_info_dict +from .Levels import level_info_dict, full_bowser_rooms, standard_bowser_rooms from .Names.TextBox import generate_goal_text, title_text_mapping, generate_text_box USHASH = 'cdd3c8c37322978ca8669b34bc89c804' @@ -728,6 +728,60 @@ def handle_swap_donut_gh_exits(rom): rom.write_bytes(0x26371, bytes([0x32])) +def handle_bowser_rooms(rom, world, player): + if world.bowser_castle_rooms[player] == "random_two_room": + chosen_rooms = world.random.sample(standard_bowser_rooms, 2) + + rom.write_byte(0x3A680, chosen_rooms[0].roomID) + rom.write_byte(0x3A684, chosen_rooms[0].roomID) + rom.write_byte(0x3A688, chosen_rooms[0].roomID) + rom.write_byte(0x3A68C, chosen_rooms[0].roomID) + + for i in range(1, len(chosen_rooms)): + rom.write_byte(chosen_rooms[i-1].exitAddress, chosen_rooms[i].roomID) + + rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD) + + elif world.bowser_castle_rooms[player] == "random_five_room": + chosen_rooms = world.random.sample(standard_bowser_rooms) + + rom.write_byte(0x3A680, chosen_rooms[0].roomID) + rom.write_byte(0x3A684, chosen_rooms[0].roomID) + rom.write_byte(0x3A688, chosen_rooms[0].roomID) + rom.write_byte(0x3A68C, chosen_rooms[0].roomID) + + for i in range(1, len(chosen_rooms)): + rom.write_byte(chosen_rooms[i-1].exitAddress, chosen_rooms[i].roomID) + + rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD) + + elif world.bowser_castle_rooms[player] == "gauntlet": + chosen_rooms = standard_bowser_rooms.copy() + world.random.shuffle(chosen_rooms) + + rom.write_byte(0x3A680, chosen_rooms[0].roomID) + rom.write_byte(0x3A684, chosen_rooms[0].roomID) + rom.write_byte(0x3A688, chosen_rooms[0].roomID) + rom.write_byte(0x3A68C, chosen_rooms[0].roomID) + + for i in range(1, len(chosen_rooms)): + rom.write_byte(chosen_rooms[i-1].exitAddress, chosen_rooms[i].roomID) + + rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD) + elif world.bowser_castle_rooms[player] == "labyrinth": + bowser_rooms_copy = full_bowser_rooms.copy() + + entrance_point = bowser_rooms_copy.pop(0) + + world.random.shuffle(bowser_rooms_copy) + + rom.write_byte(entrance_point.exitAddress, bowser_rooms_copy[0].roomID) + for i in range(0, len(bowser_rooms_copy) - 1): + rom.write_byte(bowser_rooms_copy[i].exitAddress, bowser_rooms_copy[i+1].roomID) + + rom.write_byte(chosen_rooms[len(chosen_rooms)-1].exitAddress, 0xBD) + + def patch_rom(world, rom, player, active_level_dict): local_random = world.slot_seeds[player] @@ -739,18 +793,7 @@ def patch_rom(world, rom, player, active_level_dict): intro_text = generate_text_box("Bowser has stolen all of Mario's abilities. Can you help Mario travel across Dinosaur land to get them back and save the Princess from him?") rom.write_bytes(0x2A5D9, intro_text) - # Force all 8 Bowser's Castle Rooms - rom.write_byte(0x3A680, 0xD4) - rom.write_byte(0x3A684, 0xD4) - rom.write_byte(0x3A688, 0xD4) - rom.write_byte(0x3A68C, 0xD4) - rom.write_byte(0x3A705, 0xD3) - rom.write_byte(0x3A763, 0xD2) - rom.write_byte(0x3A800, 0xD1) - rom.write_byte(0x3A83D, 0xCF) - rom.write_byte(0x3A932, 0xCE) - rom.write_byte(0x3A9E1, 0xCD) - rom.write_byte(0x3AA75, 0xCC) + handle_bowser_rooms(rom, world, player) # Prevent Title Screen Deaths rom.write_byte(0x1C6A, 0x80)