Initial Flute Shuffle implementation

This commit is contained in:
codemann8
2021-06-27 19:10:44 -05:00
parent d3fd54600f
commit 42eddaed28
17 changed files with 193 additions and 43 deletions

View File

@@ -26,6 +26,7 @@ class World(object):
self.owShuffle = owShuffle.copy() self.owShuffle = owShuffle.copy()
self.owSwap = owSwap.copy() self.owSwap = owSwap.copy()
self.owKeepSimilar = {} self.owKeepSimilar = {}
self.owFluteShuffle = {}
self.shuffle = shuffle.copy() self.shuffle = shuffle.copy()
self.doorShuffle = doorShuffle.copy() self.doorShuffle = doorShuffle.copy()
self.intensity = {} self.intensity = {}
@@ -76,6 +77,7 @@ class World(object):
self.owswaps = {} self.owswaps = {}
self.owedges = [] self.owedges = []
self._owedge_cache = {} self._owedge_cache = {}
self.owflutespots = {}
self.doors = [] self.doors = []
self._door_cache = {} self._door_cache = {}
self.paired_doors = {} self.paired_doors = {}
@@ -2157,6 +2159,7 @@ class Spoiler(object):
'ow_shuffle': self.world.owShuffle, 'ow_shuffle': self.world.owShuffle,
'ow_swap': self.world.owSwap, 'ow_swap': self.world.owSwap,
'ow_keepsimilar': self.world.owKeepSimilar, 'ow_keepsimilar': self.world.owKeepSimilar,
'ow_fluteshuffle': self.world.owFluteShuffle,
'shuffle': self.world.shuffle, 'shuffle': self.world.shuffle,
'door_shuffle': self.world.doorShuffle, 'door_shuffle': self.world.doorShuffle,
'intensity': self.world.intensity, 'intensity': self.world.intensity,
@@ -2239,6 +2242,7 @@ class Spoiler(object):
outfile.write('Overworld Tile Swap:'.ljust(line_width) + '%s\n' % self.metadata['ow_swap'][player]) outfile.write('Overworld Tile Swap:'.ljust(line_width) + '%s\n' % self.metadata['ow_swap'][player])
if self.metadata['ow_shuffle'][player] != 'vanilla': if self.metadata['ow_shuffle'][player] != 'vanilla':
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('Flute Shuffle:'.ljust(line_width) + '%s\n' % ('Yes' if self.metadata['ow_fluteshuffle'][player] != 'vanilla' else 'No'))
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('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player]) outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player]) outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player])

4
CLI.py
View File

@@ -93,7 +93,8 @@ def parse_cli(argv, no_defaults=False):
for player in range(1, multiargs.multi + 1): for player in range(1, multiargs.multi + 1):
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', 'ow_shuffle', 'ow_swap', 'ow_keepsimilar', for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'ow_shuffle', 'ow_swap', 'ow_keepsimilar', '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',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max',
@@ -144,6 +145,7 @@ def parse_settings():
"ow_shuffle": "vanilla", "ow_shuffle": "vanilla",
"ow_swap": "vanilla", "ow_swap": "vanilla",
"ow_keepsimilar": False, "ow_keepsimilar": False,
"ow_fluteshuffle": "vanilla",
"shuffle": "vanilla", "shuffle": "vanilla",
"shufflelinks": False, "shufflelinks": False,

View File

@@ -74,6 +74,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.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()
world.enemy_shuffle = args.shuffleenemies.copy() world.enemy_shuffle = args.shuffleenemies.copy()
@@ -383,6 +384,8 @@ def copy_world(world):
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
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.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()
ret.enemy_shuffle = world.enemy_shuffle.copy() ret.enemy_shuffle = world.enemy_shuffle.copy()

View File

@@ -133,9 +133,11 @@ def roll_settings(weights):
overworld_shuffle = get_choice('overworld_shuffle') overworld_shuffle = get_choice('overworld_shuffle')
ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla' ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla'
overworld_shuffle = get_choice('overworld_swap') overworld_swap = get_choice('overworld_swap')
ret.ow_swap = overworld_swap if overworld_swap != 'none' else 'vanilla' ret.ow_swap = overworld_swap if overworld_swap != 'none' else 'vanilla'
ret.ow_keepsimilar = get_choice('ow_keepsimilar') ret.ow_keepsimilar = get_choice('ow_keepsimilar')
overworld_flute = get_choice('overworld_flute')
ret.ow_swap = overworld_flute if overworld_flute != 'none' else 'vanilla'
entrance_shuffle = get_choice('entrance_shuffle') entrance_shuffle = get_choice('entrance_shuffle')
ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla' ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla'
door_shuffle = get_choice('door_shuffle') door_shuffle = get_choice('door_shuffle')

View File

@@ -722,8 +722,8 @@ OWTileGroups = {
], ],
[ [
'East Dark Death Mountain (Top)', 'East Dark Death Mountain (Top)',
'East Dark Death Mountain (Bottom)', 'East Dark Death Mountain (Bottom Left)',
'East Dark Death Mountain (Bottom Left)' 'East Dark Death Mountain (Bottom)'
] ]
), ),
("East Mountain", "Entrance"): ( ("East Mountain", "Entrance"): (

View File

@@ -1,4 +1,5 @@
import random, logging, copy import random, logging, copy
from sortedcontainers import SortedList
from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot
from OWEdges import OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel from OWEdges import OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel
@@ -141,14 +142,39 @@ def link_overworld(world, player):
region = world.get_region(name, player) region = world.get_region(name, player)
region.type = RegionType.LightWorld region.type = RegionType.LightWorld
#make new connections # flute shuffle
for owid in flute_connections.keys(): def connect_flutes(flute_destinations):
(spot, dest) = flute_connections[owid] for o in range(0, len(flute_destinations)):
if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): owid = flute_destinations[o]
connect_simple(world, spot, dest[0], player) regions = flute_data[owid][0]
else: if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
connect_simple(world, spot, dest[1], player) connect_simple(world, 'Flute Spot ' + str(o + 1), regions[0], player)
else:
connect_simple(world, 'Flute Spot ' + str(o + 1), regions[1], player)
if world.owFluteShuffle[player] == 'vanilla':
connect_flutes(default_flute_connections)
else:
flute_pool = list(flute_data.keys())
new_spots = SortedList()
# guarantee desert/mire access
flute_pool.remove(0x30)
new_spots.add(0x30)
# guarantee mountain access
owid = random.randint(0, 2) * 2 + 3
flute_pool.remove(owid)
new_spots.add(owid)
random.shuffle(flute_pool)
f = 0
while len(new_spots) < 8:
new_spots.add(flute_pool[f])
f += 1
world.owflutespots[player] = new_spots
connect_flutes(new_spots)
# make new connections
for owid in ow_connections.keys(): for owid in ow_connections.keys():
if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') == (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
for (exitname, regionname) in ow_connections[owid][0]: for (exitname, regionname) in ow_connections[owid][0]:
@@ -688,15 +714,9 @@ mandatory_connections = [('Flute Away', 'Flute Sky'),
('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
] ]
flute_connections = {0x03: ('Flute Spot 1', ['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)']), default_flute_connections = [
0x16: ('Flute Spot 2', ['Potion Shop Area', 'Dark Witch Area']), 0x03, 0x16, 0x18, 0x2c, 0x2f, 0x30, 0x3b, 0x3f
0x18: ('Flute Spot 3', ['Kakariko Area', 'Village of Outcasts Area']), ]
0x2c: ('Flute Spot 4', ['Links House Area', 'Big Bomb Shop Area']),
0x2f: ('Flute Spot 5', ['Eastern Nook Area', 'Palace of Darkness Nook Area']),
0x30: ('Flute Spot 6', ['Desert Palace Teleporter Ledge', 'Misery Mire Teleporter Ledge']),
0x3b: ('Flute Spot 7', ['Dam Area', 'Swamp Area']),
0x3f: ('Flute Spot 8', ['Octoballoon Area', 'Bomber Corner Area'])
}
ow_connections = { ow_connections = {
0x00: ([ 0x00: ([
@@ -1193,3 +1213,47 @@ default_connections = [('Lost Woods SW', 'Lost Woods Pass NW'),
('West Dark Death Mountain ES', 'East Dark Death Mountain WS'), ('West Dark Death Mountain ES', 'East Dark Death Mountain WS'),
('East Dark Death Mountain EN', 'Turtle Rock WN') ('East Dark Death Mountain EN', 'Turtle Rock WN')
] ]
flute_data = {
#OWID LW Region DW Region VRAM BG Y BG X Link Y Link X Cam Y Cam X Unk1 Unk2 IconY IconX AltY AltX
0x00: (['Lost Woods East Area', 'Skull Woods Forest'], 0x1042, 0x022e, 0x0202, 0x0290, 0x0288, 0x029b, 0x028f, 0xfff2, 0x000e, 0x0290, 0x0288, 0x0290, 0x0290),
0x02: (['Lumberjack Area', 'Dark Lumberjack Area'], 0x059c, 0x00d6, 0x04e6, 0x0138, 0x0558, 0x0143, 0x0563, 0xfffa, 0xfffa, 0x0138, 0x0550),
0x03: (['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], 0x1600, 0x02ca, 0x060e, 0x0328, 0x0678, 0x0337, 0x0683, 0xfff6, 0xfff2, 0x035b, 0x0680),
0x05: (['East Death Mountain (Bottom)', 'East Dark Death Mountain (Bottom)'], 0x1860, 0x031e, 0x0d00, 0x0388, 0x0da8, 0x038d, 0x0d7d, 0x0000, 0x0000, 0x0388, 0x0da8),
0x07: (['Death Mountain TR Pegs', 'Turtle Rock Area'], 0x0804, 0x0102, 0x0e1a, 0x0160, 0x0e90, 0x016f, 0x0e97, 0xfffe, 0x0006, 0x0160, 0x0f20),
0x0a: (['Mountain Entry Area', 'Bumper Cave Area'], 0x0180, 0x0220, 0x0206, 0x0280, 0x0488, 0x028f, 0x0493, 0x0000, 0xfffa, 0x0280, 0x0488),
0x0f: (['Zora Waterfall Area', 'Catfish Area'], 0x0316, 0x025c, 0x0eb2, 0x02c0, 0x0f28, 0x02cb, 0x0f2f, 0x0002, 0xfffe, 0x02d0, 0x0f38),
0x10: (['Lost Woods Pass West Area', 'Skull Woods Pass West Area'], 0x0080, 0x0400, 0x0000, 0x0448, 0x0058, 0x046f, 0x0085, 0x0000, 0x0000, 0x0448, 0x0058),
0x11: (['Kakariko Fortune Area', 'Dark Fortune Area'], 0x0912, 0x051e, 0x0292, 0x0588, 0x0318, 0x058d, 0x031f, 0x0000, 0xfffe, 0x0588, 0x0318),
0x12: (['Kakariko Pond Area', 'Outcast Pond Area'], 0x0890, 0x051a, 0x0476, 0x0578, 0x04f8, 0x0587, 0x0503, 0xfff6, 0x000a, 0x0578, 0x04f8),
0x13: (['Sanctuary Area', 'Dark Chapel Area'], 0x051c, 0x04aa, 0x06de, 0x0508, 0x0758, 0x0517, 0x0763, 0xfff6, 0x0002, 0x0508, 0x0758),
0x14: (['Graveyard Area', 'Dark Graveyard Area'], 0x089c, 0x051e, 0x08e6, 0x0580, 0x0958, 0x058b, 0x0963, 0x0000, 0xfffa, 0x0580, 0x0928, 0x0580, 0x0948),
0x15: (['River Bend East Bank', 'Qirn Jump East Bank'], 0x041a, 0x0486, 0x0ad2, 0x04e8, 0x0b48, 0x04f3, 0x0b4f, 0x0008, 0xfffe, 0x04f8, 0x0b60),
0x16: (['Potion Shop Area', 'Dark Witch Area'], 0x0888, 0x0516, 0x0c4e, 0x0578, 0x0cc8, 0x0583, 0x0cd3, 0xfffa, 0xfff2, 0x0598, 0x0ccf),
0x17: (['Zora Approach Ledge', 'Catfish Approach Ledge'], 0x039e, 0x047e, 0x0ef2, 0x04e0, 0x0f68, 0x04eb, 0x0f6f, 0x0000, 0xfffe, 0x04e0, 0x0f68),
0x18: (['Kakariko Area', 'Village of Outcasts Area'], 0x0b30, 0x0759, 0x017e, 0x07c8, 0x01f8, 0x07c6, 0x020b, 0x0007, 0x0002, 0x07c0, 0x0210, 0x01f8, 0x07c6),
0x1a: (['Forgotten Forest Area', 'Shield Shop Fence'], 0x081a, 0x070f, 0x04d2, 0x0770, 0x0548, 0x077c, 0x054f, 0xffff, 0xfffe, 0x0770, 0x0548),
0x1b: (['Hyrule Castle Courtyard', 'Pyramid Area'], 0x0c30, 0x077a, 0x0786, 0x07d8, 0x07f8, 0x07e7, 0x0803, 0x0006, 0xfffa, 0x07d8, 0x07f8),
0x1d: (['Wooden Bridge Area', 'Broken Bridge Northeast'], 0x0602, 0x06c2, 0x0a0e, 0x0720, 0x0a80, 0x072f, 0x0a8b, 0xfffe, 0x0002, 0x0720, 0x0a80),
0x1e: (['Eastern Palace Area', 'Palace of Darkness Area'], 0x1802, 0x091e, 0x0c0e, 0x09c0, 0x0c80, 0x098b, 0x0c8b, 0x0000, 0x0002, 0x09c0, 0x0c80),
0x22: (['Blacksmith Area', 'Hammer Pegs Area'], 0x058c, 0x08aa, 0x0462, 0x0908, 0x04d8, 0x0917, 0x04df, 0x0006, 0xfffe, 0x0908, 0x04d8),
0x25: (['Sand Dunes Area', 'Dark Dunes Area'], 0x030e, 0x085a, 0x0a76, 0x08b8, 0x0ae8, 0x08c7, 0x0af3, 0x0006, 0xfffa, 0x08b8, 0x0b08),
0x28: (['Maze Race Area', 'Dig Game Area'], 0x0908, 0x0b1e, 0x003a, 0x0b88, 0x00b8, 0x0b8d, 0x00bf, 0x0000, 0x0006, 0x0b88, 0x00b8),
0x29: (['Kakariko Suburb Area', 'Frog Area'], 0x0408, 0x0a7c, 0x0242, 0x0ae0, 0x02c0, 0x0aeb, 0x02c7, 0x0002, 0xfffe, 0x0ae0, 0x02c0),
0x2a: (['Flute Boy Area', 'Stumpy Area'], 0x058e, 0x0aac, 0x046e, 0x0b10, 0x04e8, 0x0b1b, 0x04f3, 0x0002, 0x0002, 0x0b10, 0x04e8),
0x2b: (['Central Bonk Rocks Area', 'Dark Bonk Rocks Area'], 0x0620, 0x0acc, 0x0700, 0x0b30, 0x0790, 0x0b3b, 0x0785, 0xfff2, 0x0000, 0x0b30, 0x0770),
0x2c: (['Links House Area', 'Big Bomb Shop Area'], 0x0588, 0x0ab9, 0x0840, 0x0b17, 0x08b8, 0x0b26, 0x08bf, 0xfff7, 0x0000, 0x0b20, 0x08b8),
0x2d: (['Stone Bridge Area', 'Hammer Bridge South Area'], 0x0886, 0x0b1e, 0x0a2a, 0x0ba0, 0x0aa8, 0x0b8b, 0x0aaf, 0x0000, 0x0006, 0x0bc4, 0x0ad0),
0x2e: (['Tree Line Area', 'Dark Tree Line Area'], 0x0100, 0x0a1a, 0x0c00, 0x0a78, 0x0c30, 0x0a87, 0x0c7d, 0x0006, 0x0000, 0x0a78, 0x0c58),
0x2f: (['Eastern Nook Area', 'Palace of Darkness Nook Area'], 0x0798, 0x0afa, 0x0eb2, 0x0b58, 0x0f30, 0x0b67, 0x0f37, 0xfff6, 0x000e, 0x0b50, 0x0f30),
0x30: (['Desert Palace Teleporter Ledge', 'Misery Mire Teleporter Ledge'], 0x1880, 0x0f1e, 0x0000, 0x0fa8, 0x0078, 0x0f8d, 0x008d, 0x0000, 0x0000, 0x0fb0, 0x0070),
0x32: (['Flute Boy Approach Area', 'Stumpy Approach Area'], 0x03a0, 0x0c6c, 0x0500, 0x0cd0, 0x05a8, 0x0cdb, 0x0585, 0x0002, 0x0000, 0x0cd6, 0x05a8),
0x33: (['C Whirlpool Outer Area', 'Dark C Whirlpool Outer Area'], 0x0180, 0x0c20, 0x0600, 0x0c80, 0x0628, 0x0c8f, 0x067d, 0x0000, 0x0000, 0x0c80, 0x0628),
0x34: (['Statues Area', 'Hype Cave Area'], 0x088e, 0x0d00, 0x0866, 0x0d60, 0x08d8, 0x0d6f, 0x08e3, 0x0000, 0x000a, 0x0d60, 0x08d8),
0x35: (['Lake Hylia Area', 'Ice Lake Area'], 0x0d00, 0x0da6, 0x0a06, 0x0e08, 0x0a80, 0x0e13, 0x0a8b, 0xfffa, 0xfffa, 0x0d88, 0x0a88),
0x37: (['Ice Cave Area', 'Shopping Mall Area'], 0x0786, 0x0cf6, 0x0e2e, 0x0d58, 0x0ea0, 0x0d63, 0x0eab, 0x000a, 0x0002, 0x0d48, 0x0ed0),
0x3a: (['Desert Pass Area', 'Swamp Nook Area'], 0x001a, 0x0e08, 0x04c6, 0x0e70, 0x0540, 0x0e7d, 0x054b, 0x0006, 0x000a, 0x0e70, 0x0540),
0x3b: (['Dam Area', 'Swamp Area'], 0x069e, 0x0edf, 0x06f2, 0x0f3d, 0x0778, 0x0f4c, 0x077f, 0xfff1, 0xfffe, 0x0f30, 0x0770),
0x3c: (['South Pass Area', 'Dark South Pass Area'], 0x0584, 0x0ed0, 0x081e, 0x0f38, 0x0898, 0x0f45, 0x08a3, 0xfffe, 0x0002, 0x0f38, 0x0898),
0x3f: (['Octoballoon Area', 'Bomber Corner Area'], 0x0810, 0x0f05, 0x0e75, 0x0f67, 0x0ef3, 0x0f72, 0x0efa, 0xfffb, 0x000b, 0x0f80, 0x0ef0)
}

View File

@@ -23,7 +23,7 @@ def main(args):
start_time = time.process_time() start_time = time.process_time()
# initialize the world # initialize the world
world = World(1, 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, False, False, False, None, False) world = World(1, 'vanilla', 'vanilla', 'vanilla', 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, False, False, False, None, False)
world.player_names[1].append("Player 1") world.player_names[1].append("Player 1")
logger = logging.getLogger('') logger = logging.getLogger('')

View File

@@ -85,6 +85,16 @@ OW tiles remain in their original world, but transitions can now be travel cross
This keeps similar edge transitions together. ie. The 2 west edges of Potion Shop will be paired to another set of two similar edges This keeps similar edge transitions together. ie. The 2 west edges of Potion Shop will be paired to another set of two similar edges
## Flute Shuffle (--ow_fluteshuffle)
### Vanilla
Flute spots remain unchanged.
### Random
New flute spots are chosen at random. You can also cancel out of the flute menu by pressing X.
# Command Line Options # Command Line Options
@@ -111,3 +121,9 @@ For specifying the overworld tile swap you want as above. (default: vanilla)
``` ```
This keeps similar edge transitions paired together with other pairs of transitions This keeps similar edge transitions paired together with other pairs of transitions
```
--ow_fluteshuffle <mode>
```
For randomizing the flute spots around the overworld

64
Rom.py
View File

@@ -24,10 +24,11 @@ from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc
from Items import ItemFactory from Items import ItemFactory
from EntranceShuffle import door_addresses, exit_ids from EntranceShuffle import door_addresses, exit_ids
from OverworldShuffle import default_flute_connections, flute_data
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '5fecbc1016544cfdcc596abffaed6736' RANDOMIZERBASEHASH = '6e44346357f8a9471a8499ab787635c1'
class JsonRom(object): class JsonRom(object):
@@ -625,6 +626,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
owFlags = 0 owFlags = 0
if world.owKeepSimilar[player]: if world.owKeepSimilar[player]:
owFlags |= 0x1 owFlags |= 0x1
if world.owFluteShuffle[player] != 'vanilla':
owFlags |= 0x100
write_int16(rom, 0x150004, owFlags) write_int16(rom, 0x150004, owFlags)
@@ -645,6 +648,37 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
write_int16(rom, edge.getAddress() + 0x0a, edge.vramLoc) write_int16(rom, edge.getAddress() + 0x0a, edge.vramLoc)
write_int16(rom, edge.getAddress() + 0x0e, edge.getTarget()) write_int16(rom, edge.getAddress() + 0x0e, edge.getTarget())
# patch flute spots
if world.owFluteShuffle[player] == 'vanilla':
flute_spots = default_flute_connections
else:
flute_spots = world.owflutespots[player]
for o in range(0, len(flute_spots)):
owid = flute_spots[o]
offset = 0
if (world.mode[player] == 'inverted') != (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
offset = 0x40
data = flute_data[owid]
write_int16(rom, snes_to_pc(0x02E849 + (o * 2)), owid + offset) # owid
write_int16(rom, snes_to_pc(0x02E86B + (o * 2)), data[1]) #vram
write_int16(rom, snes_to_pc(0x02E88D + (o * 2)), data[2]) # BG scroll Y
write_int16(rom, snes_to_pc(0x02E8AF + (o * 2)), data[3]) # BG scroll X
write_int16(rom, snes_to_pc(0x02E8D1 + (o * 2)), data[12] if offset > 0 and len(data) > 12 else data[4]) # link Y
write_int16(rom, snes_to_pc(0x02E8F3 + (o * 2)), data[13] if offset > 0 and len(data) > 12 else data[5]) # link X
write_int16(rom, snes_to_pc(0x02E915 + (o * 2)), data[6]) # cam Y
write_int16(rom, snes_to_pc(0x02E937 + (o * 2)), data[7]) # cam X
write_int16(rom, snes_to_pc(0x02E959 + (o * 2)), data[8]) # unknown 1
write_int16(rom, snes_to_pc(0x02E97B + (o * 2)), data[9]) # unknown 2
# flute menu blips
rom.buffer[snes_to_pc(0x0AB783 + o)] = data[11] & 0xff # X low byte
rom.buffer[snes_to_pc(0x0AB78B + o)] = data[11] // 0x100 # X high byte
rom.buffer[snes_to_pc(0x0AB793 + o)] = data[10] & 0xff # Y low byte
rom.buffer[snes_to_pc(0x0AB79B + o)] = data[10] // 0x100 # Y high byte
# patch entrance/exits/holes # patch entrance/exits/holes
for region in world.regions: for region in world.regions:
for exit in region.exits: for exit in region.exits:
@@ -2285,7 +2319,7 @@ def set_inverted_mode(world, player, rom):
write_int16(rom, 0x15AEE + 2*0x25, 0x000C) write_int16(rom, 0x15AEE + 2*0x25, 0x000C)
if (world.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E849), 0x0043) #flute spot #write_int16(rom, snes_to_pc(0x02E849), 0x0043) #flute spot
if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple']: if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple']:
rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) # mountain cave starts on OW rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) # mountain cave starts on OW
@@ -2352,12 +2386,12 @@ def set_inverted_mode(world, player, rom):
write_int16(rom, 0x15AEE + 2*0x18, 0x00E6) # DMD west UW to bumper cave top entrance write_int16(rom, 0x15AEE + 2*0x18, 0x00E6) # DMD west UW to bumper cave top entrance
if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x10 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warp under rock rom.write_bytes(snes_to_pc(0x1BC67A), [0x2E, 0x0B, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x16 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): #if (world.mode[player] == 'inverted') != (0x16 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E84B), 0x0056) #flute spot #write_int16(rom, snes_to_pc(0x02E84B), 0x0056) #flute spot
if (world.mode[player] == 'inverted') != (0x18 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): #if (world.mode[player] == 'inverted') != (0x18 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E84D), 0x0058) #flute spot #write_int16(rom, snes_to_pc(0x02E84D), 0x0058) #flute spot
write_int16(rom, snes_to_pc(0x02E8D5), 0x07C8) #flute spot #write_int16(rom, snes_to_pc(0x02E8D5), 0x07C8) #flute spot
write_int16(rom, snes_to_pc(0x02E8F7), 0x01F8) #flute spot #write_int16(rom, snes_to_pc(0x02E8F7), 0x01F8) #flute spot
if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x1B in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, 0x15AEE + 2 * 0x06, 0x0020) # post aga hyrule castle spawn write_int16(rom, 0x15AEE + 2 * 0x06, 0x0020) # post aga hyrule castle spawn
rom.write_byte(0x15B8C + 0x06, 0x1B) rom.write_byte(0x15B8C + 0x06, 0x1B)
@@ -2466,26 +2500,26 @@ def set_inverted_mode(world, player, rom):
if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x29 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) #frog pickup on contact rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) #frog pickup on contact
if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x2C in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E84F), 0x006C) #flute spot #write_int16(rom, snes_to_pc(0x02E84F), 0x006C) #flute spot
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
rom.write_byte(0x15B8C, 0x6C) #exit links at bomb shop area rom.write_byte(0x15B8C, 0x6C) #exit links at bomb shop area
rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house
rom.write_byte(0xDBB73 + 0x52, 0x01) rom.write_byte(0xDBB73 + 0x52, 0x01)
if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x2F in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E851), 0x006F) #flute spot #write_int16(rom, snes_to_pc(0x02E851), 0x006F) #flute spot
rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) # add warp under rock rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x30 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E853), 0x0070) #flute spot #write_int16(rom, snes_to_pc(0x02E853), 0x0070) #flute spot
rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) # add warp under rock rom.write_bytes(snes_to_pc(0x1BC81E), [0x94, 0x1D, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x33 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
rom.write_bytes(snes_to_pc(0x1BC3DF), [0xD8, 0xD1]) # add warp under rock rom.write_bytes(snes_to_pc(0x1BC3DF), [0xD8, 0xD1]) # add warp under rock
rom.write_bytes(snes_to_pc(0x1BD1D8), [0xA8, 0x02, 0x82, 0xFF, 0xFF]) # add warp under rock rom.write_bytes(snes_to_pc(0x1BD1D8), [0xA8, 0x02, 0x82, 0xFF, 0xFF]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): if (world.mode[player] == 'inverted') != (0x35 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock
if (world.mode[player] == 'inverted') != (0x3B in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): #if (world.mode[player] == 'inverted') != (0x3B in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E855), 0x007B) #flute spot #write_int16(rom, snes_to_pc(0x02E855), 0x007B) #flute spot
if (world.mode[player] == 'inverted') != (0x3F in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): #if (world.mode[player] == 'inverted') != (0x3F in world.owswaps[player][0] and world.owSwap[player] == 'mixed'):
write_int16(rom, snes_to_pc(0x02E857), 0x007F) #flute spot #write_int16(rom, snes_to_pc(0x02E857), 0x007F) #flute spot
if world.mode[player] == 'inverted': if world.mode[player] == 'inverted':
rom.write_byte(0x15B8C + 0x3D, rom.buffer[0x15B8C]) # houlihan exit rom.write_byte(0x15B8C + 0x3D, rom.buffer[0x15B8C]) # houlihan exit

View File

@@ -106,7 +106,7 @@ OWWorldCheck16:
OWFluteCancel: OWFluteCancel:
{ {
lda.l OWFlags+1 : and #$10 : bne + lda.l OWFlags+1 : and #$01 : bne +
jsl $02e99d : rtl jsl $02e99d : rtl
+ lda $7f5006 : cmp #$01 : beq + + lda $7f5006 : cmp #$01 : beq +
jsl $02e99d jsl $02e99d
@@ -117,7 +117,7 @@ OWFluteCancel2:
lda $f2 : ora $f0 : and #$c0 : bne + lda $f2 : ora $f0 : and #$c0 : bne +
jml $0ab7bd jml $0ab7bd
+ inc $0200 + inc $0200
lda.l OWFlags+1 : and #$10 : beq + lda.l OWFlags+1 : and #$01 : beq +
lda $f2 : cmp #$40 : bne + lda $f2 : cmp #$40 : bne +
lda #$01 : sta $7f5006 lda #$01 : sta $7f5006
+ rtl + rtl

Binary file not shown.

View File

@@ -127,6 +127,12 @@
"action": "store_true", "action": "store_true",
"type": "bool" "type": "bool"
}, },
"ow_fluteshuffle": {
"choices": [
"vanilla",
"random"
]
},
"shuffle": { "shuffle": {
"choices": [ "choices": [
"vanilla", "vanilla",

View File

@@ -214,6 +214,11 @@
"ow_keepsimilar": [ "ow_keepsimilar": [
"This keeps similar edge transitions together. ie. the two west edges on", "This keeps similar edge transitions together. ie. the two west edges on",
"Potion Shop will be paired with another similar pair." ], "Potion Shop will be paired with another similar pair." ],
"ow_fluteshuffle": [
"This randomizes the flute spot destinations.",
"Vanilla: All flute spots remain unchanged.",
"Random: New flute spots will be generated."
],
"door_shuffle": [ "door_shuffle": [
"Select Door Shuffling Algorithm. (default: %(default)s)", "Select Door Shuffling Algorithm. (default: %(default)s)",
"Basic: Doors are mixed within a single dungeon.", "Basic: Doors are mixed within a single dungeon.",

View File

@@ -121,6 +121,10 @@
"randomizer.overworld.keepsimilar": "Keep Similar Edges Together", "randomizer.overworld.keepsimilar": "Keep Similar Edges Together",
"randomizer.overworld.overworldflute": "Flute Shuffle",
"randomizer.overworld.overworldflute.vanilla": "Vanilla",
"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

@@ -17,6 +17,14 @@
"mixed", "mixed",
"crossed" "crossed"
] ]
},
"overworldflute": {
"type": "selectbox",
"default": "vanilla",
"options": [
"vanilla",
"random"
]
} }
}, },
"rightOverworldFrame": { "rightOverworldFrame": {

View File

@@ -1,6 +1,7 @@
aenum aenum
fast-enum fast-enum
python-bps-continued python-bps-continued
sortedcontainers
colorama colorama
aioconsole aioconsole
websockets websockets

View File

@@ -75,7 +75,8 @@ SETTINGSTOPROCESS = {
"overworld": { "overworld": {
"overworldshuffle": "ow_shuffle", "overworldshuffle": "ow_shuffle",
"overworldswap": "ow_swap", "overworldswap": "ow_swap",
"keepsimilar": "ow_keepsimilar" "keepsimilar": "ow_keepsimilar",
"overworldflute": "ow_fluteshuffle"
}, },
"entrance": { "entrance": {
"openpyramid": "openpyramid", "openpyramid": "openpyramid",