From 23ffc61667a27542f8cacfe4ec6abecfd31280c0 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 7 Jun 2021 11:13:28 -0600 Subject: [PATCH 01/14] Sprite author credit Reduce flashing option --- Adjuster.py | 1 + AdjusterMain.py | 3 +- CLI.py | 4 ++- Main.py | 6 ++-- RELEASENOTES.md | 3 ++ Rom.py | 31 +++++++++++++++++-- resources/app/cli/args.json | 4 +++ resources/app/cli/lang/en.json | 3 +- .../app/gui/adjust/overview/widgets.json | 3 +- resources/app/gui/lang/en.json | 2 ++ .../gui/randomize/gameoptions/widgets.json | 3 +- source/classes/constants.py | 3 +- source/gui/adjust/overview.py | 3 +- 13 files changed, 57 insertions(+), 12 deletions(-) diff --git a/Adjuster.py b/Adjuster.py index 570bcef9..c6f42e6e 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -35,6 +35,7 @@ def main(): help='Select the color of Link\'s heart meter. (default: %(default)s)') 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('--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 b1d5cadf..73f5cea2 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -24,7 +24,8 @@ def adjust(args): if not hasattr(args,"sprite"): 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) + 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) rom.write_to_file(output_path('%s.sfc' % outfilebase)) diff --git a/CLI.py b/CLI.py index 18692950..222f8a90 100644 --- a/CLI.py +++ b/CLI.py @@ -101,7 +101,8 @@ def parse_cli(argv, no_defaults=False): 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', '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']: + 'remote_items', 'shopsanity', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', + 'reduce_flashing']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) @@ -187,6 +188,7 @@ def parse_settings(): "fastmenu": "normal", "ow_palettes": "default", "uw_palettes": "default", + "reduce_flashing": False, # Spoiler defaults to TRUE # Playthrough defaults to TRUE diff --git a/Main.py b/Main.py index bc621f7c..fb44aa7c 100644 --- a/Main.py +++ b/Main.py @@ -27,7 +27,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.4.0.6-u' +__version__ = '0.4.0.7-u' class EnemizerError(RuntimeError): @@ -280,7 +280,9 @@ def main(args, seed=None, fish=None): rom_names.append((player, team, list(rom.name))) world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash) - 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]) + 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]) if args.jsonout: jsonout[f'patch_t{team}_p{player}'] = rom.patches diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8097eff2..9c3b9502 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -14,6 +14,9 @@ Thanks to qadan, cheuer, & compiling # Bug Fixes and Notes. +* 0.4.0.7 + * Reduce flashing option added + * Sprite author credit added * 0.4.0.6 * Hints now default to off * The maiden gives you a hint to the attic if you bring her to the unlit boss room diff --git a/Rom.py b/Rom.py index 9b06ec57..e9ca420d 100644 --- a/Rom.py +++ b/Rom.py @@ -495,8 +495,6 @@ class Sprite(object): sprite_name = read_utf16le(stream) author_name = read_utf16le(stream) - # Ignoring the Author Rom name for the time being. - real_csum = sum(filedata) % 0x10000 if real_csum != csum or real_csum ^ 0xFFFF != icsum: logger.warning('ZSPR file has incorrect checksum. It may be corrupted.') @@ -1638,7 +1636,8 @@ def hud_format_text(text): return output[:32] -def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, ow_palettes, uw_palettes): +def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, + ow_palettes, uw_palettes, reduce_flashing): if sprite and not isinstance(sprite, Sprite): sprite = Sprite(sprite) if os.path.isfile(sprite) else get_sprite_from_name(sprite) @@ -1695,6 +1694,32 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr if sprite is not None: write_sprite(rom, sprite) + # sprite author credits + padded_author = sprite.author_name if sprite is not None else "Nintendo" + padded_author = padded_author[:28] if len(padded_author) > 28 else padded_author + padded_author = padded_author.center(28).upper() + + def convert_char_to_credits(char): + char_map = { + " ": (0x9F, 0x9F), "0": (0x53, 0x79), "1": (0x54, 0x7A), "2": (0x55, 0x7B), "3": (0x56, 0x7C), + "4": (0x57, 0x7D), "5": (0x58, 0x7E), "6": (0x59, 0x7F), "7": (0x5A, 0x80), "8": (0x5B, 0x81), + "9": (0x5C, 0x82), "A": (0x5D, 0x83), "B": (0x5E, 0x84), "C": (0x5F, 0x85), "D": (0x60, 0x86), + "E": (0x61, 0x87), "F": (0x62, 0x88), "G": (0x63, 0x89), "H": (0x64, 0x8A), "I": (0x65, 0x8B), + "J": (0x66, 0x8C), "K": (0x67, 0x8D), "L": (0x68, 0x8E), "M": (0x69, 0x8F), "N": (0x6A, 0x90), + "O": (0x6B, 0x91), "P": (0x6C, 0x92), "Q": (0x6D, 0x93), "R": (0x6E, 0x94), "S": (0x6F, 0x95), + "T": (0x70, 0x96), "U": (0x71, 0x97), "V": (0x72, 0x98), "W": (0x73, 0x99), "X": (0x74, 0x9A), + "Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0x77, 0x9d), ".": (0xA0, 0xC0), "/": (0xA2, 0xC2), + ":": (0xA3, 0xC3), "_": (0xA6, 0xC6)} + return char_map[char] if char in char_map else (0x9F, 0x9F) + + character_bytes = map(convert_char_to_credits, padded_author) + for i, pair in enumerate(character_bytes): + rom.write_byte(0x118002 + i, pair[0]) + rom.write_byte(0x118020 + i, pair[1]) + + if reduce_flashing: + rom.write_byte(0x18017f, 1) + default_ow_palettes(rom) if ow_palettes == 'random': randomize_ow_palettes(rom) diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index a41adb17..0dc97277 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -195,6 +195,10 @@ "action": "store_true", "type": "bool" }, + "reduce_flashing": { + "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 183b3c2e..145667ef 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -278,7 +278,7 @@ "Include the Ganon's Tower and Pyramid Hole in the", "entrance shuffle pool. (default: %(default)s)" ], - "shufflelink": [ + "shufflelinks": [ "Include Link's House in the entrance shuffle pool. (default: %(default)s)" ], "heartbeep": [ @@ -293,6 +293,7 @@ "Alternatively, can be a ALttP Rom patched with a Link", "sprite that will be extracted." ], + "reduce_flashing": [ "Reduce some in-game flashing (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 f56bfd08..b61fff0e 100644 --- a/resources/app/gui/adjust/overview/widgets.json +++ b/resources/app/gui/adjust/overview/widgets.json @@ -1,7 +1,8 @@ { "checkboxes": { "nobgm": { "type": "checkbox" }, - "quickswap": { "type": "checkbox" } + "quickswap": { "type": "checkbox" }, + "reduce_flashing": {"type": "checkbox"} }, "leftAdjustFrame": { "heartcolor": { diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index aa6cd6dc..8ae98e81 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -2,6 +2,7 @@ "gui": { "adjust.nobgm": "Disable Music & MSU-1", "adjust.quickswap": "L/R Quickswapping", + "adjust.reduce_flashing": "Reduce Flashing", "adjust.heartcolor": "Heart Color", "adjust.heartcolor.red": "Red", @@ -131,6 +132,7 @@ "randomizer.gameoptions.nobgm": "Disable Music & MSU-1", "randomizer.gameoptions.quickswap": "L/R Quickswapping", + "randomizer.gameoptions.reduce_flashing": "Reduce Flashing", "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 7f612cb4..63556e0f 100644 --- a/resources/app/gui/randomize/gameoptions/widgets.json +++ b/resources/app/gui/randomize/gameoptions/widgets.json @@ -1,7 +1,8 @@ { "checkboxes": { "nobgm": { "type": "checkbox" }, - "quickswap": { "type": "checkbox" } + "quickswap": { "type": "checkbox" }, + "reduce_flashing": {"type": "checkbox"} }, "leftRomOptionsFrame": { "heartcolor": { diff --git a/source/classes/constants.py b/source/classes/constants.py index c0e95861..3e59af3c 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -105,7 +105,8 @@ SETTINGSTOPROCESS = { "heartbeep": "heartbeep", "menuspeed": "fastmenu", "owpalettes": "ow_palettes", - "uwpalettes": "uw_palettes" + "uwpalettes": "uw_palettes", + "reduce_flashing": "reduce_flashing" }, "generation": { "createspoiler": "create_spoiler", diff --git a/source/gui/adjust/overview.py b/source/gui/adjust/overview.py index b741f63e..c44e169f 100644 --- a/source/gui/adjust/overview.py +++ b/source/gui/adjust/overview.py @@ -101,7 +101,8 @@ def adjust_page(top, parent, settings): "owpalettes": "ow_palettes", "uwpalettes": "uw_palettes", "quickswap": "quickswap", - "nobgm": "disablemusic" + "nobgm": "disablemusic", + "reduce_flashing": "reduce_flashing", } guiargs = Namespace() for option in options: From 8c5a4be4802a831b6b265606295ed0d2c8f8b467 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 22 Jun 2021 13:04:37 -0600 Subject: [PATCH 02/14] Ranged crystal switch logic tweaked Couple of minor bugs fixed --- DoorShuffle.py | 15 +++++++++------ Doors.py | 23 +++++++++++++---------- Dungeons.py | 3 ++- RELEASENOTES.md | 1 + Regions.py | 5 +++-- Rules.py | 14 +++++++++----- 6 files changed, 37 insertions(+), 24 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index be46bd74..ee38f00c 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -99,6 +99,7 @@ def link_doors_main(world, player): for portal in world.dungeon_portals[player]: connect_portal(portal, world, player) + fix_big_key_doors_with_ugly_smalls(world, player) if world.doorShuffle[player] == 'vanilla': for entrance, ext in open_edges: connect_two_way(world, entrance, ext, player) @@ -676,7 +677,6 @@ def find_entrance_region(portal): # paired_door.pair = False def within_dungeon(world, player): - fix_big_key_doors_with_ugly_smalls(world, player) add_inaccessible_doors(world, player) entrances_map, potentials, connections = determine_entrance_list(world, player) connections_tuple = (entrances_map, potentials, connections) @@ -930,7 +930,6 @@ def treat_split_as_whole_dungeon(split_dungeon, name, origin_list, world, player def cross_dungeon(world, player): - fix_big_key_doors_with_ugly_smalls(world, player) add_inaccessible_doors(world, player) entrances_map, potentials, connections = determine_entrance_list(world, player) connections_tuple = (entrances_map, potentials, connections) @@ -1617,13 +1616,15 @@ def reassign_key_doors(builder, world, player): else: room.delete(d.doorListPos) d.smallKey = False - elif d.type is DoorType.Interior and d not in flat_proposal and d.dest not in flat_proposal and not d.entranceFlag: - world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal) + elif d.type is DoorType.Interior and d not in flat_proposal and d.dest not in flat_proposal: + if not d.entranceFlag: + world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal) d.smallKey = False d.dest.smallKey = False queue.remove(d.dest) - elif d.type is DoorType.Normal and d not in flat_proposal and not d.entranceFlag: - world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal) + elif d.type is DoorType.Normal and d not in flat_proposal: + if not d.entranceFlag: + world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal) d.smallKey = False for dp in world.paired_doors[player]: if dp.door_a == d.name or dp.door_b == d.name: @@ -2294,6 +2295,8 @@ logical_connections = [ ('GT Double Switch Entry to Left Barrier - Orange', 'GT Double Switch Left'), ('GT Double Switch Entry to Ranged Switches', 'GT Double Switch Entry - Ranged Switches'), ('GT Double Switch Entry Ranged Switches Exit', 'GT Double Switch Entry'), + ('GT Double Switch Left to Crystal', 'GT Double Switch Left - Crystal'), + ('GT Double Switch Left Crystal Exit', 'GT Double Switch Left'), ('GT Double Switch Left to Entry Barrier - Orange', 'GT Double Switch Entry'), ('GT Double Switch Left to Entry Bypass', 'GT Double Switch Entry'), ('GT Double Switch Left to Pot Corners Bypass', 'GT Double Switch Pot Corners'), diff --git a/Doors.py b/Doors.py index cc13826e..b0979a5d 100644 --- a/Doors.py +++ b/Doors.py @@ -397,10 +397,10 @@ def create_doors(world, player): create_door(player, 'PoD Arena Landing to Right Barrier - Blue', Lgcl), create_door(player, 'PoD Arena Landing to North Barrier - Orange', Lgcl), create_door(player, 'PoD Arena Right to Landing Barrier - Blue', Lgcl), - create_door(player, 'PoD Arena Right to Ranged Crystal', Lgcl), - create_door(player, 'PoD Arena Right Ranged Crystal Exit', Lgcl), - create_door(player, 'PoD Arena Ledge to Ranged Crystal', Lgcl), - create_door(player, 'PoD Arena Ledge Ranged Crystal Exit', Lgcl), + create_door(player, 'PoD Arena Right to Ranged Crystal', Lgcl), # considered out of logic + create_door(player, 'PoD Arena Right Ranged Crystal Exit', Lgcl).no_exit(), # blocked here for pre-validate + create_door(player, 'PoD Arena Ledge to Ranged Crystal', Lgcl), # considered out of logic + create_door(player, 'PoD Arena Ledge Ranged Crystal Exit', Lgcl).no_exit(), # blocked here for pre-validate create_door(player, 'PoD Arena Ledge ES', Nrml).dir(Ea, 0x2a, Bot, High).pos(2), create_door(player, 'PoD Sexy Statue W', Nrml).dir(We, 0x2b, Mid, High).pos(3), create_door(player, 'PoD Sexy Statue NW', Nrml).dir(No, 0x2b, Left, High).trap(0x1).pos(2), @@ -875,7 +875,7 @@ def create_doors(world, player): create_door(player, 'Mire Left Bridge Down Stairs', Sprl).dir(Dn, 0xa2, 0, HTL).ss(A, 0x12, 0x00), create_door(player, 'Mire Fishbone E', Nrml).dir(Ea, 0xa1, Mid, High).pos(1), create_door(player, 'Mire Fishbone Blue Barrier', Lgcl), - create_door(player, 'Mire Fishbone Blue Barrier Bypass', Lgcl), + create_door(player, 'Mire Fishbone Blue Barrier Bypass', Lgcl).no_exit(), # considered out of logic create_door(player, 'Mire South Fish Blue Barrier', Lgcl), create_door(player, 'Mire Fishbone SE', Nrml).dir(So, 0xa1, Right, High).small_key().pos(0), create_door(player, 'Mire Spike Barrier NE', Nrml).dir(No, 0xb1, Right, High).small_key().pos(1), @@ -1116,16 +1116,18 @@ def create_doors(world, player): create_door(player, 'GT Double Switch NW', Nrml).dir(No, 0x9b, Left, High).pos(1).kill(), create_door(player, 'GT Double Switch Entry to Pot Corners Barrier - Orange', Lgcl), create_door(player, 'GT Double Switch Entry to Left Barrier - Orange', Lgcl), - create_door(player, 'GT Double Switch Entry to Ranged Switches', Lgcl), - create_door(player, 'GT Double Switch Entry Ranged Switches Exit', Lgcl), + create_door(player, 'GT Double Switch Entry to Ranged Switches', Lgcl), # considered out of logic + create_door(player, 'GT Double Switch Entry Ranged Switches Exit', Lgcl).no_exit(), # blocked here for reasons + create_door(player, 'GT Double Switch Left to Crystal', Lgcl), + create_door(player, 'GT Double Switch Left Crystal Exit', Lgcl), create_door(player, 'GT Double Switch Left to Entry Barrier - Orange', Lgcl), create_door(player, 'GT Double Switch Left to Pot Corners Bypass', Lgcl), create_door(player, 'GT Double Switch Left to Entry Bypass', Lgcl), - create_door(player, 'GT Double Switch Left to Exit Bypass', Lgcl), + create_door(player, 'GT Double Switch Left to Exit Bypass', Lgcl).no_exit(), # considered out of logic create_door(player, 'GT Double Switch Pot Corners to Entry Barrier - Orange', Lgcl), create_door(player, 'GT Double Switch Pot Corners to Exit Barrier - Blue', Lgcl), - create_door(player, 'GT Double Switch Pot Corners to Ranged Switches', Lgcl), - create_door(player, 'GT Double Switch Pot Corners Ranged Switches Exit', Lgcl), + create_door(player, 'GT Double Switch Pot Corners to Ranged Switches', Lgcl), # considered out of logic + create_door(player, 'GT Double Switch Pot Corners Ranged Switches Exit', Lgcl).no_exit(), # blocked here create_door(player, 'GT Double Switch Exit to Blue Barrier', Lgcl), create_door(player, 'GT Double Switch EN', Intr).dir(Ea, 0x9b, Top, High).small_key().pos(0), create_door(player, 'GT Spike Crystals WN', Intr).dir(We, 0x9b, Top, High).small_key().pos(0), @@ -1438,6 +1440,7 @@ def create_doors(world, player): world.get_door('GT Double Switch Entry to Pot Corners Barrier - Orange', player).barrier(CrystalBarrier.Orange) world.get_door('GT Double Switch Entry to Left Barrier - Orange', player).barrier(CrystalBarrier.Orange) world.get_door('GT Double Switch Entry Ranged Switches Exit', player).c_switch() + world.get_door('GT Double Switch Left Crystal Exit', player).c_switch() world.get_door('GT Double Switch Left to Entry Barrier - Orange', player).barrier(CrystalBarrier.Orange) world.get_door('GT Double Switch Left to Entry Bypass', player).barrier(CrystalBarrier.Blue) world.get_door('GT Double Switch Left to Pot Corners Bypass', player).barrier(CrystalBarrier.Blue) diff --git a/Dungeons.py b/Dungeons.py index 0acf7852..d5297400 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -304,7 +304,8 @@ gt_regions = [ 'GT Crystal Conveyor Corner - Ranged Crystal', 'GT Compass Room', 'GT Invisible Bridges', 'GT Invisible Catwalk', 'GT Conveyor Cross', 'GT Hookshot East Platform', 'GT Hookshot North Platform', 'GT Hookshot South Platform', 'GT Hookshot South Entry', 'GT Hookshot South Entry - Ranged Crystal', 'GT Map Room', - 'GT Double Switch Entry', 'GT Double Switch Pot Corners - Ranged Switches', 'GT Double Switch Pot Corners', 'GT Double Switch Left', + 'GT Double Switch Entry', 'GT Double Switch Pot Corners - Ranged Switches', 'GT Double Switch Pot Corners', + 'GT Double Switch Left', 'GT Double Switch Left - Crystal', 'GT Double Switch Entry - Ranged Switches', 'GT Double Switch Exit', 'GT Spike Crystal Left', 'GT Spike Crystal Right', 'GT Warp Maze - Left Section', 'GT Warp Maze - Mid Section', 'GT Warp Maze - Right Section', 'GT Warp Maze - Pit Section', 'GT Warp Maze - Pit Exit Warp Spot', diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9c3b9502..7ece7e7b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -17,6 +17,7 @@ Thanks to qadan, cheuer, & compiling * 0.4.0.7 * Reduce flashing option added * Sprite author credit added + * Ranged Crystal switch rules tweaked * 0.4.0.6 * Hints now default to off * The maiden gives you a hint to the attic if you bring her to the unlit boss room diff --git a/Regions.py b/Regions.py index 9e6a324d..b2af454a 100644 --- a/Regions.py +++ b/Regions.py @@ -749,7 +749,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'GT Double Switch Entry', 'Ganon\'s Tower', None, ['GT Double Switch NW', 'GT Double Switch Entry to Left Barrier - Orange', 'GT Double Switch Entry to Pot Corners Barrier - Orange', 'GT Double Switch Entry to Ranged Switches']), create_dungeon_region(player, 'GT Double Switch Entry - Ranged Switches', 'Ganon\'s Tower', None, ['GT Double Switch Entry Ranged Switches Exit']), - create_dungeon_region(player, 'GT Double Switch Left', 'Ganon\'s Tower', None, ['GT Double Switch Left to Entry Barrier - Orange', 'GT Double Switch Left to Entry Bypass', 'GT Double Switch Left to Pot Corners Bypass', 'GT Double Switch Left to Exit Bypass']), + create_dungeon_region(player, 'GT Double Switch Left', 'Ganon\'s Tower', None, ['GT Double Switch Left to Crystal', 'GT Double Switch Left to Entry Barrier - Orange', 'GT Double Switch Left to Entry Bypass', 'GT Double Switch Left to Pot Corners Bypass', 'GT Double Switch Left to Exit Bypass']), + create_dungeon_region(player, 'GT Double Switch Left - Crystal', 'Ganon\'s Tower', None, ['GT Double Switch Left Crystal Exit']), create_dungeon_region(player, 'GT Double Switch Pot Corners', 'Ganon\'s Tower', ['Ganons Tower - Double Switch Pot Key'], ['GT Double Switch Pot Corners to Entry Barrier - Orange', 'GT Double Switch Pot Corners to Exit Barrier - Blue', 'GT Double Switch Pot Corners to Ranged Switches']), create_dungeon_region(player, 'GT Double Switch Pot Corners - Ranged Switches', 'Ganon\'s Tower', None, ['GT Double Switch Pot Corners Ranged Switches Exit']), create_dungeon_region(player, 'GT Double Switch Exit', 'Ganon\'s Tower', None, ['GT Double Switch EN', 'GT Double Switch Exit to Blue Barrier']), @@ -870,7 +871,7 @@ def create_dungeon_regions(world, player): world.get_region('GT Crystal Conveyor Corner - Ranged Crystal', player).crystal_switch = True world.get_region('GT Hookshot South Platform', player).crystal_switch = True world.get_region('GT Hookshot South Entry - Ranged Crystal', player).crystal_switch = True - world.get_region('GT Double Switch Left', player).crystal_switch = True + world.get_region('GT Double Switch Left - Crystal', player).crystal_switch = True world.get_region('GT Double Switch Entry - Ranged Switches', player).crystal_switch = True world.get_region('GT Double Switch Pot Corners - Ranged Switches', player).crystal_switch = True world.get_region('GT Spike Crystal Left', player).crystal_switch = True diff --git a/Rules.py b/Rules.py index f1ade62a..3762b9bb 100644 --- a/Rules.py +++ b/Rules.py @@ -402,7 +402,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Hera Front to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Hera Down Stairs Landing to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_blue(world.get_region('Hera Down Stairs Landing', player), player))) # or state.has_beam_sword(player) set_rule(world.get_entrance('Hera Up Stairs Landing to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('Hera Up Stairs Landing', player), player))) # or state.has_beam_sword(player) - set_rule(world.get_entrance('Hera Back to Ranged Crystal', player), lambda state: False) # state.can_shoot_arrows(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or state.has_beam_sword(player) or (state.has('Hookshot', player) and state.has('Red Boomerang', player)) + set_rule(world.get_entrance('Hera Back to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # or state.has_beam_sword(player) or (state.has('Hookshot', player) and state.has('Red Boomerang', player)) set_rule(world.get_entrance('Hera Front to Back Bypass', player), lambda state: state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Cane of Somaria', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player)) # or state.has_beam_sword(player) set_rule(world.get_entrance('Hera Basement Cage to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Hera Tridorm to Crystal', player), lambda state: state.can_hit_crystal(player)) @@ -430,7 +430,7 @@ def global_rules(world, player): set_rule(world.get_entrance('PoD Map Balcony to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player)) # or state.has('Red Boomerang', player) set_rule(world.get_entrance('PoD Bow Statue Left to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('PoD Bow Statue Right to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player)) - set_rule(world.get_entrance('PoD Bow Statue Left to Right Bypass', player), lambda state: state.has('Cane of Somaria', player)) # or state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has_beam_sword(player) or state.has('Red Boomrang', player) or state.has('Ice Rod', player) or state.has('Fire Rod', player) + set_rule(world.get_entrance('PoD Bow Statue Left to Right Bypass', player), lambda state: state.has('Cane of Somaria', player) or state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Ice Rod', player) or state.has('Fire Rod', player)) # or state.has_beam_sword(player) set_rule(world.get_entrance('PoD Dark Pegs Landing to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player)) # or state.can_use_bombs(player) or state.has('Blue boomerang', player) or state.has('Red boomerang', player) set_rule(world.get_entrance('PoD Dark Pegs Middle to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.can_use_bombs(player) or state.has('Red Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('PoD Dark Pegs Middle', player), player))) # or state.has_beam_sword(player) set_rule(world.get_entrance('PoD Dark Pegs Left to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # or state.has_beam_sword(player) @@ -536,14 +536,15 @@ def global_rules(world, player): set_rule(world.get_entrance('GT Crystal Conveyor Left to Corner Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('GT Crystal Conveyor Left', player), player)) set_rule(world.get_entrance('GT Crystal Circles Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('GT Crystal Circles', player), player)) - set_rule(world.get_entrance('GT Hookshot Platform Barrier Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or state.has('Cane of Somaria', player)) # or state.has_Boots(player) /// There is a super precise trick where you can throw a pot and climp into the blue barrier, then sprint out of them. - set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('GT Hookshot Platform Barrier Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or state.has('Cane of Somaria', player)) # or state.has_Boots(player) /// There is a super precise trick where you can throw a pot and climp into the blue barrier, then sprint out of them. + set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player)) + set_rule(world.get_entrance('GT Double Switch Left to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('GT Double Switch Entry to Ranged Switches', player), lambda state: False) # state.has('Cane of Somaria', player) set_rule(world.get_entrance('GT Double Switch Left to Entry Bypass', player), lambda state: True) # Can always use pots set_rule(world.get_entrance('GT Double Switch Left to Pot Corners Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player) or state.has('Red Boomerang', player)) # or (state.has('Blue Boomerang', player) and state.has('Hookshot', player)) or (state.has('Ice Rod', player) and state.has('Hookshot', player)) or state.has('Hookshot', player) /// You can do this with just a pot and a hookshot set_rule(world.get_entrance('GT Double Switch Left to Exit Bypass', player), lambda state: False) # state.can_use_bombs(player) or (state.has('Cane of Somaria', player) and (state.has('Red Boomerang', player) or (state.has('Hookshot', player) and state.has('Blue Boomerang', player)) or (state.has('Hookshot', player) and state.has('Ice Rod', player)))) set_rule(world.get_entrance('GT Double Switch Pot Corners to Ranged Switches', player), lambda state: False) # state.can_use_bombs(player) or state.has('Cane of Somaria', player) or (state.has('Cane of Somaria', player) and state.has_Boots(player)) /// There's two ways to interact with the switch. Somaria bounce at the top corner, or timed throws at the bottom corner. - set_rule(world.get_entrance('GT Spike Crystal Left to Right Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player)) # or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.can_use_beam_sword(player) + set_rule(world.get_entrance('GT Spike Crystal Left to Right Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player)) # or state.can_use_beam_sword(player) set_rule(world.get_entrance('GT Crystal Conveyor to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player)) set_rule(world.get_entrance('GT Crystal Conveyor Corner to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player)) set_rule(world.get_entrance('GT Crystal Conveyor Corner to Left Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player)) @@ -1210,6 +1211,9 @@ def standard_rules(world, player): 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']: add_rule(world.get_entrance(entrance, player), lambda state: state.has('Zelda Delivered', player)) + # don't allow bombs to get past here before zelda is rescued + set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: (state.can_use_bombs(player) and state.has('Zelda Delivered', player)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player)) + def find_rules_for_zelda_delivery(world, player): # path rules for backtracking From f77015ab0046300bd7fa74960294a9abe45b841f Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 22 Jun 2021 13:13:15 -0600 Subject: [PATCH 03/14] Baserom update --- Rom.py | 2 +- data/base2current.bps | Bin 133119 -> 133695 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index e9ca420d..aa1e816b 100644 --- a/Rom.py +++ b/Rom.py @@ -27,7 +27,7 @@ from EntranceShuffle import door_addresses, exit_ids JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '47e937d11855bc775587fe2a7acfa3a3' +RANDOMIZERBASEHASH = '712324ad3ca7bff751d9f62812a2c3b6' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index cf03abefe0079ba3423365dc823669ba215f3e10..94a1eedd3b15a5e6d8bc6d0e46caacd90a9cfb85 100644 GIT binary patch delta 6815 zcmXAN30xCb*Z0gMgd}VMLD>ldD4QB^0a+9fC!O28$Rx_q`iT72BlwsCDsTk{Ujm-(Il%{^!Cx#!$%&bbzXIdF(6 z3w2q79XqV#+nYMa`$Cu)9#T*z=d0!MALuTk@oKOD-i~a%3wq#ELiIxGYV^^X#cHFTW}EB>6=MN(YGU=IhYh3zpF{ClcIff zGq{8lL&nEtQuMnwU<⩔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%0Adv^(&Be}mz^?Jl|96MTBFXB0#{`Q0@O z=sxUb))XpmROMg~fgpxIUP3h?@3m1i&Sh4p4yR2zlRIXM&|b7`6^lfP=BmuyMK9IGJrvWlq z2GaCqJCaLm;b#F72!g+@UU|j_bNK3#6P)D@LG&?3GCjw-gq|;~9h{2iZN|xTJ9wRS z(o%xEJmBwWwn^&^@-Rp0r;AT9l=;AJFe53nU%ePM zD7f$g*k$*!Gv{C!p}{bXW@T+<&2%7_F%2RV{!C*OM;dy|iwy^+!&j}Cz9|=MiuTv6 zFsulLIZVd%jpHx2aSTOs;hiwDY6bbzJUASwS#EHc4^O~Iv>#~`34e(=e__}V316Ty zidWtu6a7gv7A|6x2(NS`P9bHnurDJZoZ{0BhFG{BK{m#bi{oGvi3~7oj)Nl+#HO4) zmH>ModpL$~6JRc4wXx`yj&69G2v^U7tH^b`;bdfe8L8b3zd^q6FpzuTKp2VJWw=}l zpSN<&Ts3~V-2&w^Ej^1|gcFu*%V$d-IXA;V?1Q&jBVjecBfM9s#C zmFJ{AgNq73GaZZs|*)E!?7%;cC_eVLw_Ud{{W&;k`Mp@ delta 6110 zcmW+)30xD`^WXOpa&QJj?jr<{Lr@eIL@damC@2Wx0YUIA7^x?Mlw&s-AwUS>0V}MS zC{hfFQ6t2omU+WwFvJGt44b!b8upEPNhOfDdxRmKGwLTpxIyxJbV*%fdY8$8S;Yj&7^{353i$q z=tz&uhk2+PZgYuL$ONOmsNyPRf}To0zFdaG(b0ODK=lH-F)qNjPtik02yZK{5Ex&p zMh#@{Wdk}=k8GK%;Io@(Gt&hwazs_k`A{>449uCZ_a&uz|*w=3!j;kboMN#Zd_;wKeA3GMt9Y(V`Cv4U9lAM~v?rY}Kl}eew`6+tO zN#{;|tqLNV1O>0rDsF)J)?VrP7b=x>@&8aG*Vpl<*D9PzZVW@wQbn|UT1xpVg|K-< zg$!H|SJ|z@ITcNUbx%~Iqem&ZOhD^*!xh4RXgSw>69l(Vfq4p}f+nw<&w|QZ4HlV< zIm0Tn+Tw3`=(To%r5=XNaK)v9`PY;l5iL~`o?n&3#xIHHB)*~M7PYi{1XiAw2t?Oh zn9VzJiC~seg%;S%1*^0Y8xFAW5mpdkGmf^-+tQEvc)`Bk_j<3akO>w)QVFYC1a>FA zbtR+UlGcw;kurt3z`4)+bc?_r@oe1#1%2N3xJ)ps&l{_dW&w~=m`an&I~L>FDSvPplBeJy(tT6MqPQe~P$v zli=Rx+G%!;lWa~uQRO&QOJC_#_LWlA6ZCWV5qSOuDw{ohl2VlPo8pm)fjyyiCjM3z z|37&Z@q`i+@2QE7zY$|y6Y^UhPH{qp*+uZqEwm^i1J;kBZz8^!DAi7T??g5US{rGP z#~qY1jUFTyy^Hu3l8=yXP7J*HNc(=y_k13iSo)U?=crG#?oLorSScD527_tZcfuN5 zxaGNa?^jH-xO{wsUp(Heh9VoDE$1RH;eJL>5HPxhxZDf{2EN|GVBKQ@^m7bsH6{xM zxHT=6O$PgeK=F94KUy;t-&TQ|?uIicI--WV4pw)6KL+{3X~9ND(xivz<=yD0{L}$V#vTWfM1C2;OW`Mc)hi8o}O!%I>Mu@MDIFNafO+FtAW#; zm`JwsFuXbKljI^#m$LD#woHQj+^T#DSupSyHjCj!z6RochNT@Uy(MO_qWuzJ!cf-z zDIUyKm1@jrt?5iD4`5=(W@d*xjgQOi*5kw^QjCt?54DttIA}wqwB#b6vP9fpBWCS8 z6ReIt#8$CoO#Xf|eu9(c&yNNW-mgk24^+}n#p4X{7`gr;6+1Tq|8a!h!;4YAM}}XW z%MbpcQKys})6sE>k%_**Q7n!i(W^Ge0)Wg9$XI;ZHdAbsJg4%YjLlbLa(X(t(&Ptj z1^@#Z{-@sIA6Rcr6UW_q0s!u`-w*6(^Ko7t@3@?gOX}J3JyNJPk&|)xuw>Xo*2;!o z8o0<2M@6{e1*-eayZrH(1u(FOU5$)tylgBK7+d(#jclj9_1J*n9sRN+eEM1AbryhphVp`l6U=_oosiEKehzGdgGudR+YvS~ z|EL$vzsUO;>wpQGO+<)+k+k>;V(;{hoSp~pCQWlnc|?R<)}$6oFtvCN{#Kea_X)DV z2}p?*zJ@~ej4#F21e1PBsG+c(oe%Itv1LBmxYG-!|Ba+OQz5PH!Oj3kui_M_7+iB4 zbrrZWLUKrStzar!Gp3>vXx@hc!DQEa=NkcTCB-qLnHm9{q5i7dfJRRT0KSo?8>sZ^i-sWm?4pW=_y)T_@!@1 z#)n>}Fyp-I@Gk5)Q)v9wRql^dqQ!4|61w`MY5y~l&OAd83fIEa1!!K8D-`WUtBZ=D zeK@*XE^-ZP`5+Cj5(z?T+$rEF2{mq~AP5Bv9tzCdGP&(*~$@z}r+gf~0?uMX{Uv(%`#S z_BrJ{#2j^usR!4eT;z-By=`c9z+K7S7bvbck FNm1NzGh2e1XREmj0xmf9Lj?33_ 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 From 11dcc719fb9664a31e7d944a666d446fc0a82b8d Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 22 Jun 2021 16:05:41 -0600 Subject: [PATCH 04/14] Stop attempting to download link sprite Create link sprite from local rom if missing Fixing output_path bugs in the adjuster and sprite display --- AdjusterMain.py | 1 + RELEASENOTES.md | 3 ++ Rom.py | 63 ++++++++++++++++++++++++++++++++ source/classes/SpriteSelector.py | 5 ++- source/gui/adjust/overview.py | 1 + 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/AdjusterMain.py b/AdjusterMain.py index 73f5cea2..bc463444 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -27,6 +27,7 @@ def adjust(args): 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) + output_path.cached_path = args.outputpath rom.write_to_file(output_path('%s.sfc' % outfilebase)) logger.info('Done. Enjoy.') diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 7ece7e7b..c0aa736f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -18,6 +18,9 @@ Thanks to qadan, cheuer, & compiling * Reduce flashing option added * Sprite author credit added * Ranged Crystal switch rules tweaked + * Baserom update: includes Credits Speedup, reduced flashing option, msu resume (but turned off by default) + * Create link sprite's zspr from local ROM and no longer attempts to download it from website + * Some minor bug fixes * 0.4.0.6 * Hints now default to off * The maiden gives you a hint to the attic if you bring her to the unlit boss room diff --git a/Rom.py b/Rom.py index aa1e816b..5c2fae02 100644 --- a/Rom.py +++ b/Rom.py @@ -1638,6 +1638,14 @@ def hud_format_text(text): def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, ow_palettes, uw_palettes, reduce_flashing): + + if not os.path.exists("data/sprites/official/001.link.1.zspr"): + dump_zspr(rom.orig_buffer[0x80000:0x87000], rom.orig_buffer[0xdd308:0xdd380], + rom.orig_buffer[0xdedf5:0xdedf9], "data/sprites/official/001.link.1.zspr", "Nintendo", "Link") + + # todo: implement a flag for msu resume delay + rom.write_bytes(0x18021D, [0, 0]) # default to off for now + if sprite and not isinstance(sprite, Sprite): sprite = Sprite(sprite) if os.path.isfile(sprite) else get_sprite_from_name(sprite) @@ -1736,6 +1744,61 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr rom.write_crc() +# .zspr file dumping logic copied with permission from SpriteSomething: +# https://github.com/Artheau/SpriteSomething/blob/master/source/meta/classes/spritelib.py#L443 (thanks miketrethewey!) +def dump_zspr(basesprite, basepalette, baseglove, outfilename, author_name, sprite_name): + palettes = basepalette + # Add glove data + palettes.extend(baseglove) + HEADER_STRING = b"ZSPR" + VERSION = 0x01 + SPRITE_TYPE = 0x01 # this format has "1" for the player sprite + RESERVED_BYTES = b'\x00\x00\x00\x00\x00\x00' + QUAD_BYTE_NULL_CHAR = b'\x00\x00\x00\x00' + DOUBLE_BYTE_NULL_CHAR = b'\x00\x00' + SINGLE_BYTE_NULL_CHAR = b'\x00' + + write_buffer = bytearray() + + write_buffer.extend(HEADER_STRING) + write_buffer.extend(struct.pack('B', VERSION)) # as_u8 + checksum_start = len(write_buffer) + write_buffer.extend(QUAD_BYTE_NULL_CHAR) # checksum + sprite_sheet_pointer = len(write_buffer) + write_buffer.extend(QUAD_BYTE_NULL_CHAR) + write_buffer.extend(struct.pack(' Date: Wed, 23 Jun 2021 13:42:23 -0600 Subject: [PATCH 05/14] Update text Add ganon jokes for when there are no silvers --- Main.py | 2 +- RELEASENOTES.md | 3 ++ Rom.py | 25 ++++++++----- Text.py | 93 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 13 deletions(-) diff --git a/Main.py b/Main.py index fb44aa7c..9600fa8f 100644 --- a/Main.py +++ b/Main.py @@ -27,7 +27,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.4.0.7-u' +__version__ = '0.4.0.8-u' class EnemizerError(RuntimeError): diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c0aa736f..112b2021 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -14,6 +14,9 @@ Thanks to qadan, cheuer, & compiling # Bug Fixes and Notes. +* 0.4.0.8 + * Ganon jokes added for when silvers aren't available + * Some text updated (Blind jokes, uncle text) * 0.4.0.7 * Reduce flashing option added * Sprite author credit added diff --git a/Rom.py b/Rom.py index 5c2fae02..734b4779 100644 --- a/Rom.py +++ b/Rom.py @@ -19,7 +19,8 @@ from KeyDoorShuffle import count_locations_exclude_logic from Regions import location_table, shop_to_location_table, retro_shops from RoomData import DoorKind from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable -from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts, junk_texts +from Text import Uncle_texts, Ganon1_texts, Ganon_Phase_3_No_Silvers_texts, TavernMan_texts, Sahasrahla2_texts +from Text import Triforce_texts, Blind_texts, BombShop2_texts, junk_texts from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts, LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc from Items import ItemFactory @@ -2133,24 +2134,30 @@ def write_strings(rom, world, player, team): # We still need the older hints of course. Those are done here. + no_silver_text = Ganon_Phase_3_No_Silvers_texts[random.randint(0, len(Ganon_Phase_3_No_Silvers_texts) - 1)] silverarrows = world.find_items('Silver Arrows', player) random.shuffle(silverarrows) - silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' - tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint - tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint + if silverarrows: + hint_phrase = hint_text(silverarrows[0]).replace("Ganon's", "my") + silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' + else: + silverarrow_hint = no_silver_text + tt['ganon_phase_3_no_silvers'] = silverarrow_hint + tt['ganon_phase_3_no_silvers_alt'] = silverarrow_hint prog_bow_locs = world.find_items('Progressive Bow', player) distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None) progressive_silvers = world.difficulty_requirements[player].progressive_bow_limit >= 2 or world.swords[player] == 'swordless' if distinguished_prog_bow_loc: prog_bow_locs.remove(distinguished_prog_bow_loc) - silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!' - tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint - + hint_phrase = hint_text(distinguished_prog_bow_loc).replace("Ganon's", "my") + silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text + tt['ganon_phase_3_no_silvers'] = silverarrow_hint if any(prog_bow_locs): - silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!' - tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint + hint_phrase = hint_text(random.choice(prog_bow_locs)).replace("Ganon's", "my") + silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text + tt['ganon_phase_3_no_silvers_alt'] = silverarrow_hint crystal5 = world.find_items('Crystal 5', player)[0] crystal6 = world.find_items('Crystal 6', player)[0] diff --git a/Text.py b/Text.py index 3bf78af3..c84a202c 100644 --- a/Text.py +++ b/Text.py @@ -21,13 +21,14 @@ text_addresses = {'Pedestal': (0x180300, 256), Uncle_texts = [ + # these ones are er specific 'Good Luck!\nYou will need it.', 'Forward this message to 10 other people or this seed will be awful.', 'I hope you like your seeds bootless and fluteless.', '10\n9\n8\n7\n6\n5\n4\n3\n2\n1\nGo!', 'I\'m off to visit cousin Fritzl.', 'Don\'t forget to check Antlion Cave.' -] * 2 + [ + # these ones are from web randomizer "We're out of\nWeetabix. To\nthe store!", "This seed is\nbootless\nuntil boots.", "Why do we only\nhave one bed?", @@ -66,15 +67,32 @@ Uncle_texts = [ "Get to the\nchop...\ncastle!", "Come with me\nif you want\nto live", "I must go\nmy planet\nneeds me", + "Are we in\ngo mode yet?", + "Darn, I\nthought this\nwas combo.", + "Don't check\nanything I\nwouldn't!", + "I know where\nthe bow is!\n", + "This message\nwill self\ndestruct.", + "Time to cast\nMeteo on\nGanon!", + "I have a\nlong, full\nlife ahead!", + "Why did that\nsoda have a\nskull on it?", + "Something\nrandom just\ncame up.", + "I'm bad at\nthis. Can you\ndo it for me?", + "Link!\n Wake up!\n ... Bye!", + "Text me when\nyou hit\ngo mode.", + "Turn off the\nstove before\nyou leave.", + "It's raining.\nI'm taking\nthe umbrella.", + "Count to 30.\nThen come\nfind me.", + "Gonna shuffle\nall the items\nreal quick." ] Triforce_texts = [ + # these ones are er specific 'Product has Hole in center. Bad seller, 0 out of 5.', 'Who stole the fourth triangle?', 'Trifource?\nMore Like Tritrice, am I right?' '\n Well Done!', 'You just wasted 2 hours of your life.', - 'This was meant to be a trapezoid' -] * 2 + [ + 'This was meant to be a trapezoid', + # these ones are from web randomizer "\n G G", "All your base\nare belong\nto us.", "You have ended\nthe domination\nof Dr. Wily", @@ -107,6 +125,13 @@ Triforce_texts = [ "You get one\nwish. Choose\nwisely, hero!", "Can you please\nbreak us three\nup? Thanks.", " Pick us up\n before we\n get dizzy!", + "Thank you,\nMikey. You’re\n2 minutes late", + "This was a\n7000 series\ntrain.", + " I'd buy\n that for\n a rupee!", + " Did you like\n that bow\n placement?", + "I promise the\nnext seed will\nbe better.", + "\n Honk.", + "Breakfast\nis served!", ] BombShop2_texts = ['Bombs!\nBombs!\nBiggest!\nBestest!\nGreatest!\nBoomest!'] Sahasrahla2_texts = ['You already got my item, idiot.', 'Why are you still talking to me?', 'This text won\'t change.', 'Have you met my brother, Hasarahshla?'] @@ -145,6 +170,34 @@ Blind_texts = [ "I tried to\ncatch fog,\nbut I mist.", "Winter is a\ngreat time\nto chill.", "Do you think\nthe Ice Rod\nis cool?", + "Pyramids?\nI never saw\nthe point.", + "Stone golems\nare created as\nblank slates.", + "Desert humor\nis often dry.\n", + "Ganon is a\nbacon of\ndespair!", + "Butchering\ncows means\nhigh steaks.", + "I can't search\nthe web...\nToo many links", + "I can whistle\nMost pitches\nbut I can't C", + "The Blinds\nStore is\ncurtain death", + "Dark Aga Rooms\nare not a\nbright idea.", + "Best advice\nfor a Goron?\nBe Boulder.", + "Equestrian\nservices are\na stable job.", + "Do I like\ndrills? Just\na bit.", + "I'd shell out\ngood rupees\nfor a conch.", + "Current\naffairs are\nshocking!", + "A lying Goron\ndeals in\nboulderdash.", + "A bread joke?\nEh, it'd be\nhalf baked.", + "I could take\na stab at a\nsword pun.", + "Gloves open\na handful\nof checks", + "Red mail?\nReturn to\nsender.", + "For sale:\nBaby boots,\nNever found", + "SRL or rtGG?\nI prefer the\nLadder", + "Ladders are\nalways up\nto something", + "Zelda's\nfashion is\nvery chic", + "Zombie geese\nare waterfoul.\n", + "I bought some\ncuccos for a\npoultry sum.", + "The stratus of\nclouds is up\nin the air.", + "Tie two ropes\ntogether?!\nI think knot!", + "Time for you\nto go on a\nBlind date!" ] Ganon1_texts = [ "Start your day\nsmiling with a\ndelicious\nwhole grain\nbreakfast\ncreated for\nyour\nincredible\ninsides.", @@ -171,6 +224,40 @@ Ganon1_texts = [ "Life, dreams,\nhope...\nWhere'd they\ncome from? And\nwhere are they\nheaded? These\nthings... I am\ngoing to\ndestroy!", "My minions all\nfailed to\nguard those\nitems?!\n\nWhy am I\nsurrounded by\nincompetent\nfools?!", ] + +Ganon_Phase_3_No_Silvers_texts = [ + "Did you find\nthe arrows on\nPlanet Zebes?", + "Did you find\nthe arrows?\nI think not.", + "Silver arrows?\nI have never\nheard of them", + "Did you find\nthe arrows on\nThe Moon?", + "Did you find\nthe arrows\nIn dev null?", + "I have sold\nthe arrows for\na green big 20", + "Did you find\nThe arrows in\nCount Dracula?", + "Error 404\nSilver arrows\nnot found.", + "No arrows for\nYou today,\nSorry", + "No arrows?\nCheck your\njunk mail." + "Careful, all\nthat spinning\nmakes me dizzy", + "Did you find\nthe arrows in\nJabu's belly?", + "Silver is not\nan appropriate\narrow material", + "Did you find\nthe arrows in\nNarnia?", + "Are you ready\nTo spin\nTo win?", + "DID YOU FIND\nTHE ARROWS IN\nKEFKA'S TOWER", + "Did you find\nthe arrows in\nRecycle Bin?", + "Silver Arrows?\n\nLUL", + "Imagine\nfinding the\narrows", + "Did you find\nsilvers in\nscenic Ohio?", + "Did you find\nThe arrows in\n*mumblemumble*", + "\nSpin To Win!\n", + "did you find\nthe arrows in\nthe hourglass?", + "Silver Arrows\nare so v30", + "OH, NO, THEY\nACTUALLY SAID\nSILVER MARROW", + "SURELY THE\nLEFTMOST TILES\nWILL STAY UP", + "Did you find\nthe arrows in\nWorld 4-2?", + "You Spin Me\nRight Round\nLike A Record", + "SILLY HERO,\nSILVER IS FOR\nWEREWOLVES!", + "Did you find\nthe silvers in\nganti's ears", +] + TavernMan_texts = [ "What do you\ncall a blind\ndinosaur?\na doyouthink-\nhesaurus.", "A blind man\nwalks into\na bar.\nAnd a table.\nAnd a chair.", From 1a7c3faa76e04ca31690b3b944bee21e42d898d0 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 1 Jul 2021 16:25:55 -0600 Subject: [PATCH 06/14] Fixed some enemizer mystery settings Added an easy way to turn off unkillable thieves --- BaseClasses.py | 2 +- Mystery.py | 18 ++++++++++-------- RELEASENOTES.md | 2 ++ Rom.py | 6 +++--- resources/app/cli/args.json | 3 ++- resources/app/gui/lang/en.json | 1 + .../app/gui/randomize/enemizer/widgets.json | 3 ++- 7 files changed, 21 insertions(+), 14 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index ba685ac2..fc0029ee 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2269,7 +2269,7 @@ access_mode = {"items": 0, "locations": 1, "none": 2} # byte 6: BSMC BBEE (big, small, maps, compass, bosses, enemies) boss_mode = {"none": 0, "simple": 1, "full": 2, "random": 3, "chaos": 3} -enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2} +enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2, "legacy": 3} # byte 7: HHHD DP?? (enemy_health, enemy_dmg, potshuffle, ?) e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4} diff --git a/Mystery.py b/Mystery.py index 6d0189f0..bd5581d6 100644 --- a/Mystery.py +++ b/Mystery.py @@ -195,15 +195,17 @@ def roll_settings(weights): boss_choice = old_style_bosses[boss_choice] ret.shufflebosses = boss_choice - ret.shuffleenemies = {'none': 'none', - 'shuffled': 'shuffled', - 'random': 'chaos' - }[get_choice('enemy_shuffle')] + enemy_choice = get_choice('enemy_shuffle') + if enemy_choice == 'chaos': + enemy_choice = 'random' + ret.shuffleenemies = enemy_choice - ret.enemy_damage = {'default': 'default', - 'shuffled': 'shuffled', - 'random': 'chaos' - }[get_choice('enemy_damage')] + old_style_damage = {'none': 'default', + 'chaos': 'random'} + damage_choice = get_choice('enemy_damage') + if damage_choice in old_style_damage: + damage_choice = old_style_damage[damage_choice] + ret.enemy_damage = damage_choice ret.enemy_health = get_choice('enemy_health') diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 112b2021..9c279af4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -17,6 +17,8 @@ Thanks to qadan, cheuer, & compiling * 0.4.0.8 * Ganon jokes added for when silvers aren't available * Some text updated (Blind jokes, uncle text) + * Fixed some enemizer Mystery settings + * Added a setting that's random enemy shuffle without Unkillable Thieves possible * 0.4.0.7 * Reduce flashing option added * Sprite author credit added diff --git a/Rom.py b/Rom.py index 734b4779..1053e182 100644 --- a/Rom.py +++ b/Rom.py @@ -201,7 +201,7 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, random_sprite_ options = { 'RandomizeEnemies': world.enemy_shuffle[player] != 'none', 'RandomizeEnemiesType': 3, - 'RandomizeBushEnemyChance': world.enemy_shuffle[player] == 'random', + 'RandomizeBushEnemyChance': world.enemy_shuffle[player] in ['random', 'legacy'], 'RandomizeEnemyHealthRange': world.enemy_health[player] != 'default', 'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[world.enemy_health[player]], 'OHKO': False, @@ -247,9 +247,9 @@ def patch_enemizer(world, player, rom, baserom_path, enemizercli, random_sprite_ 'SwordGraphics': "sword_gfx/normal.gfx", 'BeeMizer': False, 'BeesLevel': 0, - 'RandomizeTileTrapPattern': world.enemy_shuffle[player] == 'random', + 'RandomizeTileTrapPattern': world.enemy_shuffle[player] in ['random', 'legacy'], 'RandomizeTileTrapFloorTile': False, - 'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'random' else world.enemy_shuffle[player] != 'none', + 'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'legacy' else world.enemy_shuffle[player] != 'none', 'RandomizeSpriteOnHit': random_sprite_on_hit, 'DebugMode': False, 'DebugForceEnemy': False, diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 0dc97277..00d1c9db 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -340,7 +340,8 @@ "choices": [ "none", "shuffled", - "random" + "random", + "legacy" ] }, "enemy_health": { diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 8ae98e81..850bb788 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -87,6 +87,7 @@ "randomizer.enemizer.enemyshuffle.none": "None", "randomizer.enemizer.enemyshuffle.shuffled": "Shuffled", "randomizer.enemizer.enemyshuffle.random": "Random", + "randomizer.enemizer.enemyshuffle.legacy": "Random (including Thieves)", "randomizer.enemizer.bossshuffle": "Boss Shuffle", "randomizer.enemizer.bossshuffle.none": "None", diff --git a/resources/app/gui/randomize/enemizer/widgets.json b/resources/app/gui/randomize/enemizer/widgets.json index bb731cc2..d987d41a 100644 --- a/resources/app/gui/randomize/enemizer/widgets.json +++ b/resources/app/gui/randomize/enemizer/widgets.json @@ -5,7 +5,8 @@ "options": [ "none", "shuffled", - "random" + "random", + "legacy" ] }, "bossshuffle": { From 1fb849c137076ba89f575ceb974b02f00a0622dd Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 1 Jul 2021 16:48:39 -0600 Subject: [PATCH 07/14] Fixed the shop spoiler when money balancing is needed --- Fill.py | 17 +++++++++++++++-- RELEASENOTES.md | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Fill.py b/Fill.py index c1488113..922ef957 100644 --- a/Fill.py +++ b/Fill.py @@ -3,7 +3,7 @@ import logging from BaseClasses import CollectionState from Items import ItemFactory -from Regions import shop_to_location_table +from Regions import shop_to_location_table, retro_shops class FillError(RuntimeError): @@ -557,7 +557,6 @@ def balance_money_progression(world): for player in range(1, world.players+1): logger.debug(f'Money balance for P{player}: Needed: {total_price[player]} Available: {available_money[player]}') - def get_sphere_locations(sphere_state, locations): sphere_state.sweep_for_events(key_only=True, locations=locations) return [loc for loc in locations if sphere_state.can_reach(loc) and sphere_state.not_flooding_a_key(sphere_state.world, loc)] @@ -584,6 +583,17 @@ def balance_money_progression(world): path = path[1] return False + def check_shop_swap(l): + if l.parent_region.name in shop_to_location_table: + if l.name in shop_to_location_table[l.parent_region.name]: + idx = shop_to_location_table[l.parent_region.name].index(l.name) + inv_slot = l.parent_region.shop.inventory[idx] + inv_slot['item'] = l.item.name + elif location.parent_region in retro_shops: + idx = retro_shops[l.parent_region.name].index(l.name) + inv_slot = l.parent_region.shop.inventory[idx] + inv_slot['item'] = l.item.name + done = False while not done: sphere_costs = {player: 0 for player in range(1, world.players+1)} @@ -672,6 +682,7 @@ def balance_money_progression(world): logger.debug(f'Upgrading {best_target.item.name} @ {best_target.name} for 300 Rupees') best_target.item = ItemFactory('Rupees (300)', best_target.item.player) best_target.item.location = best_target + check_shop_swap(best_target.item.location) else: old_item = best_target.item logger.debug(f'Swapping {best_target.item.name} @ {best_target.name} for {best_swap.item.name} @ {best_swap.name}') @@ -679,6 +690,8 @@ def balance_money_progression(world): best_target.item.location = best_target best_swap.item = old_item best_swap.item.location = best_swap + check_shop_swap(best_target.item.location) + check_shop_swap(best_swap.item.location) increase = best_value - old_value difference -= increase wallet[target_player] += increase diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9c279af4..487b68b3 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -19,6 +19,7 @@ Thanks to qadan, cheuer, & compiling * Some text updated (Blind jokes, uncle text) * Fixed some enemizer Mystery settings * Added a setting that's random enemy shuffle without Unkillable Thieves possible + * Fixed shop spoiler when money balancing * 0.4.0.7 * Reduce flashing option added * Sprite author credit added From 3fd096844c2d2c1135f8f9a12d558bead7dcf3ea Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 1 Jul 2021 16:52:16 -0600 Subject: [PATCH 08/14] Fixed the shop spoiler when multiworld balancing --- Fill.py | 27 ++++++++++++++++----------- RELEASENOTES.md | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Fill.py b/Fill.py index 922ef957..25e85a51 100644 --- a/Fill.py +++ b/Fill.py @@ -496,6 +496,10 @@ def balance_multiworld_progression(world): new_location = replacement_locations.pop() new_location.item, old_location.item = old_location.item, new_location.item + if world.shopsanity[new_location.player]: + check_shop_swap(new_location) + if world.shopsanity[old_location.player]: + check_shop_swap(old_location) new_location.event, old_location.event = True, False state.collect(new_location.item, True, new_location) replaced_items = True @@ -516,6 +520,18 @@ def balance_multiworld_progression(world): raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') +def check_shop_swap(l): + if l.parent_region.name in shop_to_location_table: + if l.name in shop_to_location_table[l.parent_region.name]: + idx = shop_to_location_table[l.parent_region.name].index(l.name) + inv_slot = l.parent_region.shop.inventory[idx] + inv_slot['item'] = l.item.name + elif l.parent_region in retro_shops: + idx = retro_shops[l.parent_region.name].index(l.name) + inv_slot = l.parent_region.shop.inventory[idx] + inv_slot['item'] = l.item.name + + def balance_money_progression(world): logger = logging.getLogger('') state = CollectionState(world) @@ -583,17 +599,6 @@ def balance_money_progression(world): path = path[1] return False - def check_shop_swap(l): - if l.parent_region.name in shop_to_location_table: - if l.name in shop_to_location_table[l.parent_region.name]: - idx = shop_to_location_table[l.parent_region.name].index(l.name) - inv_slot = l.parent_region.shop.inventory[idx] - inv_slot['item'] = l.item.name - elif location.parent_region in retro_shops: - idx = retro_shops[l.parent_region.name].index(l.name) - inv_slot = l.parent_region.shop.inventory[idx] - inv_slot['item'] = l.item.name - done = False while not done: sphere_costs = {player: 0 for player in range(1, world.players+1)} diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 487b68b3..01f54a07 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -19,7 +19,7 @@ Thanks to qadan, cheuer, & compiling * Some text updated (Blind jokes, uncle text) * Fixed some enemizer Mystery settings * Added a setting that's random enemy shuffle without Unkillable Thieves possible - * Fixed shop spoiler when money balancing + * Fixed shop spoiler when money balancing/multiworld balancing * 0.4.0.7 * Reduce flashing option added * Sprite author credit added From 0607cf67ef01e6aae0239b4c207749cf4c22d9b5 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 6 Jul 2021 13:20:28 -0700 Subject: [PATCH 09/14] Insanity: blacksmith/bomb shop placement shouldn't preclude another multi-exit cave from exiting there --- EntranceShuffle.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index e78b35b8..e111ddff 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -963,7 +963,6 @@ def link_entrances(world, player): blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) doors.remove(blacksmith_hut) - exit_pool.remove(blacksmith_hut) # place dam and pyramid fairy, have limited options bomb_shop_doors = [door for door in bomb_shop_doors if door in doors] @@ -971,7 +970,6 @@ def link_entrances(world, player): bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) doors.remove(bomb_shop) - exit_pool.remove(bomb_shop) # handle remaining caves for cave in caves: @@ -1804,7 +1802,6 @@ def link_inverted_entrances(world, player): blacksmith_hut = blacksmith_doors.pop() connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player) doors.remove(blacksmith_hut) - exit_pool.remove(blacksmith_hut) # place dam and pyramid fairy, have limited options bomb_shop_doors = [door for door in bomb_shop_doors if door in doors] @@ -1812,7 +1809,6 @@ def link_inverted_entrances(world, player): bomb_shop = bomb_shop_doors.pop() connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player) doors.remove(bomb_shop) - exit_pool.remove(bomb_shop) # handle remaining caves for cave in caves: From 3cc4175160c29ee23a700ccfe24968db99235970 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 6 Jul 2021 13:22:24 -0700 Subject: [PATCH 10/14] Attempt at catching bps issues --- Rom.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index 1053e182..b7447b61 100644 --- a/Rom.py +++ b/Rom.py @@ -9,8 +9,11 @@ import random import struct import sys import subprocess -import bps.apply -import bps.io +try: + import bps.apply + import bps.io +except ImportError: + raise Exception('Could not load BPS module') from BaseClasses import CollectionState, ShopType, Region, Location, Door, DoorType, RegionType, PotItem from DoorShuffle import compass_data, DROptions, boss_indicator From 0f29d2c2b097b50fa51f493c86d9c7b21e6b7666 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 6 Jul 2021 13:26:36 -0700 Subject: [PATCH 11/14] Credit stats fix for now --- Rom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rom.py b/Rom.py index b7447b61..b0c7d989 100644 --- a/Rom.py +++ b/Rom.py @@ -809,7 +809,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): write_int16(rom, 0x187010, credits_total) # dynamic credits if credits_total != 216: # collection rate address: - cr_address = 0x2391BE + cr_address = 0x2391FA cr_pc = cr_address - 0x120000 # convert to pc mid_top, mid_bot = credits_digit((credits_total // 10) % 10) last_top, last_bot = credits_digit(credits_total % 10) @@ -828,7 +828,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): total += count_locations_exclude_logic(region.locations, gt_logic) # rom.write_byte(0x187012, total) # dynamic credits # gt big key address: - gtbk_address = 0x2390E0 + gtbk_address = 0x23911C gtbk_pc = gtbk_address - 0x120000 # convert to pc mid_top, mid_bot = credits_digit(total // 10) last_top, last_bot = credits_digit(total % 10) From f21dace6d5253aab3c3f1941caef548c1491fcb2 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 6 Jul 2021 13:45:45 -0700 Subject: [PATCH 12/14] A couple minor generation issues Updated release note --- Main.py | 4 +--- RELEASENOTES.md | 4 ++++ Rom.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Main.py b/Main.py index 9600fa8f..513f5a84 100644 --- a/Main.py +++ b/Main.py @@ -253,12 +253,10 @@ def main(args, seed=None, fish=None): or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or sprite_random_on_hit) - if use_enemizer: - base_patch = LocalRom(args.rom) # update base2current.json - rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom) if use_enemizer and (args.enemizercli or not args.jsonout): + base_patch = LocalRom(args.rom) # update base2current.json (side effect) if args.rom and not(os.path.isfile(args.rom)): raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom) if os.path.exists(args.enemizercli): diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 01f54a07..b6258011 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -20,6 +20,10 @@ Thanks to qadan, cheuer, & compiling * Fixed some enemizer Mystery settings * Added a setting that's random enemy shuffle without Unkillable Thieves possible * Fixed shop spoiler when money balancing/multiworld balancing + * Fixed a problem with insanity + * Fixed an issue with the credit stats specific to DR (e.g. collection rate total) + * More helpful error message when bps is missing? + * Minor generation issues involving enemizer and the link sprite * 0.4.0.7 * Reduce flashing option added * Sprite author credit added diff --git a/Rom.py b/Rom.py index b0c7d989..7611aaa8 100644 --- a/Rom.py +++ b/Rom.py @@ -1643,7 +1643,7 @@ def hud_format_text(text): def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, ow_palettes, uw_palettes, reduce_flashing): - if not os.path.exists("data/sprites/official/001.link.1.zspr"): + 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], rom.orig_buffer[0xdedf5:0xdedf9], "data/sprites/official/001.link.1.zspr", "Nintendo", "Link") From fb22c88acef9caa568a13f32ba0e6bb348c03004 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 7 Jul 2021 14:04:50 -0700 Subject: [PATCH 13/14] Baserom update --- RELEASENOTES.md | 12 ++++++++++++ Rom.py | 23 ++--------------------- data/base2current.bps | Bin 133695 -> 136204 bytes 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b6258011..dd2ce1fc 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -24,6 +24,18 @@ Thanks to qadan, cheuer, & compiling * Fixed an issue with the credit stats specific to DR (e.g. collection rate total) * More helpful error message when bps is missing? * Minor generation issues involving enemizer and the link sprite + * Baserom updates (from Bonta, kan, qwertymodo, ardnaxelark) + * Boss icon on dungeon map (if you have a compass) + * Progressive bow sprite replacement + * Quickswap - consecutive special swaps + * Bonk Counter + * One mind + * MSU fix + * Chest turn tracking (not yet in credits) + * Damaged and magic stats in credits (gt bk removed) + * Fix for infinite bombs + * Fake boots option + * Always allowed medallions for swordless (no option yet) * 0.4.0.7 * Reduce flashing option added * Sprite author credit added diff --git a/Rom.py b/Rom.py index 7611aaa8..a3f37cb2 100644 --- a/Rom.py +++ b/Rom.py @@ -31,7 +31,7 @@ from EntranceShuffle import door_addresses, exit_ids JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '712324ad3ca7bff751d9f62812a2c3b6' +RANDOMIZERBASEHASH = '736978dd2b3a2bb109ac80ed7c048e67' class JsonRom(object): @@ -809,7 +809,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): write_int16(rom, 0x187010, credits_total) # dynamic credits if credits_total != 216: # collection rate address: - cr_address = 0x2391FA + cr_address = 0x2391F2 cr_pc = cr_address - 0x120000 # convert to pc mid_top, mid_bot = credits_digit((credits_total // 10) % 10) last_top, last_bot = credits_digit(credits_total % 10) @@ -820,25 +820,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(cr_pc+0x3a, mid_bot) rom.write_byte(cr_pc+0x3b, last_bot) - if world.keydropshuffle[player] or world.doorShuffle[player] != 'vanilla': - gt = world.dungeon_layouts[player]['Ganons Tower'] - gt_logic = world.key_logic[player]['Ganons Tower'] - total = 0 - for region in gt.master_sector.regions: - total += count_locations_exclude_logic(region.locations, gt_logic) - # rom.write_byte(0x187012, total) # dynamic credits - # gt big key address: - gtbk_address = 0x23911C - gtbk_pc = gtbk_address - 0x120000 # convert to pc - mid_top, mid_bot = credits_digit(total // 10) - last_top, last_bot = credits_digit(total % 10) - # top half - rom.write_byte(gtbk_pc+0x1c, mid_top) - rom.write_byte(gtbk_pc+0x1d, last_top) - # bottom half - rom.write_byte(gtbk_pc+0x3a, mid_bot) - rom.write_byte(gtbk_pc+0x3b, last_bot) - # patch medallion requirements if world.required_medallions[player][0] == 'Bombos': rom.write_byte(0x180022, 0x00) # requirement diff --git a/data/base2current.bps b/data/base2current.bps index 94a1eedd3b15a5e6d8bc6d0e46caacd90a9cfb85..22b25506e15289afe5741a94f8aeebbfcc14272c 100644 GIT binary patch delta 19579 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 HN-h5nm5%q? From ffcc78f95c9d2f73a5beca78b32339abb1ac8f1a Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 7 Jul 2021 16:23:16 -0700 Subject: [PATCH 14/14] Fake boots checkbox/argument --- BaseClasses.py | 1 + CLI.py | 3 ++- Main.py | 1 + Mystery.py | 1 + Rom.py | 4 +++- resources/app/cli/args.json | 4 ++++ resources/app/cli/lang/en.json | 1 + resources/app/gui/lang/en.json | 1 + resources/app/gui/randomize/item/widgets.json | 3 ++- source/classes/constants.py | 1 + source/gui/randomize/item.py | 7 +++++-- 11 files changed, 22 insertions(+), 5 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index fc0029ee..7521bd6e 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -131,6 +131,7 @@ class World(object): set_player_attr('treasure_hunt_total', 0) set_player_attr('potshuffle', False) set_player_attr('pot_contents', None) + set_player_attr('fakeboots', False) set_player_attr('shopsanity', False) set_player_attr('keydropshuffle', False) diff --git a/CLI.py b/CLI.py index 222f8a90..9b6e42fb 100644 --- a/CLI.py +++ b/CLI.py @@ -97,7 +97,7 @@ def parse_cli(argv, no_defaults=False): 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', - 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', + 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'fakeboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', @@ -144,6 +144,7 @@ def parse_settings(): "shuffleganon": True, "shuffle": "vanilla", "shufflelinks": False, + "fakeboots": False, "shufflepots": False, "shuffleenemies": "none", diff --git a/Main.py b/Main.py index 513f5a84..505c6d23 100644 --- a/Main.py +++ b/Main.py @@ -90,6 +90,7 @@ def main(args, seed=None, fish=None): world.treasure_hunt_count = args.triforce_goal.copy() world.treasure_hunt_total = args.triforce_pool.copy() world.shufflelinks = args.shufflelinks.copy() + world.fakeboots = args.fakeboots.copy() world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} diff --git a/Mystery.py b/Mystery.py index bd5581d6..0fd85108 100644 --- a/Mystery.py +++ b/Mystery.py @@ -143,6 +143,7 @@ def roll_settings(weights): ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off' ret.shufflelinks = get_choice('shufflelinks') == 'on' + ret.fakeboots = get_choice('fakeboots') == 'on' ret.shopsanity = get_choice('shopsanity') == 'on' ret.keydropshuffle = get_choice('keydropshuffle') == 'on' ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent' diff --git a/Rom.py b/Rom.py index a3f37cb2..72be78d0 100644 --- a/Rom.py +++ b/Rom.py @@ -18,7 +18,6 @@ except ImportError: from BaseClasses import CollectionState, ShopType, Region, Location, Door, DoorType, RegionType, PotItem from DoorShuffle import compass_data, DROptions, boss_indicator from Dungeons import dungeon_music_addresses -from KeyDoorShuffle import count_locations_exclude_logic from Regions import location_table, shop_to_location_table, retro_shops from RoomData import DoorKind from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable @@ -1163,6 +1162,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x18017E, 0x01) # Fairy fountains only trade in bottles # Starting equipment + if world.fakeboots[player]: + rom.write_byte(0x18008E, 0x01) + equip = [0] * (0x340 + 0x4F) equip[0x36C] = 0x18 equip[0x36D] = 0x18 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 00d1c9db..584593b4 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -310,6 +310,10 @@ "action": "store_true", "type": "bool" }, + "fakeboots": { + "action": "store_true", + "type": "bool" + }, "calc_playthrough": { "action": "store_false", "type": "bool" diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 145667ef..6416e952 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -262,6 +262,7 @@ "Keys are universal, shooting arrows costs rupees,", "and a few other little things make this more like Zelda-1. (default: %(default)s)" ], + "fakeboots": [ " Players starts with fake boots that allow dashing but no item checks (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 850bb788..f4b8ca12 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -189,6 +189,7 @@ "randomizer.item.hints": "Include Helpful Hints", "randomizer.item.retro": "Retro mode (universal keys)", + "randomizer.item.fakeboots": "Start with Fake Boots", "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 1871dcaf..1f5eb19a 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -4,7 +4,8 @@ "shopsanity": { "type": "checkbox" }, "hints": { "type": "checkbox" - } + }, + "fakeboots": { "type": "checkbox" } }, "leftItemFrame": { "worldstate": { diff --git a/source/classes/constants.py b/source/classes/constants.py index 3e59af3c..14c70f79 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -58,6 +58,7 @@ SETTINGSTOPROCESS = { "hints": "hints", "retro": "retro", "shopsanity": "shopsanity", + "fakeboots": "fakeboots", "worldstate": "mode", "logiclevel": "logic", "goal": "goal", diff --git a/source/gui/randomize/item.py b/source/gui/randomize/item.py index b01892ab..81c957ce 100644 --- a/source/gui/randomize/item.py +++ b/source/gui/randomize/item.py @@ -1,4 +1,4 @@ -from tkinter import ttk, Frame, E, W, LEFT, RIGHT +from tkinter import ttk, Frame, E, W, LEFT, RIGHT, Label import source.gui.widgets as widgets import json import os @@ -17,6 +17,9 @@ def item_page(parent): self.frames["checkboxes"] = Frame(self) self.frames["checkboxes"].pack(anchor=W) + various_options = Label(self.frames["checkboxes"], text="") + various_options.pack(side=LEFT) + self.frames["leftItemFrame"] = Frame(self) self.frames["rightItemFrame"] = Frame(self) self.frames["leftItemFrame"].pack(side=LEFT) @@ -34,7 +37,7 @@ def item_page(parent): self.widgets[key] = dictWidgets[key] packAttrs = {"anchor":E} if self.widgets[key].type == "checkbox": - packAttrs["anchor"] = W + packAttrs["side"] = LEFT self.widgets[key].pack(packAttrs) return self