From 4d16559fae337b8c66468267323d538587df7348 Mon Sep 17 00:00:00 2001 From: AmazingAmpharos Date: Thu, 18 Apr 2019 16:11:11 -0500 Subject: [PATCH] TR Logic Fix This update should fully fix the key logic for Turtle Rock. This involved fixing several longstanding bugs in the program we didn't realize we had until trying to really fix this issue. Notable inclusions are that now seeds generated with a count will be identical to similarly numbered seeds generated individually and that now the always allows actually work (key for key apparently actually never happened in ER before and we didn't notice!). This also required refactoring the item pool generation before rule setting and individually moving pendant/crystal placement out of item pool generation (it's not a separate step between rule setting and normal item fill). A few exotic seed generation fails are still possible involving multi-entrance dungeons being really, really inaccessible in non-keysanity, but they're now appropriately super rare instead of being as common as they were before. --- BaseClasses.py | 2 +- EntranceShuffle.py | 22 +++++++++++++--------- ItemList.py | 3 --- Main.py | 16 ++++++++++++---- Rules.py | 4 +--- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 2a6f5e88..2395d32e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -765,7 +765,7 @@ class Location(object): self.item_rule = lambda item: True def can_fill(self, state, item, check_access=True): - return self.always_allow(item, self) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state))) + return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state))) def can_reach(self, state): if self.access_rule(state) and state.can_reach(self.parent_region): diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 90504453..1afdf261 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -7,7 +7,12 @@ def link_entrances(world): connect_two_way(world, 'Links House', 'Links House Exit') # unshuffled. For now connect_exit(world, 'Chris Houlihan Room Exit', 'Links House') # should always match link's house, except for plandos - unbias_some_entrances() + Dungeon_Exits = Dungeon_Exits_Base.copy() + Cave_Exits = Cave_Exits_Base.copy() + Old_Man_House = Old_Man_House_Base.copy() + Cave_Three_Exits = Cave_Three_Exits_Base.copy() + + unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits) # setup mandatory connections for exitname, regionname in mandatory_connections: @@ -1329,7 +1334,7 @@ def simple_shuffle_dungeons(world): connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)') connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)') -def unbias_some_entrances(): +def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits): def shuffle_lists_in_list(ls): for i, item in enumerate(ls): if isinstance(item, list): @@ -1392,7 +1397,7 @@ DW_Dungeon_Entrances = ['Thieves Town', DW_Dungeon_Entrances_Must_Exit = ['Dark Death Mountain Ledge (East)', 'Turtle Rock Isolated Ledge Entrance'] -Dungeon_Exits = [['Desert Palace Exit (South)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)'], +Dungeon_Exits_Base = [['Desert Palace Exit (South)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)'], 'Desert Palace Exit (North)', 'Eastern Palace Exit', 'Tower of Hera Exit', @@ -1422,21 +1427,20 @@ Old_Man_Entrances = ['Old Man Cave (East)', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)'] -Old_Man_House = [['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)']] +Old_Man_House_Base = [['Old Man House Exit (Bottom)', 'Old Man House Exit (Top)']] - -Cave_Exits = [['Elder House Exit (East)', 'Elder House Exit (West)'], +Cave_Exits_Base = [['Elder House Exit (East)', 'Elder House Exit (West)'], ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)'], ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)'], ['Fairy Ascension Cave Exit (Bottom)', 'Fairy Ascension Cave Exit (Top)'], ['Bumper Cave Exit (Top)', 'Bumper Cave Exit (Bottom)'], ['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']] -Cave_Exits += [('Superbunny Cave Exit (Bottom)', 'Superbunny Cave Exit (Top)'), +Cave_Exits_Base += [('Superbunny Cave Exit (Bottom)', 'Superbunny Cave Exit (Top)'), ('Spiral Cave Exit (Top)', 'Spiral Cave Exit')] - -Cave_Three_Exits = [('Spectacle Rock Cave Exit (Peak)', 'Spectacle Rock Cave Exit (Top)', + +Cave_Three_Exits_Base = [('Spectacle Rock Cave Exit (Peak)', 'Spectacle Rock Cave Exit (Top)', 'Spectacle Rock Cave Exit'), ['Paradox Cave Exit (Top)', 'Paradox Cave Exit (Middle)','Paradox Cave Exit (Bottom)']] diff --git a/ItemList.py b/ItemList.py index fb76afac..8c7f3d71 100644 --- a/ItemList.py +++ b/ItemList.py @@ -274,9 +274,6 @@ def generate_itempool(world): create_dynamic_shop_locations(world) - # distribute crystals - fill_prizes(world) - take_any_locations = [ 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', 'Fortune Teller (Light)', 'Lake Hylia Fortune Teller', 'Lumberjack House', 'Bonk Fairy (Light)', diff --git a/Main.py b/Main.py index 500e88bc..e7177011 100644 --- a/Main.py +++ b/Main.py @@ -13,7 +13,7 @@ from Rom import patch_rom, Sprite, LocalRom, JsonRom from Rules import set_rules from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items -from ItemList import generate_itempool, difficulties +from ItemList import generate_itempool, difficulties, fill_prizes from Utils import output_path __version__ = '0.6.1' @@ -62,13 +62,17 @@ def main(args, seed=None): link_entrances(world) mark_light_world_regions(world) + logger.info('Generating Item Pool.') + + generate_itempool(world) + logger.info('Calculating Access Rules.') set_rules(world) - logger.info('Generating Item Pool.') + logger.info('Placing Dungeon Prizes.') - generate_itempool(world) + fill_prizes(world) logger.info('Placing Dungeon Items.') @@ -151,6 +155,9 @@ def copy_world(world): ret.dark_world_light_cone = world.dark_world_light_cone ret.seed = world.seed ret.can_access_trock_eyebridge = world.can_access_trock_eyebridge + ret.can_access_trock_front = world.can_access_trock_front + ret.can_access_trock_big_chest = world.can_access_trock_big_chest + ret.can_access_trock_middle = world.can_access_trock_middle ret.can_take_damage = world.can_take_damage ret.difficulty_requirements = world.difficulty_requirements ret.fix_fake_world = world.fix_fake_world @@ -271,7 +278,8 @@ def create_playthrough(world): old_item = location.item location.item = None state.remove(old_item) - if world.can_beat_game(state_cache[num]): + ##if world.can_beat_game(state_cache[num]): + if world.can_beat_game(): to_delete.append(location) else: # still required, got to keep it around diff --git a/Rules.py b/Rules.py index 1f3fe0d7..8762945d 100644 --- a/Rules.py +++ b/Rules.py @@ -56,7 +56,6 @@ def set_defeat_dungeon_boss_rule(location): def set_always_allow(spot, rule): spot.always_allow = rule - def add_rule(spot, rule, combine='and'): old_rule = spot.access_rule if combine == 'or': @@ -572,7 +571,7 @@ def set_trock_key_rules(world): set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)'), lambda state: state.has_key('Small Key (Turtle Rock)', 2)) set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has_key('Small Key (Turtle Rock)', 1)) set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has_key('Small Key (Turtle Rock)', tr_big_key_chest_keys_needed(state))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)') + set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)' and state.has_key('Small Key (Turtle Rock)', 2)) non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'] @@ -585,7 +584,6 @@ def set_trock_key_rules(world): 'Turtle Rock - Eye Bridge - Top Right'] if not world.keysanity: non_big_key_locations += ['Turtle Rock - Big Key Chest'] - else: set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)'), lambda state: state.has_key('Small Key (Turtle Rock)', 2) if item_in_locations(state, 'Big Key (Turtle Rock)', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left', 'Turtle Rock - Roller Room - Right']) else state.has_key('Small Key (Turtle Rock)', 4)) set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: (state.has_key('Small Key (Turtle Rock)', 4) or item_name(state, 'Turtle Rock - Big Key Chest') == 'Small Key (Turtle Rock)'))