From a9b872b88d32cde9670a1d579b9da52d10d2add2 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 14 Sep 2021 15:00:55 -0600 Subject: [PATCH 1/7] Ambrosia logic fixes --- DungeonGenerator.py | 2 +- Fill.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 20352b37..cb834877 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -680,7 +680,7 @@ def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exceptio def filter_for_potential_bk_locations(locations, world, player): - return [x for x in locations if '- Big Chest' not in x.name and not not reserved_location(x, world, player) and + return [x for x in locations if '- Big Chest' not in x.name and not reserved_location(x, world, player) and not x.forced_item and not prize_or_event(x) and not blind_boss_unavail(x, locations, world, player)] diff --git a/Fill.py b/Fill.py index 447e6912..59a98cea 100644 --- a/Fill.py +++ b/Fill.py @@ -254,7 +254,7 @@ def valid_key_placement(item, location, itempool, world): cr_count = world.crystals_needed_for_gt[location.player] return key_logic.check_placement(unplaced_keys, location if item.bigkey else None, prize_loc, cr_count) else: - return item.is_inside_dungeon_item(world) + return not item.is_inside_dungeon_item(world) # todo: big deal for ambrosia to fix this def valid_reserved_placement(item, location, world): From c64d499bab652d847990529e0f41cd5aad22dba4 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 14 Sep 2021 15:03:51 -0600 Subject: [PATCH 2/7] Maps/compasses should not be advancement items if the restriction is none --- source/item/FillUtil.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 6aa2f5cf..0a1e54f7 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -20,6 +20,7 @@ def create_item_pool_config(world): d_name = "Thieves' Town" if dungeon.startswith('Thieves') else dungeon config.reserved_locations[player].add(f'{d_name} - Boss') for dungeon in world.dungeons: - for item in dungeon.all_items: - if item.map or item.compass: - item.advancement = True + if world.restrict_boss_items[dungeon.player] != 'none': + for item in dungeon.all_items: + if item.map or item.compass: + item.advancement = True From c5f6c46bd879f21df840c550e40143703409318e Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 14 Sep 2021 15:24:23 -0600 Subject: [PATCH 3/7] Remove unique annotation from FastEnum class --- BaseClasses.py | 1 - 1 file changed, 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 77fdf968..b7c13f45 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2740,7 +2740,6 @@ class Settings(object): args.shufflepots[p] = True if settings[7] & 0x4 else False -@unique class KeyRuleType(FastEnum): WorstCase = 0 AllowSmall = 1 From eab94043988c92b96260ec09ae88bcd76aba56be Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 14 Sep 2021 15:40:55 -0600 Subject: [PATCH 4/7] Allow Blind's Cell to be shuffled if Blind is not the boss --- DungeonGenerator.py | 8 ++++++-- ItemList.py | 2 -- Main.py | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/DungeonGenerator.py b/DungeonGenerator.py index f579f285..19ab9a1d 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -585,7 +585,8 @@ def determine_paths_for_dungeon(world, player, all_regions, name): paths.append(boss) if 'Thieves Boss' in all_r_names: paths.append('Thieves Boss') - paths.append(('Thieves Blind\'s Cell', 'Thieves Boss')) + if world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind': + paths.append(('Thieves Blind\'s Cell', 'Thieves Boss')) for drop_check in drop_path_checks: if drop_check in all_r_names: paths.append((drop_check, non_hole_portals)) @@ -1275,6 +1276,9 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, for r_name in ['Hyrule Dungeon Cellblock', 'Sanctuary']: # need to deliver zelda assign_sector(find_sector(r_name, candidate_sectors), current_dungeon, candidate_sectors, global_pole) + if key == 'Thieves Town' and world.get_dungeon("Thieves Town", player).boss.enemizer_name == 'Blind': + assign_sector(find_sector("Thieves Blind's Cell", candidate_sectors), current_dungeon, + candidate_sectors, global_pole) entrances_map, potentials, connections = connections_tuple accessible_sectors, reverse_d_map = set(), {} for key in dungeon_entrances.keys(): @@ -3898,7 +3902,7 @@ dungeon_boss_sectors = { 'Palace of Darkness': ['PoD Boss'], 'Swamp Palace': ['Swamp Boss'], 'Skull Woods': ['Skull Boss'], - 'Thieves Town': ['Thieves Blind\'s Cell', 'Thieves Boss'], + 'Thieves Town': ['Thieves Boss'], 'Ice Palace': ['Ice Boss'], 'Misery Mire': ['Mire Boss'], 'Turtle Rock': ['TR Boss'], diff --git a/ItemList.py b/ItemList.py index eda7291f..c0f2f86c 100644 --- a/ItemList.py +++ b/ItemList.py @@ -4,7 +4,6 @@ import math import RaceRandom as random from BaseClasses import Region, RegionType, Shop, ShopType, Location, CollectionState -from Bosses import place_bosses from Dungeons import get_dungeon_item_pool from EntranceShuffle import connect_entrance from Regions import shop_to_location_table, retro_shops, shop_table_by_location @@ -371,7 +370,6 @@ def generate_itempool(world, player): tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] world.required_medallions[player] = (mm_medallion, tr_medallion) - place_bosses(world, player) set_up_shops(world, player) if world.retro[player]: diff --git a/Main.py b/Main.py index be2cb2e6..11bf51d3 100644 --- a/Main.py +++ b/Main.py @@ -10,6 +10,7 @@ import time import zlib from BaseClasses import World, CollectionState, Item, Region, Location, Shop, Entrance, Settings +from Bosses import place_bosses from Items import ItemFactory from KeyDoorShuffle import validate_key_placement from OverworldGlitchRules import create_owg_connections @@ -146,6 +147,7 @@ def main(args, seed=None, fish=None): create_rooms(world, player) create_dungeons(world, player) adjust_locations(world, player) + place_bosses(world, player) if any(world.potshuffle.values()): logger.info(world.fish.translate("cli", "cli", "shuffling.pots")) From bce3bcf4fe8036c3a767c9d8cbbf98debbb68e07 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 14 Sep 2021 16:02:31 -0600 Subject: [PATCH 5/7] Remove stonewall pre-opening code in favor of dynamic softlock prevention (Promoted from experimental) --- DoorShuffle.py | 6 ++---- DungeonGenerator.py | 44 ------------------------------------------ Main.py | 2 +- RELEASENOTES.md | 8 ++++++++ Rom.py | 9 +-------- asm/overrides.asm | 6 +----- data/base2current.bps | Bin 135945 -> 135928 bytes 7 files changed, 13 insertions(+), 62 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 9d22893c..ee9cbd01 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -1358,10 +1358,8 @@ def combine_layouts(recombinant_builders, dungeon_builders, entrances_map): if recombine.master_sector is None: recombine.master_sector = builder.master_sector recombine.master_sector.name = recombine.name - recombine.pre_open_stonewalls = builder.pre_open_stonewalls else: recombine.master_sector.regions.extend(builder.master_sector.regions) - recombine.pre_open_stonewalls.update(builder.pre_open_stonewalls) recombine.layout_starts = list(entrances_map[recombine.name]) dungeon_builders[recombine.name] = recombine @@ -2043,8 +2041,8 @@ class DROptions(Flag): Debug = 0x08 # Rails = 0x10 # Unused bit now OriginalPalettes = 0x20 - Open_PoD_Wall = 0x40 # If on, pre opens the PoD wall, no bow required - Open_Desert_Wall = 0x80 # If on, pre opens the desert wall, no fire required + # Open_PoD_Wall = 0x40 # No longer pre-opening pod wall - unused + # Open_Desert_Wall = 0x80 # No longer pre-opening desert wall - unused Hide_Total = 0x100 DarkWorld_Spawns = 0x200 diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 19ab9a1d..12aaa1c9 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -56,23 +56,10 @@ def pre_validate(builder, entrance_region_names, split_dungeon, world, player): def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player): - stonewalls = check_for_stonewalls(builder) sector = generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player) - for stonewall in stonewalls: - if not stonewall_valid(stonewall): - builder.pre_open_stonewalls.add(stonewall) return sector -def check_for_stonewalls(builder): - stonewalls = set() - for sector in builder.sectors: - for door in sector.outstanding_doors: - if door.stonewall: - stonewalls.add(door) - return stonewalls - - def generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player): if builder.valid_proposal: # we made this earlier in gen, just use it proposed_map = builder.valid_proposal @@ -612,35 +599,6 @@ def winnow_hangers(hangers, hooks): hangers[hanger].remove(door) -def stonewall_valid(stonewall): - bad_door = stonewall.dest - if bad_door.blocked: - return True # great we're done with this one - loop_region = stonewall.entrance.parent_region - start_regions = [bad_door.entrance.parent_region] - if bad_door.dependents: - for dep in bad_door.dependents: - start_regions.append(dep.entrance.parent_region) - queue = deque(start_regions) - visited = set(start_regions) - while len(queue) > 0: - region = queue.popleft() - if region == loop_region: - return False # guaranteed loop - possible_entrances = list(region.entrances) - for entrance in possible_entrances: - parent = entrance.parent_region - if parent.type != RegionType.Dungeon: - return False # you can get stuck from an entrance - else: - door = entrance.door - if (door is None or (door != stonewall and not door.blocked)) and parent not in visited: - visited.add(parent) - queue.append(parent) - # we didn't find anything bad - return True - - def create_graph_piece_from_state(door, o_state, b_state, proposed_map, exception): # todo: info about dungeon events - not sure about that graph_piece = GraphPiece() @@ -1198,8 +1156,6 @@ class DungeonBuilder(object): self.path_entrances = None # used for pathing/key doors, I think self.split_flag = False - self.pre_open_stonewalls = set() # used by stonewall system - self.candidates = None self.total_keys = None self.key_doors_num = None diff --git a/Main.py b/Main.py index 11bf51d3..0db8af04 100644 --- a/Main.py +++ b/Main.py @@ -29,7 +29,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.5.1.1-u' +__version__ = '0.5.1.2-u' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4d212275..55a34c3f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -15,6 +15,14 @@ CLI: ```--bombbag``` # Bug Fixes and Notes. +* 0.5.1.2 + * Allowed Blind's Cell to be shuffled anywhere if Blind is not the boss of Thieves Town + * Remove unique annotation from a FastEnum that was causing problems + * Updated prevent mixed_travel setting to prevent more mixed travel + * Prevent key door loops on the same supertile where you could have spent 2 keys on one logical door + * Promoted dynamic soft-lock prevention on "stonewalls" from experimental to be the primary prevention (Stonewalls are now never pre-opened) + * Fix to money balancing algorithm with small item_pool, thanks Catobat + * Many fixes and refinements to key logic and generation * 0.5.1.1 * Shop hints in ER are now more generic instead of using "near X" because they aren't near that anymore * Added memory location for mutliworld scripts to read what item was just obtain (longer than one frame) diff --git a/Rom.py b/Rom.py index d6248488..22f017e3 100644 --- a/Rom.py +++ b/Rom.py @@ -32,7 +32,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'ef6e3e1aa59838c01dbd5b1b2387e70c' +RANDOMIZERBASEHASH = '11f4f494e999a919aafd7d2624e67679' class JsonRom(object): @@ -754,13 +754,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(paired_door.address_a(world, player), paired_door.rom_data_a(world, player)) rom.write_bytes(paired_door.address_b(world, player), paired_door.rom_data_b(world, player)) if world.doorShuffle[player] != 'vanilla': - if not world.experimental[player]: - for builder in world.dungeon_layouts[player].values(): - for stonewall in builder.pre_open_stonewalls: - if stonewall.name == 'Desert Wall Slide NW': - dr_flags |= DROptions.Open_Desert_Wall - elif stonewall.name == 'PoD Bow Statue Down Ladder': - dr_flags |= DROptions.Open_PoD_Wall for name, pair in boss_indicator.items(): dungeon_id, boss_door = pair opposite_door = world.get_door(boss_door, player).dest diff --git a/asm/overrides.asm b/asm/overrides.asm index f9842866..a041ae30 100644 --- a/asm/overrides.asm +++ b/asm/overrides.asm @@ -35,11 +35,7 @@ rtl OnFileLoadOverride: jsl OnFileLoad ; what I wrote over - lda.l DRFlags : and #$80 : beq + ;flag is off - lda $7ef086 : ora #$80 : sta $7ef086 - + lda.l DRFlags : and #$40 : beq + ;flag is off - lda $7ef036 : ora #$80 : sta $7ef036 - + lda.l DRFlags : and #$02 : beq + + + lda.l DRFlags : and #$02 : beq + ; Mirror Scroll lda $7ef353 : bne + lda #$01 : sta $7ef353 + rtl diff --git a/data/base2current.bps b/data/base2current.bps index 17b3e4976a8e05b8f7e0a167166c55236e3a9806..46dee4bec02691a6036774e7670b7a070e81727c 100644 GIT binary patch delta 1022 zcmW-de@q)?7{~9uw)CK^6tJ!)W4shtsRIR>QbZOk$e0eoh76n&8?uQ8H+5pj62k5Z zx2-|iYhTJ)Dgt8%6gEn{kOE6Bn-Xw;xNaGn&N#P#Q6~vkp&%g+Z~pcDkK;8ZC6i9=tEdv(0eet7PJtAD6juZMaXXrk9K@GcULzfp zfF?984+1N)6Gw^pcHMWJcDbDo6Z-&g^Z8Nb0BCuuVnYg){CQQn9uWL^ZY2O}q{uq} z)O>XwcvgAEn10V<__Lt%Sk=#i6{zm`EnH&c^WLi~27P@0rQdTv9%@_E5jl2J#sPQ= zO)c&Ob?B$Xq{sk+TIo&3*2?3*rO3Ah7+ULDB^4sI$?(}rJf0fT)EnxxxsHooDXG)r zLbSJHr8yYk0X9+V-FssPQQbRy!g2vP^MRumCna#)@%@&d={o)I#`KmTgHd*|E4D+s z&0x}){;#MfRoEVA*zrZ5gAx4?44g#{CnuqMTO+>RJ>i$?$zUWWr2%}Qh1|wTd+>8k zsA3>a_mqtBg}&}5fJOvRbuTSM^~igjB-Fqhvk5JXl>6or8BPMnQ zuN;}A2%Gt`jFZ9u^M~IxLo3h)I@;u@x{s2kTw;zg`IAujWZMHuZfk8@XuB>BblDcf z2cyxD7UZCtAsslyZ-kT}s{)ZvP0MV%NXrpKI*5mtUl&gye=$vCn2=^;sbtpN-iXL& z*z>N>5g7w(kzwVHG!J4wae4?$sP`tXHbW=7e*B=tJ%Ci@78eGBI`7_<5f%GuHX2!> zVh^#l3#{=3dG+FKvp-XShfw}16JOJbnkY_=1tkY+Iz6#z-kK=%U^Nlm>CkD;{N14& z3YY|)tLW*uIZrr6)c(djLLUJ$P+~YW?&FsPcWH|yCeYMb^5LcS2zU$~4jbdYbh_hJ zU3PJ$o<&BIie{B)B%BU{=*RF*(1q5+Wq73@?OWT0m%WQVT1!{HC3Dl)GON0aPf~2w zcHXo0@{Y*<#>~0GvnlX8yA%hKC9$c<7Z(rS&meM-`lzB=jlpLw09JKk;iunN8xqKy z*lPsrzM$u&<*E6jeub#cjWL zx`n;(1wAsfe;|Wm&10Yx-~X5I))>eJlGyC8e8ks4qg4$nrPnOOTUSPQm%FUBeJ(cZ ZHh_|>va7y|+hBAr@maM$aAWG>`hTN1v+e)@ delta 1039 zcmW-be^8Tk9LJyUvtJJk$AI8wh5Isuu^L5!H+~3BFoX zkV)*0&3=3!F~V=kho|U zC}3)fz!Q={dCj+`{3R`=Q-3p5kB%Jv3UA)d6uo++9Lz8Smlidk9(64!i8>o8paFEE zz``cbh3+h*5n(H8*UOQC;r+T|LYDm<&Cm@CO`Ox_<1t{Im#=%7}QmG!qkX0coQ?BUue64Q9$?|L!QEs)Pak69?rXv6ZhIe2^57l&r%_hc z^Y%ZHV3ZZWr)(U1-3G5&@^v!Z|A>BwHeN4GJ$Kpn1l=cH#9CV-tK8 z8H%C#MgX_8ksD}!FMh@W^Q;gz_DZiZ;<57&3o@>NYWs|AY_F)#LCWM%W6+_nS2i}k z!%FjDU55_twbBqCw5qfi97Oln)U6F;=G^;Eez9Yjol!-xVH$%O$cWP-E^uyy7O3K} z;c@r2@$)!*4jC~zj(Q0dFK2911oVW(a9Kd}Vc7cnglonqM*YS(oxSD037c-VomR$j>Z0#U~u9s*+n<;*( z#kup1OWi`)iOM6TNkNCZPTFJRPF7N=l+?#j2ar3W2CHZ;vI$J0#mI|z>Lk*vW#TD1 zRKKQ{qzT=|PjV{WFF&TIj;1iKwZg|^`eS#G?SBdPBaK{_w{ts8zk3A~m~9Ix~1 z%Vw)I{zfe@C(BPYPMVui$uF@HVNOfizu0XoiZ#DHH4nhPxieI?Uw#180ExYjf7Tvz zBB)Ou$bBs6Z?6GmwL*7w#bR*>b<+oW1-vM?qZ&s+C9fp+H~;h~$OSy+rR28Gx}m)-(bN{HFKva|0P@cI7$0bslV From ddd3aaf9a2e502335834d23f81fd8f0edf69eae5 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 16 Sep 2021 15:20:29 -0600 Subject: [PATCH 6/7] More prize door refinement Incorporating drop entrances better in the find proposal algorithm --- DoorShuffle.py | 3 ++- DungeonGenerator.py | 14 +++++++++----- KeyDoorShuffle.py | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index ee9cbd01..c70cc040 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -14,7 +14,7 @@ from RoomData import DoorKind, PairedDoor, reset_rooms from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths, drop_entrances from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances from DungeonGenerator import dungeon_portals, dungeon_drops, GenerationException -from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout +from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock from Utils import ncr, kth_combination @@ -1463,6 +1463,7 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True start_regions = [x for x in start_regions if x not in excluded.keys()] key_layout = build_key_layout(builder, start_regions, proposal, world, player) + determine_prize_lock(key_layout, world, player) while not validate_key_layout(key_layout, world, player): itr += 1 stop_early = False diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 12aaa1c9..f88499bd 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -56,11 +56,6 @@ def pre_validate(builder, entrance_region_names, split_dungeon, world, player): def generate_dungeon(builder, entrance_region_names, split_dungeon, world, player): - sector = generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player) - return sector - - -def generate_dungeon_main(builder, entrance_region_names, split_dungeon, world, player): if builder.valid_proposal: # we made this earlier in gen, just use it proposed_map = builder.valid_proposal else: @@ -99,6 +94,15 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon if (access_region.name in world.inaccessible_regions[player] and region.name not in world.enabled_entrances[player]): excluded[region] = None + elif len(region.entrances) == 1: # for holes + access_region = next(x.parent_region for x in region.entrances + if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld] + or x.parent_region.name == 'Sewer Drop') + if access_region.name == 'Sewer Drop': + access_region = next(x.parent_region for x in access_region.entrances) + if (access_region.name in world.inaccessible_regions[player] and + region.name not in world.enabled_entrances[player]): + excluded[region] = None entrance_regions = [x for x in entrance_regions if x not in excluded.keys()] doors_to_connect = {} all_regions = set() diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 1b74ae80..a84fe5e8 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -27,6 +27,7 @@ class KeyLayout(object): self.found_doors = set() self.prize_relevant = None + self.prize_can_lock = None # if true, then you may need to beat the bo # bk special? # bk required? True if big chests or big doors exists @@ -1447,7 +1448,10 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa found_forced_bk = state.found_forced_bk() smalls_done = not smalls_avail or not enough_small_locations(state, available_small_locations) bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk) - prize_done = not key_layout.prize_relevant or state.prize_doors_opened + # prize door should not be opened if the boss is reachable - but not reached yet + allow_for_prize_lock = (key_layout.prize_can_lock and + not any(x for x in state.found_locations if '- Prize' in x.name)) + prize_done = not key_layout.prize_relevant or state.prize_doors_opened or allow_for_prize_lock if smalls_done and bk_done and prize_done: return False else: @@ -1531,6 +1535,39 @@ def enough_small_locations(state, avail_small_loc): return avail_small_loc >= len(unique_d_set) +def determine_prize_lock(key_layout, world, player): + if ((world.retro[player] and (world.mode[player] != 'standard' or key_layout.sector.name != 'Hyrule Castle')) + or world.logic[player] == 'nologic'): + return # done, doesn't matter what + flat_proposal = key_layout.flat_prop + state = ExplorationState(dungeon=key_layout.sector.name) + state.key_locations = key_layout.max_chests + state.big_key_special = check_bk_special(key_layout.sector.regions, world, player) + prize_lock_possible = False + for region in key_layout.start_regions: + dungeon_entrance, portal_door = find_outside_connection(region) + prize_relevant_flag = prize_relevance(key_layout, dungeon_entrance) + if prize_relevant_flag: + state.append_door_to_list(portal_door, state.prize_doors) + state.prize_door_set[portal_door] = dungeon_entrance + key_layout.prize_relevant = prize_relevant_flag + prize_lock_possible = True + else: + state.visit_region(region, key_checks=True) + state.add_all_doors_check_keys(region, flat_proposal, world, player) + if not prize_lock_possible: + return # done, no prize entrances to worry about + expand_key_state(state, flat_proposal, world, player) + while len(state.small_doors) > 0 or len(state.big_doors) > 0: + if len(state.big_doors) > 0: + open_a_door(state.big_doors[0].door, state, flat_proposal, world, player) + elif len(state.small_doors) > 0: + open_a_door(state.small_doors[0].door, state, flat_proposal, world, player) + expand_key_state(state, flat_proposal, world, player) + if any(x for x in state.found_locations if '- Prize' in x.name): + key_layout.prize_can_lock = True + + def cnt_avail_small_locations(free_locations, key_only, state, world, player): if not world.keyshuffle[player] and not world.retro[player]: bk_adj = 1 if state.big_key_opened and not state.big_key_special else 0 From 1eda6cfa9a392a6fa3f4f99d6f560b2ff9066c95 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 16 Sep 2021 15:46:40 -0600 Subject: [PATCH 7/7] Fix lambda by binding bk_name at lambda creation --- Rules.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index 3114bde0..42bfe2f2 100644 --- a/Rules.py +++ b/Rules.py @@ -1954,7 +1954,7 @@ def add_key_logic_rules(world, player): big_chest = world.get_location(chest.name, player) add_rule(big_chest, create_rule(d_logic.bk_name, player)) if len(d_logic.bk_doors) == 0 and len(d_logic.bk_chests) <= 1: - set_always_allow(big_chest, lambda state, item: item.name == d_logic.bk_name and item.player == player) + set_always_allow(big_chest, allow_big_key_in_big_chest(d_logic.bk_name, player)) if world.retro[player]: for d_name, layout in world.key_layout[player].items(): for door in layout.flat_prop: @@ -1992,6 +1992,10 @@ def eval_small_key_door(door_name, dungeon, player): return lambda state: eval_small_key_door_main(state, door_name, dungeon, player) +def allow_big_key_in_big_chest(bk_name, player): + return lambda state, item: item.name == bk_name and item.player == player + + def retro_in_hc(spot): return spot.parent_region.dungeon.name == 'Hyrule Castle' if spot.parent_region.dungeon else False