diff --git a/BaseClasses.py b/BaseClasses.py index b41ce608..962de709 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2170,6 +2170,7 @@ class Spoiler(object): 'enemy_shuffle': self.world.enemy_shuffle, 'enemy_health': self.world.enemy_health, 'enemy_damage': self.world.enemy_damage, + 'potshuffle': self.world.potshuffle, 'players': self.world.players, 'teams': self.world.teams, 'experimental': self.world.experimental, @@ -2249,6 +2250,7 @@ class Spoiler(object): outfile.write('Enemy shuffle:'.ljust(line_width) + '%s\n' % self.metadata['enemy_shuffle'][player]) outfile.write('Enemy health:'.ljust(line_width) + '%s\n' % self.metadata['enemy_health'][player]) outfile.write('Enemy damage:'.ljust(line_width) + '%s\n' % self.metadata['enemy_damage'][player]) + outfile.write('Pot shuffle:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['potshuffle'][player] else 'No')) outfile.write('Hints:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['hints'][player] else 'No')) outfile.write('Experimental:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['experimental'][player] else 'No')) outfile.write('Key Drops shuffled:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['keydropshuffle'][player] else 'No')) diff --git a/Bosses.py b/Bosses.py index c1f311ef..ebf8b4dd 100644 --- a/Bosses.py +++ b/Bosses.py @@ -174,7 +174,7 @@ def place_bosses(world, player): boss_locations.remove(['Ice Palace', None]) placeable_bosses.remove('Kholdstare') - if world.boss_shuffle[player] == "basic": # vanilla bosses shuffled + if world.boss_shuffle[player] == "simple": # vanilla bosses shuffled bosses = placeable_bosses + ['Armos Knights', 'Lanmolas', 'Moldorm'] else: # all bosses present, the three duplicates chosen at random bosses = all_bosses + [random.choice(placeable_bosses) for _ in range(3)] diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 3ca435cc..e36005fc 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1308,12 +1308,28 @@ def link_entrances(world, player): # place remaining doors connect_doors(world, doors, door_targets, player) - elif not invFlag and world.shuffle[player] == 'insanity_legacy': + elif world.shuffle[player] == 'insanity_legacy': world.fix_fake_world[player] = False # beware ye who enter here - entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] - entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)'] + if not invFlag: + entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)'] + + doors = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + Old_Man_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + else: + entrances_must_exits = Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit + + doors = Inverted_LW_Entrances + Inverted_LW_Dungeon_Entrances + Inverted_LW_Entrances_Must_Exit + Inverted_LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Secret Entrance Stairs'] + Inverted_Old_Man_Entrances +\ + Inverted_DW_Entrances + Inverted_DW_Dungeon_Entrances + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] +\ + Inverted_LW_Single_Cave_Doors + Inverted_DW_Single_Cave_Doors + ['Desert Palace Entrance (West)', 'Desert Palace Entrance (North)'] + + exit_pool = list(doors) + + # randomize which desert ledge door is a must-exit + if random.randint(0, 1) == 0: + entrances_must_exits.append('Desert Palace Entrance (North)') + else: + entrances_must_exits.append('Desert Palace Entrance (West)') doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\ DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] @@ -1333,6 +1349,9 @@ def link_entrances(world, player): hole_targets = ['Kakariko Well (top)', 'Bat Cave (right)', 'North Fairy Cave', 'Lost Woods Hideout (top)', 'Lumberjack Tree (top)', 'Sewer Drop', 'Skull Back Drop', 'Skull Left Drop', 'Skull Pinball', 'Skull Pot Circle'] + # tavern back door cannot be shuffled yet + connect_doors(world, ['Tavern North'], ['Tavern'], player) + if world.mode[player] == 'standard': # cannot move uncle cave connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player) @@ -1342,24 +1361,32 @@ def link_entrances(world, player): hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_targets.append('Hyrule Castle Secret Entrance') doors.append('Hyrule Castle Secret Entrance Stairs') - entrances.append('Hyrule Castle Secret Entrance Stairs') caves.append('Hyrule Castle Secret Entrance Exit') if not world.shuffle_ganon: connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) - connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) + if not invFlag: + connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) + else: + connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player) else: - entrances.append('Ganons Tower') + caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) - hole_entrances.append('Pyramid Hole') hole_targets.append('Pyramid') - entrances_must_exits.append('Pyramid Entrance') - doors.extend(['Ganons Tower', 'Pyramid Entrance']) + if not invFlag: + hole_entrances.append('Pyramid Hole') + doors.extend(['Agahnims Tower', 'Pyramid Entrance']) + exit_pool.extend(['Agahnims Tower', 'Pyramid Entrance']) + else: + hole_entrances.append('Inverted Pyramid Hole') + doors.extend(['Agahnims Tower', 'Inverted Pyramid Entrance']) + exit_pool.extend(['Agahnims Tower', 'Inverted Pyramid Entrance']) random.shuffle(hole_entrances) random.shuffle(hole_targets) - random.shuffle(entrances) + random.shuffle(exit_pool) # fill up holes for hole in hole_entrances: @@ -1375,6 +1402,31 @@ def link_entrances(world, player): doors.append('Hyrule Castle Entrance (South)') entrances.append('Hyrule Castle Entrance (South)') caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) + + if not world.shufflelinks[player]: + if not invFlag: + links_house = 'Links House' + else: + links_house = 'Big Shop Shop' + else: + if not invFlag: + links_house_doors = [i for i in doors if i not in Sanctuary_Doors + Isolated_LH_Doors] + else: + links_house_doors = [i for i in doors if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors] + links_house = random.choice(links_house_doors) + connect_two_way(world, links_house, 'Links House Exit', player) + doors.remove(links_house) + exit_pool.remove(links_house) + + if not invFlag: + sanc_doors = [door for door in Sanctuary_Doors if door in exit_pool] + else: + sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in exit_pool] + sanc_door = random.choice(sanc_doors) + exit_pool.remove(sanc_door) + doors.remove(sanc_door) + connect_entrance(world, sanc_door, 'Dark Sanctuary Hint', player) + world.get_entrance('Dark Sanctuary Hint Exit', player).connect(world.get_entrance(sanc_door, player).parent_region) # now let's deal with mandatory reachable stuff def extract_reachable_exit(cavelist): @@ -1393,7 +1445,7 @@ def link_entrances(world, player): cavelist.remove(candidate) return candidate - def connect_reachable_exit(entrance, caves, doors): + def connect_reachable_exit(entrance, caves, doors, exit_pool): cave = extract_reachable_exit(caves) exit = cave[-1] @@ -1401,18 +1453,19 @@ def link_entrances(world, player): connect_exit(world, exit, entrance, player) connect_entrance(world, doors.pop(), exit, player) # rest of cave now is forced to be in this world + exit_pool.remove(entrance) caves.append(cave) # connect mandatory exits for entrance in entrances_must_exits: - connect_reachable_exit(entrance, caves, doors) + connect_reachable_exit(entrance, caves, doors, exit_pool) # place old man, has limited options # exit has to come from specific set of doors, the entrance is free to move about - old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances] + old_man_entrances = [entrance for entrance in old_man_entrances if entrance in exit_pool] random.shuffle(old_man_entrances) old_man_exit = old_man_entrances.pop() - entrances.remove(old_man_exit) + exit_pool.remove(old_man_exit) connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit, player) connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)', player) @@ -1438,16 +1491,24 @@ def link_entrances(world, player): random.shuffle(blacksmith_doors) blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) - bomb_shop_doors.extend(blacksmith_doors) + doors.remove(blacksmith_hut) + exit_pool.remove(blacksmith_hut) # place dam and pyramid fairy, have limited options random.shuffle(bomb_shop_doors) bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) - single_doors.extend(bomb_shop_doors) + doors.remove(bomb_shop) + exit_pool.remove(bomb_shop) - # tavern back door cannot be shuffled yet - connect_doors(world, ['Tavern North'], ['Tavern'], player) + # handle remaining caves + for cave in caves: + if isinstance(cave, str): + cave = (cave,) + + for exit in cave: + connect_exit(world, exit, exit_pool.pop(), player) + connect_entrance(world, doors.pop(), exit, player) # place remaining doors connect_doors(world, single_doors, door_targets, player) diff --git a/Main.py b/Main.py index 774d48c0..0b50a86b 100644 --- a/Main.py +++ b/Main.py @@ -28,7 +28,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names -__version__ = '0.4.0.4-u' +__version__ = '0.4.0.5-u' class EnemizerError(RuntimeError): diff --git a/Mystery.py b/Mystery.py index 98f0d88f..a94125fe 100644 --- a/Mystery.py +++ b/Mystery.py @@ -190,9 +190,9 @@ def roll_settings(weights): ret.item_functionality = get_choice('item_functionality') - old_style_bosses = {'simple': 'basic', - 'full': 'normal', - 'random': 'chaos'} + old_style_bosses = {'basic': 'simple', + 'normal': 'full', + 'chaos': 'random'} boss_choice = get_choice('boss_shuffle') if boss_choice in old_style_bosses.keys(): boss_choice = old_style_bosses[boss_choice] diff --git a/PotShuffle.py b/PotShuffle.py index 0f4ca4d8..e8e6d3ce 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -295,7 +295,7 @@ def shuffle_pots(world, player): elif old_pot.item == PotItem.Switch: available_pots = (pot for pot in new_pots if (pot.room == old_pot.room or pot.room in movable_switch_rooms[old_pot.room]) and not (pot.flags & PotFlags.NoSwitch)) elif old_pot.item == PotItem.Key: - if world.doorShuffle[player] == 'vanilla' and not world.retro[player] and not world.keydropshuffle[player] and world.logic != 'nologic': + if world.doorShuffle[player] == 'vanilla' and not world.retro[player] and not world.keydropshuffle[player] and world.logic[player] != 'nologic': available_pots = (pot for pot in new_pots if pot.room not in invalid_key_rooms) else: available_pots = new_pots diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6bbab2ce..5f392a15 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,6 +10,12 @@ Thanks to qadan, cheuer, & compiling # Bug Fixes and Notes. +* 0.4.0.5 + * Insanity - less restrictions on exiting (all modes) + * Fix for simple bosses shuffle + * Fix for boss shuffle from Mystery.py + * Minor msu fade out bug (thanks codemann8) + * Other bug fixes (thanks Catobat) * 0.4.0.4 * Added --shufflelinks option * Moved spawning as a bunny indoors to experimental diff --git a/Rom.py b/Rom.py index db87f11f..98d060ab 100644 --- a/Rom.py +++ b/Rom.py @@ -1347,9 +1347,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if item.name != 'Piece of Heart' or equip[0x36B] == 0: equip[0x36C] = min(equip[0x36C] + 0x08, 0xA0) equip[0x36D] = min(equip[0x36D] + 0x08, 0xA0) - elif item.name == 'Pegasus Boots': - rom.write_byte(0x183015, 0x01) - ability_flags |= 0b00000100 else: raise RuntimeError(f'Unsupported item in starting equipment: {item.name}')