Merge branch 'OverworldShuffleDev' into OverworldShuffle
This commit is contained in:
218
BaseClasses.py
218
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):
|
||||||
@@ -2621,6 +2719,7 @@ class Spoiler(object):
|
|||||||
self.world = world
|
self.world = world
|
||||||
self.hashes = {}
|
self.hashes = {}
|
||||||
self.overworlds = {}
|
self.overworlds = {}
|
||||||
|
self.maps = {}
|
||||||
self.entrances = {}
|
self.entrances = {}
|
||||||
self.doors = {}
|
self.doors = {}
|
||||||
self.doorTypes = {}
|
self.doorTypes = {}
|
||||||
@@ -2636,12 +2735,22 @@ class Spoiler(object):
|
|||||||
self.shops = []
|
self.shops = []
|
||||||
self.bosses = OrderedDict()
|
self.bosses = OrderedDict()
|
||||||
|
|
||||||
|
self.suppress_spoiler_locations = ['Big Bomb', 'Dark Blacksmith Ruins', 'Frog', 'Middle Aged Man']
|
||||||
|
|
||||||
def set_overworld(self, entrance, exit, direction, player):
|
def set_overworld(self, entrance, exit, direction, player):
|
||||||
if self.world.players == 1:
|
if self.world.players == 1:
|
||||||
self.overworlds[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)])
|
self.overworlds[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)])
|
||||||
else:
|
else:
|
||||||
self.overworlds[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)])
|
self.overworlds[(entrance, direction, player)] = OrderedDict([('player', player), ('entrance', entrance), ('exit', exit), ('direction', direction)])
|
||||||
|
|
||||||
|
def set_map(self, type, text, data, player):
|
||||||
|
if type not in self.maps:
|
||||||
|
self.maps[type] = {}
|
||||||
|
if self.world.players == 1:
|
||||||
|
self.maps[type][player] = OrderedDict([('text', text), ('data', data)])
|
||||||
|
else:
|
||||||
|
self.maps[type][player] = OrderedDict([('player', player), ('text', text), ('data', data)])
|
||||||
|
|
||||||
def set_entrance(self, entrance, exit, direction, player):
|
def set_entrance(self, entrance, exit, direction, player):
|
||||||
if self.world.players == 1:
|
if self.world.players == 1:
|
||||||
self.entrances[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)])
|
self.entrances[(entrance, direction, player)] = OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)])
|
||||||
@@ -2810,6 +2919,7 @@ class Spoiler(object):
|
|||||||
self.parse_data()
|
self.parse_data()
|
||||||
out = OrderedDict()
|
out = OrderedDict()
|
||||||
out['Overworld'] = list(self.overworlds.values())
|
out['Overworld'] = list(self.overworlds.values())
|
||||||
|
out['Maps'] = list(self.maps.values())
|
||||||
out['Entrances'] = list(self.entrances.values())
|
out['Entrances'] = list(self.entrances.values())
|
||||||
out['Doors'] = list(self.doors.values())
|
out['Doors'] = list(self.doors.values())
|
||||||
out['Lobbies'] = list(self.lobbies.values())
|
out['Lobbies'] = list(self.lobbies.values())
|
||||||
@@ -2823,7 +2933,7 @@ class Spoiler(object):
|
|||||||
if self.shops:
|
if self.shops:
|
||||||
out['Shops'] = self.shops
|
out['Shops'] = self.shops
|
||||||
out['playthrough'] = self.playthrough
|
out['playthrough'] = self.playthrough
|
||||||
out['paths'] = self.paths
|
out['paths'] = {l:p for (l, p) in self.paths if l not in self.suppress_spoiler_locations}
|
||||||
out['Bosses'] = self.bosses
|
out['Bosses'] = self.bosses
|
||||||
out['meta'] = self.metadata
|
out['meta'] = self.metadata
|
||||||
|
|
||||||
@@ -2920,29 +3030,12 @@ class Spoiler(object):
|
|||||||
if self.overworlds:
|
if self.overworlds:
|
||||||
outfile.write('\n\nOverworld:\n\n')
|
outfile.write('\n\nOverworld:\n\n')
|
||||||
# overworld tile swaps
|
# overworld tile swaps
|
||||||
swap_output = False
|
if 'swaps' in self.maps:
|
||||||
for player in range(1, self.world.players + 1):
|
outfile.write('OW Tile Swaps:\n')
|
||||||
if self.world.owMixed[player]:
|
for player in self.maps['swaps']:
|
||||||
from OverworldShuffle import tile_swap_spoiler_table
|
if self.world.players > 1:
|
||||||
if not swap_output:
|
outfile.write(str('(Player ' + str(player) + ')\n')) # player name
|
||||||
swap_output = True
|
outfile.write(self.maps['swaps'][player]['text'] + '\n\n')
|
||||||
outfile.write('OW Tile Swaps:\n')
|
|
||||||
outfile.write(('' if self.world.players == 1 else str('(Player ' + str(player) + ')')).ljust(11)) # player name
|
|
||||||
s = list(map(lambda x: ' ' if x not in self.world.owswaps[player][0] else 'S', [i for i in range(0x40)]))
|
|
||||||
outfile.write((tile_swap_spoiler_table + '\n\n') % ( s[0x02], s[0x07], \
|
|
||||||
s[0x00], s[0x03], s[0x05], \
|
|
||||||
s[0x00], s[0x02],s[0x03], s[0x05], s[0x07], s[0x0a], s[0x0f], \
|
|
||||||
s[0x0a], s[0x0f],
|
|
||||||
s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17],
|
|
||||||
s[0x18], s[0x1a],s[0x1b], s[0x1d],s[0x1e], \
|
|
||||||
s[0x22], s[0x25], s[0x1a], s[0x1d], \
|
|
||||||
s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], s[0x18], s[0x1b], s[0x1e], \
|
|
||||||
s[0x30], s[0x32],s[0x33],s[0x34],s[0x35], s[0x37], s[0x22], s[0x25], \
|
|
||||||
s[0x3a],s[0x3b],s[0x3c], s[0x3f], \
|
|
||||||
s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], \
|
|
||||||
s[0x32],s[0x33],s[0x34], s[0x37], \
|
|
||||||
s[0x30], s[0x35],
|
|
||||||
s[0x3a],s[0x3b],s[0x3c], s[0x3f]))
|
|
||||||
|
|
||||||
# overworld transitions
|
# overworld transitions
|
||||||
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","overworlds",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","overworlds",entry['exit'])) for entry in self.overworlds.values()]))
|
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","overworlds",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","overworlds",entry['exit'])) for entry in self.overworlds.values()]))
|
||||||
@@ -3008,14 +3101,21 @@ class Spoiler(object):
|
|||||||
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
||||||
outfile.write('\n\nPaths:\n\n')
|
outfile.write('\n\nPaths:\n\n')
|
||||||
path_listings = []
|
path_listings = []
|
||||||
|
displayed_regions = []
|
||||||
for location, path in sorted(self.paths.items()):
|
for location, path in sorted(self.paths.items()):
|
||||||
path_lines = []
|
if self.world.players == 1:
|
||||||
for region, exit in path:
|
region = self.world.get_location(location, 1).parent_region
|
||||||
if exit is not None:
|
if region.name in displayed_regions:
|
||||||
path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit)))
|
continue
|
||||||
else:
|
displayed_regions.append(region.name)
|
||||||
path_lines.append(self.world.fish.translate("meta","rooms",region))
|
if location not in self.suppress_spoiler_locations:
|
||||||
path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines)))
|
path_lines = []
|
||||||
|
for region, exit in path:
|
||||||
|
if exit is not None:
|
||||||
|
path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit)))
|
||||||
|
else:
|
||||||
|
path_lines.append(self.world.fish.translate("meta","rooms",region))
|
||||||
|
path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines)))
|
||||||
|
|
||||||
outfile.write('\n'.join(path_listings))
|
outfile.write('\n'.join(path_listings))
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### 0.2.3.0
|
||||||
|
- Fixed issue in Crossed OW where mirror portal sprites would disappear when changing worlds
|
||||||
|
- Added Big Red Bomb logic to support using residual mirror portals for later re-entry
|
||||||
|
- Suppressed irrelevant paths in spoiler playthru
|
||||||
|
- Suppressed identical paths in spoiler playthru if multiple locations within the same region contain progression
|
||||||
|
|
||||||
### 0.2.2.3
|
### 0.2.2.3
|
||||||
- Fixed GT entrance not being opened when Inverted and WDM is Tile Swapped
|
- Fixed GT entrance not being opened when Inverted and WDM is Tile Swapped
|
||||||
- The Goal sign is moved to the area where the hole will be (always in opposite world as starting)
|
- The Goal sign is moved to the area where the hole will be (always in opposite world as starting)
|
||||||
|
|||||||
5
Main.py
5
Main.py
@@ -567,7 +567,7 @@ def create_playthrough(world):
|
|||||||
|
|
||||||
# get locations containing progress items
|
# get locations containing progress items
|
||||||
prog_locations = [location for location in world.get_filled_locations() if location.item.advancement]
|
prog_locations = [location for location in world.get_filled_locations() if location.item.advancement]
|
||||||
optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Big Bomb']
|
optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop']
|
||||||
state_cache = [None]
|
state_cache = [None]
|
||||||
collection_spheres = []
|
collection_spheres = []
|
||||||
state = CollectionState(world)
|
state = CollectionState(world)
|
||||||
@@ -674,9 +674,6 @@ def create_playthrough(world):
|
|||||||
old_world.spoiler.paths = dict()
|
old_world.spoiler.paths = dict()
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
old_world.spoiler.paths.update({location.gen_name(): get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player})
|
old_world.spoiler.paths.update({location.gen_name(): get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player})
|
||||||
for path in dict(old_world.spoiler.paths).values():
|
|
||||||
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
|
|
||||||
old_world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = get_path(state, world.get_region('Big Bomb Shop', player))
|
|
||||||
|
|
||||||
# we can finally output our playthrough
|
# we can finally output our playthrough
|
||||||
old_world.spoiler.playthrough = {"0": [str(item) for item in world.precollected_items if item.advancement]}
|
old_world.spoiler.playthrough = {"0": [str(item) for item in world.precollected_items if item.advancement]}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl
|
|||||||
from Regions import mark_dark_world_regions, mark_light_world_regions
|
from Regions import mark_dark_world_regions, mark_light_world_regions
|
||||||
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel
|
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel
|
||||||
|
|
||||||
__version__ = '0.2.2.3-u'
|
__version__ = '0.2.3.0-u'
|
||||||
|
|
||||||
def link_overworld(world, player):
|
def link_overworld(world, player):
|
||||||
# setup mandatory connections
|
# setup mandatory connections
|
||||||
@@ -137,6 +137,24 @@ def link_overworld(world, player):
|
|||||||
assert len(swapped_edges) == 0, 'Not all edges were swapped successfully: ' + ', '.join(swapped_edges )
|
assert len(swapped_edges) == 0, 'Not all edges were swapped successfully: ' + ', '.join(swapped_edges )
|
||||||
|
|
||||||
update_world_regions(world, player)
|
update_world_regions(world, player)
|
||||||
|
|
||||||
|
# update spoiler
|
||||||
|
s = list(map(lambda x: ' ' if x not in world.owswaps[player][0] else 'S', [i for i in range(0x40)]))
|
||||||
|
text_output = tile_swap_spoiler_table.replace('s', '%s') % ( s[0x02], s[0x07],
|
||||||
|
s[0x00], s[0x03], s[0x05],
|
||||||
|
s[0x00], s[0x02],s[0x03], s[0x05], s[0x07], s[0x0a], s[0x0f],
|
||||||
|
s[0x0a], s[0x0f],
|
||||||
|
s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17],
|
||||||
|
s[0x18], s[0x1a],s[0x1b], s[0x1d],s[0x1e],
|
||||||
|
s[0x22], s[0x25], s[0x1a], s[0x1d],
|
||||||
|
s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], s[0x18], s[0x1b], s[0x1e],
|
||||||
|
s[0x30], s[0x32],s[0x33],s[0x34],s[0x35], s[0x37], s[0x22], s[0x25],
|
||||||
|
s[0x3a],s[0x3b],s[0x3c], s[0x3f],
|
||||||
|
s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f],
|
||||||
|
s[0x32],s[0x33],s[0x34], s[0x37],
|
||||||
|
s[0x30], s[0x35],
|
||||||
|
s[0x3a],s[0x3b],s[0x3c], s[0x3f])
|
||||||
|
world.spoiler.set_map('swaps', text_output, world.owswaps[player][0], player)
|
||||||
|
|
||||||
# apply tile logical connections
|
# apply tile logical connections
|
||||||
for owid in ow_connections.keys():
|
for owid in ow_connections.keys():
|
||||||
@@ -1598,21 +1616,21 @@ flute_data = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tile_swap_spoiler_table = \
|
tile_swap_spoiler_table = \
|
||||||
""" 0 1 2 3 4 5 6 7
|
""" 0 1 2 3 4 5 6 7
|
||||||
+---+-+---+---+-+
|
+---+-+---+---+-+
|
||||||
01234567 A(00)| |%s| | |%s|
|
01234567 A(00)| |s| | |s|
|
||||||
+--------+ | %s +-+ %s | %s +-+
|
+--------+ | s +-+ s | s +-+
|
||||||
A(00)|%s %s%s %s %s| B(08)| |%s| | |%s|
|
A(00)|s ss s s| B(08)| |s| | |s|
|
||||||
B(08)| %s %s| +-+-+-+-+-+-+-+-+
|
B(08)| s s| +-+-+-+-+-+-+-+-+
|
||||||
C(10)|%s%s%s%s%s%s%s%s| C(10)|%s|%s|%s|%s|%s|%s|%s|%s|
|
C(10)|ssssssss| C(10)|s|s|s|s|s|s|s|s|
|
||||||
D(18)|%s %s%s %s%s | +-+-+-+-+-+-+-+-+
|
D(18)|s ss ss | +-+-+-+-+-+-+-+-+
|
||||||
E(20)| %s %s | D(18)| |%s| |%s| |
|
E(20)| s s | D(18)| |s| |s| |
|
||||||
F(28)|%s%s%s%s%s%s%s%s| | %s +-+ %s +-+ %s |
|
F(28)|ssssssss| | s +-+ s +-+ s |
|
||||||
G(30)|%s %s%s%s%s %s| E(20)| |%s| |%s| |
|
G(30)|s ssss s| E(20)| |s| |s| |
|
||||||
H(38)| %s%s%s %s| +-+-+-+-+-+-+-+-+
|
H(38)| sss s| +-+-+-+-+-+-+-+-+
|
||||||
+--------+ F(28)|%s|%s|%s|%s|%s|%s|%s|%s|
|
+--------+ F(28)|s|s|s|s|s|s|s|s|
|
||||||
+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+
|
||||||
G(30)| |%s|%s|%s| |%s|
|
G(30)| |s|s|s| |s|
|
||||||
| %s +-+-+-+ %s +-+
|
| s +-+-+-+ s +-+
|
||||||
H(38)| |%s|%s|%s| |%s|
|
H(38)| |s|s|s| |s|
|
||||||
+---+-+-+-+---+-+"""
|
+---+-+-+-+---+-+"""
|
||||||
|
|||||||
2
Rom.py
2
Rom.py
@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '9dfff0f3d093eb9adce053e9773f523e'
|
RANDOMIZERBASEHASH = '1a9145bd70bf9b28f7a3fe7ba38fc7ab'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
|
|||||||
2
Rules.py
2
Rules.py
@@ -717,7 +717,7 @@ def default_rules(world, player):
|
|||||||
|
|
||||||
# Underworld Logic
|
# Underworld Logic
|
||||||
set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up
|
set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up
|
||||||
set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Mirror', player)) # can erase block
|
set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Magic Mirror', player)) # can erase block
|
||||||
set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player))
|
set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player))
|
||||||
set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player))
|
set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player))
|
||||||
set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling
|
set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ jsl OWEdgeTransition : nop #4 ;LDA $02A4E3,X : ORA $7EF3CA
|
|||||||
;org $02e238 ;LDX #$9E : - DEX : DEX : CMP $DAEE,X : BNE -
|
;org $02e238 ;LDX #$9E : - DEX : DEX : CMP $DAEE,X : BNE -
|
||||||
;jsl OWSpecialTransition : nop #5
|
;jsl OWSpecialTransition : nop #5
|
||||||
|
|
||||||
|
org $05af75
|
||||||
|
jsl OWPreserveMirrorSprite : nop #2 ; LDA $7EF3CA : BNE $05AFDF
|
||||||
|
|
||||||
; whirlpool shuffle cross world change
|
; whirlpool shuffle cross world change
|
||||||
org $02b3bd
|
org $02b3bd
|
||||||
jsl OWWhirlpoolUpdate ;JSL $02EA6C
|
jsl OWWhirlpoolUpdate ;JSL $02EA6C
|
||||||
@@ -141,6 +144,19 @@ OWWhirlpoolUpdate:
|
|||||||
rtl
|
rtl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OWPreserveMirrorSprite:
|
||||||
|
{
|
||||||
|
lda.l OWMode+1 : and.b #!FLAG_OW_CROSSED : beq .vanilla
|
||||||
|
rtl ; if OW Crossed, skip world check and continue
|
||||||
|
.vanilla
|
||||||
|
lda $7ef3ca : bne .deleteMirror
|
||||||
|
rtl
|
||||||
|
|
||||||
|
.deleteMirror
|
||||||
|
pla : lda #$de : pha ; in vanilla, if in dark world, jump to $05afdf
|
||||||
|
rtl
|
||||||
|
}
|
||||||
|
|
||||||
OWFluteCancel:
|
OWFluteCancel:
|
||||||
{
|
{
|
||||||
lda.l OWFlags+1 : and #$01 : bne +
|
lda.l OWFlags+1 : and #$01 : bne +
|
||||||
@@ -394,7 +410,7 @@ OWWorldUpdate: ; x = owid of destination screen
|
|||||||
lda #$38 : sta $012f ; play sfx - #$3b is an alternative
|
lda #$38 : sta $012f ; play sfx - #$3b is an alternative
|
||||||
|
|
||||||
; toggle bunny mode
|
; toggle bunny mode
|
||||||
+ lda $7ef357 : bne .nobunny
|
lda $7ef357 : bne .nobunny
|
||||||
lda.l InvertedMode : bne .inverted
|
lda.l InvertedMode : bne .inverted
|
||||||
lda $7ef3ca : and.b #$40 : bra +
|
lda $7ef3ca : and.b #$40 : bra +
|
||||||
.inverted lda $7ef3ca : and.b #$40 : eor #$40
|
.inverted lda $7ef3ca : and.b #$40 : eor #$40
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user