Swordless rom writes and inverted fixes
* Update Swordless rom writes * Remove swordless as possible mode in ItemList.py * Fix inverted HC Ledge access Added collection state helper methods for determining lw/dw access Restricted locations where Link's House can be in inverted Dark Sanc and Link's House can no longer be at the back of Skull Woods Fixed minor error in inverted bunny rules * Update Link's House Shuffling in inverted insanity * More isolated entrances not to put Link's House at * Fix Link's House in dungeons shuffles * More dungeons shuffle stuff I forgot
This commit is contained in:
parent
99a4ea17b0
commit
2859acef7d
|
@ -492,6 +492,16 @@ class CollectionState(object):
|
|||
|
||||
return region.is_light_world if self.world.mode != 'inverted' else region.is_dark_world
|
||||
|
||||
def can_reach_light_world(self, player):
|
||||
if True in [i.is_light_world for i in self.reachable_regions[player]]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_reach_dark_world(self, player):
|
||||
if True in [i.is_dark_world for i in self.reachable_regions[player]]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def has_misery_mire_medallion(self, player):
|
||||
return self.has(self.world.required_medallions[player][0], player)
|
||||
|
||||
|
|
|
@ -1184,7 +1184,8 @@ def link_inverted_entrances(world, player):
|
|||
connect_two_way(world, entrance2, exit2, player)
|
||||
|
||||
# place links house
|
||||
links_house = random.choice(list(bomb_shop_doors + blacksmith_doors))
|
||||
links_house_doors = [i for i in bomb_shop_doors + blacksmith_doors if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
|
||||
links_house = random.choice(list(links_house_doors))
|
||||
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
|
||||
if links_house in bomb_shop_doors:
|
||||
bomb_shop_doors.remove(links_house)
|
||||
|
@ -1256,7 +1257,8 @@ def link_inverted_entrances(world, player):
|
|||
door_targets = list(Inverted_Single_Cave_Targets)
|
||||
|
||||
# place links house
|
||||
links_house = random.choice(list(lw_entrances + dw_entrances + lw_must_exits))
|
||||
links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
|
||||
links_house = random.choice(list(links_house_doors))
|
||||
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
|
||||
if links_house in lw_entrances:
|
||||
lw_entrances.remove(links_house)
|
||||
|
@ -1386,8 +1388,8 @@ def link_inverted_entrances(world, player):
|
|||
caves.remove('Inverted Agahnims Tower Exit')
|
||||
|
||||
# place links house
|
||||
links_house_doors = [door for door in lw_entrances + dw_entrances + lw_must_exits]
|
||||
links_house = random.choice(links_house_doors)
|
||||
links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
|
||||
links_house = random.choice(list(links_house_doors))
|
||||
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
|
||||
if links_house in lw_entrances:
|
||||
lw_entrances.remove(links_house)
|
||||
|
@ -1525,7 +1527,8 @@ def link_inverted_entrances(world, player):
|
|||
|
||||
|
||||
# place links house
|
||||
links_house = random.choice(list(entrances + must_exits))
|
||||
links_house_doors = [i for i in entrances + must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
|
||||
links_house = random.choice(list(links_house_doors))
|
||||
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
|
||||
if links_house in entrances:
|
||||
entrances.remove(links_house)
|
||||
|
@ -1656,7 +1659,8 @@ def link_inverted_entrances(world, player):
|
|||
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
|
||||
|
||||
# place links house and dark sanc
|
||||
links_house = random.choice(list(entrances + entrances_must_exits))
|
||||
links_house_doors = [i for i in entrances + entrances_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
|
||||
links_house = random.choice(list(links_house_doors))
|
||||
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
|
||||
if links_house in entrances:
|
||||
entrances.remove(links_house)
|
||||
|
@ -2824,9 +2828,18 @@ Inverted_Dark_Sanctuary_Doors = ['Inverted Dark Sanctuary',
|
|||
'Red Shield Shop',
|
||||
'Bumper Cave (Bottom)',
|
||||
'Bumper Cave (Top)',
|
||||
'Skull Woods Final Section',
|
||||
'Thieves Town']
|
||||
|
||||
Isolated_LH_Doors = ['Kings Grave',
|
||||
'Waterfall of Wishing',
|
||||
'Desert Palace Entrance (South)',
|
||||
'Desert Palace Entrance (North)',
|
||||
'Capacity Upgrade',
|
||||
'Ice Palace',
|
||||
'Skull Woods Final Section',
|
||||
'Dark World Hammer Peg Cave',
|
||||
'Turtle Rock Isolated Ledge Entrance']
|
||||
|
||||
# these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions
|
||||
mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
|
||||
('Lake Hylia Central Island Teleporter', 'Dark Lake Hylia Central Island'),
|
||||
|
@ -3141,9 +3154,6 @@ inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia
|
|||
('Ganon Drop', 'Bottom of Pyramid'),
|
||||
('Pyramid Drop', 'East Dark World'),
|
||||
('Post Aga Teleporter', 'Light World'),
|
||||
('LW Hyrule Castle Ledge SQ', 'Hyrule Castle Ledge'),
|
||||
('EDM Hyrule Castle Ledge SQ', 'Hyrule Castle Ledge'),
|
||||
('WDM Hyrule Castle Ledge SQ', 'Hyrule Castle Ledge'),
|
||||
('Secret Passage Inner Bushes', 'Light World'),
|
||||
('Secret Passage Outer Bushes', 'Hyrule Castle Secret Entrance Area'),
|
||||
('Potion Shop Inner Bushes', 'Light World'),
|
||||
|
|
|
@ -16,7 +16,7 @@ def create_inverted_regions(world, player):
|
|||
'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)',
|
||||
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game',
|
||||
'East Dark World Mirror Spot', 'West Dark World Mirror Spot', 'South Dark World Mirror Spot', 'Cave 45', 'Checkerboard Cave', 'Mire Mirror Spot', 'Hammer Peg Area Mirror Spot',
|
||||
'Shopping Mall Mirror Spot', 'Skull Woods Mirror Spot', 'Inverted Pyramid Entrance','Hyrule Castle Entrance (South)', 'Secret Passage Outer Bushes', 'LW Hyrule Castle Ledge SQ', 'Bush Covered Lawn Outer Bushes',
|
||||
'Shopping Mall Mirror Spot', 'Skull Woods Mirror Spot', 'Inverted Pyramid Entrance','Hyrule Castle Entrance (South)', 'Secret Passage Outer Bushes', 'Bush Covered Lawn Outer Bushes',
|
||||
'Potion Shop Outer Bushes', 'Graveyard Cave Outer Bushes', 'Bomb Hut Outer Bushes']),
|
||||
create_lw_region(player, 'Bush Covered Lawn', None, ['Bush Covered House', 'Bush Covered Lawn Inner Bushes', 'Bush Covered Lawn Mirror Spot']),
|
||||
create_lw_region(player, 'Bomb Hut Area', None, ['Light World Bomb Hut', 'Bomb Hut Inner Bushes', 'Bomb Hut Mirror Spot']),
|
||||
|
@ -124,14 +124,14 @@ def create_inverted_regions(world, player):
|
|||
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave',
|
||||
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Mirror Spot', 'WDM Hyrule Castle Ledge SQ']),
|
||||
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Mirror Spot']),
|
||||
create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
|
||||
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)', 'Bumper Cave Ledge Mirror Spot']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
|
||||
create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Mirror Spot (Bottom)', 'Hookshot Fairy',
|
||||
'Fairy Ascension Rocks', 'Spiral Cave (Bottom)', 'EDM Hyrule Castle Ledge SQ']),
|
||||
'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
|
||||
create_cave_region(player, 'Hookshot Fairy', 'fairies deep in a cave'),
|
||||
create_cave_region(player, 'Paradox Cave Front', 'a connector', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)', 'Light World Death Mountain Shop']),
|
||||
create_cave_region(player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
|
||||
|
@ -347,7 +347,6 @@ def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None
|
|||
def mark_dark_world_regions(world):
|
||||
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
|
||||
# That is ok. the bunny logic will check for this case and incorporate special rules.
|
||||
|
||||
queue = collections.deque(region for region in world.regions if region.type == RegionType.DarkWorld)
|
||||
seen = set(queue)
|
||||
while queue:
|
||||
|
|
|
@ -126,7 +126,7 @@ difficulties = {
|
|||
|
||||
def generate_itempool(world, player):
|
||||
if (world.difficulty not in ['normal', 'hard', 'expert'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals']
|
||||
or world.mode not in ['open', 'standard', 'swordless', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
|
||||
or world.mode not in ['open', 'standard', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
|
||||
raise NotImplementedError('Not supported yet')
|
||||
|
||||
if world.timer in ['ohko', 'timed-ohko']:
|
||||
|
|
24
Rom.py
24
Rom.py
|
@ -785,11 +785,11 @@ def patch_rom(world, player, rom):
|
|||
rom.write_byte(0x180029, 0x01) # Smithy quick item give
|
||||
|
||||
# set swordless mode settings
|
||||
rom.write_byte(0x18003F, 0x01 if world.mode == 'swordless' else 0x00) # hammer can harm ganon
|
||||
rom.write_byte(0x180040, 0x01 if world.mode == 'swordless' else 0x00) # open curtains
|
||||
rom.write_byte(0x180041, 0x01 if world.mode == 'swordless' else 0x00) # swordless medallions
|
||||
rom.write_byte(0x180043, 0xFF if world.mode == 'swordless' else 0x00) # starting sword for link
|
||||
rom.write_byte(0x180044, 0x01 if world.mode == 'swordless' else 0x00) # hammer activates tablets
|
||||
rom.write_byte(0x18003F, 0x01 if world.swords == 'swordless' else 0x00) # hammer can harm ganon
|
||||
rom.write_byte(0x180040, 0x01 if world.swords == 'swordless' else 0x00) # open curtains
|
||||
rom.write_byte(0x180041, 0x01 if world.swords == 'swordless' else 0x00) # swordless medallions
|
||||
rom.write_byte(0x180043, 0xFF if world.swords == 'swordless' else 0x00) # starting sword for link
|
||||
rom.write_byte(0x180044, 0x01 if world.swords == 'swordless' else 0x00) # hammer activates tablets
|
||||
|
||||
# set up clocks for timed modes
|
||||
if world.shuffle == 'vanilla':
|
||||
|
@ -1407,7 +1407,7 @@ def set_inverted_mode(world, rom):
|
|||
rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof
|
||||
# the following bytes should only be written in vanilla
|
||||
# or they'll overwrite the randomizer's shuffles
|
||||
if world.shuffle == 'vanilla':
|
||||
if world.shuffle == ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
||||
rom.write_byte(0x15B8C, 0x6C)
|
||||
rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house
|
||||
rom.write_byte(0xDBB73 + 0x52, 0x01)
|
||||
|
@ -1469,7 +1469,7 @@ def set_inverted_mode(world, rom):
|
|||
rom.write_int16(snes_to_pc(0x02D9A6), 0x005A)
|
||||
rom.write_byte(snes_to_pc(0x02D9B3), 0x12)
|
||||
# keep the old man spawn point at old man house unless shuffle is vanilla
|
||||
if world.shuffle == 'vanilla':
|
||||
if world.shuffle == ['vanilla', 'dungeonsfull', 'dungeonssimple']:
|
||||
rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01])
|
||||
rom.write_int16(snes_to_pc(0x02D8DE), 0x00F1)
|
||||
rom.write_bytes(snes_to_pc(0x02D910), [0x1F, 0x1E, 0x1F, 0x1F, 0x03, 0x02, 0x03, 0x03])
|
||||
|
@ -1530,9 +1530,9 @@ def set_inverted_mode(world, rom):
|
|||
0x190F, 0x9D04, 0x9D04])
|
||||
rom.write_int16s(snes_to_pc(0x1bb810), [0x00BE, 0x00C0, 0x013E])
|
||||
rom.write_int16s(snes_to_pc(0x1bb836), [0x001B, 0x001B, 0x001B])
|
||||
rom.write_int16(snes_to_pc(0x308300), 0x0140)
|
||||
rom.write_int16(snes_to_pc(0x308300), 0x0140) # new pyramid hole entrance
|
||||
rom.write_int16(snes_to_pc(0x308320), 0x001B)
|
||||
if world.shuffle == 'vanilla':
|
||||
if world.shuffle in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
||||
rom.write_byte(snes_to_pc(0x308340), 0x7B)
|
||||
rom.write_int16(snes_to_pc(0x1af504), 0x148B)
|
||||
rom.write_int16(snes_to_pc(0x1af50c), 0x149B)
|
||||
|
@ -1569,10 +1569,10 @@ def set_inverted_mode(world, rom):
|
|||
rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82])
|
||||
rom.write_int16(0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door
|
||||
rom.write_int16(0xDBA71 + 2 * 0x35, 0x06A4)
|
||||
if world.shuffle == 'vanilla':
|
||||
if world.shuffle in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
||||
rom.write_byte(0xDBB73 + 0x35, 0x36)
|
||||
rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp
|
||||
if world.shuffle == 'vanilla':
|
||||
if world.shuffle in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
||||
rom.write_int16(0x15AEE + 2 * 0x37, 0x0010) # pyramid exit to new hc area
|
||||
rom.write_byte(0x15B8C + 0x37, 0x1B)
|
||||
rom.write_int16(0x15BDB + 2 * 0x37, 0x0418)
|
||||
|
@ -1596,8 +1596,6 @@ def set_inverted_mode(world, rom):
|
|||
def patch_shuffled_dark_sanc(world, rom, player):
|
||||
dark_sanc_entrance = str(world.get_region('Inverted Dark Sanctuary', player).entrances[0].name)
|
||||
room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[dark_sanc_entrance][1]
|
||||
if dark_sanc_entrance == 'Skull Woods Final Section':
|
||||
link_y = 0x00F8
|
||||
door_index = door_addresses[str(dark_sanc_entrance)][0]
|
||||
|
||||
rom.write_byte(0x180241, 0x01)
|
||||
|
|
21
Rules.py
21
Rules.py
|
@ -115,7 +115,7 @@ def global_rules(world, player):
|
|||
|
||||
if world.mode == 'standard':
|
||||
world.get_region('Hyrule Castle Secret Entrance', player).can_reach_private = lambda state: True
|
||||
old_rule = world.get_region('Links House', player).can_reach
|
||||
old_rule = world.get_region('Links House', player).can_reach_private
|
||||
world.get_region('Links House', player).can_reach_private = lambda state: state.can_reach('Sanctuary', 'Region', player) or old_rule(state)
|
||||
else:
|
||||
# these are default save&quit points and always accessible
|
||||
|
@ -123,7 +123,7 @@ def global_rules(world, player):
|
|||
world.get_region('Sanctuary', player).can_reach_private = lambda state: True
|
||||
|
||||
# we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled!
|
||||
old_rule = world.get_region('Old Man House', player).can_reach
|
||||
old_rule = world.get_region('Old Man House', player).can_reach_private
|
||||
world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state)
|
||||
|
||||
# overworld requirements
|
||||
|
@ -469,13 +469,17 @@ def inverted_rules(world, player):
|
|||
|
||||
add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player)
|
||||
|
||||
# s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached
|
||||
world.get_region('Inverted Links House', player).can_reach_private = lambda state: True
|
||||
world.get_region('Inverted Links House', player).entrances[0].can_reach = lambda state: True
|
||||
world.get_region('Inverted Dark Sanctuary', player).entrances[0].parent_region.can_reach_private = lambda state: True
|
||||
|
||||
# we can s&q to the old man house after we rescue him. This may be somewhere completely different if caves are shuffled!
|
||||
if world.shuffle != 'vanilla':
|
||||
old_rule = world.get_region('Old Man House', player).can_reach
|
||||
world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state)
|
||||
old_rule = world.get_region('Old Man House', player).can_reach_private
|
||||
world.get_region('Old Man House', player).can_reach_private = lambda state: state.can_reach('Old Man', 'Location', player) or old_rule(state)
|
||||
|
||||
old_rule = world.get_region('Hyrule Castle Ledge', player).can_reach_private
|
||||
world.get_region('Hyrule Castle Ledge', player).can_reach_private = lambda state: (state.has_Mirror(player) and state.has('Beat Agahnim 1', player) and state.can_reach_light_world(player)) or old_rule(state)
|
||||
|
||||
# overworld requirements
|
||||
set_rule(world.get_location('Maze Race', player), lambda state: state.has_Pearl(player))
|
||||
set_rule(world.get_entrance('Mini Moldorm Cave', player), lambda state: state.has_Pearl(player))
|
||||
|
@ -540,9 +544,6 @@ def inverted_rules(world, player):
|
|||
set_rule(world.get_entrance('Agahnim 1', player), lambda state: state.has_sword(player) and state.has_key('Small Key (Agahnims Tower)', player, 2))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player))
|
||||
set_rule(world.get_location('Castle Tower - Dark Maze', player), lambda state: state.has_key('Small Key (Agahnims Tower)', player))
|
||||
set_rule(world.get_entrance('LW Hyrule Castle Ledge SQ', player), lambda state: state.has('Beat Agahnim 1', player))
|
||||
set_rule(world.get_entrance('EDM Hyrule Castle Ledge SQ', player), lambda state: state.has('Beat Agahnim 1', player))
|
||||
set_rule(world.get_entrance('WDM Hyrule Castle Ledge SQ', player), lambda state: state.has('Beat Agahnim 1', player))
|
||||
set_rule(world.get_entrance('Hyrule Castle Secret Entrance Drop', player), lambda state: state.has_Pearl(player))
|
||||
set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up
|
||||
set_rule(world.get_entrance('Broken Bridge (West)', player), lambda state: state.has('Hookshot', player) and state.has_Pearl(player))
|
||||
|
@ -1395,7 +1396,7 @@ def set_inverted_big_bomb_rules(world, player):
|
|||
'Hookshot Cave',
|
||||
'Turtle Rock Isolated Ledge Entrance',
|
||||
'Hookshot Cave Back Entrance',
|
||||
'Inverted Ganons Tower']
|
||||
'Inverted Agahnims Tower']
|
||||
Isolated_LW_entrances = ['Capacity Upgrade',
|
||||
'Tower of Hera',
|
||||
'Death Mountain Return Cave (West)',
|
||||
|
|
Loading…
Reference in New Issue