Individual settings: mode

This commit is contained in:
Bonta-kun 2019-12-16 16:54:46 +01:00
parent 79786c7c9e
commit ab28858a8f
13 changed files with 101 additions and 106 deletions

View File

@ -12,7 +12,7 @@ class World(object):
self.players = players
self.shuffle = shuffle
self.logic = logic.copy()
self.mode = mode
self.mode = mode.copy()
self.swords = swords
self.difficulty = difficulty
self.difficulty_adjustments = difficulty_adjustments
@ -39,7 +39,7 @@ class World(object):
self.powder_patch_required = {player: False for player in range(1, players + 1)}
self.ganon_at_pyramid = {player: True for player in range(1, players + 1)}
self.ganonstower_vanilla = {player: True for player in range(1, players + 1)}
self.sewer_light_cone = mode == 'standard'
self.sewer_light_cone = {player: mode[player] == 'standard' for player in range(1, players + 1)}
self.light_world_light_cone = False
self.dark_world_light_cone = False
self.treasure_hunt_count = 0
@ -48,7 +48,7 @@ class World(object):
self.rupoor_cost = 10
self.aga_randomness = True
self.lock_aga_door_in_escape = False
self.fix_trock_doors = self.shuffle != 'vanilla' or self.mode == 'inverted'
self.fix_trock_doors = {player: self.shuffle != 'vanilla' or self.mode[player] == 'inverted' for player in range(1, players + 1)}
self.save_and_quit_from_boss = True
self.accessibility = accessibility
self.fix_skullwoods_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']
@ -74,7 +74,7 @@ class World(object):
self.difficulty_requirements = None
self.fix_fake_world = True
self.boss_shuffle = boss_shuffle
self.escape_assist = []
self.escape_assist = {player: [] for player in range(1, players + 1)}
self.hints = hints
self.crystals_needed_for_ganon = 7
self.crystals_needed_for_gt = 7
@ -499,7 +499,7 @@ class CollectionState(object):
if self.has_Pearl(player):
return True
return region.is_light_world if self.world.mode != 'inverted' else region.is_dark_world
return region.is_light_world if self.world.mode[player] != 'inverted' else region.is_dark_world
def can_reach_light_world(self, player):
if True in [i.is_light_world for i in self.reachable_regions[player]]:
@ -689,7 +689,7 @@ class Region(object):
or (item.bigkey and not self.world.bigkeyshuffle)
or (item.map and not self.world.mapshuffle)
or (item.compass and not self.world.compassshuffle))
sewer_hack = self.world.mode == 'standard' and item.name == 'Small Key (Escape)'
sewer_hack = self.world.mode[item.player] == 'standard' and item.name == 'Small Key (Escape)'
if sewer_hack or inside_dungeon_item:
return self.dungeon and self.dungeon.is_dungeon_item(item) and item.player == self.player
@ -1025,7 +1025,7 @@ class Spoiler(object):
self.bosses[str(player)]["Ice Palace"] = self.world.get_dungeon("Ice Palace", player).boss.name
self.bosses[str(player)]["Misery Mire"] = self.world.get_dungeon("Misery Mire", player).boss.name
self.bosses[str(player)]["Turtle Rock"] = self.world.get_dungeon("Turtle Rock", player).boss.name
if self.world.mode != 'inverted':
if self.world.mode[player] != 'inverted':
self.bosses[str(player)]["Ganons Tower Basement"] = self.world.get_dungeon('Ganons Tower', player).bosses['bottom'].name
self.bosses[str(player)]["Ganons Tower Middle"] = self.world.get_dungeon('Ganons Tower', player).bosses['middle'].name
self.bosses[str(player)]["Ganons Tower Top"] = self.world.get_dungeon('Ganons Tower', player).bosses['top'].name

View File

@ -141,7 +141,7 @@ def place_bosses(world, player):
if world.boss_shuffle == 'none':
return
# Most to least restrictive order
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
boss_locations = [
['Ganons Tower', 'top'],
['Tower of Hera', None],

View File

@ -27,7 +27,7 @@ def create_dungeons(world, 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 != 'inverted':
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))
else:

View File

@ -286,6 +286,7 @@ def parse_arguments(argv, no_defaults=False):
getattr(ret, name)[player] = value
set_player_arg("logic")
set_player_arg("mode")
return ret

View File

@ -39,7 +39,7 @@ def link_entrances(world, player):
lw_entrances = list(LW_Dungeon_Entrances)
dw_entrances = list(DW_Dungeon_Entrances)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
@ -52,7 +52,7 @@ def link_entrances(world, player):
dw_entrances.append('Ganons Tower')
dungeon_exits.append('Ganons Tower Exit')
if world.mode == 'standard':
if world.mode[player] == 'standard':
# rest of hyrule castle must be in light world, so it has to be the one connected to east exit of desert
connect_mandatory_exits(world, lw_entrances, [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], list(LW_Dungeon_Entrances_Must_Exit), player)
else:
@ -273,7 +273,7 @@ def link_entrances(world, player):
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
@ -309,7 +309,7 @@ def link_entrances(world, player):
pass
else: #if the cave wasn't placed we get here
connect_caves(world, lw_entrances, [], old_man_house, player)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# rest of hyrule castle must be in light world
connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player)
@ -376,7 +376,7 @@ def link_entrances(world, player):
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
@ -392,7 +392,7 @@ def link_entrances(world, player):
#place must-exit caves
connect_mandatory_exits(world, entrances, caves, must_exits, player)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# rest of hyrule castle must be dealt with
connect_caves(world, entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player)
@ -451,7 +451,7 @@ def link_entrances(world, player):
blacksmith_doors = list(Blacksmith_Single_Cave_Doors)
door_targets = list(Single_Cave_Targets)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
@ -471,7 +471,7 @@ def link_entrances(world, player):
else:
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# rest of hyrule castle must be in light world
connect_caves(world, lw_entrances, [], [('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')], player)
@ -552,7 +552,7 @@ def link_entrances(world, player):
('Lumberjack Tree Exit', 'Lumberjack Tree (top)'),
(('Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)'), 'Skull Woods Second Section (Drop)')]
if world.mode == 'standard':
if world.mode[player] == 'standard':
# cannot move uncle cave
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player)
@ -606,7 +606,7 @@ def link_entrances(world, player):
connect_entrance(world, hole, target, player)
# hyrule castle handling
if world.mode == 'standard':
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player)
@ -792,7 +792,7 @@ def link_entrances(world, player):
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
if world.mode == 'standard':
if world.mode[player] == 'standard':
# cannot move uncle cave
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player)
@ -825,7 +825,7 @@ def link_entrances(world, player):
connect_entrance(world, hole, hole_targets.pop(), player)
# hyrule castle handling
if world.mode == 'standard':
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player)
@ -927,7 +927,7 @@ def link_entrances(world, player):
hole_targets = ['Kakariko Well (top)', 'Bat Cave (right)', 'North Fairy Cave', 'Lost Woods Hideout (top)', 'Lumberjack Tree (top)', 'Sewer Drop', 'Skull Woods Second Section (Drop)',
'Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)']
if world.mode == 'standard':
if world.mode[player] == 'standard':
# cannot move uncle cave
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
connect_exit(world, 'Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Stairs', player)
@ -960,7 +960,7 @@ def link_entrances(world, player):
connect_entrance(world, hole, hole_targets.pop(), player)
# hyrule castle handling
if world.mode == 'standard':
if world.mode[player] == 'standard':
# must connect front of hyrule castle to do escape
connect_entrance(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
connect_exit(world, 'Hyrule Castle Exit (South)', 'Hyrule Castle Entrance (South)', player)
@ -1831,7 +1831,7 @@ def scramble_holes(world, player):
else:
hole_targets.append(('Pyramid Exit', 'Pyramid'))
if world.mode == 'standard':
if world.mode[player] == 'standard':
# cannot move uncle cave
connect_two_way(world, 'Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Exit', player)
connect_entrance(world, 'Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance', player)
@ -1931,11 +1931,11 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player, dp_m
if len(cave) == 2:
entrance = entrances.pop()
# ToDo Better solution, this is a hot fix. Do not connect both sides of trock/desert ledge only to each other
if world.mode != 'inverted' and entrance == 'Dark Death Mountain Ledge (West)':
if world.mode[player] != 'inverted' and entrance == 'Dark Death Mountain Ledge (West)':
new_entrance = entrances.pop()
entrances.append(entrance)
entrance = new_entrance
if world.mode == 'inverted' and entrance == dp_must_exit:
if world.mode[player] == 'inverted' and entrance == dp_must_exit:
new_entrance = entrances.pop()
entrances.append(entrance)
entrance = new_entrance
@ -2006,7 +2006,7 @@ def simple_shuffle_dungeons(world, player):
dungeon_entrances = ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace']
dungeon_exits = ['Eastern Palace Exit', 'Tower of Hera Exit', 'Thieves Town Exit', 'Skull Woods Final Section Exit', 'Palace of Darkness Exit', 'Ice Palace Exit', 'Misery Mire Exit', 'Swamp Palace Exit']
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
if not world.shuffle_ganon:
connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player)
else:
@ -2021,13 +2021,13 @@ def simple_shuffle_dungeons(world, player):
# mix up 4 door dungeons
multi_dungeons = ['Desert', 'Turtle Rock']
if world.mode == 'open' or (world.mode == 'inverted' and world.shuffle_ganon):
if world.mode[player] == 'open' or (world.mode[player] == 'inverted' and world.shuffle_ganon):
multi_dungeons.append('Hyrule Castle')
random.shuffle(multi_dungeons)
dp_target = multi_dungeons[0]
tr_target = multi_dungeons[1]
if world.mode not in ['open', 'inverted'] or (world.mode == 'inverted' and world.shuffle_ganon is False):
if world.mode[player] not in ['open', 'inverted'] or (world.mode[player] == 'inverted' and world.shuffle_ganon is False):
# place hyrule castle as intended
hc_target = 'Hyrule Castle'
else:
@ -2035,7 +2035,7 @@ def simple_shuffle_dungeons(world, player):
# ToDo improve this?
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
if hc_target == 'Hyrule Castle':
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
connect_two_way(world, 'Hyrule Castle Entrance (East)', 'Hyrule Castle Exit (East)', player)

View File

@ -239,8 +239,8 @@ def distribute_items_restrictive(world, gftower_trash_count=0, fill_locations=No
fill_locations.reverse()
# Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots
if world.keyshuffle and world.mode == 'standard':
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' else 0)
if world.keyshuffle:
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.mode[item.player] == 'standard' else 0)
fill_restrictive(world, world.state, fill_locations, progitempool)

View File

@ -344,10 +344,10 @@ def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None
ret.locations.append(Location(player, location, address, crystal, hint_text, ret, player_address))
return ret
def mark_dark_world_regions(world):
def mark_dark_world_regions(world, player):
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
# That is ok. the bunny logic will check for this case and incorporate special rules.
queue = collections.deque(region for region in world.regions if region.type == RegionType.DarkWorld)
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.DarkWorld)
seen = set(queue)
while queue:
current = queue.popleft()
@ -360,7 +360,7 @@ def mark_dark_world_regions(world):
seen.add(exit.connected_region)
queue.append(exit.connected_region)
queue = collections.deque(region for region in world.regions if region.type == RegionType.LightWorld)
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.LightWorld)
seen = set(queue)
while queue:
current = queue.popleft()

View File

@ -126,7 +126,7 @@ difficulties = {
def generate_itempool(world, player):
if (world.difficulty not in ['normal', 'hard', 'expert'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals']
or world.mode not in ['open', 'standard', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
or world.mode[player] not in ['open', 'standard', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
raise NotImplementedError('Not supported yet')
if world.timer in ['ohko', 'timed-ohko']:
@ -138,7 +138,7 @@ def generate_itempool(world, player):
world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False)
if world.goal in ['triforcehunt']:
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
region = world.get_region('Light World',player)
else:
region = world.get_region('Hyrule Castle Courtyard', player)
@ -177,15 +177,15 @@ def generate_itempool(world, player):
# set up item pool
if world.custom:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.swords, world.retro, world.customitemarray)
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode[player], world.swords, world.retro, world.customitemarray)
world.rupoor_cost = min(world.customitemarray[69], 9999)
else:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.swords, world.retro)
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode[player], world.swords, world.retro)
for item in precollected_items:
world.push_precollected(ItemFactory(item, player))
if world.mode == 'standard' and not world.state.has_blunt_weapon(player) and "Link's Uncle" not in placed_items:
if world.mode[player] == 'standard' and not world.state.has_blunt_weapon(player) and "Link's Uncle" not in placed_items:
found_sword = False
found_bow = False
possible_weapons = []
@ -261,7 +261,7 @@ take_any_locations = [
'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint']
def set_up_take_anys(world, player):
if world.mode == 'inverted' and 'Dark Sanctuary Hint' in take_any_locations:
if world.mode[player] == 'inverted' and 'Dark Sanctuary Hint' in take_any_locations:
take_any_locations.remove('Dark Sanctuary Hint')
regions = random.sample(take_any_locations, 5)

44
Main.py
View File

@ -56,30 +56,26 @@ def main(args, seed=None):
logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed)
world.difficulty_requirements = difficulties[world.difficulty]
if world.mode == 'standard' and (args.shuffleenemies != 'none' or args.enemy_health not in ['default', 'easy']):
world.escape_assist.append(['bombs']) # enemized escape assumes infinite bombs available and will likely be unbeatable without it
if world.mode != 'inverted':
for player in range(1, world.players + 1):
for player in range(1, world.players + 1):
if world.mode[player] == 'standard' and (args.shuffleenemies != 'none' or args.enemy_health not in ['default', 'easy']):
world.escape_assist[player].append(['bombs']) # enemized escape assumes infinite bombs available and will likely be unbeatable without it
if world.mode[player] != 'inverted':
create_regions(world, player)
create_dungeons(world, player)
else:
for player in range(1, world.players + 1):
else:
create_inverted_regions(world, player)
create_dungeons(world, player)
create_dungeons(world, player)
logger.info('Shuffling the World about.')
if world.mode != 'inverted':
for player in range(1, world.players + 1):
for player in range(1, world.players + 1):
if world.mode[player] != 'inverted':
link_entrances(world, player)
mark_light_world_regions(world)
else:
for player in range(1, world.players + 1):
mark_light_world_regions(world, player)
else:
link_inverted_entrances(world, player)
mark_dark_world_regions(world)
mark_dark_world_regions(world, player)
logger.info('Generating Item Pool.')
@ -189,7 +185,7 @@ def main(args, seed=None):
outfilesuffix = ('%s%s_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s' % (f'_P{player}' if world.players > 1 else '',
f'_{player_names[player]}' if player in player_names else '',
world.logic[player], world.difficulty, world.difficulty_adjustments,
world.mode, world.goal,
world.mode[player], world.goal,
"" if world.timer in ['none', 'display'] else "-" + world.timer,
world.shuffle, world.algorithm, mcsb_name,
"-retro" if world.retro else "",
@ -232,7 +228,7 @@ def copy_world(world):
ret.ganonstower_vanilla = world.ganonstower_vanilla.copy()
ret.treasure_hunt_count = world.treasure_hunt_count
ret.treasure_hunt_icon = world.treasure_hunt_icon
ret.sewer_light_cone = world.sewer_light_cone
ret.sewer_light_cone = world.sewer_light_cone.copy()
ret.light_world_light_cone = world.light_world_light_cone
ret.dark_world_light_cone = world.dark_world_light_cone
ret.seed = world.seed
@ -251,14 +247,12 @@ def copy_world(world):
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon
ret.crystals_needed_for_gt = world.crystals_needed_for_gt
if world.mode != 'inverted':
for player in range(1, world.players + 1):
for player in range(1, world.players + 1):
if world.mode[player] != 'inverted':
create_regions(ret, player)
create_dungeons(ret, player)
else:
for player in range(1, world.players + 1):
else:
create_inverted_regions(ret, player)
create_dungeons(ret, player)
create_dungeons(ret, player)
copy_dynamic_regions_and_locations(world, ret)
@ -436,7 +430,7 @@ def create_playthrough(world):
old_world.spoiler.paths.update({ str(location) : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player})
for _, path in dict(old_world.spoiler.paths).items():
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
old_world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = get_path(state, world.get_region('Big Bomb Shop', player))
else:
old_world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = get_path(state, world.get_region('Inverted Big Bomb Shop', player))

View File

@ -116,7 +116,7 @@ def fill_world(world, plando, text_patches):
tr_medallion = medallionstr.strip()
elif line.startswith('!mode'):
_, modestr = line.split(':', 1)
world.mode = modestr.strip()
world.mode = {1: modestr.strip()}
elif line.startswith('!logic'):
_, logicstr = line.split(':', 1)
world.logic = {1: logicstr.strip()}
@ -125,7 +125,7 @@ def fill_world(world, plando, text_patches):
world.goal = goalstr.strip()
elif line.startswith('!light_cone_sewers'):
_, sewerstr = line.split(':', 1)
world.sewer_light_cone = sewerstr.strip().lower() == 'true'
world.sewer_light_cone = {1: sewerstr.strip().lower() == 'true'}
elif line.startswith('!light_cone_lw'):
_, lwconestr = line.split(':', 1)
world.light_world_light_cone = lwconestr.strip().lower() == 'true'
@ -134,7 +134,7 @@ def fill_world(world, plando, text_patches):
world.dark_world_light_cone = dwconestr.strip().lower() == 'true'
elif line.startswith('!fix_trock_doors'):
_, trdstr = line.split(':', 1)
world.fix_trock_doors = trdstr.strip().lower() == 'true'
world.fix_trock_doors = {1: trdstr.strip().lower() == 'true'}
elif line.startswith('!fix_trock_exit'):
_, trfstr = line.split(':', 1)
world.fix_trock_exit = trfstr.strip().lower() == 'true'

View File

@ -335,10 +335,10 @@ def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None
ret.locations.append(Location(player, location, address, crystal, hint_text, ret, player_address))
return ret
def mark_light_world_regions(world):
def mark_light_world_regions(world, player):
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
# That is ok. the bunny logic will check for this case and incorporate special rules.
queue = collections.deque(region for region in world.regions if region.type == RegionType.LightWorld)
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.LightWorld)
seen = set(queue)
while queue:
current = queue.popleft()
@ -351,7 +351,7 @@ def mark_light_world_regions(world):
seen.add(exit.connected_region)
queue.append(exit.connected_region)
queue = collections.deque(region for region in world.regions if region.type == RegionType.DarkWorld)
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.DarkWorld)
seen = set(queue)
while queue:
current = queue.popleft()

44
Rom.py
View File

@ -248,7 +248,7 @@ def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shuffleene
}
}
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
options['ManualBosses']['GanonsTower1'] = world.get_dungeon('Ganons Tower', player).bosses['bottom'].enemizer_name
options['ManualBosses']['GanonsTower2'] = world.get_dungeon('Ganons Tower', player).bosses['middle'].enemizer_name
options['ManualBosses']['GanonsTower3'] = world.get_dungeon('Ganons Tower', player).bosses['top'].enemizer_name
@ -550,7 +550,7 @@ def patch_rom(world, player, rom, enemized):
else:
# patch door table
rom.write_byte(0xDBB73 + exit.addresses, exit.target)
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
patch_shuffled_dark_sanc(world, rom, player)
write_custom_shops(rom, world, player)
@ -578,11 +578,11 @@ def patch_rom(world, player, rom, enemized):
rom.write_byte(0x51DE, 0x00)
# set open mode:
if world.mode in ['open', 'inverted']:
if world.mode[player] in ['open', 'inverted']:
rom.write_byte(0x180032, 0x01) # open mode
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
set_inverted_mode(world, rom)
elif world.mode == 'standard':
elif world.mode[player] == 'standard':
rom.write_byte(0x180032, 0x00) # standard mode
uncle_location = world.get_location('Link\'s Uncle', player)
@ -600,7 +600,7 @@ def patch_rom(world, player, rom, enemized):
rom.write_bytes(0x6D323, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
# set light cones
rom.write_byte(0x180038, 0x01 if world.sewer_light_cone else 0x00)
rom.write_byte(0x180038, 0x01 if world.sewer_light_cone[player] else 0x00)
rom.write_byte(0x180039, 0x01 if world.light_world_light_cone else 0x00)
rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00)
@ -892,7 +892,7 @@ def patch_rom(world, player, rom, enemized):
# assorted fixes
rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark world dungeon before killing aga1
rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted
rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death
rom.write_byte(0x180173, 0x01) # Bob is enabled
@ -930,18 +930,18 @@ def patch_rom(world, player, rom, enemized):
else:
raise RuntimeError("Unsupported pre-collected item: {}".format(item))
rom.write_byte(0x18004A, 0x00 if world.mode != 'inverted' else 0x01) # Inverted mode
rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode
rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier
rom.write_byte(0x2AF79, 0xD0 if world.mode != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
rom.write_byte(0x3A943, 0xD0 if world.mode != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both)
rom.write_byte(0x3A96D, 0xF0 if world.mode != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
rom.write_byte(0x2AF79, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
rom.write_byte(0x3A943, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both)
rom.write_byte(0x3A96D, 0xF0 if world.mode[player] != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader))
rom.write_bytes(0x180080, [50, 50, 70, 70]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10)
rom.write_byte(0x18004D, ((0x01 if 'arrows' in world.escape_assist else 0x00) |
(0x02 if 'bombs' in world.escape_assist else 0x00) |
(0x04 if 'magic' in world.escape_assist else 0x00))) # Escape assist
rom.write_byte(0x18004D, ((0x01 if 'arrows' in world.escape_assist[player] else 0x00) |
(0x02 if 'bombs' in world.escape_assist[player] else 0x00) |
(0x04 if 'magic' in world.escape_assist[player] else 0x00))) # Escape assist
if world.goal in ['pedestal', 'triforcehunt']:
rom.write_byte(0x18003E, 0x01) # make ganon invincible
@ -954,7 +954,7 @@ def patch_rom(world, player, rom, enemized):
rom.write_byte(0x18005E, world.crystals_needed_for_gt)
rom.write_byte(0x18005F, world.crystals_needed_for_ganon)
rom.write_byte(0x18008A, 0x01 if world.mode == "standard" else 0x00) # block HC upstairs doors in rain state in standard mode
rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" else 0x00) # block HC upstairs doors in rain state in standard mode
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle else 0x00)
| (0x02 if world.compassshuffle else 0x00)
@ -1031,7 +1031,7 @@ def patch_rom(world, player, rom, enemized):
rom.write_bytes(0x180185, [0,0,0]) # Uncle respawn refills (magic, bombs, arrows)
rom.write_bytes(0x180188, [0,0,0]) # Zelda respawn refills (magic, bombs, arrows)
rom.write_bytes(0x18018B, [0,0,0]) # Mantle respawn refills (magic, bombs, arrows)
if world.mode == 'standard':
if world.mode[player] == 'standard':
if uncle_location.item is not None and uncle_location.item.name in ['Bow', 'Progressive Bow']:
rom.write_byte(0x18004E, 1) # Escape Fill (arrows)
write_int16(rom, 0x180183, 300) # Escape fill rupee bow
@ -1068,7 +1068,7 @@ def patch_rom(world, player, rom, enemized):
rom.write_byte(0x4E3BB, 0xEB)
# fix trock doors for reverse entrances
if world.fix_trock_doors:
if world.fix_trock_doors[player]:
rom.write_byte(0xFED31, 0x0E) # preopen bombable exit
rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit
# included unconditionally in base2current
@ -1375,7 +1375,7 @@ def write_strings(rom, world, player):
entrances_to_hint = {}
entrances_to_hint.update(InconvenientDungeonEntrances)
if world.shuffle_ganon:
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
entrances_to_hint.update({'Inverted Ganons Tower': 'The sealed castle door'})
else:
entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'})
@ -1408,14 +1408,14 @@ def write_strings(rom, world, player):
if world.shuffle not in ['simple', 'restricted', 'restricted_legacy']:
entrances_to_hint.update(ConnectorEntrances)
entrances_to_hint.update(DungeonEntrances)
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
entrances_to_hint.update({'Inverted Agahnims Tower': 'The dark mountain tower'})
else:
entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'})
elif world.shuffle == 'restricted':
entrances_to_hint.update(ConnectorEntrances)
entrances_to_hint.update(OtherEntrances)
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
entrances_to_hint.update({'Inverted Dark Sanctuary': 'The dark sanctuary cave'})
entrances_to_hint.update({'Inverted Big Bomb Shop': 'The old hero\'s dark home'})
entrances_to_hint.update({'Inverted Links House': 'The old hero\'s light home'})
@ -1425,7 +1425,7 @@ def write_strings(rom, world, player):
if world.shuffle in ['insanity', 'madness_legacy', 'insanity_legacy']:
entrances_to_hint.update(InsanityEntrances)
if world.shuffle_ganon:
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
else:
entrances_to_hint.update({'Pyramid Ledge': 'The pyramid ledge'})
@ -1592,7 +1592,7 @@ def write_strings(rom, world, player):
tt['tablet_bombos_book'] = bombos_text
# inverted spawn menu changes
if world.mode == 'inverted':
if world.mode[player] == 'inverted':
tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n{CHOICE3}"
tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n Mountain Cave\n{CHOICE2}"
tt['intro_main'] = CompressedTextMapper.convert(

View File

@ -7,7 +7,7 @@ def set_rules(world, player):
if world.logic[player] == 'nologic':
logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!')
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
world.get_region('Links House', player).can_reach_private = lambda state: True
world.get_region('Sanctuary', player).can_reach_private = lambda state: True
old_rule = world.get_region('Old Man House', player).can_reach
@ -22,14 +22,14 @@ def set_rules(world, player):
return
global_rules(world, player)
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
default_rules(world, player)
if world.mode == 'open':
if world.mode[player] == 'open':
open_rules(world, player)
elif world.mode == 'standard':
elif world.mode[player] == 'standard':
standard_rules(world, player)
elif world.mode == 'inverted':
elif world.mode[player] == 'inverted':
open_rules(world, player)
inverted_rules(world, player)
else:
@ -49,7 +49,7 @@ def set_rules(world, player):
# require aga2 to beat ganon
add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player))
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
set_big_bomb_rules(world, player)
else:
set_inverted_big_bomb_rules(world, player)
@ -58,7 +58,7 @@ def set_rules(world, player):
if not world.swamp_patch_required[player]:
add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has_Mirror(player))
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
set_bunny_rules(world, player)
else:
set_inverted_bunny_rules(world, player)
@ -345,7 +345,7 @@ def global_rules(world, player):
def default_rules(world, player):
if world.mode == 'standard':
if world.mode[player] == 'standard':
world.get_region('Hyrule Castle Secret Entrance', player).can_reach_private = lambda state: True
old_rule = world.get_region('Links House', player).can_reach_private
world.get_region('Links House', player).can_reach_private = lambda state: state.can_reach('Sanctuary', 'Region', player) or old_rule(state)
@ -621,7 +621,7 @@ def inverted_rules(world, player):
set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player))
def no_glitches_rules(world, player):
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player))
set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Flippers', player)) # can be fake flippered to
set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has('Flippers', player))
@ -672,7 +672,7 @@ def no_glitches_rules(world, player):
add_conditional_lamp('Palace of Darkness Maze Door', 'Palace of Darkness (Entrance)', 'Entrance')
add_conditional_lamp('Palace of Darkness - Dark Basement - Left', 'Palace of Darkness (Entrance)', 'Location')
add_conditional_lamp('Palace of Darkness - Dark Basement - Right', 'Palace of Darkness (Entrance)', 'Location')
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
add_conditional_lamp('Agahnim 1', 'Agahnims Tower', 'Entrance')
add_conditional_lamp('Castle Tower - Dark Maze', 'Agahnims Tower', 'Location')
else:
@ -688,7 +688,7 @@ def no_glitches_rules(world, player):
add_conditional_lamp('Eastern Palace - Boss', 'Eastern Palace', 'Location')
add_conditional_lamp('Eastern Palace - Prize', 'Eastern Palace', 'Location')
if not world.sewer_light_cone:
if not world.sewer_light_cone[player]:
add_lamp_requirement(world.get_location('Sewers - Dark Cross', player), player)
add_lamp_requirement(world.get_entrance('Sewers Back Door', player), player)
add_lamp_requirement(world.get_entrance('Throne Room', player), player)
@ -709,7 +709,7 @@ def swordless_rules(world, player):
set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon, player))
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop
if world.mode != 'inverted':
if world.mode[player] != 'inverted':
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_Pearl(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!)
set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_Pearl(player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!)