Refactor spoiler generation for VT integration.

This commit is contained in:
LLCoolDave 2017-07-18 12:44:13 +02:00
parent 3b7b65cf56
commit b6c4bf6bd6
5 changed files with 219 additions and 216 deletions

View File

@ -1,5 +1,7 @@
import copy import copy
import logging import logging
import json
from collections import OrderedDict
class World(object): class World(object):
@ -22,7 +24,6 @@ class World(object):
self._entrance_cache = {} self._entrance_cache = {}
self._location_cache = {} self._location_cache = {}
self._item_cache = {} self._item_cache = {}
self.spoiler = ''
self.required_locations = [] self.required_locations = []
self.place_dungeon_items = place_dungeon_items # configurable in future self.place_dungeon_items = place_dungeon_items # configurable in future
self.shuffle_bonk_prizes = False self.shuffle_bonk_prizes = False
@ -44,6 +45,7 @@ class World(object):
self.shuffle_ganon = shuffle_ganon self.shuffle_ganon = shuffle_ganon
self.fix_gtower_exit = self.shuffle_ganon self.fix_gtower_exit = self.shuffle_ganon
self.can_access_trock_eyebridge = None self.can_access_trock_eyebridge = None
self.spoiler = Spoiler(self)
def get_region(self, regionname): def get_region(self, regionname):
if isinstance(regionname, Region): if isinstance(regionname, Region):
@ -533,3 +535,55 @@ class Item(object):
# have 6 address that need to be filled # have 6 address that need to be filled
class Crystal(Item): class Crystal(Item):
pass pass
class Spoiler(object):
def __init__(self, world):
self.world = world
self.entrances = []
self.medallions = {}
self.playthrough = {}
self.locations = {}
self.metadata = {}
def set_entrance(self, entrance, exit, direction):
self.entrances.append(OrderedDict([('entrance', entrance), ('exit', exit), ('direction', direction)]))
def parse_data(self):
self.medallions = OrderedDict([('Misery Mire', self.world.required_medallions[0]), ('Turtle Rock', self.world.required_medallions[1])])
self.locations = {'other locations': OrderedDict([(str(location), str(location.item)) if location.item is not None else 'Nothing' for location in self.world.get_locations()])}
from Main import __version__ as ERVersion
self.metadata = {'version': ERVersion,
'seed': self.world.seed,
'logic': self.world.logic,
'mode': self.world.mode,
'goal': self.world.goal,
'shuffle': self.world.shuffle,
'algorithm': self.world.algorithm}
def to_json(self):
self.parse_data()
out = OrderedDict()
out['entrances'] = self.entrances
out.update(self.locations)
out['medallions'] = self.medallions
out['playthrough'] = self.playthrough
out['meta'] = self.metadata
return json.dumps(out)
def to_file(self, filename):
self.parse_data()
with open(filename, 'w') as outfile:
outfile.write('ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (self.metadata['version'], self.metadata['seed']))
outfile.write('Logic: %s Mode: %s Goal: %s Entrance Shuffle: %s Filling Algorithm: %s' % (self.metadata['logic'], self.metadata['mode'], self.metadata['goal'], self.metadata['shuffle'], self.metadata['algorithm']))
if self.entrances:
outfile.write('\n\nEntrances:\n\n')
outfile.write('\n'.join(['%s %s %s' % (entry['entrance'], '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', entry['exit']) for entry in self.entrances]))
outfile.write('\n\nMedallions')
outfile.write('\n\nMisery Mire Medallion: %s' % self.medallions['Misery Mire'])
outfile.write('\nTurtle Rock Medallion: %s' % self.medallions['Turtle Rock'])
outfile.write('\n\nLocations:\n\n')
outfile.write('\n'.join(['%s: %s' % (location, item) for (location, item) in self.locations['other locations'].items()]))
outfile.write('\n\nPlaythrough:\n\n')
outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (location, item) for (location, item) in sphere.items()])) for (sphere_nr, sphere) in self.playthrough.items()]))

View File

@ -83,8 +83,6 @@ def fill_dungeons(world):
world.state._clear_cache() world.state._clear_cache()
return ''
dungeon_music_addresses = {'Armos - Pendant': [0x1559A], dungeon_music_addresses = {'Armos - Pendant': [0x1559A],
'Lanmolas - Pendant': [0x1559B, 0x1559C, 0x1559D, 0x1559E], 'Lanmolas - Pendant': [0x1559B, 0x1559C, 0x1559D, 0x1559E],

View File

@ -4,7 +4,6 @@ import random
def link_entrances(world): def link_entrances(world):
ret = []
# setup mandatory connections # setup mandatory connections
for exitname, regionname in mandatory_connections: for exitname, regionname in mandatory_connections:
connect_simple(world, exitname, regionname) connect_simple(world, exitname, regionname)
@ -17,20 +16,16 @@ def link_entrances(world):
connect_simple(world, exitname, regionname) connect_simple(world, exitname, regionname)
elif world.shuffle == 'dungeonssimple': elif world.shuffle == 'dungeonssimple':
ret.append('Mixed Entrances:\n\n')
for exitname, regionname in default_connections: for exitname, regionname in default_connections:
connect_simple(world, exitname, regionname) connect_simple(world, exitname, regionname)
ret.append(simple_shuffle_dungeons(world)) simple_shuffle_dungeons(world)
elif world.shuffle == 'dungeonsfull': elif world.shuffle == 'dungeonsfull':
ret.append('Mixed Entrances:\n\n')
for exitname, regionname in default_connections: for exitname, regionname in default_connections:
connect_simple(world, exitname, regionname) connect_simple(world, exitname, regionname)
ret.append(skull_woods_shuffle(world)) skull_woods_shuffle(world)
dungeon_exits = list(Dungeon_Exits) dungeon_exits = list(Dungeon_Exits)
lw_entrances = list(LW_Dungeon_Entrances) lw_entrances = list(LW_Dungeon_Entrances)
@ -38,30 +33,28 @@ def link_entrances(world):
if world.mode == 'standard': if world.mode == 'standard':
# must connect front of hyrule castle to do escape # must connect front of hyrule castle to do escape
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')) connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')
else: else:
dungeon_exits.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) dungeon_exits.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
lw_entrances.append('Hyrule Castle Entrance (South)') lw_entrances.append('Hyrule Castle Entrance (South)')
if not world.shuffle_ganon: if not world.shuffle_ganon:
ret.append(connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')) connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')
else: else:
dw_entrances.append('Ganons Tower') dw_entrances.append('Ganons Tower')
dungeon_exits.append('Ganons Tower Exit') dungeon_exits.append('Ganons Tower Exit')
if world.mode == 'standard': if world.mode == 'standard':
# rest of hyrule castle must be in light world to avoid fake darkworld stuff, so it has to be the one connected to east exit of desert # rest of hyrule castle must be in light world to avoid fake darkworld stuff, so it has to be the one connected to east exit of desert
ret.append(connect_mandatory_exits(world, lw_entrances, [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], list(LW_Dungeon_Entrances_Must_Exit))) connect_mandatory_exits(world, lw_entrances, [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], list(LW_Dungeon_Entrances_Must_Exit))
else: else:
ret.append(connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit))) connect_mandatory_exits(world, lw_entrances, dungeon_exits, list(LW_Dungeon_Entrances_Must_Exit))
ret.append(connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit))) connect_mandatory_exits(world, dw_entrances, dungeon_exits, list(DW_Dungeon_Entrances_Must_Exit))
ret.append(connect_caves(world, lw_entrances, [], list(LW_Dungeon_Exits))) # Agahnim must be light world connect_caves(world, lw_entrances, [], list(LW_Dungeon_Exits)) # Agahnim must be light world
ret.append(connect_caves(world, lw_entrances, dw_entrances, dungeon_exits)) connect_caves(world, lw_entrances, dw_entrances, dungeon_exits)
elif world.shuffle == 'simple': elif world.shuffle == 'simple':
ret.append('Mixed Entrances:\n\n') simple_shuffle_dungeons(world)
ret.append(simple_shuffle_dungeons(world))
old_man_entrances = list(Old_Man_Entrances) old_man_entrances = list(Old_Man_Entrances)
caves = list(Cave_Exits) caves = list(Cave_Exits)
@ -80,8 +73,8 @@ def link_entrances(world):
while two_door_caves: while two_door_caves:
entrance1, entrance2 = two_door_caves.pop() entrance1, entrance2 = two_door_caves.pop()
exit1, exit2 = caves.pop() exit1, exit2 = caves.pop()
ret.append(connect_two_way(world, entrance1, exit1)) connect_two_way(world, entrance1, exit1)
ret.append(connect_two_way(world, entrance2, exit2)) connect_two_way(world, entrance2, exit2)
# now the remaining pairs # now the remaining pairs
two_door_caves = list(Two_Door_Caves) two_door_caves = list(Two_Door_Caves)
@ -89,8 +82,8 @@ def link_entrances(world):
while two_door_caves: while two_door_caves:
entrance1, entrance2 = two_door_caves.pop() entrance1, entrance2 = two_door_caves.pop()
exit1, exit2 = caves.pop() exit1, exit2 = caves.pop()
ret.append(connect_two_way(world, entrance1, exit1)) connect_two_way(world, entrance1, exit1)
ret.append(connect_two_way(world, entrance2, exit2)) connect_two_way(world, entrance2, exit2)
# at this point only Light World death mountain entrances remain # at this point only Light World death mountain entrances remain
# place old man, has limited options # place old man, has limited options
@ -101,41 +94,39 @@ def link_entrances(world):
remaining_entrances.extend(old_man_entrances) remaining_entrances.extend(old_man_entrances)
random.shuffle(remaining_entrances) random.shuffle(remaining_entrances)
old_man_entrance = remaining_entrances.pop() old_man_entrance = remaining_entrances.pop()
ret.append(connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)')) connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)')
ret.append(connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)')) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)')
# add old man house to ensure it is alwayxs somewhere on light death mountain # add old man house to ensure it is alwayxs somewhere on light death mountain
caves.append(('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)')) caves.append(('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)'))
caves.extend(list(three_exit_caves)) caves.extend(list(three_exit_caves))
# connect rest # connect rest
ret.append(connect_caves(world, remaining_entrances, [], caves)) connect_caves(world, remaining_entrances, [], caves)
# scramble holes # scramble holes
ret.append(scramble_holes(world)) scramble_holes(world)
# place blacksmith, has limited options # place blacksmith, has limited options
random.shuffle(blacksmith_doors) random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop() blacksmith_hut = blacksmith_doors.pop()
ret.append(connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')) connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')
bomb_shop_doors.extend(blacksmith_doors) bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options # place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors) random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop() bomb_shop = bomb_shop_doors.pop()
ret.append(connect_entrance(world, bomb_shop, 'Big Bomb Shop')) connect_entrance(world, bomb_shop, 'Big Bomb Shop')
single_doors.extend(bomb_shop_doors) single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet # tavern back door cannot be shuffled yet
ret.append(connect_doors(world, ['Tavern North'], ['Tavern'])) connect_doors(world, ['Tavern North'], ['Tavern'])
# place remaining doors # place remaining doors
ret.append(connect_doors(world, single_doors, door_targets)) connect_doors(world, single_doors, door_targets)
elif world.shuffle == 'restricted': elif world.shuffle == 'restricted':
ret.append('Mixed Entrances:\n\n') simple_shuffle_dungeons(world)
ret.append(simple_shuffle_dungeons(world))
lw_entrances = list(LW_Entrances) lw_entrances = list(LW_Entrances)
dw_entrances = list(DW_Entrances) dw_entrances = list(DW_Entrances)
@ -149,7 +140,7 @@ def link_entrances(world):
door_targets = list(Single_Cave_Targets) door_targets = list(Single_Cave_Targets)
# only use two exit caves to do mandatory dw connections # only use two exit caves to do mandatory dw connections
ret.append(connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits)) connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits)
# add three exit doors to pool for remainder # add three exit doors to pool for remainder
caves.extend(three_exit_caves) caves.extend(three_exit_caves)
@ -160,40 +151,38 @@ def link_entrances(world):
lw_entrances.extend(old_man_entrances) lw_entrances.extend(old_man_entrances)
random.shuffle(lw_entrances) random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop() old_man_entrance = lw_entrances.pop()
ret.append(connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)')) connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)')
ret.append(connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)')) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)')
# place Old Man House in Light World, so using the s&q point does not cause fake dark world # place Old Man House in Light World, so using the s&q point does not cause fake dark world
ret.append(connect_caves(world, lw_entrances, [], [('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)')])) connect_caves(world, lw_entrances, [], [('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)')])
# connect rest. There's 2 dw entrances remaining, so we will not run into parity issue placing caves # connect rest. There's 2 dw entrances remaining, so we will not run into parity issue placing caves
ret.append(connect_caves(world, lw_entrances, dw_entrances, caves)) connect_caves(world, lw_entrances, dw_entrances, caves)
# scramble holes # scramble holes
ret.append(scramble_holes(world)) scramble_holes(world)
# place blacksmith, has limited options # place blacksmith, has limited options
random.shuffle(blacksmith_doors) random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop() blacksmith_hut = blacksmith_doors.pop()
ret.append(connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')) connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')
bomb_shop_doors.extend(blacksmith_doors) bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options # place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors) random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop() bomb_shop = bomb_shop_doors.pop()
ret.append(connect_entrance(world, bomb_shop, 'Big Bomb Shop')) connect_entrance(world, bomb_shop, 'Big Bomb Shop')
single_doors.extend(bomb_shop_doors) single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet # tavern back door cannot be shuffled yet
ret.append(connect_doors(world, ['Tavern North'], ['Tavern'])) connect_doors(world, ['Tavern North'], ['Tavern'])
# place remaining doors # place remaining doors
ret.append(connect_doors(world, single_doors, door_targets)) connect_doors(world, single_doors, door_targets)
elif world.shuffle == 'full': elif world.shuffle == 'full':
ret.append('Mixed Entrances:\n\n') skull_woods_shuffle(world)
ret.append(skull_woods_shuffle(world))
lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances) lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances)
dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances) dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances)
@ -208,28 +197,28 @@ def link_entrances(world):
if world.mode == 'standard': if world.mode == 'standard':
# must connect front of hyrule castle to do escape # must connect front of hyrule castle to do escape
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')) connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')
else: else:
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
lw_entrances.append('Hyrule Castle Entrance (South)') lw_entrances.append('Hyrule Castle Entrance (South)')
if not world.shuffle_ganon: if not world.shuffle_ganon:
ret.append(connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')) connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')
else: else:
dw_entrances.append('Ganons Tower') dw_entrances.append('Ganons Tower')
caves.append('Ganons Tower Exit') caves.append('Ganons Tower Exit')
# we randomize which world requirements we fulfill first so we get better dungeon distribution # we randomize which world requirements we fulfill first so we get better dungeon distribution
if random.randint(0, 1) == 0: if random.randint(0, 1) == 0:
ret.append(connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits)) connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits)
ret.append(connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits)) connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits)
else: else:
ret.append(connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits)) connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits)
ret.append(connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits)) connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits)
if world.mode == 'standard': if world.mode == 'standard':
# rest of hyrule castle must be in light world to avoid fake darkworld stuff # rest of hyrule castle must be in light world to avoid fake darkworld stuff
ret.append(connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')])) connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')])
ret.append(connect_caves(world, lw_entrances, [], list(LW_Dungeon_Exits))) # Agahnim must be light world connect_caves(world, lw_entrances, [], list(LW_Dungeon_Exits)) # Agahnim must be light world
# place old man, has limited options # place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about # exit has to come from specific set of doors, the entrance is free to move about
@ -238,40 +227,38 @@ def link_entrances(world):
lw_entrances.extend(old_man_entrances) lw_entrances.extend(old_man_entrances)
random.shuffle(lw_entrances) random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop() old_man_entrance = lw_entrances.pop()
ret.append(connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)')) connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)')
ret.append(connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)')) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)')
# place Old Man House in Light World, so using the s&q point does not cause fake dark world # place Old Man House in Light World, so using the s&q point does not cause fake dark world
ret.append(connect_caves(world, lw_entrances, [], [('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)')])) connect_caves(world, lw_entrances, [], [('Old Man House Exit (Bottom)', 'Old Man House Exit (Top)')])
# now scramble the rest # now scramble the rest
ret.append(connect_caves(world, lw_entrances, dw_entrances, caves)) connect_caves(world, lw_entrances, dw_entrances, caves)
# scramble holes # scramble holes
ret.append(scramble_holes(world)) scramble_holes(world)
# place blacksmith, has limited options # place blacksmith, has limited options
random.shuffle(blacksmith_doors) random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop() blacksmith_hut = blacksmith_doors.pop()
ret.append(connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')) connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')
bomb_shop_doors.extend(blacksmith_doors) bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options # place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors) random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop() bomb_shop = bomb_shop_doors.pop()
ret.append(connect_entrance(world, bomb_shop, 'Big Bomb Shop')) connect_entrance(world, bomb_shop, 'Big Bomb Shop')
single_doors.extend(bomb_shop_doors) single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet # tavern back door cannot be shuffled yet
ret.append(connect_doors(world, ['Tavern North'], ['Tavern'])) connect_doors(world, ['Tavern North'], ['Tavern'])
# place remaining doors # place remaining doors
ret.append(connect_doors(world, single_doors, door_targets)) connect_doors(world, single_doors, door_targets)
elif world.shuffle == 'madness': elif world.shuffle == 'madness':
# here lie dragons, connections are no longer two way # here lie dragons, connections are no longer two way
ret.append('Mixed Entrances:\n\n')
lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances) lw_entrances = list(LW_Entrances + LW_Dungeon_Entrances)
dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances) dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances)
dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit)
@ -310,18 +297,18 @@ def link_entrances(world):
if world.mode == 'standard': if world.mode == 'standard':
# cannot move uncle cave # cannot move uncle cave
ret.append(connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance')) connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance')
ret.append(connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs')) connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs')
ret.append(connect_entrance(world, lw_doors.pop(), 'Hyrule Castle Secret Entrance Exit')) connect_entrance(world, lw_doors.pop(), 'Hyrule Castle Secret Entrance Exit')
else: else:
lw_hole_entrances.append('Hyrule Castle Secret Entrance Drop') lw_hole_entrances.append('Hyrule Castle Secret Entrance Drop')
hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance'))
lw_entrances.append('Hyrule Castle Secret Entrance Stairs') lw_entrances.append('Hyrule Castle Secret Entrance Stairs')
if not world.shuffle_ganon: if not world.shuffle_ganon:
ret.append(connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')) connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')
ret.append(connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit')) connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit')
ret.append(connect_entrance(world, 'Pyramid Hole', 'Pyramid')) connect_entrance(world, 'Pyramid Hole', 'Pyramid')
else: else:
dw_entrances.append('Ganons Tower') dw_entrances.append('Ganons Tower')
caves.append('Ganons Tower Exit') caves.append('Ganons Tower Exit')
@ -343,29 +330,29 @@ def link_entrances(world):
sw_hole_pool = dw_hole_entrances sw_hole_pool = dw_hole_entrances
mandatory_dark_world.append('Skull Woods First Section Exit') mandatory_dark_world.append('Skull Woods First Section Exit')
for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)']: for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)']:
ret.append(connect_entrance(world, sw_hole_pool.pop(), target)) connect_entrance(world, sw_hole_pool.pop(), target)
# sanctuary has to be in light world # sanctuary has to be in light world
ret.append(connect_entrance(world, lw_hole_entrances.pop(), 'Sewer Drop')) connect_entrance(world, lw_hole_entrances.pop(), 'Sewer Drop')
mandatory_light_world.append('Sanctuary Exit') mandatory_light_world.append('Sanctuary Exit')
# fill up remaining holes # fill up remaining holes
for hole in dw_hole_entrances: for hole in dw_hole_entrances:
exits, target = hole_targets.pop() exits, target = hole_targets.pop()
mandatory_dark_world.append(exits) mandatory_dark_world.append(exits)
ret.append(connect_entrance(world, hole, target)) connect_entrance(world, hole, target)
for hole in lw_hole_entrances: for hole in lw_hole_entrances:
exits, target = hole_targets.pop() exits, target = hole_targets.pop()
mandatory_light_world.append(exits) mandatory_light_world.append(exits)
ret.append(connect_entrance(world, hole, target)) connect_entrance(world, hole, target)
# hyrule castle handling # hyrule castle handling
if world.mode == 'standard': if world.mode == 'standard':
# must connect front of hyrule castle to do escape # must connect front of hyrule castle to do escape
ret.append(connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')) connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')
random.shuffle(lw_entrances) random.shuffle(lw_entrances)
ret.append(connect_exit(world, 'Hyrule Castle Exit (South)', lw_entrances.pop())) connect_exit(world, 'Hyrule Castle Exit (South)', lw_entrances.pop())
mandatory_light_world.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) mandatory_light_world.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
else: else:
lw_doors.append('Hyrule Castle Entrance (South)') lw_doors.append('Hyrule Castle Entrance (South)')
@ -404,8 +391,8 @@ def link_entrances(world):
exit = cave[-1] exit = cave[-1]
cave = cave[:-1] cave = cave[:-1]
ret.append(connect_exit(world, exit, entrance)) connect_exit(world, exit, entrance)
ret.append(connect_entrance(world, worldoors.pop(), exit)) connect_entrance(world, worldoors.pop(), exit)
# rest of cave now is forced to be in this world # rest of cave now is forced to be in this world
worldspecific.append(cave) worldspecific.append(cave)
@ -428,8 +415,8 @@ def link_entrances(world):
lw_entrances.extend(old_man_entrances) lw_entrances.extend(old_man_entrances)
random.shuffle(lw_entrances) random.shuffle(lw_entrances)
ret.append(connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit)) connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit)
ret.append(connect_entrance(world, lw_doors.pop(), 'Old Man Cave Exit (East)')) connect_entrance(world, lw_doors.pop(), 'Old Man Cave Exit (East)')
mandatory_light_world.append('Old Man Cave Exit (West)') mandatory_light_world.append('Old Man Cave Exit (West)')
# we connect up the mandatory associations we have found # we connect up the mandatory associations we have found
@ -438,18 +425,18 @@ def link_entrances(world):
mandatory = (mandatory,) mandatory = (mandatory,)
for exit in mandatory: for exit in mandatory:
# point out somewhere # point out somewhere
ret.append(connect_exit(world, exit, lw_entrances.pop())) connect_exit(world, exit, lw_entrances.pop())
# point in from somewhere # point in from somewhere
ret.append(connect_entrance(world, lw_doors.pop(), exit)) connect_entrance(world, lw_doors.pop(), exit)
for mandatory in mandatory_dark_world: for mandatory in mandatory_dark_world:
if not isinstance(mandatory, tuple): if not isinstance(mandatory, tuple):
mandatory = (mandatory,) mandatory = (mandatory,)
for exit in mandatory: for exit in mandatory:
# point out somewhere # point out somewhere
ret.append(connect_exit(world, exit, dw_entrances.pop())) connect_exit(world, exit, dw_entrances.pop())
# point in from somewhere # point in from somewhere
ret.append(connect_entrance(world, dw_doors.pop(), exit)) connect_entrance(world, dw_doors.pop(), exit)
# handle remaining caves # handle remaining caves
while caves: while caves:
@ -483,8 +470,8 @@ def link_entrances(world):
target_entrances = dw_entrances target_entrances = dw_entrances
for exit in cave: for exit in cave:
ret.append(connect_exit(world, exit, target_entrances.pop())) connect_exit(world, exit, target_entrances.pop())
ret.append(connect_entrance(world, target_doors.pop(), exit)) connect_entrance(world, target_doors.pop(), exit)
# handle simple doors # handle simple doors
@ -496,24 +483,23 @@ def link_entrances(world):
# place blacksmith, has limited options # place blacksmith, has limited options
random.shuffle(blacksmith_doors) random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop() blacksmith_hut = blacksmith_doors.pop()
ret.append(connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')) connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')
bomb_shop_doors.extend(blacksmith_doors) bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options # place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors) random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop() bomb_shop = bomb_shop_doors.pop()
ret.append(connect_entrance(world, bomb_shop, 'Big Bomb Shop')) connect_entrance(world, bomb_shop, 'Big Bomb Shop')
single_doors.extend(bomb_shop_doors) single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet # tavern back door cannot be shuffled yet
ret.append(connect_doors(world, ['Tavern North'], ['Tavern'])) connect_doors(world, ['Tavern North'], ['Tavern'])
# place remaining doors # place remaining doors
ret.append(connect_doors(world, single_doors, door_targets)) connect_doors(world, single_doors, door_targets)
elif world.shuffle == 'insanity': elif world.shuffle == 'insanity':
# beware ye who enter here # beware ye who enter here
ret.append('Mixed Entrances:\n\n')
entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Thieves Forest Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)'] entrances = LW_Entrances + LW_Dungeon_Entrances + DW_Entrances + DW_Dungeon_Entrances + ['Skull Woods Second Section Door (East)', 'Skull Woods First Section Door', 'Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Thieves Forest Hideout Stump', 'Lumberjack Tree Cave', 'Hyrule Castle Entrance (South)']
entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)'] entrances_must_exits = DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + LW_Dungeon_Entrances_Must_Exit + ['Skull Woods Second Section Door (West)']
@ -538,9 +524,9 @@ def link_entrances(world):
if world.mode == 'standard': if world.mode == 'standard':
# cannot move uncle cave # cannot move uncle cave
ret.append(connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance')) connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance')
ret.append(connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs')) connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs')
ret.append(connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit')) connect_entrance(world, doors.pop(), 'Hyrule Castle Secret Entrance Exit')
else: else:
hole_entrances.append('Hyrule Castle Secret Entrance Drop') hole_entrances.append('Hyrule Castle Secret Entrance Drop')
hole_targets.append('Hyrule Castle Secret Entrance') hole_targets.append('Hyrule Castle Secret Entrance')
@ -548,9 +534,9 @@ def link_entrances(world):
caves.append('Hyrule Castle Secret Entrance Exit') caves.append('Hyrule Castle Secret Entrance Exit')
if not world.shuffle_ganon: if not world.shuffle_ganon:
ret.append(connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')) connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')
ret.append(connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit')) connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit')
ret.append(connect_entrance(world, 'Pyramid Hole', 'Pyramid')) connect_entrance(world, 'Pyramid Hole', 'Pyramid')
else: else:
entrances.append('Ganons Tower') entrances.append('Ganons Tower')
caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) caves.extend(['Ganons Tower Exit', 'Pyramid Exit'])
@ -565,13 +551,13 @@ def link_entrances(world):
# fill up holes # fill up holes
for hole in hole_entrances: for hole in hole_entrances:
ret.append(connect_entrance(world, hole, hole_targets.pop())) connect_entrance(world, hole, hole_targets.pop())
# hyrule castle handling # hyrule castle handling
if world.mode == 'standard': if world.mode == 'standard':
# must connect front of hyrule castle to do escape # must connect front of hyrule castle to do escape
ret.append(connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')) connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')
ret.append(connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop())) connect_exit(world, 'Hyrule Castle Exit (South)', entrances.pop())
caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) caves.append(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
else: else:
doors.append('Hyrule Castle Entrance (South)') doors.append('Hyrule Castle Entrance (South)')
@ -599,8 +585,8 @@ def link_entrances(world):
exit = cave[-1] exit = cave[-1]
cave = cave[:-1] cave = cave[:-1]
ret.append(connect_exit(world, exit, entrance)) connect_exit(world, exit, entrance)
ret.append(connect_entrance(world, doors.pop(), exit)) connect_entrance(world, doors.pop(), exit)
# rest of cave now is forced to be in this world # rest of cave now is forced to be in this world
caves.append(cave) caves.append(cave)
@ -615,8 +601,8 @@ def link_entrances(world):
entrances.extend(old_man_entrances) entrances.extend(old_man_entrances)
random.shuffle(entrances) random.shuffle(entrances)
ret.append(connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit)) connect_exit(world, 'Old Man Cave Exit (East)', old_man_exit)
ret.append(connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)')) connect_entrance(world, doors.pop(), 'Old Man Cave Exit (East)')
caves.append('Old Man Cave Exit (West)') caves.append('Old Man Cave Exit (West)')
# handle remaining caves # handle remaining caves
@ -625,8 +611,8 @@ def link_entrances(world):
cave = (cave,) cave = (cave,)
for exit in cave: for exit in cave:
ret.append(connect_exit(world, exit, entrances.pop())) connect_exit(world, exit, entrances.pop())
ret.append(connect_entrance(world, doors.pop(), exit)) connect_entrance(world, doors.pop(), exit)
# handle simple doors # handle simple doors
@ -638,20 +624,20 @@ def link_entrances(world):
# place blacksmith, has limited options # place blacksmith, has limited options
random.shuffle(blacksmith_doors) random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop() blacksmith_hut = blacksmith_doors.pop()
ret.append(connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')) connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut')
bomb_shop_doors.extend(blacksmith_doors) bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options # place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors) random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop() bomb_shop = bomb_shop_doors.pop()
ret.append(connect_entrance(world, bomb_shop, 'Big Bomb Shop')) connect_entrance(world, bomb_shop, 'Big Bomb Shop')
single_doors.extend(bomb_shop_doors) single_doors.extend(bomb_shop_doors)
# tavern back door cannot be shuffled yet # tavern back door cannot be shuffled yet
ret.append(connect_doors(world, ['Tavern North'], ['Tavern'])) connect_doors(world, ['Tavern North'], ['Tavern'])
# place remaining doors # place remaining doors
ret.append(connect_doors(world, single_doors, door_targets)) connect_doors(world, single_doors, door_targets)
else: else:
raise NotImplementedError('Shuffling not supported yet') raise NotImplementedError('Shuffling not supported yet')
@ -660,8 +646,6 @@ def link_entrances(world):
if world.get_entrance('Dam').connected_region.name != 'Dam' or world.get_entrance('Swamp Palace').connected_region.name != 'Swamp Palace (Entrance)': if world.get_entrance('Dam').connected_region.name != 'Dam' or world.get_entrance('Swamp Palace').connected_region.name != 'Swamp Palace (Entrance)':
world.swamp_patch_required = True world.swamp_patch_required = True
return '\n'.join(ret) + '\n\n'
def connect_simple(world, exitname, regionname): def connect_simple(world, exitname, regionname):
world.get_entrance(exitname).connect(world.get_region(regionname)) world.get_entrance(exitname).connect(world.get_region(regionname))
@ -690,7 +674,7 @@ def connect_entrance(world, entrancename, exitname):
vanilla = None vanilla = None
entrance.connect(region, addresses, target, vanilla) entrance.connect(region, addresses, target, vanilla)
return '%s => %s' % (entrance.name, exit.name if exit is not None else region.name) world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance')
def connect_exit(world, exitname, entrancename): def connect_exit(world, exitname, entrancename):
@ -702,7 +686,7 @@ def connect_exit(world, exitname, entrancename):
exit.connected_region.entrances.remove(exit) exit.connected_region.entrances.remove(exit)
exit.connect(entrance.parent_region, door_addresses[entrance.name][0][1], exit_ids[exit.name][1]) exit.connect(entrance.parent_region, door_addresses[entrance.name][0][1], exit_ids[exit.name][1])
return '%s <= %s' % (entrance.name, exit.name) world.spoiler.set_entrance(entrance.name, exit.name, 'exit')
def connect_two_way(world, entrancename, exitname): def connect_two_way(world, entrancename, exitname):
@ -717,12 +701,10 @@ def connect_two_way(world, entrancename, exitname):
entrance.connect(exit.parent_region, door_addresses[entrance.name][0][0], exit_ids[exit.name][0]) entrance.connect(exit.parent_region, door_addresses[entrance.name][0][0], exit_ids[exit.name][0])
exit.connect(entrance.parent_region, door_addresses[entrance.name][0][1], exit_ids[exit.name][1]) exit.connect(entrance.parent_region, door_addresses[entrance.name][0][1], exit_ids[exit.name][1])
return '%s <=> %s' % (entrance.name, exit.name) world.spoiler.set_entrance(entrance.name, exit.name, 'both')
def scramble_holes(world): def scramble_holes(world):
ret = []
hole_entrances = [('Kakariko Well Cave', 'Kakariko Well Drop'), hole_entrances = [('Kakariko Well Cave', 'Kakariko Well Drop'),
('Bat Cave Cave', 'Bat Cave Drop'), ('Bat Cave Cave', 'Bat Cave Drop'),
('North Fairy Cave', 'North Fairy Cave Drop'), ('North Fairy Cave', 'North Fairy Cave Drop'),
@ -738,16 +720,16 @@ def scramble_holes(world):
('Sanctuary Exit', 'Sewer Drop')] ('Sanctuary Exit', 'Sewer Drop')]
if not world.shuffle_ganon: if not world.shuffle_ganon:
ret.append(connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit')) connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit')
ret.append(connect_entrance(world, 'Pyramid Hole', 'Pyramid')) connect_entrance(world, 'Pyramid Hole', 'Pyramid')
else: else:
hole_entrances.append(('Pyramid Entrance', 'Pyramid Hole')) hole_entrances.append(('Pyramid Entrance', 'Pyramid Hole'))
hole_targets.append(('Pyramid Exit', 'Pyramid')) hole_targets.append(('Pyramid Exit', 'Pyramid'))
if world.mode == 'standard': if world.mode == 'standard':
# cannot move uncle cave # cannot move uncle cave
ret.append(connect_two_way(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit')) connect_two_way(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit')
ret.append(connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance')) connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance')
else: else:
hole_entrances.append(('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Drop')) hole_entrances.append(('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Drop'))
hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance'))
@ -755,30 +737,23 @@ def scramble_holes(world):
random.shuffle(hole_targets) random.shuffle(hole_targets)
for entrance, drop in hole_entrances: for entrance, drop in hole_entrances:
exit, target = hole_targets.pop() exit, target = hole_targets.pop()
ret.append(connect_two_way(world, entrance, exit)) connect_two_way(world, entrance, exit)
ret.append(connect_entrance(world, drop, target)) connect_entrance(world, drop, target)
return '\n'.join(ret)
def connect_random(world, exitlist, targetlist, two_way=False): def connect_random(world, exitlist, targetlist, two_way=False):
targetlist = list(targetlist) targetlist = list(targetlist)
random.shuffle(targetlist) random.shuffle(targetlist)
ret = []
for exit, target in zip(exitlist, targetlist): for exit, target in zip(exitlist, targetlist):
if two_way: if two_way:
ret.append(connect_two_way(world, exit, target)) connect_two_way(world, exit, target)
else: else:
ret.append(connect_entrance(world, exit, target)) connect_entrance(world, exit, target)
return '\n'.join(ret)
def connect_mandatory_exits(world, entrances, caves, must_be_exits): def connect_mandatory_exits(world, entrances, caves, must_be_exits):
"""This works inplace""" """This works inplace"""
ret = []
random.shuffle(entrances) random.shuffle(entrances)
random.shuffle(caves) random.shuffle(caves)
while must_be_exits: while must_be_exits:
@ -805,15 +780,12 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits):
entrances.append(entrance) entrances.append(entrance)
entrance = new_entrance entrance = new_entrance
ret.append(connect_two_way(world, entrance, cave[i])) connect_two_way(world, entrance, cave[i])
ret.append(connect_two_way(world, exit, cave[-1])) connect_two_way(world, exit, cave[-1])
return '\n'.join(ret)
def connect_caves(world, lw_entrances, dw_entrances, caves): def connect_caves(world, lw_entrances, dw_entrances, caves):
"""This works inplace""" """This works inplace"""
ret = []
random.shuffle(lw_entrances) random.shuffle(lw_entrances)
random.shuffle(dw_entrances) random.shuffle(dw_entrances)
random.shuffle(caves) random.shuffle(caves)
@ -837,47 +809,40 @@ def connect_caves(world, lw_entrances, dw_entrances, caves):
target = lw_entrances if target is dw_entrances else dw_entrances target = lw_entrances if target is dw_entrances else dw_entrances
for exit in cave: for exit in cave:
ret.append(connect_two_way(world, target.pop(), exit)) connect_two_way(world, target.pop(), exit)
return '\n'.join(ret)
def connect_doors(world, doors, targets): def connect_doors(world, doors, targets):
"""This works inplace""" """This works inplace"""
ret = []
random.shuffle(doors) random.shuffle(doors)
random.shuffle(targets) random.shuffle(targets)
while doors: while doors:
door = doors.pop() door = doors.pop()
target = targets.pop() target = targets.pop()
ret.append(connect_entrance(world, door, target)) connect_entrance(world, door, target)
return '\n'.join(ret)
def skull_woods_shuffle(world): def skull_woods_shuffle(world):
ret = connect_random(world, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'], connect_random(world, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'],
['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)', 'Skull Woods Second Section']) ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)', 'Skull Woods Second Section'])
return ret + '\n' + connect_random(world, ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'], connect_random(world, ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'],
['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'], True) ['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'], True)
def simple_shuffle_dungeons(world): def simple_shuffle_dungeons(world):
ret = [] skull_woods_shuffle(world)
ret.append(skull_woods_shuffle(world))
dungeon_entrances = ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace'] dungeon_entrances = ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace']
dungeon_exits = ['Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit', 'Skull Woods Final Section Exit', 'Dark Palace Exit', 'Ice Palace Exit', 'Misery Mire Exit', 'Swamp Palace Exit'] dungeon_exits = ['Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit', 'Skull Woods Final Section Exit', 'Dark Palace Exit', 'Ice Palace Exit', 'Misery Mire Exit', 'Swamp Palace Exit']
if not world.shuffle_ganon: if not world.shuffle_ganon:
ret.append(connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')) connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit')
else: else:
dungeon_entrances.append('Ganons Tower') dungeon_entrances.append('Ganons Tower')
dungeon_exits.append('Ganons Tower Exit') dungeon_exits.append('Ganons Tower Exit')
# shuffle up single entrance dungeons # shuffle up single entrance dungeons
ret.append(connect_random(world, dungeon_entrances, dungeon_exits, True)) connect_random(world, dungeon_entrances, dungeon_exits, True)
# mix up 4 door dungeons # mix up 4 door dungeons
multi_dungeons = ['Desert', 'Turtle Rock'] multi_dungeons = ['Desert', 'Turtle Rock']
@ -895,54 +860,52 @@ def simple_shuffle_dungeons(world):
# ToDo improve this? # ToDo improve this?
if hc_target == 'Hyrule Castle': if hc_target == 'Hyrule Castle':
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')) connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)')
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)')) connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)')
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)')) connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Hyrule Castle Exit (West)')
ret.append(connect_two_way(world, 'Agahnims Tower', 'Agahnims Tower Exit')) connect_two_way(world, 'Agahnims Tower', 'Agahnims Tower Exit')
elif hc_target == 'Desert': elif hc_target == 'Desert':
ret.append(connect_two_way(world, 'Desert Palace Entrance (South)', 'Hyrule Castle Exit (South)')) connect_two_way(world, 'Desert Palace Entrance (South)', 'Hyrule Castle Exit (South)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (East)', 'Hyrule Castle Exit (East)')) connect_two_way(world, 'Desert Palace Entrance (East)', 'Hyrule Castle Exit (East)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (West)', 'Hyrule Castle Exit (West)')) connect_two_way(world, 'Desert Palace Entrance (West)', 'Hyrule Castle Exit (West)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (North)', 'Agahnims Tower Exit')) connect_two_way(world, 'Desert Palace Entrance (North)', 'Agahnims Tower Exit')
elif hc_target == 'Turtle Rock': elif hc_target == 'Turtle Rock':
ret.append(connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)')) connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)')
ret.append(connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Hyrule Castle Exit (East)')) connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Hyrule Castle Exit (East)')
ret.append(connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)')) connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)')
ret.append(connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Agahnims Tower Exit')) connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Agahnims Tower Exit')
if dp_target == 'Hyrule Castle': if dp_target == 'Hyrule Castle':
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Desert Palace Exit (South)')) connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Desert Palace Exit (South)')
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Desert Palace Exit (East)')) connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Desert Palace Exit (East)')
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Desert Palace Exit (West)')) connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Desert Palace Exit (West)')
ret.append(connect_two_way(world, 'Agahnims Tower', 'Desert Palace Exit (North)')) connect_two_way(world, 'Agahnims Tower', 'Desert Palace Exit (North)')
elif dp_target == 'Desert': elif dp_target == 'Desert':
ret.append(connect_two_way(world, 'Desert Palace Entrance (South)', 'Desert Palace Exit (South)')) connect_two_way(world, 'Desert Palace Entrance (South)', 'Desert Palace Exit (South)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (East)', 'Desert Palace Exit (East)')) connect_two_way(world, 'Desert Palace Entrance (East)', 'Desert Palace Exit (East)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (West)', 'Desert Palace Exit (West)')) connect_two_way(world, 'Desert Palace Entrance (West)', 'Desert Palace Exit (West)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (North)', 'Desert Palace Exit (North)')) connect_two_way(world, 'Desert Palace Entrance (North)', 'Desert Palace Exit (North)')
elif dp_target == 'Turtle Rock': elif dp_target == 'Turtle Rock':
ret.append(connect_two_way(world, 'Turtle Rock', 'Desert Palace Exit (South)')) connect_two_way(world, 'Turtle Rock', 'Desert Palace Exit (South)')
ret.append(connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Desert Palace Exit (East)')) connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Desert Palace Exit (East)')
ret.append(connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Desert Palace Exit (West)')) connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Desert Palace Exit (West)')
ret.append(connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Desert Palace Exit (North)')) connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Desert Palace Exit (North)')
if tr_target == 'Hyrule Castle': if tr_target == 'Hyrule Castle':
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Turtle Rock Exit (Front)')) connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Turtle Rock Exit (Front)')
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Turtle Rock Ledge Exit (East)')) connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Turtle Rock Ledge Exit (East)')
ret.append(connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Turtle Rock Ledge Exit (West)')) connect_two_way(world, 'Hyrule Castle Entrance (West)', 'Turtle Rock Ledge Exit (West)')
ret.append(connect_two_way(world, 'Agahnims Tower', 'Turtle Rock Isolated Ledge Exit')) connect_two_way(world, 'Agahnims Tower', 'Turtle Rock Isolated Ledge Exit')
elif tr_target == 'Desert': elif tr_target == 'Desert':
ret.append(connect_two_way(world, 'Desert Palace Entrance (South)', 'Turtle Rock Exit (Front)')) connect_two_way(world, 'Desert Palace Entrance (South)', 'Turtle Rock Exit (Front)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (North)', 'Turtle Rock Ledge Exit (East)')) connect_two_way(world, 'Desert Palace Entrance (North)', 'Turtle Rock Ledge Exit (East)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (West)', 'Turtle Rock Ledge Exit (West)')) connect_two_way(world, 'Desert Palace Entrance (West)', 'Turtle Rock Ledge Exit (West)')
ret.append(connect_two_way(world, 'Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Exit')) connect_two_way(world, 'Desert Palace Entrance (East)', 'Turtle Rock Isolated Ledge Exit')
elif tr_target == 'Turtle Rock': elif tr_target == 'Turtle Rock':
ret.append(connect_two_way(world, 'Turtle Rock', 'Turtle Rock Exit (Front)')) connect_two_way(world, 'Turtle Rock', 'Turtle Rock Exit (Front)')
ret.append(connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Isolated Ledge Exit')) connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Isolated Ledge Exit')
ret.append(connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)')) connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)')
ret.append(connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)')) connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)')
return '\n'.join(ret)
LW_Dungeon_Entrances = ['Desert Palace Entrance (South)', LW_Dungeon_Entrances = ['Desert Palace Entrance (South)',

30
Main.py
View File

@ -5,6 +5,7 @@ from Rom import patch_rom, LocalRom, JsonRom
from Rules import set_rules from Rules import set_rules
from Dungeons import fill_dungeons from Dungeons import fill_dungeons
from Items import ItemFactory from Items import ItemFactory
from collections import OrderedDict
import random import random
import time import time
import logging import logging
@ -36,28 +37,25 @@ def main(args, seed=None):
world.seed = int(seed) world.seed = int(seed)
random.seed(world.seed) random.seed(world.seed)
world.spoiler += 'ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (__version__, world.seed) logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (__version__, world.seed))
world.spoiler += 'Logic: %s Mode: %s Goal: %s Entrance Shuffle: %s Filling Algorithm: %s\n\n' % (args.logic, args.mode, args.goal, args.shuffle, args.algorithm) # todo
logger.info(world.spoiler)
create_regions(world) create_regions(world)
logger.info('Shuffling the World about.') logger.info('Shuffling the World about.')
world.spoiler += link_entrances(world) link_entrances(world)
logger.info('Generating Item Pool.') logger.info('Generating Item Pool.')
world.spoiler += generate_itempool(world) generate_itempool(world)
logger.info('Calculating Access Rules.') logger.info('Calculating Access Rules.')
world.spoiler += set_rules(world) set_rules(world)
logger.info('Placing Dungeon Items.') logger.info('Placing Dungeon Items.')
world.spoiler += fill_dungeons(world) fill_dungeons(world)
logger.info('Fill the world.') logger.info('Fill the world.')
@ -72,11 +70,9 @@ def main(args, seed=None):
elif args.algorithm == 'restrictive': elif args.algorithm == 'restrictive':
distribute_items_restrictive(world, 10 if world.goal is not 'starhunt' else 0) distribute_items_restrictive(world, 10 if world.goal is not 'starhunt' else 0)
world.spoiler += print_location_spoiler(world)
logger.info('Calculating playthrough.') logger.info('Calculating playthrough.')
world.spoiler += create_playthrough(world) create_playthrough(world)
logger.info('Patching ROM.') logger.info('Patching ROM.')
@ -94,13 +90,12 @@ def main(args, seed=None):
rom = LocalRom(args.rom) rom = LocalRom(args.rom)
patch_rom(world, rom, bytearray(logic_hash), args.quickswap, args.heartbeep, sprite) patch_rom(world, rom, bytearray(logic_hash), args.quickswap, args.heartbeep, sprite)
if args.jsonout: if args.jsonout:
print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler})) print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()}))
else: else:
rom.write_to_file(args.jsonout or '%s.sfc' % outfilebase) rom.write_to_file(args.jsonout or '%s.sfc' % outfilebase)
if args.create_spoiler and not args.jsonout: if args.create_spoiler and not args.jsonout:
with open('%s_Spoiler.txt' % outfilebase, 'w') as outfile: world.spoiler.to_file('%s_Spoiler.txt' % outfilebase)
outfile.write(world.spoiler)
logger.info('Done. Enjoy.') logger.info('Done. Enjoy.')
logger.debug('Total Time: %s' % (time.clock() - start)) logger.debug('Total Time: %s' % (time.clock() - start))
@ -523,8 +518,6 @@ def generate_itempool(world):
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
world.required_medallions = (mm_medallion, tr_medallion) world.required_medallions = (mm_medallion, tr_medallion)
return 'Misery Mire Medallion: %s\nTurtle Rock Medallion: %s\n\n' % (mm_medallion, tr_medallion)
def copy_world(world): def copy_world(world):
# ToDo: Not good yet # ToDo: Not good yet
@ -637,8 +630,5 @@ def create_playthrough(world):
old_world.required_locations = [location.name for sphere in collection_spheres for location in sphere] old_world.required_locations = [location.name for sphere in collection_spheres for location in sphere]
# we can finally output our playthrough # we can finally output our playthrough
return 'Playthrough:\n' + ''.join(['%s: {\n%s}\n' % (i + 1, ''.join([' %s: %s\n' % (location, location.item) for location in sphere])) for i, sphere in enumerate(collection_spheres)]) + '\n' old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)])
def print_location_spoiler(world):
return 'Locations:\n\n' + '\n'.join(['%s: %s' % (location, location.item if location.item is not None else 'Nothing') for location in world.get_locations()]) + '\n\n'

View File

@ -30,8 +30,6 @@ def set_rules(world):
if not world.swamp_patch_required: if not world.swamp_patch_required:
add_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has_Mirror()) add_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has_Mirror())
return ''
def set_rule(spot, rule): def set_rule(spot, rule):
spot.access_rule = rule spot.access_rule = rule