From ad26921d960d1edd660deca81f75df5b210ea3e1 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 2 Jul 2021 00:22:29 -0500 Subject: [PATCH 1/8] Updated install instructions --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3bb1fe3..d6b459cb 100644 --- a/README.md +++ b/README.md @@ -25,21 +25,22 @@ This is a very new mode of LTTPR so the tools and info is very limited. - There - In Mixed OW Tile Swap, Smith and Stumpy have issues when their tiles are swapped. Progression cannot be found on them when these tiles are swapped - Screens that loop on itself and also have free-standing items, the sprites are duplicated and can cause item duplication - When OWG are performed to enter mega-tile screens (large OW screens), there is a small chance that an incorrect VRAM reference value causes the map graphics to offset in increments of 16 pixels -- There may be an issue with progression being front-loaded in the seed in some scenarios, due to an unsophisticated shuffle algorithm that could make varying-sized parts of each world unreachable # Feedback and Bug Reports All feedback and dev conversation happens in the #ow-rando channel on the [ALTTP Randomizer discord](https://discordapp.com/invite/alttprandomizer). -# Installation from source +# Installation from Source See these instructions. https://github.com/codemann8/ALttPDoorRandomizer/blob/OverworldShuffle/docs/BUILDING.md -When installing platform specific dependencies, don't forget to run the appropriate command from the bottom of the page! Those will install missing pip dependencies. +This program requires all python dependencies that are necessary to run Aerinon's Door Randomizer plus an additional 'sortedcontainers' package. Try running ```pip install sortedcontainers``` on the command line to install the dependency. -Running the MultiServer and MultiClient for multiworld should run resources/ci/common/local_install.py for those dependencies as well. +Alternatively, run ```resources/ci/common/local_install.py``` to install all the missing dependencies as well. + +# Running the Program To use the CLI, run ```DungeonRandomizer.py```. From 6e49f38bce5ae6379a74f11192c30f9673c2388a Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 2 Jul 2021 00:29:34 -0500 Subject: [PATCH 2/8] Updated install instructions --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6b459cb..8e836500 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,16 @@ All feedback and dev conversation happens in the #ow-rando channel on the [ALTTP # Installation from Source -See these instructions. +Download the source code from the repository directly and put it in a folder of your choosing. -https://github.com/codemann8/ALttPDoorRandomizer/blob/OverworldShuffle/docs/BUILDING.md +You must have Python installed (version 3.6 - 3.9 supported) This program requires all python dependencies that are necessary to run Aerinon's Door Randomizer plus an additional 'sortedcontainers' package. Try running ```pip install sortedcontainers``` on the command line to install the dependency. Alternatively, run ```resources/ci/common/local_install.py``` to install all the missing dependencies as well. +See the following link if you have additional trouble: https://github.com/codemann8/ALttPDoorRandomizer/blob/OverworldShuffle/docs/BUILDING.md + # Running the Program To use the CLI, run ```DungeonRandomizer.py```. From 433a1dfc3545c4ee523042f3113f55ac83cd6cbc Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 4 Jul 2021 11:23:08 -0500 Subject: [PATCH 3/8] Doing GitHub's work for them and ACTUALLY merging in DR v0.4.0.7 changes --- Adjuster.py | 1 + AdjusterMain.py | 4 +- CLI.py | 4 +- DoorShuffle.py | 15 +-- Doors.py | 23 +++-- Dungeons.py | 3 +- Main.py | 4 +- Regions.py | 5 +- Rom.py | 92 ++++++++++++++++++- Rules.py | 13 ++- 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 | 1 + .../gui/randomize/gameoptions/widgets.json | 3 +- source/classes/SpriteSelector.py | 5 +- source/classes/constants.py | 3 +- source/gui/adjust/overview.py | 4 +- 18 files changed, 153 insertions(+), 37 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..bc463444 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -24,8 +24,10 @@ 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) + output_path.cached_path = args.outputpath rom.write_to_file(output_path('%s.sfc' % outfilebase)) logger.info('Done. Enjoy.') diff --git a/CLI.py b/CLI.py index 3e13f790..467ccf0d 100644 --- a/CLI.py +++ b/CLI.py @@ -102,7 +102,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}) @@ -192,6 +193,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/DoorShuffle.py b/DoorShuffle.py index 3478d027..5634efce 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) @@ -674,7 +675,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: @@ -2299,6 +2300,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/Main.py b/Main.py index f7dee19f..7faef477 100644 --- a/Main.py +++ b/Main.py @@ -290,7 +290,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/Regions.py b/Regions.py index bbec1ca0..6fd64c33 100644 --- a/Regions.py +++ b/Regions.py @@ -876,7 +876,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']), @@ -997,7 +998,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/Rom.py b/Rom.py index 01f930b3..c3ff380e 100644 --- a/Rom.py +++ b/Rom.py @@ -496,8 +496,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.') @@ -1723,7 +1721,15 @@ 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 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) @@ -1780,6 +1786,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) @@ -1795,6 +1827,60 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr if isinstance(rom, LocalRom): 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: Sun, 4 Jul 2021 11:29:44 -0500 Subject: [PATCH 4/8] Adding OW Rando credits --- Rom.py | 6 +++--- data/base2current.bps | Bin 138574 -> 138609 bytes 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Rom.py b/Rom.py index c3ff380e..79768a46 100644 --- a/Rom.py +++ b/Rom.py @@ -28,7 +28,7 @@ from OverworldShuffle import default_flute_connections, flute_data JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '6e44346357f8a9471a8499ab787635c1' +RANDOMIZERBASEHASH = '6a018a61c2ba2d9d5c75cd5fb8ac003e' class JsonRom(object): @@ -890,7 +890,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 = 0x2391CC 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) @@ -909,7 +909,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 = 0x2390EE gtbk_pc = gtbk_address - 0x120000 # convert to pc mid_top, mid_bot = credits_digit(total // 10) last_top, last_bot = credits_digit(total % 10) diff --git a/data/base2current.bps b/data/base2current.bps index d788ef0229b0c36f0ef7f2bbddcb07a75539fb1e..42006a56bd4494db21c72494f988fc6bf2574611 100644 GIT binary patch delta 3490 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 Date: Sun, 4 Jul 2021 11:38:34 -0500 Subject: [PATCH 5/8] Adding OW Rando credits --- Rom.py | 2 +- data/base2current.bps | Bin 138609 -> 138611 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 79768a46..36687a45 100644 --- a/Rom.py +++ b/Rom.py @@ -28,7 +28,7 @@ from OverworldShuffle import default_flute_connections, flute_data JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '6a018a61c2ba2d9d5c75cd5fb8ac003e' +RANDOMIZERBASEHASH = 'f10e887af782159e242c99a74df13874' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 42006a56bd4494db21c72494f988fc6bf2574611..c7dd4c1c93a938e513d179043cf6e1ee114241d6 100644 GIT binary patch delta 147 zcmV;E0Bry9x(M^S2(UZ>1OycO>$5-s5rhpWFCHr}C@Uo$t1^Q1lU{`w4?R3eOF&Jx zErEq+UAJs<~OG`YTN4pJ-mI&1HGmnX5FXb`Y BGGhP$ delta 145 zcmV;C0B-;Dx(M;Q2(UZ>1jrLN?6W`t5rhp!O*~0XMoB|Es~&*)lU{`w4IdsXEg~-- zyD5UV0EUx=g+UTZM@c|SJfAx}O*}(JM?^eDOOx1z&Koi)E-RlbDJvo^9-kXB3Kc|3 zOrJ_jM@L3UOOwHd8W1TOB_1s;9-k~BE*~Ey9xH?EhPUg60f3eW8{fl?7QJYE@_I0o From b0a7237744775b003407c35a52f8d2ec467fb810 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 4 Jul 2021 11:42:22 -0500 Subject: [PATCH 6/8] Version bump 0.1.6.3 --- CHANGELOG.md | 7 ++++++- OverworldShuffle.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0017790..dc84da1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### 0.1.6.3 +- Fixed borked credits (and missing Sprite Author) when collection rate isn't 216 or if GTBK count isn't /22 +- Added OW Rando in credits +- Actually merged in DR v0.4.0.7 (with no thanks to GitHub) + ### 0.1.6.2 - Added Balanced option for Flute Shuffle - Fixed issue with Flute Spot to Mountain Entry softlocking @@ -28,7 +33,7 @@ ### 0.1.5.0 - Added OW Tile Swap setting - Fixed horizontal VRAM visual loading glitch on megatiles -- Merged DR v0.4.0.7 - Fast Credits / Reduced Flashing / Sprite Author in Credits +- ~~Merged DR v0.4.0.7 - Fast Credits / Reduced Flashing / Sprite Author in Credits~~ ### 0.1.4.3 - Merged DR v0.4.0.6 - TT Maiden Attic Hint / DR Entrance Floor Mat Mods / Hard/Expert Item Pool Fix diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 506996c2..cc0c6491 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -3,7 +3,7 @@ from sortedcontainers import SortedList from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSlot from OWEdges import OWTileGroups, OWEdgeGroups, OpenStd, parallel_links, IsParallel -__version__ = '0.1.6.2-u' +__version__ = '0.1.6.3-u' def link_overworld(world, player): # setup mandatory connections From e43c67ee80eaa197d3d34ddf2aaf740339a85250 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 4 Jul 2021 12:07:03 -0500 Subject: [PATCH 7/8] Fixed local_install.py to work when double-clicked --- resources/ci/common/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 5874d0dd..7bc6a37e 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -6,7 +6,7 @@ import subprocess # do stuff at the shell level env = common.prepare_env() -pip_requirements = os.path.join(".","resources","app","meta","manifests","pip_requirements.txt") +pip_requirements = os.path.join("..","..","..","resources","app","meta","manifests","pip_requirements.txt") def run_install(PY_VERSION,USER): # get executables From e789531e36f2366d0dc4c1a2b6bc3b9b9f5031b6 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 4 Jul 2021 12:29:46 -0500 Subject: [PATCH 8/8] Fixed local_install.py to work when double-clicked --- resources/ci/common/install.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 7bc6a37e..6830905e 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -6,7 +6,9 @@ import subprocess # do stuff at the shell level env = common.prepare_env() -pip_requirements = os.path.join("..","..","..","resources","app","meta","manifests","pip_requirements.txt") +pip_requirements = os.path.join("..","resources","app","meta","manifests","pip_requirements.txt") +if not os.path.isfile(pip_requirements): + pip_requirements = os.path.join("..","..","..","resources","app","meta","manifests","pip_requirements.txt") def run_install(PY_VERSION,USER): # get executables