Moved Crossed OW Shuffle to its own setting, Renamed Tile Swap to Mixed

This commit is contained in:
codemann8
2021-08-16 22:46:26 -05:00
parent 7b81c5d9da
commit be6203e3f0
20 changed files with 645 additions and 345 deletions

View File

@@ -19,13 +19,14 @@ from RoomData import Room
class World(object):
def __init__(self, players, owShuffle, owSwap, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
def __init__(self, players, owShuffle, owCrossed, owMixed, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
self.players = players
self.teams = 1
self.owShuffle = owShuffle.copy()
self.owSwap = owSwap.copy()
self.owCrossed = owCrossed.copy()
self.owKeepSimilar = {}
self.owMixed = owMixed.copy()
self.owFluteShuffle = {}
self.shuffle = shuffle.copy()
self.doorShuffle = doorShuffle.copy()
@@ -2308,8 +2309,9 @@ class Spoiler(object):
'weapons': self.world.swords,
'goal': self.world.goal,
'ow_shuffle': self.world.owShuffle,
'ow_swap': self.world.owSwap,
'ow_crossed': self.world.owCrossed,
'ow_keepsimilar': self.world.owKeepSimilar,
'ow_mixed': self.world.owMixed,
'ow_fluteshuffle': self.world.owFluteShuffle,
'shuffle': self.world.shuffle,
'door_shuffle': self.world.doorShuffle,
@@ -2390,9 +2392,10 @@ class Spoiler(object):
outfile.write('Difficulty:'.ljust(line_width) + '%s\n' % self.metadata['item_pool'][player])
outfile.write('Item Functionality:'.ljust(line_width) + '%s\n' % self.metadata['item_functionality'][player])
outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player])
outfile.write('Overworld Tile Swap:'.ljust(line_width) + '%s\n' % self.metadata['ow_swap'][player])
if self.metadata['ow_shuffle'][player] != 'vanilla':
outfile.write('Crossed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_crossed'][player] else 'No'))
outfile.write('Keep Similar OW Edges Together:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_keepsimilar'][player] else 'No'))
outfile.write('Mixed OW:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_mixed'][player] else 'No'))
outfile.write('Flute Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_fluteshuffle'][player])
outfile.write('Entrance Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['shuffle'][player])
outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
@@ -2555,8 +2558,7 @@ class Pot(object):
# byte 0: DDOO OEEE (DR, OR, ER)
dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0}
or_mode = {"vanilla": 0, "parallel": 1, "full": 2}
orswap_mode = {"vanilla": 0, "mixed": 1, "crossed": 1}
or_mode = {"vanilla": 0, "parallel": 1, "full": 1}
er_mode = {"vanilla": 0, "simple": 1, "restricted": 3, "full": 3, "crossed": 4, "insanity": 5, "dungeonsfull": 7, "dungeonssimple": 7}
# byte 1: LLLW WSSR (logic, mode, sword, retro)
@@ -2592,7 +2594,7 @@ class Settings(object):
@staticmethod
def make_code(w, p):
code = bytes([
(dr_mode[w.doorShuffle[p]] << 6) | (or_mode[w.owShuffle[p]] << 4) | (orswap_mode[w.owSwap[p]] << 3) | er_mode[w.shuffle[p]],
(dr_mode[w.doorShuffle[p]] << 6) | (or_mode[w.owShuffle[p]] << 5) | (0x10 if w.owCrossed[p] else 0) | (0x08 if w.owMixed[p] else 0) | er_mode[w.shuffle[p]],
(logic_mode[w.logic[p]] << 5) | (world_mode[w.mode[p]] << 3)
| (sword_mode[w.swords[p]] << 1) | (1 if w.retro[p] else 0),

5
CLI.py
View File

@@ -94,7 +94,7 @@ def parse_cli(argv, no_defaults=False):
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'ow_shuffle', 'ow_swap', 'ow_keepsimilar', 'ow_fluteshuffle',
'ow_shuffle', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_fluteshuffle',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'bombbag',
@@ -146,8 +146,9 @@ def parse_settings():
"openpyramid": False,
"shuffleganon": True,
"ow_shuffle": "vanilla",
"ow_swap": "vanilla",
"ow_crossed": False,
"ow_keepsimilar": False,
"ow_mixed": False,
"ow_fluteshuffle": "vanilla",
"shuffle": "vanilla",
"shufflelinks": False,

View File

@@ -214,7 +214,7 @@ def vanilla_key_logic(world, player):
analyze_dungeon(key_layout, world, player)
world.key_logic[player][builder.name] = key_layout.key_logic
log_key_logic(builder.name, key_layout.key_logic)
if world.shuffle[player] == 'vanilla' and world.owShuffle[player] == 'vanilla' and world.owSwap[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]:
if world.shuffle[player] == 'vanilla' and world.owShuffle[player] == 'vanilla' and not world.owCrossed[player] and world.owMixed[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]:
validate_vanilla_key_logic(world, player)

View File

@@ -50,18 +50,18 @@ def link_entrances(world, player):
# inverted entrance mods
for owid in swapped_connections.keys():
if invFlag != (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if invFlag != (owid in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed') and \
invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if invFlag != (0x03 in world.owswaps[player][0] and world.owMixed[player]) and \
invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed') and \
invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
elif invFlag != (0x0a in world.owswaps[player][0] and world.owMixed[player]) and \
invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
connect_two_way(world, 'Bumper Cave (Top)', 'Death Mountain Return Cave Exit (West)', player)
# dungeon entrance shuffle
@@ -225,7 +225,7 @@ def link_entrances(world, player):
random.shuffle(remaining_entrances)
old_man_entrance = remaining_entrances.pop()
connect_two_way(world, old_man_entrance if invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_entrance if invFlag == (0x0a in world.owswaps[player][0] and world.owMixed[player]) else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
if invFlag and old_man_exit == 'Spike Cave':
bomb_shop_doors.remove('Spike Cave')
@@ -791,8 +791,8 @@ 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.owSwap[player] == 'mixed') else 'Inverted Pyramid Entrance', 'Pyramid Exit', player)
connect_entrance(world, 'Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', 'Pyramid', 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)
else:
caves.extend(['Ganons Tower Exit', 'Pyramid Exit'])
hole_targets.append('Pyramid')
@@ -804,7 +804,7 @@ def link_entrances(world, player):
exit_pool.extend(['Agahnims Tower'])
doors.extend(['Agahnims Tower'])
if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if invFlag == (0x1b in world.owswaps[player][0] and world.owMixed[player]):
exit_pool.extend(['Pyramid Entrance'])
hole_entrances.append('Pyramid Hole')
entrances_must_exits.append('Pyramid Entrance')
@@ -943,7 +943,7 @@ def link_entrances(world, player):
world.powder_patch_required[player] = True
# check for ganon location
if world.get_entrance('Pyramid Hole' if invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid':
if world.get_entrance('Pyramid Hole' if invFlag == (0x03 in world.owswaps[player][0] and world.owMixed[player]) else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid':
world.ganon_at_pyramid[player] = False
# check for Ganon's Tower location
@@ -1030,7 +1030,7 @@ def scramble_holes(world, player):
('Lumberjack Tree Exit', 'Lumberjack Tree (top)')]
if not world.shuffle_ganon:
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
else:
@@ -1053,7 +1053,7 @@ def scramble_holes(world, player):
if world.shuffle_ganon:
random.shuffle(hole_targets)
exit, target = hole_targets.pop()
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[player]):
connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player)
connect_entrance(world, 'Pyramid Hole', 'Pyramid', player)
else:

View File

@@ -258,7 +258,7 @@ def generate_itempool(world, player):
(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)
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.owShuffle[player], world.owSwap[player], 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])
(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])
if player in world.pool_adjustment.keys():
amt = world.pool_adjustment[player]
@@ -727,7 +727,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)'
'Rupees (100)': 100, 'Rupees (300)': 300}
def get_pool_core(progressive, owShuffle, owSwap, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic):
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic):
pool = []
placed_items = {}
precollected_items = []
@@ -990,9 +990,8 @@ def test():
for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']:
for retro in [True, False]:
for door_shuffle in ['basic', 'crossed', 'vanilla']:
for owShuffle in ['full', 'parallel', 'vanilla']:
for owSwap in ['vanilla', 'mixed', 'crossed']:
out = get_pool_core(progressive, owShuffle, owSwap, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic)
for bombbag in [True, False]:
out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic)
count = len(out[0]) + len(out[1])
correct_count = total_items_to_place

View File

@@ -61,7 +61,7 @@ def main(args, seed=None, fish=None):
for player, code in args.code.items():
if code:
Settings.adjust_args_from_code(code, player, args)
world = World(args.multi, args.ow_shuffle, args.ow_swap, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
world = World(args.multi, args.ow_shuffle, args.ow_crossed, args.ow_mixed, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
logger = logging.getLogger('')
@@ -263,7 +263,7 @@ def main(args, seed=None, fish=None):
customize_shops(world, player)
balance_money_progression(world)
if world.owShuffle[1] != 'vanilla' or world.owSwap[1] != 'vanilla' or str(world.seed).startswith('M'):
if world.owShuffle[1] != 'vanilla' or world.owCrossed[1] or world.owMixed[1] or str(world.seed).startswith('M'):
outfilebase = f'OR_{args.outputname if args.outputname else world.seed}'
else:
outfilebase = f'DR_{args.outputname if args.outputname else world.seed}'
@@ -369,7 +369,7 @@ def main(args, seed=None, fish=None):
def copy_world(world):
# ToDo: Not good yet
ret = World(world.players, world.owShuffle, world.owSwap, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
ret = World(world.players, world.owShuffle, world.owCrossed, world.owMixed, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm,
world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
ret.teams = world.teams

View File

@@ -133,9 +133,9 @@ def roll_settings(weights):
overworld_shuffle = get_choice('overworld_shuffle')
ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla'
overworld_swap = get_choice('overworld_swap')
ret.ow_swap = overworld_swap if overworld_swap != 'none' else 'vanilla'
ret.ow_crossed = get_choice('overworld_crossed')
ret.ow_keepsimilar = get_choice('overworld_keepsimilar')
ret.ow_mixed = get_choice('overworld_mixed')
overworld_flute = get_choice('flute_shuffle')
ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla'
entrance_shuffle = get_choice('entrance_shuffle')

View File

@@ -11,9 +11,25 @@ def link_overworld(world, player):
for exitname, destname in temporary_mandatory_connections:
connect_two_way(world, exitname, destname, player)
# tile shuffle
trimmed_groups = copy.deepcopy(OWEdgeGroups)
if world.owSwap[player] != 'vanilla':
# adjust Frog/Dig Game swap manually due to NP/P relationship with LW
if world.owShuffle[player] == 'parallel' and not world.owKeepSimilar[player]:
for group in trimmed_groups.keys():
(std, region, axis, terrain, _, _) = group
(forward_edges, back_edges) = trimmed_groups[group]
if ['Dig Game EC', 'Dig Game ES'] in forward_edges:
forward_edges = list(filter((['Dig Game EC', 'Dig Game ES']).__ne__, forward_edges))
trimmed_groups[(std, region, axis, terrain, IsParallel.Yes, 1)][0].append(['Dig Game ES'])
trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][0].append(['Dig Game EC'])
if ['Frog WC', 'Frog WS'] in back_edges:
back_edges = list(filter((['Frog WC', 'Frog WS']).__ne__, back_edges))
trimmed_groups[(std, region, axis, terrain, IsParallel.Yes, 1)][1].append(['Frog WS'])
trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][1].append(['Frog WC'])
trimmed_groups[group] = (forward_edges, back_edges)
# tile shuffle
if world.owMixed[player]:
tile_groups = {}
for (name, groupType) in OWTileGroups.keys():
if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks']:
@@ -44,7 +60,7 @@ def link_overworld(world, player):
exist_dw_regions.extend(OWTileRegions.inverse[owid])
tile_groups[(name, groupType)] = (exist_owids, exist_lw_regions, exist_dw_regions)
#tile shuffle happens here, the groups that remain in the list are the tiles that get swapped
# tile shuffle happens here, the groups that remain in the list are the tiles that get swapped
removed = list()
for group in tile_groups.keys():
if random.randint(0, 1):
@@ -52,7 +68,7 @@ def link_overworld(world, player):
for group in removed:
tile_groups.pop(group, None)
#save shuffled tiles to world object
# save shuffled tiles to world object
for group in tile_groups.keys():
(owids, lw_regions, dw_regions) = tile_groups[group]
(exist_owids, exist_lw_regions, exist_dw_regions) = world.owswaps[player]
@@ -61,7 +77,7 @@ def link_overworld(world, player):
exist_dw_regions.extend(dw_regions)
world.owswaps[player] = [exist_owids, exist_lw_regions, exist_dw_regions]
#replace LW edges with DW
# replace LW edges with DW
ignore_list = list() #TODO: Remove ignore_list when special OW areas are included in pool
for edgeset in temporary_mandatory_connections:
for edge in edgeset:
@@ -108,7 +124,7 @@ def link_overworld(world, player):
#TODO: Figure out a way to handle index changes on the fly when removing items
logging.getLogger('').warning('OW Tile Swap encountered minor IndexError... retrying')
if 0x28 in world.owswaps[player][0]: #handle Frog/Dig Game swap manually due to NP/P relationship with LW
if world.owShuffle[player] != 'parallel' and 0x28 in world.owswaps[player][0]: # handle Frog/Dig Game swap manually due to NP/P relationship with LW
trimmed_groups[(OpenStd.Open, WorldType.Dark, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][0].append(['Maze Race ES'])
trimmed_groups[(OpenStd.Open, WorldType.Dark, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][1].append(['Kakariko Suburb WS'])
trimmed_groups[(OpenStd.Open, WorldType.Light, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][0].remove(['Maze Race ES'])
@@ -139,7 +155,7 @@ def link_overworld(world, player):
# make new connections
for owid in ow_connections.keys():
if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owMixed[player]):
for (exitname, regionname) in ow_connections[owid][0]:
connect_simple(world, exitname, regionname, player)
else:
@@ -151,57 +167,90 @@ def link_overworld(world, player):
connect_custom(world, connected_edges, player)
# layout shuffle
if world.owShuffle[player] == 'vanilla':
if world.owShuffle[player] == 'vanilla' and not world.owCrossed[player]:
# vanilla transitions
groups = list(trimmed_groups.values())
for (forward_edge_sets, back_edge_sets) in groups:
assert len(forward_edge_sets) == len(back_edge_sets)
for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets):
assert len(forward_set) == len(back_set)
for (forward_edge, back_edge) in zip(forward_set, back_set):
connect_two_way(world, forward_edge, back_edge, player)
connected_edges.append(forward_edge)
connected_edges.append(back_edge)
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
assert len(connected_edges) == len(default_connections) * 2, connected_edges
else:
if world.owKeepSimilar[player] and world.owShuffle[player] == 'parallel':
for exitname, destname in parallelsimilar_connections:
connect_two_way(world, exitname, destname, player)
connected_edges.append(exitname)
connected_edges.append(destname)
connect_two_way(world, exitname, destname, player, connected_edges)
if world.owShuffle[player] == 'vanilla' and world.owCrossed[player]:
if world.mode[player] == 'standard':
# connect vanilla std
for group in trimmed_groups.keys():
(std, _, _, _, _, _) = group
if std == OpenStd.Standard:
(forward_set, back_set) = trimmed_groups[group]
for (forward_edges, back_edges) in zip(forward_set, back_set):
for (forward_edge, back_edge) in zip(forward_edges, back_edges):
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
# connect non-parallel edges
for group in trimmed_groups.keys():
(_, _, _, _, parallel, _) = group
if parallel == IsParallel.No:
(forward_set, back_set) = trimmed_groups[group]
for (forward_edges, back_edges) in zip(forward_set, back_set):
for (forward_edge, back_edge) in zip(forward_edges, back_edges):
if forward_edge not in connected_edges and back_edge not in connected_edges:
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
#TODO: Remove, just for testing
for exitname, destname in test_connections:
connect_two_way(world, exitname, destname, player)
connected_edges.append(exitname)
connected_edges.append(destname)
connect_two_way(world, exitname, destname, player, connected_edges)
trimmed_groups = remove_reserved(world, trimmed_groups, connected_edges, player)
groups = reorganize_groups(world, trimmed_groups, player)
#all shuffling occurs here
# all layout shuffling occurs here
if world.owShuffle[player] != 'vanilla':
# layout shuffle
random.shuffle(groups)
for (forward_edge_sets, back_edge_sets) in groups:
assert len(forward_edge_sets) == len(back_edge_sets)
random.shuffle(forward_edge_sets)
random.shuffle(back_edge_sets)
for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets):
if len(forward_edge_sets) > 0:
f = 0
b = 0
while f < len(forward_edge_sets) and b < len(back_edge_sets):
forward_set = forward_edge_sets[f]
back_set = back_edge_sets[b]
while forward_set[0] in connected_edges:
f += 1
if f < len(forward_edge_sets):
forward_set = forward_edge_sets[f]
f += 1
while back_set[0] in connected_edges:
b += 1
if b < len(back_edge_sets):
back_set = back_edge_sets[b]
b += 1
assert len(forward_set) == len(back_set)
for (forward_edge, back_edge) in zip(forward_set, back_set):
connect_two_way(world, forward_edge, back_edge, player)
connected_edges.append(forward_edge)
connected_edges.append(back_edge)
if world.owShuffle[player] == 'parallel':
if forward_edge in parallel_links.keys() or forward_edge in parallel_links.inverse.keys():
try:
parallel_forward_edge = parallel_links[forward_edge] if forward_edge in parallel_links.keys() else parallel_links.inverse[forward_edge][0]
parallel_back_edge = parallel_links[back_edge] if back_edge in parallel_links.keys() else parallel_links.inverse[back_edge][0]
connect_two_way(world, parallel_forward_edge, parallel_back_edge, player)
connected_edges.append(parallel_forward_edge)
connected_edges.append(parallel_back_edge)
except KeyError:
# TODO: Figure out why non-parallel edges are getting into parallel groups
raise KeyError('No parallel edge for edge %d' % back_edge)
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
else:
# vanilla/crossed shuffle
for (forward_edge_sets, back_edge_sets) in groups:
assert len(forward_edge_sets) == len(back_edge_sets)
for (forward_set, back_set) in zip(forward_edge_sets, back_edge_sets):
assert len(forward_set) == len(back_set)
swapped = random.randint(0, 1)
for (forward_edge, back_edge) in zip(forward_set, back_set):
if forward_edge not in connected_edges and back_edge not in connected_edges:
if swapped:
forward_edge = parallel_links[forward_edge] if forward_edge in parallel_links else parallel_links.inverse[forward_edge][0]
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
assert len(connected_edges) == len(default_connections) * 2, connected_edges
@@ -210,7 +259,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (flute_data[owslot][1] in world.owswaps[player][0] and world.owMixed[player]):
connect_simple(world, 'Flute Spot ' + str(o + 1), regions[0], player)
else:
connect_simple(world, 'Flute Spot ' + str(o + 1), regions[1], player)
@@ -232,7 +281,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (flute_data[owid][1] in world.owswaps[player][0] and world.owMixed[player]):
new_region = flute_data[owid][0][0]
else:
new_region = flute_data[owid][0][1]
@@ -269,18 +318,15 @@ def link_overworld(world, player):
world.owflutespots[player] = new_spots
connect_flutes(new_spots)
def connect_custom(world, connected_edges, player):
if hasattr(world, 'custom_overworld') and world.custom_overworld[player]:
for edgename1, edgename2 in world.custom_overworld[player]:
connect_two_way(world, edgename1, edgename2, player)
connected_edges.append(edgename1)
connected_edges.append(edgename2)
connect_two_way(world, edgename1, edgename2, player, connected_edges)
def connect_simple(world, exitname, regionname, player):
world.get_entrance(exitname, player).connect(world.get_region(regionname, player))
def connect_two_way(world, edgename1, edgename2, player):
def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
edge1 = world.get_entrance(edgename1, player)
edge2 = world.get_entrance(edgename2, player)
@@ -302,23 +348,41 @@ def connect_two_way(world, edgename1, edgename2, player):
x.dest = y
y.dest = x
if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla':
if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player]:
world.spoiler.set_overworld(edgename2, edgename1, 'both', player)
if connected_edges is not None:
connected_edges.append(edgename1)
connected_edges.append(edgename2)
# connecting parallel connections
if world.owShuffle[player] == 'parallel' or (world.owShuffle[player] == 'vanilla' and world.owCrossed[player]):
if (edgename1 in parallel_links.keys() or edgename1 in parallel_links.inverse.keys()):
try:
parallel_forward_edge = parallel_links[edgename1] if edgename1 in parallel_links.keys() else parallel_links.inverse[edgename1][0]
parallel_back_edge = parallel_links[edgename2] if edgename2 in parallel_links.keys() else parallel_links.inverse[edgename2][0]
if not (parallel_forward_edge in connected_edges) and not (parallel_back_edge in connected_edges):
connect_two_way(world, parallel_forward_edge, parallel_back_edge, player, connected_edges)
except KeyError:
# TODO: Figure out why non-parallel edges are getting into parallel groups
raise KeyError('No parallel edge for edge %s' % edgename2)
def remove_reserved(world, groupedlist, connected_edges, player):
new_grouping = {}
for group in groupedlist.keys():
new_grouping[group] = ([], [])
for group in groupedlist.keys():
(std, region, axis, terrain, parallel, count) = group
(_, region, _, _, _, _) = group
(forward_edges, back_edges) = groupedlist[group]
# remove edges already connected (thru plando and other forced connections)
for edge in connected_edges:
forward_edges = list(list(filter((edge).__ne__, i)) for i in forward_edges)
back_edges = list(list(filter((edge).__ne__, i)) for i in back_edges)
if world.owShuffle[player] == 'parallel' and region == WorldType.Dark:
# remove parallel edges from pool, since they get added during shuffle
if (not world.owCrossed[player] and world.owShuffle[player] == 'parallel') and region == WorldType.Dark:
for edge in parallel_links:
forward_edges = list(list(filter((parallel_links[edge]).__ne__, i)) for i in forward_edges)
back_edges = list(list(filter((parallel_links[edge]).__ne__, i)) for i in back_edges)
@@ -329,8 +393,6 @@ def remove_reserved(world, groupedlist, connected_edges, player):
forward_edges = list(filter(([]).__ne__, forward_edges))
back_edges = list(filter(([]).__ne__, back_edges))
#TODO: Remove edges set in connect_custom. The lists above can be left with invalid counts if connect_custom removes entries, they need to get put into their appropriate group
(exist_forward_edges, exist_back_edges) = new_grouping[group]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
@@ -340,11 +402,94 @@ def remove_reserved(world, groupedlist, connected_edges, player):
return new_grouping
def reorganize_groups(world, groups, player):
# predefined shuffle groups get reorganized here
# this restructures the candidate pool based on the chosen settings
if world.owShuffle[player] == 'full':
#predefined shuffle groups get reorganized here
if world.owCrossed[player]:
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
#tuple stays (A,B,C,D,_,F)
# tuple goes to (A,_,C,D,_,F)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(std, _, axis, terrain, _, count) = group
new_grouping[(std, axis, terrain, count)] = ([], [])
for group in grouping.keys():
(std, _, axis, terrain, _, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, count)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(std, axis, terrain, count)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,_,C,D,_,F)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, _, axis, terrain, _, count) = group
new_grouping[(axis, terrain, count)] = ([], [])
for group in grouping.keys():
(_, _, axis, terrain, _, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, count)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(axis, terrain, count)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
if world.mode[player] == 'standard':
# tuple goes to (A,_,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(std, _, axis, terrain, _, _) = group
new_grouping[(std, axis, terrain)] = ([], [])
for group in grouping.keys():
(std, _, axis, terrain, _, _) = group
(forward_edges, back_edges) = grouping[group]
forward_edges = [[i] for l in forward_edges for i in l]
back_edges = [[i] for l in back_edges for i in l]
(exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(std, axis, terrain)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,_,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, _, axis, terrain, _, _) = group
new_grouping[(axis, terrain)] = ([], [])
for group in grouping.keys():
(_, _, axis, terrain, _, _) = group
(forward_edges, back_edges) = grouping[group]
forward_edges = [[i] for l in forward_edges for i in l]
back_edges = [[i] for l in back_edges for i in l]
(exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(axis, terrain)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
# tuple goes to (A,B,C,D,_,F)
for grouping in (groups,):
new_grouping = {}
@@ -362,7 +507,7 @@ def reorganize_groups(world, groups, player):
return list(new_grouping.values())
else:
#tuple goes to (_,B,C,D,_,F)
# tuple goes to (_,B,C,D,_,F)
for grouping in (groups,):
new_grouping = {}
@@ -381,7 +526,7 @@ def reorganize_groups(world, groups, player):
return list(new_grouping.values())
else:
if world.mode[player] == 'standard':
#tuple stays (A,B,C,D,_,_)
# tuple goes to (A,B,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
@@ -402,7 +547,7 @@ def reorganize_groups(world, groups, player):
return list(new_grouping.values())
else:
#tuple goes to (_,B,C,D,_,_)
# tuple goes to (_,B,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
@@ -423,14 +568,95 @@ def reorganize_groups(world, groups, player):
return list(new_grouping.values())
elif world.owShuffle[player] == 'parallel':
#predefined shuffle groups get reorganized here
if world.owCrossed[player]:
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
#tuple stays (A,B,C,D,E,F)
# tuple goes to (A,_,C,D,E,F)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(std, _, axis, terrain, parallel, count) = group
new_grouping[(std, axis, terrain, parallel, count)] = ([], [])
for group in grouping.keys():
(std, _, axis, terrain, parallel, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, parallel, count)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(std, axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,_,C,D,E,F)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, _, axis, terrain, parallel, count) = group
new_grouping[(axis, terrain, parallel, count)] = ([], [])
for group in grouping.keys():
(_, _, axis, terrain, parallel, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, parallel, count)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
if world.mode[player] == 'standard':
# tuple goes to (A,_,C,D,E,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(std, _, axis, terrain, parallel, _) = group
new_grouping[(std, axis, terrain, parallel)] = ([], [])
for group in grouping.keys():
(std, _, axis, terrain, parallel, _) = group
(forward_edges, back_edges) = grouping[group]
forward_edges = [[i] for l in forward_edges for i in l]
back_edges = [[i] for l in back_edges for i in l]
(exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, parallel)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(std, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,_,C,D,E,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, _, axis, terrain, parallel, _) = group
new_grouping[(axis, terrain, parallel)] = ([], [])
for group in grouping.keys():
(_, _, axis, terrain, parallel, _) = group
(forward_edges, back_edges) = grouping[group]
forward_edges = [[i] for l in forward_edges for i in l]
back_edges = [[i] for l in back_edges for i in l]
(exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, parallel)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
# tuple stays (A,B,C,D,E,F)
for grouping in (groups,):
return list(grouping.values())
else:
#tuple goes to (_,B,C,D,E,F)
# tuple goes to (_,B,C,D,E,F)
for grouping in (groups,):
new_grouping = {}
@@ -449,7 +675,7 @@ def reorganize_groups(world, groups, player):
return list(new_grouping.values())
else:
if world.mode[player] == 'standard':
#tuple stays (A,B,C,D,E,_)
# tuple goes to (A,B,C,D,E,_)
for grouping in (groups,):
new_grouping = {}
@@ -470,7 +696,7 @@ def reorganize_groups(world, groups, player):
return list(new_grouping.values())
else:
#tuple goes to (_,B,C,D,E,_)
# tuple goes to (_,B,C,D,E,_)
for grouping in (groups,):
new_grouping = {}
@@ -489,14 +715,95 @@ def reorganize_groups(world, groups, player):
exist_back_edges.extend(back_edges)
new_grouping[(region, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
elif world.owShuffle[player] == 'vanilla' and world.owCrossed[player]:
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
# tuple goes to (A,B,C,D,_,F)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(std, region, axis, terrain, _, count) = group
new_grouping[(std, region, axis, terrain, count)] = ([], [])
for group in grouping.keys():
(std, region, axis, terrain, _, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain, count)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(std, region, axis, terrain, count)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,B,C,D,_,F)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, region, axis, terrain, _, count) = group
new_grouping[(region, axis, terrain, count)] = ([], [])
for group in grouping.keys():
(_, region, axis, terrain, _, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain, count)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(region, axis, terrain, count)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
if world.mode[player] == 'standard':
# tuple goes to (A,B,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(std, region, axis, terrain, _, _) = group
new_grouping[(std, region, axis, terrain)] = ([], [])
for group in grouping.keys():
(std, region, axis, terrain, _, _) = group
(forward_edges, back_edges) = grouping[group]
forward_edges = [[i] for l in forward_edges for i in l]
back_edges = [[i] for l in back_edges for i in l]
(exist_forward_edges, exist_back_edges) = new_grouping[(std, region, axis, terrain)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(std, region, axis, terrain)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,B,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, region, axis, terrain, _, _) = group
new_grouping[(region, axis, terrain)] = ([], [])
for group in grouping.keys():
(_, region, axis, terrain, _, _) = group
(forward_edges, back_edges) = grouping[group]
forward_edges = [[i] for l in forward_edges for i in l]
back_edges = [[i] for l in back_edges for i in l]
(exist_forward_edges, exist_back_edges) = new_grouping[(region, axis, terrain)]
exist_forward_edges.extend(forward_edges)
exist_back_edges.extend(back_edges)
new_grouping[(region, axis, terrain)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
raise NotImplementedError('Shuffling not supported yet')
def create_flute_exits(world, player):
for region in (r for r in world.regions if r.player == player and r.terrain == Terrain.Land and r.name not in ['Zoras Domain', 'Master Sword Meadow', 'Hobo Bridge']):
if (world.owSwap[player] != 'mixed' and region.type == RegionType.LightWorld) \
or (world.owSwap[player] == 'mixed' and region.type in [RegionType.LightWorld, RegionType.DarkWorld] \
if (not world.owMixed[player] and region.type == RegionType.LightWorld) \
or (world.owMixed[player] and region.type in [RegionType.LightWorld, RegionType.DarkWorld] \
and (region.name not in world.owswaps[player][1] or region.name in world.owswaps[player][2])):
exitname = 'Flute From ' + region.name
exit = Entrance(region.player, exitname, region)
@@ -506,7 +813,7 @@ def create_flute_exits(world, player):
world.initialize_regions()
def update_world_regions(world, player):
if world.owSwap[player] == 'mixed':
if world.owMixed[player]:
for name in world.owswaps[player][1]:
world.get_region(name, player).type = RegionType.DarkWorld
for name in world.owswaps[player][2]:
@@ -1110,8 +1417,7 @@ ow_connections = {
}
parallelsimilar_connections = [('Maze Race ES', 'Kakariko Suburb WS'),
('Dig Game EC', 'Frog WC'),
('Dig Game ES', 'Frog WS')
('Dig Game EC', 'Frog WC')
]
# non shuffled overworld

View File

@@ -66,19 +66,9 @@ OW Transitions are shuffled, but both worlds will have a matching layout.
OW Transitions are shuffled within each world separately.
## Overworld Tile Swap (--ow_swap)
## Crossed (--ow_crossed)
### Vanilla
OW tiles remain in their original worlds.
### Mixed
OW tiles are randomly chosen to become a part of the opposite world
### Crossed
OW tiles remain in their original world, but transitions can now be travel cross-world.
This allows OW connections to be shuffled cross-world.
## Visual Representation of Main OW Shuffle Settings
@@ -88,6 +78,10 @@ OW tiles remain in their original world, but transitions can now be travel cross
This keeps similar edge transitions together. ie. The 2 west edges of Potion Shop will be paired to another set of two similar edges
## Mixed Overworld (--ow_mixed)
OW tiles are randomly chosen to become a part of the opposite world
## Flute Shuffle (--ow_fluteshuffle)
When enabled, new flute spots are generated and gives the player the option to cancel out of the flute menu by pressing X.
@@ -120,10 +114,10 @@ Show the help message and exit.
For specifying the overworld layout shuffle you want as above. (default: vanilla)
```
--ow_swap <mode>
--ow_crossed
```
For specifying the overworld tile swap you want as above. (default: vanilla)
This allows cross-world connections on the overworld
```
--ow_keepsimilar
@@ -131,6 +125,12 @@ For specifying the overworld tile swap you want as above. (default: vanilla)
This keeps similar edge transitions paired together with other pairs of transitions
```
--ow_mixed
```
This gives each OW tile a random chance to be swapped to the opposite world
```
--ow_fluteshuffle <mode>
```

137
Rom.py
View File

@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'cc8fc59caa0bbe6d26ac64b9d2893709'
RANDOMIZERBASEHASH = '99f3f57ab2c9449172cade4927a462d6'
class JsonRom(object):
@@ -612,71 +612,20 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
if world.pot_contents[player]:
write_pots_to_rom(rom, world.pot_contents[player])
# patch overworld edges
inverted_buffer = [0] * 0x82
if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla':
owMode = 0
if world.owShuffle[player] == 'parallel':
owMode = 1
elif world.owShuffle[player] == 'full':
owMode = 2
if world.owSwap[player] == 'mixed':
owMode |= 0x100
world.fix_fake_world[player] = True
elif world.owSwap[player] == 'crossed':
owMode |= 0x200
world.fix_fake_world[player] = True
write_int16(rom, 0x150002, owMode)
owFlags = 0
if world.owKeepSimilar[player]:
owFlags |= 0x1
if world.owFluteShuffle[player] != 'vanilla':
owFlags |= 0x100
write_int16(rom, 0x150004, owFlags)
rom.write_byte(0x18004C, 0x01) # patch for allowing Frogsmith to enter multi-entrance caves
# patches map data specific for OW Shuffle
#inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM
inverted_buffer[0x1A] = inverted_buffer[0x1A] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x1B] = inverted_buffer[0x1B] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x22] = inverted_buffer[0x22] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x3F] = inverted_buffer[0x3F] | 0x2 # added C to terrain
#inverted_buffer[0x43] = inverted_buffer[0x43] | 0x2 # convenient portal on WDDM
inverted_buffer[0x5A] = inverted_buffer[0x5A] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x5B] = inverted_buffer[0x5B] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x62] = inverted_buffer[0x62] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x7F] = inverted_buffer[0x7F] | 0x2 # added C to terrain
if world.owSwap[player] == 'mixed':
for b in world.owswaps[player][0]:
# load inverted maps
inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2)
# set world flag
rom.write_byte(0x153A00 + b, 0x00 if b >= 0x40 else 0x40)
for edge in world.owedges:
if edge.dest is not None and isinstance(edge.dest, OWEdge) and edge.player == player:
write_int16(rom, edge.getAddress() + 0x0a, edge.vramLoc)
write_int16(rom, edge.getAddress() + 0x0e, edge.getTarget())
# patch flute spots
owFlags = 0
if world.owFluteShuffle[player] == 'vanilla':
flute_spots = default_flute_connections
else:
flute_spots = world.owflutespots[player]
owFlags |= 0x100
for o in range(0, len(flute_spots)):
owslot = flute_spots[o]
offset = 0
data = flute_data[owslot]
if (world.mode[player] == 'inverted') != (data[1] in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (data[1] in world.owswaps[player][0] and world.owMixed[player]):
offset = 0x40
write_int16(rom, snes_to_pc(0x02E849 + (o * 2)), data[1] + offset) # owid
@@ -696,6 +645,54 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(snes_to_pc(0x0AB793 + o), data[11] & 0xff) # Y low byte
rom.write_byte(snes_to_pc(0x0AB79B + o), data[11] // 0x100) # Y high byte
# patch overworld edges
inverted_buffer = [0] * 0x82
if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] or world.owMixed[player]:
owMode = 0
if world.owShuffle[player] == 'parallel':
owMode = 1
elif world.owShuffle[player] == 'full':
owMode = 2
if world.owKeepSimilar[player] and (world.owShuffle[player] != 'vanilla' or world.owCrossed[player]):
owMode |= 0x100
if world.owCrossed[player]:
owMode |= 0x200
world.fix_fake_world[player] = True
if world.owMixed[player]:
owMode |= 0x400
write_int16(rom, 0x150002, owMode)
write_int16(rom, 0x150004, owFlags)
rom.write_byte(0x18004C, 0x01) # patch for allowing Frogsmith to enter multi-entrance caves
# patches map data specific for OW Shuffle
#inverted_buffer[0x03] = inverted_buffer[0x03] | 0x2 # convenient portal on WDM
inverted_buffer[0x1A] = inverted_buffer[0x1A] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x1B] = inverted_buffer[0x1B] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x22] = inverted_buffer[0x22] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x3F] = inverted_buffer[0x3F] | 0x2 # added C to terrain
#inverted_buffer[0x43] = inverted_buffer[0x43] | 0x2 # convenient portal on WDDM
inverted_buffer[0x5A] = inverted_buffer[0x5A] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x5B] = inverted_buffer[0x5B] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x62] = inverted_buffer[0x62] | 0x2 # rocks added to prevent OWG hardlock
inverted_buffer[0x7F] = inverted_buffer[0x7F] | 0x2 # added C to terrain
if world.owMixed[player]:
for b in world.owswaps[player][0]:
# load inverted maps
inverted_buffer[b] = (inverted_buffer[b] & 0xFE) | ((inverted_buffer[b] + 1) % 2)
# set world flag
rom.write_byte(0x153A00 + b, 0x00 if b >= 0x40 else 0x40)
for edge in world.owedges:
if edge.dest is not None and isinstance(edge.dest, OWEdge) and edge.player == player:
write_int16(rom, edge.getAddress() + 0x0a, edge.vramLoc)
write_int16(rom, edge.getAddress() + 0x0e, edge.getTarget())
# patch entrance/exits/holes
for region in world.regions:
@@ -2177,7 +2174,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.owSwap[player] == 'mixed'):
if world.mode[player] == 'inverted' != (0x1b in world.owswaps[player][0] and world.owMixed[player]):
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
else:
entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'})
@@ -2247,7 +2244,7 @@ def write_strings(rom, world, player, team):
tt[hint_locations.pop(0)] = this_hint
# Adding a guaranteed hint for the Flute in overworld shuffle.
if world.owShuffle[player] in ['parallel','full']:
if world.owShuffle[player] != 'vanilla' or world.owMixed[player]:
this_location = world.find_items_not_key_only('Ocarina', player)
if this_location:
this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.'
@@ -2255,7 +2252,7 @@ def write_strings(rom, world, player, team):
# Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well.
items_to_hint = RelevantItems.copy()
if world.owShuffle[player] in ['parallel','full']:
if world.owShuffle[player] != 'vanilla' or world.owMixed[player]:
items_to_hint.remove('Ocarina')
if world.keyshuffle[player]:
items_to_hint.extend(SmallKeys)
@@ -2264,7 +2261,7 @@ def write_strings(rom, world, player, team):
random.shuffle(items_to_hint)
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8
hint_count += 2 if world.doorShuffle[player] == 'crossed' else 0
hint_count += 1 if world.owShuffle[player] in ['parallel', 'full'] else 0
hint_count += 1 if world.owShuffle[player] != 'vanilla' or world.owMixed[player] else 0
while hint_count > 0:
this_item = items_to_hint.pop(0)
this_location = world.find_items_not_key_only(this_item, player)
@@ -2475,7 +2472,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3:
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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owMixed[player]):
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple']:
rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) # mountain cave starts on OW
@@ -2498,18 +2495,18 @@ 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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x05 in world.owswaps[player][0] and world.owMixed[player]):
rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x07 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x07 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owMixed[player]):
rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owMixed[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)
@@ -2597,21 +2594,21 @@ def set_inverted_mode(world, player, rom, inverted_buffer):
write_int16(rom, 0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door
write_int16(rom, 0xDBA71 + 2 * 0x35, 0x011C)
if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owMixed[player]):
rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owMixed[player]):
rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owMixed[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
if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owMixed[player]):
rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock
# apply inverted map changes

View File

@@ -844,7 +844,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x00 in world.owswaps[player][0] and world.owMixed[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))
@@ -859,12 +859,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x02 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owMixed[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:
@@ -872,7 +872,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x05 in world.owswaps[player][0] and world.owMixed[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))
@@ -894,7 +894,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x07 in world.owswaps[player][0] and world.owMixed[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:
@@ -902,7 +902,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x0a in world.owswaps[player][0] and world.owMixed[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))
@@ -911,12 +911,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x0f in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x10 in world.owswaps[player][0] and world.owMixed[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 East Bottom Mirror Spot', player), lambda state: state.has_Mirror(player))
@@ -929,24 +929,24 @@ def ow_rules(world, player):
set_rule(world.get_entrance('West Dark World Teleporter (Hammer)', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player))
set_rule(world.get_entrance('West Dark World Teleporter (Rock)', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) # bunny cannot lift bushes
if (world.mode[player] == 'inverted') == (0x11 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x11 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x12 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x13 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x14 in world.owswaps[player][0] and world.owMixed[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:
@@ -956,28 +956,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x15 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x16 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x17 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x18 in world.owswaps[player][0] and world.owMixed[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:
@@ -985,13 +985,13 @@ 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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x1a in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x1b in world.owswaps[player][0] and world.owMixed[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])
@@ -1017,7 +1017,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x1d in world.owswaps[player][0] and world.owMixed[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))
@@ -1026,12 +1026,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x1e in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x22 in world.owswaps[player][0] and world.owMixed[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))
@@ -1039,19 +1039,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x25 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x28 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x29 in world.owswaps[player][0] and world.owMixed[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:
@@ -1059,24 +1059,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x2a in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x2b in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x2c in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x2d in world.owswaps[player][0] and world.owMixed[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))
@@ -1085,19 +1085,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x2e in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x2f in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x30 in world.owswaps[player][0] and world.owMixed[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))
@@ -1112,14 +1112,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x32 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x33 in world.owswaps[player][0] and world.owMixed[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
@@ -1130,12 +1130,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x34 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x35 in world.owswaps[player][0] and world.owMixed[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))
@@ -1152,12 +1152,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x37 in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x3a in world.owswaps[player][0] and world.owMixed[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:
@@ -1165,17 +1165,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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x3b in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x3c in world.owswaps[player][0] and world.owMixed[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.owSwap[player] == 'mixed'):
if (world.mode[player] == 'inverted') == (0x3f in world.owswaps[player][0] and world.owMixed[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))
@@ -2050,7 +2050,7 @@ def set_inverted_big_bomb_rules(world, player):
else:
raise Exception('No logic found for routing from %s to the pyramid.' % bombshop_entrance.name)
if world.owShuffle[player] != 'vanilla' or world.owSwap[player] != 'vanilla':
if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player]:
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: False) #temp disable progression until routing to Pyramid get be guaranteed

View File

@@ -341,7 +341,7 @@ OWNewDestination:
sep #$30 : lda OWOppSlotOffset,y : !add $04 : asl : and #$7f : sta $700
; crossed OW shuffle
lda.l OWMode+1 : and #$ff : cmp #$02 : bne .return
lda.l OWMode+1 : and.b #$02 : beq .return
ldx $05 : lda.l OWTileWorldAssoc,x : sta.l $7ef3ca ; change world
; toggle bunny mode

Binary file not shown.

View File

@@ -3,13 +3,15 @@
vanilla: 0
parallel: 2
full: 2
overworld_crossed:
on: 1
off: 1
overworld_keepsimilar:
on: 1
off: 1
overworld_swap:
vanilla: 0
mixed: 2
crossed: 2
overworld_mixed:
on: 1
off: 1
flute_shuffle:
vanilla: 0
balanced: 1

View File

@@ -116,17 +116,18 @@
"full"
]
},
"ow_swap": {
"choices": [
"vanilla",
"mixed",
"crossed"
]
"ow_crossed": {
"action": "store_true",
"type": "bool"
},
"ow_keepsimilar": {
"action": "store_true",
"type": "bool"
},
"ow_mixed": {
"action": "store_true",
"type": "bool"
},
"ow_fluteshuffle": {
"choices": [
"vanilla",

View File

@@ -201,22 +201,19 @@
"Full: Overworld transitions are shuffled, but both worlds",
" will have an independent map shape."
],
"ow_swap": [
"This swaps the tiles of the overworld from one world to the other.",
"Vanilla: All overworld tiles remain in their original world as",
" they were in the base game.",
"Mixed: Overworld tiles are randomly chosen to become part of",
" the opposite world.",
"Crossed: Overworld tiles remain in their original world, but",
" the transitions are shuffled cross world."
],
"ow_crossed": [
"This allows cross-world connections to occur on the overworld." ],
"ow_keepsimilar": [
"This keeps similar edge transitions together. ie. the two west edges on",
"Potion Shop will be paired with another similar pair." ],
"ow_mixed": [
"Overworld tiles are randomly chosen to become part of the opposite world."
],
"ow_fluteshuffle": [
"This randomizes the flute spot destinations.",
"Vanilla: All flute spots remain unchanged.",
"Balanced: New flute spots will be generated but prevents flute spots from being on any adjacent screen.",
"Balanced: New flute spots will be generated but prevents flute",
" spots from being on any adjacent screen.",
"Random: New flute spots will be generated with minimal bias."
],
"door_shuffle": [

View File

@@ -117,12 +117,9 @@
"randomizer.overworld.overworldshuffle.parallel": "Parallel",
"randomizer.overworld.overworldshuffle.full": "Full",
"randomizer.overworld.overworldswap": "Tile Swap",
"randomizer.overworld.overworldswap.vanilla": "Vanilla",
"randomizer.overworld.overworldswap.mixed": "Mixed",
"randomizer.overworld.overworldswap.crossed": "Crossed",
"randomizer.overworld.crossed": "Crossed",
"randomizer.overworld.keepsimilar": "Keep Similar Edges Together",
"randomizer.overworld.mixed": "Mixed",
"randomizer.overworld.overworldflute": "Flute Shuffle",
"randomizer.overworld.overworldflute.vanilla": "Vanilla",

View File

@@ -9,15 +9,6 @@
"full"
]
},
"overworldswap": {
"type": "selectbox",
"default": "mixed",
"options": [
"vanilla",
"mixed",
"crossed"
]
},
"overworldflute": {
"type": "selectbox",
"default": "vanilla",
@@ -29,6 +20,14 @@
}
},
"rightOverworldFrame": {
"crossed": {
"type": "checkbox",
"default": false
},
"mixed": {
"type": "checkbox",
"default": true
},
"keepsimilar": {
"type": "checkbox",
"default": true

View File

@@ -76,8 +76,9 @@ SETTINGSTOPROCESS = {
},
"overworld": {
"overworldshuffle": "ow_shuffle",
"overworldswap": "ow_swap",
"crossed": "ow_crossed",
"keepsimilar": "ow_keepsimilar",
"mixed": "ow_mixed",
"overworldflute": "ow_fluteshuffle"
},
"entrance": {

View File

@@ -1,4 +1,4 @@
from tkinter import ttk, Frame, Label, W, E, NW, LEFT, RIGHT, X, TOP
from tkinter import ttk, Frame, Label, W, E, NW, LEFT, RIGHT, X, Y, TOP
import source.gui.widgets as widgets
import json
import os
@@ -16,12 +16,10 @@ def overworld_page(parent):
# Load Overworld Shuffle option widgets as defined by JSON file
# Defns include frame name, widget type, widget options, widget placement attributes
# These get split left & right
self.frames["widgets"] = Frame(self)
self.frames["leftOverworldFrame"] = Frame(self.frames["widgets"])
self.frames["rightOverworldFrame"] = Frame(self.frames["widgets"])
self.frames["widgets"].pack(fill=X)
self.frames["leftOverworldFrame"].pack(side=LEFT)
self.frames["rightOverworldFrame"].pack(side=LEFT, anchor=NW)
self.frames["leftOverworldFrame"] = Frame(self)
self.frames["rightOverworldFrame"] = Frame(self)
self.frames["leftOverworldFrame"].pack(side=LEFT, anchor=NW)
self.frames["rightOverworldFrame"].pack(anchor=NW)
with open(os.path.join("resources","app","gui","randomize","overworld","widgets.json")) as overworldWidgets:
myDict = json.load(overworldWidgets)
@@ -29,8 +27,8 @@ def overworld_page(parent):
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.widgets[key] = dictWidgets[key]
if key == "rightOverworldFrame":
self.widgets[key].pack(anchor=E)
if framename == "rightOverworldFrame":
self.widgets[key].pack(side=LEFT)
else:
self.widgets[key].pack(anchor=E)