Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2021-08-26 08:02:24 -05:00
19 changed files with 431 additions and 461 deletions

View File

@@ -2980,7 +2980,7 @@ class Settings(object):
@staticmethod
def make_code(w, p):
code = bytes([
(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]],
(dr_mode[w.doorShuffle[p]] << 6) | (or_mode[w.owShuffle[p]] << 5) | (0x10 if w.owCrossed[p] != 'none' 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),

View File

@@ -1,5 +1,12 @@
# Changelog
### 0.1.9.0
- Expanded Crossed OW to four separate options, see Readme for details
- Crossed OW will now play a short SFX when changing worlds
- Improved Link/Bunny state in Crossed OW
- Fixed issue with TR Pegs when fluting directly from an area with hammerpegs
- Updated OW GUI layout
### 0.1.8.2
- Fixed issue with game crashing on using Flute
- Fixed issues with Link/Bunny state in Crossed OW
@@ -7,7 +14,6 @@
- Fixed issue with Mystery for OW boolean options
- ~~Merged DR v0.5.1.0 - Major Keylogic Update~~
### 0.1.8.1
- Fixed issue with activating flute in DW (OW Mixed)
- Fixed issue with Parallel+Crossed not generating

2
CLI.py
View File

@@ -146,7 +146,7 @@ def parse_settings():
"openpyramid": False,
"shuffleganon": True,
"ow_shuffle": "vanilla",
"ow_crossed": False,
"ow_crossed": "none",
"ow_keepsimilar": False,
"ow_mixed": False,
"ow_fluteshuffle": "vanilla",

View File

@@ -215,7 +215,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 not world.owCrossed[player] and not world.owMixed[player] 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 world.owCrossed[player] == 'none' and not world.owMixed[player] and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]:
# validate_vanilla_key_logic(world, player)

View File

@@ -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.owCrossed[1] or world.owMixed[1] or str(world.seed).startswith('M'):
if world.owShuffle[1] != 'vanilla' or world.owCrossed[1] != 'none' 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}'

View File

@@ -135,8 +135,8 @@ def roll_settings(weights):
overworld_shuffle = get_choice('overworld_shuffle')
ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla'
ret.ow_crossed = get_choice('overworld_crossed')
ret.ow_keepsimilar = get_choice('overworld_keepsimilar') == 'on'
ret.ow_crossed = get_choice('overworld_crossed') == 'on'
ret.ow_mixed = get_choice('overworld_mixed') == 'on'
overworld_flute = get_choice('flute_shuffle')
ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla'

View File

@@ -2,7 +2,7 @@ import RaceRandom as random, logging, copy
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot, Entrance
from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel
__version__ = '0.1.8.2-u'
__version__ = '0.1.9.0-u'
def link_overworld(world, player):
# setup mandatory connections
@@ -11,10 +11,108 @@ def link_overworld(world, player):
for exitname, destname in temporary_mandatory_connections:
connect_two_way(world, exitname, destname, player)
def performSwap(groups, swaps):
def getParallel(edgename):
if edgename in parallel_links:
return parallel_links[edgename]
elif edgename in parallel_links.inverse:
return parallel_links.inverse[edgename][0]
else:
raise Exception('No parallel edge found for edge %s', edgename)
def getNewSets(all_set, other_set):
new_all_set = list(map(getParallel, all_set))
if not all(edge in orig_swaps for edge in new_all_set):
raise Exception('Cannot move a parallel edge without the other')
else:
for edge in new_all_set:
swaps.remove(edge)
new_other_set = getNewSet(other_set)
return (new_all_set, new_other_set)
def getNewSet(edge_set):
new_set = []
for edge in edge_set:
if edge in orig_swaps:
new_edge = getParallel(edge)
if new_edge not in orig_swaps:
raise Exception('Cannot move a parallel edge without the other')
new_set.append(new_edge)
swaps.remove(new_edge)
else:
new_set.append(edge)
return new_set
# swaps edges from one pool to another
orig_swaps = copy.deepcopy(swaps)
new_groups = {}
for group in groups.keys():
new_groups[group] = ([],[])
for group in groups.keys():
(mode, wrld, dir, terrain, parallel, count) = group
for (forward_set, back_set) in zip(groups[group][0], groups[group][1]):
anyF = any(edge in orig_swaps for edge in forward_set)
anyB = any(edge in orig_swaps for edge in back_set)
allF = all(edge in orig_swaps for edge in forward_set)
allB = all(edge in orig_swaps for edge in back_set)
if not (anyF or anyB):
# no change
new_groups[group][0].append(forward_set)
new_groups[group][1].append(back_set)
elif allF and allB:
# move both sets
if parallel == IsParallel.Yes and not (all(edge in orig_swaps for edge in map(getParallel, forward_set)) and all(edge in orig_swaps for edge in map(getParallel, back_set))):
raise Exception('Cannot move a parallel edge without the other')
new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][0].append(forward_set)
new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][1].append(back_set)
for edge in forward_set:
swaps.remove(edge)
for edge in back_set:
swaps.remove(edge)
elif anyF or anyB:
if parallel == IsParallel.Yes:
if allF or allB:
# move one set
if allF and not (world.owKeepSimilar[player] and anyB):
(new_forward_set, new_back_set) = getNewSets(forward_set, back_set)
elif allB and not (world.owKeepSimilar[player] and anyF):
(new_back_set, new_forward_set) = getNewSets(back_set, forward_set)
else:
raise Exception('Cannot move an edge out of a Similar group')
new_groups[group][0].append(new_forward_set)
new_groups[group][1].append(new_back_set)
else:
# move individual edges
if not world.owKeepSimilar[player]:
new_groups[group][0].append(getNewSet(forward_set) if anyF else forward_set)
new_groups[group][1].append(getNewSet(back_set) if anyB else back_set)
else:
raise Exception('Cannot move an edge out of a Similar group')
else:
raise NotImplementedError('Cannot move one side of a non-parallel connection')
else:
raise NotImplementedError('Invalid OW Edge swap scenario')
return new_groups
tile_groups = reorganize_tile_groups(world, player)
trimmed_groups = copy.deepcopy(OWEdgeGroups)
swapped_edges = list()
# adjust Frog/Dig Game swap manually due to NP/P relationship with LW
if world.owShuffle[player] == 'parallel' and not world.owKeepSimilar[player]:
# restructure Maze Race/Suburb/Frog/Dig Game manually due to NP/P relationship
if world.owKeepSimilar[player]:
for group in trimmed_groups.keys():
(std, region, axis, terrain, parallel, _) = group
if parallel == IsParallel.Yes:
(forward_edges, back_edges) = trimmed_groups[group]
if ['Maze Race ES'] in forward_edges:
forward_edges = list(filter((['Maze Race ES']).__ne__, forward_edges))
trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][0].append(['Maze Race ES'])
if ['Kakariko Suburb WS'] in back_edges:
back_edges = list(filter((['Kakariko Suburb WS']).__ne__, back_edges))
trimmed_groups[(std, region, axis, terrain, IsParallel.No, 1)][1].append(['Kakariko Suburb WS'])
trimmed_groups[group] = (forward_edges, back_edges)
else:
for group in trimmed_groups.keys():
(std, region, axis, terrain, _, _) = group
(forward_edges, back_edges) = trimmed_groups[group]
@@ -29,131 +127,17 @@ def link_overworld(world, player):
trimmed_groups[group] = (forward_edges, back_edges)
# tile shuffle
logging.getLogger('').debug('Swapping overworld tiles')
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']:
if world.shuffle[player] in ['vanilla', 'simple', 'dungeonssimple']:
tile_groups[(name,)] = ([], [], [])
else:
tile_groups[(name, groupType)] = ([], [], [])
for (name, groupType) in OWTileGroups.keys():
if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks']:
(lw_owids, dw_owids) = OWTileGroups[(name, groupType,)]
if world.shuffle[player] in ['vanilla', 'simple', 'dungeonssimple']:
(exist_owids, exist_lw_regions, exist_dw_regions) = tile_groups[(name,)]
exist_owids.extend(lw_owids)
exist_owids.extend(dw_owids)
for owid in lw_owids:
exist_lw_regions.extend(OWTileRegions.inverse[owid])
for owid in dw_owids:
exist_dw_regions.extend(OWTileRegions.inverse[owid])
tile_groups[(name,)] = (exist_owids, exist_lw_regions, exist_dw_regions)
else:
(exist_owids, exist_lw_regions, exist_dw_regions) = tile_groups[(name, groupType)]
exist_owids.extend(lw_owids)
exist_owids.extend(dw_owids)
for owid in lw_owids:
exist_lw_regions.extend(OWTileRegions.inverse[owid])
for owid in dw_owids:
exist_dw_regions.extend(OWTileRegions.inverse[owid])
tile_groups[(name, groupType)] = (exist_owids, exist_lw_regions, exist_dw_regions)
swapped_edges = shuffle_tiles(world, tile_groups, world.owswaps[player], player)
# 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):
removed.append(group)
for group in removed:
tile_groups.pop(group, None)
# move swapped regions/edges to other world
trimmed_groups = performSwap(trimmed_groups, swapped_edges)
assert len(swapped_edges) == 0, 'Not all edges were swapped successfully: ' + ', '.join(swapped_edges )
# 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]
exist_owids.extend(owids)
exist_lw_regions.extend(lw_regions)
exist_dw_regions.extend(dw_regions)
world.owswaps[player] = [exist_owids, exist_lw_regions, exist_dw_regions]
# 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:
ignore_list.append(edge)
swapped_edges = list()
def getSwappedEdges(world, lst, player):
for regionname in lst:
region = world.get_region(regionname, player)
for exit in region.exits:
if exit.spot_type == 'OWEdge' and exit.name not in ignore_list:
swapped_edges.append(exit.name)
getSwappedEdges(world, world.owswaps[player][1], player)
getSwappedEdges(world, world.owswaps[player][2], player)
def performSwap(groups, swaps, nonParallelOnly=False):
try:
for group in groups.keys():
(mode, wrld, dir, terrain, parallel, count) = group
for p in range(0, len(groups[group])):
edgepool = groups[group][p]
for s in range(0, len(edgepool)):
if s <= len(edgepool):
for e in range(0, len(edgepool[s])):
if len(edgepool) > 0 and edgepool[s][e] in swaps:
if parallel == IsParallel.Yes:
if not nonParallelOnly:
if wrld == WorldType.Light and edgepool[s][e] in parallel_links:
logging.getLogger('').debug('%s was moved', edgepool[s][e])
swaps.remove(edgepool[s][e])
groups[group][p][s][e] = parallel_links[edgepool[s][e]]
elif wrld == WorldType.Dark and edgepool[s][e] in parallel_links.inverse:
logging.getLogger('').debug('%s was moved', edgepool[s][e])
swaps.remove(edgepool[s][e])
groups[group][p][s][e] = parallel_links.inverse[edgepool[s][e]][0]
else:
for edge in edgepool[s]:
logging.getLogger('').debug('%s was moved', edge)
swaps.remove(edge)
groups[(mode, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][p].append(edgepool[s])
groups[group][p].remove(edgepool[s])
except IndexError:
#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 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'])
trimmed_groups[(OpenStd.Open, WorldType.Light, PolSlot.EastWest, Terrain.Land, IsParallel.Yes, 1)][1].remove(['Kakariko Suburb WS'])
trimmed_groups[(OpenStd.Open, WorldType.Light, PolSlot.EastWest, Terrain.Land, IsParallel.No, 2)][0].append(['Dig Game EC', 'Dig Game ES'])
trimmed_groups[(OpenStd.Open, WorldType.Light, PolSlot.EastWest, Terrain.Land, IsParallel.No, 2)][1].append(['Frog WC', 'Frog WS'])
trimmed_groups[(OpenStd.Open, WorldType.Dark, PolSlot.EastWest, Terrain.Land, IsParallel.No, 2)] = [[],[]]
swapped_edges.remove('Maze Race ES')
swapped_edges.remove('Kakariko Suburb WS')
swapped_edges.remove('Dig Game EC')
swapped_edges.remove('Dig Game ES')
swapped_edges.remove('Frog WC')
swapped_edges.remove('Frog WS')
tries = 5
while tries > 0:
performSwap(trimmed_groups, swapped_edges)
if len(swapped_edges) == 0:
tries = 0
continue
tries -= 1
assert len(swapped_edges) == 0
#move swapped regions to other world
update_world_regions(world, player)
# make new connections
# 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]):
for (exitname, regionname) in ow_connections[owid][0]:
@@ -162,10 +146,49 @@ def link_overworld(world, player):
for (exitname, regionname) in ow_connections[owid][1]:
connect_simple(world, exitname, regionname, player)
connected_edges = []
# crossed shuffle
logging.getLogger('').debug('Crossing overworld edges')
if world.owCrossed[player] in ['grouped', 'limited', 'chaos']:
if world.owCrossed[player] == 'grouped':
crossed_edges = shuffle_tiles(world, tile_groups, [[],[],[]], player)
elif world.owCrossed[player] in ['limited', 'chaos']:
crossed_edges = list()
crossed_candidates = list()
for group in trimmed_groups.keys():
(mode, wrld, dir, terrain, parallel, count) = group
if parallel == IsParallel.Yes and wrld == WorldType.Light and (mode == OpenStd.Open or world.mode[player] != 'standard'):
for (forward_set, back_set) in zip(trimmed_groups[group][0], trimmed_groups[group][1]):
if world.owKeepSimilar[player]:
if world.owCrossed[player] == 'chaos' and random.randint(0, 1):
for edge in forward_set:
crossed_edges.append(edge)
elif world.owCrossed[player] == 'limited':
crossed_candidates.append(forward_set)
else:
for edge in forward_set:
if world.owCrossed[player] == 'chaos' and random.randint(0, 1):
crossed_edges.append(edge)
elif world.owCrossed[player] == 'limited':
crossed_candidates.append(edge)
if world.owCrossed[player] == 'limited':
random.shuffle(crossed_candidates)
for edge_set in crossed_candidates[:9]:
for edge in edge_set:
crossed_edges.append(edge)
for edge in copy.deepcopy(crossed_edges):
if edge in parallel_links:
crossed_edges.append(parallel_links[edge])
elif edge in parallel_links.inverse:
crossed_edges.append(parallel_links.inverse[edge][0])
trimmed_groups = performSwap(trimmed_groups, crossed_edges)
assert len(crossed_edges) == 0, 'Not all edges were crossed successfully: ' + ', '.join(crossed_edges)
# layout shuffle
if world.owShuffle[player] == 'vanilla' and not world.owCrossed[player]:
logging.getLogger('').debug('Shuffling overworld layout')
connected_edges = []
if world.owShuffle[player] == 'vanilla':
# vanilla transitions
groups = list(trimmed_groups.values())
for (forward_edge_sets, back_edge_sets) in groups:
@@ -174,100 +197,61 @@ def link_overworld(world, player):
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)
assert len(connected_edges) == len(default_connections) * 2, connected_edges
else:
if world.owKeepSimilar[player] and world.owShuffle[player] == 'parallel':
if world.owKeepSimilar[player] and world.owShuffle[player] in ['vanilla', 'parallel']:
for exitname, destname in parallelsimilar_connections:
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)
connect_custom(world, connected_edges, player)
# layout shuffle
trimmed_groups = remove_reserved(world, trimmed_groups, connected_edges, player)
groups = reorganize_groups(world, trimmed_groups, player)
# all layout shuffling occurs here
if world.owShuffle[player] != 'vanilla':
# layout shuffle
if world.mode[player] == 'standard':
random.shuffle(groups[2:]) # keep first 2 groups (Standard) first
else:
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)
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]
else:
forward_set = None
break
f += 1
while back_set[0] in connected_edges:
b += 1
if b < len(back_edge_sets):
back_set = back_edge_sets[b]
else:
back_set = None
break
b += 1
if forward_set is not None and back_set is not None:
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)
elif forward_set is not None:
logging.getLogger('').warning("Edge '%s' could not find a valid connection" % forward_set[0])
elif back_set is not None:
logging.getLogger('').warning("Edge '%s' could not find a valid connection" % back_set[0])
if world.mode[player] == 'standard':
random.shuffle(groups[2:]) # keep first 2 groups (Standard) first
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]
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)
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]
else:
forward_set = None
break
f += 1
while back_set[0] in connected_edges:
b += 1
if b < len(back_edge_sets):
back_set = back_edge_sets[b]
else:
back_set = None
break
b += 1
if forward_set is not None and back_set is not None:
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)
assert len(connected_edges) == len(default_connections) * 2, connected_edges
elif forward_set is not None:
logging.getLogger('').warning("Edge '%s' could not find a valid connection" % forward_set[0])
elif back_set is not None:
logging.getLogger('').warning("Edge '%s' could not find a valid connection" % back_set[0])
assert len(connected_edges) == len(default_connections) * 2, connected_edges
# flute shuffle
def connect_flutes(flute_destinations):
@@ -352,7 +336,20 @@ def connect_simple(world, exitname, regionname, player):
def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
edge1 = world.get_entrance(edgename1, player)
edge2 = world.get_entrance(edgename2, player)
x = world.check_for_owedge(edgename1, player)
y = world.check_for_owedge(edgename2, player)
if x is None:
raise Exception('%s is not a valid edge.', edgename1)
elif y is None:
raise Exception('%s is not a valid edge.', edgename2)
if connected_edges is not None:
if edgename1 in connected_edges or edgename2 in connected_edges:
if (x.dest and x.dest.name == edgename2) and (y.dest and y.dest.name == edgename1):
return
else:
raise Exception('Edges \'%s\' and \'%s\' already connected elsewhere', edgename1, edgename2)
# if these were already connected somewhere, remove the backreference
if edge1.connected_region is not None:
edge1.connected_region.entrances.remove(edge1)
@@ -361,17 +358,10 @@ def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
edge1.connect(edge2.parent_region)
edge2.connect(edge1.parent_region)
x = world.check_for_owedge(edgename1, player)
y = world.check_for_owedge(edgename2, player)
if x is None:
logging.getLogger('').error('%s is not a valid edge.', edgename1)
elif y is None:
logging.getLogger('').error('%s is not a valid edge.', edgename2)
else:
x.dest = y
y.dest = x
x.dest = y
y.dest = x
if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player]:
if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player] != 'none':
world.spoiler.set_overworld(edgename2, edgename1, 'both', player)
if connected_edges is not None:
@@ -379,7 +369,7 @@ def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
connected_edges.append(edgename2)
# connecting parallel connections
if world.owShuffle[player] == 'parallel' or (world.owShuffle[player] == 'vanilla' and world.owCrossed[player]):
if world.owShuffle[player] in ['vanilla', 'parallel']:
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]
@@ -390,6 +380,77 @@ def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
# TODO: Figure out why non-parallel edges are getting into parallel groups
raise KeyError('No parallel edge for edge %s' % edgename2)
def shuffle_tiles(world, groups, result_list, player):
swapped_edges = list()
# tile shuffle happens here
removed = list()
for group in groups.keys():
if random.randint(0, 1):
removed.append(group)
# save shuffled tiles to list
for group in groups.keys():
if group not in removed:
(owids, lw_regions, dw_regions) = groups[group]
(exist_owids, exist_lw_regions, exist_dw_regions) = result_list
exist_owids.extend(owids)
exist_lw_regions.extend(lw_regions)
exist_dw_regions.extend(dw_regions)
result_list = [exist_owids, exist_lw_regions, exist_dw_regions]
# 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:
ignore_list.append(edge)
if world.owCrossed[player] != 'polar':
# in polar, the actual edge connections remain vanilla
def getSwappedEdges(world, lst, player):
for regionname in lst:
region = world.get_region(regionname, player)
for exit in region.exits:
if exit.spot_type == 'OWEdge' and exit.name not in ignore_list:
swapped_edges.append(exit.name)
getSwappedEdges(world, result_list[1], player)
getSwappedEdges(world, result_list[2], player)
return swapped_edges
def reorganize_tile_groups(world, player):
groups = {}
for (name, groupType) in OWTileGroups.keys():
if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks']:
if world.shuffle[player] in ['vanilla', 'simple', 'dungeonssimple']:
groups[(name,)] = ([], [], [])
else:
groups[(name, groupType)] = ([], [], [])
for (name, groupType) in OWTileGroups.keys():
if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks']:
(lw_owids, dw_owids) = OWTileGroups[(name, groupType,)]
if world.shuffle[player] in ['vanilla', 'simple', 'dungeonssimple']:
(exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name,)]
exist_owids.extend(lw_owids)
exist_owids.extend(dw_owids)
for owid in lw_owids:
exist_lw_regions.extend(OWTileRegions.inverse[owid])
for owid in dw_owids:
exist_dw_regions.extend(OWTileRegions.inverse[owid])
groups[(name,)] = (exist_owids, exist_lw_regions, exist_dw_regions)
else:
(exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name, groupType)]
exist_owids.extend(lw_owids)
exist_owids.extend(dw_owids)
for owid in lw_owids:
exist_lw_regions.extend(OWTileRegions.inverse[owid])
for owid in dw_owids:
exist_dw_regions.extend(OWTileRegions.inverse[owid])
groups[(name, groupType)] = (exist_owids, exist_lw_regions, exist_dw_regions)
return groups
def remove_reserved(world, groupedlist, connected_edges, player):
new_grouping = {}
for group in groupedlist.keys():
@@ -405,7 +466,7 @@ def remove_reserved(world, groupedlist, connected_edges, player):
back_edges = list(list(filter((edge).__ne__, i)) for i in back_edges)
# 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:
if 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)
@@ -428,250 +489,87 @@ 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':
if world.owCrossed[player]:
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
# 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 = {}
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())
elif world.owShuffle[player] == 'parallel' and world.owCrossed[player]:
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
# tuple goes to (A,_,C,D,E,F)
# tuple goes to (A,B,C,D,_,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)] = ([], [])
(std, region, axis, terrain, _, count) = group
new_grouping[(std, region, axis, terrain, count)] = ([], [])
for group in grouping.keys():
(std, _, axis, terrain, parallel, count) = group
(std, region, axis, terrain, _, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(std, axis, terrain, parallel, count)]
(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, axis, terrain, parallel, count)] = (exist_forward_edges, exist_back_edges)
new_grouping[(std, region, axis, terrain, count)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,_,C,D,E,F)
# tuple goes to (_,B,C,D,_,F)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, _, axis, terrain, parallel, count) = group
new_grouping[(axis, terrain, parallel, count)] = ([], [])
(_, region, axis, terrain, _, count) = group
new_grouping[(region, axis, terrain, count)] = ([], [])
for group in grouping.keys():
(_, _, axis, terrain, parallel, count) = group
(_, region, axis, terrain, _, count) = group
(forward_edges, back_edges) = grouping[group]
(exist_forward_edges, exist_back_edges) = new_grouping[(axis, terrain, parallel, count)]
(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[(axis, terrain, parallel, count)] = (exist_forward_edges, exist_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,_,C,D,E,_)
# tuple goes to (A,B,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(std, _, axis, terrain, parallel, _) = group
new_grouping[(std, axis, terrain, parallel)] = ([], [])
(std, region, axis, terrain, _, _) = group
new_grouping[(std, region, axis, terrain)] = ([], [])
for group in grouping.keys():
(std, _, axis, terrain, parallel, _) = group
(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, axis, terrain, parallel)]
(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, axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges)
new_grouping[(std, region, axis, terrain)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
else:
# tuple goes to (_,_,C,D,E,_)
# tuple goes to (_,B,C,D,_,_)
for grouping in (groups,):
new_grouping = {}
for group in grouping.keys():
(_, _, axis, terrain, parallel, _) = group
new_grouping[(axis, terrain, parallel)] = ([], [])
(_, region, axis, terrain, _, _) = group
new_grouping[(region, axis, terrain)] = ([], [])
for group in grouping.keys():
(_, _, axis, terrain, parallel, _) = group
(_, 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[(axis, terrain, parallel)]
(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[(axis, terrain, parallel)] = (exist_forward_edges, exist_back_edges)
new_grouping[(region, axis, terrain)] = (exist_forward_edges, exist_back_edges)
return list(new_grouping.values())
elif world.owShuffle[player] == 'parallel' or (world.owShuffle[player] == 'vanilla' and world.owCrossed[player]):
elif world.owShuffle[player] == 'parallel':
if world.owKeepSimilar[player]:
if world.mode[player] == 'standard':
# tuple stays (A,B,C,D,E,F)

View File

@@ -162,8 +162,7 @@ def prefill_world(world, plando, text_patches):
world.owShuffle = {1: modestr.strip()}
elif line.startswith('!owCrossed'):
_, modestr = line.split(':', 1)
modestr = modestr.strip().lower()
world.owCrossed = {1: True if modestr in ('true', 'yes', 'on', 'enabled') else False}
world.owCrossed = {1: modestr.strip()}
elif line.startswith('!owKeepSimilar'):
_, modestr = line.split(':', 1)
modestr = modestr.strip().lower()

View File

@@ -246,6 +246,6 @@ Ganon: Triforce
# set Overworld connections (lines starting with $, separate edges with =)
!owShuffle: parallel
#!owMixed: true # Mixed OW not supported yet
!owCrossed: true
!owCrossed: none
!owKeepSimilar: true
$Links House NE = Kakariko Village SE

View File

@@ -9,20 +9,19 @@ See https://alttpr.com/ for more details on the normal randomizer.
This is a very new mode of LTTPR so the tools and info is very limited.
- There is an [OW Rando Cheat Sheet](https://zelda.codemann8.com/images/shared/ow-rando-reference-sheet.png) that shows all the transitions that exist and are candidates for shuffle.
- There is OW tracking capability within the following trackers:
- CodeTracker, an [EmoTracker](https://emotracker.net) package for LTTPR
- [Community Tracker](https://alttptracker.dunka.net/)
- CodeTracker, an [EmoTracker](https://emotracker.net) package for LTTPR
- There is an [OW OWG Reference Sheet](https://zelda.codemann8.com/images/shared/ow-owg-reference-sheet.png) that shows all the in-logic places where boots/mirror clips and fake flippers are expected from the player.
# Known Issues
(Updated 2021-06-23)
(Updated 2021-08-26)
### If you want to playtest this, know these things:
- Big Red Bomb may require bomb duping as ledge drops may be in the way of your path to the Pyramid Fairy crack
- If you fake flipper, beware of transitioning south. You could end up at the top of the waterfall in the southeast of either world. If you mistakenly drop down, it is important to NOT make any other movements and S+Q immediately when the game allows you to (might take several seconds, the game has to scroll back to the original point of water entry) or there will be a hardlock. Falling from the waterfall is avoidable but it is super easy to do as it is super close to the transition.
- In Crossed OW Tile Swap, there are some interesting bunny water-walk situations that can occur, these are meant to be out-of-logic but beware of logic bugs around this area.
- In Crossed OW, there are some interesting bunny swimming situations that can occur, these are meant to be out-of-logic but beware of logic bugs around this area. But also, hardlocks can occur; if you take damage, be sure to S+Q immediately before moving in any direction, or you may get an infinite screen wrap glitch.
### Known bugs:
- ~~In Mixed OW Tile Swap, Smith and Stumpy have issues when their tiles are swapped. Progression cannot be found on them when these tiles are swapped~~ (Fixed in 0.1.6.4)
- Screens that loop on itself and also have free-standing items, the sprites are duplicated and can cause item duplication
- When OWG are performed to enter mega-tile screens (large OW screens), there is a small chance that an incorrect VRAM reference value causes the map graphics to offset in increments of 16 pixels
@@ -66,26 +65,58 @@ OW Transitions are shuffled, but both worlds will have a matching layout.
OW Transitions are shuffled within each world separately.
## Crossed (--ow_crossed)
## Crossed Options (--ow_crossed)
This allows OW connections to be shuffled cross-world.
## Visual Representation of Main OW Shuffle Settings
Polar and Grouped both are guaranteed to result in two separated planes of tiles. To navigate to the other plane, you have the following methods: 1) Normal portals 2) Mirroring on DW tiles 3) Fluting to a LW tile that was previously unreachable
![OW Shuffle Settings Combination](https://zelda.codemann8.com/images/shared/ow-modes.gif)
Limited and Chaos are not bound to follow a two-plane framework. This means that it could be possible to travel on foot to every tile without entering a normal portal.
See each option to get more details on the differences.
### None
Transitions will remain same-world.
### Polar
Only effective if Mixed/Tile Swap is enabled. Enabling Polar preserves the original/vanilla connections even when tiles are swapped/mixed. This results in a completely vanilla overworld, except that some tiles will transform Link to a Bunny (as per Mixed swapping some tiles to the other world). This offers an interesting twist on Mixed where you have a pre-conditioned knowledge of the terrain you will encounter, but not necessarily be able to do what you need to do there. (see Tile Swap/Mixed section for more details)
### Grouped
This option shuffles connections cross-world in the same manner as Tile Swap/Mixed, the connections leading in and coming out of a group of tiles are crossed. Unlike Polar, this uses a different set of tile groups as a basis of crossing connections, albeit the same rule govern which groups of tiles must cross together (see Tile Swap/Mixed for more details)
### Limited
Every transition independently is a candidate to be chosen as a cross-world connection, however only 9 transitions become crossed (in each world). This option abides by the Keep Similar Edges Together option and will guarantee same effect on all edges in a Similar Edge group if enabled. If a Similar Edge group is chosen from the pool of candidates, it only counts as one portal, not multiple.
Note: Only parallel connections (a connection that also exists in the opposite world) are considered for cross-world connections, which means that the same connection in the opposite world will also connect cross-world.
Motive: Why 9 connections? To imitate the effect of the 9 standard portals that exist.
### Chaos
Same as Limited, except that there is no limit to the number of cross-world connections that are made. Each transition has an equal 50/50 chance of being a crossed connection.
## Keep Similar Edges Together (--ow_keepsimilar)
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)
Note: This affects OW Layout Shuffle mostly, but also affects Limited and Chaos modes in Crossed OW.
OW tiles are randomly chosen to become a part of the opposite world
## Tile Swap / Mixed Overworld (--ow_mixed)
OW tiles are randomly chosen to become a part of the opposite world. When on the Overworld, there will be an L or D in the upper left corner, indicating which world you are currently in. Mirroring still works the same, you must be in the DW to mirror to the LW.
Note: Tiles are put into groups that must be shuffled together when certain settings are enabled. For instance, if ER is disabled, then any tiles that have a connector cave that leads to another tile, those tiles must swap together; (an exception to this is the Old Man Rescue cave which has been modified similar to how Inverted modifies it, Old Man Rescue is ALWAYS accessible from the Light 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.
Note: Desert Teleporter Ledge is always guaranteed to be chosen. One of the three Mountain tiles are guaranteed if OW Layout Shuffle is set to Vanilla.
### Vanilla
Flute spots remain unchanged.
@@ -114,10 +145,10 @@ Show the help message and exit.
For specifying the overworld layout shuffle you want as above. (default: vanilla)
```
--ow_crossed
--ow_crossed <mode>
```
This allows cross-world connections on the overworld
For specifying the type of cross-world connections you want on the overworld
```
--ow_keepsimilar

8
Rom.py
View File

@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '363c49821f25327f10bc200c98375bde'
RANDOMIZERBASEHASH = '712ebda1ef6818c59cde0371b5a4e4a9'
class JsonRom(object):
@@ -646,16 +646,16 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# patch overworld edges
inverted_buffer = [0] * 0x82
if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] or world.owMixed[player]:
if world.owShuffle[player] != 'vanilla' or world.owCrossed[player] != 'none' 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]):
if world.owKeepSimilar[player] and (world.owShuffle[player] != 'vanilla' or world.owCrossed[player] in ['limited', 'chaos']):
owMode |= 0x100
if world.owCrossed[player]:
if world.owCrossed[player] != 'none' and (world.owCrossed[player] != 'polar' or world.owMixed[player]):
owMode |= 0x200
world.fix_fake_world[player] = True
if world.owMixed[player]:

View File

@@ -2053,7 +2053,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.owMixed[player] or world.owCrossed[player]:
if world.owShuffle[player] != 'vanilla' or world.owMixed[player] or world.owCrossed[player] != 'none':
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: False) #temp disable progression until routing to Pyramid get be guaranteed

View File

@@ -342,21 +342,26 @@ OWNewDestination:
; crossed OW shuffle
LDA.l OWMode+1 : AND.b #!FLAG_OW_CROSSED : beq .return
ldx $05 : lda.l OWTileWorldAssoc,x : sta.l $7ef3ca ; change world
ldx $05 : lda.l OWTileWorldAssoc,x : cmp.l $7ef3ca : beq .return
sta.l $7ef3ca ; change world
lda #$38 : sta $012f ; play sfx - #$3b is an alternative
; toggle bunny mode
lda $7ef357 : bne .nobunny
lda.l InvertedMode : bne .inverted
lda $7ef3ca : and.b #$40 : bra +
.inverted lda $7ef3ca : and.b #$40 : eor #$40
+ cmp #$40 : bne .nobunny
; turn into bunny
lda #$17 : sta $5d
lda #$01 : sta $02e0 : sta $56
bra .return
; toggle bunny mode
+ lda $7ef357 : bne .nobunny
lda.l InvertedMode : bne .inverted
lda $7ef3ca : and.b #$40 : bra +
.inverted lda $7ef3ca : and.b #$40 : eor #$40
+ cmp #$40 : bne .nobunny
; turn into bunny
lda $5d : cmp #$04 : beq + ; if swimming, continue
lda #$17 : sta $5d
+ lda #$01 : sta $02e0 : sta $56
bra .return
.nobunny
stz $5d : stz $02e0 : stz $56
.nobunny
lda $5d : cmp #$17 : bne + ; retain current state unless bunny
stz $5d
+ stz $02e0 : stz $56
.return
lda $05 : sta $8a

Binary file not shown.

View File

@@ -117,8 +117,13 @@
]
},
"ow_crossed": {
"action": "store_true",
"type": "bool"
"choices": [
"none",
"polar",
"grouped",
"limited",
"chaos"
]
},
"ow_keepsimilar": {
"action": "store_true",

View File

@@ -202,7 +202,15 @@
" will have an independent map shape."
],
"ow_crossed": [
"This allows cross-world connections to occur on the overworld." ],
"This allows cross-world connections to occur on the overworld.",
"None: No transitions are cross-world connections.",
"Polar: Only used when Mixed is enabled. This retains original",
" connections even when overworld tiles are swapped.",
"Limited: Exactly nine transitions are randomly chosen as",
" cross-world connections (to emulate the nine portals).",
"Chaos: Every transition has a 50/50 chance to become a",
" crossworld connection."
],
"ow_keepsimilar": [
"This keeps similar edge transitions together. ie. the two west edges on",
"Potion Shop will be paired with another similar pair." ],

View File

@@ -118,8 +118,13 @@
"randomizer.overworld.overworldshuffle.full": "Full",
"randomizer.overworld.crossed": "Crossed",
"randomizer.overworld.crossed.none": "None",
"randomizer.overworld.crossed.polar": "Polar",
"randomizer.overworld.crossed.grouped": "Grouped",
"randomizer.overworld.crossed.limited": "Limited",
"randomizer.overworld.crossed.chaos": "Chaos",
"randomizer.overworld.keepsimilar": "Keep Similar Edges Together",
"randomizer.overworld.mixed": "Mixed",
"randomizer.overworld.mixed": "Tile Swap (Mixed)",
"randomizer.overworld.overworldflute": "Flute Shuffle",
"randomizer.overworld.overworldflute.vanilla": "Vanilla",

View File

@@ -9,6 +9,21 @@
"full"
]
},
"crossed": {
"type": "selectbox",
"default": "vanilla",
"options": [
"none",
"polar",
"grouped",
"limited",
"chaos"
]
},
"mixed": {
"type": "checkbox",
"default": true
},
"overworldflute": {
"type": "selectbox",
"default": "vanilla",
@@ -20,14 +35,6 @@
}
},
"rightOverworldFrame": {
"crossed": {
"type": "checkbox",
"default": false
},
"mixed": {
"type": "checkbox",
"default": true
},
"keepsimilar": {
"type": "checkbox",
"default": true

View File

@@ -18,8 +18,9 @@ def overworld_page(parent):
# These get split left & right
self.frames["leftOverworldFrame"] = Frame(self)
self.frames["rightOverworldFrame"] = Frame(self)
self.frames["leftOverworldFrame"].pack(side=LEFT, anchor=NW)
self.frames["rightOverworldFrame"].pack(anchor=NW)
self.frames["leftOverworldFrame"].pack(side=LEFT, anchor=NW, fill=Y)
self.frames["rightOverworldFrame"].pack(anchor=NW, fill=Y)
with open(os.path.join("resources","app","gui","randomize","overworld","widgets.json")) as overworldWidgets:
myDict = json.load(overworldWidgets)
@@ -27,9 +28,14 @@ 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 framename == "rightOverworldFrame":
self.widgets[key].pack(side=LEFT)
else:
self.widgets[key].pack(anchor=E)
packAttrs = {"anchor":E}
if key == "keepsimilar":
packAttrs = {"side":LEFT, "pady":(18,0)}
elif key == "overworldflute":
packAttrs["pady"] = (20,0)
elif key == "mixed":
packAttrs = {"anchor":W, "padx":(79,0)}
self.widgets[key].pack(packAttrs)
return self