Adding Customizer support for Crossed OWR
Also made Layout OWR compatible with Mixed OWR with asterisk notation
This commit is contained in:
@@ -87,6 +87,7 @@ class World(object):
|
|||||||
self.owedges = []
|
self.owedges = []
|
||||||
self._owedge_cache = {}
|
self._owedge_cache = {}
|
||||||
self.owswaps = {}
|
self.owswaps = {}
|
||||||
|
self.owcrossededges = {}
|
||||||
self.owwhirlpools = {}
|
self.owwhirlpools = {}
|
||||||
self.owflutespots = {}
|
self.owflutespots = {}
|
||||||
self.owsectors = {}
|
self.owsectors = {}
|
||||||
@@ -114,6 +115,7 @@ class World(object):
|
|||||||
set_player_attr('_region_cache', {})
|
set_player_attr('_region_cache', {})
|
||||||
set_player_attr('player_names', [])
|
set_player_attr('player_names', [])
|
||||||
set_player_attr('owswaps', [[],[],[]])
|
set_player_attr('owswaps', [[],[],[]])
|
||||||
|
set_player_attr('owcrossededges', [])
|
||||||
set_player_attr('owwhirlpools', [])
|
set_player_attr('owwhirlpools', [])
|
||||||
set_player_attr('owsectors', None)
|
set_player_attr('owsectors', None)
|
||||||
set_player_attr('remote_items', False)
|
set_player_attr('remote_items', False)
|
||||||
@@ -321,6 +323,16 @@ class World(object):
|
|||||||
if isinstance(edgename, OWEdge):
|
if isinstance(edgename, OWEdge):
|
||||||
return edgename
|
return edgename
|
||||||
try:
|
try:
|
||||||
|
if edgename[-1] == '*':
|
||||||
|
edgename = edgename[:-1]
|
||||||
|
edge = self.check_for_owedge(edgename, player)
|
||||||
|
if self.is_tile_swapped(edge.owIndex, player):
|
||||||
|
from OverworldShuffle import parallel_links
|
||||||
|
if edgename in parallel_links.keys() or edgename in parallel_links.inverse.keys():
|
||||||
|
edgename = parallel_links[edgename] if edgename in parallel_links.keys() else parallel_links.inverse[edgename][0]
|
||||||
|
return self.check_for_owedge(edgename, player)
|
||||||
|
else:
|
||||||
|
raise Exception("Edge notated with * doesn't have a parallel edge: %s" & edgename)
|
||||||
return self._owedge_cache[(edgename, player)]
|
return self._owedge_cache[(edgename, player)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
for edge in self.owedges:
|
for edge in self.owedges:
|
||||||
@@ -2248,7 +2260,7 @@ class OWEdge(object):
|
|||||||
self.unknownX = 0x0
|
self.unknownX = 0x0
|
||||||
self.unknownY = 0x0
|
self.unknownY = 0x0
|
||||||
|
|
||||||
if self.owIndex < 0x40 or self.owIndex >= 0x80:
|
if self.owIndex & 0x40 == 0:
|
||||||
self.worldType = WorldType.Light
|
self.worldType = WorldType.Light
|
||||||
else:
|
else:
|
||||||
self.worldType = WorldType.Dark
|
self.worldType = WorldType.Dark
|
||||||
@@ -2289,6 +2301,12 @@ class OWEdge(object):
|
|||||||
self.specialID = special_id
|
self.specialID = special_id
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def is_tile_swapped(self, world):
|
||||||
|
return world.is_tile_swapped(self.owIndex, self.player)
|
||||||
|
|
||||||
|
def is_lw(self, world):
|
||||||
|
return (self.worldType == WorldType.Light) != self.is_tile_swapped(world)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return isinstance(other, self.__class__) and self.name == other.name
|
return isinstance(other, self.__class__) and self.name == other.name
|
||||||
|
|
||||||
|
|||||||
@@ -205,20 +205,40 @@ def link_overworld(world, player):
|
|||||||
|
|
||||||
# crossed shuffle
|
# crossed shuffle
|
||||||
logging.getLogger('').debug('Crossing overworld edges')
|
logging.getLogger('').debug('Crossing overworld edges')
|
||||||
crossed_edges = list()
|
|
||||||
|
|
||||||
#TODO: Revisit with changes to Limited/Allowed
|
# customizer setup
|
||||||
if world.owCrossed[player] not in ['none', 'grouped', 'polar', 'chaos']:
|
force_crossed = set()
|
||||||
|
force_noncrossed = set()
|
||||||
|
count_crossed = 0
|
||||||
|
limited_crossed = 9 if world.owCrossed[player] == 'limited' else -1
|
||||||
|
if world.customizer:
|
||||||
|
custom_crossed = world.customizer.get_owcrossed()
|
||||||
|
if custom_crossed and player in custom_crossed:
|
||||||
|
custom_crossed = custom_crossed[player]
|
||||||
|
if 'force_crossed' in custom_crossed and len(custom_crossed['force_crossed']) > 0:
|
||||||
|
for edgename in custom_crossed['force_crossed']:
|
||||||
|
edge = world.check_for_owedge(edgename, player)
|
||||||
|
force_crossed.add(edge.name)
|
||||||
|
if 'force_noncrossed' in custom_crossed and len(custom_crossed['force_noncrossed']) > 0:
|
||||||
|
for edgename in custom_crossed['force_noncrossed']:
|
||||||
|
edge = world.check_for_owedge(edgename, player)
|
||||||
|
force_noncrossed.add(edge.name)
|
||||||
|
if 'limit_crossed' in custom_crossed:
|
||||||
|
limited_crossed = custom_crossed['limit_crossed']
|
||||||
|
|
||||||
|
if limited_crossed > -1:
|
||||||
|
# connect forced crossed non-parallel edges based on previously determined tile flips
|
||||||
for edge in swapped_edges:
|
for edge in swapped_edges:
|
||||||
crossed_edges.append(edge)
|
|
||||||
|
|
||||||
if world.owCrossed[player] in ['grouped', 'limited'] or (world.owShuffle[player] == 'vanilla' and world.owCrossed[player] == 'chaos'):
|
|
||||||
if edge not in parallel_links_new:
|
if edge not in parallel_links_new:
|
||||||
|
world.owcrossededges[player].append(edge)
|
||||||
|
count_crossed = count_crossed + 1
|
||||||
|
|
||||||
|
if world.owCrossed[player] == 'grouped' or (world.owShuffle[player] == 'vanilla' and world.owCrossed[player] == 'chaos') or limited_crossed > -1:
|
||||||
if world.owCrossed[player] == 'grouped':
|
if world.owCrossed[player] == 'grouped':
|
||||||
# the idea is to XOR the new flips with the ones from Mixed so that non-parallel edges still work
|
# the idea is to XOR the new flips with the ones from Mixed so that non-parallel edges still work
|
||||||
# Polar corresponds to Grouped with no flips in ow_crossed_tiles_mask
|
# Polar corresponds to Grouped with no flips in ow_crossed_tiles_mask
|
||||||
ow_crossed_tiles_mask = [[],[],[]]
|
ow_crossed_tiles_mask = [[],[],[]]
|
||||||
crossed_edges = shuffle_tiles(world, define_tile_groups(world, player, True), ow_crossed_tiles_mask, True, player)
|
world.owcrossededges[player] = shuffle_tiles(world, define_tile_groups(world, player, True), ow_crossed_tiles_mask, True, player)
|
||||||
ow_crossed_tiles = [i for i in range(0x82) if (i in world.owswaps[player][0]) != (i in ow_crossed_tiles_mask[0])]
|
ow_crossed_tiles = [i for i in range(0x82) if (i in world.owswaps[player][0]) != (i in ow_crossed_tiles_mask[0])]
|
||||||
|
|
||||||
# update spoiler
|
# update spoiler
|
||||||
@@ -244,32 +264,77 @@ def link_overworld(world, player):
|
|||||||
(mode, wrld, dir, terrain, parallel, count) = group
|
(mode, wrld, dir, terrain, parallel, count) = group
|
||||||
if wrld == WorldType.Light and mode != OpenStd.Standard:
|
if wrld == WorldType.Light and mode != OpenStd.Standard:
|
||||||
for (forward_set, back_set) in zip(trimmed_groups[group][0], trimmed_groups[group][1]):
|
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)
|
|
||||||
if forward_set[0] in parallel_links_new:
|
if forward_set[0] in parallel_links_new:
|
||||||
|
forward_parallel = [parallel_links_new[e] for e in forward_set]
|
||||||
|
back_parallel = [parallel_links_new[e] for e in back_set]
|
||||||
|
forward_combine = forward_set+forward_parallel
|
||||||
|
back_combine = back_set+back_parallel
|
||||||
|
combine_set = forward_combine+back_combine
|
||||||
|
|
||||||
|
skip_forward = False
|
||||||
|
if world.owShuffle[player] == 'vanilla':
|
||||||
|
if any(edge in force_crossed for edge in combine_set):
|
||||||
|
if not any(edge in force_noncrossed for edge in combine_set):
|
||||||
|
if any(edge in force_crossed for edge in forward_combine):
|
||||||
|
world.owcrossededges[player].extend(forward_set)
|
||||||
|
count_crossed = count_crossed + 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
world.owcrossededges[player].extend(back_set)
|
||||||
|
count_crossed = count_crossed + 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise GenerationException('Conflict detected in force_crossed and force_noncrossed')
|
||||||
|
if any(edge in list(force_noncrossed)+world.owcrossededges[player] for edge in combine_set):
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
for edge in forward_set:
|
skip_back = False
|
||||||
if world.owCrossed[player] == 'chaos' and random.randint(0, 1):
|
if any(edge in force_crossed for edge in forward_combine):
|
||||||
crossed_edges.append(edge)
|
if not any(edge in force_noncrossed for edge in forward_combine):
|
||||||
elif world.owCrossed[player] == 'limited':
|
world.owcrossededges[player].extend(forward_set)
|
||||||
crossed_candidates.append([edge])
|
count_crossed = count_crossed + 1
|
||||||
if world.owCrossed[player] == 'limited':
|
skip_forward = True
|
||||||
|
else:
|
||||||
|
raise GenerationException('Conflict detected in force_crossed and force_noncrossed')
|
||||||
|
if any(edge in force_crossed for edge in back_combine):
|
||||||
|
if not any(edge in force_noncrossed for edge in back_combine):
|
||||||
|
world.owcrossededges[player].extend(back_set)
|
||||||
|
count_crossed = count_crossed + 1
|
||||||
|
skip_back = True
|
||||||
|
else:
|
||||||
|
raise GenerationException('Conflict detected in force_crossed and force_noncrossed')
|
||||||
|
if any(edge in list(force_noncrossed)+world.owcrossededges[player] for edge in forward_combine):
|
||||||
|
skip_forward = True
|
||||||
|
if any(edge in list(force_noncrossed)+world.owcrossededges[player] for edge in back_combine):
|
||||||
|
skip_back = True
|
||||||
|
if not skip_back:
|
||||||
|
if limited_crossed > -1:
|
||||||
|
crossed_candidates.append(back_set)
|
||||||
|
elif random.randint(0, 1):
|
||||||
|
world.owcrossededges[player].extend(back_set)
|
||||||
|
count_crossed = count_crossed + 1
|
||||||
|
if not skip_forward:
|
||||||
|
if limited_crossed > -1:
|
||||||
|
crossed_candidates.append(forward_set)
|
||||||
|
elif random.randint(0, 1):
|
||||||
|
world.owcrossededges[player].extend(forward_set)
|
||||||
|
count_crossed = count_crossed + 1
|
||||||
|
assert len(world.owcrossededges[player]) == len(set(world.owcrossededges[player])), "Same edge added to crossed edges"
|
||||||
|
|
||||||
|
if limited_crossed > -1:
|
||||||
|
limit = limited_crossed - count_crossed
|
||||||
random.shuffle(crossed_candidates)
|
random.shuffle(crossed_candidates)
|
||||||
for edge_set in crossed_candidates[:9]:
|
for edge_set in crossed_candidates[:limit]:
|
||||||
for edge in edge_set:
|
world.owcrossededges[player].extend(edge_set)
|
||||||
crossed_edges.append(edge)
|
assert len(world.owcrossededges[player]) == len(set(world.owcrossededges[player])), "Same edge candidate added to crossed edges"
|
||||||
for edge in copy.deepcopy(crossed_edges):
|
|
||||||
|
for edge in copy.deepcopy(world.owcrossededges[player]):
|
||||||
if edge in parallel_links_new:
|
if edge in parallel_links_new:
|
||||||
crossed_edges.append(parallel_links_new[edge])
|
if parallel_links_new[edge] not in world.owcrossededges[player]:
|
||||||
elif edge in parallel_links_new.inverse:
|
world.owcrossededges[player].append(parallel_links_new[edge])
|
||||||
crossed_edges.append(parallel_links_new.inverse[edge][0])
|
|
||||||
|
|
||||||
# after tile flip and crossed, determine edges that need to flip
|
# after tile flip and crossed, determine edges that need to flip
|
||||||
edges_to_swap = [e for e in swapped_edges+crossed_edges if (e not in swapped_edges) or (e not in crossed_edges)]
|
edges_to_swap = [e for e in swapped_edges+world.owcrossededges[player] if (e not in swapped_edges) or (e not in world.owcrossededges[player])]
|
||||||
|
|
||||||
# whirlpool shuffle
|
# whirlpool shuffle
|
||||||
logging.getLogger('').debug('Shuffling whirlpools')
|
logging.getLogger('').debug('Shuffling whirlpools')
|
||||||
@@ -367,53 +432,142 @@ def link_overworld(world, player):
|
|||||||
# layout shuffle
|
# layout shuffle
|
||||||
groups = adjust_edge_groups(world, trimmed_groups, edges_to_swap, player)
|
groups = adjust_edge_groups(world, trimmed_groups, edges_to_swap, player)
|
||||||
|
|
||||||
connect_custom(world, connected_edges, groups, player)
|
connect_custom(world, connected_edges, groups, (force_crossed, force_noncrossed), player)
|
||||||
|
|
||||||
tries = 100
|
tries = 100
|
||||||
valid_layout = False
|
valid_layout = False
|
||||||
connected_edge_cache = connected_edges.copy()
|
connected_edge_cache = connected_edges.copy()
|
||||||
|
groups_cache = copy.deepcopy(groups)
|
||||||
while not valid_layout and tries > 0:
|
while not valid_layout and tries > 0:
|
||||||
|
def connect_set(forward_set, back_set, connected_edges):
|
||||||
|
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])
|
||||||
|
|
||||||
connected_edges = connected_edge_cache.copy()
|
connected_edges = connected_edge_cache.copy()
|
||||||
|
groups = copy.deepcopy(groups_cache)
|
||||||
|
groupKeys = list(groups.keys())
|
||||||
|
|
||||||
if world.mode[player] == 'standard':
|
if world.mode[player] == 'standard':
|
||||||
random.shuffle(groups[2:]) # keep first 2 groups (Standard) first
|
random.shuffle(groupKeys[2:]) # keep first 2 groups (Standard) first
|
||||||
else:
|
else:
|
||||||
random.shuffle(groups)
|
random.shuffle(groupKeys)
|
||||||
|
|
||||||
for (forward_edge_sets, back_edge_sets) in groups:
|
for key in groupKeys:
|
||||||
assert len(forward_edge_sets) == len(back_edge_sets)
|
(mode, wrld, dir, terrain, parallel, count) = key
|
||||||
|
(forward_edge_sets, back_edge_sets) = groups[key]
|
||||||
|
def remove_connected():
|
||||||
|
s = 0
|
||||||
|
while s < len(forward_edge_sets):
|
||||||
|
forward_set = forward_edge_sets[s]
|
||||||
|
if forward_set[0] in connected_edges:
|
||||||
|
del forward_edge_sets[s]
|
||||||
|
continue
|
||||||
|
s += 1
|
||||||
|
s = 0
|
||||||
|
while s < len(back_edge_sets):
|
||||||
|
back_set = back_edge_sets[s]
|
||||||
|
if back_set[0] in connected_edges:
|
||||||
|
del back_edge_sets[s]
|
||||||
|
continue
|
||||||
|
s += 1
|
||||||
|
assert len(forward_edge_sets) == len(back_edge_sets)
|
||||||
|
|
||||||
|
remove_connected()
|
||||||
random.shuffle(forward_edge_sets)
|
random.shuffle(forward_edge_sets)
|
||||||
random.shuffle(back_edge_sets)
|
random.shuffle(back_edge_sets)
|
||||||
if len(forward_edge_sets) > 0:
|
if wrld is None and len(force_crossed) + len(force_noncrossed) > 0:
|
||||||
f = 0
|
# divide forward/back sets into LW/DW
|
||||||
b = 0
|
forward_lw_sets, forward_dw_sets = [], []
|
||||||
while f < len(forward_edge_sets) and b < len(back_edge_sets):
|
back_lw_sets, back_dw_sets = [], []
|
||||||
forward_set = forward_edge_sets[f]
|
forward_parallel_lw_sets, forward_parallel_dw_sets = [], []
|
||||||
back_set = back_edge_sets[b]
|
back_parallel_lw_sets, back_parallel_dw_sets = [], []
|
||||||
while forward_set[0] in connected_edges:
|
|
||||||
f += 1
|
for edge_set in forward_edge_sets:
|
||||||
if f < len(forward_edge_sets):
|
if world.check_for_owedge(edge_set[0], player).is_lw(world):
|
||||||
forward_set = forward_edge_sets[f]
|
forward_lw_sets.append(edge_set)
|
||||||
|
if parallel == IsParallel.Yes:
|
||||||
|
forward_parallel_lw_sets.append([parallel_links_new[e] for e in edge_set])
|
||||||
|
else:
|
||||||
|
forward_dw_sets.append(edge_set)
|
||||||
|
if parallel == IsParallel.Yes:
|
||||||
|
forward_parallel_dw_sets.append([parallel_links_new[e] for e in edge_set])
|
||||||
|
for edge_set in back_edge_sets:
|
||||||
|
if world.check_for_owedge(edge_set[0], player).is_lw(world):
|
||||||
|
back_lw_sets.append(edge_set)
|
||||||
|
if parallel == IsParallel.Yes:
|
||||||
|
back_parallel_lw_sets.append([parallel_links_new[e] for e in edge_set])
|
||||||
|
else:
|
||||||
|
back_dw_sets.append(edge_set)
|
||||||
|
if parallel == IsParallel.Yes:
|
||||||
|
back_parallel_dw_sets.append([parallel_links_new[e] for e in edge_set])
|
||||||
|
|
||||||
|
crossed_sets = []
|
||||||
|
noncrossed_sets = []
|
||||||
|
def add_to_crossed_sets(sets, parallel_sets):
|
||||||
|
for i in range(0, len(sets)):
|
||||||
|
affected_edges = set(sets[i]+(parallel_sets[i] if parallel == IsParallel.Yes else []))
|
||||||
|
if sets[i] not in crossed_sets and len(set.intersection(set(force_crossed), affected_edges)) > 0:
|
||||||
|
crossed_sets.append(sets[i])
|
||||||
|
if sets not in noncrossed_sets and len(set.intersection(set(force_noncrossed), affected_edges)) > 0:
|
||||||
|
noncrossed_sets.append(sets[i])
|
||||||
|
if sets[i] in crossed_sets and sets[i] in noncrossed_sets:
|
||||||
|
raise GenerationException('Conflict in force crossed/non-crossed definition')
|
||||||
|
add_to_crossed_sets(forward_lw_sets, forward_parallel_lw_sets)
|
||||||
|
add_to_crossed_sets(forward_dw_sets, forward_parallel_dw_sets)
|
||||||
|
add_to_crossed_sets(back_lw_sets, back_parallel_lw_sets)
|
||||||
|
add_to_crossed_sets(back_dw_sets, back_parallel_dw_sets)
|
||||||
|
|
||||||
|
# random connect forced crossed/noncrossed
|
||||||
|
c = 0
|
||||||
|
while c < len(noncrossed_sets):
|
||||||
|
if noncrossed_sets[c] in forward_edge_sets:
|
||||||
|
forward_set = noncrossed_sets[c]
|
||||||
|
if forward_set in forward_lw_sets:
|
||||||
|
back_set = next(s for s in back_lw_sets if s in back_edge_sets and s not in crossed_sets)
|
||||||
else:
|
else:
|
||||||
forward_set = None
|
back_set = next(s for s in back_dw_sets if s in back_edge_sets and s not in crossed_sets)
|
||||||
break
|
elif noncrossed_sets[c] in back_edge_sets:
|
||||||
f += 1
|
back_set = noncrossed_sets[c]
|
||||||
while back_set[0] in connected_edges:
|
if back_set in back_lw_sets:
|
||||||
b += 1
|
forward_set = next(s for s in forward_lw_sets if s in forward_edge_sets and s not in crossed_sets)
|
||||||
if b < len(back_edge_sets):
|
|
||||||
back_set = back_edge_sets[b]
|
|
||||||
else:
|
else:
|
||||||
back_set = None
|
forward_set = next(s for s in forward_dw_sets if s in forward_edge_sets and s not in crossed_sets)
|
||||||
break
|
else:
|
||||||
b += 1
|
c = c + 1
|
||||||
if forward_set is not None and back_set is not None:
|
continue
|
||||||
assert len(forward_set) == len(back_set)
|
connect_set(forward_set, back_set, connected_edges)
|
||||||
for (forward_edge, back_edge) in zip(forward_set, back_set):
|
remove_connected()
|
||||||
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
c = c + 1
|
||||||
elif forward_set is not None:
|
c = 0
|
||||||
logging.getLogger('').warning("Edge '%s' could not find a valid connection" % forward_set[0])
|
while c < len(crossed_sets):
|
||||||
elif back_set is not None:
|
if crossed_sets[c] in forward_edge_sets:
|
||||||
logging.getLogger('').warning("Edge '%s' could not find a valid connection" % back_set[0])
|
forward_set = crossed_sets[c]
|
||||||
|
if forward_set in forward_lw_sets:
|
||||||
|
back_set = next(s for s in back_dw_sets if s in back_edge_sets)
|
||||||
|
else:
|
||||||
|
back_set = next(s for s in back_lw_sets if s in back_edge_sets)
|
||||||
|
elif crossed_sets[c] in back_edge_sets:
|
||||||
|
back_set = crossed_sets[c]
|
||||||
|
if back_set in back_lw_sets:
|
||||||
|
forward_set = next(s for s in forward_dw_sets if s in forward_edge_sets)
|
||||||
|
else:
|
||||||
|
forward_set = next(s for s in forward_lw_sets if s in forward_edge_sets)
|
||||||
|
else:
|
||||||
|
c = c + 1
|
||||||
|
continue
|
||||||
|
connect_set(forward_set, back_set, connected_edges)
|
||||||
|
remove_connected()
|
||||||
|
c = c + 1
|
||||||
|
|
||||||
|
while len(forward_edge_sets) > 0 and len(back_edge_sets) > 0:
|
||||||
|
connect_set(forward_edge_sets[0], back_edge_sets[0], connected_edges)
|
||||||
|
remove_connected()
|
||||||
assert len(connected_edges) == len(default_connections) * 2, connected_edges
|
assert len(connected_edges) == len(default_connections) * 2, connected_edges
|
||||||
|
|
||||||
world.owsectors[player] = build_sectors(world, player)
|
world.owsectors[player] = build_sectors(world, player)
|
||||||
@@ -583,8 +737,9 @@ def link_overworld(world, player):
|
|||||||
s[0x3a],s[0x3b],s[0x3c], s[0x3f])
|
s[0x3a],s[0x3b],s[0x3c], s[0x3f])
|
||||||
world.spoiler.set_map('flute', text_output, new_spots, player)
|
world.spoiler.set_map('flute', text_output, new_spots, player)
|
||||||
|
|
||||||
def connect_custom(world, connected_edges, groups, player):
|
def connect_custom(world, connected_edges, groups, forced, player):
|
||||||
def remove_pair_from_pool(edgename1, edgename2):
|
forced_crossed, forced_noncrossed = forced
|
||||||
|
def remove_pair_from_pool(edgename1, edgename2, is_crossed):
|
||||||
def add_to_unresolved(forward_set, back_set):
|
def add_to_unresolved(forward_set, back_set):
|
||||||
if len(forward_set) > 1:
|
if len(forward_set) > 1:
|
||||||
if edgename1 in forward_set:
|
if edgename1 in forward_set:
|
||||||
@@ -593,8 +748,8 @@ def connect_custom(world, connected_edges, groups, player):
|
|||||||
else:
|
else:
|
||||||
back_set.remove(edgename1)
|
back_set.remove(edgename1)
|
||||||
forward_set.remove(edgename2)
|
forward_set.remove(edgename2)
|
||||||
unresolved_similars.append(tuple((forward_set, back_set)))
|
unresolved_similars.append(tuple((forward_set, back_set, is_crossed)))
|
||||||
for forward_pool, back_pool in groups:
|
for forward_pool, back_pool in groups.values():
|
||||||
if not len(forward_pool):
|
if not len(forward_pool):
|
||||||
continue
|
continue
|
||||||
if len(forward_pool[0]) == 1:
|
if len(forward_pool[0]) == 1:
|
||||||
@@ -635,7 +790,7 @@ def connect_custom(world, connected_edges, groups, player):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
for pair in unresolved_similars:
|
for pair in unresolved_similars:
|
||||||
forward_set, back_set = pair
|
forward_set, back_set, _ = pair
|
||||||
if edgename1 in forward_set:
|
if edgename1 in forward_set:
|
||||||
if edgename2 in back_set:
|
if edgename2 in back_set:
|
||||||
unresolved_similars.remove(pair)
|
unresolved_similars.remove(pair)
|
||||||
@@ -659,24 +814,45 @@ def connect_custom(world, connected_edges, groups, player):
|
|||||||
custom_edges = custom_edges[player]
|
custom_edges = custom_edges[player]
|
||||||
if 'two-way' in custom_edges:
|
if 'two-way' in custom_edges:
|
||||||
unresolved_similars = []
|
unresolved_similars = []
|
||||||
|
def validate_crossed_allowed(edge1, edge2, is_crossed):
|
||||||
|
return not ((not is_crossed and (edge1 in forced_crossed or edge2 in forced_crossed))
|
||||||
|
or (is_crossed and (edge1 in forced_noncrossed or edge2 in forced_noncrossed)))
|
||||||
for edgename1, edgename2 in custom_edges['two-way'].items():
|
for edgename1, edgename2 in custom_edges['two-way'].items():
|
||||||
edge1 = world.check_for_owedge(edgename1, player)
|
edge1 = world.check_for_owedge(edgename1, player)
|
||||||
edge2 = world.check_for_owedge(edgename2, player)
|
edge2 = world.check_for_owedge(edgename2, player)
|
||||||
if edgename1 not in connected_edges and edgename2 not in connected_edges:
|
is_crossed = edge1.is_lw(world) != edge2.is_lw(world)
|
||||||
|
if not validate_crossed_allowed(edge1.name, edge2.name, is_crossed):
|
||||||
|
if edgename2[-1] == '*':
|
||||||
|
edge2 = world.check_for_owedge(edge2.name + '*', player)
|
||||||
|
is_crossed = not is_crossed
|
||||||
|
else:
|
||||||
|
raise GenerationException('Violation of force crossed rules: \'%s\' <-> \'%s\'', edgename1, edgename2)
|
||||||
|
if edge1.name not in connected_edges and edge2.name not in connected_edges:
|
||||||
# attempt connection
|
# attempt connection
|
||||||
remove_pair_from_pool(edgename1, edgename2)
|
remove_pair_from_pool(edge1.name, edge2.name, is_crossed)
|
||||||
connect_two_way(world, edgename1, edgename2, player, connected_edges)
|
connect_two_way(world, edge1.name, edge2.name, player, connected_edges)
|
||||||
# resolve parallel
|
# resolve parallel
|
||||||
remove_pair_from_pool(parallel_forward_edge, parallel_back_edge)
|
|
||||||
elif not edge1.dest or not edge2.dest or edge1.dest.name != edgename2 or edge2.dest.name != edgename1:
|
|
||||||
if world.owShuffle[player] == 'parallel' and edge1.name in parallel_links_new:
|
if world.owShuffle[player] == 'parallel' and edge1.name in parallel_links_new:
|
||||||
parallel_forward_edge = parallel_links_new[edge1.name]
|
parallel_forward_edge = parallel_links_new[edge1.name]
|
||||||
parallel_back_edge = parallel_links_new[edge2.name]
|
parallel_back_edge = parallel_links_new[edge2.name]
|
||||||
|
if validate_crossed_allowed(parallel_forward_edge, parallel_back_edge, is_crossed):
|
||||||
|
remove_pair_from_pool(parallel_forward_edge, parallel_back_edge, is_crossed)
|
||||||
|
else:
|
||||||
|
raise GenerationException('Violation of force crossed rules on parallel connection: \'%s\' <-> \'%s\'', edgename1, edgename2)
|
||||||
|
elif not edge1.dest or not edge2.dest or edge1.dest.name != edge2.name or edge2.dest.name != edge1.name:
|
||||||
raise GenerationException('OW Edge already connected: \'%s\' <-> \'%s\'', edgename1, edgename2)
|
raise GenerationException('OW Edge already connected: \'%s\' <-> \'%s\'', edgename1, edgename2)
|
||||||
# connect leftover similars
|
# connect leftover similars
|
||||||
for forward_pool, back_pool in unresolved_similars:
|
for forward_pool, back_pool, is_crossed in unresolved_similars:
|
||||||
for (forward_edge, back_edge) in zip(forward_pool, back_pool):
|
for (forward_edge, back_edge) in zip(forward_pool, back_pool):
|
||||||
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
if validate_crossed_allowed(forward_edge, back_edge, is_crossed):
|
||||||
|
connect_two_way(world, forward_edge, back_edge, player, connected_edges)
|
||||||
|
else:
|
||||||
|
raise GenerationException('Violation of force crossed rules on unresolved similars: \'%s\' <-> \'%s\'', forward_edge, back_edge)
|
||||||
|
if world.owShuffle[player] == 'parallel' and forward_edge in parallel_links_new:
|
||||||
|
parallel_forward_edge = parallel_links_new[forward_edge]
|
||||||
|
parallel_back_edge = parallel_links_new[back_edge]
|
||||||
|
if not validate_crossed_allowed(parallel_forward_edge, parallel_back_edge, is_crossed):
|
||||||
|
raise GenerationException('Violation of force crossed rules on parallel unresolved similars: \'%s\' <-> \'%s\'', forward_edge, back_edge)
|
||||||
|
|
||||||
def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
|
def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
|
||||||
edge1 = world.get_entrance(edgename1, player)
|
edge1 = world.get_entrance(edgename1, player)
|
||||||
@@ -1054,12 +1230,16 @@ def reorganize_groups(world, groups, player):
|
|||||||
|
|
||||||
def adjust_edge_groups(world, trimmed_groups, edges_to_swap, player):
|
def adjust_edge_groups(world, trimmed_groups, edges_to_swap, player):
|
||||||
groups = defaultdict(lambda: ([],[]))
|
groups = defaultdict(lambda: ([],[]))
|
||||||
|
limited_crossed = False
|
||||||
|
if world.customizer:
|
||||||
|
custom_crossed = world.customizer.get_owcrossed()
|
||||||
|
limited_crossed = custom_crossed and (player in custom_crossed) and ('limit_crossed' in custom_crossed[player])
|
||||||
for (key, group) in trimmed_groups.items():
|
for (key, group) in trimmed_groups.items():
|
||||||
(mode, wrld, dir, terrain, parallel, count) = key
|
(mode, wrld, dir, terrain, parallel, count) = key
|
||||||
if mode == OpenStd.Standard:
|
if mode == OpenStd.Standard:
|
||||||
groups[key] = group
|
groups[key] = group
|
||||||
else:
|
else:
|
||||||
if world.owCrossed[player] == 'chaos':
|
if world.owCrossed[player] == 'chaos' and not limited_crossed:
|
||||||
groups[(mode, None, dir, terrain, parallel, count)][0].extend(group[0])
|
groups[(mode, None, dir, terrain, parallel, count)][0].extend(group[0])
|
||||||
groups[(mode, None, dir, terrain, parallel, count)][1].extend(group[1])
|
groups[(mode, None, dir, terrain, parallel, count)][1].extend(group[1])
|
||||||
else:
|
else:
|
||||||
@@ -1069,7 +1249,7 @@ def adjust_edge_groups(world, trimmed_groups, edges_to_swap, player):
|
|||||||
if edge_set[0] in edges_to_swap:
|
if edge_set[0] in edges_to_swap:
|
||||||
new_world += 1
|
new_world += 1
|
||||||
groups[(mode, WorldType(new_world % 2), dir, terrain, parallel, count)][i].append(edge_set)
|
groups[(mode, WorldType(new_world % 2), dir, terrain, parallel, count)][i].append(edge_set)
|
||||||
return list(groups.values())
|
return groups
|
||||||
|
|
||||||
def create_flute_exits(world, player):
|
def create_flute_exits(world, player):
|
||||||
flute_in_pool = True if player not in world.customitemarray else any(i for i, n in world.customitemarray[player].items() if i == 'flute' and n > 0)
|
flute_in_pool = True if player not in world.customitemarray else any(i for i, n in world.customitemarray[player].items() if i == 'flute' and n > 0)
|
||||||
|
|||||||
@@ -69,6 +69,24 @@ placements:
|
|||||||
Palace of Darkness - Big Chest: Hammer
|
Palace of Darkness - Big Chest: Hammer
|
||||||
Capacity Upgrade - Left: Moon Pearl
|
Capacity Upgrade - Left: Moon Pearl
|
||||||
Turtle Rock - Pokey 2 Key Drop: Ice Rod
|
Turtle Rock - Pokey 2 Key Drop: Ice Rod
|
||||||
|
ow-edges:
|
||||||
|
1:
|
||||||
|
two-way:
|
||||||
|
Kakariko Fortune ES*: Sanctuary WN*
|
||||||
|
Central Bonk Rocks EC: Potion Shop WN
|
||||||
|
Central Bonk Rocks ES: Potion Shop WC
|
||||||
|
ow-crossed:
|
||||||
|
1:
|
||||||
|
force_crossed:
|
||||||
|
- Links House ES*
|
||||||
|
- Kakariko Fortune ES*
|
||||||
|
force_noncrossed:
|
||||||
|
- Links House NE
|
||||||
|
limit_crossed: 9 # emulates Limited Crossed
|
||||||
|
ow-whirlpools:
|
||||||
|
1:
|
||||||
|
two-way:
|
||||||
|
River Bend Whirlpool: Lake Hylia Whirlpool
|
||||||
ow-tileflips:
|
ow-tileflips:
|
||||||
1:
|
1:
|
||||||
force_flip:
|
force_flip:
|
||||||
|
|||||||
@@ -201,6 +201,11 @@ class CustomSettings(object):
|
|||||||
return self.file_source['ow-edges']
|
return self.file_source['ow-edges']
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_owcrossed(self):
|
||||||
|
if 'ow-crossed' in self.file_source:
|
||||||
|
return self.file_source['ow-crossed']
|
||||||
|
return None
|
||||||
|
|
||||||
def get_whirlpools(self):
|
def get_whirlpools(self):
|
||||||
if 'ow-whirlpools' in self.file_source:
|
if 'ow-whirlpools' in self.file_source:
|
||||||
return self.file_source['ow-whirlpools']
|
return self.file_source['ow-whirlpools']
|
||||||
|
|||||||
Reference in New Issue
Block a user