From 68dc27902b46bbc7998490bf002287bdb667225f Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 15 Jul 2021 16:02:07 -0700 Subject: [PATCH 01/11] SFX Shuffle initial implementation --- Adjuster.py | 1 + AdjusterMain.py | 2 +- CLI.py | 3 +- Main.py | 3 +- Mystery.py | 1 + Rom.py | 7 +- mystery_example.yml | 3 + resources/app/cli/args.json | 4 + resources/app/cli/lang/en.json | 1 + .../app/gui/adjust/overview/widgets.json | 3 +- resources/app/gui/lang/en.json | 2 + .../gui/randomize/gameoptions/widgets.json | 3 +- source/classes/SFX.py | 191 ++++++++++++++++++ source/classes/constants.py | 3 +- source/gui/adjust/overview.py | 1 + 15 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 source/classes/SFX.py diff --git a/Adjuster.py b/Adjuster.py index c6f42e6e..a6e964a8 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -36,6 +36,7 @@ def main(): parser.add_argument('--ow_palettes', default='default', choices=['default', 'random', 'blackout']) parser.add_argument('--uw_palettes', default='default', choices=['default', 'random', 'blackout']) parser.add_argument('--reduce_flashing', help='Reduce some in-game flashing.', action='store_true') + parser.add_argument('--shuffle_sfx', help='Shuffles sound sfx', action='store_true') parser.add_argument('--sprite', help='''\ Path to a sprite sheet to use for Link. Needs to be in binary format and have a length of 0x7000 (28672) bytes, diff --git a/AdjusterMain.py b/AdjusterMain.py index bc463444..7d7e2f6e 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -25,7 +25,7 @@ def adjust(args): args.sprite = None apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, - args.sprite, args.ow_palettes, args.uw_palettes, args.reduce_flashing) + args.sprite, args.ow_palettes, args.uw_palettes, args.reduce_flashing, args.shuffle_sfx) output_path.cached_path = args.outputpath rom.write_to_file(output_path('%s.sfc' % outfilebase)) diff --git a/CLI.py b/CLI.py index d87e1e2c..dc374769 100644 --- a/CLI.py +++ b/CLI.py @@ -102,7 +102,7 @@ def parse_cli(argv, no_defaults=False): 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', 'remote_items', 'shopsanity', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', - 'reduce_flashing']: + 'reduce_flashing', 'shuffle_sfx']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) @@ -190,6 +190,7 @@ def parse_settings(): "ow_palettes": "default", "uw_palettes": "default", "reduce_flashing": False, + "shuffle_sfx": False, # Spoiler defaults to TRUE # Playthrough defaults to TRUE diff --git a/Main.py b/Main.py index eb8380f3..71ef017a 100644 --- a/Main.py +++ b/Main.py @@ -281,7 +281,8 @@ def main(args, seed=None, fish=None): apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], - args.ow_palettes[player], args.uw_palettes[player], args.reduce_flashing[player]) + args.ow_palettes[player], args.uw_palettes[player], args.reduce_flashing[player], + args.shuffle_sfx[player]) if args.jsonout: jsonout[f'patch_t{team}_p{player}'] = rom.patches diff --git a/Mystery.py b/Mystery.py index f82e64d2..8914c1be 100644 --- a/Mystery.py +++ b/Mystery.py @@ -231,6 +231,7 @@ def roll_settings(weights): ret.heartbeep = get_choice('heartbeep', romweights) ret.ow_palettes = get_choice('ow_palettes', romweights) ret.uw_palettes = get_choice('uw_palettes', romweights) + ret.uw_palettes = get_choice('shuffle_sfx', romweights) == 'on' return ret diff --git a/Rom.py b/Rom.py index 5c61e5c2..0e2215a7 100644 --- a/Rom.py +++ b/Rom.py @@ -28,6 +28,8 @@ from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_ from Items import ItemFactory from EntranceShuffle import door_addresses, exit_ids +from source.classes.SFX import randomize_sfx + JAP10HASH = '03a63945398191337e896e5771f77173' RANDOMIZERBASEHASH = '25dd18672e1234c85900f5b2155e7e4f' @@ -1624,7 +1626,7 @@ def hud_format_text(text): def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, - ow_palettes, uw_palettes, reduce_flashing): + ow_palettes, uw_palettes, reduce_flashing, shuffle_sfx): if not os.path.exists("data/sprites/official/001.link.1.zspr") and rom.orig_buffer: dump_zspr(rom.orig_buffer[0x80000:0x87000], rom.orig_buffer[0xdd308:0xdd380], @@ -1727,6 +1729,9 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr elif uw_palettes == 'blackout': blackout_uw_palettes(rom) + if shuffle_sfx: + randomize_sfx(rom) + if isinstance(rom, LocalRom): rom.write_crc() diff --git a/mystery_example.yml b/mystery_example.yml index e349063d..028e7110 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -132,3 +132,6 @@ half: 0 quarter: 1 off: 0 + shuffle_sfx: + on: 1 + off: 1 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 47bb3987..dc4b917c 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -199,6 +199,10 @@ "action": "store_true", "type": "bool" }, + "shuffle_sfx": { + "action": "store_true", + "type": "bool" + }, "mapshuffle": { "action": "store_true", "type": "bool" diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 0ff910a9..8c5c0f8c 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -295,6 +295,7 @@ "sprite that will be extracted." ], "reduce_flashing": [ "Reduce some in-game flashing (default: %(default)s)" ], + "shuffle_sfx": [ "Shuffle sounds effects (default: %(default)s)" ], "create_rom": [ "Create an output rom file. (default: %(default)s)" ], "gui": [ "Launch the GUI. (default: %(default)s)" ], "jsonout": [ diff --git a/resources/app/gui/adjust/overview/widgets.json b/resources/app/gui/adjust/overview/widgets.json index b61fff0e..85efcf1f 100644 --- a/resources/app/gui/adjust/overview/widgets.json +++ b/resources/app/gui/adjust/overview/widgets.json @@ -2,7 +2,8 @@ "checkboxes": { "nobgm": { "type": "checkbox" }, "quickswap": { "type": "checkbox" }, - "reduce_flashing": {"type": "checkbox"} + "reduce_flashing": {"type": "checkbox"}, + "shuffle_sfx": {"type": "checkbox"} }, "leftAdjustFrame": { "heartcolor": { diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 0d9e3836..24134897 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -3,6 +3,7 @@ "adjust.nobgm": "Disable Music & MSU-1", "adjust.quickswap": "L/R Quickswapping", "adjust.reduce_flashing": "Reduce Flashing", + "adjust.shuffle_sfx": "Shuffle Sound Effects", "adjust.heartcolor": "Heart Color", "adjust.heartcolor.red": "Red", @@ -134,6 +135,7 @@ "randomizer.gameoptions.nobgm": "Disable Music & MSU-1", "randomizer.gameoptions.quickswap": "L/R Quickswapping", "randomizer.gameoptions.reduce_flashing": "Reduce Flashing", + "randomizer.gameoptions.shuffle_sfx": "Shuffle Sound Effects", "randomizer.gameoptions.heartcolor": "Heart Color", "randomizer.gameoptions.heartcolor.red": "Red", diff --git a/resources/app/gui/randomize/gameoptions/widgets.json b/resources/app/gui/randomize/gameoptions/widgets.json index 63556e0f..6efe32c8 100644 --- a/resources/app/gui/randomize/gameoptions/widgets.json +++ b/resources/app/gui/randomize/gameoptions/widgets.json @@ -2,7 +2,8 @@ "checkboxes": { "nobgm": { "type": "checkbox" }, "quickswap": { "type": "checkbox" }, - "reduce_flashing": {"type": "checkbox"} + "reduce_flashing": {"type": "checkbox"}, + "shuffle_sfx": {"type": "checkbox"} }, "leftRomOptionsFrame": { "heartcolor": { diff --git a/source/classes/SFX.py b/source/classes/SFX.py new file mode 100644 index 00000000..ed6d93f6 --- /dev/null +++ b/source/classes/SFX.py @@ -0,0 +1,191 @@ +import random +from Utils import int16_as_bytes + + +class SFX(object): + + def __init__(self, name, sfx_set, orig_id, addr, chain, accomp=False): + self.name = name + self.sfx_set = sfx_set + self.orig_id = orig_id + self.addr = addr + self.chain = chain + self.accomp = accomp + + self.target_set = None + self.target_id = None + self.target_chain = None + + +def init_sfx_data(): + sfx_pool = [SFX('Slash1', 0x02, 0x01, 0x2614, []), SFX('Slash2', 0x02, 0x02, 0x2625, []), + SFX('Slash3', 0x02, 0x03, 0x2634, []), SFX('Slash4', 0x02, 0x04, 0x2643, []), + SFX('Wall clink', 0x02, 0x05, 0x25DD, []), SFX('Bombable door clink', 0x02, 0x06, 0x25D7, []), + SFX('Fwoosh shooting', 0x02, 0x07, 0x25B7, []), SFX('Arrow hitting wall', 0x02, 0x08, 0x25E3, []), + SFX('Boomerang whooshing', 0x02, 0x09, 0x25AD, []), SFX('Hookshot', 0x02, 0x0A, 0x25C7, []), + SFX('Placing bomb', 0x02, 0x0B, 0x2478, []), + SFX('Bomb exploding/Quake/Bombos/Exploding wall', 0x02, 0x0C, 0x269C, []), + SFX('Powder', 0x02, 0x0D, 0x2414, [0x3f]), SFX('Fire rod shot', 0x02, 0x0E, 0x2404, []), + SFX('Ice rod shot', 0x02, 0x0F, 0x24C3, []), SFX('Hammer use', 0x02, 0x10, 0x23FA, []), + SFX('Hammering peg', 0x02, 0x11, 0x23F0, []), SFX('Digging', 0x02, 0x12, 0x23CD, []), + SFX('Flute use', 0x02, 0x13, 0x23A0, [0x3e]), SFX('Cape on', 0x02, 0x14, 0x2380, []), + SFX('Cape off/Wallmaster grab', 0x02, 0x15, 0x2390, []), SFX('Staircase', 0x02, 0x16, 0x232C, []), + SFX('Staircase', 0x02, 0x17, 0x2344, []), SFX('Staircase', 0x02, 0x18, 0x2356, []), + SFX('Staircase', 0x02, 0x19, 0x236E, []), SFX('Tall grass/Hammer hitting bush', 0x02, 0x1A, 0x2316, []), + SFX('Mire shallow water', 0x02, 0x1B, 0x2307, []), SFX('Shallow water', 0x02, 0x1C, 0x2301, []), + SFX('Lifting object', 0x02, 0x1D, 0x22BB, []), SFX('Cutting grass', 0x02, 0x1E, 0x2577, []), + SFX('Item breaking', 0x02, 0x1F, 0x22E9, []), SFX('Item falling in pit', 0x02, 0x20, 0x22DA, []), + SFX('Bomb hitting ground/General bang', 0x02, 0x21, 0x22CF, []), + SFX('Pushing object/Armos bounce', 0x02, 0x22, 0x2107, []), SFX('Boots dust', 0x02, 0x23, 0x22B1, []), + SFX('Splashing', 0x02, 0x24, 0x22A5, [0x3d]), SFX('Mire shallow water again?', 0x02, 0x25, 0x2296, []), + SFX('Link taking damage', 0x02, 0x26, 0x2844, []), SFX('Fainting', 0x02, 0x27, 0x2252, []), + SFX('Item splash', 0x02, 0x28, 0x2287, []), SFX('Rupee refill', 0x02, 0x29, 0x243F, [0x3b]), + SFX('Fire rod shot hitting wall/Bombos spell', 0x02, 0x2A, 0x2033, []), + SFX('Heart beep/Text box', 0x02, 0x2B, 0x1FF2, []), SFX('Sword up', 0x02, 0x2C, 0x1FD9, [0x3a]), + SFX('Magic drain', 0x02, 0x2D, 0x20A6, []), SFX('GT opening', 0x02, 0x2E, 0x1FCA, [0x39]), + SFX('GT opening/Water drain', 0x02, 0x2F, 0x1F47, [0x38]), SFX('Cucco', 0x02, 0x30, 0x1EF1, []), + SFX('Fairy', 0x02, 0x31, 0x20CE, []), SFX('Bug net', 0x02, 0x32, 0x1D47, []), + SFX('Teleport2', 0x02, 0x33, 0x1CDC, [], True), SFX('Teleport1', 0x02, 0x34, 0x1F6F, [0x33]), + SFX('Quake/Vitreous/Zora king/Armos/Pyramid/Lanmo', 0x02, 0x35, 0x1C67, [0x36]), + SFX('Mire entrance (extends above)', 0x02, 0x36, 0x1C64, [], True), + SFX('Spin charged', 0x02, 0x37, 0x1A43, []), SFX('Water sound', 0x02, 0x38, 0x1F6F, [], True), + SFX('GT opening thunder', 0x02, 0x39, 0x1F9C, [], True), SFX('Sword up', 0x02, 0x3A, 0x1FE7, [], True), + SFX('Quiet rupees', 0x02, 0x3B, 0x2462, [], True), SFX('Error beep', 0x02, 0x3C, 0x1A37, []), + SFX('Big splash', 0x02, 0x3D, 0x22AB, [], True), SFX('Flute again', 0x02, 0x3E, 0x23B5, [], True), + SFX('Powder paired', 0x02, 0x3F, 0x2435, [], True), + + SFX('Sword beam', 0x03, 0x01, 0x1A18, []), + SFX('TR opening', 0x03, 0x02, 0x254E, []), SFX('Pyramid hole', 0x03, 0x03, 0x224A, []), + SFX('Angry soldier', 0x03, 0x04, 0x220E, []), SFX('Lynel shot/Javelin toss', 0x03, 0x05, 0x25B7, []), + SFX('BNC swing/Phantom ganon/Helma tail/Arrghus swoosh', 0x03, 0x06, 0x21F5, []), + SFX('Cannon fire', 0x03, 0x07, 0x223D, []), SFX('Damage to enemy; $0BEX.4=1', 0x03, 0x08, 0x21E6, []), + SFX('Enemy death', 0x03, 0x09, 0x21C1, []), SFX('Collecting rupee', 0x03, 0x0A, 0x21A9, []), + SFX('Collecting heart', 0x03, 0x0B, 0x2198, []), + SFX('Non-blank text character', 0x03, 0x0C, 0x218E, []), + SFX('HUD heart (used explicitly by sanc heart?)', 0x03, 0x0D, 0x21B5, []), + SFX('Opening chest', 0x03, 0x0E, 0x2182, []), + SFX('♪Do do do doooooo♫', 0x03, 0x0F, 0x24B9, [0x3C, 0x3D, 0x3E, 0x3F]), + SFX('Opening/Closing map (paired)', 0x03, 0x10, 0x216D, [0x3b]), + SFX('Opening item menu/Bomb shop guy breathing', 0x03, 0x11, 0x214F, []), + SFX('Closing item menu/Bomb shop guy breathing', 0x03, 0x12, 0x215E, []), + SFX('Throwing object (sprites use it as well)/Stalfos jump', 0x03, 0x13, 0x213B, []), + SFX('Key door/Trinecks/Dash key landing/Stalfos Knight collapse', 0x03, 0x14, 0x246C, []), + SFX('Door closing/OW door opening/Chest opening (w/ $29 in $012E)', 0x03, 0x15, 0x212F, []), + SFX('Armos Knight thud', 0x03, 0x16, 0x2123, []), SFX('Rat squeak', 0x03, 0x17, 0x25A6, []), + SFX('Dragging/Mantle moving', 0x03, 0x18, 0x20DD, []), + SFX('Fireball/Laser shot; Somehow used by Trinexx???', 0x03, 0x19, 0x250A, []), + SFX('Chest reveal jingle ', 0x03, 0x1A, 0x1E8A, [0x38]), + SFX('Puzzle jingle', 0x03, 0x1B, 0x20B6, [0x3a]), SFX('Damage to enemy', 0x03, 0x1C, 0x1A62, []), + SFX('Potion refill/Magic drain', 0x03, 0x1D, 0x20A6, []), + SFX('Flapping (Duck/Cucco swarm/Ganon bats/Keese/Raven/Vulture)', 0x03, 0x1E, 0x2091, []), + SFX('Link falling', 0x03, 0x1F, 0x204B, []), SFX('Menu/Text cursor moved', 0x03, 0x20, 0x276C, []), + SFX('Damage to boss', 0x03, 0x21, 0x27E2, []), SFX('Boss dying/Deleting file', 0x03, 0x22, 0x26CF, []), + SFX('Spin attack/Medallion swoosh', 0x03, 0x23, 0x2001, [0x39]), + SFX('OW map perspective change', 0x03, 0x24, 0x2043, []), + SFX('Pressure switch', 0x03, 0x25, 0x1E9D, []), + SFX('Lightning/Game over/Laser/Ganon bat/Trinexx lunge', 0x03, 0x26, 0x1E7B, []), + SFX('Agahnim charge', 0x03, 0x27, 0x1E40, []), SFX('Agahnim/Ganon teleport', 0x03, 0x28, 0x26F7, []), + SFX('Agahnim shot', 0x03, 0x29, 0x1E21, []), + SFX('Somaria/Byrna/Ether spell/Helma fire ball', 0x03, 0x2A, 0x1E12, []), + SFX('Electrocution', 0x03, 0x2B, 0x1DF3, []), SFX('Bees', 0x03, 0x2C, 0x1DC0, []), + SFX('Milestone, also via text', 0x03, 0x2D, 0x1DA9, [0x37]), + SFX('Collecting heart container', 0x03, 0x2E, 0x1D5D, [0x35, 0x34]), + SFX('Collecting absorbable key', 0x03, 0x2F, 0x1D80, [0x33]), + SFX('Byrna spark/Item plop/Magic bat zap/Blob emerge', 0x03, 0x30, 0x1B53, []), + SFX('Sprite falling/Moldorm shuffle', 0x03, 0x31, 0x1ACA, []), + SFX('Bumper boing/Somaria punt/Blob transmutation/Sprite boings', 0x03, 0x32, 0x1A78, []), + SFX('Jingle (paired $2F→$33)', 0x03, 0x33, 0x1D93, [], True), + SFX('Depressing jingle (paired $2E→$35→$34)', 0x03, 0x34, 0x1D66, [], True), + SFX('Ugly jingle (paired $2E→$35→$34)', 0x03, 0x35, 0x1D73, [], True), + SFX('Wizzrobe shot/Helma fireball split/Mothula beam/Blue balls', 0x03, 0x36, 0x1AA7, []), + SFX('Dinky jingle (paired $2D→$37)', 0x03, 0x37, 0x1DB4, [], True), + SFX('Apathetic jingle (paired $1A→$38)', 0x03, 0x38, 0x1E93, [], True), + SFX('Quiet swish (paired $23→$39)', 0x03, 0x39, 0x2017, [], True), + SFX('Defective jingle (paired $1B→$3A)', 0x03, 0x3A, 0x20C0, [], True), + SFX('Petulant jingle (paired $10→$3B)', 0x03, 0x3B, 0x2176, [], True), + SFX('Triumphant jingle (paired $0F→$3C→$3D→$3E→$3F)', 0x03, 0x3C, 0x248A, [], True), + SFX('Less triumphant jingle ($0F→$3C→$3D→$3E→$3F)', 0x03, 0x3D, 0x2494, [], True), + SFX('"You tried, I guess" jingle (paired $0F→$3C→$3D→$3E→$3F)', 0x03, 0x3E, 0x249E, [], True), + SFX('"You didn\'t really try" jingle (paired $0F→$3C→$3D→$3E→$3F)', 0x03, 0x3F, 0x2480, [], True)] + return sfx_pool + + +def shuffle_sfx_data(): + sfx_pool = init_sfx_data() + sfx_map = {2: {}, 3: {}} + accompaniment_map = {2: set(), 3: set()} + candidates = [] + for sfx in sfx_pool: + sfx_map[sfx.sfx_set][sfx.orig_id] = sfx + if not sfx.accomp: + candidates.append((sfx.sfx_set, sfx.orig_id)) + else: + accompaniment_map[sfx.sfx_set].add(sfx.orig_id) + chained_sfx = [x for x in sfx_pool if len(x.chain) > 0] + + random.shuffle(candidates) + + # place chained sfx first + random.shuffle(chained_sfx) # todo: sort largest to smallest + chained_sfx = sorted(chained_sfx, key=lambda x: len(x.chain), reverse=True) + for chained in chained_sfx: + chosen_slot = next(x for x in candidates if len(accompaniment_map[x[0]]) - len(chained.chain) >= 0) + if chosen_slot is None: + raise Exception('Something went wrong with sfx chains') + chosen_set, chosen_id = chosen_slot + chained.target_set, chained.target_id = chosen_slot + chained.target_chain = [] + for downstream in chained.chain: + next_slot = accompaniment_map[chosen_set].pop() + ds_acc = sfx_map[chained.sfx_set][downstream] + ds_acc.target_set, ds_acc.target_id = chosen_set, next_slot + chained.target_chain.append(next_slot) + candidates.remove(chosen_slot) + sfx_pool.remove(chained) + + unchained_sfx = [x for x in sfx_pool if not x.accomp] + # do the rest + for sfx in unchained_sfx: + chosen_slot = candidates.pop() + sfx.target_set, sfx.target_id = chosen_slot + + return sfx_map + + +sfx_table = { + 2: 0x1a8c29, + 3: 0x1A8D25 +} + +# 0x1a8c29 +# d8059 + +sfx_accompaniment_table = { + 2: 0x1A8CA7, + 3: 0x1A8DA3 +} + + +def randomize_sfx(rom): + sfx_map = shuffle_sfx_data() + + for shuffled_sfx in sfx_map.values(): + for sfx in shuffled_sfx.values(): + base_address = sfx_table[sfx.target_set] + rom.write_bytes(base_address + (sfx.target_id * 2) - 2, int16_as_bytes(sfx.addr)) + ac_base = sfx_accompaniment_table[sfx.target_set] + last = sfx.target_id + if sfx.target_chain: + for chained in sfx.target_chain: + rom.write_byte(ac_base + last - 1, chained) + last = chained + rom.write_byte(ac_base + last - 1, 0) + + + + + + + + + diff --git a/source/classes/constants.py b/source/classes/constants.py index 04cbde2e..e03fba44 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -107,7 +107,8 @@ SETTINGSTOPROCESS = { "menuspeed": "fastmenu", "owpalettes": "ow_palettes", "uwpalettes": "uw_palettes", - "reduce_flashing": "reduce_flashing" + "reduce_flashing": "reduce_flashing", + "shuffle_sfx": "shuffle_sfx", }, "generation": { "createspoiler": "create_spoiler", diff --git a/source/gui/adjust/overview.py b/source/gui/adjust/overview.py index 4ae57e2e..7e16b1a9 100644 --- a/source/gui/adjust/overview.py +++ b/source/gui/adjust/overview.py @@ -103,6 +103,7 @@ def adjust_page(top, parent, settings): "quickswap": "quickswap", "nobgm": "disablemusic", "reduce_flashing": "reduce_flashing", + "shuffle_sfx": "shuffle_sfx", } guiargs = Namespace() for option in options: From 4c15c193f42e6156e66e5558ef8daf68210754e3 Mon Sep 17 00:00:00 2001 From: StructuralMike <66819228+StructuralMike@users.noreply.github.com> Date: Thu, 29 Jul 2021 15:27:49 +0200 Subject: [PATCH 02/11] Set bomb upgrades as advancement before beemizer Beemizer can replace bomb upgrades in bomblogic unless they're tagged as advancement earlier --- ItemList.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ItemList.py b/ItemList.py index 57566418..9128436a 100644 --- a/ItemList.py +++ b/ItemList.py @@ -301,6 +301,11 @@ def generate_itempool(world, player): world.get_location(location, player).event = True world.get_location(location, player).locked = True + if world.bomblogic[player]: + for item in world.itempool: + if item.name == 'Bomb Upgrade (+10)' and item.player == player: + item.advancement = True + if world.shopsanity[player]: for shop in world.shops[player]: if shop.region.name in shop_to_location_table: @@ -524,9 +529,6 @@ def set_up_shops(world, player): cap_shop = world.get_region('Capacity Upgrade', player).shop cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro if world.bomblogic[player]: - for item in world.itempool: - if item.name == 'Bomb Upgrade (+10)' and item.player == player: - item.advancement = True if world.shopsanity[player]: removals = [item for item in world.itempool if item.name == 'Bomb Upgrade (+5)' and item.player == player] for remove in removals: From c06477c0f20fb00fdbd80b893e10ffb7b6a3ad42 Mon Sep 17 00:00:00 2001 From: StructuralMike <66819228+StructuralMike@users.noreply.github.com> Date: Thu, 29 Jul 2021 15:51:56 +0200 Subject: [PATCH 03/11] Update ItemList.py --- ItemList.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ItemList.py b/ItemList.py index 9128436a..e79392b8 100644 --- a/ItemList.py +++ b/ItemList.py @@ -301,11 +301,6 @@ def generate_itempool(world, player): world.get_location(location, player).event = True world.get_location(location, player).locked = True - if world.bomblogic[player]: - for item in world.itempool: - if item.name == 'Bomb Upgrade (+10)' and item.player == player: - item.advancement = True - if world.shopsanity[player]: for shop in world.shops[player]: if shop.region.name in shop_to_location_table: @@ -323,6 +318,11 @@ def generate_itempool(world, player): p_item = next(item for item in items if item.name == potion and item.player == player) p_item.priority = True # don't beemize one of each potion + if world.bomblogic[player]: + for item in items: + if item.name == 'Bomb Upgrade (+10)' and item.player == player: + item.advancement = True + world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms if clock_mode is not None: From bcaee735a2835ca2d3ef3b99a24d29ae9b40be2c Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 2 Aug 2021 15:24:13 -0600 Subject: [PATCH 04/11] SFX shuffle added --- Main.py | 2 +- RELEASENOTES.md | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Main.py b/Main.py index e44c804e..df4426e8 100644 --- a/Main.py +++ b/Main.py @@ -28,7 +28,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names -__version__ = '0.5.0.1-u' +__version__ = '0.5.0.2-u' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c0fae696..b6be375d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,12 +1,22 @@ # New Features -Bomb Logic added as an option. This removes your ability to use bombs until you find a "bomb bag", a +10 Bomb Capacity item. It is accounted for in the logic, so you aren't expected to get items behind bomb walls until you have found the bomb capacity item. The upgrades are removed from the upgrade fairy as well. +## Shuffle SFX + +Shuffles a large portion of the sounds effects. Can be used with the adjuster. + +CLI: ```--shuffle_sfx``` -``` ---bomblogic -``` +## Bomb Logic + +When enabling this option, you do not start with bomb capacity but rather you must find 1 of 2 bomb bags. (They are represented by the +10 capacity item.) Bomb capacity upgrades are otherwise unavailable. + +CLI: ```--bomblogic``` + # Bug Fixes and Notes. + +* 0.5.0.2 + * --shuffle_sfx option added * 0.5.0.1 * --bomblogic option added * 0.5.0.0 From 44efe51e022a8fe509e95344085989791c038f9f Mon Sep 17 00:00:00 2001 From: StructuralMike <66819228+StructuralMike@users.noreply.github.com> Date: Wed, 11 Aug 2021 10:22:54 +0200 Subject: [PATCH 05/11] Changing the cli to be bombbags over bomblogic Bombbags seemed better over bomblogic which is more ambiguous when considering new stuff OW and the bomb-only modes. Also added bombbags to the example yaml --- BaseClasses.py | 8 ++--- CLI.py | 4 +-- ItemList.py | 36 +++++++++---------- Main.py | 4 +-- Mystery.py | 2 +- RELEASENOTES.md | 4 +-- Rom.py | 6 ++-- Rules.py | 2 +- mystery_example.yml | 3 ++ resources/app/cli/args.json | 2 +- resources/app/cli/lang/en.json | 2 +- resources/app/gui/lang/en.json | 2 +- resources/app/gui/randomize/item/widgets.json | 2 +- source/classes/constants.py | 2 +- 14 files changed, 41 insertions(+), 38 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index b7a80af7..e8e471c9 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -114,7 +114,7 @@ class World(object): set_player_attr('compassshuffle', False) set_player_attr('keyshuffle', False) set_player_attr('bigkeyshuffle', False) - set_player_attr('bomblogic', False) + set_player_attr('bombbags', False) set_player_attr('difficulty_requirements', None) set_player_attr('boss_shuffle', 'none') set_player_attr('enemy_shuffle', 'none') @@ -687,7 +687,7 @@ class CollectionState(object): # In the future, this can be used to check if the player starts without bombs def can_use_bombs(self, player): - return (not self.world.bomblogic[player] or self.has('Bomb Upgrade (+10)', player)) + return (not self.world.bombbags[player] or self.has('Bomb Upgrade (+10)', player)) def can_hit_crystal(self, player): return (self.can_use_bombs(player) @@ -2014,7 +2014,7 @@ class Spoiler(object): 'logic': self.world.logic, 'mode': self.world.mode, 'retro': self.world.retro, - 'bomblogic': self.world.bomblogic, + 'bombbags': self.world.bombbags, 'weapons': self.world.swords, 'goal': self.world.goal, 'shuffle': self.world.shuffle, @@ -2113,7 +2113,7 @@ class Spoiler(object): outfile.write('Experimental: %s\n' % ('Yes' if self.metadata['experimental'][player] else 'No')) outfile.write('Key Drops shuffled: %s\n' % ('Yes' if self.metadata['keydropshuffle'][player] else 'No')) outfile.write(f"Shopsanity: {'Yes' if self.metadata['shopsanity'][player] else 'No'}\n") - outfile.write('Bomblogic: %s\n' % ('Yes' if self.metadata['bomblogic'][player] else 'No')) + outfile.write('Bombbags: %s\n' % ('Yes' if self.metadata['bombbags'][player] else 'No')) if self.doors: outfile.write('\n\nDoors:\n\n') outfile.write('\n'.join( diff --git a/CLI.py b/CLI.py index 911f03ab..0730eb03 100644 --- a/CLI.py +++ b/CLI.py @@ -96,7 +96,7 @@ def parse_cli(argv, no_defaults=False): for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', - 'bomblogic', + 'bombbags', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', @@ -127,7 +127,7 @@ def parse_settings(): settings = { "lang": "en", "retro": False, - "bomblogic": False, + "bombbags": False, "mode": "open", "logic": "noglitches", "goal": "ganon", diff --git a/ItemList.py b/ItemList.py index e79392b8..f227856c 100644 --- a/ItemList.py +++ b/ItemList.py @@ -37,7 +37,7 @@ Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother', - 'retro', 'bomblogic', + 'retro', 'bombbags', 'extras', 'progressive_sword_limit', 'progressive_shield_limit', 'progressive_armor_limit', 'progressive_bottle_limit', 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) @@ -61,7 +61,7 @@ difficulties = { timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10, - bomblogic = ['Bomb Upgrade (+10)'] * 2, + bombbags = ['Bomb Upgrade (+10)'] * 2, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 4, progressive_shield_limit = 3, @@ -87,7 +87,7 @@ difficulties = { timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, - bomblogic = ['Bomb Upgrade (+10)'] * 2, + bombbags = ['Bomb Upgrade (+10)'] * 2, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 3, progressive_shield_limit = 2, @@ -113,7 +113,7 @@ difficulties = { timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, - bomblogic = ['Bomb Upgrade (+10)'] * 2, + bombbags = ['Bomb Upgrade (+10)'] * 2, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 2, progressive_shield_limit = 1, @@ -254,10 +254,10 @@ def generate_itempool(world, player): # set up item pool if world.custom: - (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bomblogic[player], world.customitemarray) + (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbags[player], world.customitemarray) world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) else: - (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bomblogic[player], world.doorShuffle[player], world.logic[player]) + (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbags[player], world.doorShuffle[player], world.logic[player]) if player in world.pool_adjustment.keys(): amt = world.pool_adjustment[player] @@ -287,7 +287,7 @@ def generate_itempool(world, player): if item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']: if item not in possible_weapons: possible_weapons.append(item) - if not world.bomblogic[player] and item in ['Bombs (10)']: + if not world.bombbags[player] and item in ['Bombs (10)']: if item not in possible_weapons and world.doorShuffle[player] != 'crossed': possible_weapons.append(item) starting_weapon = random.choice(possible_weapons) @@ -318,7 +318,7 @@ def generate_itempool(world, player): p_item = next(item for item in items if item.name == potion and item.player == player) p_item.priority = True # don't beemize one of each potion - if world.bomblogic[player]: + if world.bombbags[player]: for item in items: if item.name == 'Bomb Upgrade (+10)' and item.player == player: item.advancement = True @@ -528,7 +528,7 @@ def set_up_shops(world, player): rss.locked = True cap_shop = world.get_region('Capacity Upgrade', player).shop cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro - if world.bomblogic[player]: + if world.bombbags[player]: if world.shopsanity[player]: removals = [item for item in world.itempool if item.name == 'Bomb Upgrade (+5)' and item.player == player] for remove in removals: @@ -536,7 +536,7 @@ def set_up_shops(world, player): world.itempool.append(ItemFactory('Rupees (50)', player)) # replace the bomb upgrade else: cap_shop = world.get_region('Capacity Upgrade', player).shop - cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bomblogic + cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bombbags def customize_shops(world, player): @@ -578,7 +578,7 @@ def customize_shops(world, player): shop.shopkeeper_config = shopkeeper # handle capacity upgrades - randomly choose a bomb bunch or arrow bunch to become capacity upgrades if world.difficulty[player] == 'normal': - if not found_bomb_upgrade and len(possible_replacements) > 0 and not world.bomblogic[player]: + if not found_bomb_upgrade and len(possible_replacements) > 0 and not world.bombbags[player]: choices = [] for shop, idx, loc, item in possible_replacements: if item.name in ['Bombs (3)', 'Bombs (10)']: @@ -726,7 +726,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)' 'Rupees (100)': 100, 'Rupees (300)': 300} -def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bomblogic, door_shuffle, logic): +def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbags, door_shuffle, logic): pool = [] placed_items = {} precollected_items = [] @@ -773,10 +773,10 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, diff = difficulties[difficulty] pool.extend(diff.baseitems) - if bomblogic: + if bombbags: pool = [item.replace('Bomb Upgrade (+5)','Rupees (5)') for item in pool] pool = [item.replace('Bomb Upgrade (+10)','Rupees (5)') for item in pool] - pool.extend(diff.bomblogic) + pool.extend(diff.bombbags) # expert+ difficulties produce the same contents for # all bottles, since only one bottle is available @@ -872,7 +872,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, pool.extend(['Small Key (Universal)']) return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) -def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bomblogic, customitemarray): +def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbags, customitemarray): if isinstance(customitemarray,dict) and 1 in customitemarray: customitemarray = customitemarray[1] pool = [] @@ -988,9 +988,9 @@ def test(): for shuffle in ['full', 'insanity_legacy']: for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']: for retro in [True, False]: - for bomblogic in [True, False]: + for bombbags in [True, False]: for door_shuffle in ['basic', 'crossed', 'vanilla']: - out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bomblogic, door_shuffle, logic) + out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbags, door_shuffle, logic) count = len(out[0]) + len(out[1]) correct_count = total_items_to_place @@ -1000,7 +1000,7 @@ def test(): if retro: correct_count += 28 try: - assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bomblogic)) + assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbags)) except AssertionError as e: print(e) diff --git a/Main.py b/Main.py index df4426e8..2d49300b 100644 --- a/Main.py +++ b/Main.py @@ -79,7 +79,7 @@ def main(args, seed=None, fish=None): world.compassshuffle = args.compassshuffle.copy() world.keyshuffle = args.keyshuffle.copy() world.bigkeyshuffle = args.bigkeyshuffle.copy() - world.bomblogic = args.bomblogic.copy() + world.bombbags = args.bombbags.copy() world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)} world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)} world.crystals_ganon_orig = args.crystals_ganon.copy() @@ -384,7 +384,7 @@ def copy_world(world): ret.compassshuffle = world.compassshuffle.copy() ret.keyshuffle = world.keyshuffle.copy() ret.bigkeyshuffle = world.bigkeyshuffle.copy() - ret.bomblogic = world.bomblogic.copy() + ret.bombbags = world.bombbags.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() diff --git a/Mystery.py b/Mystery.py index 306b5c27..d02bd423 100644 --- a/Mystery.py +++ b/Mystery.py @@ -176,7 +176,7 @@ def roll_settings(weights): ret.retro = True ret.retro = get_choice('retro') == 'on' # this overrides world_state if used - ret.bomblogic = get_choice('bomblogic') == 'on' + ret.bombbags = get_choice('bombbags') == 'on' ret.hints = get_choice('hints') == 'on' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b6be375d..ca2f0e13 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,7 +10,7 @@ CLI: ```--shuffle_sfx``` When enabling this option, you do not start with bomb capacity but rather you must find 1 of 2 bomb bags. (They are represented by the +10 capacity item.) Bomb capacity upgrades are otherwise unavailable. -CLI: ```--bomblogic``` +CLI: ```--bombbags``` # Bug Fixes and Notes. @@ -18,7 +18,7 @@ CLI: ```--bomblogic``` * 0.5.0.2 * --shuffle_sfx option added * 0.5.0.1 - * --bomblogic option added + * --bombbags option added * 0.5.0.0 * Handles headered roms for enemizer (Thanks compiling) * Warning added for earlier version of python (Thanks compiling) diff --git a/Rom.py b/Rom.py index 8d86a16e..c2617aa0 100644 --- a/Rom.py +++ b/Rom.py @@ -1051,7 +1051,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x184000, [ # original_item, limit, replacement_item, filler 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees - 0x51, 0x00 if world.bomblogic[player] else 0x06, 0x31 if world.bomblogic[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bomblogic -> turns into Bombs (10) + 0x51, 0x00 if world.bombbags[player] else 0x06, 0x31 if world.bombbags[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bombbags -> turns into Bombs (10) 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade 0x58, 0x01, 0x36 if world.retro[player] else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode) 0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20 @@ -1188,7 +1188,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): equip[0x36C] = 0x18 equip[0x36D] = 0x18 equip[0x379] = 0x68 - if world.bomblogic[player]: + if world.bombbags[player]: starting_max_bombs = 0 else: starting_max_bombs = 10 @@ -1483,7 +1483,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x180188, [0, 0, 10]) # Zelda respawn refills (magic, bombs, arrows) rom.write_bytes(0x18018B, [0, 0, 10]) # Mantle respawn refills (magic, bombs, arrows) bow_max, bow_small = 70, 10 - elif uncle_location.item is not None and uncle_location.item.name in ['Bomb Upgrade (+10)' if world.bomblogic[player] else 'Bombs (10)']: + elif uncle_location.item is not None and uncle_location.item.name in ['Bomb Upgrade (+10)' if world.bombbags[player] else 'Bombs (10)']: rom.write_byte(0x18004E, 2) # Escape Fill (bombs) rom.write_bytes(0x180185, [0, 50, 0]) # Uncle respawn refills (magic, bombs, arrows) rom.write_bytes(0x180188, [0, 3, 0]) # Zelda respawn refills (magic, bombs, arrows) diff --git a/Rules.py b/Rules.py index 15847491..8252cfeb 100644 --- a/Rules.py +++ b/Rules.py @@ -1162,7 +1162,7 @@ def standard_rules(world, player): def bomb_escape_rule(): loc = world.get_location("Link's Uncle", player) - return loc.item and loc.item.name in ['Bomb Upgrade (+10)' if world.bomblogic[player] else 'Bombs (10)'] + return loc.item and loc.item.name in ['Bomb Upgrade (+10)' if world.bombbags[player] else 'Bombs (10)'] def standard_escape_rule(state): return state.can_kill_most_things(player) or bomb_escape_rule() diff --git a/mystery_example.yml b/mystery_example.yml index 028e7110..084e3e14 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -16,6 +16,9 @@ pot_shuffle: on: 1 off: 3 + bombbags: + on: 1 + off: 4 entrance_shuffle: none: 15 dungeonssimple: 3 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index f749e20a..7fa7a52c 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -224,7 +224,7 @@ "type": "bool", "help": "suppress" }, - "bomblogic": { + "bombbags": { "action": "store_true", "type": "bool" }, diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 3b96b6f8..eb83d4e9 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -264,7 +264,7 @@ "and a few other little things make this more like Zelda-1. (default: %(default)s)" ], "pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"], - "bomblogic": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], + "bombbags": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], "startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ], "usestartinventory": [ "Toggle usage of Starting Inventory." ], "custom": [ "Not supported." ], diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index fe7fa9d6..54994697 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -192,7 +192,7 @@ "randomizer.item.hints": "Include Helpful Hints", "randomizer.item.retro": "Retro mode (universal keys)", "randomizer.item.pseudoboots": "Start with Pseudo Boots", - "randomizer.item.bomblogic": "Bomblogic", + "randomizer.item.bombbags": "Bombbags", "randomizer.item.worldstate": "World State", "randomizer.item.worldstate.standard": "Standard", diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 15d049c8..e81f50d5 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -1,7 +1,7 @@ { "checkboxes": { "retro": { "type": "checkbox" }, - "bomblogic": { "type": "checkbox" }, + "bombbags": { "type": "checkbox" }, "shopsanity": { "type": "checkbox" }, "hints": { "type": "checkbox" diff --git a/source/classes/constants.py b/source/classes/constants.py index 90411547..86dc31bc 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -57,7 +57,7 @@ SETTINGSTOPROCESS = { "item": { "hints": "hints", "retro": "retro", - "bomblogic": "bomblogic", + "bombbags": "bombbags", "shopsanity": "shopsanity", "pseudoboots": "pseudoboots", "worldstate": "mode", From dcba2be7574c5bb6dd0bd7f4da33f8c8863db439 Mon Sep 17 00:00:00 2001 From: StructuralMike <66819228+StructuralMike@users.noreply.github.com> Date: Wed, 11 Aug 2021 16:29:12 +0200 Subject: [PATCH 06/11] Bombbag instead of Bombbags --- BaseClasses.py | 8 ++--- CLI.py | 4 +-- ItemList.py | 36 +++++++++---------- Main.py | 4 +-- Mystery.py | 2 +- RELEASENOTES.md | 4 +-- Rom.py | 6 ++-- Rules.py | 2 +- mystery_example.yml | 2 +- resources/app/cli/args.json | 2 +- resources/app/cli/lang/en.json | 2 +- resources/app/gui/lang/en.json | 2 +- resources/app/gui/randomize/item/widgets.json | 2 +- source/classes/constants.py | 2 +- 14 files changed, 39 insertions(+), 39 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index e8e471c9..69669a33 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -114,7 +114,7 @@ class World(object): set_player_attr('compassshuffle', False) set_player_attr('keyshuffle', False) set_player_attr('bigkeyshuffle', False) - set_player_attr('bombbags', False) + set_player_attr('bombbag', False) set_player_attr('difficulty_requirements', None) set_player_attr('boss_shuffle', 'none') set_player_attr('enemy_shuffle', 'none') @@ -687,7 +687,7 @@ class CollectionState(object): # In the future, this can be used to check if the player starts without bombs def can_use_bombs(self, player): - return (not self.world.bombbags[player] or self.has('Bomb Upgrade (+10)', player)) + return (not self.world.bombbag[player] or self.has('Bomb Upgrade (+10)', player)) def can_hit_crystal(self, player): return (self.can_use_bombs(player) @@ -2014,7 +2014,7 @@ class Spoiler(object): 'logic': self.world.logic, 'mode': self.world.mode, 'retro': self.world.retro, - 'bombbags': self.world.bombbags, + 'bombbag': self.world.bombbag, 'weapons': self.world.swords, 'goal': self.world.goal, 'shuffle': self.world.shuffle, @@ -2113,7 +2113,7 @@ class Spoiler(object): outfile.write('Experimental: %s\n' % ('Yes' if self.metadata['experimental'][player] else 'No')) outfile.write('Key Drops shuffled: %s\n' % ('Yes' if self.metadata['keydropshuffle'][player] else 'No')) outfile.write(f"Shopsanity: {'Yes' if self.metadata['shopsanity'][player] else 'No'}\n") - outfile.write('Bombbags: %s\n' % ('Yes' if self.metadata['bombbags'][player] else 'No')) + outfile.write('Bombbag: %s\n' % ('Yes' if self.metadata['bombbag'][player] else 'No')) if self.doors: outfile.write('\n\nDoors:\n\n') outfile.write('\n'.join( diff --git a/CLI.py b/CLI.py index 0730eb03..bb9ab0a2 100644 --- a/CLI.py +++ b/CLI.py @@ -96,7 +96,7 @@ def parse_cli(argv, no_defaults=False): for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', - 'bombbags', + 'bombbag', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', @@ -127,7 +127,7 @@ def parse_settings(): settings = { "lang": "en", "retro": False, - "bombbags": False, + "bombbag": False, "mode": "open", "logic": "noglitches", "goal": "ganon", diff --git a/ItemList.py b/ItemList.py index f227856c..2edad3fe 100644 --- a/ItemList.py +++ b/ItemList.py @@ -37,7 +37,7 @@ Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother', - 'retro', 'bombbags', + 'retro', 'bombbag', 'extras', 'progressive_sword_limit', 'progressive_shield_limit', 'progressive_armor_limit', 'progressive_bottle_limit', 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) @@ -61,7 +61,7 @@ difficulties = { timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10, - bombbags = ['Bomb Upgrade (+10)'] * 2, + bombbag = ['Bomb Upgrade (+10)'] * 2, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 4, progressive_shield_limit = 3, @@ -87,7 +87,7 @@ difficulties = { timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, - bombbags = ['Bomb Upgrade (+10)'] * 2, + bombbag = ['Bomb Upgrade (+10)'] * 2, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 3, progressive_shield_limit = 2, @@ -113,7 +113,7 @@ difficulties = { timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, - bombbags = ['Bomb Upgrade (+10)'] * 2, + bombbag = ['Bomb Upgrade (+10)'] * 2, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 2, progressive_shield_limit = 1, @@ -254,10 +254,10 @@ def generate_itempool(world, player): # set up item pool if world.custom: - (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbags[player], world.customitemarray) + (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray) world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) else: - (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbags[player], world.doorShuffle[player], world.logic[player]) + (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player]) if player in world.pool_adjustment.keys(): amt = world.pool_adjustment[player] @@ -287,7 +287,7 @@ def generate_itempool(world, player): if item in ['Hammer', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']: if item not in possible_weapons: possible_weapons.append(item) - if not world.bombbags[player] and item in ['Bombs (10)']: + if not world.bombbag[player] and item in ['Bombs (10)']: if item not in possible_weapons and world.doorShuffle[player] != 'crossed': possible_weapons.append(item) starting_weapon = random.choice(possible_weapons) @@ -318,7 +318,7 @@ def generate_itempool(world, player): p_item = next(item for item in items if item.name == potion and item.player == player) p_item.priority = True # don't beemize one of each potion - if world.bombbags[player]: + if world.bombbag[player]: for item in items: if item.name == 'Bomb Upgrade (+10)' and item.player == player: item.advancement = True @@ -528,7 +528,7 @@ def set_up_shops(world, player): rss.locked = True cap_shop = world.get_region('Capacity Upgrade', player).shop cap_shop.inventory[1] = None # remove arrow capacity upgrades in retro - if world.bombbags[player]: + if world.bombbag[player]: if world.shopsanity[player]: removals = [item for item in world.itempool if item.name == 'Bomb Upgrade (+5)' and item.player == player] for remove in removals: @@ -536,7 +536,7 @@ def set_up_shops(world, player): world.itempool.append(ItemFactory('Rupees (50)', player)) # replace the bomb upgrade else: cap_shop = world.get_region('Capacity Upgrade', player).shop - cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bombbags + cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bombbag def customize_shops(world, player): @@ -578,7 +578,7 @@ def customize_shops(world, player): shop.shopkeeper_config = shopkeeper # handle capacity upgrades - randomly choose a bomb bunch or arrow bunch to become capacity upgrades if world.difficulty[player] == 'normal': - if not found_bomb_upgrade and len(possible_replacements) > 0 and not world.bombbags[player]: + if not found_bomb_upgrade and len(possible_replacements) > 0 and not world.bombbag[player]: choices = [] for shop, idx, loc, item in possible_replacements: if item.name in ['Bombs (3)', 'Bombs (10)']: @@ -726,7 +726,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)' 'Rupees (100)': 100, 'Rupees (300)': 300} -def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbags, door_shuffle, logic): +def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic): pool = [] placed_items = {} precollected_items = [] @@ -773,10 +773,10 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, diff = difficulties[difficulty] pool.extend(diff.baseitems) - if bombbags: + if bombbag: pool = [item.replace('Bomb Upgrade (+5)','Rupees (5)') for item in pool] pool = [item.replace('Bomb Upgrade (+10)','Rupees (5)') for item in pool] - pool.extend(diff.bombbags) + pool.extend(diff.bombbag) # expert+ difficulties produce the same contents for # all bottles, since only one bottle is available @@ -872,7 +872,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, pool.extend(['Small Key (Universal)']) return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) -def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbags, customitemarray): +def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag, customitemarray): if isinstance(customitemarray,dict) and 1 in customitemarray: customitemarray = customitemarray[1] pool = [] @@ -988,9 +988,9 @@ def test(): for shuffle in ['full', 'insanity_legacy']: for logic in ['noglitches', 'minorglitches', 'owglitches', 'nologic']: for retro in [True, False]: - for bombbags in [True, False]: + for bombbag in [True, False]: for door_shuffle in ['basic', 'crossed', 'vanilla']: - out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbags, door_shuffle, logic) + out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic) count = len(out[0]) + len(out[1]) correct_count = total_items_to_place @@ -1000,7 +1000,7 @@ def test(): if retro: correct_count += 28 try: - assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbags)) + assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag)) except AssertionError as e: print(e) diff --git a/Main.py b/Main.py index 2d49300b..60ff9a6b 100644 --- a/Main.py +++ b/Main.py @@ -79,7 +79,7 @@ def main(args, seed=None, fish=None): world.compassshuffle = args.compassshuffle.copy() world.keyshuffle = args.keyshuffle.copy() world.bigkeyshuffle = args.bigkeyshuffle.copy() - world.bombbags = args.bombbags.copy() + world.bombbag = args.bombbag.copy() world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)} world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)} world.crystals_ganon_orig = args.crystals_ganon.copy() @@ -384,7 +384,7 @@ def copy_world(world): ret.compassshuffle = world.compassshuffle.copy() ret.keyshuffle = world.keyshuffle.copy() ret.bigkeyshuffle = world.bigkeyshuffle.copy() - ret.bombbags = world.bombbags.copy() + ret.bombbag = world.bombbag.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() diff --git a/Mystery.py b/Mystery.py index d02bd423..e2095e4a 100644 --- a/Mystery.py +++ b/Mystery.py @@ -176,7 +176,7 @@ def roll_settings(weights): ret.retro = True ret.retro = get_choice('retro') == 'on' # this overrides world_state if used - ret.bombbags = get_choice('bombbags') == 'on' + ret.bombbag = get_choice('bombbag') == 'on' ret.hints = get_choice('hints') == 'on' diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ca2f0e13..76783862 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,7 +10,7 @@ CLI: ```--shuffle_sfx``` When enabling this option, you do not start with bomb capacity but rather you must find 1 of 2 bomb bags. (They are represented by the +10 capacity item.) Bomb capacity upgrades are otherwise unavailable. -CLI: ```--bombbags``` +CLI: ```--bombbag``` # Bug Fixes and Notes. @@ -18,7 +18,7 @@ CLI: ```--bombbags``` * 0.5.0.2 * --shuffle_sfx option added * 0.5.0.1 - * --bombbags option added + * --bombbag option added * 0.5.0.0 * Handles headered roms for enemizer (Thanks compiling) * Warning added for earlier version of python (Thanks compiling) diff --git a/Rom.py b/Rom.py index c2617aa0..063cab58 100644 --- a/Rom.py +++ b/Rom.py @@ -1051,7 +1051,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x184000, [ # original_item, limit, replacement_item, filler 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees - 0x51, 0x00 if world.bombbags[player] else 0x06, 0x31 if world.bombbags[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bombbags -> turns into Bombs (10) + 0x51, 0x00 if world.bombbag[player] else 0x06, 0x31 if world.bombbag[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bombbag -> turns into Bombs (10) 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade 0x58, 0x01, 0x36 if world.retro[player] else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode) 0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20 @@ -1188,7 +1188,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): equip[0x36C] = 0x18 equip[0x36D] = 0x18 equip[0x379] = 0x68 - if world.bombbags[player]: + if world.bombbag[player]: starting_max_bombs = 0 else: starting_max_bombs = 10 @@ -1483,7 +1483,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x180188, [0, 0, 10]) # Zelda respawn refills (magic, bombs, arrows) rom.write_bytes(0x18018B, [0, 0, 10]) # Mantle respawn refills (magic, bombs, arrows) bow_max, bow_small = 70, 10 - elif uncle_location.item is not None and uncle_location.item.name in ['Bomb Upgrade (+10)' if world.bombbags[player] else 'Bombs (10)']: + elif uncle_location.item is not None and uncle_location.item.name in ['Bomb Upgrade (+10)' if world.bombbag[player] else 'Bombs (10)']: rom.write_byte(0x18004E, 2) # Escape Fill (bombs) rom.write_bytes(0x180185, [0, 50, 0]) # Uncle respawn refills (magic, bombs, arrows) rom.write_bytes(0x180188, [0, 3, 0]) # Zelda respawn refills (magic, bombs, arrows) diff --git a/Rules.py b/Rules.py index 8252cfeb..23a15f67 100644 --- a/Rules.py +++ b/Rules.py @@ -1162,7 +1162,7 @@ def standard_rules(world, player): def bomb_escape_rule(): loc = world.get_location("Link's Uncle", player) - return loc.item and loc.item.name in ['Bomb Upgrade (+10)' if world.bombbags[player] else 'Bombs (10)'] + return loc.item and loc.item.name in ['Bomb Upgrade (+10)' if world.bombbag[player] else 'Bombs (10)'] def standard_escape_rule(state): return state.can_kill_most_things(player) or bomb_escape_rule() diff --git a/mystery_example.yml b/mystery_example.yml index 084e3e14..065fefc1 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -16,7 +16,7 @@ pot_shuffle: on: 1 off: 3 - bombbags: + bombbag: on: 1 off: 4 entrance_shuffle: diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 7fa7a52c..a0113222 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -224,7 +224,7 @@ "type": "bool", "help": "suppress" }, - "bombbags": { + "bombbag": { "action": "store_true", "type": "bool" }, diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index eb83d4e9..3ace31f7 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -264,7 +264,7 @@ "and a few other little things make this more like Zelda-1. (default: %(default)s)" ], "pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"], - "bombbags": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], + "bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], "startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ], "usestartinventory": [ "Toggle usage of Starting Inventory." ], "custom": [ "Not supported." ], diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 54994697..c4cd8a11 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -192,7 +192,7 @@ "randomizer.item.hints": "Include Helpful Hints", "randomizer.item.retro": "Retro mode (universal keys)", "randomizer.item.pseudoboots": "Start with Pseudo Boots", - "randomizer.item.bombbags": "Bombbags", + "randomizer.item.bombbag": "Bombbag", "randomizer.item.worldstate": "World State", "randomizer.item.worldstate.standard": "Standard", diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index e81f50d5..038e668c 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -1,7 +1,7 @@ { "checkboxes": { "retro": { "type": "checkbox" }, - "bombbags": { "type": "checkbox" }, + "bombbag": { "type": "checkbox" }, "shopsanity": { "type": "checkbox" }, "hints": { "type": "checkbox" diff --git a/source/classes/constants.py b/source/classes/constants.py index 86dc31bc..b184643b 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -57,7 +57,7 @@ SETTINGSTOPROCESS = { "item": { "hints": "hints", "retro": "retro", - "bombbags": "bombbags", + "bombbag": "bombbag", "shopsanity": "shopsanity", "pseudoboots": "pseudoboots", "worldstate": "mode", From 2f957df3fa04f07a3383440b332dd0a43bdb0f57 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Wed, 11 Aug 2021 20:59:44 -0500 Subject: [PATCH 07/11] Fixed issue with Swapped Ganon Hole not being accessible --- EntranceShuffle.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 495a99be..2d06b423 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2863,8 +2863,12 @@ default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'), ('Hookshot Cave Back Entrance', 'Hookshot Cave (Back)'), ('Mimic Cave', 'Mimic Cave'), - ('Pyramid Exit', 'Pyramid Exit Ledge') - ] + ('Pyramid Exit', 'Pyramid Exit Ledge'), + ('Pyramid Hole', 'Pyramid'), + ('Pyramid Entrance', 'Bottom of Pyramid'), + ('Inverted Pyramid Hole', 'Pyramid'), + ('Inverted Pyramid Entrance', 'Bottom of Pyramid') + ] swapped_connections = { 0x03: [ @@ -2883,18 +2887,14 @@ swapped_connections = { ] } -open_default_connections = [('Links House', 'Links House'), +open_default_connections = [('Links House', 'Links House'), ('Links House Exit', 'Links House Area'), - ('Big Bomb Shop', 'Big Bomb Shop'), - ('Pyramid Hole', 'Pyramid'), - ('Pyramid Entrance', 'Bottom of Pyramid') + ('Big Bomb Shop', 'Big Bomb Shop') ] -inverted_default_connections = [('Links House', 'Big Bomb Shop'), - ('Links House Exit', 'Big Bomb Shop Area'), - ('Big Bomb Shop', 'Links House'), - ('Inverted Pyramid Hole', 'Pyramid'), - ('Inverted Pyramid Entrance', 'Bottom of Pyramid') +inverted_default_connections = [('Links House', 'Big Bomb Shop'), + ('Links House Exit', 'Big Bomb Shop Area'), + ('Big Bomb Shop', 'Links House') ] # non shuffled dungeons @@ -2950,19 +2950,19 @@ default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert South ('Dark Death Mountain Ledge (East)', 'Turtle Rock Chest Portal'), ('Turtle Rock Isolated Ledge Exit', 'Dark Death Mountain Isolated Ledge'), ('Turtle Rock Isolated Ledge Entrance', 'Turtle Rock Eye Bridge Portal') - ] + ] open_default_dungeon_connections = [('Ganons Tower', 'Ganons Tower Portal'), ('Ganons Tower Exit', 'West Dark Death Mountain (Top)'), ('Agahnims Tower', 'Agahnims Tower Portal'), ('Agahnims Tower Exit', 'Hyrule Castle Ledge') - ] + ] inverted_default_dungeon_connections = [('Ganons Tower', 'Agahnims Tower Portal'), ('Ganons Tower Exit', 'Hyrule Castle Ledge'), ('Agahnims Tower', 'Ganons Tower Portal'), ('Agahnims Tower Exit', 'West Dark Death Mountain (Top)') - ] + ] indirect_connections = { 'Turtle Rock Ledge': 'Turtle Rock', From e3695e6d569e3fd8c240c3f7e103cd61aceec493 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 13 Aug 2021 03:37:01 -0500 Subject: [PATCH 08/11] Fixed issue with shuffling dropdowns in Mixed OW shuffle --- EntranceShuffle.py | 88 +++++++++++----------------------------------- 1 file changed, 21 insertions(+), 67 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 2d06b423..aea9ef58 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -50,18 +50,18 @@ def link_entrances(world, player): # inverted entrance mods for owid in swapped_connections.keys(): - if (world.mode[player] == 'inverted') != (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if invFlag != (owid in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): for (entrancename, exitname) in swapped_connections[owid]: try: connect_two_way(world, entrancename, exitname, player) except RuntimeError: connect_entrance(world, entrancename, exitname, player) - if (world.mode[player] == 'inverted') != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \ - (world.mode[player] == 'inverted') == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + if invFlag != (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \ + invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): connect_entrance(world, 'Death Mountain Return Cave (West)', 'Dark Death Mountain Healer Fairy', player) - elif (world.mode[player] == 'inverted') != (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \ - (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + elif invFlag != (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') and \ + invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): connect_two_way(world, 'Bumper Cave (Top)', 'Death Mountain Return Cave Exit (West)', player) # dungeon entrance shuffle @@ -239,10 +239,7 @@ def link_entrances(world, player): connect_caves(world, remaining_entrances if not invFlag else lw_dm_entrances, [], caves, player) # scramble holes - if not invFlag: - scramble_holes(world, player) - else: - scramble_inverted_holes(world, player) + scramble_holes(world, player) # place blacksmith, has limited options if invFlag: @@ -371,10 +368,7 @@ def link_entrances(world, player): connect_caves(world, lw_entrances, dw_entrances, caves, player) # scramble holes - if not invFlag: - scramble_holes(world, player) - else: - scramble_inverted_holes(world, player) + scramble_holes(world, player) doors = lw_entrances + dw_entrances @@ -632,10 +626,7 @@ def link_entrances(world, player): connect_caves(world, lw_entrances, dw_entrances, caves, player) # scramble holes - if not invFlag: - scramble_holes(world, player) - else: - scramble_inverted_holes(world, player) + scramble_holes(world, player) doors = lw_entrances + dw_entrances @@ -776,10 +767,7 @@ def link_entrances(world, player): connect_caves(world, entrances, [], caves, player) # scramble holes - if not invFlag: - scramble_holes(world, player) - else: - scramble_inverted_holes(world, player) + scramble_holes(world, player) # place remaining doors connect_doors(world, entrances, door_targets, player) @@ -1601,8 +1589,12 @@ def scramble_holes(world, player): ('Lumberjack Tree Exit', 'Lumberjack Tree (top)')] if not world.shuffle_ganon: - connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) - connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) + if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) + else: + connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player) else: hole_targets.append(('Pyramid Exit', 'Pyramid')) @@ -1620,50 +1612,12 @@ def scramble_holes(world, player): if world.shuffle_ganon: random.shuffle(hole_targets) exit, target = hole_targets.pop() - connect_two_way(world, 'Pyramid Entrance', exit, player) - connect_entrance(world, 'Pyramid Hole', target, player) - if world.shuffle[player] != 'crossed': - hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) - - random.shuffle(hole_targets) - for entrance, drop in hole_entrances: - exit, target = hole_targets.pop() - connect_two_way(world, entrance, exit, player) - connect_entrance(world, drop, target, player) - - -def scramble_inverted_holes(world, player): - hole_entrances = [('Kakariko Well Cave', 'Kakariko Well Drop'), - ('Bat Cave Cave', 'Bat Cave Drop'), - ('North Fairy Cave', 'North Fairy Cave Drop'), - ('Lost Woods Hideout Stump', 'Lost Woods Hideout Drop'), - ('Lumberjack Tree Cave', 'Lumberjack Tree Tree'), - ('Sanctuary', 'Sanctuary Grave')] - - hole_targets = [('Kakariko Well Exit', 'Kakariko Well (top)'), - ('Bat Cave Exit', 'Bat Cave (right)'), - ('North Fairy Cave Exit', 'North Fairy Cave'), - ('Lost Woods Hideout Exit', 'Lost Woods Hideout (top)'), - ('Lumberjack Tree Exit', 'Lumberjack Tree (top)')] - - if not world.shuffle_ganon: - connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player) - connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player) - else: - hole_targets.append(('Pyramid Exit', 'Pyramid')) - - - hole_entrances.append(('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance Drop')) - hole_targets.append(('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance')) - - # do not shuffle sanctuary into pyramid hole unless shuffle is crossed - if world.shuffle[player] == 'crossed': - hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) - if world.shuffle_ganon: - random.shuffle(hole_targets) - exit, target = hole_targets.pop() - connect_two_way(world, 'Inverted Pyramid Entrance', exit, player) - connect_entrance(world, 'Inverted Pyramid Hole', target, player) + if (world.mode[player] == 'inverted') == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) + else: + connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player) if world.shuffle[player] != 'crossed': hole_targets.append(('Sanctuary Exit', 'Sewer Drop')) From 899e0444473681cbda4993b1f575a9af21877bba Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 13 Aug 2021 05:10:47 -0500 Subject: [PATCH 09/11] Separated default connections to grouped lists --- EntranceShuffle.py | 247 +++++++++++++++++++++++---------------------- 1 file changed, 126 insertions(+), 121 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index aea9ef58..abdc651d 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -29,7 +29,7 @@ def link_entrances(world, player): # if we do not shuffle, set default connections if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: - for exitname, regionname in default_connections: + for exitname, regionname in default_connections + default_connector_connections + default_drop_connections + default_item_connections + default_shop_connections: connect_simple(world, exitname, regionname, player) if world.shuffle[player] == 'vanilla': for exitname, regionname in default_dungeon_connections: @@ -2662,7 +2662,7 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'), ('Ganon Drop', 'Bottom of Pyramid') - ] + ] open_mandatory_connections = [('Sanctuary S&Q', 'Sanctuary'), ('Other World S&Q', 'Pyramid Area')] @@ -2672,40 +2672,13 @@ inverted_mandatory_connections = [('Sanctuary S&Q', 'Dark Sanctuary Hint'), ('Dark Sanctuary Hint Exit', 'Dark Chapel Area')] # non-shuffled entrance links -default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'), - ("Blinds Hideout", "Blinds Hideout"), - ('Dam', 'Dam'), - ('Lumberjack House', 'Lumberjack House'), - ('Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance'), - ('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance'), - ('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Courtyard Northeast'), +default_connections = [('Lumberjack House', 'Lumberjack House'), ('Bonk Fairy (Light)', 'Bonk Fairy (Light)'), ('Lake Hylia Fairy', 'Lake Hylia Healer Fairy'), ('Lake Hylia Fortune Teller', 'Lake Hylia Fortune Teller'), ('Light Hype Fairy', 'Swamp Healer Fairy'), ('Desert Fairy', 'Desert Healer Fairy'), - ('Kings Grave', 'Kings Grave'), ('Tavern North', 'Tavern'), - ('Chicken House', 'Chicken House'), - ('Aginahs Cave', 'Aginahs Cave'), - ('Sahasrahlas Hut', 'Sahasrahlas Hut'), - ('Cave Shop (Lake Hylia)', 'Cave Shop (Lake Hylia)'), - ('Capacity Upgrade', 'Capacity Upgrade'), - ('Kakariko Well Drop', 'Kakariko Well (top)'), - ('Kakariko Well Cave', 'Kakariko Well (bottom)'), - ('Kakariko Well Exit', 'Kakariko Area'), - ('Blacksmiths Hut', 'Blacksmiths Hut'), - ('Bat Cave Drop', 'Bat Cave (right)'), - ('Bat Cave Cave', 'Bat Cave (left)'), - ('Bat Cave Exit', 'Blacksmith Area'), - ('Sick Kids House', 'Sick Kids House'), - ('Elder House (East)', 'Elder House'), - ('Elder House (West)', 'Elder House'), - ('Elder House Exit (East)', 'Kakariko Area'), - ('Elder House Exit (West)', 'Kakariko Area'), - ('North Fairy Cave Drop', 'North Fairy Cave'), - ('North Fairy Cave', 'North Fairy Cave'), - ('North Fairy Cave Exit', 'River Bend Area'), ('Lost Woods Gamble', 'Lost Woods Gamble'), ('Fortune Teller (Light)', 'Fortune Teller (Light)'), ('Snitch Lady (East)', 'Snitch Lady (East)'), @@ -2713,117 +2686,149 @@ default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing'), ('Bush Covered House', 'Bush Covered House'), ('Tavern (Front)', 'Tavern (Front)'), ('Light World Bomb Hut', 'Light World Bomb Hut'), - ('Kakariko Shop', 'Kakariko Shop'), - ('Lost Woods Hideout Drop', 'Lost Woods Hideout (top)'), - ('Lost Woods Hideout Stump', 'Lost Woods Hideout (bottom)'), - ('Lost Woods Hideout Exit', 'Lost Woods East Area'), - ('Lumberjack Tree Tree', 'Lumberjack Tree (top)'), - ('Lumberjack Tree Cave', 'Lumberjack Tree (bottom)'), - ('Lumberjack Tree Exit', 'Lumberjack Area'), - ('Cave 45', 'Cave 45'), - ('Graveyard Cave', 'Graveyard Cave'), - ('Checkerboard Cave', 'Checkerboard Cave'), - ('Mini Moldorm Cave', 'Mini Moldorm Cave'), ('Long Fairy Cave', 'Long Fairy Cave'), # near East Light World Teleporter ('Good Bee Cave', 'Good Bee Cave'), ('20 Rupee Cave', '20 Rupee Cave'), ('50 Rupee Cave', '50 Rupee Cave'), - ('Ice Rod Cave', 'Ice Rod Cave'), - ('Bonk Rock Cave', 'Bonk Rock Cave'), - ('Library', 'Library'), ('Kakariko Gamble Game', 'Kakariko Gamble Game'), - ('Potion Shop', 'Potion Shop'), - ('Two Brothers House (East)', 'Two Brothers House'), - ('Two Brothers House (West)', 'Two Brothers House'), - ('Two Brothers House Exit (East)', 'Kakariko Suburb Area'), - ('Two Brothers House Exit (West)', 'Maze Race Ledge'), - - ('Sanctuary', 'Sanctuary Portal'), - ('Sanctuary Grave', 'Sewer Drop'), - ('Sanctuary Exit', 'Sanctuary Area'), - - ('Old Man Cave (West)', 'Old Man Cave Ledge'), - ('Old Man Cave (East)', 'Old Man Cave'), - ('Old Man Cave Exit (West)', 'Mountain Entry Entrance'), - ('Old Man Cave Exit (East)', 'West Death Mountain (Bottom)'), - ('Old Man House (Bottom)', 'Old Man House'), - ('Old Man House Exit (Bottom)', 'West Death Mountain (Bottom)'), - ('Old Man House (Top)', 'Old Man House Back'), - ('Old Man House Exit (Top)', 'West Death Mountain (Bottom)'), - ('Death Mountain Return Cave (East)', 'Death Mountain Return Cave'), - ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave'), - ('Death Mountain Return Cave Exit (West)', 'Mountain Entry Ledge'), - ('Death Mountain Return Cave Exit (East)', 'West Death Mountain (Bottom)'), - ('Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Peak)'), - ('Spectacle Rock Cave (Bottom)', 'Spectacle Rock Cave (Bottom)'), - ('Spectacle Rock Cave', 'Spectacle Rock Cave (Top)'), - ('Spectacle Rock Cave Exit', 'West Death Mountain (Bottom)'), - ('Spectacle Rock Cave Exit (Top)', 'West Death Mountain (Bottom)'), - ('Spectacle Rock Cave Exit (Peak)', 'West Death Mountain (Bottom)'), - ('Paradox Cave (Bottom)', 'Paradox Cave Front'), - ('Paradox Cave (Middle)', 'Paradox Cave'), - ('Paradox Cave (Top)', 'Paradox Cave'), - ('Paradox Cave Exit (Bottom)', 'East Death Mountain (Bottom)'), - ('Paradox Cave Exit (Middle)', 'East Death Mountain (Bottom)'), - ('Paradox Cave Exit (Top)', 'East Death Mountain (Top East)'), ('Hookshot Fairy', 'Hookshot Fairy'), - ('Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Bottom)'), - ('Fairy Ascension Cave (Top)', 'Fairy Ascension Cave (Top)'), - ('Fairy Ascension Cave Exit (Bottom)', 'Fairy Ascension Plateau'), - ('Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Ledge'), - ('Spiral Cave', 'Spiral Cave (Top)'), - ('Spiral Cave (Bottom)', 'Spiral Cave (Bottom)'), - ('Spiral Cave Exit', 'East Death Mountain (Bottom)'), - ('Spiral Cave Exit (Top)', 'Spiral Cave Ledge'), - - ('Pyramid Fairy', 'Pyramid Fairy'), + ('East Dark World Hint', 'East Dark World Hint'), ('Palace of Darkness Hint', 'Palace of Darkness Hint'), - ('Dark Lake Hylia Shop', 'Dark Lake Hylia Shop'), ('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'), ('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy'), ('Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Spike Cave'), ('Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Hint'), - ('Hype Cave', 'Hype Cave'), ('Bonk Fairy (Dark)', 'Bonk Fairy (Dark)'), - ('Brewery', 'Brewery'), - ('C-Shaped House', 'C-Shaped House'), - ('Chest Game', 'Chest Game'), - ('Dark World Hammer Peg Cave', 'Dark World Hammer Peg Cave'), - ('Bumper Cave (Bottom)', 'Bumper Cave'), - ('Bumper Cave (Top)', 'Bumper Cave'), - ('Red Shield Shop', 'Red Shield Shop'), ('Dark Sanctuary Hint', 'Dark Sanctuary Hint'), ('Fortune Teller (Dark)', 'Fortune Teller (Dark)'), - ('Dark World Shop', 'Village of Outcasts Shop'), - ('Dark World Lumberjack Shop', 'Dark World Lumberjack Shop'), - ('Dark World Potion Shop', 'Dark World Potion Shop'), ('Archery Game', 'Archery Game'), - ('Bumper Cave Exit (Top)', 'Bumper Cave Ledge'), - ('Bumper Cave Exit (Bottom)', 'Bumper Cave Entrance'), - ('Mire Shed', 'Mire Shed'), ('Dark Desert Hint', 'Dark Desert Hint'), ('Dark Desert Fairy', 'Dark Desert Healer Fairy'), - ('Spike Cave', 'Spike Cave'), - ('Hookshot Cave', 'Hookshot Cave (Front)'), - ('Superbunny Cave (Top)', 'Superbunny Cave (Top)'), - ('Cave Shop (Dark Death Mountain)', 'Cave Shop (Dark Death Mountain)'), ('Dark Death Mountain Fairy', 'Dark Death Mountain Healer Fairy'), - ('Superbunny Cave (Bottom)', 'Superbunny Cave (Bottom)'), - ('Superbunny Cave Exit (Top)', 'East Dark Death Mountain (Top)'), - ('Superbunny Cave Exit (Bottom)', 'East Dark Death Mountain (Bottom)'), - ('Hookshot Cave Front Exit', 'East Dark Death Mountain (Top)'), - ('Hookshot Cave Back Exit', 'Dark Death Mountain Floating Island'), - ('Hookshot Cave Back Entrance', 'Hookshot Cave (Back)'), - ('Mimic Cave', 'Mimic Cave'), - - ('Pyramid Exit', 'Pyramid Exit Ledge'), - ('Pyramid Hole', 'Pyramid'), - ('Pyramid Entrance', 'Bottom of Pyramid'), - ('Inverted Pyramid Hole', 'Pyramid'), - ('Inverted Pyramid Entrance', 'Bottom of Pyramid') ] +default_connector_connections = [('Old Man Cave (West)', 'Old Man Cave Ledge'), + ('Old Man Cave (East)', 'Old Man Cave'), + ('Old Man Cave Exit (West)', 'Mountain Entry Entrance'), + ('Old Man Cave Exit (East)', 'West Death Mountain (Bottom)'), + ('Old Man House (Bottom)', 'Old Man House'), + ('Old Man House Exit (Bottom)', 'West Death Mountain (Bottom)'), + ('Old Man House (Top)', 'Old Man House Back'), + ('Old Man House Exit (Top)', 'West Death Mountain (Bottom)'), + ('Death Mountain Return Cave (East)', 'Death Mountain Return Cave'), + ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave'), + ('Death Mountain Return Cave Exit (West)', 'Mountain Entry Ledge'), + ('Death Mountain Return Cave Exit (East)', 'West Death Mountain (Bottom)'), + ('Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Peak)'), + ('Spectacle Rock Cave (Bottom)', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave', 'Spectacle Rock Cave (Top)'), + ('Spectacle Rock Cave Exit', 'West Death Mountain (Bottom)'), + ('Spectacle Rock Cave Exit (Top)', 'West Death Mountain (Bottom)'), + ('Spectacle Rock Cave Exit (Peak)', 'West Death Mountain (Bottom)'), + ('Spiral Cave', 'Spiral Cave (Top)'), + ('Spiral Cave (Bottom)', 'Spiral Cave (Bottom)'), + ('Spiral Cave Exit', 'East Death Mountain (Bottom)'), + ('Spiral Cave Exit (Top)', 'Spiral Cave Ledge'), + ('Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Bottom)'), + ('Fairy Ascension Cave (Top)', 'Fairy Ascension Cave (Top)'), + ('Fairy Ascension Cave Exit (Bottom)', 'Fairy Ascension Plateau'), + ('Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Ledge'), + ('Paradox Cave (Bottom)', 'Paradox Cave Front'), + ('Paradox Cave (Middle)', 'Paradox Cave'), + ('Paradox Cave (Top)', 'Paradox Cave'), + ('Paradox Cave Exit (Bottom)', 'East Death Mountain (Bottom)'), + ('Paradox Cave Exit (Middle)', 'East Death Mountain (Bottom)'), + ('Paradox Cave Exit (Top)', 'East Death Mountain (Top East)'), + ('Elder House (East)', 'Elder House'), + ('Elder House (West)', 'Elder House'), + ('Elder House Exit (East)', 'Kakariko Area'), + ('Elder House Exit (West)', 'Kakariko Area'), + ('Two Brothers House (East)', 'Two Brothers House'), + ('Two Brothers House (West)', 'Two Brothers House'), + ('Two Brothers House Exit (East)', 'Kakariko Suburb Area'), + ('Two Brothers House Exit (West)', 'Maze Race Ledge'), + ('Bumper Cave (Bottom)', 'Bumper Cave'), + ('Bumper Cave (Top)', 'Bumper Cave'), + ('Bumper Cave Exit (Top)', 'Bumper Cave Ledge'), + ('Bumper Cave Exit (Bottom)', 'Bumper Cave Entrance'), + ('Superbunny Cave (Top)', 'Superbunny Cave (Top)'), + ('Superbunny Cave (Bottom)', 'Superbunny Cave (Bottom)'), + ('Superbunny Cave Exit (Top)', 'East Dark Death Mountain (Top)'), + ('Superbunny Cave Exit (Bottom)', 'East Dark Death Mountain (Bottom)'), + ('Hookshot Cave', 'Hookshot Cave (Front)'), + ('Hookshot Cave Back Entrance', 'Hookshot Cave (Back)'), + ('Hookshot Cave Front Exit', 'East Dark Death Mountain (Top)'), + ('Hookshot Cave Back Exit', 'Dark Death Mountain Floating Island') + ] + +default_item_connections = [('Mimic Cave', 'Mimic Cave'), + ('Waterfall of Wishing', 'Waterfall of Wishing'), + ('Bonk Rock Cave', 'Bonk Rock Cave'), + ('Graveyard Cave', 'Graveyard Cave'), + ('Kings Grave', 'Kings Grave'), + ('Potion Shop', 'Potion Shop'), + ('Blinds Hideout', 'Blinds Hideout'), + ('Chicken House', 'Chicken House'), + ('Sick Kids House', 'Sick Kids House'), + ('Sahasrahlas Hut', 'Sahasrahlas Hut'), + ('Blacksmiths Hut', 'Blacksmiths Hut'), + ('Library', 'Library'), + ('Checkerboard Cave', 'Checkerboard Cave'), + ('Aginahs Cave', 'Aginahs Cave'), + ('Cave 45', 'Cave 45'), + ('Mini Moldorm Cave', 'Mini Moldorm Cave'), + ('Ice Rod Cave', 'Ice Rod Cave'), + ('Dam', 'Dam'), + ('Spike Cave', 'Spike Cave'), + ('Chest Game', 'Chest Game'), + ('C-Shaped House', 'C-Shaped House'), + ('Brewery', 'Brewery'), + ('Pyramid Fairy', 'Pyramid Fairy'), + ('Dark World Hammer Peg Cave', 'Dark World Hammer Peg Cave'), + ('Mire Shed', 'Mire Shed'), + ('Hype Cave', 'Hype Cave') + ] + +default_shop_connections = [('Kakariko Shop', 'Kakariko Shop'), + ('Cave Shop (Lake Hylia)', 'Cave Shop (Lake Hylia)'), + ('Capacity Upgrade', 'Capacity Upgrade'), + ('Dark World Lumberjack Shop', 'Dark World Lumberjack Shop'), + ('Cave Shop (Dark Death Mountain)', 'Cave Shop (Dark Death Mountain)'), + ('Dark World Potion Shop', 'Dark World Potion Shop'), + ('Dark World Shop', 'Village of Outcasts Shop'), + ('Red Shield Shop', 'Red Shield Shop'), + ('Dark Lake Hylia Shop', 'Dark Lake Hylia Shop') + ] + +default_drop_connections = [('Lost Woods Hideout Drop', 'Lost Woods Hideout (top)'), + ('Lost Woods Hideout Stump', 'Lost Woods Hideout (bottom)'), + ('Lost Woods Hideout Exit', 'Lost Woods East Area'), + ('Lumberjack Tree Tree', 'Lumberjack Tree (top)'), + ('Lumberjack Tree Cave', 'Lumberjack Tree (bottom)'), + ('Lumberjack Tree Exit', 'Lumberjack Area'), + ('Sanctuary', 'Sanctuary Portal'), + ('Sanctuary Grave', 'Sewer Drop'), + ('Sanctuary Exit', 'Sanctuary Area'), + ('North Fairy Cave Drop', 'North Fairy Cave'), + ('North Fairy Cave', 'North Fairy Cave'), + ('North Fairy Cave Exit', 'River Bend Area'), + ('Kakariko Well Drop', 'Kakariko Well (top)'), + ('Kakariko Well Cave', 'Kakariko Well (bottom)'), + ('Kakariko Well Exit', 'Kakariko Area'), + ('Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance'), + ('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance'), + ('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Courtyard Northeast'), + ('Bat Cave Drop', 'Bat Cave (right)'), + ('Bat Cave Cave', 'Bat Cave (left)'), + ('Bat Cave Exit', 'Blacksmith Area'), + ('Pyramid Hole', 'Pyramid'), + ('Pyramid Entrance', 'Bottom of Pyramid'), + ('Inverted Pyramid Hole', 'Pyramid'), + ('Inverted Pyramid Entrance', 'Bottom of Pyramid'), + ('Pyramid Exit', 'Pyramid Exit Ledge') + ] + swapped_connections = { 0x03: [ ('Old Man Cave (East)', 'Death Mountain Return Cave Exit (West)'), @@ -2844,7 +2849,7 @@ swapped_connections = { open_default_connections = [('Links House', 'Links House'), ('Links House Exit', 'Links House Area'), ('Big Bomb Shop', 'Big Bomb Shop') - ] + ] inverted_default_connections = [('Links House', 'Big Bomb Shop'), ('Links House Exit', 'Big Bomb Shop Area'), From 2b4c4a6595432ccf3487a0213b0bc784d9f231bd Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 13 Aug 2021 05:31:07 -0500 Subject: [PATCH 10/11] Various Mixed OW Shuffle (Inverted) fixes --- EntranceShuffle.py | 57 ++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index abdc651d..9b31b652 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -225,7 +225,7 @@ def link_entrances(world, player): random.shuffle(remaining_entrances) old_man_entrance = remaining_entrances.pop() - connect_two_way(world, old_man_entrance if not invFlag else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player) + connect_two_way(world, old_man_entrance if invFlag == (0x0a in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player) connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) if invFlag and old_man_exit == 'Spike Cave': bomb_shop_doors.remove('Spike Cave') @@ -469,9 +469,7 @@ def link_entrances(world, player): connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) elif invFlag or world.doorShuffle[player] == 'vanilla': caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3))) - lw_entrances.append('Hyrule Castle Entrance (South)') - else: - lw_entrances.append('Hyrule Castle Entrance (South)') + lw_entrances.append('Hyrule Castle Entrance (South)') if not world.shuffle_ganon: connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) @@ -1166,20 +1164,28 @@ def link_entrances(world, player): if not world.shuffle_ganon: connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) - connect_two_way(world, 'Pyramid Entrance' if not invFlag else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) - connect_entrance(world, 'Pyramid Hole' if not invFlag else 'Inverted Pyramid Hole', 'Pyramid', player) + connect_two_way(world, 'Pyramid Entrance' if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) + connect_entrance(world, 'Pyramid Hole' if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', 'Pyramid', player) else: caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) hole_targets.append('Pyramid') + if not invFlag: - exit_pool.extend(['Ganons Tower', 'Pyramid Entrance']) + exit_pool.extend(['Ganons Tower']) + doors.extend(['Ganons Tower']) + else: + exit_pool.extend(['Agahnims Tower']) + doors.extend(['Agahnims Tower']) + + if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): + exit_pool.extend(['Pyramid Entrance']) hole_entrances.append('Pyramid Hole') entrances_must_exits.append('Pyramid Entrance') - doors.extend(['Ganons Tower', 'Pyramid Entrance']) + doors.extend(['Pyramid Entrance']) else: - exit_pool.extend(['Agahnims Tower', 'Inverted Pyramid Entrance']) + exit_pool.extend(['Inverted Pyramid Entrance']) hole_entrances.append('Inverted Pyramid Hole') - doors.extend(['Agahnims Tower', 'Inverted Pyramid Entrance']) + doors.extend(['Inverted Pyramid Entrance']) random.shuffle(hole_entrances) random.shuffle(hole_targets) @@ -1355,24 +1361,31 @@ def link_entrances(world, player): if not world.shuffle_ganon: connect_two_way(world, 'Ganons Tower', 'Ganons Tower Exit', player) - if not invFlag: + if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): connect_two_way(world, 'Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Pyramid Hole', 'Pyramid', player) else: connect_two_way(world, 'Inverted Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Inverted Pyramid Hole', 'Pyramid', player) else: - caves.extend(['Ganons Tower Exit', 'Pyramid Exit']) hole_targets.append('Pyramid') + if not invFlag: + doors.extend(['Ganons Tower']) + exit_pool.extend(['Ganons Tower']) + else: + doors.extend(['Agahnims Tower']) + exit_pool.extend(['Agahnims Tower']) + + if invFlag == (0x1b in world.owswaps[player][0] and world.owSwap[player] == 'mixed'): hole_entrances.append('Pyramid Hole') - doors.extend(['Agahnims Tower', 'Pyramid Entrance']) - exit_pool.extend(['Agahnims Tower', 'Pyramid Entrance']) + doors.extend(['Pyramid Entrance']) + exit_pool.extend(['Pyramid Entrance']) else: hole_entrances.append('Inverted Pyramid Hole') - doors.extend(['Agahnims Tower', 'Inverted Pyramid Entrance']) - exit_pool.extend(['Agahnims Tower', 'Inverted Pyramid Entrance']) + doors.extend(['Inverted Pyramid Entrance']) + exit_pool.extend(['Inverted Pyramid Entrance']) random.shuffle(hole_entrances) random.shuffle(hole_targets) @@ -1502,7 +1515,7 @@ def link_entrances(world, player): world.powder_patch_required[player] = True # check for ganon location - if world.get_entrance('Pyramid Hole' if not invFlag else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid': + if world.get_entrance('Pyramid Hole' if invFlag == (0x03 in world.owswaps[player][0] and world.owSwap[player] == 'mixed') else 'Inverted Pyramid Hole', player).connected_region.name != 'Pyramid': world.ganon_at_pyramid[player] = False # check for Ganon's Tower location @@ -2926,12 +2939,12 @@ inverted_default_dungeon_connections = [('Ganons Tower', 'Agahnims Tower Portal' indirect_connections = { 'Turtle Rock Ledge': 'Turtle Rock', 'Pyramid Area': 'Pyramid Fairy', - 'East Dark World': 'Pyramid Fairy', + #'East Dark World': 'Pyramid Fairy', 'Big Bomb Shop': 'Pyramid Fairy', - 'Dark Desert': 'Pyramid Fairy', - 'West Dark World': 'Pyramid Fairy', - 'South Dark World': 'Pyramid Fairy', - 'Light World': 'Pyramid Fairy', + #'Dark Desert': 'Pyramid Fairy', + #'West Dark World': 'Pyramid Fairy', + #'South Dark World': 'Pyramid Fairy', + #'Light World': 'Pyramid Fairy', 'Old Man Cave': 'Old Man S&Q' } # format: From 651f057adc3b5a03ae2518d7734dd1492e5da800 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 13 Aug 2021 05:36:49 -0500 Subject: [PATCH 11/11] Version bump 0.1.7.4 --- CHANGELOG.md | 5 +++++ OverworldShuffle.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33f08a41..e97665ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### 0.1.7.4 +- Fixed issue with Mixed OW failing to generate when HC/Pyramid is swapped +- Various fixes to improve generation rates for Mixed OW Shuffle +- ~~Merged DR v0.5.0.2 - Shuffle SFX~~ + ### 0.1.7.3 - Fixed minor issue with ambient SFX stopping and starting on OW screen load - MSU-1 changed to play LW2 (track 60) when Aga1 is killed instead of ped pull diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 6aa22ece..881cffdd 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -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.7.3-u' +__version__ = '0.1.7.4-u' def link_overworld(world, player): # setup mandatory connections