From 409f7d50d5829e779a6bfcf9ebf6ec2e7895f20b Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 24 Feb 2023 14:25:04 -0700 Subject: [PATCH] Rom changes (see that commmit message or release notes) Customizer improvements: - Better logic around customized lobbies - Better logic around customized door types Fix to key doors that was causing extra key doors Generation improvement around crystal switches Fix bug in dungeon_only that wasn't using pot key locations (known issue still exists in pottery modes) Fixes an issue when keys are found in own dungeon for another player when using the bizhawk plugin --- DoorShuffle.py | 80 +++++++++++++++++++++----------- Doors.py | 2 +- DungeonGenerator.py | 9 +++- Main.py | 2 +- README.md | 23 ++++----- RELEASENOTES.md | 13 ++++++ Rom.py | 4 +- RoomData.py | 4 +- data/base2current.bps | Bin 93523 -> 93908 bytes source/item/FillUtil.py | 2 +- test/customizer/multi_test.yaml | 23 +++++++++ test/customizer/test_stuff.yaml | 54 ++++++++++++++------- 12 files changed, 152 insertions(+), 64 deletions(-) create mode 100644 test/customizer/multi_test.yaml diff --git a/DoorShuffle.py b/DoorShuffle.py index 308a0731..6bafd806 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -128,7 +128,7 @@ def link_doors_prep(world, player): vanilla_key_logic(world, player) -def link_doors_main(world, player): +def create_dungeon_pool(world, player): pool = None if world.doorShuffle[player] == 'basic': pool = [([name], regions) for name, regions in dungeon_regions.items()] @@ -142,6 +142,11 @@ def link_doors_main(world, player): elif world.doorShuffle[player] != 'vanilla': logging.getLogger('').error('Invalid door shuffle setting: %s' % world.doorShuffle[player]) raise Exception('Invalid door shuffle setting: %s' % world.doorShuffle[player]) + return pool + + +def link_doors_main(world, player): + pool = create_dungeon_pool(world, player) if pool: main_dungeon_pool(pool, world, player) if world.doorShuffle[player] != 'vanilla': @@ -557,7 +562,9 @@ def customizer_portals(master_door_list, world, player): custom_doors = world.customizer.get_doors()[player] if custom_doors and 'lobbies' in custom_doors: for portal, assigned_door in custom_doors['lobbies'].items(): - door = next(x for x in master_door_list if x.name == assigned_door) + door = next((x for x in master_door_list if x.name == assigned_door), None) + if door is None: + raise Exception(f'{assigned_door} not found. Check for typos') custom_portals[portal] = door assigned_doors.add(door) if custom_doors and 'doors' in custom_doors: @@ -570,6 +577,24 @@ def customizer_portals(master_door_list, world, player): elif 'dest' in dest: door = world.get_door(dest['dest'], player) assigned_doors.add(door) + # restricts connected doors to the customized portals + if assigned_doors: + pool = create_dungeon_pool(world, player) + if pool: + pool_map = {} + for pool, region_list in pool: + sector_pool = convert_to_sectors(region_list, world, player) + merge_sectors(sector_pool, world, player) + for p in pool: + pool_map[p] = sector_pool + for portal, assigned_door in custom_portals.items(): + portal_region = world.get_door(assigned_door, player).entrance.parent_region + portal_dungeon = world.get_region(f'{portal} Portal', player).dungeon.name + sector_pool = pool_map[portal_dungeon] + sector = next((s for s in sector_pool if portal_region in s.regions), None) + for door in sector.outstanding_doors: + if door.portalAble: + door.dungeonLink = portal_dungeon return custom_portals, assigned_doors @@ -1718,7 +1743,6 @@ def setup_custom_door_types(world, player): custom_doors = custom_doors[player] if 'doors' not in custom_doors: return - # todo: dash/bomb door pool specific customizeable_types = ['Key Door', 'Dash Door', 'Bomb Door', 'Trap Door', 'Big Key Door'] world.custom_door_types[player] = type_map = {x: defaultdict(list) for x in customizeable_types} for door, dest in custom_doors['doors'].items(): @@ -1731,7 +1755,7 @@ def setup_custom_door_types(world, player): type_map[door_kind][dungeon.name].append(d) else: # check if the dest is paired - if d.dest.type in [DoorType.Interior, DoorType.Normal] and door_kind != 'Trap Door': + if d.dest and d.dest.type in [DoorType.Interior, DoorType.Normal] and door_kind != 'Trap Door': type_map[door_kind][dungeon.name].append((d, d.dest)) else: type_map[door_kind][dungeon.name].append(d) @@ -1783,18 +1807,24 @@ def shuffle_door_types(door_type_pools, paths, world, player): start_regions_map[name] = start_regions builder.candidates = BuilderDoorCandidates() + all_custom = defaultdict(list) + if player in world.custom_door_types: + for custom_dict in world.custom_door_types[player].values(): + for dungeon, doors in custom_dict.items(): + all_custom[dungeon].extend(doors) + world.paired_doors[player].clear() - used_doors = shuffle_trap_doors(door_type_pools, paths, start_regions_map, world, player) + used_doors = shuffle_trap_doors(door_type_pools, paths, start_regions_map, all_custom, world, player) # big keys - used_doors = shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, world, player) + used_doors = shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, all_custom, world, player) # small keys - used_doors = shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, world, player) + used_doors = shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, all_custom, world, player) # bombable / dashable - used_doors = shuffle_bomb_dash_doors(door_type_pools, used_doors, start_regions_map, world, player) + used_doors = shuffle_bomb_dash_doors(door_type_pools, used_doors, start_regions_map, all_custom, world, player) # handle paired list -def shuffle_trap_doors(door_type_pools, paths, start_regions_map, world, player): +def shuffle_trap_doors(door_type_pools, paths, start_regions_map, all_custom, world, player): used_doors = set() for pool, door_type_pool in door_type_pools: if world.trap_door_mode[player] != 'oneway': @@ -1805,15 +1835,14 @@ def shuffle_trap_doors(door_type_pools, paths, start_regions_map, world, player) custom_trap_doors = world.custom_door_types[player]['Trap Door'] else: custom_trap_doors = defaultdict(list) - for dungeon in pool: builder = world.dungeon_layouts[player][dungeon] if 'Mire Warping Pool' in builder.master_sector.region_set(): custom_trap_doors[dungeon].append(world.get_door('Mire Warping Pool ES', player)) world.custom_door_types[player]['Trap Door'] = custom_trap_doors find_trappable_candidates(builder, world, player) - if custom_trap_doors[dungeon]: - builder.candidates.trap = filter_key_door_pool(builder.candidates.trap, custom_trap_doors[dungeon]) + if all_custom[dungeon]: + builder.candidates.trap = filter_key_door_pool(builder.candidates.trap, all_custom[dungeon]) remaining -= len(custom_trap_doors[dungeon]) ttl += len(builder.candidates.trap) if ttl == 0: @@ -1865,7 +1894,7 @@ def shuffle_trap_doors(door_type_pools, paths, start_regions_map, world, player) return used_doors -def shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, world, player): +def shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, all_custom, world, player): for pool, door_type_pool in door_type_pools: ttl = 0 suggestion_map, bk_map, flex_map = {}, {}, {} @@ -1878,8 +1907,8 @@ def shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, world, for dungeon in pool: builder = world.dungeon_layouts[player][dungeon] find_big_key_candidates(builder, start_regions_map[dungeon], used_doors, world, player) - if custom_bk_doors[dungeon]: - builder.candidates.big = filter_key_door_pool(builder.candidates.big, custom_bk_doors[dungeon]) + if all_custom[dungeon]: + builder.candidates.big = filter_key_door_pool(builder.candidates.big, all_custom[dungeon]) remaining -= len(custom_bk_doors[dungeon]) ttl += len(builder.candidates.big) if ttl == 0: @@ -1925,7 +1954,7 @@ def shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, world, return used_doors -def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, world, player): +def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, all_custom, world, player): max_computation = 11 # this is around 6 billion worse case factorial don't want to exceed this much for pool, door_type_pool in door_type_pools: ttl = 0 @@ -1943,8 +1972,8 @@ def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, worl builder.total_keys = total_keys find_small_key_door_candidates(builder, start_regions_map[dungeon], used_doors, world, player) custom_doors = 0 - if custom_key_doors[dungeon]: - builder.candidates.small = filter_key_door_pool(builder.candidates.small, custom_key_doors[dungeon]) + if all_custom[dungeon]: + builder.candidates.small = filter_key_door_pool(builder.candidates.small, all_custom[dungeon]) custom_doors = len(custom_key_doors[dungeon]) remaining -= custom_doors builder.key_doors_num = max(0, len(builder.candidates.small) - builder.key_drop_cnt) + custom_doors @@ -1960,13 +1989,10 @@ def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, worl suggested = min(calculated, limit) key_door_num = min(suggested + builder.key_drop_cnt, max_computation) combo_size = ncr(len(builder.candidates.small), key_door_num) - while combo_size > 500000 and suggested > 0: - suggested -= 1 - combo_size = ncr(len(builder.candidates.small), key_door_num) suggestion_map[dungeon] = builder.key_doors_num = key_door_num - remaining -= suggested + builder.key_drop_cnt + remaining -= key_door_num + builder.key_drop_cnt builder.combo_size = combo_size - flex_map[dungeon] = (limit - suggested) if suggested < limit else 0 + flex_map[dungeon] = (limit - key_door_num) if key_door_num < limit else 0 for dungeon in pool: builder = world.dungeon_layouts[player][dungeon] if total_adjustable: @@ -2025,7 +2051,7 @@ def shuffle_small_key_doors(door_type_pools, used_doors, start_regions_map, worl return used_doors -def shuffle_bomb_dash_doors(door_type_pools, used_doors, start_regions_map, world, player): +def shuffle_bomb_dash_doors(door_type_pools, used_doors, start_regions_map, all_custom, world, player): for pool, door_type_pool in door_type_pools: ttl = 0 suggestion_map, bd_map = {}, {} @@ -2042,11 +2068,9 @@ def shuffle_bomb_dash_doors(door_type_pools, used_doors, start_regions_map, worl for dungeon in pool: builder = world.dungeon_layouts[player][dungeon] find_bd_candidates(builder, start_regions_map[dungeon], used_doors, world, player) - if custom_bomb_doors[dungeon]: - builder.candidates.bomb_dash = filter_key_door_pool(builder.candidates.bomb_dash, custom_bomb_doors[dungeon]) + if all_custom[dungeon]: + builder.candidates.bomb_dash = filter_key_door_pool(builder.candidates.bomb_dash, all_custom[dungeon]) remaining_bomb -= len(custom_bomb_doors[dungeon]) - if custom_dash_doors[dungeon]: - builder.candidates.bomb_dash = filter_key_door_pool(builder.candidates.bomb_dash, custom_dash_doors[dungeon]) remaining_dash -= len(custom_dash_doors[dungeon]) ttl += len(builder.candidates.bomb_dash) if ttl == 0: diff --git a/Doors.py b/Doors.py index edc51ac0..0bd13742 100644 --- a/Doors.py +++ b/Doors.py @@ -66,7 +66,7 @@ def create_doors(world, player): create_door(player, 'Hyrule Castle Back Hall Down Stairs', Sprl).dir(Dn, 0x01, 0, HTL).ss(A, 0x2a, 0x00), create_door(player, 'Hyrule Castle Throne Room Tapestry', Lgcl), create_door(player, 'Hyrule Castle Tapestry Backwards', Lgcl), - create_door(player, 'Hyrule Castle Throne Room N', Nrml).dir(No, 0x51, Mid, High).pos(1), + create_door(player, 'Hyrule Castle Throne Room N', Nrml).dir(No, 0x51, Mid, High).pos(0), create_door(player, 'Hyrule Castle Throne Room South Stairs', StrS).dir(So, 0x51, Mid, Low), # hyrule dungeon level diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 9be244f8..e429719a 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -1824,6 +1824,7 @@ def ensure_crystal_switches_reachable(dungeon_map, crystal_switches, polarized_s for name, builder in dungeon_map.items(): if builder.c_switch_present and builder.c_switch_required and not builder.c_locked: invalid_builders.append(builder) + random.shuffle(invalid_builders) while len(invalid_builders) > 0: valid_builders = [] for builder in invalid_builders: @@ -1849,6 +1850,7 @@ def ensure_crystal_switches_reachable(dungeon_map, crystal_switches, polarized_s if eq.c_switch: reachable_crystals[hook_from_door(eq.door)] = True valid_ent_sectors = [] + random.shuffle(entrance_sectors) for entrance_sector in entrance_sectors: other_sectors = [x for x in builder.sectors if x != entrance_sector] reachable, access = is_c_switch_reachable(entrance_sector, reachable_crystals, other_sectors) @@ -1866,7 +1868,12 @@ def ensure_crystal_switches_reachable(dungeon_map, crystal_switches, polarized_s while not valid: if len(candidates) <= 0: raise GenerationException(f'need to provide more sophisticated crystal connection for {entrance_sector}') - sector, which_list = random.choice(list(candidates.items())) + # prioritize candidates + if any(x == 'Crystals' for x in candidates.values()): + cand_list = [x for x in candidates.items() if x[1] == 'Crystals'] + else: + cand_list = list(candidates.items()) + sector, which_list = random.choice(cand_list) del candidates[sector] valid = global_pole.is_valid_choice(dungeon_map, builder, [sector]) if which_list == 'Polarized': diff --git a/Main.py b/Main.py index a39fbe57..7266628d 100644 --- a/Main.py +++ b/Main.py @@ -34,7 +34,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new from source.tools.BPS import create_bps_from_data from source.classes.CustomSettings import CustomSettings -__version__ = '1.2.0.8-u' +__version__ = '1.2.0.9-u' from source.classes.BabelFish import BabelFish diff --git a/README.md b/README.md index 6af52f44..fbe9eed5 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,15 @@ See https://alttpr.com/ for more details on the normal randomizer. 1. [Dungeon Door Shuffle](#door-shuffle) 2. [Intensity Level](#intensity---intensity-number) 3. [Key Drop Shuffle (Legacy)](#key-drop-shuffle-legacy---keydropshuffle) - 4. [Door Type Shuffle](#door-type_shuffle) - 5. [Decouple Doors](#decouple-doors) - 6. [Pottery](#pottery) - 7. [Small Key Shuffle](#small-key-shuffle) - 8. [Shuffle Enemy Key Drops](#shuffle-enemy-key-drops) - 9. [Experimental Features](#experimental-features) - 10. [Crossed Dungeon Specific Settings](#crossed-dungeon-specific-settings) + 4. [Door Type Shuffle](#door-type-shuffle) + 5. [Trap Door Removal](#trap-door-removal) + 6. [Key Logic Algorithm](#key-logic-algorithm) + 7. [Decouple Doors](#decouple-doors) + 8. [Pottery](#pottery) + 9. [Small Key Shuffle](#small-key-shuffle) + 10. [Shuffle Enemy Key Drops](#shuffle-enemy-key-drops) + 11. [Experimental Features](#experimental-features) + 12. [Crossed Dungeon Specific Settings](#crossed-dungeon-specific-settings) 2. [Item Randomization Changes](#item-randomization) 1. [New "Items"](#new-items) 2. [Shopsanity](#shopsanity) @@ -40,7 +42,7 @@ See https://alttpr.com/ for more details on the normal randomizer. ### Feedback and Bug Reports -Please just DM me on discord for now. I (Aerinon) can be found at the [ALTTP Randomizer discord](https://discordapp.com/invite/alttprandomizer). +You can use the #bug-reports or #door-rando channel at the [ALTTP Randomizer discord](https://discordapp.com/invite/alttprandomizer) to provide feedback or bug reports. ### Installation @@ -73,15 +75,14 @@ Most of these apply only when the door shuffle is not vanilla. ### Starting Item -You start with a “Mirror Scroll”, a dumbed-down mirror that only works in dungeons, not the overworld and can’t erase blocks like the Mirror. +You start with a “Mirror Scroll” (it looks like a map), a dumbed-down mirror that only works in dungeons, not the overworld, and can’t erase blocks like the Mirror. ### Navigation -* The Pinball Room’s trap door can be removed in the case where it is required to go through to get to the back of Skull Woods. * Holes in Mire Torches Top and Mire Torches Bottom fall through to rooms below (you only need fire to get the chest) * You can Hookshot from the left Mire wooden Bridge to the right one. * In the PoD Arena, you can bonk with Boots between the two blue crystal barriers against the ladder to reach the Arena Bridge chest and door. (Bomb Jump also possible but not in logic - Boots are required) -* Flooded Rooms in Swamp can be traversed backward and may be required. +* Flooded Rooms in Swamp can be traversed backward and may be required. The flippers are needed to get out of the water. ### Other Logic diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0328487c..d5a492b1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -108,6 +108,19 @@ These are now independent of retro mode and have three options: None, Random, an * Bonk Fairy (Dark) # Bug Fixes and Notes +* 1.2.0.9-u + * Disallowed standard exits (due to ER) are now graphically half blocked instead of missing + * Graphical issues with Sanctuary and Swamp Hub lobbies are fixed + * Fixes an issue surrounding door state and decoupled doors leading to blocked doors + * Customizer improvements: + * Better logic around customized lobbies + * Better logic around customized door types + * Fix to key doors that was causing extra key doors + * Generation improvement around crystal switches + * Fix bug in dungeon_only that wasn't using pot key locations (known issue still exists in pottery modes) + * Fixes for multiworld: + * Fixes an issue when keys are found in own dungeon for another player when using the bizhawk plugin. + * Fixes an issue with absorbables for another player also being received by the player picking it up. * 1.2.0.8-u * New Features: trap_door_mode and key_logic_algorithm * Change S&Q in door shuffle + standard during escape to spawn as Uncle diff --git a/Rom.py b/Rom.py index 3e353b3a..1dd05a8f 100644 --- a/Rom.py +++ b/Rom.py @@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127 JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '6f64fcea052e37b39d6b4bb24ae2f548' +RANDOMIZERBASEHASH = '67279b96a589f09e3ba8393a5bc5f071' class JsonRom(object): @@ -572,7 +572,7 @@ class Sprite(object): def handle_native_dungeon(location, itemid): # Keys in their native dungeon should use the original item code for keys - if location.parent_region.dungeon: + if location.parent_region.dungeon and location.player == location.item.player: if location.parent_region.dungeon.name == location.item.dungeon: if location.item.bigkey: return 0x32 diff --git a/RoomData.py b/RoomData.py index d8d3a82a..13339364 100644 --- a/RoomData.py +++ b/RoomData.py @@ -244,7 +244,9 @@ def create_rooms(world, player): # Room(player, 0xff, 0x52c9a).door(Position.InteriorW, DoorKind.Bombable).door(Position.InteriorE, DoorKind.Bombable).door(Position.SouthE, DoorKind.CaveEntrance), ] # fix some wonky things - world.get_room(0x51, player).change(1, DoorKind.Normal) # fix the dungeon changer + # should I put back the dungeon changer for certain logic - like no logic? maybe in basic + if world.doorShuffle[player] != 'vanilla': + world.get_room(0x51, player).delete(1) # remove the dungeon changer world.get_room(0x60, player).swap(2, 4) # puts the exit at pos 2 - enables pos 3 world.get_room(0x61, player).swap(1, 6) # puts the WN door at pos 1 - enables it world.get_room(0x61, player).swap(5, 6) # puts the Incognito Entrance at the end, so it can be deleted diff --git a/data/base2current.bps b/data/base2current.bps index 93e4cfd280c90a77261c2e40c3ac5c95ef7ece69..be3d11ea1f94a232964df475e95479bc094ec0cc 100644 GIT binary patch delta 3181 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 H2Alo^TSOZ1 delta 2862 zcmW+&dstM}7C-CE6BypF;UOFjh>Qx3n2LxD>@#qHVI1ah zgbj?u@R()>!T~1pS{X=c(*3G2qpwT(-NNwd+_Yn1b^YpckN1zg_u6Z%z4ki4-&yON zFRx3kO-js-FyCUhw)6URng)U-&d#14WX`siOWrCPVw%;>9rQ?((@bjo&!(bgdS{ap z0oR>4ZJ}FDPJh)EBYl0yb;xY-(@LZB*}675-{>3!jrxXu(dAwWS?FPI1iIlO_cBSx z)|_yrH4e}%6L$Kq-vk$5tLZJMH_}C%ja(HIe8TA%;-xK>IX^v(^v(57vCc?e;O>$! z%TLrg#mGqC{JeLxjhL1)1FqRy_qh3=oiHp>C7+fTY?3QCb@Uyxgi~= zUH(d>5BS7hjP#4RKjbEOi#rie9CF?0yy&21O-{d`{>{k!8n9VDHstdE%}8J4HU|C< zUhfk@e}S020wI2dm67CZ6wq3E+Lb6g!i(HTidV@eP2Q~F1`=G}v!U;b;f}W~>bwdl z@521e0GV9X>LN1F1hwz%VlkY7 zg+?*Esnn|cvRQlj1+hMV&Ae2GsQSU3iAV(GW51u zXK}QF#AQ-QPvvWmpg2driXi{pHzGq$im!F_=iHVPZ2u8Nmh@Yf81cJO*c!3&U(BtR z1M3WPk(l~`WeI8m$IBoW#CW?57O#s@{1A=CkAYd@4g)04xxJ6jP}}Rq2|jIH#wU&g zuN)T-D#uBmluIz^L&in8^u#%zcucL-jHz=p)C6l*;Z={~&t#yAURTh{95m_LsvgFx z2b_&MH!Q*mIi%0aTM&+zpiUhVYSJ;_)G;K znc>KGWInEvL!BhW*b~`=WdX2IF`(xvJGlLEZF(vWuMU9Rd3lh4?hD998XSG=S*bt@ zxjtuF$_KO3K;@o3w$BcxcT6&D4}cBi6P5T*03?R3FabJ4u`)YQD-6`n4LB}PU?mfO z9tio%vn?#`oLIkyk7 z;?_XrtKvTDDpo6imIe1PB3p3}-l>4a5Qhy4$lIHZPfUQn?u_6{QJg|p__x;^LFctB;vNe6oJ_AN#{N?5;??ur3fC3 zWFfNi4V)MZ$%=z>!cpg@#Bjt_4B)lFFehTo+ty7+%lIte3uuo%qT^mG$8Q9~(S)e| zjojQ_?L)$wBylvV3bY<6a^gpJwc}W<2!UhpIBpDqCDm_6KYo{4*B6r2%Is;`O|xBu zuh{c|ck(o@d_{(sgw8$X_Nzefk2wg{EHN(FSCqQtgx1rDiNoBz1>OIhy>)AZ=bhuT zv;3S(cHg}zYUEemd5Q1^wH!WbJ~Kv@=4i(#^mVCH+fB4@t1@?x?l7%8lx?={fMM(l zfjI(ncSB%V_2?`wqrSD;Nuks;xbazD*?#yr%YOv5ggRysl;YAw@cIYwgJvBx^ycb>l_SX>T*s^nPGArm%&)aQ^`#ei(e z6t07$S~-?*DDcrazqN@qI5P}V65nU!Cf$IUFiMz_<1!?vH0sq*b-P2CKL`2_{GgQI|_0l zR$(qo=a3OTQPC>w4$tu9;i@R0s58w@W%bo&r)xyRJKNU8T)E=DpC+t5OLO=xIi*8W8NYZMd&9&NF$KyD$d92Z0b1+n<0Xov?9u8M}F zxWhQ($OG-<2-WKBqqejPV&i&lp^lSaZ#1YN7hjKt{Oa^M?fRGujcHu?m$}?Ki4R^F z3R>ScGB|TQzEhYUyoR=__4-|Q!c)r-PF>p(4Qn?xiSf&u^m>xdZqF5a27D?HKkK|? z=fgZdbMqZ~xo5^y=k|q9sok;c^LM`^@qVd|9a9ILEZEE7*oYBRNiOPqYAd5xNQq$E zGjCkPzr;e8sA9SIY?=}ZWlxr~^2uF|fqf%)PP^E^CjKh*)qr{Nh*DFX7U)w+0y_n1 zN^K)VQOM{cew?`yq&5C5Hp62;7bVMG79 zEkpepYA;@y3N4At54L5ZDfI)PAjYWE&B3r@=}xdG}dQ8q8TQS6-Xk{O94@?Ee6hg`^Sy diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 8c85bfdd..0252673f 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -163,7 +163,7 @@ def create_item_pool_config(world): dungeon_set = (mode_grouping['Big Chests'] + mode_grouping['Dungeon Trash'] + mode_grouping['Big Keys'] + mode_grouping['Heart Containers'] + mode_grouping['GT Trash'] + mode_grouping['Small Keys'] + mode_grouping['Compasses'] + mode_grouping['Maps'] + mode_grouping['Key Drops'] + - mode_grouping['Big Key Drops']) + mode_grouping['Pot Keys'] + mode_grouping['Big Key Drops']) for player in range(1, world.players + 1): config.item_pool[player] = determine_major_items(world, player) config.location_groups[0].locations = set(dungeon_set) diff --git a/test/customizer/multi_test.yaml b/test/customizer/multi_test.yaml new file mode 100644 index 00000000..360499ef --- /dev/null +++ b/test/customizer/multi_test.yaml @@ -0,0 +1,23 @@ +meta: + players: 2 +settings: + 1: + pottery: cavekeys + keysanity: True + 2: + keysanity: True +placements: + 1: + Sanctuary: Small Key (Escape)#2 + 'Links House Pot #3': Rupees (20)#2 +start_inventory: + 1: + - Pegasus Boots + - Ocarina (Activated) + - Magic Mirror + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container + - Red Mail + - Golden Sword \ No newline at end of file diff --git a/test/customizer/test_stuff.yaml b/test/customizer/test_stuff.yaml index 552d5490..313d35d2 100644 --- a/test/customizer/test_stuff.yaml +++ b/test/customizer/test_stuff.yaml @@ -1,28 +1,46 @@ meta: players: 1 - race: true + algorithm: dungeon_only settings: 1: - shopsanity: true + goal: dungeons pseudoboots: true - goal: crystals - crystals_gt: random - keysanity: true door_shuffle: crossed + decoupledoors: true intensity: 3 - door_type_mode: big - pottery: keys - dropshuffle: true + door_type_mode: all experimental: true dungeon_counters: 'on' - hints: true - msu_resume: true - collection_rate: true - quickswap: true -start_inventory: + compassshuffle: true + mapshuffle: true + keydropshuffle: true +doors: 1: - - Pegasus Boots - - Ocarina (Activated) - - Magic Mirror - - Boss Heart Container - - Blue Mail \ No newline at end of file + lobbies: + Hyrule Castle South: Thieves Lobby S + doors: + Thieves Lobby NE Edge: + dest: Hyrule Castle Throne Room South Stairs + one-way: True + Hyrule Castle Throne Room South Stairs: + dest: Desert Beamos Hall NE + one-way: True + Desert Beamos Hall NE: + dest: Ice Spike Cross SE + type: Key Door + one-way: True + Ice Spike Cross SE: + dest: Thieves Lobby NE Edge + one-way: True + Hyrule Castle Throne Room N: + dest: Ice Firebar Down Ladder + type: Key Door + Swamp Lobby S: + dest: Thieves Lobby N Edge + type: Bomb Door + Thieves Lobby E: Thieves Compass Room W + Thieves Compass Room NW Edge: Ice Tall Hint SE + Thieves Compass Room N Edge: PoD Bow Statue Down Ladder + + +