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:
parent
a124a7a82a
commit
5daadcb2d5
|
@ -95,10 +95,6 @@ class MultiWorld():
|
||||||
set_player_attr('can_access_trock_big_chest', None)
|
set_player_attr('can_access_trock_big_chest', None)
|
||||||
set_player_attr('can_access_trock_middle', None)
|
set_player_attr('can_access_trock_middle', None)
|
||||||
set_player_attr('fix_fake_world', True)
|
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('difficulty_requirements', None)
|
||||||
set_player_attr('boss_shuffle', 'none')
|
set_player_attr('boss_shuffle', 'none')
|
||||||
set_player_attr('enemy_shuffle', False)
|
set_player_attr('enemy_shuffle', False)
|
||||||
|
@ -118,7 +114,6 @@ class MultiWorld():
|
||||||
set_player_attr('blue_clock_time', 2)
|
set_player_attr('blue_clock_time', 2)
|
||||||
set_player_attr('green_clock_time', 4)
|
set_player_attr('green_clock_time', 4)
|
||||||
set_player_attr('can_take_damage', True)
|
set_player_attr('can_take_damage', True)
|
||||||
set_player_attr('glitch_boots', True)
|
|
||||||
set_player_attr('progression_balancing', True)
|
set_player_attr('progression_balancing', True)
|
||||||
set_player_attr('local_items', set())
|
set_player_attr('local_items', set())
|
||||||
set_player_attr('non_local_items', set())
|
set_player_attr('non_local_items', set())
|
||||||
|
@ -158,6 +153,10 @@ class MultiWorld():
|
||||||
def get_game_players(self, game_name: str):
|
def get_game_players(self, game_name: str):
|
||||||
return tuple(player for player in self.player_ids if self.game[player] == game_name)
|
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:
|
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)})'
|
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}
|
locations = {location for location in locations if location.event}
|
||||||
while new_locations:
|
while new_locations:
|
||||||
reachable_events = {location for location in locations if
|
reachable_events = {location for location in locations if
|
||||||
(not key_only or
|
(not key_only or getattr(location.item, "locked_dungeon_item", False))
|
||||||
(not self.world.keyshuffle[location.item.player] and location.item.smallkey)
|
|
||||||
or (not self.world.bigkeyshuffle[location.item.player] and location.item.bigkey))
|
|
||||||
and location.can_reach(self)}
|
and location.can_reach(self)}
|
||||||
new_locations = reachable_events - self.events
|
new_locations = reachable_events - self.events
|
||||||
for event in new_locations:
|
for event in new_locations:
|
||||||
|
@ -572,7 +569,7 @@ class CollectionState(object):
|
||||||
def has_key(self, item, player, count: int = 1):
|
def has_key(self, item, player, count: int = 1):
|
||||||
if self.world.logic[player] == 'nologic':
|
if self.world.logic[player] == 'nologic':
|
||||||
return True
|
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.can_buy_unlimited('Small Key (Universal)', player)
|
||||||
return self.prog_items[item, player] >= count
|
return self.prog_items[item, player] >= count
|
||||||
|
|
||||||
|
@ -807,12 +804,6 @@ class Region(object):
|
||||||
return True
|
return True
|
||||||
return False
|
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):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
@ -854,8 +845,8 @@ class Entrance(object):
|
||||||
world = self.parent_region.world if self.parent_region else None
|
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})'
|
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):
|
def __init__(self, name: str, regions, big_key, small_keys, dungeon_items, player: int):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.regions = regions
|
self.regions = regions
|
||||||
|
@ -930,7 +921,7 @@ class Location():
|
||||||
self.item: Optional[Item] = None
|
self.item: Optional[Item] = None
|
||||||
|
|
||||||
def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool:
|
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:
|
def can_reach(self, state: CollectionState) -> bool:
|
||||||
# self.access_rule computes faster on average, so placing it first for faster abort
|
# 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,
|
'open_pyramid': self.world.open_pyramid,
|
||||||
'accessibility': self.world.accessibility,
|
'accessibility': self.world.accessibility,
|
||||||
'hints': self.world.hints,
|
'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,
|
'boss_shuffle': self.world.boss_shuffle,
|
||||||
'enemy_shuffle': self.world.enemy_shuffle,
|
'enemy_shuffle': self.world.enemy_shuffle,
|
||||||
'enemy_health': self.world.enemy_health,
|
'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('Entrance Shuffle Seed %s\n' % self.metadata['er_seeds'][player])
|
||||||
outfile.write('Pyramid hole pre-opened: %s\n' % (
|
outfile.write('Pyramid hole pre-opened: %s\n' % (
|
||||||
'Yes' if self.metadata['open_pyramid'][player] else 'No'))
|
'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' %
|
outfile.write('Shop inventory shuffle: %s\n' %
|
||||||
bool_to_text("i" in self.metadata["shop_shuffle"][player]))
|
bool_to_text("i" in self.metadata["shop_shuffle"][player]))
|
||||||
outfile.write('Shop price shuffle: %s\n' %
|
outfile.write('Shop price shuffle: %s\n' %
|
||||||
|
|
23
Generate.py
23
Generate.py
|
@ -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)
|
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')
|
entrance_shuffle = get_choice_legacy('entrance_shuffle', weights, 'vanilla')
|
||||||
if entrance_shuffle.startswith('none-'):
|
if entrance_shuffle.startswith('none-'):
|
||||||
ret.shuffle = 'vanilla'
|
ret.shuffle = 'vanilla'
|
||||||
|
@ -661,13 +645,6 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
||||||
if not ret.required_medallions[index]:
|
if not ret.required_medallions[index]:
|
||||||
raise Exception(f"unknown Medallion {medallion} for {'misery mire' if index == 0 else 'turtle rock'}")
|
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 = []
|
ret.plando_items = []
|
||||||
if "items" in plando_options:
|
if "items" in plando_options:
|
||||||
|
|
||||||
|
|
21
Main.py
21
Main.py
|
@ -66,11 +66,6 @@ def main(args, seed=None):
|
||||||
world.retro = args.retro.copy()
|
world.retro = args.retro.copy()
|
||||||
|
|
||||||
world.hints = args.hints.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.open_pyramid = args.open_pyramid.copy()
|
||||||
world.boss_shuffle = args.shufflebosses.copy()
|
world.boss_shuffle = args.shufflebosses.copy()
|
||||||
world.enemy_shuffle = args.enemy_shuffle.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.green_clock_time = args.green_clock_time.copy()
|
||||||
world.shufflepots = args.shufflepots.copy()
|
world.shufflepots = args.shufflepots.copy()
|
||||||
world.dungeon_counters = args.dungeon_counters.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_available = args.triforce_pieces_available.copy()
|
||||||
world.triforce_pieces_required = args.triforce_pieces_required.copy()
|
world.triforce_pieces_required = args.triforce_pieces_required.copy()
|
||||||
world.shop_shuffle = args.shop_shuffle.copy()
|
world.shop_shuffle = args.shop_shuffle.copy()
|
||||||
|
@ -140,21 +134,6 @@ def main(args, seed=None):
|
||||||
if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
|
if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
|
||||||
world.local_items[player].add('Triforce Piece')
|
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.
|
# 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['Pendants']
|
||||||
world.non_local_items[player] -= item_name_groups['Crystals']
|
world.non_local_items[player] -= item_name_groups['Crystals']
|
||||||
|
|
12
Options.py
12
Options.py
|
@ -9,7 +9,7 @@ class AssembleOptions(type):
|
||||||
name_lookup = attrs["name_lookup"] = {}
|
name_lookup = attrs["name_lookup"] = {}
|
||||||
# merge parent class options
|
# merge parent class options
|
||||||
for base in bases:
|
for base in bases:
|
||||||
if hasattr(base, "options"):
|
if getattr(base, "options", None):
|
||||||
options.update(base.options)
|
options.update(base.options)
|
||||||
name_lookup.update(base.name_lookup)
|
name_lookup.update(base.name_lookup)
|
||||||
new_options = {name[7:].lower(): option_id for name, option_id in attrs.items() if
|
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(data)
|
||||||
return cls.from_text(str(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):
|
class Range(Option, int):
|
||||||
range_start = 0
|
range_start = 0
|
||||||
|
|
|
@ -93,10 +93,10 @@ def launch_generator(pool: multiprocessing.pool.Pool, generation: Generation):
|
||||||
"sid": generation.id,
|
"sid": generation.id,
|
||||||
"owner": generation.owner},
|
"owner": generation.owner},
|
||||||
handle_generation_success, handle_generation_failure)
|
handle_generation_success, handle_generation_failure)
|
||||||
except:
|
except Exception as e:
|
||||||
generation.state = STATE_ERROR
|
generation.state = STATE_ERROR
|
||||||
commit()
|
commit()
|
||||||
raise
|
logging.exception(e)
|
||||||
else:
|
else:
|
||||||
generation.state = STATE_STARTED
|
generation.state = STATE_STARTED
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,8 @@ progression_balancing:
|
||||||
# exclude_locations: # Force certain locations to never contain progression items, and always be filled with junk.
|
# exclude_locations: # Force certain locations to never contain progression items, and always be filled with junk.
|
||||||
# - "Master Sword Pedestal"
|
# - "Master Sword Pedestal"
|
||||||
{{ game }}:
|
{{ game }}:
|
||||||
{%- for option_name, option in options.items() %}
|
{%- for option_key, option in options.items() %}
|
||||||
{{ option_name }}:{% if option.__doc__ %} # {{ option.__doc__ | replace('\n', '\n#') | indent(4, first=False) }}{% endif %}
|
{{ option_key }}:{% if option.__doc__ %} # {{ option.__doc__ | replace('\n', '\n#') | indent(4, first=False) }}{% endif %}
|
||||||
{%- if option.range_start is defined %}
|
{%- if option.range_start is defined %}
|
||||||
# you can add additional values between minimum and maximum
|
# you can add additional values between minimum and maximum
|
||||||
{{ option.range_start }}: 0 # minimum value
|
{{ option.range_start }}: 0 # minimum value
|
||||||
|
@ -62,7 +62,7 @@ progression_balancing:
|
||||||
random-low: 0
|
random-low: 0
|
||||||
random-high: 0
|
random-high: 0
|
||||||
{%- elif option.options -%}
|
{%- 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 %}
|
{{ sub_option_name }}: {% if suboption_option_id == option.default %}50{% else %}0{% endif %}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{%- else %}
|
{%- else %}
|
||||||
|
|
|
@ -7,8 +7,11 @@ from worlds.alttp.Regions import lookup_boss_drops
|
||||||
|
|
||||||
def create_dungeons(world, player):
|
def create_dungeons(world, player):
|
||||||
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
|
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)
|
dungeon_items, player)
|
||||||
|
for item in dungeon.all_items:
|
||||||
|
item.dungeon = dungeon
|
||||||
dungeon.boss = BossFactory(default_boss, player) if default_boss else None
|
dungeon.boss = BossFactory(default_boss, player) if default_boss else None
|
||||||
for region in dungeon.regions:
|
for region in dungeon.regions:
|
||||||
world.get_region(region, player).dungeon = dungeon
|
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'],
|
EP = make_dungeon('Eastern Palace', 'Armos Knights', ['Eastern Palace'],
|
||||||
ItemFactory('Big Key (Eastern Palace)', player), [],
|
ItemFactory('Big Key (Eastern Palace)', player), [],
|
||||||
ItemFactory(['Map (Eastern Palace)', 'Compass (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))
|
DP = make_dungeon('Desert Palace', 'Lanmolas',
|
||||||
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))
|
['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)',
|
||||||
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))
|
'Desert Palace East'], ItemFactory('Big Key (Desert Palace)', 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))
|
[ItemFactory('Small Key (Desert Palace)', 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))
|
ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], 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))
|
ToH = make_dungeon('Tower of Hera', 'Moldorm',
|
||||||
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))
|
['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'],
|
||||||
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))
|
ItemFactory('Big Key (Tower of Hera)', 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))
|
[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':
|
if world.mode[player] != 'inverted':
|
||||||
AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
|
AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None,
|
||||||
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))
|
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:
|
else:
|
||||||
AT = make_dungeon('Inverted Agahnims Tower', 'Agahnim', ['Inverted Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2, player), [])
|
AT = make_dungeon('Inverted Agahnims Tower', 'Agahnim', ['Inverted Agahnims Tower', 'Agahnim 1'], None,
|
||||||
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))
|
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['bottom'] = BossFactory('Armos Knights', player)
|
||||||
GT.bosses['middle'] = BossFactory('Lanmolas', player)
|
GT.bosses['middle'] = BossFactory('Lanmolas', player)
|
||||||
|
@ -53,25 +114,49 @@ def get_dungeon_item_pool(world):
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def fill_dungeons_restrictive(world):
|
def get_dungeon_item_pool_player(world, player):
|
||||||
"""Places dungeon-native items into their dungeons, places nothing if everything is shuffled outside."""
|
items = [item for dungeon in world.dungeons.values() if dungeon.player == player for item in dungeon.all_items]
|
||||||
dungeon_items = [item for item in get_dungeon_item_pool(world) if
|
for item in items:
|
||||||
(((item.smallkey and not world.keyshuffle[item.player])
|
item.world = world
|
||||||
or (item.bigkey and not world.bigkeyshuffle[item.player])
|
return items
|
||||||
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
|
|
||||||
|
|
||||||
world.random.shuffle(locations)
|
|
||||||
all_state_base = world.get_all_state()
|
def fill_dungeons_restrictive(autoworld, world):
|
||||||
# sort in the order Big Key, Small Key, Other before placing dungeon items
|
"""Places dungeon-native items into their dungeons, places nothing if everything is shuffled outside."""
|
||||||
sort_order = {"BigKey": 3, "SmallKey": 2}
|
localized: set = set()
|
||||||
dungeon_items.sort(key=lambda item: sort_order.get(item.type, 1))
|
dungeon_specific: set = set()
|
||||||
fill_restrictive(world, all_state_base, locations, dungeon_items, True, True)
|
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],
|
dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
|
||||||
|
@ -80,7 +165,8 @@ dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A],
|
||||||
'Palace of Darkness - Prize': [0x155B8],
|
'Palace of Darkness - Prize': [0x155B8],
|
||||||
'Swamp Palace - Prize': [0x155B7],
|
'Swamp Palace - Prize': [0x155B7],
|
||||||
'Thieves\' Town - Prize': [0x155C6],
|
'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],
|
'Ice Palace - Prize': [0x155BF],
|
||||||
'Misery Mire - Prize': [0x155B9],
|
'Misery Mire - Prize': [0x155B9],
|
||||||
'Turtle Rock - Prize': [0x155C7, 0x155A7, 0x155AA, 0x155AB]}
|
'Turtle Rock - Prize': [0x155C7, 0x155A7, 0x155AA, 0x155AB]}
|
||||||
|
|
|
@ -206,26 +206,10 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
time).
|
time).
|
||||||
''', type=int)
|
''', 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='''\
|
parser.add_argument('--retro', default=defval(False), help='''\
|
||||||
Keys are universal, shooting arrows costs rupees,
|
Keys are universal, shooting arrows costs rupees,
|
||||||
and a few other little things make this more like Zelda-1.
|
and a few other little things make this more like Zelda-1.
|
||||||
''', action='store_true')
|
''', 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(''),
|
parser.add_argument('--local_items', default=defval(''),
|
||||||
help='Specifies a list of items that will not spread across the multiworld (separated by commas)')
|
help='Specifies a list of items that will not spread across the multiworld (separated by commas)')
|
||||||
parser.add_argument('--non_local_items', default=defval(''),
|
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('--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('--multi', default=defval(1), type=lambda value: min(max(int(value), 1), 255))
|
||||||
parser.add_argument('--names', default=defval(''))
|
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('--outputpath')
|
||||||
parser.add_argument('--game', default="A Link to the Past")
|
parser.add_argument('--game', default="A Link to the Past")
|
||||||
parser.add_argument('--race', default=defval(False), action='store_true')
|
parser.add_argument('--race', default=defval(False), action='store_true')
|
||||||
parser.add_argument('--outputname')
|
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')
|
parser.add_argument('--start_hints')
|
||||||
if multiargs.multi:
|
if multiargs.multi:
|
||||||
for player in range(1, multiargs.multi + 1):
|
for player in range(1, multiargs.multi + 1):
|
||||||
|
@ -314,7 +295,6 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
ret.plando_connections = []
|
ret.plando_connections = []
|
||||||
ret.er_seeds = {}
|
ret.er_seeds = {}
|
||||||
|
|
||||||
ret.glitch_boots = not ret.disable_glitch_boots
|
|
||||||
if ret.timer == "none":
|
if ret.timer == "none":
|
||||||
ret.timer = False
|
ret.timer = False
|
||||||
if ret.dungeon_counters == 'on':
|
if ret.dungeon_counters == 'on':
|
||||||
|
@ -322,12 +302,6 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
elif ret.dungeon_counters == 'off':
|
elif ret.dungeon_counters == 'off':
|
||||||
ret.dungeon_counters = False
|
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:
|
if multiargs.multi:
|
||||||
defaults = copy.deepcopy(ret)
|
defaults = copy.deepcopy(ret)
|
||||||
for player in range(1, multiargs.multi + 1):
|
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',
|
for name in ['logic', 'mode', 'swordless', 'goal', 'difficulty', 'item_functionality',
|
||||||
'shuffle', 'open_pyramid', 'timer',
|
'shuffle', 'open_pyramid', 'timer',
|
||||||
'countdown_start_time', 'red_clock_time', 'blue_clock_time', 'green_clock_time',
|
'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',
|
'local_items', 'non_local_items', 'retro', 'accessibility', 'hints', 'beemizer',
|
||||||
'shufflebosses', 'enemy_shuffle', 'enemy_health', 'enemy_damage', 'shufflepots',
|
'shufflebosses', 'enemy_shuffle', 'enemy_health', 'enemy_damage', 'shufflepots',
|
||||||
'sprite',
|
'sprite',
|
||||||
|
@ -344,7 +317,7 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
"triforce_pieces_required", "shop_shuffle",
|
"triforce_pieces_required", "shop_shuffle",
|
||||||
"required_medallions", "start_hints",
|
"required_medallions", "start_hints",
|
||||||
"plando_items", "plando_texts", "plando_connections", "er_seeds",
|
"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',
|
'tile_shuffle', 'bush_shuffle', 'shuffle_prizes', 'sprite_pool', 'dark_room_logic',
|
||||||
'restrict_dungeon_item_on_boss', 'game']:
|
'restrict_dungeon_item_on_boss', 'game']:
|
||||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||||
|
|
|
@ -5,9 +5,9 @@ from BaseClasses import Region, RegionType
|
||||||
from worlds.alttp.SubClasses import ALttPLocation
|
from worlds.alttp.SubClasses import ALttPLocation
|
||||||
from worlds.alttp.Shops import TakeAny, total_shop_slots, set_up_shops, shuffle_shops
|
from worlds.alttp.Shops import TakeAny, total_shop_slots, set_up_shops, shuffle_shops
|
||||||
from worlds.alttp.Bosses import place_bosses
|
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 worlds.alttp.EntranceShuffle import connect_entrance
|
||||||
from Fill import FillError, fill_restrictive
|
from Fill import FillError
|
||||||
from worlds.alttp.Items import ItemFactory, GetBeemizerItem
|
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.
|
# 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(['Rupees (300)'] * 34)
|
||||||
itempool.extend(['Bombs (10)'] * 5)
|
itempool.extend(['Bombs (10)'] * 5)
|
||||||
itempool.extend(['Arrows (10)'] * 7)
|
itempool.extend(['Arrows (10)'] * 7)
|
||||||
if world.keyshuffle[player] == 'universal':
|
if world.smallkeyshuffle[player] == 'universal':
|
||||||
itempool.extend(itemdiff.universal_keys)
|
itempool.extend(itemdiff.universal_keys)
|
||||||
itempool.append('Small Key (Universal)')
|
itempool.append('Small Key (Universal)')
|
||||||
|
|
||||||
|
@ -362,12 +362,8 @@ def generate_itempool(world):
|
||||||
if treasure_hunt_icon is not None:
|
if treasure_hunt_icon is not None:
|
||||||
world.treasure_hunt_icon[player] = treasure_hunt_icon
|
world.treasure_hunt_icon[player] = treasure_hunt_icon
|
||||||
|
|
||||||
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.player == player
|
dungeon_items = [item for item in get_dungeon_item_pool_player(world, player)
|
||||||
and ((item.smallkey and world.keyshuffle[player])
|
if item.name not in world.worlds[player].dungeon_local_item_names]
|
||||||
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')]
|
|
||||||
|
|
||||||
if world.goal[player] == 'icerodhunt':
|
if world.goal[player] == 'icerodhunt':
|
||||||
for item in dungeon_items:
|
for item in dungeon_items:
|
||||||
|
@ -637,7 +633,7 @@ def get_pool_core(world, player: int):
|
||||||
if retro:
|
if retro:
|
||||||
replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)'}
|
replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)'}
|
||||||
pool = ['Rupees (5)' if item in replace else item for item in pool]
|
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)
|
pool.extend(diff.universal_keys)
|
||||||
item_to_place = 'Small Key (Universal)' if goal != 'icerodhunt' else 'Nothing'
|
item_to_place = 'Small Key (Universal)' if goal != 'icerodhunt' else 'Nothing'
|
||||||
if mode == 'standard':
|
if mode == 'standard':
|
||||||
|
@ -774,7 +770,7 @@ def make_custom_item_pool(world, player):
|
||||||
itemtotal = itemtotal + 1
|
itemtotal = itemtotal + 1
|
||||||
|
|
||||||
if mode == 'standard':
|
if mode == 'standard':
|
||||||
if world.keyshuffle[player] == "universal":
|
if world.smallkeyshuffle[player] == "universal":
|
||||||
key_location = world.random.choice(
|
key_location = world.random.choice(
|
||||||
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
||||||
'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
|
'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(['Magic Mirror'] * customitemarray[22])
|
||||||
pool.extend(['Moon Pearl'] * customitemarray[28])
|
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
|
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode
|
||||||
if itemtotal < total_items_to_place:
|
if itemtotal < total_items_to_place:
|
||||||
pool.extend(['Nothing'] * (total_items_to_place - itemtotal))
|
pool.extend(['Nothing'] * (total_items_to_place - itemtotal))
|
||||||
|
|
|
@ -28,6 +28,46 @@ class Goal(Choice):
|
||||||
option_hand_in = 2
|
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):
|
class Crystals(Range):
|
||||||
range_start = 0
|
range_start = 0
|
||||||
range_end = 7
|
range_end = 7
|
||||||
|
@ -181,6 +221,10 @@ class TriforceHud(Choice):
|
||||||
alttp_options: typing.Dict[str, type(Option)] = {
|
alttp_options: typing.Dict[str, type(Option)] = {
|
||||||
"crystals_needed_for_gt": CrystalsTower,
|
"crystals_needed_for_gt": CrystalsTower,
|
||||||
"crystals_needed_for_ganon": CrystalsGanon,
|
"crystals_needed_for_ganon": CrystalsGanon,
|
||||||
|
"bigkeyshuffle": BigKeyShuffle,
|
||||||
|
"smallkeyshuffle": SmallKeyShuffle,
|
||||||
|
"compassshuffle": CompassShuffle,
|
||||||
|
"mapshuffle": MapShuffle,
|
||||||
"progressive": Progressive,
|
"progressive": Progressive,
|
||||||
"shop_item_slots": ShopItemSlots,
|
"shop_item_slots": ShopItemSlots,
|
||||||
"ow_palettes": OWPalette,
|
"ow_palettes": OWPalette,
|
||||||
|
@ -195,6 +239,7 @@ alttp_options: typing.Dict[str, type(Option)] = {
|
||||||
"menuspeed": MenuSpeed,
|
"menuspeed": MenuSpeed,
|
||||||
"music": Music,
|
"music": Music,
|
||||||
"reduceflashing": ReduceFlashing,
|
"reduceflashing": ReduceFlashing,
|
||||||
"triforcehud": TriforceHud
|
"triforcehud": TriforceHud,
|
||||||
|
"glitch_boots": DefaultOnToggle
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1491,7 +1491,7 @@ def patch_rom(world, rom, player, enemized):
|
||||||
# block HC upstairs doors in rain state in standard mode
|
# 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(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)
|
| (0x02 if world.compassshuffle[player] else 0x00)
|
||||||
| (0x04 if world.mapshuffle[player] else 0x00)
|
| (0x04 if world.mapshuffle[player] else 0x00)
|
||||||
| (0x08 if world.bigkeyshuffle[player] else 0x00))) # free roaming item text boxes
|
| (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
|
# b - Big Key
|
||||||
# a - Small 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)
|
| (0x02 if world.bigkeyshuffle[player] else 0x00)
|
||||||
| (0x04 if world.mapshuffle[player] else 0x00)
|
| (0x04 if world.mapshuffle[player] else 0x00)
|
||||||
| (0x08 if world.compassshuffle[player] else 0x00))) # free roaming items in menu
|
| (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[
|
rom.write_int16(0x18017C, get_reveal_bytes('Crystal 5') | get_reveal_bytes('Crystal 6') if world.mapshuffle[
|
||||||
player] else 0x0000) # Bomb Shop Reveal
|
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(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(0x180175, 0x01 if world.retro[player] else 0x00) # rupee bow
|
||||||
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost
|
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.
|
# 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()
|
items_to_hint = RelevantItems.copy()
|
||||||
if world.keyshuffle[player]:
|
if world.smallkeyshuffle[player]:
|
||||||
items_to_hint.extend(SmallKeys)
|
items_to_hint.extend(SmallKeys)
|
||||||
if world.bigkeyshuffle[player]:
|
if world.bigkeyshuffle[player]:
|
||||||
items_to_hint.extend(BigKeys)
|
items_to_hint.extend(BigKeys)
|
||||||
|
|
|
@ -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')
|
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')
|
set_bunny_rules(world, player, world.mode[player] == 'inverted')
|
||||||
|
|
||||||
|
|
||||||
def mirrorless_path_to_castle_courtyard(world, player):
|
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.
|
# 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),
|
set_rule(world.get_entrance('Sewers Door', player),
|
||||||
lambda state: state.has_key('Small Key (Hyrule Castle)', player) or (
|
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
|
player] == 'standard')) # standard universal small keys cannot access the shop
|
||||||
set_rule(world.get_entrance('Sewers Back Door', player),
|
set_rule(world.get_entrance('Sewers Back Door', player),
|
||||||
lambda state: state.has_key('Small Key (Hyrule Castle)', 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))
|
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
|
# 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))
|
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))
|
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':
|
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_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))
|
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)
|
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))
|
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
|
return 4
|
||||||
|
|
||||||
# If TR is only accessible from the middle, the big key must be further restricted to prevent softlock potential
|
# 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
|
# 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)
|
forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Big Key (Turtle Rock)', player)
|
||||||
if not can_reach_big_chest:
|
if not can_reach_big_chest:
|
||||||
|
|
|
@ -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.
|
# 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"] = \
|
||||||
player_shop_table["Dark Lake Hylia Shop"]._replace(items=_inverted_hylia_shop_defaults, locked=None)
|
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():
|
for region_name, (room_id, type, shopkeeper, custom, locked, inventory, sram_offset) in player_shop_table.items():
|
||||||
region = world.get_region(region_name, player)
|
region = world.get_region(region_name, player)
|
||||||
shop: Shop = shop_class_mapping[type](region, room_id, shopkeeper, custom, locked, sram_offset)
|
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
|
rss = world.get_region('Red Shield Shop', player).shop
|
||||||
replacement_items = [['Red Potion', 150], ['Green Potion', 75], ['Blue Potion', 200], ['Bombs (10)', 50],
|
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.
|
['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_items.append(['Small Key (Universal)', 100])
|
||||||
replacement_item = world.random.choice(replacement_items)
|
replacement_item = world.random.choice(replacement_items)
|
||||||
rss.add_inventory(2, 'Single Arrow', 80, 1, replacement_item[0], replacement_item[1])
|
rss.add_inventory(2, 'Single Arrow', 80, 1, replacement_item[0], replacement_item[1])
|
||||||
rss.locked = True
|
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
|
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],
|
s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player],
|
||||||
5):
|
5):
|
||||||
|
@ -385,7 +385,7 @@ def set_up_shops(world, player: int):
|
||||||
slots = [0, 1, 2]
|
slots = [0, 1, 2]
|
||||||
world.random.shuffle(slots)
|
world.random.shuffle(slots)
|
||||||
slots = iter(slots)
|
slots = iter(slots)
|
||||||
if world.keyshuffle[player] == "universal":
|
if world.smallkeyshuffle[player] == "universal":
|
||||||
shop.add_inventory(next(slots), 'Small Key (Universal)', 100)
|
shop.add_inventory(next(slots), 'Small Key (Universal)', 100)
|
||||||
if world.retro[player]:
|
if world.retro[player]:
|
||||||
shop.push_inventory(next(slots), 'Single Arrow', 80)
|
shop.push_inventory(next(slots), 'Single Arrow', 80)
|
||||||
|
|
|
@ -18,6 +18,7 @@ class ALttPLocation(Location):
|
||||||
|
|
||||||
class ALttPItem(Item):
|
class ALttPItem(Item):
|
||||||
game: str = "A Link to the Past"
|
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,
|
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):
|
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
|
@property
|
||||||
def dungeon_item(self) -> Optional[str]:
|
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
|
return self.type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shuffled_dungeon_item(self) -> bool:
|
def locked_dungeon_item(self):
|
||||||
dungeon_item_type = self.dungeon_item
|
return self.location.locked and 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
|
|
|
@ -40,9 +40,16 @@ class ALTTPWorld(World):
|
||||||
|
|
||||||
create_items = generate_itempool
|
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):
|
def generate_early(self):
|
||||||
player = self.player
|
player = self.player
|
||||||
world = self.world
|
world = self.world
|
||||||
|
|
||||||
# system for sharing ER layouts
|
# system for sharing ER layouts
|
||||||
world.er_seeds[player] = str(world.random.randint(0, 2 ** 64))
|
world.er_seeds[player] = str(world.random.randint(0, 2 ** 64))
|
||||||
|
|
||||||
|
@ -58,13 +65,20 @@ class ALTTPWorld(World):
|
||||||
world.er_seeds[player] = seed
|
world.er_seeds[player] = seed
|
||||||
elif world.shuffle[player] == "vanilla":
|
elif world.shuffle[player] == "vanilla":
|
||||||
world.er_seeds[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]]
|
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
# noinspection PyAttributeOutsideInit
|
|
||||||
self.rom_name_available_event = threading.Event()
|
|
||||||
|
|
||||||
player = self.player
|
player = self.player
|
||||||
world = self.world
|
world = self.world
|
||||||
if world.open_pyramid[player] == 'goal':
|
if world.open_pyramid[player] == 'goal':
|
||||||
|
@ -89,7 +103,6 @@ class ALTTPWorld(World):
|
||||||
create_shops(world, player)
|
create_shops(world, player)
|
||||||
create_dungeons(world, player)
|
create_dungeons(world, player)
|
||||||
|
|
||||||
|
|
||||||
if world.logic[player] not in ["noglitches", "minorglitches"] and world.shuffle[player] in \
|
if world.logic[player] not in ["noglitches", "minorglitches"] and world.shuffle[player] in \
|
||||||
{"vanilla", "dungeonssimple", "dungeonsfull", "simple", "restricted", "full"}:
|
{"vanilla", "dungeonssimple", "dungeonsfull", "simple", "restricted", "full"}:
|
||||||
world.fix_fake_world[player] = False
|
world.fix_fake_world[player] = False
|
||||||
|
@ -227,7 +240,7 @@ class ALTTPWorld(World):
|
||||||
@classmethod
|
@classmethod
|
||||||
def stage_pre_fill(cls, world):
|
def stage_pre_fill(cls, world):
|
||||||
from .Dungeons import fill_dungeons_restrictive
|
from .Dungeons import fill_dungeons_restrictive
|
||||||
fill_dungeons_restrictive(world)
|
fill_dungeons_restrictive(cls, world)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def stage_post_fill(cls, world):
|
def stage_post_fill(cls, world):
|
||||||
|
@ -311,7 +324,8 @@ class ALTTPWorld(World):
|
||||||
trash_counts = {}
|
trash_counts = {}
|
||||||
standard_keyshuffle_players = set()
|
standard_keyshuffle_players = set()
|
||||||
for player in world.get_game_players("A Link to the Past"):
|
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)
|
standard_keyshuffle_players.add(player)
|
||||||
if not world.ganonstower_vanilla[player] or \
|
if not world.ganonstower_vanilla[player] or \
|
||||||
world.logic[player] in {'owglitches', 'hybridglitches', "nologic"}:
|
world.logic[player] in {'owglitches', 'hybridglitches', "nologic"}:
|
||||||
|
|
Loading…
Reference in New Issue