Implemented Whirlpool Shuffle

This commit is contained in:
codemann8
2021-10-27 01:19:01 -05:00
parent 32d6a55838
commit 08ab32537b
15 changed files with 225 additions and 86 deletions

View File

@@ -26,6 +26,7 @@ class World(object):
self.owCrossed = owCrossed.copy() self.owCrossed = owCrossed.copy()
self.owKeepSimilar = {} self.owKeepSimilar = {}
self.owMixed = owMixed.copy() self.owMixed = owMixed.copy()
self.owWhirlpoolShuffle = {}
self.owFluteShuffle = {} self.owFluteShuffle = {}
self.shuffle = shuffle.copy() self.shuffle = shuffle.copy()
self.doorShuffle = doorShuffle.copy() self.doorShuffle = doorShuffle.copy()
@@ -76,6 +77,7 @@ class World(object):
self.spoiler = Spoiler(self) self.spoiler = Spoiler(self)
self.lamps_needed_for_dark_rooms = 1 self.lamps_needed_for_dark_rooms = 1
self.owswaps = {} self.owswaps = {}
self.owwhirlpools = {}
self.owedges = [] self.owedges = []
self._owedge_cache = {} self._owedge_cache = {}
self.owflutespots = {} self.owflutespots = {}
@@ -105,6 +107,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('owwhirlpools', [])
set_player_attr('remote_items', False) set_player_attr('remote_items', False)
set_player_attr('required_medallions', ['Ether', 'Quake']) set_player_attr('required_medallions', ['Ether', 'Quake'])
set_player_attr('swamp_patch_required', False) set_player_attr('swamp_patch_required', False)
@@ -2693,6 +2696,7 @@ class Spoiler(object):
'ow_crossed': self.world.owCrossed, 'ow_crossed': self.world.owCrossed,
'ow_keepsimilar': self.world.owKeepSimilar, 'ow_keepsimilar': self.world.owKeepSimilar,
'ow_mixed': self.world.owMixed, 'ow_mixed': self.world.owMixed,
'ow_whirlpool': self.world.owWhirlpoolShuffle,
'ow_fluteshuffle': self.world.owFluteShuffle, 'ow_fluteshuffle': self.world.owFluteShuffle,
'shuffle': self.world.shuffle, 'shuffle': self.world.shuffle,
'shuffleganon': self.world.shuffle_ganon, 'shuffleganon': self.world.shuffle_ganon,
@@ -2784,6 +2788,7 @@ class Spoiler(object):
outfile.write('Keep Similar OW Edges Together:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_keepsimilar'][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('Crossed OW:'.ljust(line_width) + '%s\n' % self.metadata['ow_crossed'][player]) outfile.write('Crossed OW:'.ljust(line_width) + '%s\n' % self.metadata['ow_crossed'][player])
outfile.write('Swapped OW (Mixed):'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_mixed'][player] else 'No')) outfile.write('Swapped OW (Mixed):'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_mixed'][player] else 'No'))
outfile.write('Whirlpool Shuffle:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_whirlpool'][player] else 'No'))
outfile.write('Flute Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_fluteshuffle'][player]) 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('Entrance Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['shuffle'][player])
outfile.write('Shuffle GT/Ganon:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['shuffleganon'][player] else 'No')) outfile.write('Shuffle GT/Ganon:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['shuffleganon'][player] else 'No'))
@@ -2807,6 +2812,7 @@ class Spoiler(object):
if self.startinventory: if self.startinventory:
outfile.write('Starting Inventory:'.ljust(line_width)) outfile.write('Starting Inventory:'.ljust(line_width))
outfile.write('\n'.ljust(line_width+1).join(self.startinventory)) outfile.write('\n'.ljust(line_width+1).join(self.startinventory))
outfile.write('\n\nRequirements:\n\n') outfile.write('\n\nRequirements:\n\n')
for dungeon, medallion in self.medallions.items(): for dungeon, medallion in self.medallions.items():
outfile.write(f'{dungeon}:'.ljust(line_width) + '%s Medallion\n' % medallion) outfile.write(f'{dungeon}:'.ljust(line_width) + '%s Medallion\n' % medallion)

3
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) playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'ow_shuffle', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_fluteshuffle', 'ow_shuffle', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'bombbag', 'shuffleganon', 'bombbag', 'shuffleganon',
@@ -149,6 +149,7 @@ def parse_settings():
"ow_crossed": "none", "ow_crossed": "none",
"ow_keepsimilar": False, "ow_keepsimilar": False,
"ow_mixed": False, "ow_mixed": False,
"ow_whirlpool": False,
"ow_fluteshuffle": "vanilla", "ow_fluteshuffle": "vanilla",
"shuffle": "vanilla", "shuffle": "vanilla",
"shufflelinks": False, "shufflelinks": False,

View File

@@ -87,6 +87,7 @@ def main(args, seed=None, fish=None):
world.crystals_ganon_orig = args.crystals_ganon.copy() world.crystals_ganon_orig = args.crystals_ganon.copy()
world.crystals_gt_orig = args.crystals_gt.copy() world.crystals_gt_orig = args.crystals_gt.copy()
world.owKeepSimilar = args.ow_keepsimilar.copy() world.owKeepSimilar = args.ow_keepsimilar.copy()
world.owWhirlpoolShuffle = args.ow_whirlpool.copy()
world.owFluteShuffle = args.ow_fluteshuffle.copy() world.owFluteShuffle = args.ow_fluteshuffle.copy()
world.open_pyramid = args.openpyramid.copy() world.open_pyramid = args.openpyramid.copy()
world.boss_shuffle = args.shufflebosses.copy() world.boss_shuffle = args.shufflebosses.copy()
@@ -406,6 +407,7 @@ def copy_world(world):
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()
ret.crystals_gt_orig = world.crystals_gt_orig.copy() ret.crystals_gt_orig = world.crystals_gt_orig.copy()
ret.owKeepSimilar = world.owKeepSimilar.copy() ret.owKeepSimilar = world.owKeepSimilar.copy()
ret.owWhirlpoolShuffle = world.owWhirlpoolShuffle.copy()
ret.owFluteShuffle = world.owFluteShuffle.copy() ret.owFluteShuffle = world.owFluteShuffle.copy()
ret.open_pyramid = world.open_pyramid.copy() ret.open_pyramid = world.open_pyramid.copy()
ret.boss_shuffle = world.boss_shuffle.copy() ret.boss_shuffle = world.boss_shuffle.copy()

View File

@@ -138,6 +138,7 @@ def roll_settings(weights):
ret.ow_crossed = get_choice('overworld_crossed') ret.ow_crossed = get_choice('overworld_crossed')
ret.ow_keepsimilar = get_choice('overworld_keepsimilar') == 'on' ret.ow_keepsimilar = get_choice('overworld_keepsimilar') == 'on'
ret.ow_mixed = get_choice('overworld_swap') == 'on' ret.ow_mixed = get_choice('overworld_swap') == 'on'
ret.ow_whirlpool = get_choice('whirlpool_shuffle') == 'on'
overworld_flute = get_choice('flute_shuffle') overworld_flute = get_choice('flute_shuffle')
ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla' ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla'
entrance_shuffle = get_choice('entrance_shuffle') entrance_shuffle = get_choice('entrance_shuffle')

View File

@@ -969,7 +969,7 @@ OWTileRegions = bidict({
}) })
OWTileGroups = { OWTileGroups = {
("Woods", "Regular"): ( ("Woods", "Regular", "None"): (
[ [
0x00, 0x2d, 0x80 0x00, 0x2d, 0x80
], ],
@@ -977,7 +977,7 @@ OWTileGroups = {
0x40, 0x6d 0x40, 0x6d
] ]
), ),
("Lumberjack", "Regular"): ( ("Lumberjack", "Regular", "None"): (
[ [
0x02 0x02
], ],
@@ -985,7 +985,7 @@ OWTileGroups = {
0x42 0x42
] ]
), ),
("West Mountain", "Regular"): ( ("West Mountain", "Regular", "None"): (
[ [
0x03 0x03
], ],
@@ -993,7 +993,7 @@ OWTileGroups = {
0x43 0x43
] ]
), ),
("East Mountain", "Regular"): ( ("East Mountain", "Regular", "None"): (
[ [
0x05 0x05
], ],
@@ -1001,23 +1001,31 @@ OWTileGroups = {
0x45 0x45
] ]
), ),
("East Mountain", "Entrance"): ( ("East Mountain", "Entrance", "None"): (
[ [
0x07, 0x07
], ],
[ [
0x47 0x47
] ]
), ),
("Lake", "Regular"): ( ("Lake", "Regular", "Zora"): (
[ [
0x0f, 0x35, 0x81 0x0f, 0x81
], ],
[ [
0x4f, 0x75 0x4f
] ]
), ),
("Mountain Entry", "Regular"): ( ("Lake", "Regular", "Lake"): (
[
0x35
],
[
0x75
]
),
("Mountain Entry", "Regular", "None"): (
[ [
0x0a 0x0a
], ],
@@ -1025,7 +1033,7 @@ OWTileGroups = {
0x4a 0x4a
] ]
), ),
("Woods Pass", "Regular"): ( ("Woods Pass", "Regular", "None"): (
[ [
0x10 0x10
], ],
@@ -1033,7 +1041,7 @@ OWTileGroups = {
0x50 0x50
] ]
), ),
("Fortune", "Regular"): ( ("Fortune", "Regular", "None"): (
[ [
0x11 0x11
], ],
@@ -1041,15 +1049,39 @@ OWTileGroups = {
0x51 0x51
] ]
), ),
("Whirlpools", "Regular"): ( ("Whirlpools", "Regular", "Pond"): (
[ [
0x12, 0x15, 0x33, 0x3f 0x12
], ],
[ [
0x52, 0x55, 0x73, 0x7f 0x52
] ]
), ),
("Castle", "Entrance"): ( ("Whirlpools", "Regular", "Witch"): (
[
0x15
],
[
0x55
]
),
("Whirlpools", "Regular", "CWhirlpool"): (
[
0x33
],
[
0x73
]
),
("Whirlpools", "Regular", "Southeast"): (
[
0x3f
],
[
0x7f
]
),
("Castle", "Entrance", "None"): (
[ [
0x13, 0x14 0x13, 0x14
], ],
@@ -1057,7 +1089,7 @@ OWTileGroups = {
0x53, 0x54 0x53, 0x54
] ]
), ),
("Castle", "Regular"): ( ("Castle", "Regular", "None"): (
[ [
0x1a, 0x1b 0x1a, 0x1b
], ],
@@ -1065,7 +1097,7 @@ OWTileGroups = {
0x5a, 0x5b 0x5a, 0x5b
] ]
), ),
("Witch", "Regular"): ( ("Witch", "Regular", "None"): (
[ [
0x16 0x16
], ],
@@ -1073,7 +1105,7 @@ OWTileGroups = {
0x56 0x56
] ]
), ),
("Water Approach", "Regular"): ( ("Water Approach", "Regular", "None"): (
[ [
0x17 0x17
], ],
@@ -1081,7 +1113,7 @@ OWTileGroups = {
0x57 0x57
] ]
), ),
("Village", "Regular"): ( ("Village", "Regular", "None"): (
[ [
0x18 0x18
], ],
@@ -1089,7 +1121,7 @@ OWTileGroups = {
0x58 0x58
] ]
), ),
("Wooden Bridge", "Regular"): ( ("Wooden Bridge", "Regular", "None"): (
[ [
0x1d 0x1d
], ],
@@ -1097,7 +1129,7 @@ OWTileGroups = {
0x5d 0x5d
] ]
), ),
("Eastern", "Regular"): ( ("Eastern", "Regular", "None"): (
[ [
0x1e 0x1e
], ],
@@ -1105,7 +1137,7 @@ OWTileGroups = {
0x5e 0x5e
] ]
), ),
("Blacksmith", "Regular"): ( ("Blacksmith", "Regular", "None"): (
[ [
0x22 0x22
], ],
@@ -1113,7 +1145,7 @@ OWTileGroups = {
0x62 0x62
] ]
), ),
("Dunes", "Regular"): ( ("Dunes", "Regular", "None"): (
[ [
0x25 0x25
], ],
@@ -1121,7 +1153,7 @@ OWTileGroups = {
0x65 0x65
] ]
), ),
("Game", "Regular"): ( ("Game", "Regular", "None"): (
[ [
0x28, 0x29 0x28, 0x29
], ],
@@ -1129,7 +1161,7 @@ OWTileGroups = {
0x68, 0x69 0x68, 0x69
] ]
), ),
("Grove", "Regular"): ( ("Grove", "Regular", "None"): (
[ [
0x2a 0x2a
], ],
@@ -1137,7 +1169,7 @@ OWTileGroups = {
0x6a 0x6a
] ]
), ),
("Central Bonk Rocks", "Regular"): ( ("Central Bonk Rocks", "Regular", "None"): (
[ [
0x2b 0x2b
], ],
@@ -1145,7 +1177,7 @@ OWTileGroups = {
0x6b 0x6b
] ]
), ),
# ("Links", "Regular"): ( # ("Links", "Regular", "None"): (
# [ # [
# 0x2c # 0x2c
# ], # ],
@@ -1153,7 +1185,7 @@ OWTileGroups = {
# 0x6c # 0x6c
# ] # ]
# ), # ),
("Tree Line", "Regular"): ( ("Tree Line", "Regular", "None"): (
[ [
0x2e 0x2e
], ],
@@ -1161,7 +1193,7 @@ OWTileGroups = {
0x6e 0x6e
] ]
), ),
("Nook", "Regular"): ( ("Nook", "Regular", "None"): (
[ [
0x2f 0x2f
], ],
@@ -1169,7 +1201,7 @@ OWTileGroups = {
0x6f 0x6f
] ]
), ),
("Desert", "Regular"): ( ("Desert", "Regular", "None"): (
[ [
0x30, 0x3a 0x30, 0x3a
], ],
@@ -1177,7 +1209,7 @@ OWTileGroups = {
0x70, 0x7a 0x70, 0x7a
] ]
), ),
("Grove Approach", "Regular"): ( ("Grove Approach", "Regular", "None"): (
[ [
0x32 0x32
], ],
@@ -1185,7 +1217,7 @@ OWTileGroups = {
0x72 0x72
] ]
), ),
("Hype", "Regular"): ( ("Hype", "Regular", "None"): (
[ [
0x34 0x34
], ],
@@ -1193,7 +1225,7 @@ OWTileGroups = {
0x74 0x74
] ]
), ),
("Shopping Mall", "Regular"): ( ("Shopping Mall", "Regular", "None"): (
[ [
0x37 0x37
], ],
@@ -1201,7 +1233,7 @@ OWTileGroups = {
0x77 0x77
] ]
), ),
("Swamp", "Regular"): ( ("Swamp", "Regular", "None"): (
[ [
0x3b 0x3b
], ],
@@ -1209,7 +1241,7 @@ OWTileGroups = {
0x7b 0x7b
] ]
), ),
("South Pass", "Regular"): ( ("South Pass", "Regular", "None"): (
[ [
0x3c 0x3c
], ],

View File

@@ -187,6 +187,44 @@ def link_overworld(world, player):
trimmed_groups = performSwap(trimmed_groups, crossed_edges) trimmed_groups = performSwap(trimmed_groups, crossed_edges)
assert len(crossed_edges) == 0, 'Not all edges were crossed successfully: ' + ', '.join(crossed_edges) assert len(crossed_edges) == 0, 'Not all edges were crossed successfully: ' + ', '.join(crossed_edges)
# whirlpool shuffle
logging.getLogger('').debug('Shuffling whirlpools')
if not world.owWhirlpoolShuffle[player]:
for (_, from_whirlpool, from_region), (_, to_whirlpool, to_region) in default_whirlpool_connections:
connect_simple(world, from_whirlpool, to_region, player)
connect_simple(world, to_whirlpool, from_region, player)
else:
whirlpool_candidates = [[],[]]
for (from_owid, from_whirlpool, from_region), (to_owid, to_whirlpool, to_region) in default_whirlpool_connections:
if world.owCrossed[player] != 'none':
whirlpool_candidates[0].append(tuple((from_owid, from_whirlpool, from_region)))
whirlpool_candidates[0].append(tuple((to_owid, to_whirlpool, to_region)))
else:
if world.get_region(from_region, player).type == RegionType.LightWorld:
whirlpool_candidates[0].append(tuple((from_owid, from_whirlpool, from_region)))
else:
whirlpool_candidates[1].append(tuple((from_owid, from_whirlpool, from_region)))
if world.get_region(to_region, player).type == RegionType.LightWorld:
whirlpool_candidates[0].append(tuple((to_owid, to_whirlpool, to_region)))
else:
whirlpool_candidates[1].append(tuple((to_owid, to_whirlpool, to_region)))
# shuffle happens here
world.owwhirlpools[player] = [None] * 8
whirlpool_map = [ 0x35, 0x0f, 0x15, 0x33, 0x12, 0x3f, 0x55, 0x7f ]
for whirlpools in whirlpool_candidates:
random.shuffle(whirlpools)
while len(whirlpools):
from_owid, from_whirlpool, from_region = whirlpools.pop()
to_owid, to_whirlpool, to_region = whirlpools.pop()
connect_simple(world, from_whirlpool, to_region, player)
connect_simple(world, to_whirlpool, from_region, player)
world.owwhirlpools[player][next(i for i, v in enumerate(whirlpool_map) if v == to_owid)] = from_owid
world.owwhirlpools[player][next(i for i, v in enumerate(whirlpool_map) if v == from_owid)] = to_owid
world.spoiler.set_overworld(from_whirlpool, to_whirlpool, 'both', player)
# layout shuffle # layout shuffle
logging.getLogger('').debug('Shuffling overworld layout') logging.getLogger('').debug('Shuffling overworld layout')
connected_edges = [] connected_edges = []
@@ -387,22 +425,33 @@ def connect_two_way(world, edgename1, edgename2, player, connected_edges=None):
def shuffle_tiles(world, groups, result_list, player): def shuffle_tiles(world, groups, result_list, player):
swapped_edges = list() swapped_edges = list()
valid_whirlpool_parity = False
# tile shuffle happens here while not valid_whirlpool_parity:
removed = list() # tile shuffle happens here
for group in groups.keys(): removed = list()
if random.randint(0, 1): for group in groups.keys():
removed.append(group) # if group[0] in ['Links', 'Central Bonk Rocks', 'Castle']: # TODO: Standard + Inverted
if random.randint(0, 1):
removed.append(group)
# save shuffled tiles to list # save shuffled tiles to list
for group in groups.keys(): new_results = [[],[],[]]
if group not in removed: for group in groups.keys():
(owids, lw_regions, dw_regions) = groups[group] if group not in removed:
(exist_owids, exist_lw_regions, exist_dw_regions) = result_list (owids, lw_regions, dw_regions) = groups[group]
exist_owids.extend(owids) (exist_owids, exist_lw_regions, exist_dw_regions) = new_results
exist_lw_regions.extend(lw_regions) exist_owids.extend(owids)
exist_dw_regions.extend(dw_regions) exist_lw_regions.extend(lw_regions)
result_list = [exist_owids, exist_lw_regions, exist_dw_regions] exist_dw_regions.extend(dw_regions)
# check whirlpool parity
valid_whirlpool_parity = world.owCrossed[player] != 'none' or len(set(new_results[0]) & set({0x0f, 0x12, 0x15, 0x33, 0x35, 0x3f, 0x55, 0x7f})) % 2 == 0
(exist_owids, exist_lw_regions, exist_dw_regions) = result_list
exist_owids.extend(new_results[0])
exist_lw_regions.extend(new_results[1])
exist_dw_regions.extend(new_results[2])
# 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 ignore_list = list() #TODO: Remove ignore_list when special OW areas are included in pool
@@ -426,36 +475,62 @@ def shuffle_tiles(world, groups, result_list, player):
def reorganize_tile_groups(world, player): def reorganize_tile_groups(world, player):
groups = {} groups = {}
for (name, groupType) in OWTileGroups.keys(): for (name, groupType, whirlpoolGroup) in OWTileGroups.keys():
if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks'] \ if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks'] \
or (world.mode[player] == 'standard' and world.shuffle[player] in ['lite', 'lean', 'crossed', 'insanity'] and name == 'Castle' and groupType == 'Entrance'): or (world.mode[player] == 'standard' and world.shuffle[player] in ['lite', 'lean', 'crossed', 'insanity'] and name == 'Castle' and groupType == 'Entrance'):
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted']: if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted']:
groups[(name,)] = ([], [], []) if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none':
groups[(name, whirlpoolGroup)] = ([], [], [])
else:
groups[(name,)] = ([], [], [])
else: else:
groups[(name, groupType)] = ([], [], []) if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none':
groups[(name, groupType, whirlpoolGroup)] = ([], [], [])
else:
groups[(name, groupType)] = ([], [], [])
for (name, groupType) in OWTileGroups.keys(): for (name, groupType, whirlpoolGroup) in OWTileGroups.keys():
if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks'] \ if world.mode[player] != 'standard' or name not in ['Castle', 'Links', 'Central Bonk Rocks'] \
or (world.mode[player] == 'standard' and world.shuffle[player] in ['lite', 'lean', 'crossed', 'insanity'] and name == 'Castle' and groupType == 'Entrance'): or (world.mode[player] == 'standard' and world.shuffle[player] in ['lite', 'lean', 'crossed', 'insanity'] and name == 'Castle' and groupType == 'Entrance'):
(lw_owids, dw_owids) = OWTileGroups[(name, groupType,)] (lw_owids, dw_owids) = OWTileGroups[(name, groupType, whirlpoolGroup)]
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted']: if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple', 'restricted']:
(exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name,)] if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none':
exist_owids.extend(lw_owids) (exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name, whirlpoolGroup)]
exist_owids.extend(dw_owids) exist_owids.extend(lw_owids)
for owid in lw_owids: exist_owids.extend(dw_owids)
exist_lw_regions.extend(OWTileRegions.inverse[owid]) for owid in lw_owids:
for owid in dw_owids: exist_lw_regions.extend(OWTileRegions.inverse[owid])
exist_dw_regions.extend(OWTileRegions.inverse[owid]) for owid in dw_owids:
groups[(name,)] = (exist_owids, exist_lw_regions, exist_dw_regions) exist_dw_regions.extend(OWTileRegions.inverse[owid])
groups[(name, whirlpoolGroup)] = (exist_owids, exist_lw_regions, exist_dw_regions)
else:
(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: else:
(exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name, groupType)] if world.owWhirlpoolShuffle[player] or world.owCrossed[player] != 'none':
exist_owids.extend(lw_owids) (exist_owids, exist_lw_regions, exist_dw_regions) = groups[(name, groupType, whirlpoolGroup)]
exist_owids.extend(dw_owids) exist_owids.extend(lw_owids)
for owid in lw_owids: exist_owids.extend(dw_owids)
exist_lw_regions.extend(OWTileRegions.inverse[owid]) for owid in lw_owids:
for owid in dw_owids: exist_lw_regions.extend(OWTileRegions.inverse[owid])
exist_dw_regions.extend(OWTileRegions.inverse[owid]) for owid in dw_owids:
groups[(name, groupType)] = (exist_owids, exist_lw_regions, exist_dw_regions) exist_dw_regions.extend(OWTileRegions.inverse[owid])
groups[(name, groupType, whirlpoolGroup)] = (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 return groups
def remove_reserved(world, groupedlist, connected_edges, player): def remove_reserved(world, groupedlist, connected_edges, player):
@@ -733,17 +808,7 @@ temporary_mandatory_connections = [
] ]
# these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions # these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions
mandatory_connections = [# Whirlpool Connections mandatory_connections = [# Intra-tile OW Connections
('C Whirlpool', 'River Bend Water'),
('River Bend Whirlpool', 'C Whirlpool Water'),
('Lake Hylia Whirlpool', 'Zora Waterfall Water'),
('Zora Whirlpool', 'Lake Hylia Water'),
('Kakariko Pond Whirlpool', 'Octoballoon Water'),
('Octoballoon Whirlpool', 'Kakariko Pond Area'),
('Qirn Jump Whirlpool', 'Bomber Corner Water'),
('Bomber Corner Whirlpool', 'Qirn Jump Water'),
# Intra-tile OW Connections
('Lost Woods Bush (West)', 'Lost Woods East Area'), #pearl ('Lost Woods Bush (West)', 'Lost Woods East Area'), #pearl
('Lost Woods Bush (East)', 'Lost Woods West Area'), #pearl ('Lost Woods Bush (East)', 'Lost Woods West Area'), #pearl
('West Death Mountain Drop', 'West Death Mountain (Bottom)'), ('West Death Mountain Drop', 'West Death Mountain (Bottom)'),
@@ -967,6 +1032,13 @@ mandatory_connections = [# Whirlpool Connections
('Dark Tree Line WC Cliff Water Drop', 'Dark Tree Line Water') #fake flipper ('Dark Tree Line WC Cliff Water Drop', 'Dark Tree Line Water') #fake flipper
] ]
default_whirlpool_connections = [
((0x33, 'C Whirlpool', 'C Whirlpool Water'), (0x15, 'River Bend Whirlpool', 'River Bend Water')),
((0x35, 'Lake Hylia Whirlpool', 'Lake Hylia Water'), (0x0f, 'Zora Whirlpool', 'Zora Waterfall Water')),
((0x12, 'Kakariko Pond Whirlpool', 'Kakariko Pond Area'), (0x3f, 'Octoballoon Whirlpool', 'Octoballoon Water')),
((0x55, 'Qirn Jump Whirlpool', 'Qirn Jump Water'), (0x7f, 'Bomber Corner Whirlpool', 'Bomber Corner Water'))
]
default_flute_connections = [ default_flute_connections = [
0x0b, 0x16, 0x18, 0x2c, 0x2f, 0x38, 0x3b, 0x3f 0x0b, 0x16, 0x18, 0x2c, 0x2f, 0x38, 0x3b, 0x3f
] ]

7
Rom.py
View File

@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'c6c2a2d5d89a3c84871f58806bbb3acf' RANDOMIZERBASEHASH = 'e9dea70e0a0b15bfa0ff7ecd63228a0c'
class JsonRom(object): class JsonRom(object):
@@ -644,6 +644,11 @@ 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(0x0AB793 + o), data[11] & 0xff) # Y low byte
rom.write_byte(snes_to_pc(0x0AB79B + o), data[11] // 0x100) # Y high byte rom.write_byte(snes_to_pc(0x0AB79B + o), data[11] // 0x100) # Y high byte
# patch whirlpools
if world.owWhirlpoolShuffle[player]:
owFlags |= 0x01
write_int16s(rom, snes_to_pc(0x02EA5C), world.owwhirlpools[player])
# patch overworld edges # patch overworld edges
inverted_buffer = [0] * 0x82 inverted_buffer = [0] * 0x82
owMode = 0 owMode = 0

Binary file not shown.

View File

@@ -15,6 +15,9 @@
overworld_swap: overworld_swap:
on: 1 on: 1
off: 1 off: 1
whirlpool_shuffle:
on: 1
off: 1
flute_shuffle: flute_shuffle:
vanilla: 0 vanilla: 0
balanced: 1 balanced: 1

View File

@@ -133,6 +133,10 @@
"action": "store_true", "action": "store_true",
"type": "bool" "type": "bool"
}, },
"ow_whirlpool": {
"action": "store_true",
"type": "bool"
},
"ow_fluteshuffle": { "ow_fluteshuffle": {
"choices": [ "choices": [
"vanilla", "vanilla",

View File

@@ -221,6 +221,9 @@
"ow_mixed": [ "ow_mixed": [
"Overworld tiles are randomly chosen to become part of the opposite world." "Overworld tiles are randomly chosen to become part of the opposite world."
], ],
"ow_whirlpool": [
"Whirlpools will be shuffled and paired together."
],
"ow_fluteshuffle": [ "ow_fluteshuffle": [
"This randomizes the flute spot destinations.", "This randomizes the flute spot destinations.",
"Vanilla: All flute spots remain unchanged.", "Vanilla: All flute spots remain unchanged.",

View File

@@ -123,14 +123,19 @@
"randomizer.overworld.crossed.grouped": "Grouped", "randomizer.overworld.crossed.grouped": "Grouped",
"randomizer.overworld.crossed.limited": "Limited", "randomizer.overworld.crossed.limited": "Limited",
"randomizer.overworld.crossed.chaos": "Chaos", "randomizer.overworld.crossed.chaos": "Chaos",
"randomizer.overworld.keepsimilar": "Keep Similar Edges Together", "randomizer.overworld.keepsimilar": "Keep Similar Edges Together",
"randomizer.overworld.mixed": "Tile Swap (Mixed)", "randomizer.overworld.mixed": "Tile Swap (Mixed)",
"randomizer.overworld.whirlpool": "Whirlpool Shuffle",
"randomizer.overworld.overworldflute": "Flute Shuffle", "randomizer.overworld.overworldflute": "Flute Shuffle",
"randomizer.overworld.overworldflute.vanilla": "Vanilla", "randomizer.overworld.overworldflute.vanilla": "Vanilla",
"randomizer.overworld.overworldflute.balanced": "Balanced", "randomizer.overworld.overworldflute.balanced": "Balanced",
"randomizer.overworld.overworldflute.random": "Random", "randomizer.overworld.overworldflute.random": "Random",
"randomizer.entrance.openpyramid": "Pre-open Pyramid Hole", "randomizer.entrance.openpyramid": "Pre-open Pyramid Hole",
"randomizer.entrance.shuffleganon": "Include Ganon's Tower and Pyramid Hole in shuffle pool", "randomizer.entrance.shuffleganon": "Include Ganon's Tower and Pyramid Hole in shuffle pool",
"randomizer.entrance.shufflelinks": "Include Link's House in the shuffle pool", "randomizer.entrance.shufflelinks": "Include Link's House in the shuffle pool",

View File

@@ -24,6 +24,10 @@
"type": "checkbox", "type": "checkbox",
"default": true "default": true
}, },
"whirlpool": {
"type": "checkbox",
"default": false
},
"overworldflute": { "overworldflute": {
"type": "selectbox", "type": "selectbox",
"default": "vanilla", "default": "vanilla",

View File

@@ -79,6 +79,7 @@ SETTINGSTOPROCESS = {
"crossed": "ow_crossed", "crossed": "ow_crossed",
"keepsimilar": "ow_keepsimilar", "keepsimilar": "ow_keepsimilar",
"mixed": "ow_mixed", "mixed": "ow_mixed",
"whirlpool": "ow_whirlpool",
"overworldflute": "ow_fluteshuffle" "overworldflute": "ow_fluteshuffle"
}, },
"entrance": { "entrance": {

View File

@@ -33,7 +33,7 @@ def overworld_page(parent):
packAttrs = {"side":LEFT, "pady":(18,0)} packAttrs = {"side":LEFT, "pady":(18,0)}
elif key == "overworldflute": elif key == "overworldflute":
packAttrs["pady"] = (20,0) packAttrs["pady"] = (20,0)
elif key == "mixed": elif key in ["whirlpool", "mixed"]:
packAttrs = {"anchor":W, "padx":(79,0)} packAttrs = {"anchor":W, "padx":(79,0)}
self.widgets[key].pack(packAttrs) self.widgets[key].pack(packAttrs)