Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2022-01-03 18:10:42 -06:00
16 changed files with 188 additions and 154 deletions

View File

@@ -298,6 +298,9 @@ class World(object):
self._owedge_cache[(edgename, player)] = edge
return edge
return None
def is_tile_swapped(self, owid, player):
return (self.mode[player] == 'inverted') != (owid in self.owswaps[player][0] and self.owMixed[player])
def check_for_door(self, doorname, player):
if isinstance(doorname, Door):
@@ -2831,6 +2834,12 @@ class Spoiler(object):
'triforcepool': self.world.treasure_hunt_total,
'code': {p: Settings.make_code(self.world, p) for p in range(1, self.world.players + 1)}
}
for p in range(1, self.world.players + 1):
from ItemList import set_default_triforce
if self.world.custom and p in self.world.customitemarray:
self.metadata['triforcegoal'][p], self.metadata['triforcepool'][p] = set_default_triforce(self.metadata['goal'][p], self.world.customitemarray[p]["triforcepiecesgoal"], self.world.customitemarray[p]["triforcepieces"])
else:
self.metadata['triforcegoal'][p], self.metadata['triforcepool'][p] = set_default_triforce(self.metadata['goal'][p], 0, 0)
def parse_data(self):
self.medallions = OrderedDict()
@@ -2965,7 +2974,7 @@ class Spoiler(object):
outfile.write('Retro:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['retro'][player] else 'No'))
outfile.write('Swords:'.ljust(line_width) + '%s\n' % self.metadata['weapons'][player])
outfile.write('Goal:'.ljust(line_width) + '%s\n' % self.metadata['goal'][player])
if self.metadata['goal'][player] == 'triforcehunt':
if self.metadata['goal'][player] in ['triforcehunt', 'trinity']:
outfile.write('Triforce Pieces Required:'.ljust(line_width) + '%s\n' % self.metadata['triforcegoal'][player])
outfile.write('Triforce Pieces Total:'.ljust(line_width) + '%s\n' % self.metadata['triforcepool'][player])
outfile.write('Crystals Required for GT:'.ljust(line_width) + '%s\n' % str(self.world.crystals_gt_orig[player]))
@@ -2986,7 +2995,8 @@ class Spoiler(object):
if self.metadata['shuffle'][player] != 'vanilla':
outfile.write('Shuffle GT/Ganon:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['shuffleganon'][player] else 'No'))
outfile.write('Shuffle Links:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['shufflelinks'][player] else 'No'))
outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
if self.metadata['goal'][player] != 'trinity':
outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['open_pyramid'][player] else 'No'))
outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
if self.metadata['door_shuffle'][player] != 'vanilla':
outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player])
@@ -3212,7 +3222,7 @@ world_mode = {"open": 0, "standard": 1, "inverted": 2}
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
# byte 2: GGGD DFFH (goal, diff, item_func, hints)
goal_mode = {"ganon": 0, "pedestal": 1, "dungeons": 2, "triforcehunt": 3, "crystals": 4}
goal_mode = {"ganon": 0, "pedestal": 1, "dungeons": 2, "triforcehunt": 3, "crystals": 4, "trinity": 5}
diff_mode = {"normal": 0, "hard": 1, "expert": 2}
func_mode = {"normal": 0, "hard": 1, "expert": 2}

View File

@@ -1,5 +1,10 @@
# Changelog
### 0.2.3.6
- Added Trinity goal (8/10 default TF pieces)
- Many improvements to TFH pool allocation
- Fixed some issues with Multiworld generation with Custom Item Pools
### 0.2.3.5
- Fixed issue with multiworld generation
- Added infinite loop detection

View File

@@ -89,11 +89,11 @@ def link_doors_main(world, player):
if world.mode[player] == 'standard':
world.get_portal('Sanctuary', player).destination = True
world.get_portal('Desert East', player).destination = True
if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x30, player):
world.get_portal('Desert West', player).destination = True
if (world.mode[player] == 'inverted') == (0x00 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x00, player):
world.get_portal('Skull 2 West', player).destination = True
if (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x05, player):
world.get_portal('Turtle Rock Lazy Eyes', player).destination = True
world.get_portal('Turtle Rock Eye Bridge', player).destination = True
else:
@@ -1837,7 +1837,7 @@ def find_inaccessible_regions(world, player):
if connect.type is not RegionType.Dungeon or connect.name.endswith(' Portal'):
queue.append(connect)
world.inaccessible_regions[player].extend([r.name for r in all_regions if r not in visited_regions and valid_inaccessible_region(r)])
if (world.mode[player] == 'inverted') != (0x1b in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x1b, player):
ledge = world.get_region('Hyrule Castle Ledge', player)
if any(x for x in ledge.exits if x.connected_region and x.connected_region.name == 'Agahnims Tower Portal'):
world.inaccessible_regions[player].append('Hyrule Castle Ledge')
@@ -1857,7 +1857,7 @@ def find_accessible_entrances(world, player, builder):
start_regions = ['Links House', 'Sanctuary']
else:
start_regions = ['Links House', 'Dark Sanctuary Hint']
if (world.mode[player] == 'inverted') != (0x1b in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x1b, player):
start_regions.append('Hyrule Castle Ledge')
regs = convert_regions(start_regions, world, player)
visited_regions = set()
@@ -1873,7 +1873,7 @@ def find_accessible_entrances(world, player, builder):
while len(queue) > 0:
next_region = queue.popleft()
visited_regions.add(next_region)
if (world.mode[player] == 'inverted') != (0x1b in world.owswaps[player][0] and world.owMixed[player]) and next_region.name == 'Tower Agahnim 1':
if world.is_tile_swapped(0x1b, player) and next_region.name == 'Tower Agahnim 1':
connect = world.get_region('Hyrule Castle Ledge', player)
if connect not in queue and connect not in visited_regions:
queue.append(connect)

View File

@@ -34,7 +34,7 @@ def link_entrances(world, player):
world.owsectors[player] = build_sectors(world, player)
# modifications to lists
if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x1b, player):
drop_connections.append(tuple(('Pyramid Hole', 'Pyramid')))
dropexit_connections.append(tuple(('Pyramid Entrance', 'Pyramid Exit')))
connect_simple(world, 'Other World S&Q', 'Pyramid Area', player)
@@ -47,7 +47,7 @@ def link_entrances(world, player):
dropexit_connections.append(tuple(('Inverted Pyramid Entrance', 'Pyramid Exit')))
connect_simple(world, 'Other World S&Q', 'Hyrule Castle Ledge', player)
if invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x03, player):
connect_simple(world, 'Old Man S&Q', 'Old Man House', player)
else:
connect_simple(world, 'Old Man S&Q', 'West Dark Death Mountain (Bottom)', player)
@@ -89,18 +89,16 @@ def link_entrances(world, player):
# inverted entrance mods
ignore_pool = True
for owid in swapped_connections.keys():
if invFlag != (owid in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(owid, player):
for (entrancename, exitname) in swapped_connections[owid]:
try:
connect_two_way(world, entrancename, exitname, player)
except RuntimeError:
connect_entrance(world, entrancename, exitname, player)
if invFlag != (0x03 in world.owswaps[player][0] and world.owMixed[player]) and \
invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x03, player) and not world.is_tile_swapped(0x0a, player):
connect_entrance(world, 'Death Mountain Return Cave (West)', 'Dark Death Mountain Healer Fairy', player)
elif invFlag != (0x0a in world.owswaps[player][0] and world.owMixed[player]) and \
invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
elif world.is_tile_swapped(0x0a, player) and not world.is_tile_swapped(0x03, player):
connect_two_way(world, 'Bumper Cave (Top)', 'Death Mountain Return Cave Exit (West)', player)
ignore_pool = False
@@ -144,15 +142,15 @@ def link_entrances(world, player):
Two_Door_Caves_Directional = list()
Two_Door_Caves = [('Elder House (East)', 'Elder House (West)'),
('Superbunny Cave (Bottom)', 'Superbunny Cave (Top)')]
if invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x0a, player):
Two_Door_Caves_Directional.append(tuple({'Bumper Cave (Bottom)', 'Bumper Cave (Top)'}))
else:
Two_Door_Caves_Directional.append(tuple({'Old Man Cave (West)', 'Death Mountain Return Cave (West)'}))
if invFlag == (0x05 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x05, player):
Two_Door_Caves_Directional.append(tuple({'Hookshot Cave', 'Hookshot Cave Back Entrance'}))
else:
Two_Door_Caves.append(tuple({'Hookshot Cave', 'Hookshot Cave Back Entrance'}))
if invFlag == (0x28 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x28, player):
Two_Door_Caves.append(tuple({'Two Brothers House (East)', 'Two Brothers House (West)'}))
else:
Two_Door_Caves_Directional.append(tuple({'Two Brothers House (East)', 'Two Brothers House (West)'}))
@@ -181,14 +179,14 @@ def link_entrances(world, player):
caves.extend(list(Old_Man_House))
caves.extend(list(three_exit_caves))
if invFlag == (0x18 in world.owswaps[player][0] and world.owMixed[player]) or invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]): # ability to activate flute in LW
if (not world.is_tile_swapped(0x18, player)) or (not world.is_tile_swapped(0x03, player)): # ability to activate flute in LW
candidates = [e for e in lw_wdm_entrances if e != 'Old Man House (Bottom)']
random.shuffle(candidates)
old_man_exit = candidates.pop()
lw_wdm_entrances.remove(old_man_exit)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
if invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x0a, player):
lw_wdm_entrances.extend(['Old Man Cave (West)', 'Death Mountain Return Cave (West)'])
else:
lw_wdm_entrances.extend(['Bumper Cave (Bottom)', 'Bumper Cave (Top)'])
@@ -213,7 +211,7 @@ def link_entrances(world, player):
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
# place old man, bumper cave bottom to DDM entrances not in east bottom
if invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x0a, player):
connect_two_way(world, 'Old Man Cave (West)', 'Old Man Cave Exit (West)', player)
else:
connect_two_way(world, 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player)
@@ -223,7 +221,7 @@ def link_entrances(world, player):
connect_caves(world, lw_wdm_entrances + lw_edm_entrances, [], caves, player)
else:
# place Old Man House in WDM if not swapped
if invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x03, player):
connect_caves(world, lw_wdm_entrances, [], list(Old_Man_House), player)
else:
connect_caves(world, lw_edm_entrances, [], list(Old_Man_House), player)
@@ -258,7 +256,7 @@ def link_entrances(world, player):
# place bomb shop, has limited options
bomb_shop_doors = list(entrance_pool)
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player):
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
bomb_shop = random.choice(bomb_shop_doors)
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
@@ -303,7 +301,7 @@ def link_entrances(world, player):
# place bomb shop, has limited options
bomb_shop_doors = list(entrance_pool)
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player):
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
bomb_shop = random.choice(bomb_shop_doors)
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
@@ -360,7 +358,7 @@ def link_entrances(world, player):
# place bomb shop, has limited options
bomb_shop_doors = [e for e in entrance_pool if e not in list(zip(*drop_connections + dropexit_connections))[0]]
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player):
bomb_shop_doors = [e for e in bomb_shop_doors if e not in ['Pyramid Fairy']]
bomb_shop = random.choice(bomb_shop_doors)
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
@@ -462,7 +460,7 @@ def link_entrances(world, player):
# place bomb shop, has limited options
bomb_shop_doors = list(entrance_pool)
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player):
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
bomb_shop = random.choice(bomb_shop_doors)
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
@@ -516,7 +514,7 @@ def link_entrances(world, player):
# place bomb shop, has limited options
bomb_shop_doors = list(entrance_pool)
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player):
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
bomb_shop = random.choice(bomb_shop_doors)
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
@@ -565,7 +563,7 @@ def link_entrances(world, player):
# place bomb shop, has limited options
bomb_shop_doors = list(entrance_pool)
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player):
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
bomb_shop = random.choice(bomb_shop_doors)
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
@@ -604,11 +602,11 @@ def link_entrances(world, player):
if not world.shuffle_ganon:
connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player)
connect_two_way(world, 'Pyramid Entrance' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
connect_entrance(world, 'Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole', 'Pyramid', player)
connect_two_way(world, 'Pyramid Entrance' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
connect_entrance(world, 'Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole', 'Pyramid', player)
else:
caves.extend(['Ganons Tower Exit', 'Pyramid Exit'])
hole_entrances.append('Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole')
hole_entrances.append('Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole')
hole_targets.append('Pyramid')
# shuffle holes
@@ -648,7 +646,7 @@ def link_entrances(world, player):
# place bomb shop, has limited options
bomb_shop_doors = list(entrance_pool)
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player):
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
@@ -693,7 +691,7 @@ def link_entrances(world, player):
world.powder_patch_required[player] = True
# check for ganon location
if world.get_entrance('Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid':
if world.get_entrance('Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid':
world.ganon_at_pyramid[player] = False
# check for Ganon's Tower location
@@ -1074,7 +1072,7 @@ def scramble_holes(world, player):
hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance'))
if world.shuffle_ganon:
hole_entrances.append(('Pyramid Entrance', 'Pyramid Hole') if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]) else ('Inverted Pyramid Entrance', 'Inverted Pyramid Hole'))
hole_entrances.append(('Pyramid Entrance', 'Pyramid Hole') if not world.is_tile_swapped(0x1b, player) else ('Inverted Pyramid Entrance', 'Inverted Pyramid Hole'))
hole_targets.append(('Pyramid Exit', 'Pyramid'))
# shuffle sanctuary hole in same world as other HC entrances
@@ -1099,11 +1097,11 @@ def scramble_holes(world, player):
else:
# checks if drop candidates exist in LW
drop_owids = [ 0x00, 0x02, 0x13, 0x15, 0x18, 0x1b, 0x22 ]
hc_in_lw = any([invFlag == (owid in world.owswaps[player][0] and world.owMixed[player]) for owid in drop_owids])
hc_in_lw = any([not world.is_tile_swapped(owid, player) for owid in drop_owids])
candidate_drops = list()
for door, drop in hole_entrances:
if hc_in_lw == (drop_owid_map[door][1] == (invFlag == (drop_owid_map[door][0] in world.owswaps[player][0] and world.owMixed[player]))):
if hc_in_lw == (drop_owid_map[door][1] == (not world.is_tile_swapped(drop_owid_map[door][0], player))):
candidate_drops.append(tuple((door, drop)))
random.shuffle(candidate_drops)
@@ -1117,7 +1115,7 @@ def scramble_holes(world, player):
# place pyramid hole
if not world.shuffle_ganon:
exit, target = ('Pyramid Exit', 'Pyramid')
if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x1b, player):
connect_two_way(world, 'Pyramid Entrance', exit, player)
connect_entrance(world, 'Pyramid Hole', target, player)
else:
@@ -1183,16 +1181,16 @@ def simple_shuffle_dungeons(world, player):
# checks if drop candidates exist in LW
drop_owids = [ 0x00, 0x02, 0x13, 0x15, 0x18, 0x1b, 0x22 ]
drops_in_light_world = any([invFlag == (owid in world.owswaps[player][0] and world.owMixed[player]) for owid in drop_owids])
drops_in_light_world = any([not world.is_tile_swapped(owid, player) for owid in drop_owids])
# placing HC in guaranteed same-world as available dropdowns
if not drops_in_light_world or not invFlag:
candidate_dungeons = list()
for d in multi_dungeons:
if not drops_in_light_world and dungeon_owid_map[d][1] == (invFlag != (dungeon_owid_map[d][0] in world.owswaps[player][0] and world.owMixed[player])):
if not drops_in_light_world and dungeon_owid_map[d][1] == world.is_tile_swapped(dungeon_owid_map[d][0], player):
# only adding DW candidates
candidate_dungeons.append(d)
elif not invFlag and dungeon_owid_map[d][1] == (invFlag == (dungeon_owid_map[d][0] in world.owswaps[player][0] and world.owMixed[player])):
elif not invFlag and dungeon_owid_map[d][1] == (not world.is_tile_swapped(dungeon_owid_map[d][0], player)):
# only adding LW candidates
candidate_dungeons.append(d)
random.shuffle(candidate_dungeons)
@@ -1218,7 +1216,7 @@ def simple_shuffle_dungeons(world, player):
elif hc_target == 'Turtle Rock':
connect_two_way(world, 'Turtle Rock', 'Hyrule Castle Exit (South)', player)
connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Hyrule Castle Exit (West)', player)
if invFlag == (0x45 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x45, player):
connect_two_way(world, 'Turtle Rock Isolated Ledge Entrance', 'Hyrule Castle Exit (East)', player)
connect_two_way(world, 'Dark Death Mountain Ledge (East)', at_door, player)
else:
@@ -1302,7 +1300,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player):
lw_entrances = list()
dw_entrances = list()
for owid in dungeon_owid_map.keys():
if dungeon_owid_map[owid][1] == (invFlag == (owid in world.owswaps[player][0] and world.owMixed[player])):
if dungeon_owid_map[owid][1] == (not world.is_tile_swapped(owid, player)):
lw_entrances.extend([e for e in dungeon_owid_map[owid][0] if e in entrance_pool])
else:
dw_entrances.extend([e for e in dungeon_owid_map[owid][0] if e in entrance_pool])
@@ -1315,7 +1313,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player):
dw_must_exit = list()
lw_related = list()
dw_related = list()
if invFlag == (0x45 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x45, player):
dw_entrances.remove('Turtle Rock Isolated Ledge Entrance')
dw_must_exit.append('Turtle Rock Isolated Ledge Entrance')
if 'Dark Death Mountain Ledge' in world.inaccessible_regions[player]:
@@ -1324,7 +1322,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player):
random.shuffle(ledge)
dw_must_exit.append(ledge.pop())
dw_related.extend(ledge)
if invFlag == (0x30 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x30, player):
if 'Desert Palace Mouth' in world.inaccessible_regions[player]:
lw_entrances.remove('Desert Palace Entrance (East)')
lw_must_exit.append('Desert Palace Entrance (East)')
@@ -1337,7 +1335,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player):
random.shuffle(ledge)
dw_must_exit.append(ledge.pop())
dw_related.extend(ledge)
if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x1b, player):
if 'Hyrule Castle Ledge' in world.inaccessible_regions[player]:
ledge = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Agahnims Tower']
lw_entrances = [e for e in lw_entrances if e not in ledge]
@@ -1352,7 +1350,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player):
hyrule_castle_exits = list([tuple(e for e in hyrule_castle_exits if e in exit_pool)])
hyrule_castle_exits.extend([e for e in dungeon_exits if isinstance(e, str)])
dungeon_exits = [e for e in dungeon_exits if not isinstance(e, str)]
if invFlag == (0x13 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x13, player):
connect_mandatory_exits(world, lw_entrances, hyrule_castle_exits, lw_must_exit, player, False)
dungeon_exits.extend([e for e in hyrule_castle_exits if isinstance(e, str)])
hyrule_castle_exits = [e for e in hyrule_castle_exits if not isinstance(e, str)]
@@ -1444,7 +1442,7 @@ def place_blacksmith(world, links_house, player):
def place_old_man(world, pool, player, ignore_list=[]):
# exit has to come from specific set of doors, the entrance is free to move about
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x03, player):
region_name = 'West Death Mountain (Top)'
else:
region_name = 'West Dark Death Mountain (Top)'
@@ -1537,7 +1535,7 @@ def connect_inaccessible_regions(world, lw_entrances, dw_entrances, caves, playe
for region_name in inaccessible_regions.copy():
region = world.get_region(region_name, player)
if region.type not in [RegionType.LightWorld, RegionType.DarkWorld] or not any((not exit.connected_region and exit.spot_type == 'Entrance') for exit in region.exits) \
or (region_name == 'Pyramid Exit Ledge' and world.shuffle[player] != 'insanity' or invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
or (region_name == 'Pyramid Exit Ledge' and world.shuffle[player] != 'insanity' or world.is_tile_swapped(0x1b, player)):
inaccessible_regions.remove(region_name)
elif region.type == (RegionType.LightWorld if not invFlag else RegionType.DarkWorld):
must_exit_regions.append(region_name)

View File

@@ -358,7 +358,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
if not gftower_trash or not world.ganonstower_vanilla[player] or world.doorShuffle[player] == 'crossed' or world.logic[player] in ['owglitches', 'nologic']:
continue
gftower_trash_count = (random.randint(15, 50) if world.goal[player] == 'triforcehunt' else random.randint(0, 15))
gftower_trash_count = (random.randint(15, 50) if world.goal[player] in ['triforcehunt', 'trinity'] else random.randint(0, 15))
gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player]
random.shuffle(gtower_locations)
@@ -701,7 +701,7 @@ def balance_money_progression(world):
sphere_locations = get_sphere_locations(state, unchecked_locations)
checked_locations = []
for player in range(1, world.players+1):
kiki_payable = state.prog_items[('Moon Pearl', player)] > 0 or (world.mode[player] == 'inverted') != (0x1e in world.owswaps[player][0] and world.owMixed[player])
kiki_payable = state.prog_items[('Moon Pearl', player)] > 0 or world.is_tile_swapped(0x1e, player)
if kiki_payable and world.get_region('Palace of Darkness Area', player) in state.reachable_regions[player]:
if not kiki_paid[player]:
kiki_check[player] = True

View File

@@ -178,7 +178,7 @@ def get_custom_array_key(item):
def generate_itempool(world, player):
if (world.difficulty[player] not in ['normal', 'hard', 'expert'] or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals']
if (world.difficulty[player] not in ['normal', 'hard', 'expert'] or world.goal[player] not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'trinity', 'crystals']
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')
@@ -190,7 +190,7 @@ def generate_itempool(world, player):
else:
world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False)
if world.goal[player] in ['triforcehunt']:
if world.goal[player] in ['triforcehunt', 'trinity']:
region = world.get_region('Hyrule Castle Courtyard',player)
loc = Location(player, "Murahdahla", parent=region)
@@ -262,8 +262,8 @@ def generate_itempool(world, player):
world.get_location('Zelda Drop Off', player).locked = True
# 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[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray)
if world.custom and player in world.customitemarray:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_total, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray[player])
world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999)
else:
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player])
@@ -337,14 +337,9 @@ def generate_itempool(world, player):
if clock_mode is not None:
world.clock_mode = clock_mode
if world.goal[player] == 'triforcehunt':
if world.treasure_hunt_count[player] == 0:
world.treasure_hunt_count[player] = 20
if world.treasure_hunt_total[player] == 0:
world.treasure_hunt_total[player] = 30
if world.goal[player] in ['triforcehunt', 'trinity']:
world.treasure_hunt_count[player], world.treasure_hunt_total[player] = set_default_triforce(world.goal[player], world.treasure_hunt_count[player], world.treasure_hunt_total[player])
world.treasure_hunt_icon[player] = 'Triforce Piece'
if world.custom:
world.treasure_hunt_count[player] = treasure_hunt_count
world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player
and ((item.smallkey and world.keyshuffle[player])
@@ -355,9 +350,9 @@ def generate_itempool(world, player):
# logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
# rather than making all hearts/heart pieces progression items (which slows down generation considerably)
# We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
if world.difficulty[player] in ['normal', 'hard'] and not (world.custom and world.customitemarray[player]["heartcontainer"] == 0):
if world.difficulty[player] in ['normal', 'hard'] and not (world.custom and player in world.customitemarray and world.customitemarray[player]["heartcontainer"] == 0):
next(item for item in items if item.name == 'Boss Heart Container').advancement = True
elif world.difficulty[player] in ['expert'] and not (world.custom and world.customitemarray[player]["heartpiece"] < 4):
elif world.difficulty[player] in ['expert'] and not (world.custom and player in world.customitemarray and world.customitemarray[player]["heartpiece"] < 4):
adv_heart_pieces = (item for item in items if item.name == 'Piece of Heart')
for i in range(4):
next(adv_heart_pieces).advancement = True
@@ -414,7 +409,7 @@ def set_up_take_anys(world, player):
if world.mode[player] == 'inverted':
if 'Dark Sanctuary Hint' in take_any_locations:
take_any_locations.remove('Dark Sanctuary Hint')
if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x29, player):
if 'Archery Game' in take_any_locations:
take_any_locations.remove('Archery Game')
@@ -759,7 +754,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
placed_items = {}
precollected_items = []
clock_mode = None
if goal == 'triforcehunt':
if goal in ['triforcehunt', 'trinity']:
if treasure_hunt_total == 0:
treasure_hunt_total = 30
triforcepool = ['Triforce Piece'] * int(treasure_hunt_total)
@@ -841,7 +836,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
place_item('Link\'s Uncle', swords_to_use.pop())
place_item('Blacksmith', swords_to_use.pop())
place_item('Pyramid Fairy - Left', swords_to_use.pop())
if goal != 'pedestal':
if goal not in ['pedestal', 'trinity']:
place_item('Master Sword Pedestal', swords_to_use.pop())
else:
place_item('Master Sword Pedestal', 'Triforce')
@@ -866,7 +861,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
pool.extend(diff.timedohko)
extraitems -= len(diff.timedohko)
clock_mode = 'countdown-ohko'
if goal == 'triforcehunt':
if goal in ['triforcehunt', 'trinity']:
pool.extend(triforcepool)
extraitems -= len(triforcepool)
@@ -877,7 +872,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
pool.extend(extra)
extraitems -= len(extra)
if goal == 'pedestal' and swords != 'vanilla':
if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
place_item('Master Sword Pedestal', 'Triforce')
if retro:
pool = [item.replace('Single Arrow','Rupees (5)') for item in pool]
@@ -901,13 +896,12 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms)
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag, customitemarray):
if isinstance(customitemarray,dict) and 1 in customitemarray:
customitemarray = customitemarray[1]
pool = []
placed_items = {}
precollected_items = []
clock_mode = None
treasure_hunt_count = None
treasure_hunt_total = None
treasure_hunt_icon = None
def place_item(loc, item):
@@ -916,15 +910,19 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
# Correct for insanely oversized item counts and take initial steps to handle undersized pools.
# Bow to Silver Arrows Upgrade, including Generic Keys & Rupoors
for x in [*range(0, 66 + 1), 68, 69]:
for x in [*range(0, 69)]:
key = CONST.CUSTOMITEMS[x]
if customitemarray[key] > total_items_to_place:
customitemarray[key] = total_items_to_place
# Triforce
if customitemarray["triforce"] > total_items_to_place:
customitemarray["triforce"] = total_items_to_place
# Triforce Pieces
if goal in ['triforcehunt', 'trinity']:
customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"] = set_default_triforce(goal, customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"])
itemtotal = 0
# Bow to Silver Arrows Upgrade, including Generic Keys & Rupoors
for x in [*range(0, 66 + 1), 68, 69]:
@@ -955,13 +953,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
pool.append(thisbottle)
if customitemarray["triforcepieces"] > 0 or customitemarray["triforcepiecesgoal"] > 0:
treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], 99), 1) #To display, count must be between 1 and 99.
treasure_hunt_icon = 'Triforce Piece'
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
if (customitemarray["triforcepieces"] < treasure_hunt_count) and (goal == 'triforcehunt') and (customitemarray["triforce"] == 0):
extrapieces = treasure_hunt_count - customitemarray["triforcepieces"]
pool.extend(['Triforce Piece'] * extrapieces)
itemtotal = itemtotal + extrapieces
if timer in ['display', 'timed', 'timed-countdown']:
clock_mode = 'countdown' if timer == 'timed-countdown' else 'stopwatch'
@@ -970,7 +962,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
elif timer == 'ohko':
clock_mode = 'ohko'
if goal == 'pedestal':
if goal in ['pedestal', 'trinity']:
place_item('Master Sword Pedestal', 'Triforce')
itemtotal = itemtotal + 1
@@ -1003,12 +995,24 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
# print("Placing " + str(nothings) + " Nothings")
pool.extend(['Nothing'] * nothings)
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms)
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_total, treasure_hunt_icon, lamps_needed_for_dark_rooms)
def set_default_triforce(goal, custom_goal, custom_total):
triforce_goal, triforce_total = 0, 0
if goal == 'triforcehunt':
triforce_goal, triforce_total = 20, 30
elif goal == 'trinity':
triforce_goal, triforce_total = 8, 10
if custom_goal > 0:
triforce_goal = max(min(custom_goal, 128), 1)
if custom_total > 0 or custom_goal > 0:
triforce_total = max(min(custom_total, 128), triforce_goal) #128 max to ensure other progression can fit.
return (triforce_goal, triforce_total)
# A quick test to ensure all combinations generate the correct amount of items.
def test():
for difficulty in ['normal', 'hard', 'expert']:
for goal in ['ganon', 'triforcehunt', 'pedestal']:
for goal in ['ganon', 'triforcehunt', 'pedestal', 'trinity']:
for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
for mode in ['open', 'standard', 'inverted', 'retro']:
for swords in ['random', 'assured', 'swordless', 'vanilla']:
@@ -1022,7 +1026,7 @@ def test():
count = len(out[0]) + len(out[1])
correct_count = total_items_to_place
if goal == 'pedestal' and swords != 'vanilla':
if goal in ['pedestal', 'trinity'] and swords != 'vanilla':
# pedestal goals generate one extra item
correct_count += 1
if retro:

View File

@@ -1970,7 +1970,7 @@ def val_mire(key_logic, world, player):
def val_turtle(key_logic, world, player):
# todo: check vanilla key logic when TR back doors are accessible
if world.shuffle[player] == 'vanilla' and (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owMixed[player]) and world.logic[player] in ('noglitches', 'minorglitches'):
if world.shuffle[player] == 'vanilla' and (not world.is_tile_swapped(0x05, player)) and world.logic[player] in ('noglitches', 'minorglitches'):
val_rule(key_logic.door_rules['TR Hub NW'], 1)
val_rule(key_logic.door_rules['TR Pokey 1 NW'], 2)
val_rule(key_logic.door_rules['TR Chain Chomps Down Stairs'], 3)

View File

@@ -179,9 +179,10 @@ def roll_settings(weights):
'fast_ganon': 'crystals',
'dungeons': 'dungeons',
'pedestal': 'pedestal',
'triforce-hunt': 'triforcehunt'
'triforce-hunt': 'triforcehunt',
'trinity': 'trinity'
}[goal]
ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False
ret.openpyramid = goal in ['fast_ganon', 'trinity'] if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False
ret.shuffleganon = get_choice('shuffleganon') == 'on'

View File

@@ -1,10 +1,11 @@
import RaceRandom as random, logging, copy
from collections import OrderedDict
from DungeonGenerator import GenerationException
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance
from Regions import mark_dark_world_regions, mark_light_world_regions
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel
__version__ = '0.2.3.5-u'
__version__ = '0.2.3.6-u'
def link_overworld(world, player):
# setup mandatory connections
@@ -159,7 +160,7 @@ def link_overworld(world, player):
# apply tile logical connections
for owid in ow_connections.keys():
if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(owid, player):
for (exitname, regionname) in ow_connections[owid][0]:
connect_simple(world, exitname, regionname, player)
else:
@@ -342,7 +343,7 @@ def link_overworld(world, player):
for o in range(0, len(flute_destinations)):
owslot = flute_destinations[o]
regions = flute_data[owslot][0]
if (world.mode[player] == 'inverted') == (flute_data[owslot][1] in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(flute_data[owslot][1], player):
connect_simple(world, 'Flute Spot ' + str(o + 1), regions[0], player)
else:
connect_simple(world, 'Flute Spot ' + str(o + 1), regions[1], player)
@@ -364,7 +365,7 @@ def link_overworld(world, player):
new_ignored.add(exit.connected_region.name)
getIgnored(exit.connected_region.name, base_owid, OWTileRegions[exit.connected_region.name])
if (world.mode[player] == 'inverted') == (flute_data[owid][1] in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(flute_data[owid][1], player):
new_region = flute_data[owid][0][0]
else:
new_region = flute_data[owid][0][1]

View File

@@ -136,6 +136,12 @@ New flute spots are chosen at random, with restrictions that limit the promixity
New flute spots are chosen at random with minimum bias.
## New Goal Options (--goal)
### Trinity
This goal gives you the choice between 3 goals, only one of which the player needs to complete: Defeat Ganon (no Aga2), Pulling Pedestal, or turn in TF pieces to Murahdulah. By default, you need to find 8 of 10 total TF pieces but this can be changed with a Custom Item Pool. It is recommended to set GT Entry to 7 crystals and Ganon to 5 crystals or Random crystals, although the player can flexibly change these settings as they seem fit.
## New Entrance Shuffle Options (--shuffle)
### Lite

42
Rom.py
View File

@@ -624,7 +624,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
offset = 0
data = flute_data[owslot]
if (world.mode[player] == 'inverted') != (data[1] in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(data[1], player):
offset = 0x40
write_int16(rom, snes_to_pc(0x02E849 + (o * 2)), data[1] + offset) # owid
@@ -755,7 +755,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
dr_flags = DROptions.Eternal_Mini_Bosses if world.doorShuffle[player] == 'vanilla' else DROptions.Town_Portal
if world.doorShuffle[player] == 'crossed':
dr_flags |= DROptions.Map_Info
if world.experimental[player] and world.goal[player] != 'triforcehunt':
if world.experimental[player] and world.goal[player] not in ['triforcehunt', 'trinity']:
dr_flags |= DROptions.Debug
if world.doorShuffle[player] == 'crossed' and world.logic[player] != 'nologic'\
and world.mixed_travel[player] == 'prevent':
@@ -1235,7 +1235,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# set up goals for treasure hunt
rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon[player] == 'Triforce Piece' else [0x0D, 0x28])
if world.goal[player] == 'triforcehunt':
if world.goal[player] in ['triforcehunt', 'trinity']:
rom.write_byte(0x180167, int(world.treasure_hunt_count[player]) % 256)
rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled)
@@ -1261,7 +1261,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest
rom.write_byte(0x50599, 0x00) # disable below ganon chest
rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest
rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] else 0x00) # pre-open Pyramid Hole
rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] or world.goal[player] == 'trinity' else 0x00) # pre-open Pyramid Hole
rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0
rom.write_byte(0xF5D73, 0xF0) # bees are catchable
rom.write_byte(0xF5F10, 0xF0) # bees are catchable
@@ -1446,7 +1446,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x18003E, 0x01) # make ganon invincible
elif world.goal[player] in ['dungeons']:
rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat
elif world.goal[player] in ['crystals']:
elif world.goal[player] in ['crystals', 'trinity']:
rom.write_byte(0x18003E, 0x04) # make ganon invincible until all crystals
else:
rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected
@@ -2186,7 +2186,7 @@ def write_strings(rom, world, player, team):
elif world.shopsanity[player]:
entrances_to_hint.update(ShopEntrances)
if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
if world.mode[player] == 'inverted' != (0x2c in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x2c, player):
entrances_to_hint.update({'Links House': 'The hero\'s old residence'})
if world.shufflelinks[player]:
entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'})
@@ -2198,7 +2198,7 @@ def write_strings(rom, world, player, team):
if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']:
entrances_to_hint.update(InsanityEntrances)
if world.shuffle_ganon:
if world.mode[player] == 'inverted' != (0x1b in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x1b, player):
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
else:
entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'})
@@ -2374,6 +2374,10 @@ def write_strings(rom, world, player, team):
tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.'
tt['sign_ganon'] = 'You need to get to the pedestal... Ganon is invincible!'
else:
if world.goal[player] in ['trinity']:
trinity_crystal_text = ('%d crystal to beat Ganon.' if world.crystals_needed_for_ganon[player] == 1 else '%d crystals to beat Ganon.') % world.crystals_needed_for_ganon[player]
tt['sign_ganon'] = 'Three ways to victory! %s Get to it!' % trinity_crystal_text
tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % int(world.treasure_hunt_count[player])
tt['ganon_fall_in'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)]
tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!'
tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!'
@@ -2495,9 +2499,9 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
write_int16(rom, 0x15AEE + 2*0x38, 0x00E0)
write_int16(rom, 0x15AEE + 2*0x25, 0x000C)
if (world.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x03, player):
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] \
or (world.shuffle[player] == 'simple' and (world.mode[player] == 'inverted' != (0x05 in world.owswaps[player][0] and world.owMixed[player]))):
or (world.shuffle[player] == 'simple' and world.is_tile_swapped(0x05, player)):
rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) # mountain cave starts on OW
write_int16(rom, snes_to_pc(0x02D8DE), 0x00F1) # change mountain cave spawn point to just outside old man cave
@@ -2519,20 +2523,20 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
rom.write_byte(snes_to_pc(0x02D9B8), 0x12)
rom.write_bytes(0x180247, [0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00]) # indicates the overworld door being used for the single entrance spawn point
if (world.mode[player] == 'inverted') != (0x05 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x05, player):
rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) # add warp under rock
rom.write_byte(snes_to_pc(0x1BC428), 0x00) # remove secret portal
if (world.mode[player] == 'inverted') != (0x07 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x07, player):
rom.write_bytes(snes_to_pc(0x1BC387), [0xDD, 0xD1]) # add warps under rocks
rom.write_bytes(snes_to_pc(0x1BD1DD), [0xA4, 0x06, 0x82, 0x9E, 0x06, 0x82, 0xFF, 0xFF]) # add warps under rocks
rom.write_byte(0x180089, 0x01) # open TR after exit
rom.write_bytes(0x0086E, [0x5C, 0x00, 0xA0, 0xA1]) # TR tail
if world.shuffle[player] in ['vanilla']:
world.fix_trock_doors[player] = True
if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x10, player):
rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warp under rock
rom.write_byte(snes_to_pc(0x1BC43A), 0x00) # remove secret portal
if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x1b, player):
write_int16(rom, 0x15AEE + 2 * 0x06, 0x0020) # post aga hyrule castle spawn
rom.write_byte(0x15B8C + 0x06, 0x1B)
write_int16(rom, 0x15BDB + 2 * 0x06, 0x00AE)
@@ -2637,24 +2641,24 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
rom.write_byte(0x1607C + 0x37, 0x0A)
write_int16(rom, 0x160CB + 2 * 0x37, 0x0000)
write_int16(rom, 0x16169 + 2 * 0x37, 0x811C)
if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x29, player):
rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) # frog pickup on contact
if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x2c, player):
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
rom.write_byte(0x15B8C, 0x6C) # exit links at bomb shop area
rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house
rom.write_byte(0xDBB73 + 0x52, 0x01)
if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x2f, player):
rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) # add warp under rock
rom.write_byte(snes_to_pc(0x1BC590), 0x00) # remove secret portal
if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x30, player):
rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) # add warp under rock
rom.write_byte(snes_to_pc(0x1BC5A1), 0x00) # remove secret portal
if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x33, player):
rom.write_bytes(snes_to_pc(0x1BC3DF), [0xD8, 0xD1]) # add warp under rock
rom.write_bytes(snes_to_pc(0x1BD1D8), [0xA8, 0x02, 0x82, 0xFF, 0xFF]) # add warp under rock
rom.write_byte(snes_to_pc(0x1BC5B1), 0x00) # remove secret portal
if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owMixed[player]):
if world.is_tile_swapped(0x35, player):
rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock
rom.write_byte(snes_to_pc(0x1BC5C7), 0x00) # remove secret portal

View File

@@ -55,7 +55,7 @@ def set_rules(world, player):
elif world.goal[player] == 'ganon':
# require aga2 to beat ganon
add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player))
elif world.goal[player] == 'triforcehunt':
if world.goal[player] in ['triforcehunt', 'trinity']:
if ('Murahdahla', player) in world._location_cache:
add_rule(world.get_location('Murahdahla', player), lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) >= int(state.world.treasure_hunt_count[player]))
@@ -860,7 +860,7 @@ def ow_rules(world, player):
else:
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player))
if (world.mode[player] == 'inverted') == (0x00 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x00, player):
set_rule(world.get_entrance('Lost Woods East Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Lost Woods Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Lost Woods Pedestal Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -875,12 +875,12 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Skull Woods Forgotten (Middle) Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Skull Woods Front Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x02 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x02, player):
set_rule(world.get_entrance('Lumberjack Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dark Lumberjack Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x03, player):
set_rule(world.get_entrance('West Death Mountain (Top) Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Spectacle Rock Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
@@ -888,7 +888,7 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Bubble Boy Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('West Dark Death Mountain (Bottom) Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x05, player):
set_rule(world.get_entrance('East Death Mountain (Top West) Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('East Death Mountain (Top East) Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Mimic Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -910,7 +910,7 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Dark Floating Island Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Dark Death Mountain Teleporter (East)', player), lambda state: state.can_lift_heavy_rocks(player))
if (world.mode[player] == 'inverted') == (0x07 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x07, player):
set_rule(world.get_entrance('TR Pegs Area Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('TR Pegs Teleporter', player), lambda state: state.has('Hammer', player))
else:
@@ -918,7 +918,7 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Turtle Rock Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_heavy_rocks(player) and state.has_Pearl(player))
if (world.mode[player] == 'inverted') == (0x0a in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x0a, player):
set_rule(world.get_entrance('Mountain Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Mountain Entry Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Mountain Entry Entrance Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -927,12 +927,12 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Bumper Cave Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x0f in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x0f, player):
set_rule(world.get_entrance('Zora Waterfall Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Catfish Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x10 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x10, player):
set_rule(world.get_entrance('Lost Woods Pass West Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Lost Woods Pass East Top Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Lost Woods Pass Portal Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -944,24 +944,24 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Skull Woods Pass Portal Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Skull Woods Pass East Bottom Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x11 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x11, player):
set_rule(world.get_entrance('Kakariko Fortune Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Outcast Fortune Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x12 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x12, player):
set_rule(world.get_entrance('Kakariko Pond Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Outcast Pond Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x13 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x13, player):
set_rule(world.get_entrance('Sanctuary Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Bonk Rock Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dark Chapel Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Dark Chapel Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x14 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x14, player):
set_rule(world.get_entrance('Graveyard Ledge Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player))
set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player))
else:
@@ -971,28 +971,28 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Dark Graveyard Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Dark Graveyard Grave Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x15 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x15, player):
set_rule(world.get_entrance('River Bend Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('River Bend East Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Qirn Jump Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Qirn Jump East Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x16 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x16, player):
set_rule(world.get_entrance('Potion Shop Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Potion Shop Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dark Witch Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Dark Witch Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x17 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x17, player):
set_rule(world.get_entrance('Zora Approach Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Zora Approach Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Catfish Approach Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Catfish Approach Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x18 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x18, player):
set_rule(world.get_entrance('Kakariko Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Kakariko Grass Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
@@ -1000,16 +1000,16 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Village of Outcasts Southwest Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Hammer House Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x1a in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x1a, player):
set_rule(world.get_entrance('Forgotton Forest Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Forgotton Forest Fence Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Shield Shop Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x1b in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x1b, player):
set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: False)
set_rule(world.get_entrance('Inverted Pyramid Entrance', player), lambda state: False)
set_rule(world.get_entrance('Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player) or world.open_pyramid[player])
set_rule(world.get_entrance('Pyramid Hole', player), lambda state: world.open_pyramid[player] or world.goal[player] == 'trinity' or state.has('Beat Agahnim 2', player))
set_rule(world.get_entrance('HC Area Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('HC Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -1020,8 +1020,8 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Top of Pyramid', player), lambda state: state.has('Beat Agahnim 1', player))
set_rule(world.get_entrance('Top of Pyramid (Inner)', player), lambda state: state.has('Beat Agahnim 1', player))
else:
set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player) or world.open_pyramid[player])
add_rule(world.get_entrance('Pyramid Hole', player), lambda state: False)
set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: world.open_pyramid[player] or world.goal[player] == 'trinity' or state.has('Beat Agahnim 2', player))
set_rule(world.get_entrance('Pyramid Hole', player), lambda state: False)
set_rule(world.get_entrance('Pyramid Entrance', player), lambda state: False)
set_rule(world.get_entrance('Pyramid Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -1032,7 +1032,7 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Pyramid Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Post Aga Inverted Teleporter', player), lambda state: state.has('Beat Agahnim 1', player))
if (world.mode[player] == 'inverted') == (0x1d in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x1d, player):
set_rule(world.get_entrance('Wooden Bridge Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Wooden Bridge Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Wooden Bridge West Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -1041,12 +1041,12 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Broken Bridge East Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Broken Bridge Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x1e in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x1e, player):
set_rule(world.get_entrance('Eastern Palace Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Palace of Darkness Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x22 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x22, player):
set_rule(world.get_entrance('Blacksmith Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Blacksmith Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Bat Cave Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -1054,19 +1054,19 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Hammer Pegs Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Hammer Pegs Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x25 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x25, player):
set_rule(world.get_entrance('Sand Dunes Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dark Dunes Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x28 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x28, player):
set_rule(world.get_entrance('Maze Race Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Maze Race Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dig Game Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Dig Game Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x29 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x29, player):
set_rule(world.get_entrance('Kakariko Suburb Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Kakariko Suburb South Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
@@ -1074,24 +1074,24 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Frog Prison Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Archery Game Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x2a in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x2a, player):
set_rule(world.get_entrance('Flute Boy Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Flute Boy Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Stumpy Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Stumpy Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x2b in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x2b, player):
set_rule(world.get_entrance('Central Bonk Rocks Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dark Bonk Rocks Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x2c in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x2c, player):
set_rule(world.get_entrance('Links House Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Big Bomb Shop Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x2d in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x2d, player):
set_rule(world.get_entrance('Stone Bridge Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Stone Bridge South Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Hobo Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player))
@@ -1100,19 +1100,19 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Hammer Bridge South Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Dark Hobo Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player))
if (world.mode[player] == 'inverted') == (0x2e in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x2e, player):
set_rule(world.get_entrance('Tree Line Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dark Tree Line Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x2f in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x2f, player):
set_rule(world.get_entrance('Eastern Nook Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer
else:
set_rule(world.get_entrance('Darkness Nook Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player))
if (world.mode[player] == 'inverted') == (0x30 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x30, player):
set_rule(world.get_entrance('Checkerboard Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Desert Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Desert Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -1127,14 +1127,14 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Misery Mire Main Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Misery Mire Teleporter', player), lambda state: state.can_lift_heavy_rocks(player))
if (world.mode[player] == 'inverted') == (0x32 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x32, player):
set_rule(world.get_entrance('Cave 45 Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Flute Boy Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Stumpy Approach Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Stumpy Bush Entry Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x33 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x33, player):
set_rule(world.get_entrance('C Whirlpool Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('C Whirlpool Outer Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer
@@ -1145,12 +1145,12 @@ def ow_rules(world, player):
set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player))
set_rule(world.get_entrance('South Teleporter Cliff Ledge Drop', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player))
if (world.mode[player] == 'inverted') == (0x34 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x34, player):
set_rule(world.get_entrance('Statues Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Hype Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x35 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x35, player):
set_rule(world.get_entrance('Lake Hylia Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Lake Hylia Northeast Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Lake Hylia Island Mirror Spot', player), lambda state: state.has_Mirror(player) and state.has_Pearl(player) and state.has('Flippers', player))
@@ -1167,12 +1167,12 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Ice Palace Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Ice Palace Teleporter', player), lambda state: state.can_lift_heavy_rocks(player))
if (world.mode[player] == 'inverted') == (0x37 in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x37, player):
set_rule(world.get_entrance('Ice Cave Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Shopping Mall Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x3a in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x3a, player):
set_rule(world.get_entrance('Desert Pass Ledge Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Desert Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
@@ -1180,17 +1180,17 @@ def ow_rules(world, player):
set_rule(world.get_entrance('Swamp Nook Southeast Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Swamp Nook Pegs Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x3b in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x3b, player):
set_rule(world.get_entrance('Dam Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Swamp Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x3c in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x3c, player):
set_rule(world.get_entrance('South Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Dark South Pass Mirror Spot', player), lambda state: state.has_Mirror(player))
if (world.mode[player] == 'inverted') == (0x3f in world.owswaps[player][0] and world.owMixed[player]):
if not world.is_tile_swapped(0x3f, player):
set_rule(world.get_entrance('Octoballoon Mirror Spot', player), lambda state: state.has_Mirror(player))
else:
set_rule(world.get_entrance('Bomber Corner Mirror Spot', player), lambda state: state.has_Mirror(player))

View File

@@ -73,6 +73,7 @@
dungeons: 1
pedestal: 2
triforce-hunt: 2
trinity: 1
triforce_goal_min: 10
triforce_goal_max: 30
triforce_pool_min: 20

View File

@@ -103,7 +103,9 @@
"All Dungeons: Collect all crystals, pendants, beat both",
" Agahnim fights and then defeat Ganon.",
"Triforce Hunt: Places 30 Triforce Pieces in the world, collect",
" 20 of them to beat the game."
" 20 of them to beat the game.",
"Trinity: Can beat the game by defeating Ganon, pulling",
" Pedestal, or delivering Triforce Pieces."
],
"difficulty": [
"Select game difficulty. Affects available itempool. (default: %(default)s)",

View File

@@ -238,6 +238,7 @@
"randomizer.item.goal.pedestal": "Master Sword Pedestal",
"randomizer.item.goal.dungeons": "All Dungeons",
"randomizer.item.goal.triforcehunt": "Triforce Hunt",
"randomizer.item.goal.trinity": "Trinity",
"randomizer.item.goal.crystals": "Crystals",
"randomizer.item.crystals_gt": "Crystals to open GT",

View File

@@ -35,6 +35,7 @@
"pedestal",
"dungeons",
"triforcehunt",
"trinity",
"crystals"
]
},