Modeled mirror portal re-entry logic with followers
This commit is contained in:
154
BaseClasses.py
154
BaseClasses.py
@@ -1584,16 +1584,16 @@ class Entrance(object):
|
|||||||
self.temp_path = []
|
self.temp_path = []
|
||||||
|
|
||||||
def can_reach(self, state):
|
def can_reach(self, state):
|
||||||
# Destination Pickup OW Only No Ledges Can S&Q
|
# Destination Pickup OW Only No Ledges Can S&Q Allow Mirror
|
||||||
multi_step_locations = { 'Pyramid Crack': ('Big Bomb', True, True, False),
|
multi_step_locations = { 'Pyramid Crack': ('Big Bomb', True, True, False, True),
|
||||||
'Missing Smith': ('Frog', True, False, True),
|
'Missing Smith': ('Frog', True, False, True, True),
|
||||||
'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True) }
|
'Middle Aged Man': ('Dark Blacksmith Ruins', True, False, True, True) }
|
||||||
|
|
||||||
if self.name in multi_step_locations:
|
if self.name in multi_step_locations:
|
||||||
if self not in state.path:
|
if self not in state.path:
|
||||||
world = self.parent_region.world if self.parent_region else None
|
world = self.parent_region.world
|
||||||
step_location = world.get_location(multi_step_locations[self.name][0], self.player)
|
step_location = world.get_location(multi_step_locations[self.name][0], self.player)
|
||||||
if step_location.can_reach(state) and self.can_reach_thru(state, step_location.parent_region, multi_step_locations[self.name][1], multi_step_locations[self.name][2], multi_step_locations[self.name][3]) and self.access_rule(state):
|
if step_location.can_reach(state) and self.can_reach_thru(state, step_location, multi_step_locations[self.name][1], multi_step_locations[self.name][2], multi_step_locations[self.name][3], multi_step_locations[self.name][4]) and self.access_rule(state):
|
||||||
if not self in state.path:
|
if not self in state.path:
|
||||||
path = state.path.get(step_location.parent_region, (step_location.parent_region.name, None))
|
path = state.path.get(step_location.parent_region, (step_location.parent_region.name, None))
|
||||||
item_name = step_location.item.name if step_location.item else 'Pick Up Item'
|
item_name = step_location.item.name if step_location.item else 'Pick Up Item'
|
||||||
@@ -1615,8 +1615,8 @@ class Entrance(object):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def can_reach_thru(self, state, start_region, ignore_underworld=False, ignore_ledges=False, allow_save_quit=False):
|
def can_reach_thru(self, state, step_location, ignore_underworld=False, ignore_ledges=False, allow_save_quit=False, allow_mirror_reentry=False):
|
||||||
def explore_region(region, path = []):
|
def explore_region(region, destination, path = []):
|
||||||
nonlocal found
|
nonlocal found
|
||||||
if region not in explored_regions:
|
if region not in explored_regions:
|
||||||
explored_regions[region] = path
|
explored_regions[region] = path
|
||||||
@@ -1624,34 +1624,132 @@ class Entrance(object):
|
|||||||
if exit.connected_region and (not ignore_ledges or exit.spot_type != 'Ledge') \
|
if exit.connected_region and (not ignore_ledges or exit.spot_type != 'Ledge') \
|
||||||
and exit.connected_region.name not in ['Dig Game Area'] \
|
and exit.connected_region.name not in ['Dig Game Area'] \
|
||||||
and exit.access_rule(state):
|
and exit.access_rule(state):
|
||||||
if exit.connected_region == self.parent_region:
|
if exit.connected_region == destination:
|
||||||
found = True
|
found = True
|
||||||
explored_regions[self.parent_region] = path + [exit]
|
explored_regions[destination] = path + [exit]
|
||||||
elif not ignore_underworld or region.type == exit.connected_region.type or exit.connected_region.type not in [RegionType.Cave, RegionType.Dungeon]:
|
elif not ignore_underworld or region.type == exit.connected_region.type or exit.connected_region.type not in [RegionType.Cave, RegionType.Dungeon]:
|
||||||
exits_to_traverse.append(tuple((exit, path)))
|
exits_to_traverse.append(tuple((exit, path)))
|
||||||
|
|
||||||
def traverse_paths(region, start_path=[]):
|
def traverse_paths(region, destination, start_path=[]):
|
||||||
explore_region(region, start_path)
|
nonlocal explored_regions, exits_to_traverse
|
||||||
while not found and len(exits_to_traverse):
|
|
||||||
exit, path = exits_to_traverse.pop(0)
|
|
||||||
explore_region(exit.connected_region, path + [exit])
|
|
||||||
if found:
|
|
||||||
self.temp_path = explored_regions[self.parent_region]
|
|
||||||
|
|
||||||
found = False
|
|
||||||
explored_regions = {}
|
|
||||||
exits_to_traverse = list()
|
|
||||||
traverse_paths(start_region.entrances[0].parent_region)
|
|
||||||
|
|
||||||
if not found and allow_save_quit:
|
|
||||||
explored_regions = {}
|
explored_regions = {}
|
||||||
exits_to_traverse = list()
|
exits_to_traverse = list()
|
||||||
world = self.parent_region.world if self.parent_region else None
|
explore_region(region, destination, start_path)
|
||||||
exit = world.get_entrance('Links House S&Q', self.player)
|
while not found and len(exits_to_traverse):
|
||||||
traverse_paths(exit.connected_region, [exit])
|
exit, path = exits_to_traverse.pop(0)
|
||||||
|
explore_region(exit.connected_region, destination, path + [exit])
|
||||||
|
if found:
|
||||||
|
self.temp_path = explored_regions[destination]
|
||||||
|
|
||||||
#TODO: Implement residual mirror portal placing for the previous leg, to be used for the final destination
|
start_region = step_location.parent_region
|
||||||
|
explored_regions = {}
|
||||||
|
exits_to_traverse = list()
|
||||||
|
found = False
|
||||||
|
|
||||||
|
if not found and allow_mirror_reentry and state.has('Magic Mirror', self.player):
|
||||||
|
# check for path using mirror portal re-entry at location of the follower pickup
|
||||||
|
# this is checked first as this often the shortest path
|
||||||
|
follower_region = start_region
|
||||||
|
if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]:
|
||||||
|
follower_region = start_region.entrances[0].parent_region
|
||||||
|
if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld):
|
||||||
|
from OWEdges import OWTileRegions
|
||||||
|
from OverworldShuffle import ow_connections
|
||||||
|
owid = OWTileRegions[follower_region.name]
|
||||||
|
(mirror_map, other_world) = ow_connections[owid % 0x40]
|
||||||
|
mirror_map.extend(other_world)
|
||||||
|
mirror_exit = None
|
||||||
|
while len(mirror_map):
|
||||||
|
if mirror_map[0][1] == follower_region.name:
|
||||||
|
mirror_exit = mirror_map[0][0]
|
||||||
|
break
|
||||||
|
mirror_map.pop(0)
|
||||||
|
if mirror_exit:
|
||||||
|
mirror_region = follower_region.world.get_entrance(mirror_exit, self.player).parent_region
|
||||||
|
if mirror_region.can_reach(state):
|
||||||
|
traverse_paths(mirror_region, self.parent_region)
|
||||||
|
if found:
|
||||||
|
path = state.path.get(mirror_region, (mirror_region.name, None))
|
||||||
|
path = (follower_region.name, (mirror_exit, path))
|
||||||
|
item_name = step_location.item.name if step_location.item else 'Pick Up Item'
|
||||||
|
if start_region.name != follower_region.name:
|
||||||
|
path = (start_region.name, (start_region.entrances[0].name, path))
|
||||||
|
path = (f'{step_location.parent_region.name} Exit', ('Leave Item Area', (item_name, path)))
|
||||||
|
else:
|
||||||
|
path = (item_name, path)
|
||||||
|
path = ('Use Mirror Portal', (follower_region.name, path))
|
||||||
|
while len(self.temp_path):
|
||||||
|
exit = self.temp_path.pop(0)
|
||||||
|
path = (exit.name, (exit.parent_region.name, path))
|
||||||
|
item_name = self.connected_region.locations[0].item.name if self.connected_region.locations[0].item else 'Deliver Item'
|
||||||
|
path = (self.parent_region.name, path)
|
||||||
|
state.path[self] = (self.name, path)
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
# check normal paths
|
||||||
|
traverse_paths(start_region.entrances[0].parent_region, self.parent_region)
|
||||||
|
|
||||||
|
if not found and allow_save_quit:
|
||||||
|
# check paths involving save and quit
|
||||||
|
exit = self.parent_region.world.get_entrance('Links House S&Q', self.player)
|
||||||
|
traverse_paths(exit.connected_region, self.parent_region, [exit])
|
||||||
|
|
||||||
|
if not found and allow_mirror_reentry and state.has('Magic Mirror', self.player):
|
||||||
|
# check for paths using mirror portal re-entry at location of final destination
|
||||||
|
# this is checked last as this is the most complicated/exhaustive check
|
||||||
|
follower_region = start_region
|
||||||
|
if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]:
|
||||||
|
follower_region = start_region.entrances[0].parent_region
|
||||||
|
if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld):
|
||||||
|
dest_region = self.parent_region
|
||||||
|
if dest_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]:
|
||||||
|
dest_region = start_region.entrances[0].parent_region
|
||||||
|
if (dest_region.world.mode[self.player] != 'inverted') != (dest_region.type == RegionType.LightWorld):
|
||||||
|
from OWEdges import OWTileRegions
|
||||||
|
from OverworldShuffle import ow_connections
|
||||||
|
owid = OWTileRegions[dest_region.name]
|
||||||
|
(mirror_map, other_world) = ow_connections[owid % 0x40]
|
||||||
|
mirror_map.extend(other_world)
|
||||||
|
mirror_map = [(x, d) for (x, d) in mirror_map if x in [e.name for e in dest_region.exits]]
|
||||||
|
# loop thru potential places to leave a mirror portal
|
||||||
|
while len(mirror_map) and not found:
|
||||||
|
mirror_exit = dest_region.world.get_entrance(mirror_map[0][0], self.player)
|
||||||
|
if mirror_exit.connected_region.type != dest_region.type:
|
||||||
|
# find path from placed mirror portal to the follower pickup
|
||||||
|
from Items import ItemFactory
|
||||||
|
mirror_item = ItemFactory('Magic Mirror', self.player)
|
||||||
|
while state.prog_items['Magic Mirror', self.player]:
|
||||||
|
state.remove(mirror_item)
|
||||||
|
temp_ignore_ledges = ignore_ledges
|
||||||
|
ignore_ledges = False
|
||||||
|
traverse_paths(mirror_exit.connected_region, start_region)
|
||||||
|
ignore_ledges = temp_ignore_ledges
|
||||||
|
state.collect(mirror_item, True)
|
||||||
|
if found:
|
||||||
|
path_to_pickup = self.temp_path
|
||||||
|
# find path from follower pickup to placed mirror portal
|
||||||
|
found = False
|
||||||
|
state.remove(mirror_item)
|
||||||
|
traverse_paths(follower_region, mirror_exit.connected_region)
|
||||||
|
state.collect(mirror_item, True)
|
||||||
|
mirror_map.pop(0)
|
||||||
|
if found:
|
||||||
|
path = state.path.get(self.parent_region, (self.parent_region.name, None))
|
||||||
|
path = (mirror_exit.name, path)
|
||||||
|
|
||||||
|
while len(path_to_pickup):
|
||||||
|
exit = path_to_pickup.pop(0)
|
||||||
|
path = (exit.name, (exit.parent_region.name, path))
|
||||||
|
item_name = step_location.item.name if step_location.item else 'Pick Up Item'
|
||||||
|
path = (f'{step_location.parent_region.name} Exit', (item_name, path))
|
||||||
|
|
||||||
|
while len(self.temp_path):
|
||||||
|
exit = self.temp_path.pop(0)
|
||||||
|
path = (exit.name, (exit.parent_region.name, path))
|
||||||
|
path = ('Use Mirror Portal', (mirror_exit.connected_region.name, path))
|
||||||
|
path = (self.parent_region.name, path)
|
||||||
|
state.path[self] = (self.name, path)
|
||||||
|
|
||||||
return found
|
return found
|
||||||
|
|
||||||
def connect(self, region, addresses=None, target=None, vanilla=None):
|
def connect(self, region, addresses=None, target=None, vanilla=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user