Spoiler log improvements

Fix multi-entrance checks
Tower lobby door pairing fixed
This commit is contained in:
aerinon
2020-11-25 23:00:54 -07:00
parent cd9b574e90
commit ef3bc0aee0
6 changed files with 49 additions and 17 deletions

View File

@@ -123,6 +123,8 @@ class World(object):
set_player_attr('escape_assist', [])
set_player_attr('crystals_needed_for_ganon', 7)
set_player_attr('crystals_needed_for_gt', 7)
set_player_attr('crystals_ganon_orig', {})
set_player_attr('crystals_gt_orig', {})
set_player_attr('open_pyramid', False)
set_player_attr('treasure_hunt_icon', 'Triforce Piece')
set_player_attr('treasure_hunt_count', 0)
@@ -1654,6 +1656,15 @@ class Location(object):
return True
return False
def gen_name(self):
name = self.name
world = self.parent_region.world if self.parent_region and self.parent_region.world else None
if self.parent_region.dungeon and world and world.doorShuffle[self.player] == 'crossed':
name += f' @ {self.parent_region.dungeon.name}'
if world and world.players > 1:
name += f' ({world.get_player_names(self.player)})'
return name
def __str__(self):
return str(self.__unicode__())
@@ -1833,25 +1844,25 @@ class Spoiler(object):
listed_locations = set()
lw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.LightWorld]
self.locations['Light World'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in lw_locations])
self.locations['Light World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in lw_locations])
listed_locations.update(lw_locations)
dw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.DarkWorld]
self.locations['Dark World'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in dw_locations])
self.locations['Dark World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dw_locations])
listed_locations.update(dw_locations)
cave_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.Cave]
self.locations['Caves'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in cave_locations])
self.locations['Caves'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in cave_locations])
listed_locations.update(cave_locations)
for dungeon in self.world.dungeons:
dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon]
self.locations[str(dungeon)] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations])
self.locations[str(dungeon)] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations])
listed_locations.update(dungeon_locations)
other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations]
if other_locations:
self.locations['Other Locations'] = OrderedDict([(str(location), str(location.item) if location.item is not None else 'Nothing') for location in other_locations])
self.locations['Other Locations'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in other_locations])
listed_locations.update(other_locations)
self.shops = []
@@ -1970,8 +1981,10 @@ class Spoiler(object):
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'][player])
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'][player])
addition = ' (Random)' if self.world.crystals_gt_orig[player] == 'random' else ''
outfile.write('Crystals required for GT: %s\n' % (str(self.metadata['gt_crystals'][player]) + addition))
addition = ' (Random)' if self.world.crystals_ganon_orig[player] == 'random' else ''
outfile.write('Crystals required for Ganon: %s\n' % (str(self.metadata['ganon_crystals'][player]) + addition))
outfile.write('Pyramid hole pre-opened: %s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
outfile.write('Accessibility: %s\n' % self.metadata['accessibility'][player])
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
@@ -2019,7 +2032,7 @@ 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
# items: Item names
outfile.write('\n\nLocations:\n\n')
outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta","locations",location), self.world.fish.translate("meta","items",item)) for grouping in self.locations.values() for (location, item) in grouping.items()]))
outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta", "locations", location), self.world.fish.translate("meta", "items", item)) for grouping in self.locations.values() for (location, item) in grouping.items()]))
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
# items: Item names

View File

@@ -1063,7 +1063,7 @@ def check_entrance_fixes(world, player):
dungeon = entrance.connected_region.dungeon
if dungeon:
layout = world.dungeon_layouts[player][dungeon.name]
if 'Sanctuary' in layout.master_sector.region_set():
if 'Sanctuary' in layout.master_sector.region_set() or dungeon.name in ['Hyrule Castle', 'Desert Palace', 'Skull Woods', 'Turtle Rock']:
portal = None
for portal_name in dungeon_portals[dungeon.name]:
test_portal = world.get_portal(portal_name, player)
@@ -1071,8 +1071,6 @@ def check_entrance_fixes(world, player):
portal = test_portal
break
world.force_fix[player][key] = portal
elif dungeon.name in ['Hyrule Castle', 'Desert Palace', 'Skull Woods', 'Turtle Rock']:
world.force_fix[player][key] = portal
def palette_assignment(world, player):

14
Main.py
View File

@@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
from ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
from Utils import output_path, parse_player_names
__version__ = '0.2.0.14u'
__version__ = '0.2.0.15u'
class EnemizerError(RuntimeError):
pass
@@ -56,6 +56,8 @@ def main(args, seed=None, fish=None):
world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)}
world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)}
world.crystals_ganon_orig = args.crystals_ganon.copy()
world.crystals_gt_orig = args.crystals_gt.copy()
world.open_pyramid = args.openpyramid.copy()
world.boss_shuffle = args.shufflebosses.copy()
world.enemy_shuffle = args.shuffleenemies.copy()
@@ -374,6 +376,8 @@ def copy_world(world):
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
ret.crystals_gt_orig = world.crystals_gt_orig.copy()
ret.open_pyramid = world.open_pyramid.copy()
ret.boss_shuffle = world.boss_shuffle.copy()
ret.enemy_shuffle = world.enemy_shuffle.copy()
@@ -419,6 +423,10 @@ def copy_world(world):
copied_region = ret.get_region(region.name, region.player)
copied_region.is_light_world = region.is_light_world
copied_region.is_dark_world = region.is_dark_world
copied_region.dungeon = region.dungeon
copied_region.locations = [copy.copy(location) for location in region.locations]
for location in copied_region.locations:
location.parent_region = copied_region
for entrance in region.entrances:
ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
@@ -598,7 +606,7 @@ def create_playthrough(world):
old_world.spoiler.paths = dict()
for player in range(1, world.players + 1):
old_world.spoiler.paths.update({ str(location) : 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).items():
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
if world.mode[player] != 'inverted':
@@ -609,4 +617,4 @@ def create_playthrough(world):
# we can finally output our playthrough
old_world.spoiler.playthrough = OrderedDict([("0", [str(item) for item in world.precollected_items if item.advancement])])
for i, sphere in enumerate(collection_spheres):
old_world.spoiler.playthrough[str(i + 1)] = {str(location): str(location.item) for location in sphere}
old_world.spoiler.playthrough[str(i + 1)] = {location.gen_name(): str(location.item) for location in sphere}

View File

@@ -68,6 +68,18 @@ Redesign of Keysanity Menu complete for crossed dungeon and moved out of experim
* 1st Column: indicate if you have foudn the map of not for that dungeon
* 2nd and 3rd Column: You must have the compass to see these columns. A two-digit display that show you how
many chests are left in the dungeon. If -keydropshuffle is off, this does not count key drop. If on, it does.
## Potshuffle by compiling
Same flag as before but uses python logic written by compiling instead of the enemizer logic-less version. Needs some
testing to verify logic is all good.
## Other features
### Spoiler log improvements
* In crossed mode, the new dungeon is listed along with the location designated by a '@' sign
* Random gt crystals and ganon crystal are noted in the settings for better reproduction of seeds
### Experimental features
@@ -80,6 +92,9 @@ Redesign of Keysanity Menu complete for crossed dungeon and moved out of experim
# Bug Fixes
* 2.0.15u
* Allow Aga Tower lobby door as a a paired keydoor (typo)
* Fix portal check for multi-entrance dungeons
* 2.0.14u
* Removal of key doors no longer messes up certain lobbies
* Fixed ER entrances when Desert Back is a connector

2
Rom.py
View File

@@ -713,8 +713,6 @@ def patch_rom(world, rom, player, team, enemized):
if portal.boss_exit_idx > -1:
rom.write_byte(0x7939 + portal.boss_exit_idx, portal.current_room())
world.force_fix[player]['sw'] |= world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla'
# fix exits, if not fixed during exit patching
if world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla':
write_int16(rom, 0x15DB5 + 2 * exit_ids['Skull Woods Final Section Exit'][1], 0x00F8)

View File

@@ -62,7 +62,7 @@ door_pair_offset_table = {
0xb8: 0x01a4, 0xb9: 0x01a6, 0xba: 0x01aa, 0xbb: 0x01ad, 0xbc: 0x01b3, 0xbe: 0x01bb, 0xbf: 0x01be, 0xc0: 0x01bf,
0xc1: 0x01c2, 0xc2: 0x01ca, 0xc3: 0x01d2, 0xc4: 0x01d9, 0xc5: 0x01da, 0xc6: 0x01dd, 0xc7: 0x01e3, 0xc8: 0x01e6,
0xc9: 0x01e7, 0xcb: 0x01ec, 0xcc: 0x01ed, 0xce: 0x01f0, 0xd0: 0x01f1, 0xd1: 0x01f3, 0xd2: 0x01f7, 0xd5: 0x01f8,
0xd6: 0x01fa, 0xd8: 0x01fd, 0xd9: 0x0200, 0xda: 0x0203, 0xdb: 0x0204, 0xdc: 0x0206, 0xe0: 0x020
0xd6: 0x01fa, 0xd8: 0x01fd, 0xd9: 0x0200, 0xda: 0x0203, 0xdb: 0x0204, 0xdc: 0x0206, 0xe0: 0x0207
}
# Note: 0-7 correspond to 1,2,3,4,5,6,a,14 respectively, see doortables.asm : MultDivInfo