feat: skull woods er options
This commit is contained in:
@@ -2551,6 +2551,8 @@ class Spoiler(object):
|
||||
'shuffleganon': self.world.shuffle_ganon,
|
||||
'shufflelinks': self.world.shufflelinks,
|
||||
'shuffletavern': self.world.shuffletavern,
|
||||
'skullwoods': self.world.skullwoods,
|
||||
'linked_drops': self.world.linked_drops,
|
||||
'take_any': self.world.take_any,
|
||||
'overworld_map': self.world.overworld_map,
|
||||
'door_shuffle': self.world.doorShuffle,
|
||||
@@ -2779,6 +2781,9 @@ class Spoiler(object):
|
||||
if self.metadata['shuffle'][player] != 'vanilla':
|
||||
outfile.write(f"Link's House Shuffled: {yn(self.metadata['shufflelinks'][player])}\n")
|
||||
outfile.write(f"Back of Tavern Shuffled: {yn(self.metadata['shuffletavern'][player])}\n")
|
||||
outfile.write(f"Skull Woods Shuffle: {self.metadata['skullwoods'][player]}\n")
|
||||
if self.metadata['linked_drops'] != "unset":
|
||||
outfile.write(f"Linked Drops Override: {self.metadata['linked_drops'][player]}\n")
|
||||
outfile.write(f"GT/Ganon Shuffled: {yn(self.metadata['shuffleganon'])}\n")
|
||||
outfile.write(f"Overworld Map: {self.metadata['overworld_map'][player]}\n")
|
||||
outfile.write('Pyramid hole pre-opened: %s\n' % (self.metadata['open_pyramid'][player]))
|
||||
@@ -3120,6 +3125,10 @@ overworld_map_mode = {'default': 0, 'compass': 1, 'map': 2}
|
||||
trap_door_mode = {'vanilla': 0, 'optional': 1, 'boss': 2, 'oneway': 3}
|
||||
key_logic_algo = {'default': 0, 'partial': 1, 'strict': 2}
|
||||
|
||||
# byte 13: SSDD ???? (skullwoods, linked_drops, 4 free bytes)
|
||||
skullwoods_mode = {'original': 0, 'restricted': 1, 'loose': 2, 'followlinked': 3}
|
||||
linked_drops_mode = {'unset': 0, 'linked': 1, 'independent': 2}
|
||||
|
||||
# sfx_shuffle and other adjust items does not affect settings code
|
||||
|
||||
# Bump this when making changes that are not backwards compatible (nearly all of them)
|
||||
@@ -3169,6 +3178,8 @@ class Settings(object):
|
||||
|
||||
((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5
|
||||
| trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]),
|
||||
|
||||
(skullwoods_mode[w.skullwoods[p]] << 6 | linked_drops_mode[w.linked_drops[p]] << 4),
|
||||
])
|
||||
return base64.b64encode(code, "+-".encode()).decode()
|
||||
|
||||
@@ -3242,6 +3253,9 @@ class Settings(object):
|
||||
args.overworld_map[p] = r(overworld_map_mode)[(settings[12] & 0x60) >> 5]
|
||||
args.trap_door_mode[p] = r(trap_door_mode)[(settings[12] & 0x18) >> 3]
|
||||
args.key_logic_algorithm[p] = r(key_logic_algo)[settings[12] & 0x07]
|
||||
if len(settings) > 13:
|
||||
args.skullwoods[p] = r(skullwoods_mode)[(settings[13] & 0xc0) >> 6]
|
||||
args.linked_drops[p] = r(linked_drops_mode)[(settings[13] & 0x30) >> 4]
|
||||
|
||||
|
||||
class KeyRuleType(FastEnum):
|
||||
|
||||
3
CLI.py
3
CLI.py
@@ -135,6 +135,7 @@ def parse_cli(argv, no_defaults=False):
|
||||
'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items', 'triforce_max_difference',
|
||||
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
|
||||
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
|
||||
'skullwoods', 'linked_drops',
|
||||
'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
||||
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
||||
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor',
|
||||
@@ -191,6 +192,8 @@ def parse_settings():
|
||||
"shuffle": "vanilla",
|
||||
"shufflelinks": False,
|
||||
"shuffletavern": True,
|
||||
'skullwoods': 'original',
|
||||
'linked_drops': 'unset',
|
||||
"overworld_map": "default",
|
||||
'take_any': 'none',
|
||||
"pseudoboots": False,
|
||||
|
||||
23
ItemList.py
23
ItemList.py
@@ -10,7 +10,7 @@ from PotShuffle import vanilla_pots
|
||||
from Items import ItemFactory
|
||||
|
||||
from source.dungeon.EnemyList import add_drop_contents
|
||||
from source.overworld.EntranceShuffle2 import connect_entrance
|
||||
from source.overworld.EntranceShuffle2 import exit_ids, door_addresses
|
||||
from source.item.FillUtil import trash_items, pot_items
|
||||
|
||||
import source.classes.constants as CONST
|
||||
@@ -549,6 +549,27 @@ def set_up_take_anys(world, player, skip_adjustments=False):
|
||||
world.initialize_regions()
|
||||
|
||||
|
||||
def connect_entrance(world, entrancename, exitname, player):
|
||||
entrance = world.get_entrance(entrancename, player)
|
||||
# check if we got an entrance or a region to connect to
|
||||
try:
|
||||
region = world.get_region(exitname, player)
|
||||
exit = None
|
||||
except RuntimeError:
|
||||
exit = world.get_entrance(exitname, player)
|
||||
region = exit.parent_region
|
||||
|
||||
# if this was already connected somewhere, remove the backreference
|
||||
if entrance.connected_region is not None:
|
||||
entrance.connected_region.entrances.remove(entrance)
|
||||
|
||||
target = exit_ids[exit.name][0] if exit is not None else exit_ids.get(region.name, None)
|
||||
addresses = door_addresses[entrance.name][0]
|
||||
|
||||
entrance.connect(region, addresses, target)
|
||||
world.spoiler.set_entrance(entrance.name, exit.name if exit is not None else region.name, 'entrance', player)
|
||||
|
||||
|
||||
def create_dynamic_shop_locations(world, player):
|
||||
for shop in world.shops[player]:
|
||||
if shop.region.player == player:
|
||||
|
||||
4
Main.py
4
Main.py
@@ -38,7 +38,7 @@ from source.enemizer.DamageTables import DamageTable
|
||||
from source.enemizer.Enemizer import randomize_enemies
|
||||
from source.rom.DataTables import init_data_tables
|
||||
|
||||
version_number = '1.4.1.12'
|
||||
version_number = '1.4.2'
|
||||
version_branch = '-u'
|
||||
__version__ = f'{version_number}{version_branch}'
|
||||
|
||||
@@ -135,6 +135,8 @@ def main(args, seed=None, fish=None):
|
||||
world.standardize_palettes = args.standardize_palettes.copy()
|
||||
world.shufflelinks = args.shufflelinks.copy()
|
||||
world.shuffletavern = args.shuffletavern.copy()
|
||||
world.skullwoods = args.skullwoods.copy()
|
||||
world.linked_drops = args.linked_drops.copy()
|
||||
world.pseudoboots = args.pseudoboots.copy()
|
||||
world.overworld_map = args.overworld_map.copy()
|
||||
world.take_any = args.take_any.copy()
|
||||
|
||||
@@ -125,6 +125,47 @@ Designed by Codemann, these are available now (only with experimental turned on
|
||||
- Both dungeons and connectors can be cross-world connections
|
||||
- No dungeon guarantees like in Lite ER
|
||||
|
||||
### Skull Woods Shuffle
|
||||
|
||||
In an effort to reduce annoying Skull Woods layouts, several new options have been created.
|
||||
|
||||
- Original: Skull woods shuffles classically amongst itself unless insanity is the mode. This should mimic prior behavior.
|
||||
- Restricted (Vanilla Drops, Entrances Restricted): Skull woods drops are vanilla. Skull woods entrances stay in skull woods and are shuffled.
|
||||
- Loose (Vanilla Drops, Entrances use Shuffle): Skull woods drops are vanilla. The main ER mode's pool determines how to handle.
|
||||
- Followdrops (Follow Linked Drops Setting): This looks at the new linked drop settings. If linked drops are turned on, then two new pairs of linked drop down and holes are formed. Skull front and the hole near the big chest form a pair. The east entrance to Skull 2 and th hole in the back of skull woods form another pair. If the mode is not a cross-world shuffle, then these 2 new drop-down pairs are limited to the dark world. The other drop-down in skull woods, the front two holes will be vanilla. If linked drops are off, then the mode determines how to handle the holes and entrances.
|
||||
|
||||
### Linked Drops Override
|
||||
|
||||
This controls whether drops should be linked to nearby entrances or not.
|
||||
|
||||
- Unset: This uses the mode's default which is considered linked for all modes except insanity
|
||||
- Linked: Forces drops to be linked to their entrances.
|
||||
- Independent: Decouples drops from their entrances. In same-world shuffles, holes & entrances may be restricted to a singe world depending on settings and placement to prevent cross-world connection through holes and/or entrances in dungeons.
|
||||
|
||||
### Brief Explanations
|
||||
|
||||
Loose Skull Woods Shuffle:
|
||||
|
||||
- Simple dungeons modes will attempt to fix the layout of skull woods to be more vanilla. This includes dungeonssimple, simple, and restricted ER modes.
|
||||
- The dungeonsfull mode allows skull woods to be used as a connector but attempt to maintain same-world connectivity.
|
||||
- Same world modes like lite & full will generally keep skull woods entrances to a single world to prevent cross-world connections. If not inverted, this is not guaranteed to be the dark world though.
|
||||
- Cross-world modes will be eaiser to comprehend due to fewer restrictions like crossed, lean and swapped.
|
||||
|
||||
Followdrops with Linked Drops:
|
||||
|
||||
- Some modes don't care much about linked drops: simple, dungeonssimple, dungeonsfull
|
||||
- Same-world modes like restricted, full, & lite will often keep skull woods drop pairs in the dark world and there are only 3 options there: pyramid, and the vanilla locations
|
||||
- Cross-world modes will benefit the most from the changes as the drop pool expands by two new options for drop placement and guarantees a way out from skull woods west, though the connector must be located.
|
||||
- Insanity with linked drops will kind of allow a player to scout holes, at the cost of not being to get back to the hole immediately.
|
||||
|
||||
Followdrops with Independent Drops:
|
||||
- dungeonssimple will place holes vanilla anyway
|
||||
- dungeonsfull will shuffle the holes
|
||||
- Same-world modes like simple, restricted, full, & lite will likely pull all skull woods entrances to a single world. (It'll likely be the light world if a single hole is in the light world, unless inverted, then the reverse.)
|
||||
- Cross-world modes like swapped, lean, and crossed will mean drops are no longer scoutable. Enjoy your coin flips!
|
||||
- This is insanity's default anyway, no change.
|
||||
|
||||
|
||||
### Back of Tavern Shuffle (Experimental required)
|
||||
|
||||
Thanks goes to Catobat which now allows the back of tavern to be shuffled anywhere and any valid cave can be at the back of tavern with this option checked. Available in experimental only for now as it requires the new algorithm to be shuffled properly.
|
||||
@@ -141,6 +182,11 @@ These are now independent of retro mode and have three options: None, Random, an
|
||||
|
||||
# Patch Notes
|
||||
|
||||
* 1.4.2
|
||||
* New ER Options:
|
||||
* [Skull Woods shuffle options](#skull-woods-shuffle)
|
||||
* [New option](#linked-drops-override) to override linked drop down behavior
|
||||
* MultiClient: change default port to 23074 for newer SNI versions
|
||||
* 1.4.1.12u
|
||||
* New Entrance Shuffle Algorithm no longer experimental
|
||||
* Back of Tavern Shuffle now on by default
|
||||
|
||||
@@ -71,6 +71,15 @@ shufflelinks:
|
||||
shuffletavern:
|
||||
on: 1
|
||||
off: 1
|
||||
skullwoods:
|
||||
original: 1
|
||||
restricted: 1
|
||||
loose: 1
|
||||
followlinked: 1
|
||||
linked_drops:
|
||||
unset: 1
|
||||
linked: 1
|
||||
independent: 1
|
||||
world_state:
|
||||
standard: 1
|
||||
open: 1
|
||||
|
||||
@@ -439,6 +439,21 @@
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"skullwoods": {
|
||||
"choices": [
|
||||
"original",
|
||||
"restricted",
|
||||
"loose",
|
||||
"followlinked"
|
||||
]
|
||||
},
|
||||
"linked_drops": {
|
||||
"choices": [
|
||||
"unset",
|
||||
"linked",
|
||||
"independent"
|
||||
]
|
||||
},
|
||||
"overworld_map": {
|
||||
"choices": [
|
||||
"default",
|
||||
|
||||
@@ -385,6 +385,20 @@
|
||||
"shuffletavern": [
|
||||
"Include the back of the tavern in the entrance shuffle pool. (default: %(default)s)"
|
||||
],
|
||||
"skullwoods": [
|
||||
"Select how to shuffle skull woods (default: %(default)s)",
|
||||
"Original: Skull Woods is shuffled amongst itself",
|
||||
"Restricted: Drops are vanilla. Entrances stay in skull woods",
|
||||
"Loose: Drops are vanilla. Entrances go in the main pool",
|
||||
"Followlinked: If drops are linked, then pinball/left side will be vanilla",
|
||||
" with other drops paired with an entrance. Otherwise, all go into the main pool"
|
||||
],
|
||||
"linked_drops": [
|
||||
"Select how drops are treated in entrance shuffle. (default: %(default)s)",
|
||||
"Unset: The shuffle mode determines the setting.",
|
||||
"Linked: Dropdowns will be linked with entrance caves",
|
||||
"Independent: Dropdowns will not be linked"
|
||||
],
|
||||
"overworld_map": [
|
||||
"Control if and how the overworld map indicators show the locations of dungeons (default: %(default)s)"
|
||||
],
|
||||
|
||||
@@ -162,6 +162,17 @@
|
||||
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
|
||||
"randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple",
|
||||
|
||||
"randomizer.entrance.skullwoods": "Skull Woods Shuffle",
|
||||
"randomizer.entrance.skullwoods.original": "Original",
|
||||
"randomizer.entrance.skullwoods.restricted": "Vanilla Drops, Entrances Restricted",
|
||||
"randomizer.entrance.skullwoods.loose": "Vanilla Drops, Entrances use Shuffle",
|
||||
"randomizer.entrance.skullwoods.followlinked": "Follow Linked Drops Setting",
|
||||
|
||||
"randomizer.entrance.linked_drops": "Linked Drops Override",
|
||||
"randomizer.entrance.linked_drops.unset": "Determined by Shuffle",
|
||||
"randomizer.entrance.linked_drops.linked": "Always Linked",
|
||||
"randomizer.entrance.linked_drops.independent": "Independent",
|
||||
|
||||
"randomizer.gameoptions.nobgm": "Disable Music & MSU-1",
|
||||
"randomizer.gameoptions.quickswap": "L/R Quickswapping",
|
||||
"randomizer.gameoptions.reduce_flashing": "Reduce Flashing",
|
||||
|
||||
@@ -34,6 +34,26 @@
|
||||
"padx": [20,0]
|
||||
}
|
||||
},
|
||||
"skullwoods": {
|
||||
"type": "selectbox",
|
||||
"options": [
|
||||
"original",
|
||||
"restricted",
|
||||
"loose",
|
||||
"followlinked"
|
||||
],
|
||||
"config": {
|
||||
"width": 30
|
||||
}
|
||||
},
|
||||
"linked_drops": {
|
||||
"type": "selectbox",
|
||||
"options": [
|
||||
"unset",
|
||||
"linked",
|
||||
"independent"
|
||||
]
|
||||
},
|
||||
"openpyramid": {
|
||||
"type": "selectbox",
|
||||
"options": [
|
||||
|
||||
@@ -143,6 +143,8 @@ class CustomSettings(object):
|
||||
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
|
||||
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p])
|
||||
args.shuffletavern[p] = get_setting(settings['shuffletavern'], args.shuffletavern[p])
|
||||
args.skullwoods[p] = get_setting(settings['skullwoods'], args.skullwoods[p])
|
||||
args.linked_drops[p] = get_setting(settings['linked_drops'], args.linked_drops[p])
|
||||
args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p])
|
||||
args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p])
|
||||
args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p])
|
||||
@@ -279,6 +281,8 @@ class CustomSettings(object):
|
||||
settings_dict[p]['bombbag'] = world.bombbag[p]
|
||||
settings_dict[p]['shufflelinks'] = world.shufflelinks[p]
|
||||
settings_dict[p]['shuffletavern'] = world.shuffletavern[p]
|
||||
settings_dict[p]['skullwoods'] = world.skullwoods[p]
|
||||
settings_dict[p]['linked_drops'] = world.linked_drops[p]
|
||||
settings_dict[p]['overworld_map'] = world.overworld_map[p]
|
||||
settings_dict[p]['pseudoboots'] = world.pseudoboots[p]
|
||||
settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p]
|
||||
|
||||
@@ -92,6 +92,8 @@ SETTINGSTOPROCESS = {
|
||||
"shuffleganon": "shuffleganon",
|
||||
"shufflelinks": "shufflelinks",
|
||||
"shuffletavern": "shuffletavern",
|
||||
"skullwoods": "skullwoods",
|
||||
"linked_drops": "linked_drops",
|
||||
"openpyramid": "openpyramid",
|
||||
"overworld_map": "overworld_map",
|
||||
},
|
||||
|
||||
@@ -23,6 +23,7 @@ class EntrancePool(object):
|
||||
self.decoupled_exits = []
|
||||
self.original_entrances = set()
|
||||
self.original_exits = set()
|
||||
self.same_world_restricted = {}
|
||||
|
||||
self.world = world
|
||||
self.player = player
|
||||
@@ -90,6 +91,10 @@ def link_entrances_new(world, player):
|
||||
if mode not in modes:
|
||||
raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
|
||||
mode_cfg = copy.deepcopy(modes[mode])
|
||||
|
||||
if world.linked_drops[player] != 'unset':
|
||||
mode_cfg['keep_drops_together'] = 'on' if world.linked_drops[player] == 'linked' else 'off'
|
||||
|
||||
avail_pool.swapped = mode_cfg['undefined'] == 'swap'
|
||||
if avail_pool.is_standard():
|
||||
do_standard_connections(avail_pool)
|
||||
@@ -97,11 +102,7 @@ def link_entrances_new(world, player):
|
||||
for pool_name, pool in pool_list.items():
|
||||
special_shuffle = pool['special'] if 'special' in pool else None
|
||||
if special_shuffle == 'drops':
|
||||
holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances'])
|
||||
if avail_pool.swapped:
|
||||
connect_swapped(holes, targets, avail_pool)
|
||||
else:
|
||||
connect_random(holes, targets, avail_pool)
|
||||
handle_skull_woods_drops(avail_pool, pool['entrances'], mode_cfg)
|
||||
elif special_shuffle == 'fixed_shuffle':
|
||||
do_fixed_shuffle(avail_pool, pool['entrances'])
|
||||
elif special_shuffle == 'same_world':
|
||||
@@ -124,12 +125,7 @@ def link_entrances_new(world, player):
|
||||
elif special_shuffle == 'vanilla':
|
||||
do_vanilla_connect(pool, avail_pool)
|
||||
elif special_shuffle == 'skull':
|
||||
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
||||
if avail_pool.swapped:
|
||||
connect_swapped(entrances, exits, avail_pool, True)
|
||||
else:
|
||||
connect_random(entrances, exits, avail_pool, True)
|
||||
avail_pool.skull_handled = True
|
||||
handle_skull_woods_entrances(avail_pool, pool['entrances'])
|
||||
else:
|
||||
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
||||
do_main_shuffle(entrances, exits, avail_pool, mode_cfg)
|
||||
@@ -239,12 +235,13 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
||||
# mandatory exits
|
||||
rem_entrances, rem_exits = set(), set()
|
||||
if not cross_world:
|
||||
determine_dungeon_restrictions(avail)
|
||||
mand_exits = figure_out_must_exits_same_world(entrances, exits, avail)
|
||||
must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves, hyrule_forced = mand_exits
|
||||
if hyrule_forced:
|
||||
do_mandatory_connections(avail, lw_entrances, hyrule_forced, must_exit_lw)
|
||||
else:
|
||||
do_mandatory_connections(avail, lw_entrances, multi_exit_caves, must_exit_lw)
|
||||
must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves = mand_exits
|
||||
lw_candidates = filter_restricted_caves(multi_exit_caves, 'LightWorld', avail)
|
||||
other_candidates = [x for x in multi_exit_caves if x not in lw_candidates] # remember those not passed in
|
||||
do_mandatory_connections(avail, lw_entrances, lw_candidates, must_exit_lw)
|
||||
multi_exit_caves = other_candidates + lw_candidates # rebuild list from the lw_candidates and those not passed
|
||||
# remove old man house as connector - not valid for dw must_exit if it is a spawn point
|
||||
if not avail.inverted:
|
||||
new_mec = []
|
||||
@@ -254,7 +251,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
||||
else:
|
||||
new_mec.append(cave_option)
|
||||
multi_exit_caves = new_mec
|
||||
do_mandatory_connections(avail, dw_entrances, multi_exit_caves, must_exit_dw)
|
||||
dw_candidates = filter_restricted_caves(multi_exit_caves, 'DarkWorld', avail)
|
||||
other_candidates = [x for x in multi_exit_caves if x not in dw_candidates] # remember those not passed in
|
||||
do_mandatory_connections(avail, dw_entrances, dw_candidates, must_exit_dw)
|
||||
multi_exit_caves = other_candidates + dw_candidates # rebuild list from the dw_candidates and those not passed
|
||||
rem_entrances.update(lw_entrances)
|
||||
rem_entrances.update(dw_entrances)
|
||||
else:
|
||||
@@ -351,6 +351,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
||||
if bonk_fairy_exception(x):
|
||||
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
|
||||
do_same_world_connectors(lw_entrances, dw_entrances, multi_exit_caves, avail)
|
||||
if avail.world.doorShuffle[avail.player] != 'vanilla':
|
||||
determine_dungeon_restrictions(avail)
|
||||
possibles = figure_out_possible_exits(rem_exits)
|
||||
do_same_world_possible_connectors(lw_entrances, dw_entrances, possibles, avail)
|
||||
unused_entrances.update(lw_entrances)
|
||||
unused_entrances.update(dw_entrances)
|
||||
else:
|
||||
@@ -436,12 +440,16 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
|
||||
|
||||
if not keep_together:
|
||||
targets = [avail.one_way_map[x] for x in holes_to_shuffle]
|
||||
connect_random(holes_to_shuffle, targets, avail)
|
||||
if avail.swapped:
|
||||
connect_swapped(holes_to_shuffle, targets, avail)
|
||||
else:
|
||||
connect_random(holes_to_shuffle, targets, avail)
|
||||
remove_from_list(entrances, holes_to_shuffle)
|
||||
remove_from_list(exits, targets)
|
||||
return # we're done here
|
||||
|
||||
hole_entrances, hole_targets = [], []
|
||||
leftover_hole_entrances, leftover_hole_targets = [], []
|
||||
for hole in drop_map:
|
||||
if hole in avail.original_entrances and hole in linked_drop_map:
|
||||
linked_entrance = linked_drop_map[hole]
|
||||
@@ -451,40 +459,39 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
|
||||
target_drop = avail.one_way_map[hole]
|
||||
if target_exit in exits and target_drop in exits:
|
||||
hole_targets.append((target_exit, target_drop))
|
||||
else:
|
||||
if hole in avail.original_entrances and hole in entrances:
|
||||
leftover_hole_entrances.append(hole)
|
||||
if drop_map[hole] in exits:
|
||||
leftover_hole_targets.append(drop_map[hole])
|
||||
|
||||
random.shuffle(hole_entrances)
|
||||
if not cross_world and 'Sanctuary Grave' in holes_to_shuffle:
|
||||
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
|
||||
is_hc_in_dw = avail.world.mode[avail.player] == 'inverted'
|
||||
if hc.connected_region:
|
||||
is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld
|
||||
chosen_entrance = None
|
||||
if is_hc_in_dw:
|
||||
if avail.swapped:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary')
|
||||
if not cross_world:
|
||||
if 'Sanctuary Grave' in holes_to_shuffle:
|
||||
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
|
||||
is_hc_in_dw = avail.world.mode[avail.player] == 'inverted'
|
||||
if hc.connected_region:
|
||||
is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld
|
||||
chosen_entrance = None
|
||||
if is_hc_in_dw:
|
||||
if avail.swapped:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary')
|
||||
if not chosen_entrance:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
|
||||
if not chosen_entrance:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
|
||||
if not chosen_entrance:
|
||||
if avail.swapped:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary')
|
||||
if not chosen_entrance:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances)
|
||||
if avail.swapped:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary')
|
||||
if not chosen_entrance:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances)
|
||||
|
||||
if chosen_entrance:
|
||||
hole_entrances.remove(chosen_entrance)
|
||||
sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit')
|
||||
hole_targets.remove(sanc_interior)
|
||||
connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit
|
||||
connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole
|
||||
remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]])
|
||||
remove_from_list(exits, [sanc_interior[0], sanc_interior[1]])
|
||||
if avail.swapped and drop_map[chosen_entrance[1]] != sanc_interior[1]:
|
||||
swap_ent, swap_ext = connect_swap(chosen_entrance[0], sanc_interior[0], avail)
|
||||
swap_drop, swap_tgt = connect_swap(chosen_entrance[1], sanc_interior[1], avail)
|
||||
hole_entrances.remove((swap_ent, swap_drop))
|
||||
hole_targets.remove((swap_ext, swap_tgt))
|
||||
remove_from_list(entrances, [swap_ent, swap_drop])
|
||||
remove_from_list(exits, [swap_ext, swap_tgt])
|
||||
if chosen_entrance:
|
||||
connect_hole_via_interior(chosen_entrance, 'Sanctuary Exit', hole_entrances, hole_targets, entrances, exits, avail)
|
||||
if 'Skull Woods First Section Hole (North)' in holes_to_shuffle:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
|
||||
connect_hole_via_interior(chosen_entrance, 'Skull Woods First Section Exit', hole_entrances, hole_targets, entrances, exits, avail)
|
||||
if 'Skull Woods Second Section Hole' in holes_to_shuffle:
|
||||
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
|
||||
connect_hole_via_interior(chosen_entrance, 'Skull Woods Second Section Exit (East)', hole_entrances, hole_targets, entrances, exits, avail)
|
||||
|
||||
random.shuffle(hole_targets)
|
||||
while len(hole_entrances):
|
||||
@@ -506,6 +513,31 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
|
||||
remove_from_list(entrances, [swap_ent, swap_drop])
|
||||
remove_from_list(exits, [swap_ext, swap_tgt])
|
||||
|
||||
if leftover_hole_entrances and leftover_hole_targets:
|
||||
remove_from_list(entrances, leftover_hole_entrances)
|
||||
remove_from_list(exits, leftover_hole_targets)
|
||||
if avail.swapped:
|
||||
connect_swapped(leftover_hole_entrances, leftover_hole_targets, avail)
|
||||
else:
|
||||
connect_random(leftover_hole_entrances, leftover_hole_targets, avail)
|
||||
|
||||
|
||||
def connect_hole_via_interior(chosen_entrance, interior, hole_entrances, hole_targets, entrances, exits, avail):
|
||||
hole_entrances.remove(chosen_entrance)
|
||||
interior = next(target for target in hole_targets if target[0] == interior)
|
||||
hole_targets.remove(interior)
|
||||
connect_two_way(chosen_entrance[0], interior[0], avail)
|
||||
connect_entrance(chosen_entrance[1], interior[1], avail)
|
||||
remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]])
|
||||
remove_from_list(exits, [interior[0], interior[1]])
|
||||
if avail.swapped and drop_map[chosen_entrance[1]] != interior[1]:
|
||||
swap_ent, swap_ext = connect_swap(chosen_entrance[0], interior[0], avail)
|
||||
swap_drop, swap_tgt = connect_swap(chosen_entrance[1], interior[1], avail)
|
||||
hole_entrances.remove((swap_ent, swap_drop))
|
||||
hole_targets.remove((swap_ext, swap_tgt))
|
||||
remove_from_list(entrances, [swap_ent, swap_drop])
|
||||
remove_from_list(exits, [swap_ext, swap_tgt])
|
||||
|
||||
|
||||
def do_links_house(entrances, exits, avail, cross_world):
|
||||
lh_exit = 'Links House Exit'
|
||||
@@ -628,38 +660,77 @@ def figure_out_true_exits(exits, avail):
|
||||
return multi_exit_caves
|
||||
|
||||
|
||||
# todo: figure out hyrule forced better
|
||||
def figure_out_possible_exits(exits):
|
||||
possible_multi_exit_caves = []
|
||||
for item in doors_possible_connectors:
|
||||
if item in exits:
|
||||
remove_from_list(exits, item)
|
||||
possible_multi_exit_caves.append(item)
|
||||
return possible_multi_exit_caves
|
||||
|
||||
|
||||
def determine_dungeon_restrictions(avail):
|
||||
check_for_hc = (avail.is_standard() or avail.world.doorShuffle[avail.player] != 'vanilla')
|
||||
for check in dungeon_restriction_checks:
|
||||
dungeon_exits, drop_regions = check
|
||||
if check_for_hc and any('Hyrule Castle' in x for x in dungeon_exits):
|
||||
avail.same_world_restricted.update({x: 'LightWorld' for x in dungeon_exits})
|
||||
else:
|
||||
restriction = None
|
||||
for x in dungeon_exits:
|
||||
ent = avail.world.get_entrance(x, avail.player)
|
||||
if ent.connected_region:
|
||||
if ent.connected_region.type == RegionType.LightWorld:
|
||||
restriction = 'LightWorld'
|
||||
elif ent.connected_region.type == RegionType.DarkWorld:
|
||||
restriction = 'DarkWorld'
|
||||
# Holes only restrict
|
||||
for x in drop_regions:
|
||||
region = avail.world.get_region(x, avail.player)
|
||||
ent = next((ent for ent in region.entrances if ent.parent_region and ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]), None)
|
||||
if ent:
|
||||
if ent.parent_region.type == RegionType.LightWorld and not avail.inverted:
|
||||
restriction = 'LightWorld'
|
||||
elif ent.parent_region.type == RegionType.DarkWorld and avail.inverted:
|
||||
restriction = 'DarkWorld'
|
||||
if restriction:
|
||||
avail.same_world_restricted.update({x: restriction for x in dungeon_exits})
|
||||
|
||||
|
||||
def figure_out_must_exits_same_world(entrances, exits, avail):
|
||||
lw_entrances, dw_entrances = [], []
|
||||
hyrule_forced = None
|
||||
check_for_hc = (avail.is_standard() or avail.world.doorShuffle[avail.player] != 'vanilla')
|
||||
|
||||
for x in entrances:
|
||||
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
|
||||
|
||||
multi_exit_caves = figure_out_connectors(exits)
|
||||
if check_for_hc:
|
||||
for option in multi_exit_caves:
|
||||
if any(x in option for x in ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (East)',
|
||||
'Hyrule Castle Exit (West)']):
|
||||
hyrule_forced = [option]
|
||||
if hyrule_forced:
|
||||
remove_from_list(multi_exit_caves, hyrule_forced)
|
||||
if not avail.inverted and not avail.skull_handled:
|
||||
skull_connector = [x for x in ['Skull Woods Second Section Exit (West)', 'Skull Woods Second Section Exit (East)'] if x in exits]
|
||||
multi_exit_caves.append(skull_connector)
|
||||
|
||||
must_exit_lw, must_exit_dw, unfiltered_lw, unfiltered_dw = must_exits_helper(avail, lw_entrances, dw_entrances)
|
||||
|
||||
return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves, hyrule_forced
|
||||
return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves
|
||||
|
||||
|
||||
def must_exits_helper(avail, lw_entrances, dw_entrances):
|
||||
must_exit_lw_orig = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy()
|
||||
must_exit_dw_orig = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy()
|
||||
if not avail.inverted and not avail.skull_handled:
|
||||
must_exit_dw_orig.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section'))
|
||||
must_exit_dw_orig.append('Skull Woods Second Section Door (West)')
|
||||
must_exit_lw = must_exit_filter(avail, must_exit_lw_orig, lw_entrances)
|
||||
must_exit_dw = must_exit_filter(avail, must_exit_dw_orig, dw_entrances)
|
||||
return must_exit_lw, must_exit_dw, flatten(must_exit_lw_orig), flatten(must_exit_dw_orig)
|
||||
|
||||
|
||||
def filter_restricted_caves(multi_exit_caves, restriction, avail):
|
||||
candidates = []
|
||||
for cave in multi_exit_caves:
|
||||
if all(x not in avail.same_world_restricted or avail.same_world_restricted[x] == restriction for x in cave):
|
||||
candidates.append(cave)
|
||||
return candidates
|
||||
|
||||
|
||||
def flatten(list_to_flatten):
|
||||
ret = []
|
||||
for item in list_to_flatten:
|
||||
@@ -672,11 +743,14 @@ def flatten(list_to_flatten):
|
||||
|
||||
def figure_out_must_exits_cross_world(entrances, exits, avail):
|
||||
multi_exit_caves = figure_out_connectors(exits)
|
||||
if not avail.skull_handled:
|
||||
skull_connector = [x for x in ['Skull Woods Second Section Exit (West)', 'Skull Woods Second Section Exit (East)'] if x in exits]
|
||||
multi_exit_caves.append(skull_connector)
|
||||
|
||||
must_exit_lw = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy()
|
||||
must_exit_dw = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy()
|
||||
if not avail.inverted and not avail.skull_handled:
|
||||
must_exit_dw.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section'))
|
||||
must_exit_dw.append('Skull Woods Second Section Door (West)')
|
||||
must_exit = must_exit_filter(avail, must_exit_lw + must_exit_dw, entrances)
|
||||
|
||||
return must_exit, multi_exit_caves
|
||||
@@ -687,7 +761,7 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
|
||||
random.shuffle(dw_entrances)
|
||||
random.shuffle(caves)
|
||||
while caves:
|
||||
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill
|
||||
# connect highest-exit-count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill
|
||||
cave_candidate = (None, 0)
|
||||
for i, cave in enumerate(caves):
|
||||
if isinstance(cave, str):
|
||||
@@ -696,12 +770,19 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
|
||||
cave_candidate = (i, len(cave))
|
||||
cave = caves.pop(cave_candidate[0])
|
||||
|
||||
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
|
||||
if isinstance(cave, str):
|
||||
cave = (cave,)
|
||||
target, restriction = None, None
|
||||
if any(x in avail.same_world_restricted for x in cave):
|
||||
restriction = next(avail.same_world_restricted[x] for x in cave if x in avail.same_world_restricted)
|
||||
target = lw_entrances if restriction == 'LightWorld' else dw_entrances
|
||||
if target is None:
|
||||
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
|
||||
|
||||
# check if we can still fit the cave into our target group
|
||||
if len(target) < len(cave):
|
||||
if restriction:
|
||||
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (main)')
|
||||
# need to use other set
|
||||
target = lw_entrances if target is dw_entrances else dw_entrances
|
||||
|
||||
@@ -715,6 +796,18 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
|
||||
connect_two_way(target.pop(), ext, avail)
|
||||
|
||||
|
||||
def do_same_world_possible_connectors(lw_entrances, dw_entrances, possibles, avail):
|
||||
random.shuffle(possibles)
|
||||
while possibles:
|
||||
possible = possibles.pop()
|
||||
target = None
|
||||
if possible in avail.same_world_restricted:
|
||||
target = lw_entrances if avail.same_world_restricted[possible] == 'LightWorld' else dw_entrances
|
||||
if target is None:
|
||||
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
|
||||
connect_two_way(target.pop(), possible, avail)
|
||||
determine_dungeon_restrictions(avail)
|
||||
|
||||
def do_cross_world_connectors(entrances, caves, avail):
|
||||
random.shuffle(entrances)
|
||||
random.shuffle(caves)
|
||||
@@ -762,6 +855,37 @@ def do_cross_world_connectors(entrances, caves, avail):
|
||||
break
|
||||
|
||||
|
||||
def handle_skull_woods_drops(avail, pool, mode_cfg):
|
||||
skull_woods = avail.world.skullwoods[avail.player]
|
||||
if skull_woods in ['restricted', 'loose']:
|
||||
for drop in pool:
|
||||
target = drop_map[drop]
|
||||
connect_entrance(drop, target, avail)
|
||||
elif skull_woods == 'original':
|
||||
holes, targets = find_entrances_and_targets_drops(avail, pool)
|
||||
if avail.swapped:
|
||||
connect_swapped(holes, targets, avail)
|
||||
else:
|
||||
connect_random(holes, targets, avail)
|
||||
elif skull_woods == 'followlinked':
|
||||
keep_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True
|
||||
if keep_together:
|
||||
for drop in ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)']:
|
||||
target = drop_map[drop]
|
||||
connect_entrance(drop, target, avail)
|
||||
|
||||
|
||||
def handle_skull_woods_entrances(avail, pool):
|
||||
skull_woods = avail.world.skullwoods[avail.player]
|
||||
if skull_woods in ['restricted', 'original']:
|
||||
entrances, exits = find_entrances_and_exits(avail, pool)
|
||||
if avail.swapped:
|
||||
connect_swapped(entrances, exits, avail, True)
|
||||
else:
|
||||
connect_random(entrances, exits, avail, True)
|
||||
avail.skull_handled = True
|
||||
|
||||
|
||||
def do_fixed_shuffle(avail, entrance_list):
|
||||
max_size = 0
|
||||
options = {}
|
||||
@@ -803,8 +927,6 @@ def do_same_world_shuffle(avail, pool_def):
|
||||
multi_exit = pool_def['connectors']
|
||||
# complete_entrance_set = set()
|
||||
lw_entrances, dw_entrances, multi_exits_caves, other_exits = [], [], [], []
|
||||
hyrule_forced = None
|
||||
check_for_hc = avail.is_standard() or avail.world.doorShuffle[avail.player] != 'vanilla'
|
||||
|
||||
single_entrances, single_exits = find_entrances_and_exits(avail, single_exit)
|
||||
other_exits.extend(single_exits)
|
||||
@@ -814,12 +936,7 @@ def do_same_world_shuffle(avail, pool_def):
|
||||
for option in multi_exit:
|
||||
multi_entrances, multi_exits = find_entrances_and_exits(avail, option)
|
||||
# complete_entrance_set.update(multi_entrances)
|
||||
if check_for_hc and any(x in multi_entrances for x in ['Hyrule Castle Entrance (South)',
|
||||
'Hyrule Castle Entrance (East)',
|
||||
'Hyrule Castle Entrance (West)']):
|
||||
hyrule_forced = [multi_exits]
|
||||
else:
|
||||
multi_exits_caves.append(multi_exits)
|
||||
multi_exits_caves.append(multi_exits)
|
||||
for x in multi_entrances:
|
||||
(dw_entrances, lw_entrances)[x in LW_Entrances].append(x)
|
||||
|
||||
@@ -828,11 +945,16 @@ def do_same_world_shuffle(avail, pool_def):
|
||||
must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances)
|
||||
must_exit_dw = must_exit_filter(avail, must_exit_dw, dw_entrances)
|
||||
|
||||
if hyrule_forced:
|
||||
do_mandatory_connections(avail, lw_entrances, hyrule_forced, must_exit_lw)
|
||||
else:
|
||||
do_mandatory_connections(avail, lw_entrances, multi_exits_caves, must_exit_lw)
|
||||
do_mandatory_connections(avail, dw_entrances, multi_exits_caves, must_exit_dw)
|
||||
determine_dungeon_restrictions(avail)
|
||||
lw_candidates = filter_restricted_caves(multi_exits_caves, 'LightWorld', avail)
|
||||
other_candidates = [x for x in multi_exits_caves if x not in lw_candidates] # remember those not passed in
|
||||
do_mandatory_connections(avail, lw_entrances, lw_candidates, must_exit_lw)
|
||||
multi_exits_caves = other_candidates + lw_candidates # rebuild list from the lw_candidates and those not passed
|
||||
|
||||
dw_candidates = filter_restricted_caves(multi_exits_caves, 'DarkWorld', avail)
|
||||
other_candidates = [x for x in multi_exits_caves if x not in dw_candidates] # remember those not passed in
|
||||
do_mandatory_connections(avail, dw_entrances, dw_candidates, must_exit_dw)
|
||||
multi_exits_caves = other_candidates + dw_candidates # rebuild list from the dw_candidates and those not passed
|
||||
|
||||
# connect caves
|
||||
random.shuffle(lw_entrances)
|
||||
@@ -845,8 +967,15 @@ def do_same_world_shuffle(avail, pool_def):
|
||||
cave_candidate = (i, len(cave))
|
||||
cave = multi_exits_caves.pop(cave_candidate[0])
|
||||
|
||||
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
|
||||
target, restriction = None, None
|
||||
if any(x in avail.same_world_restricted for x in cave):
|
||||
restriction = next(avail.same_world_restricted[x] for x in cave if x in avail.same_world_restricted)
|
||||
target = lw_entrances if restriction == 'LightWorld' else dw_entrances
|
||||
if target is None:
|
||||
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
|
||||
if len(target) < len(cave): # swap because we ran out of entrances in that world
|
||||
if restriction:
|
||||
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (dungeonsfull)')
|
||||
target = lw_entrances if target is dw_entrances else dw_entrances
|
||||
|
||||
for ext in cave:
|
||||
@@ -1386,6 +1515,12 @@ modes = {
|
||||
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||
'Skull Woods Second Section Door (West)']
|
||||
},
|
||||
'skull_layout': {
|
||||
'special': 'vanilla',
|
||||
'condition': '',
|
||||
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||
'Skull Woods Second Section Door (West)']
|
||||
},
|
||||
'single_entrance_dungeon': {
|
||||
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
|
||||
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
|
||||
@@ -1419,13 +1554,15 @@ modes = {
|
||||
'sanc_flag': 'light_world', # always light world flag
|
||||
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
|
||||
'Agahnims Tower', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace',
|
||||
'Ganons Tower'],
|
||||
'Ganons Tower', 'Desert Palace Entrance (North)', 'Dark Death Mountain Ledge (East)'],
|
||||
'connectors': [['Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (East)',
|
||||
'Hyrule Castle Entrance (West)'],
|
||||
['Desert Palace Entrance (South)', 'Desert Palace Entrance (East)',
|
||||
'Desert Palace Entrance (West)', 'Desert Palace Entrance (North)'],
|
||||
'Desert Palace Entrance (West)'],
|
||||
['Turtle Rock', 'Turtle Rock Isolated Ledge Entrance',
|
||||
'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)']]
|
||||
'Dark Death Mountain Ledge (West)'],
|
||||
['Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)',
|
||||
'Skull Woods First Section Door']]
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -1611,6 +1748,12 @@ modes = {
|
||||
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||
'Skull Woods Second Section Door (West)']
|
||||
},
|
||||
'skull_layout': {
|
||||
'special': 'vanilla',
|
||||
'condition': '',
|
||||
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||
'Skull Woods Second Section Door (West)']
|
||||
},
|
||||
'single_entrance_dungeon': {
|
||||
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
|
||||
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
|
||||
@@ -1700,6 +1843,12 @@ modes = {
|
||||
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||
'Skull Woods Second Section Door (West)']
|
||||
},
|
||||
'skull_layout': {
|
||||
'special': 'vanilla',
|
||||
'condition': '',
|
||||
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||
'Skull Woods Second Section Door (West)']
|
||||
},
|
||||
'single_entrance_dungeon': {
|
||||
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
|
||||
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
|
||||
@@ -1801,7 +1950,10 @@ linked_drop_map = {
|
||||
'Lumberjack Tree Tree': 'Lumberjack Tree Cave',
|
||||
'Sanctuary Grave': 'Sanctuary',
|
||||
'Pyramid Hole': 'Pyramid Entrance',
|
||||
'Inverted Pyramid Hole': 'Inverted Pyramid Entrance'
|
||||
'Inverted Pyramid Hole': 'Inverted Pyramid Entrance',
|
||||
|
||||
'Skull Woods First Section Hole (North)': 'Skull Woods First Section Door',
|
||||
'Skull Woods Second Section Hole': 'Skull Woods Second Section Door (East)',
|
||||
}
|
||||
|
||||
entrance_map = {
|
||||
@@ -2066,6 +2218,19 @@ Connector_Exit_Set = {
|
||||
'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)'
|
||||
}
|
||||
|
||||
dungeon_restriction_checks = [
|
||||
(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Sanctuary Exit'], ['Sewer Drop']),
|
||||
(['Desert Palace Exit (South)', 'Desert Palace Exit (East)', 'Desert Palace Exit (West)', 'Desert Palace Exit (North)'], []),
|
||||
(['Turtle Rock Exit (Front)', 'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)', 'Turtle Rock Ledge Exit (East)'], []),
|
||||
(['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit'],
|
||||
['Skull Pinball', 'Skull Left Drop', 'Skull Pot Circle', 'Skull Back Drop'])
|
||||
]
|
||||
|
||||
doors_possible_connectors = [
|
||||
'Sanctuary Exit', 'Desert Palace Exit (North)', 'Skull Woods First Section Exit',
|
||||
'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit'
|
||||
]
|
||||
|
||||
# Entrances that cannot be used to access a must_exit entrance - symmetrical to allow reverse lookups
|
||||
Must_Exit_Invalid_Connections = defaultdict(set, {
|
||||
'Dark Death Mountain Ledge (East)': {'Dark Death Mountain Ledge (West)', 'Mimic Cave'},
|
||||
|
||||
@@ -131,6 +131,8 @@ def roll_settings(weights):
|
||||
|
||||
ret.shufflelinks = get_choice_bool('shufflelinks')
|
||||
ret.shuffletavern = get_choice_bool('shuffletavern')
|
||||
ret.skullwoods = get_choice('skullwoods')
|
||||
ret.linked_drops = get_choice('linked_drops')
|
||||
ret.pseudoboots = get_choice_bool('pseudoboots')
|
||||
ret.shopsanity = get_choice_bool('shopsanity')
|
||||
keydropshuffle = get_choice_bool('keydropshuffle')
|
||||
|
||||
Reference in New Issue
Block a user