Spoiler log improvements
Fix multi-entrance checks Tower lobby door pairing fixed
This commit is contained in:
@@ -123,6 +123,8 @@ class World(object):
|
|||||||
set_player_attr('escape_assist', [])
|
set_player_attr('escape_assist', [])
|
||||||
set_player_attr('crystals_needed_for_ganon', 7)
|
set_player_attr('crystals_needed_for_ganon', 7)
|
||||||
set_player_attr('crystals_needed_for_gt', 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('open_pyramid', False)
|
||||||
set_player_attr('treasure_hunt_icon', 'Triforce Piece')
|
set_player_attr('treasure_hunt_icon', 'Triforce Piece')
|
||||||
set_player_attr('treasure_hunt_count', 0)
|
set_player_attr('treasure_hunt_count', 0)
|
||||||
@@ -1654,6 +1656,15 @@ class Location(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
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):
|
def __str__(self):
|
||||||
return str(self.__unicode__())
|
return str(self.__unicode__())
|
||||||
|
|
||||||
@@ -1833,25 +1844,25 @@ class Spoiler(object):
|
|||||||
listed_locations = set()
|
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]
|
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)
|
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]
|
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)
|
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]
|
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)
|
listed_locations.update(cave_locations)
|
||||||
|
|
||||||
for dungeon in self.world.dungeons:
|
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]
|
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)
|
listed_locations.update(dungeon_locations)
|
||||||
|
|
||||||
other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations]
|
other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations]
|
||||||
if other_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)
|
listed_locations.update(other_locations)
|
||||||
|
|
||||||
self.shops = []
|
self.shops = []
|
||||||
@@ -1970,8 +1981,10 @@ class Spoiler(object):
|
|||||||
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
||||||
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
||||||
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
|
outfile.write('Intensity: %s\n' % self.metadata['intensity'][player])
|
||||||
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'][player])
|
addition = ' (Random)' if self.world.crystals_gt_orig[player] == 'random' else ''
|
||||||
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'][player])
|
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('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('Accessibility: %s\n' % self.metadata['accessibility'][player])
|
||||||
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
|
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
|
# 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
|
# items: Item names
|
||||||
outfile.write('\n\nLocations:\n\n')
|
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
|
# 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
|
# items: Item names
|
||||||
|
|||||||
@@ -1063,7 +1063,7 @@ def check_entrance_fixes(world, player):
|
|||||||
dungeon = entrance.connected_region.dungeon
|
dungeon = entrance.connected_region.dungeon
|
||||||
if dungeon:
|
if dungeon:
|
||||||
layout = world.dungeon_layouts[player][dungeon.name]
|
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
|
portal = None
|
||||||
for portal_name in dungeon_portals[dungeon.name]:
|
for portal_name in dungeon_portals[dungeon.name]:
|
||||||
test_portal = world.get_portal(portal_name, player)
|
test_portal = world.get_portal(portal_name, player)
|
||||||
@@ -1071,8 +1071,6 @@ def check_entrance_fixes(world, player):
|
|||||||
portal = test_portal
|
portal = test_portal
|
||||||
break
|
break
|
||||||
world.force_fix[player][key] = portal
|
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):
|
def palette_assignment(world, player):
|
||||||
|
|||||||
14
Main.py
14
Main.py
@@ -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 ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
__version__ = '0.2.0.14u'
|
__version__ = '0.2.0.15u'
|
||||||
|
|
||||||
class EnemizerError(RuntimeError):
|
class EnemizerError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
@@ -56,6 +56,8 @@ def main(args, seed=None, fish=None):
|
|||||||
world.bigkeyshuffle = args.bigkeyshuffle.copy()
|
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_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_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.open_pyramid = args.openpyramid.copy()
|
||||||
world.boss_shuffle = args.shufflebosses.copy()
|
world.boss_shuffle = args.shufflebosses.copy()
|
||||||
world.enemy_shuffle = args.shuffleenemies.copy()
|
world.enemy_shuffle = args.shuffleenemies.copy()
|
||||||
@@ -374,6 +376,8 @@ def copy_world(world):
|
|||||||
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
||||||
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.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_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.open_pyramid = world.open_pyramid.copy()
|
||||||
ret.boss_shuffle = world.boss_shuffle.copy()
|
ret.boss_shuffle = world.boss_shuffle.copy()
|
||||||
ret.enemy_shuffle = world.enemy_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 = ret.get_region(region.name, region.player)
|
||||||
copied_region.is_light_world = region.is_light_world
|
copied_region.is_light_world = region.is_light_world
|
||||||
copied_region.is_dark_world = region.is_dark_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:
|
for entrance in region.entrances:
|
||||||
ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
|
ret.get_entrance(entrance.name, entrance.player).connect(copied_region)
|
||||||
|
|
||||||
@@ -598,7 +606,7 @@ 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({ 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():
|
for _, path in dict(old_world.spoiler.paths).items():
|
||||||
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
|
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
@@ -609,4 +617,4 @@ def create_playthrough(world):
|
|||||||
# we can finally output our playthrough
|
# we can finally output our playthrough
|
||||||
old_world.spoiler.playthrough = OrderedDict([("0", [str(item) for item in world.precollected_items if item.advancement])])
|
old_world.spoiler.playthrough = OrderedDict([("0", [str(item) for item in world.precollected_items if item.advancement])])
|
||||||
for i, sphere in enumerate(collection_spheres):
|
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}
|
||||||
|
|||||||
@@ -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
|
* 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
|
* 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.
|
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
|
### Experimental features
|
||||||
|
|
||||||
@@ -80,6 +92,9 @@ Redesign of Keysanity Menu complete for crossed dungeon and moved out of experim
|
|||||||
|
|
||||||
# Bug Fixes
|
# 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
|
* 2.0.14u
|
||||||
* Removal of key doors no longer messes up certain lobbies
|
* Removal of key doors no longer messes up certain lobbies
|
||||||
* Fixed ER entrances when Desert Back is a connector
|
* Fixed ER entrances when Desert Back is a connector
|
||||||
|
|||||||
2
Rom.py
2
Rom.py
@@ -713,8 +713,6 @@ def patch_rom(world, rom, player, team, enemized):
|
|||||||
if portal.boss_exit_idx > -1:
|
if portal.boss_exit_idx > -1:
|
||||||
rom.write_byte(0x7939 + portal.boss_exit_idx, portal.current_room())
|
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
|
# fix exits, if not fixed during exit patching
|
||||||
if world.fix_skullwoods_exit[player] and world.shuffle[player] == 'vanilla':
|
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)
|
write_int16(rom, 0x15DB5 + 2 * exit_ids['Skull Woods Final Section Exit'][1], 0x00F8)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ door_pair_offset_table = {
|
|||||||
0xb8: 0x01a4, 0xb9: 0x01a6, 0xba: 0x01aa, 0xbb: 0x01ad, 0xbc: 0x01b3, 0xbe: 0x01bb, 0xbf: 0x01be, 0xc0: 0x01bf,
|
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,
|
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,
|
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
|
# Note: 0-7 correspond to 1,2,3,4,5,6,a,14 respectively, see doortables.asm : MultDivInfo
|
||||||
|
|||||||
Reference in New Issue
Block a user