LttP: implement new dungeon_items handling

LttP: move glitch_boots to new options system
WebHost: options.yaml no longer lists aliases
General: remove region.can_fill, it was only used as a hack to make dungeon-specific items to work
This commit is contained in:
Fabian Dill 2021-08-30 16:31:56 +02:00
parent a124a7a82a
commit 5daadcb2d5
15 changed files with 235 additions and 192 deletions

View File

@ -95,10 +95,6 @@ class MultiWorld():
set_player_attr('can_access_trock_big_chest', None)
set_player_attr('can_access_trock_middle', None)
set_player_attr('fix_fake_world', True)
set_player_attr('mapshuffle', False)
set_player_attr('compassshuffle', False)
set_player_attr('keyshuffle', False)
set_player_attr('bigkeyshuffle', False)
set_player_attr('difficulty_requirements', None)
set_player_attr('boss_shuffle', 'none')
set_player_attr('enemy_shuffle', False)
@ -118,7 +114,6 @@ class MultiWorld():
set_player_attr('blue_clock_time', 2)
set_player_attr('green_clock_time', 4)
set_player_attr('can_take_damage', True)
set_player_attr('glitch_boots', True)
set_player_attr('progression_balancing', True)
set_player_attr('local_items', set())
set_player_attr('non_local_items', set())
@ -158,6 +153,10 @@ class MultiWorld():
def get_game_players(self, game_name: str):
return tuple(player for player in self.player_ids if self.game[player] == game_name)
@functools.lru_cache()
def get_game_worlds(self, game_name: str):
return tuple(world for player, world in self.worlds.items() if self.game[player] == game_name)
def get_name_string_for_object(self, obj) -> str:
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_name(obj.player)})'
@ -537,9 +536,7 @@ class CollectionState(object):
locations = {location for location in locations if location.event}
while new_locations:
reachable_events = {location for location in locations if
(not key_only or
(not self.world.keyshuffle[location.item.player] and location.item.smallkey)
or (not self.world.bigkeyshuffle[location.item.player] and location.item.bigkey))
(not key_only or getattr(location.item, "locked_dungeon_item", False))
and location.can_reach(self)}
new_locations = reachable_events - self.events
for event in new_locations:
@ -572,7 +569,7 @@ class CollectionState(object):
def has_key(self, item, player, count: int = 1):
if self.world.logic[player] == 'nologic':
return True
if self.world.keyshuffle[player] == "universal":
if self.world.smallkeyshuffle[player] == "universal":
return self.can_buy_unlimited('Small Key (Universal)', player)
return self.prog_items[item, player] >= count
@ -807,12 +804,6 @@ class Region(object):
return True
return False
# look into moving this to ALttPLocation?
def can_fill(self, item: Item):
if getattr(item, "locked_dungeon_item", False):
return item.player == self.player and self.dungeon.is_dungeon_item(item)
return True
def __repr__(self):
return self.__str__()
@ -854,8 +845,8 @@ class Entrance(object):
world = self.parent_region.world if self.parent_region else None
return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})'
class Dungeon(object):
class Dungeon(object):
def __init__(self, name: str, regions, big_key, small_keys, dungeon_items, player: int):
self.name = name
self.regions = regions
@ -930,7 +921,7 @@ class Location():
self.item: Optional[Item] = None
def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool:
return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state)))
return self.always_allow(state, item) or (self.item_rule(item) and (not check_access or self.can_reach(state)))
def can_reach(self, state: CollectionState) -> bool:
# self.access_rule computes faster on average, so placing it first for faster abort
@ -1146,10 +1137,6 @@ class Spoiler():
'open_pyramid': self.world.open_pyramid,
'accessibility': self.world.accessibility,
'hints': self.world.hints,
'mapshuffle': self.world.mapshuffle,
'compassshuffle': self.world.compassshuffle,
'keyshuffle': self.world.keyshuffle,
'bigkeyshuffle': self.world.bigkeyshuffle,
'boss_shuffle': self.world.boss_shuffle,
'enemy_shuffle': self.world.enemy_shuffle,
'enemy_health': self.world.enemy_health,
@ -1244,15 +1231,6 @@ class Spoiler():
outfile.write('Entrance Shuffle Seed %s\n' % self.metadata['er_seeds'][player])
outfile.write('Pyramid hole pre-opened: %s\n' % (
'Yes' if self.metadata['open_pyramid'][player] else 'No'))
outfile.write('Map shuffle: %s\n' %
('Yes' if self.metadata['mapshuffle'][player] else 'No'))
outfile.write('Compass shuffle: %s\n' %
('Yes' if self.metadata['compassshuffle'][player] else 'No'))
outfile.write(
'Small Key shuffle: %s\n' % (bool_to_text(self.metadata['keyshuffle'][player])))
outfile.write('Big Key shuffle: %s\n' % (
'Yes' if self.metadata['bigkeyshuffle'][player] else 'No'))
outfile.write('Shop inventory shuffle: %s\n' %
bool_to_text("i" in self.metadata["shop_shuffle"][player]))
outfile.write('Shop price shuffle: %s\n' %

View File

@ -547,22 +547,6 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
ret.restrict_dungeon_item_on_boss = get_choice_legacy('restrict_dungeon_item_on_boss', weights, False)
dungeon_items = get_choice_legacy('dungeon_items', weights)
if dungeon_items == 'full' or dungeon_items == True:
dungeon_items = 'mcsb'
elif dungeon_items == 'standard':
dungeon_items = ""
elif not dungeon_items:
dungeon_items = ""
if "u" in dungeon_items:
dungeon_items.replace("s", "")
ret.mapshuffle = get_choice_legacy('map_shuffle', weights, 'm' in dungeon_items)
ret.compassshuffle = get_choice_legacy('compass_shuffle', weights, 'c' in dungeon_items)
ret.keyshuffle = get_choice_legacy('smallkey_shuffle', weights,
'universal' if 'u' in dungeon_items else 's' in dungeon_items)
ret.bigkeyshuffle = get_choice_legacy('bigkey_shuffle', weights, 'b' in dungeon_items)
entrance_shuffle = get_choice_legacy('entrance_shuffle', weights, 'vanilla')
if entrance_shuffle.startswith('none-'):
ret.shuffle = 'vanilla'
@ -661,13 +645,6 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
if not ret.required_medallions[index]:
raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}")
ret.glitch_boots = get_choice_legacy('glitch_boots', weights, True)
if get_choice_legacy("local_keys", weights, "l" in dungeon_items):
# () important for ordering of commands, without them the Big Keys section is part of the Small Key else
ret.local_items |= item_name_groups["Small Keys"] if ret.keyshuffle else set()
ret.local_items |= item_name_groups["Big Keys"] if ret.bigkeyshuffle else set()
ret.plando_items = []
if "items" in plando_options:

21
Main.py
View File

@ -66,11 +66,6 @@ def main(args, seed=None):
world.retro = args.retro.copy()
world.hints = args.hints.copy()
world.mapshuffle = args.mapshuffle.copy()
world.compassshuffle = args.compassshuffle.copy()
world.keyshuffle = args.keyshuffle.copy()
world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.open_pyramid = args.open_pyramid.copy()
world.boss_shuffle = args.shufflebosses.copy()
world.enemy_shuffle = args.enemy_shuffle.copy()
@ -87,7 +82,6 @@ def main(args, seed=None):
world.green_clock_time = args.green_clock_time.copy()
world.shufflepots = args.shufflepots.copy()
world.dungeon_counters = args.dungeon_counters.copy()
world.glitch_boots = args.glitch_boots.copy()
world.triforce_pieces_available = args.triforce_pieces_available.copy()
world.triforce_pieces_required = args.triforce_pieces_required.copy()
world.shop_shuffle = args.shop_shuffle.copy()
@ -140,21 +134,6 @@ def main(args, seed=None):
if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
world.local_items[player].add('Triforce Piece')
# dungeon items can't be in non-local if the appropriate dungeon item shuffle setting is not set.
if not world.mapshuffle[player]:
world.non_local_items[player] -= item_name_groups['Maps']
if not world.compassshuffle[player]:
world.non_local_items[player] -= item_name_groups['Compasses']
if not world.keyshuffle[player]:
world.non_local_items[player] -= item_name_groups['Small Keys']
# This could probably use a more elegant solution.
elif world.keyshuffle[player] == True and world.mode[player] == "Standard":
world.local_items[player].add("Small Key (Hyrule Castle)")
if not world.bigkeyshuffle[player]:
world.non_local_items[player] -= item_name_groups['Big Keys']
# Not possible to place pendants/crystals out side of boss prizes yet.
world.non_local_items[player] -= item_name_groups['Pendants']
world.non_local_items[player] -= item_name_groups['Crystals']

View File

@ -9,7 +9,7 @@ class AssembleOptions(type):
name_lookup = attrs["name_lookup"] = {}
# merge parent class options
for base in bases:
if hasattr(base, "options"):
if getattr(base, "options", None):
options.update(base.options)
name_lookup.update(base.name_lookup)
new_options = {name[7:].lower(): option_id for name, option_id in attrs.items() if
@ -147,6 +147,16 @@ class Choice(Option):
return cls(data)
return cls.from_text(str(data))
def __eq__(self, other):
if type(other) == str:
return other == self.current_key
elif type(other) == int:
return other == self.value
elif type(other) == bool:
return other == bool(self.value)
else:
raise TypeError(f"Can't compare {self.__class__.__name__} with {other.__class__.__name__}")
class Range(Option, int):
range_start = 0

View File

@ -93,10 +93,10 @@ def launch_generator(pool: multiprocessing.pool.Pool, generation: Generation):
"sid": generation.id,
"owner": generation.owner},
handle_generation_success, handle_generation_failure)
except:
except Exception as e:
generation.state = STATE_ERROR
commit()
raise
logging.exception(e)
else:
generation.state = STATE_STARTED

View File

@ -52,8 +52,8 @@ progression_balancing:
# exclude_locations: # Force certain locations to never contain progression items, and always be filled with junk.
# - "Master Sword Pedestal"
{{ game }}:
{%- for option_name, option in options.items() %}
{{ option_name }}:{% if option.__doc__ %} # {{ option.__doc__ | replace('\n', '\n#') | indent(4, first=False) }}{% endif %}
{%- for option_key, option in options.items() %}
{{ option_key }}:{% if option.__doc__ %} # {{ option.__doc__ | replace('\n', '\n#') | indent(4, first=False) }}{% endif %}
{%- if option.range_start is defined %}
# you can add additional values between minimum and maximum
{{ option.range_start }}: 0 # minimum value
@ -62,7 +62,7 @@ progression_balancing:
random-low: 0
random-high: 0
{%- elif option.options -%}
{%- for sub_option_name, suboption_option_id in option.options.items() %}
{%- for suboption_option_id, sub_option_name in option.name_lookup.items() %}
{{ sub_option_name }}: {% if suboption_option_id == option.default %}50{% else %}0{% endif %}
{%- endfor -%}
{%- else %}

View File

@ -7,8 +7,11 @@ from worlds.alttp.Regions import lookup_boss_drops
def create_dungeons(world, player):
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.keyshuffle[player] == "universal" else small_keys,
dungeon = Dungeon(name, dungeon_regions, big_key,
[] if world.smallkeyshuffle[player] == "universal" else small_keys,
dungeon_items, player)
for item in dungeon.all_items:
item.dungeon = dungeon
dungeon.boss = BossFactory(default_boss, player) if default_boss else None
for region in dungeon.regions:
world.get_region(region, player).dungeon = dungeon
@ -21,22 +24,80 @@ def create_dungeons(world, player):
EP = make_dungeon('Eastern Palace', 'Armos Knights', ['Eastern Palace'],
ItemFactory('Big Key (Eastern Palace)', player), [],
ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], player))
DP = make_dungeon('Desert Palace', 'Lanmolas', ['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)', 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)', player), [ItemFactory('Small Key (Desert Palace)', player)], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
ToH = make_dungeon('Tower of Hera', 'Moldorm', ['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'], ItemFactory('Big Key (Tower of Hera)', player), [ItemFactory('Small Key (Tower of Hera)', player)], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
PoD = make_dungeon('Palace of Darkness', 'Helmasaur King', ['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)', 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)', 'Palace of Darkness (North)', 'Palace of Darkness (Maze)', 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'], ItemFactory('Big Key (Palace of Darkness)', player), ItemFactory(['Small Key (Palace of Darkness)'] * 6, player), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], player))
TT = make_dungeon('Thieves Town', 'Blind', ['Thieves Town (Entrance)', 'Thieves Town (Deep)', 'Blind Fight'], ItemFactory('Big Key (Thieves Town)', player), [ItemFactory('Small Key (Thieves Town)', player)], ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'], player))
SW = make_dungeon('Skull Woods', 'Mothula', ['Skull Woods Final Section (Entrance)', 'Skull Woods First Section', 'Skull Woods Second Section', 'Skull Woods Second Section (Drop)', 'Skull Woods Final Section (Mothula)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)'], ItemFactory('Big Key (Skull Woods)', player), ItemFactory(['Small Key (Skull Woods)'] * 3, player), ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player))
SP = make_dungeon('Swamp Palace', 'Arrghus', ['Swamp Palace (Entrance)', 'Swamp Palace (First Room)', 'Swamp Palace (Starting Area)', 'Swamp Palace (Center)', 'Swamp Palace (North)'], ItemFactory('Big Key (Swamp Palace)', player), [ItemFactory('Small Key (Swamp Palace)', player)], ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], player))
IP = make_dungeon('Ice Palace', 'Kholdstare', ['Ice Palace (Entrance)', 'Ice Palace (Main)', 'Ice Palace (East)', 'Ice Palace (East Top)', 'Ice Palace (Kholdstare)'], ItemFactory('Big Key (Ice Palace)', player), ItemFactory(['Small Key (Ice Palace)'] * 2, player), ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
MM = make_dungeon('Misery Mire', 'Vitreous', ['Misery Mire (Entrance)', 'Misery Mire (Main)', 'Misery Mire (West)', 'Misery Mire (Final Area)', 'Misery Mire (Vitreous)'], ItemFactory('Big Key (Misery Mire)', player), ItemFactory(['Small Key (Misery Mire)'] * 3, player), ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
TR = make_dungeon('Turtle Rock', 'Trinexx', ['Turtle Rock (Entrance)', 'Turtle Rock (First Section)', 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Turtle Rock (Crystaroller Room)', 'Turtle Rock (Dark Room)', 'Turtle Rock (Eye Bridge)', 'Turtle Rock (Trinexx)'], ItemFactory('Big Key (Turtle Rock)', player), ItemFactory(['Small Key (Turtle Rock)'] * 4, player), ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
DP = make_dungeon('Desert Palace', 'Lanmolas',
['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)',
'Desert Palace East'], ItemFactory('Big Key (Desert Palace)', player),
[ItemFactory('Small Key (Desert Palace)', player)],
ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player))
ToH = make_dungeon('Tower of Hera', 'Moldorm',
['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'],
ItemFactory('Big Key (Tower of Hera)', player),
[ItemFactory('Small Key (Tower of Hera)', player)],
ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player))
PoD = make_dungeon('Palace of Darkness', 'Helmasaur King',
['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)',
'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)',
'Palace of Darkness (North)', 'Palace of Darkness (Maze)',
'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'],
ItemFactory('Big Key (Palace of Darkness)', player),
ItemFactory(['Small Key (Palace of Darkness)'] * 6, player),
ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], player))
TT = make_dungeon('Thieves Town', 'Blind', ['Thieves Town (Entrance)', 'Thieves Town (Deep)', 'Blind Fight'],
ItemFactory('Big Key (Thieves Town)', player), [ItemFactory('Small Key (Thieves Town)', player)],
ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'], player))
SW = make_dungeon('Skull Woods', 'Mothula', ['Skull Woods Final Section (Entrance)', 'Skull Woods First Section',
'Skull Woods Second Section', 'Skull Woods Second Section (Drop)',
'Skull Woods Final Section (Mothula)',
'Skull Woods First Section (Right)',
'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)'],
ItemFactory('Big Key (Skull Woods)', player),
ItemFactory(['Small Key (Skull Woods)'] * 3, player),
ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player))
SP = make_dungeon('Swamp Palace', 'Arrghus',
['Swamp Palace (Entrance)', 'Swamp Palace (First Room)', 'Swamp Palace (Starting Area)',
'Swamp Palace (Center)', 'Swamp Palace (North)'], ItemFactory('Big Key (Swamp Palace)', player),
[ItemFactory('Small Key (Swamp Palace)', player)],
ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], player))
IP = make_dungeon('Ice Palace', 'Kholdstare',
['Ice Palace (Entrance)', 'Ice Palace (Main)', 'Ice Palace (East)', 'Ice Palace (East Top)',
'Ice Palace (Kholdstare)'], ItemFactory('Big Key (Ice Palace)', player),
ItemFactory(['Small Key (Ice Palace)'] * 2, player),
ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player))
MM = make_dungeon('Misery Mire', 'Vitreous',
['Misery Mire (Entrance)', 'Misery Mire (Main)', 'Misery Mire (West)', 'Misery Mire (Final Area)',
'Misery Mire (Vitreous)'], ItemFactory('Big Key (Misery Mire)', player),
ItemFactory(['Small Key (Misery Mire)'] * 3, player),
ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player))
TR = make_dungeon('Turtle Rock', 'Trinexx',
['Turtle Rock (Entrance)', 'Turtle Rock (First Section)', 'Turtle Rock (Chain Chomp Room)',
'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Turtle Rock (Crystaroller Room)',
'Turtle Rock (Dark Room)', 'Turtle Rock (Eye Bridge)', 'Turtle Rock (Trinexx)'],
ItemFactory('Big Key (Turtle Rock)', player),
ItemFactory(['Small Key (Turtle Rock)'] * 4, player),
ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player))
if world.mode[player] != 'inverted':
AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
GT = make_dungeon('Ganons Tower', 'Agahnim2', ['Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)', 'Ganons Tower (Compass Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)', 'Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)', 'Ganons Tower (Top)', 'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)', 'Agahnim 2'], ItemFactory('Big Key (Ganons Tower)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None,
ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
GT = make_dungeon('Ganons Tower', 'Agahnim2',
['Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)', 'Ganons Tower (Compass Room)',
'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)', 'Ganons Tower (Firesnake Room)',
'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)', 'Ganons Tower (Top)',
'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)', 'Agahnim 2'],
ItemFactory('Big Key (Ganons Tower)', player),
ItemFactory(['Small Key (Ganons Tower)'] * 4, player),
ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
else:
AT = make_dungeon('Inverted Agahnims Tower', 'Agahnim', ['Inverted Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
GT = make_dungeon('Inverted Ganons Tower', 'Agahnim2', ['Inverted Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)', 'Ganons Tower (Compass Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)', 'Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)', 'Ganons Tower (Top)', 'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)', 'Agahnim 2'], ItemFactory('Big Key (Ganons Tower)', player), ItemFactory(['Small Key (Ganons Tower)'] * 4, player), ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
AT = make_dungeon('Inverted Agahnims Tower', 'Agahnim', ['Inverted Agahnims Tower', 'Agahnim 1'], None,
ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
GT = make_dungeon('Inverted Ganons Tower', 'Agahnim2',
['Inverted Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)',
'Ganons Tower (Compass Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)',
'Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)',
'Ganons Tower (Top)', 'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)',
'Agahnim 2'], ItemFactory('Big Key (Ganons Tower)', player),
ItemFactory(['Small Key (Ganons Tower)'] * 4, player),
ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player))
GT.bosses['bottom'] = BossFactory('Armos Knights', player)
GT.bosses['middle'] = BossFactory('Lanmolas', player)
@ -53,25 +114,49 @@ def get_dungeon_item_pool(world):
return items
def fill_dungeons_restrictive(world):
"""Places dungeon-native items into their dungeons, places nothing if everything is shuffled outside."""
dungeon_items = [item for item in get_dungeon_item_pool(world) if
(((item.smallkey and not world.keyshuffle[item.player])
or (item.bigkey and not world.bigkeyshuffle[item.player])
or (item.map and not world.mapshuffle[item.player])
or (item.compass and not world.compassshuffle[item.player])
) and world.goal[item.player] != 'icerodhunt')]
if dungeon_items:
restricted_players = {player for player, restricted in world.restrict_dungeon_item_on_boss.items() if restricted}
locations = [location for location in world.get_unfilled_dungeon_locations()
if not (location.player in restricted_players and location.name in lookup_boss_drops)] # filter boss
def get_dungeon_item_pool_player(world, player):
items = [item for dungeon in world.dungeons.values() if dungeon.player == player for item in dungeon.all_items]
for item in items:
item.world = world
return items
world.random.shuffle(locations)
all_state_base = world.get_all_state()
# sort in the order Big Key, Small Key, Other before placing dungeon items
sort_order = {"BigKey": 3, "SmallKey": 2}
dungeon_items.sort(key=lambda item: sort_order.get(item.type, 1))
fill_restrictive(world, all_state_base, locations, dungeon_items, True, True)
def fill_dungeons_restrictive(autoworld, world):
"""Places dungeon-native items into their dungeons, places nothing if everything is shuffled outside."""
localized: set = set()
dungeon_specific: set = set()
for subworld in world.get_game_worlds("A Link to the Past"):
player = subworld.player
localized |= {(player, item_name) for item_name in
subworld.dungeon_local_item_names}
dungeon_specific |= {(player, item_name) for item_name in
subworld.dungeon_specific_item_names}
if localized:
in_dungeon_items = [item for item in get_dungeon_item_pool(world) if (item.player, item.name) in localized]
if in_dungeon_items:
restricted_players = {player for player, restricted in world.restrict_dungeon_item_on_boss.items() if
restricted}
locations = [location for location in world.get_unfilled_dungeon_locations()
# filter boss
if not (location.player in restricted_players and location.name in lookup_boss_drops)]
if dungeon_specific:
for location in locations:
dungeon = location.parent_region.dungeon
orig_rule = location.item_rule
location.item_rule = lambda item, location=location, dungeon=dungeon, orig_rule=orig_rule: \
(not (item.player, item.name) in dungeon_specific or item.dungeon is dungeon) and orig_rule
world.random.shuffle(locations)
all_state_base = world.get_all_state()
# Dungeon-locked items have to be placed first, to not run out of spaces for dungeon-locked items
# subsort in the order Big Key, Small Key, Other before placing dungeon items
sort_order = {"BigKey": 3, "SmallKey": 2}
in_dungeon_items.sort(
key=lambda item: sort_order.get(item.type, 1) +
(5 if (item.player, item.name) in dungeon_specific else 0))
fill_restrictive(world, all_state_base, locations, in_dungeon_items, True, True)
dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
@ -80,7 +165,8 @@ dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
'Palace of Darkness - Prize': [0x155B8],
'Swamp Palace - Prize': [0x155B7],
'Thieves\' Town - Prize': [0x155C6],
'Skull Woods - Prize': [0x155BA, 0x155BB, 0x155BC, 0x155BD, 0x15608, 0x15609, 0x1560A, 0x1560B],
'Skull Woods - Prize': [0x155BA, 0x155BB, 0x155BC, 0x155BD, 0x15608, 0x15609, 0x1560A,
0x1560B],
'Ice Palace - Prize': [0x155BF],
'Misery Mire - Prize': [0x155B9],
'Turtle Rock - Prize': [0x155C7, 0x155A7, 0x155AA, 0x155AB]}

View File

@ -206,26 +206,10 @@ def parse_arguments(argv, no_defaults=False):
time).
''', type=int)
parser.add_argument('--mapshuffle', default=defval(False),
help='Maps are no longer restricted to their dungeons, but can be anywhere',
action='store_true')
parser.add_argument('--compassshuffle', default=defval(False),
help='Compasses are no longer restricted to their dungeons, but can be anywhere',
action='store_true')
parser.add_argument('--keyshuffle', default=defval("off"), help='\
on: Small Keys are no longer restricted to their dungeons, but can be anywhere.\
universal: Makes all Small Keys usable in any dungeon and places shops to buy more keys.',
choices=["on", "universal", "off"])
parser.add_argument('--bigkeyshuffle', default=defval(False),
help='Big Keys are no longer restricted to their dungeons, but can be anywhere',
action='store_true')
parser.add_argument('--keysanity', default=defval(False), help=argparse.SUPPRESS, action='store_true')
parser.add_argument('--retro', default=defval(False), help='''\
Keys are universal, shooting arrows costs rupees,
and a few other little things make this more like Zelda-1.
''', action='store_true')
parser.add_argument('--startinventory', default=defval(''),
help='Specifies a list of items that will be in your starting inventory (separated by commas)')
parser.add_argument('--local_items', default=defval(''),
help='Specifies a list of items that will not spread across the multiworld (separated by commas)')
parser.add_argument('--non_local_items', default=defval(''),
@ -291,13 +275,10 @@ def parse_arguments(argv, no_defaults=False):
parser.add_argument('--restrict_dungeon_item_on_boss', default=defval(False), action="store_true")
parser.add_argument('--multi', default=defval(1), type=lambda value: min(max(int(value), 1), 255))
parser.add_argument('--names', default=defval(''))
parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1))
parser.add_argument('--outputpath')
parser.add_argument('--game', default="A Link to the Past")
parser.add_argument('--race', default=defval(False), action='store_true')
parser.add_argument('--outputname')
parser.add_argument('--disable_glitch_boots', default=defval(False), action='store_true', help='''\
turns off starting with Pegasus Boots in glitched modes.''')
parser.add_argument('--start_hints')
if multiargs.multi:
for player in range(1, multiargs.multi + 1):
@ -314,7 +295,6 @@ def parse_arguments(argv, no_defaults=False):
ret.plando_connections = []
ret.er_seeds = {}
ret.glitch_boots = not ret.disable_glitch_boots
if ret.timer == "none":
ret.timer = False
if ret.dungeon_counters == 'on':
@ -322,12 +302,6 @@ def parse_arguments(argv, no_defaults=False):
elif ret.dungeon_counters == 'off':
ret.dungeon_counters = False
if ret.keysanity:
ret.mapshuffle = ret.compassshuffle = ret.keyshuffle = ret.bigkeyshuffle = True
elif ret.keyshuffle == "on":
ret.keyshuffle = True
elif ret.keyshuffle == "off":
ret.keyshuffle = False
if multiargs.multi:
defaults = copy.deepcopy(ret)
for player in range(1, multiargs.multi + 1):
@ -336,7 +310,6 @@ def parse_arguments(argv, no_defaults=False):
for name in ['logic', 'mode', 'swordless', 'goal', 'difficulty', 'item_functionality',
'shuffle', 'open_pyramid', 'timer',
'countdown_start_time', 'red_clock_time', 'blue_clock_time', 'green_clock_time',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'local_items', 'non_local_items', 'retro', 'accessibility', 'hints', 'beemizer',
'shufflebosses', 'enemy_shuffle', 'enemy_health', 'enemy_damage', 'shufflepots',
'sprite',
@ -344,7 +317,7 @@ def parse_arguments(argv, no_defaults=False):
"triforce_pieces_required", "shop_shuffle",
"required_medallions", "start_hints",
"plando_items", "plando_texts", "plando_connections", "er_seeds",
'dungeon_counters', 'glitch_boots', 'killable_thieves',
'dungeon_counters', 'killable_thieves',
'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic',
'restrict_dungeon_item_on_boss', 'game']:
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)

View File

@ -5,9 +5,9 @@ from BaseClasses import Region, RegionType
from worlds.alttp.SubClasses import ALttPLocation
from worlds.alttp.Shops import TakeAny, total_shop_slots, set_up_shops, shuffle_shops
from worlds.alttp.Bosses import place_bosses
from worlds.alttp.Dungeons import get_dungeon_item_pool
from worlds.alttp.Dungeons import get_dungeon_item_pool_player
from worlds.alttp.EntranceShuffle import connect_entrance
from Fill import FillError, fill_restrictive
from Fill import FillError
from worlds.alttp.Items import ItemFactory, GetBeemizerItem
# This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space.
@ -274,7 +274,7 @@ def generate_itempool(world):
itempool.extend(['Rupees (300)'] * 34)
itempool.extend(['Bombs (10)'] * 5)
itempool.extend(['Arrows (10)'] * 7)
if world.keyshuffle[player] == 'universal':
if world.smallkeyshuffle[player] == 'universal':
itempool.extend(itemdiff.universal_keys)
itempool.append('Small Key (Universal)')
@ -362,12 +362,8 @@ def generate_itempool(world):
if treasure_hunt_icon is not None:
world.treasure_hunt_icon[player] = treasure_hunt_icon
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.player == player
and ((item.smallkey and world.keyshuffle[player])
or (item.bigkey and world.bigkeyshuffle[player])
or (item.map and world.mapshuffle[player])
or (item.compass and world.compassshuffle[player])
or world.goal[player] == 'icerodhunt')]
dungeon_items = [item for item in get_dungeon_item_pool_player(world, player)
if item.name not in world.worlds[player].dungeon_local_item_names]
if world.goal[player] == 'icerodhunt':
for item in dungeon_items:
@ -637,7 +633,7 @@ def get_pool_core(world, player: int):
if retro:
replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)'}
pool = ['Rupees (5)' if item in replace else item for item in pool]
if world.keyshuffle[player] == "universal":
if world.smallkeyshuffle[player] == "universal":
pool.extend(diff.universal_keys)
item_to_place = 'Small Key (Universal)' if goal != 'icerodhunt' else 'Nothing'
if mode == 'standard':
@ -774,7 +770,7 @@ def make_custom_item_pool(world, player):
itemtotal = itemtotal + 1
if mode == 'standard':
if world.keyshuffle[player] == "universal":
if world.smallkeyshuffle[player] == "universal":
key_location = world.random.choice(
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
@ -797,7 +793,7 @@ def make_custom_item_pool(world, player):
pool.extend(['Magic Mirror'] * customitemarray[22])
pool.extend(['Moon Pearl'] * customitemarray[28])
if world.keyshuffle == "universal":
if world.smallkeyshuffle == "universal":
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode
if itemtotal < total_items_to_place:
pool.extend(['Nothing'] * (total_items_to_place - itemtotal))

View File

@ -28,6 +28,46 @@ class Goal(Choice):
option_hand_in = 2
class DungeonItem(Choice):
value: int
option_original_dungeon = 0
option_own_dungeons = 1
option_own_world = 2
option_any_world = 3
option_different_world = 4
alias_true = 3
alias_false = 0
@property
def in_dungeon(self):
return self.value in {0, 1}
class BigKeyShuffle(DungeonItem):
"""Big Key Placement"""
item_name_group = "Big Keys"
displayname = "Big Key Shuffle"
class SmallKeyShuffle(DungeonItem):
"""Small Key Placement"""
option_universal = 5
item_name_group = "Small Keys"
displayname = "Small Key Shuffle"
class CompassShuffle(DungeonItem):
"""Compass Placement"""
item_name_group = "Compasses"
displayname = "Compass Shuffle"
class MapShuffle(DungeonItem):
"""Map Placement"""
item_name_group = "Maps"
displayname = "Map Shuffle"
class Crystals(Range):
range_start = 0
range_end = 7
@ -181,6 +221,10 @@ class TriforceHud(Choice):
alttp_options: typing.Dict[str, type(Option)] = {
"crystals_needed_for_gt": CrystalsTower,
"crystals_needed_for_ganon": CrystalsGanon,
"bigkeyshuffle": BigKeyShuffle,
"smallkeyshuffle": SmallKeyShuffle,
"compassshuffle": CompassShuffle,
"mapshuffle": MapShuffle,
"progressive": Progressive,
"shop_item_slots": ShopItemSlots,
"ow_palettes": OWPalette,
@ -195,6 +239,7 @@ alttp_options: typing.Dict[str, type(Option)] = {
"menuspeed": MenuSpeed,
"music": Music,
"reduceflashing": ReduceFlashing,
"triforcehud": TriforceHud
"triforcehud": TriforceHud,
"glitch_boots": DefaultOnToggle
}

View File

@ -1491,7 +1491,7 @@ def patch_rom(world, rom, player, enemized):
# block HC upstairs doors in rain state in standard mode
rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' else 0x00)
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] is True else 0x00)
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.smallkeyshuffle[player] else 0x00)
| (0x02 if world.compassshuffle[player] else 0x00)
| (0x04 if world.mapshuffle[player] else 0x00)
| (0x08 if world.bigkeyshuffle[player] else 0x00))) # free roaming item text boxes
@ -1515,7 +1515,7 @@ def patch_rom(world, rom, player, enemized):
# b - Big Key
# a - Small Key
#
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] is True else 0x00)
rom.write_byte(0x180045, ((0x01 if world.smallkeyshuffle[player] is True else 0x00)
| (0x02 if world.bigkeyshuffle[player] else 0x00)
| (0x04 if world.mapshuffle[player] else 0x00)
| (0x08 if world.compassshuffle[player] else 0x00))) # free roaming items in menu
@ -1548,7 +1548,7 @@ def patch_rom(world, rom, player, enemized):
rom.write_int16(0x18017C, get_reveal_bytes('Crystal 5') | get_reveal_bytes('Crystal 6') if world.mapshuffle[
player] else 0x0000) # Bomb Shop Reveal
rom.write_byte(0x180172, 0x01 if world.keyshuffle[player] == "universal" else 0x00) # universal keys
rom.write_byte(0x180172, 0x01 if world.smallkeyshuffle[player] == "universal" else 0x00) # universal keys
rom.write_byte(0x18637E, 0x01 if world.retro[player] else 0x00) # Skip quiver in item shops once bought
rom.write_byte(0x180175, 0x01 if world.retro[player] else 0x00) # rupee bow
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost
@ -2247,7 +2247,7 @@ def write_strings(rom, world, player):
# Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well.
items_to_hint = RelevantItems.copy()
if world.keyshuffle[player]:
if world.smallkeyshuffle[player]:
items_to_hint.extend(SmallKeys)
if world.bigkeyshuffle[player]:
items_to_hint.extend(BigKeys)

View File

@ -99,7 +99,7 @@ def set_rules(world):
add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.world.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or')
set_bunny_rules(world, player, world.mode[player] == 'inverted')
def mirrorless_path_to_castle_courtyard(world, player):
# If Agahnim is defeated then the courtyard needs to be accessible without using the mirror for the mirror offset glitch.
@ -212,7 +212,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('Sewers Door', player),
lambda state: state.has_key('Small Key (Hyrule Castle)', player) or (
world.keyshuffle[player] == "universal" and world.mode[
world.smallkeyshuffle[player] == "universal" and world.mode[
player] == 'standard')) # standard universal small keys cannot access the shop
set_rule(world.get_entrance('Sewers Back Door', player),
lambda state: state.has_key('Small Key (Hyrule Castle)', player))
@ -243,7 +243,7 @@ def global_rules(world, player):
set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state.has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and state.world.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state))
# logic patch to prevent placing a crystal in Desert that's required to reach the required keys
if not (world.keyshuffle[player] and world.bigkeyshuffle[player]):
if not (world.smallkeyshuffle[player] and world.bigkeyshuffle[player]):
add_rule(world.get_location('Desert Palace - Prize', player), lambda state: state.world.get_region('Desert Palace Main (Outer)', player).can_reach(state))
set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state.has_key('Small Key (Tower of Hera)', player) or item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player))
@ -260,7 +260,7 @@ def global_rules(world, player):
if world.accessibility[player] != 'locations':
set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player)
set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player))
if not world.keyshuffle[player] and world.logic[player] != 'nologic':
if not world.smallkeyshuffle[player] and world.logic[player] != 'nologic':
forbid_item(world.get_location('Swamp Palace - Entrance', player), 'Big Key (Swamp Palace)', player)
set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player))
@ -915,7 +915,7 @@ def set_trock_key_rules(world, player):
return 4
# If TR is only accessible from the middle, the big key must be further restricted to prevent softlock potential
if not can_reach_front and not world.keyshuffle[player]:
if not can_reach_front and not world.smallkeyshuffle[player]:
# Must not go in the Big Key Chest - only 1 other chest available and 2+ keys required for all other chests
forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Big Key (Turtle Rock)', player)
if not can_reach_big_chest:

View File

@ -271,7 +271,7 @@ def create_shops(world, player: int):
# make sure that blue potion is available in inverted, special case locked = None; lock when done.
player_shop_table["Dark Lake Hylia Shop"] = \
player_shop_table["Dark Lake Hylia Shop"]._replace(items=_inverted_hylia_shop_defaults, locked=None)
chance_100 = int(world.retro[player])*0.25+int(world.keyshuffle[player] == "universal") * 0.5
chance_100 = int(world.retro[player])*0.25+int(world.smallkeyshuffle[player] == "universal") * 0.5
for region_name, (room_id, type, shopkeeper, custom, locked, inventory, sram_offset) in player_shop_table.items():
region = world.get_region(region_name, player)
shop: Shop = shop_class_mapping[type](region, room_id, shopkeeper, custom, locked, sram_offset)
@ -371,13 +371,13 @@ def set_up_shops(world, player: int):
rss = world.get_region('Red Shield Shop', player).shop
replacement_items = [['Red Potion', 150], ['Green Potion', 75], ['Blue Potion', 200], ['Bombs (10)', 50],
['Blue Shield', 50], ['Small Heart', 10]] # Can't just replace the single arrow with 10 arrows as retro doesn't need them.
if world.keyshuffle[player] == "universal":
if world.smallkeyshuffle[player] == "universal":
replacement_items.append(['Small Key (Universal)', 100])
replacement_item = world.random.choice(replacement_items)
rss.add_inventory(2, 'Single Arrow', 80, 1, replacement_item[0], replacement_item[1])
rss.locked = True
if world.keyshuffle[player] == "universal" or world.retro[player]:
if world.smallkeyshuffle[player] == "universal" or world.retro[player]:
for shop in world.random.sample([s for s in world.shops if
s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player],
5):
@ -385,7 +385,7 @@ def set_up_shops(world, player: int):
slots = [0, 1, 2]
world.random.shuffle(slots)
slots = iter(slots)
if world.keyshuffle[player] == "universal":
if world.smallkeyshuffle[player] == "universal":
shop.add_inventory(next(slots), 'Small Key (Universal)', 100)
if world.retro[player]:
shop.push_inventory(next(slots), 'Single Arrow', 80)

View File

@ -18,6 +18,7 @@ class ALttPLocation(Location):
class ALttPItem(Item):
game: str = "A Link to the Past"
dungeon = None
def __init__(self, name, player, advancement=False, type=None, item_code=None, pedestal_hint=None, pedestal_credit=None,
sick_kid_credit=None, zora_credit=None, witch_credit=None, flute_boy_credit=None, hint_text=None):
@ -53,25 +54,9 @@ class ALttPItem(Item):
@property
def dungeon_item(self) -> Optional[str]:
if self.game == "A Link to the Past" and self.type in {"SmallKey", "BigKey", "Map", "Compass"}:
if self.type in {"SmallKey", "BigKey", "Map", "Compass"}:
return self.type
@property
def shuffled_dungeon_item(self) -> bool:
dungeon_item_type = self.dungeon_item
if dungeon_item_type:
return {"SmallKey" : self.world.keyshuffle,
"BigKey": self.world.bigkeyshuffle,
"Map": self.world.mapshuffle,
"Compass": self.world.compassshuffle}[dungeon_item_type][self.player]
return False
@property
def locked_dungeon_item(self) -> bool:
dungeon_item_type = self.dungeon_item
if dungeon_item_type:
return not {"SmallKey" : self.world.keyshuffle,
"BigKey": self.world.bigkeyshuffle,
"Map": self.world.mapshuffle,
"Compass": self.world.compassshuffle}[dungeon_item_type][self.player]
return False
def locked_dungeon_item(self):
return self.location.locked and self.dungeon_item

View File

@ -40,9 +40,16 @@ class ALTTPWorld(World):
create_items = generate_itempool
def __init__(self, *args, **kwargs):
self.dungeon_local_item_names = set()
self.dungeon_specific_item_names = set()
self.rom_name_available_event = threading.Event()
super(ALTTPWorld, self).__init__(*args, **kwargs)
def generate_early(self):
player = self.player
world = self.world
# system for sharing ER layouts
world.er_seeds[player] = str(world.random.randint(0, 2 ** 64))
@ -58,13 +65,20 @@ class ALTTPWorld(World):
world.er_seeds[player] = seed
elif world.shuffle[player] == "vanilla":
world.er_seeds[player] = "vanilla"
for dungeon_item in ["smallkeyshuffle", "bigkeyshuffle", "compassshuffle", "mapshuffle"]:
option = getattr(world, dungeon_item)[player]
if option == "own_world":
world.local_items[player] |= self.item_name_groups[option.item_name_group]
elif option == "different_world":
world.non_local_items[player] |= self.item_name_groups[option.item_name_group]
elif option.in_dungeon:
self.dungeon_local_item_names |= self.item_name_groups[option.item_name_group]
if option == "original_dungeon":
self.dungeon_specific_item_names |= self.item_name_groups[option.item_name_group]
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
def create_regions(self):
# noinspection PyAttributeOutsideInit
self.rom_name_available_event = threading.Event()
player = self.player
world = self.world
if world.open_pyramid[player] == 'goal':
@ -89,7 +103,6 @@ class ALTTPWorld(World):
create_shops(world, player)
create_dungeons(world, player)
if world.logic[player] not in ["noglitches", "minorglitches"] and world.shuffle[player] in \
{"vanilla", "dungeonssimple", "dungeonsfull", "simple", "restricted", "full"}:
world.fix_fake_world[player] = False
@ -227,7 +240,7 @@ class ALTTPWorld(World):
@classmethod
def stage_pre_fill(cls, world):
from .Dungeons import fill_dungeons_restrictive
fill_dungeons_restrictive(world)
fill_dungeons_restrictive(cls, world)
@classmethod
def stage_post_fill(cls, world):
@ -311,7 +324,8 @@ class ALTTPWorld(World):
trash_counts = {}
standard_keyshuffle_players = set()
for player in world.get_game_players("A Link to the Past"):
if world.mode[player] == 'standard' and world.keyshuffle[player] is True:
if world.mode[player] == 'standard' and world.smallkeyshuffle[player] \
and world.smallkeyshuffle[player] != "universal":
standard_keyshuffle_players.add(player)
if not world.ganonstower_vanilla[player] or \
world.logic[player] in {'owglitches', 'hybridglitches', "nologic"}: