Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2021-08-31 19:49:52 -05:00
16 changed files with 130 additions and 42 deletions
+2 -2
View File
@@ -87,6 +87,7 @@ class World(object):
self._room_cache = {} self._room_cache = {}
self.dungeon_layouts = {} self.dungeon_layouts = {}
self.inaccessible_regions = {} self.inaccessible_regions = {}
self.enabled_entrances = {}
self.key_logic = {} self.key_logic = {}
self.pool_adjustment = {} self.pool_adjustment = {}
self.key_layout = defaultdict(dict) self.key_layout = defaultdict(dict)
@@ -153,6 +154,7 @@ class World(object):
set_player_attr('prizes', {'pull': [0, 0, 0], 'crab': [0, 0], 'stun': 0, 'fish': 0}) set_player_attr('prizes', {'pull': [0, 0, 0], 'crab': [0, 0], 'stun': 0, 'fish': 0})
set_player_attr('exp_cache', defaultdict(dict)) set_player_attr('exp_cache', defaultdict(dict))
set_player_attr('enabled_entrances', {})
def get_name_string_for_object(self, obj): def get_name_string_for_object(self, obj):
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})' return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
@@ -485,8 +487,6 @@ class World(object):
return True return True
state = starting_state.copy() state = starting_state.copy()
else: else:
if self.has_beaten_game(self.state):
return True
state = CollectionState(self) state = CollectionState(self)
if self.has_beaten_game(state): if self.has_beaten_game(state):
+3 -3
View File
@@ -181,11 +181,11 @@ def place_bosses(world, player):
logging.getLogger('').debug('Bosses chosen %s', bosses) logging.getLogger('').debug('Bosses chosen %s', bosses)
random.shuffle(bosses)
for [loc, level] in boss_locations: for [loc, level] in boss_locations:
loc_text = loc + (' ('+level+')' if level else '') loc_text = loc + (' ('+level+')' if level else '')
boss = next((b for b in bosses if can_place_boss(world, player, b, loc, level)), None) try:
if not boss: boss = random.choice([b for b in bosses if can_place_boss(world, player, b, loc, level)])
except IndexError:
raise FillError('Could not place boss for location %s' % loc_text) raise FillError('Could not place boss for location %s' % loc_text)
bosses.remove(boss) bosses.remove(boss)
+4
View File
@@ -1,5 +1,9 @@
# Changelog # Changelog
### 0.1.9.1
- Fixed logic issue with leaving IP entrance not requiring flippers
- ~~Merged DR v0.5.1.1 - Map Indicator Fix/Boss Shuffle Bias/Shop Hints~~
### 0.1.9.0 ### 0.1.9.0
- Expanded Crossed OW to four separate options, see Readme for details - Expanded Crossed OW to four separate options, see Readme for details
- Crossed OW will now play a short SFX when changing worlds - Crossed OW will now play a short SFX when changing worlds
+33 -8
View File
@@ -751,7 +751,7 @@ def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map,
def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player): def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player):
entrances_map, potentials, connections = connections_tuple entrances_map, potentials, connections = connections_tuple
enabled_entrances = {} enabled_entrances = world.enabled_entrances[player] = {}
sector_queue = deque(dungeon_builders.values()) sector_queue = deque(dungeon_builders.values())
last_key, loops = None, 0 last_key, loops = None, 0
logging.getLogger('').info(world.fish.translate("cli", "cli", "generating.dungeon")) logging.getLogger('').info(world.fish.translate("cli", "cli", "generating.dungeon"))
@@ -1456,6 +1456,14 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True
random.shuffle(sample_list) random.shuffle(sample_list)
proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num) proposal = kth_combination(sample_list[itr], builder.candidates, builder.key_doors_num)
# eliminate start region if portal marked as destination
excluded = {}
for region in start_regions:
portal = next((x for x in world.dungeon_portals[player] if x.door.entrance.parent_region == region), None)
if portal and portal.destination:
excluded[region] = None
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) key_layout = build_key_layout(builder, start_regions, proposal, world, player)
while not validate_key_layout(key_layout, world, player): while not validate_key_layout(key_layout, world, player):
itr += 1 itr += 1
@@ -1914,14 +1922,18 @@ def check_required_paths(paths, world, player):
if dungeon_name in world.dungeon_layouts[player].keys(): if dungeon_name in world.dungeon_layouts[player].keys():
builder = world.dungeon_layouts[player][dungeon_name] builder = world.dungeon_layouts[player][dungeon_name]
if len(paths[dungeon_name]) > 0: if len(paths[dungeon_name]) > 0:
states_to_explore = defaultdict(list) states_to_explore = {}
for path in paths[dungeon_name]: for path in paths[dungeon_name]:
if type(path) is tuple: if type(path) is tuple:
states_to_explore[tuple([path[0]])] = path[1] states_to_explore[tuple([path[0]])] = (path[1], 'any')
else: else:
states_to_explore[tuple(builder.path_entrances)].append(path) common_starts = tuple(builder.path_entrances)
if common_starts not in states_to_explore:
states_to_explore[common_starts] = ([], 'all')
states_to_explore[common_starts][0].append(path)
cached_initial_state = None cached_initial_state = None
for start_regs, dest_regs in states_to_explore.items(): for start_regs, info in states_to_explore.items():
dest_regs, path_type = info
if type(dest_regs) is not list: if type(dest_regs) is not list:
dest_regs = [dest_regs] dest_regs = [dest_regs]
check_paths = convert_regions(dest_regs, world, player) check_paths = convert_regions(dest_regs, world, player)
@@ -1938,11 +1950,17 @@ def check_required_paths(paths, world, player):
cached_initial_state = state cached_initial_state = state
else: else:
state = cached_initial_state state = cached_initial_state
valid, bad_region = check_if_regions_visited(state, check_paths) if path_type == 'any':
valid, bad_region = check_if_any_regions_visited(state, check_paths)
else:
valid, bad_region = check_if_all_regions_visited(state, check_paths)
if not valid: if not valid:
if check_for_pinball_fix(state, bad_region, world, player): if check_for_pinball_fix(state, bad_region, world, player):
explore_state(state, world, player) explore_state(state, world, player)
valid, bad_region = check_if_regions_visited(state, check_paths) if path_type == 'any':
valid, bad_region = check_if_any_regions_visited(state, check_paths)
else:
valid, bad_region = check_if_all_regions_visited(state, check_paths)
if not valid: if not valid:
raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name)) raise Exception('%s cannot reach %s' % (dungeon_name, bad_region.name))
@@ -1982,7 +2000,7 @@ def explore_state_not_inaccessible(state, world, player):
state.add_all_doors_check_unattached(connect_region, world, player) state.add_all_doors_check_unattached(connect_region, world, player)
def check_if_regions_visited(state, check_paths): def check_if_any_regions_visited(state, check_paths):
valid = False valid = False
breaking_region = None breaking_region = None
for region_target in check_paths: for region_target in check_paths:
@@ -1994,6 +2012,13 @@ def check_if_regions_visited(state, check_paths):
return valid, breaking_region return valid, breaking_region
def check_if_all_regions_visited(state, check_paths):
for region_target in check_paths:
if not state.visited_at_all(region_target):
return False, region_target
return True, None
def check_for_pinball_fix(state, bad_region, world, player): def check_for_pinball_fix(state, bad_region, world, player):
pinball_region = world.get_region('Skull Pinball', player) pinball_region = world.get_region('Skull Pinball', player)
# todo: lobby shuffle # todo: lobby shuffle
+2 -1
View File
@@ -109,7 +109,8 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
p_region = portal.door.entrance.connected_region p_region = portal.door.entrance.connected_region
access_region = next(x.parent_region for x in p_region.entrances access_region = next(x.parent_region for x in p_region.entrances
if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]) if x.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld])
if access_region.name in world.inaccessible_regions[player]: if (access_region.name in world.inaccessible_regions[player] and
region.name not in world.enabled_entrances[player]):
excluded[region] = None excluded[region] = None
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()] entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
doors_to_connect = {} doors_to_connect = {}
+32 -12
View File
@@ -375,6 +375,38 @@ flexible_starts = {
'Skull Woods': ['Skull Left Drop', 'Skull Pinball'] 'Skull Woods': ['Skull Left Drop', 'Skull Pinball']
} }
class DungeonInfo:
def __init__(self, free, keys, bk, map, compass, bk_drop, drops, prize=None):
# todo reduce static maps ideas: prize, bk_name, sm_name, cmp_name, map_name):
self.free_items = free
self.key_num = keys
self.bk_present = bk
self.map_present = map
self.compass_present = compass
self.bk_drops = bk_drop
self.key_drops = drops
self.prize = prize
dungeon_table = {
'Hyrule Castle': DungeonInfo(6, 1, False, True, False, True, 3, None),
'Eastern Palace': DungeonInfo(3, 0, True, True, True, False, 2, 'Eastern Palace - Prize'),
'Desert Palace': DungeonInfo(2, 1, True, True, True, False, 3, 'Desert Palace - Prize'),
'Tower of Hera': DungeonInfo(2, 1, True, True, True, False, 0, 'Tower of Hera - Prize'),
'Agahnims Tower': DungeonInfo(0, 2, False, False, False, False, 2, None),
'Palace of Darkness': DungeonInfo(5, 6, True, True, True, False, 0, 'Palace of Darkness - Prize'),
'Swamp Palace': DungeonInfo(6, 1, True, True, True, False, 5, 'Swamp Palace - Prize'),
'Skull Woods': DungeonInfo(2, 3, True, True, True, False, 2, 'Skull Woods - Prize'),
'Thieves Town': DungeonInfo(4, 1, True, True, True, False, 2, "Thieves' Town - Prize"),
'Ice Palace': DungeonInfo(3, 2, True, True, True, False, 4, 'Ice Palace - Prize'),
'Misery Mire': DungeonInfo(2, 3, True, True, True, False, 3, 'Misery Mire - Prize'),
'Turtle Rock': DungeonInfo(5, 4, True, True, True, False, 2, 'Turtle Rock - Prize'),
'Ganons Tower': DungeonInfo(20, 4, True, True, True, False, 4, None),
}
dungeon_keys = { dungeon_keys = {
'Hyrule Castle': 'Small Key (Escape)', 'Hyrule Castle': 'Small Key (Escape)',
'Eastern Palace': 'Small Key (Eastern Palace)', 'Eastern Palace': 'Small Key (Eastern Palace)',
@@ -407,18 +439,6 @@ dungeon_bigs = {
'Ganons Tower': 'Big Key (Ganons Tower)' 'Ganons Tower': 'Big Key (Ganons Tower)'
} }
dungeon_prize = {
'Eastern Palace': 'Eastern Palace - Prize',
'Desert Palace': 'Desert Palace - Prize',
'Tower of Hera': 'Tower of Hera - Prize',
'Palace of Darkness': 'Palace of Darkness - Prize',
'Swamp Palace': 'Swamp Palace - Prize',
'Skull Woods': 'Skull Woods - Prize',
'Thieves Town': 'Thieves Town - Prize',
'Ice Palace': 'Ice Palace - Prize',
'Misery Mire': 'Misery Mire - Prize',
'Turtle Rock': 'Turtle Rock - Prize',
}
dungeon_hints = { dungeon_hints = {
'Hyrule Castle': 'in Hyrule Castle', 'Hyrule Castle': 'in Hyrule Castle',
+10
View File
@@ -606,6 +606,7 @@ def customize_shops(world, player):
upgrade.location = loc upgrade.location = loc
change_shop_items_to_rupees(world, player, shops_to_customize) change_shop_items_to_rupees(world, player, shops_to_customize)
balance_prices(world, player) balance_prices(world, player)
check_hints(world, player)
def randomize_price(price): def randomize_price(price):
@@ -709,6 +710,15 @@ def balance_prices(world, player):
# loc.parent_region.shop.inventory[slot]['price'] = new_price # loc.parent_region.shop.inventory[slot]['price'] = new_price
def check_hints(world, player):
if world.shuffle[player] in ['simple', 'restricted', 'full', 'crossed', 'insanity']:
for shop, location_list in shop_to_location_table.items():
if shop in ['Capacity Upgrade', 'Light World Death Mountain Shop', 'Potion Shop']:
continue # near the queen, near potions, and near 7 chests are fine
for loc_name in location_list: # other shops are indistinguishable in ER
world.get_location(loc_name, player).hint_text = f'for sale'
repeatable_shop_items = ['Single Arrow', 'Arrows (10)', 'Bombs (3)', 'Bombs (10)', 'Red Potion', 'Small Heart', repeatable_shop_items = ['Single Arrow', 'Arrows (10)', 'Bombs (3)', 'Bombs (10)', 'Red Potion', 'Small Heart',
'Blue Shield', 'Red Shield', 'Bee', 'Small Key (Universal)', 'Blue Potion', 'Green Potion'] 'Blue Shield', 'Red Shield', 'Bee', 'Small Key (Universal)', 'Blue Potion', 'Green Potion']
+17 -6
View File
@@ -4,7 +4,7 @@ from collections import defaultdict, deque
from BaseClasses import DoorType, dungeon_keys, KeyRuleType, RegionType from BaseClasses import DoorType, dungeon_keys, KeyRuleType, RegionType
from Regions import dungeon_events from Regions import dungeon_events
from Dungeons import dungeon_keys, dungeon_bigs, dungeon_prize from Dungeons import dungeon_keys, dungeon_bigs, dungeon_table
from DungeonGenerator import ExplorationState, special_big_key_doors from DungeonGenerator import ExplorationState, special_big_key_doors
@@ -282,6 +282,12 @@ def analyze_dungeon(key_layout, world, player):
if not big_avail: if not big_avail:
if chest_keys == non_big_locs and chest_keys > 0 and available <= possible_smalls and not avail_bigs: if chest_keys == non_big_locs and chest_keys > 0 and available <= possible_smalls and not avail_bigs:
key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations)) key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations))
# note to self: this is due to the enough_small_locations function in validate_key_layout_sub_loop
# I don't like this exception here or there
elif available <= possible_smalls and avail_bigs and non_big_locs > 0:
max_ctr = find_max_counter(key_layout)
bk_lockdown = [x for x in max_ctr.free_locations if x not in key_counter.free_locations]
key_logic.bk_restricted.update(filter_big_chest(bk_lockdown))
# try to relax the rules here? - smallest requirement that doesn't force a softlock # try to relax the rules here? - smallest requirement that doesn't force a softlock
child_queue = deque() child_queue = deque()
for child in key_counter.child_doors.keys(): for child in key_counter.child_doors.keys():
@@ -1387,7 +1393,7 @@ def validate_key_layout(key_layout, world, player):
dungeon_entrance, portal_door = find_outside_connection(region) dungeon_entrance, portal_door = find_outside_connection(region)
if (len(key_layout.start_regions) > 1 and dungeon_entrance and if (len(key_layout.start_regions) > 1 and dungeon_entrance and
dungeon_entrance.name in ['Ganons Tower', 'Pyramid Fairy'] dungeon_entrance.name in ['Ganons Tower', 'Pyramid Fairy']
and key_layout.key_logic.dungeon in dungeon_prize): and dungeon_table[key_layout.key_logic.dungeon].prize):
state.append_door_to_list(portal_door, state.prize_doors) state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = True key_layout.prize_relevant = True
@@ -1550,7 +1556,7 @@ def create_key_counters(key_layout, world, player):
dungeon_entrance, portal_door = find_outside_connection(region) dungeon_entrance, portal_door = find_outside_connection(region)
if (len(key_layout.start_regions) > 1 and dungeon_entrance and if (len(key_layout.start_regions) > 1 and dungeon_entrance and
dungeon_entrance.name in ['Ganons Tower', 'Pyramid Fairy'] dungeon_entrance.name in ['Ganons Tower', 'Pyramid Fairy']
and key_layout.key_logic.dungeon in dungeon_prize): and dungeon_table[key_layout.key_logic.dungeon].prize):
state.append_door_to_list(portal_door, state.prize_doors) state.append_door_to_list(portal_door, state.prize_doors)
state.prize_door_set[portal_door] = dungeon_entrance state.prize_door_set[portal_door] = dungeon_entrance
key_layout.prize_relevant = True key_layout.prize_relevant = True
@@ -1619,8 +1625,13 @@ def can_open_door_by_counter(door, counter: KeyCounter, layout, world, player):
# ttl_small_key_only = len(counter.key_only_locations) # ttl_small_key_only = len(counter.key_only_locations)
return cnt_avail_small_locations_by_ctr(ttl_locations, counter, layout, world, player) > 0 return cnt_avail_small_locations_by_ctr(ttl_locations, counter, layout, world, player) > 0
elif door.bigKey: elif door.bigKey:
if counter.big_key_opened:
return False
if layout.big_key_special:
return any(x for x in counter.other_locations.keys() if x.forced_item and x.forced_item.bigkey)
else:
available_big_locations = cnt_avail_big_locations_by_ctr(ttl_locations, counter, layout, world, player) available_big_locations = cnt_avail_big_locations_by_ctr(ttl_locations, counter, layout, world, player)
return not counter.big_key_opened and available_big_locations > 0 and not layout.big_key_special return available_big_locations > 0
else: else:
return True return True
@@ -1975,8 +1986,8 @@ def validate_key_placement(key_layout, world, player):
len(counter.key_only_locations) + keys_outside len(counter.key_only_locations) + keys_outside
if key_layout.prize_relevant: if key_layout.prize_relevant:
found_prize = any(x for x in counter.important_locations if '- Prize' in x.name) found_prize = any(x for x in counter.important_locations if '- Prize' in x.name)
if not found_prize and key_layout.sector.name in dungeon_prize: if not found_prize and dungeon_table[key_layout.sector.name].prize:
prize_loc = world.get_location(dungeon_prize[key_layout.sector.name], player) prize_loc = world.get_location(dungeon_table[key_layout.sector.name].prize, player)
# todo: pyramid fairy only care about crystals 5 & 6 # todo: pyramid fairy only care about crystals 5 & 6
found_prize = 'Crystal' not in prize_loc.item.name found_prize = 'Crystal' not in prize_loc.item.name
else: else:
+1 -1
View File
@@ -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 ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
from Utils import output_path, parse_player_names from Utils import output_path, parse_player_names
__version__ = '0.5.1.0-u' __version__ = '0.5.1.1-u'
from source.classes.BabelFish import BabelFish from source.classes.BabelFish import BabelFish
+1 -1
View File
@@ -2,7 +2,7 @@ import RaceRandom as random, logging, copy
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel
__version__ = '0.1.9.0-u' __version__ = '0.1.9.1-u'
def link_overworld(world, player): def link_overworld(world, player):
# setup mandatory connections # setup mandatory connections
+11
View File
@@ -15,6 +15,17 @@ CLI: ```--bombbag```
# Bug Fixes and Notes. # Bug Fixes and Notes.
* 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)
* Fix for bias in boss shuffle "full"
* Fix for certain lone big chests in keysanity (allowed you to get contents without big key)
* Fix for pinball checking
* Fix for multi-entrance dungeons
* 2 fixes for big key placement logic
* ensure big key is placed early if the validator assumes it)
* Open big key doors appropriately when generating rules and big key is forced somewhere
* Updated cutoff entrances for intensity 3
* 0.5.1.0 * 0.5.1.0
* Large logic refactor introducing a new method of key logic * Large logic refactor introducing a new method of key logic
* Some performance optimization * Some performance optimization
+3 -3
View File
@@ -1536,9 +1536,9 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Red Shield Shop - Left': (None, None, False, 'for sale as a curiosity'), 'Red Shield Shop - Left': (None, None, False, 'for sale as a curiosity'),
'Red Shield Shop - Middle': (None, None, False, 'for sale as a curiosity'), 'Red Shield Shop - Middle': (None, None, False, 'for sale as a curiosity'),
'Red Shield Shop - Right': (None, None, False, 'for sale as a curiosity'), 'Red Shield Shop - Right': (None, None, False, 'for sale as a curiosity'),
'Potion Shop - Left': (None, None, False, 'for sale near the witch'), 'Potion Shop - Left': (None, None, False, 'for sale near potions'),
'Potion Shop - Middle': (None, None, False, 'for sale near the witch'), 'Potion Shop - Middle': (None, None, False, 'for sale near potions'),
'Potion Shop - Right': (None, None, False, 'for sale near the witch'), 'Potion Shop - Right': (None, None, False, 'for sale near potions'),
} }
lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int}
+1 -1
View File
@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '712ebda1ef6818c59cde0371b5a4e4a9' RANDOMIZERBASEHASH = '9e8b765fca0f00b54e5be78bb6eb62a3'
class JsonRom(object): class JsonRom(object):
+6 -2
View File
@@ -828,6 +828,7 @@ def default_rules(world, player):
set_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Ice Lake Moat Water Entry', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Shopping Mall SW', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Shopping Mall SW', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Bomber Corner Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Bomber Corner Water Drop', player), lambda state: state.has('Flippers', player))
set_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), lambda state: state.has('Flippers', player))
@@ -1324,6 +1325,7 @@ def ow_bunny_rules(world, player):
add_bunny_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), player) add_bunny_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), player)
add_bunny_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), player) add_bunny_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), player)
add_bunny_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), player) add_bunny_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), player)
add_bunny_rule(world.get_entrance('Ice Lake Moat Water Entry', player), player)
add_bunny_rule(world.get_entrance('Shopping Mall SW', player), player) add_bunny_rule(world.get_entrance('Shopping Mall SW', player), player)
add_bunny_rule(world.get_entrance('Bomber Corner Water Drop', player), player) add_bunny_rule(world.get_entrance('Bomber Corner Water Drop', player), player)
add_bunny_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), player) add_bunny_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), player)
@@ -2338,9 +2340,11 @@ def add_key_logic_rules(world, player):
forbid_item(location, d_logic.small_key_name, player) forbid_item(location, d_logic.small_key_name, player)
for door in d_logic.bk_doors: for door in d_logic.bk_doors:
add_rule(world.get_entrance(door.name, player), create_rule(d_logic.bk_name, player)) add_rule(world.get_entrance(door.name, player), create_rule(d_logic.bk_name, player))
if len(d_logic.bk_doors) > 0 or len(d_logic.bk_chests) > 1:
for chest in d_logic.bk_chests: for chest in d_logic.bk_chests:
add_rule(world.get_location(chest.name, player), create_rule(d_logic.bk_name, 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)
if world.retro[player]: if world.retro[player]:
for d_name, layout in world.key_layout[player].items(): for d_name, layout in world.key_layout[player].items():
for door in layout.flat_prop: for door in layout.flat_prop:
+2
View File
@@ -582,6 +582,8 @@ dw $00bc, $00a2, $00a3, $00c2, $001a, $0049, $0014, $008c
dw $009f, $0066, $005d, $00a8, $00a9, $00aa, $00b9, $0052 dw $009f, $0066, $005d, $00a8, $00a9, $00aa, $00b9, $0052
; HC West Hall, TR Dash Bridge, TR Hub, Pod Arena, GT Petting Zoo, Ice Spike Cross ; HC West Hall, TR Dash Bridge, TR Hub, Pod Arena, GT Petting Zoo, Ice Spike Cross
dw $0050, $00c5, $00c6, $0009, $0003, $002a, $007d, $005e dw $0050, $00c5, $00c6, $0009, $0003, $002a, $007d, $005e
; Sewer Drop, Mire Cross, GT Crystal Circles
dw $0011, $00b2, $003d
dw $ffff dw $ffff
; dungeon tables ; dungeon tables
Binary file not shown.