diff --git a/Adjuster.py b/Adjuster.py index 393b7b75..d3a8239c 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -15,7 +15,8 @@ class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): def main(): parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) - parser.add_argument('--rom', default='ER_base.sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.') + parser.add_argument('--rom', default='ER_base.sfc', help='Path to an ALttPR rom to adjust.') + parser.add_argument('--baserom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.') parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.') parser.add_argument('--fastmenu', default='normal', const='normal', nargs='?', choices=['normal', 'instant', 'double', 'triple', 'quadruple', 'half'], help='''\ @@ -31,6 +32,8 @@ def main(): ''') parser.add_argument('--heartcolor', default='red', const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow', 'random'], 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('--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, @@ -43,7 +46,7 @@ def main(): # ToDo: Validate files further than mere existance if not os.path.isfile(args.rom): - input('Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom) + input('Could not find valid rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom) sys.exit(1) if args.sprite is not None and not os.path.isfile(args.sprite): input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite) diff --git a/AdjusterMain.py b/AdjusterMain.py index f4dbb663..5bf6eeb4 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -24,10 +24,13 @@ def adjust(args): if os.stat(args.rom).st_size in (0x200000, 0x400000) and os.path.splitext(args.rom)[-1].lower() == '.sfc': rom = LocalRom(args.rom, False) + if os.path.isfile(args.baserom): + baserom = LocalRom(args.baserom, True) + rom.orig_buffer = baserom.orig_buffer else: raise RuntimeError('Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.') - apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, sprite, parse_names_string(args.names)) + apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, sprite, args.ow_palettes, args.uw_palettes, parse_names_string(args.names)) rom.write_to_file(output_path('%s.sfc' % outfilebase)) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 064d58ff..c315c962 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -242,6 +242,8 @@ def parse_arguments(argv, no_defaults=False): ''') parser.add_argument('--heartcolor', default=defval('red'), const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow', 'random'], help='Select the color of Link\'s heart meter. (default: %(default)s)') + parser.add_argument('--ow_palettes', default=defval('default'), choices=['default', 'random', 'blackout']) + parser.add_argument('--uw_palettes', default=defval('default'), choices=['default', 'random', 'blackout']) 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, @@ -261,7 +263,6 @@ def parse_arguments(argv, no_defaults=False): parser.add_argument('--shuffleenemies', default=defval('none'), choices=['none', 'shuffled', 'chaos']) parser.add_argument('--enemy_health', default=defval('default'), choices=['default', 'easy', 'normal', 'hard', 'expert']) parser.add_argument('--enemy_damage', default=defval('default'), choices=['default', 'shuffled', 'chaos']) - parser.add_argument('--shufflepalette', default=defval(False), action='store_true') parser.add_argument('--shufflepots', default=defval(False), action='store_true') parser.add_argument('--beemizer', default=defval(0), type=lambda value: min(max(int(value), 0), 4)) parser.add_argument('--multi', default=defval(1), type=lambda value: min(max(int(value), 1), 255)) @@ -286,8 +287,9 @@ def parse_arguments(argv, no_defaults=False): for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', - 'retro', 'accessibility', 'hints', 'shufflepalette', 'shufflepots', 'beemizer', - 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage']: + 'retro', 'accessibility', 'hints', 'shufflepots', 'beemizer', + 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', + 'ow_palettes', 'uw_palettes']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) diff --git a/Gui.py b/Gui.py index 29b15442..cac05534 100755 --- a/Gui.py +++ b/Gui.py @@ -60,8 +60,6 @@ def guiMain(args=None): createSpoilerCheckbutton = Checkbutton(checkBoxFrame, text="Create Spoiler Log", variable=createSpoilerVar) suppressRomVar = IntVar() suppressRomCheckbutton = Checkbutton(checkBoxFrame, text="Do not create patched Rom", variable=suppressRomVar) - quickSwapVar = IntVar() - quickSwapCheckbutton = Checkbutton(checkBoxFrame, text="Enabled L/R Item quickswapping", variable=quickSwapVar) openpyramidVar = IntVar() openpyramidCheckbutton = Checkbutton(checkBoxFrame, text="Pre-open Pyramid Hole", variable=openpyramidVar) mcsbshuffleFrame = Frame(checkBoxFrame) @@ -76,8 +74,6 @@ def guiMain(args=None): bigkeyshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="BigKeys", variable=bigkeyshuffleVar) retroVar = IntVar() retroCheckbutton = Checkbutton(checkBoxFrame, text="Retro mode (universal keys)", variable=retroVar) - disableMusicVar = IntVar() - disableMusicCheckbutton = Checkbutton(checkBoxFrame, text="Disable game music", variable=disableMusicVar) shuffleGanonVar = IntVar() shuffleGanonVar.set(1) #set default shuffleGanonCheckbutton = Checkbutton(checkBoxFrame, text="Include Ganon's Tower and Pyramid Hole in shuffle pool", variable=shuffleGanonVar) @@ -89,7 +85,6 @@ def guiMain(args=None): createSpoilerCheckbutton.pack(expand=True, anchor=W) suppressRomCheckbutton.pack(expand=True, anchor=W) - quickSwapCheckbutton.pack(expand=True, anchor=W) openpyramidCheckbutton.pack(expand=True, anchor=W) mcsbshuffleFrame.pack(expand=True, anchor=W) mcsbLabel.grid(row=0, column=0) @@ -98,57 +93,23 @@ def guiMain(args=None): keyshuffleCheckbutton.grid(row=0, column=3) bigkeyshuffleCheckbutton.grid(row=0, column=4) retroCheckbutton.pack(expand=True, anchor=W) - disableMusicCheckbutton.pack(expand=True, anchor=W) shuffleGanonCheckbutton.pack(expand=True, anchor=W) hintsCheckbutton.pack(expand=True, anchor=W) customCheckbutton.pack(expand=True, anchor=W) - fileDialogFrame = Frame(rightHalfFrame) + romOptionsFrame = LabelFrame(rightHalfFrame, text="Rom options") + romOptionsFrame.columnconfigure(0, weight=1) + romOptionsFrame.columnconfigure(1, weight=1) + for i in range(5): + romOptionsFrame.rowconfigure(i, weight=1) - heartbeepFrame = Frame(fileDialogFrame) - heartbeepVar = StringVar() - heartbeepVar.set('normal') - heartbeepOptionMenu = OptionMenu(heartbeepFrame, heartbeepVar, 'double', 'normal', 'half', 'quarter', 'off') - heartbeepOptionMenu.pack(side=RIGHT) - heartbeepLabel = Label(heartbeepFrame, text='Heartbeep sound rate') - heartbeepLabel.pack(side=LEFT, padx=(0,52)) + disableMusicVar = IntVar() + disableMusicCheckbutton = Checkbutton(romOptionsFrame, text="Disable music", variable=disableMusicVar) + disableMusicCheckbutton.grid(row=0, column=0, sticky=E) - heartcolorFrame = Frame(fileDialogFrame) - heartcolorVar = StringVar() - heartcolorVar.set('red') - heartcolorOptionMenu = OptionMenu(heartcolorFrame, heartcolorVar, 'red', 'blue', 'green', 'yellow', 'random') - heartcolorOptionMenu.pack(side=RIGHT) - heartcolorLabel = Label(heartcolorFrame, text='Heart color') - heartcolorLabel.pack(side=LEFT, padx=(0,127)) - - fastMenuFrame = Frame(fileDialogFrame) - fastMenuVar = StringVar() - fastMenuVar.set('normal') - fastMenuOptionMenu = OptionMenu(fastMenuFrame, fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') - fastMenuOptionMenu.pack(side=RIGHT) - fastMenuLabel = Label(fastMenuFrame, text='Menu speed') - fastMenuLabel.pack(side=LEFT, padx=(0,100)) - - heartbeepFrame.pack(expand=True, anchor=E) - heartcolorFrame.pack(expand=True, anchor=E) - fastMenuFrame.pack(expand=True, anchor=E) - - romDialogFrame = Frame(fileDialogFrame) - baseRomLabel = Label(romDialogFrame, text='Base Rom') - romVar = StringVar(value="Zelda no Densetsu - Kamigami no Triforce (Japan).sfc") - romEntry = Entry(romDialogFrame, textvariable=romVar) - - def RomSelect(): - rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")]) - romVar.set(rom) - romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect) - - baseRomLabel.pack(side=LEFT) - romEntry.pack(side=LEFT) - romSelectButton.pack(side=LEFT) - - spriteDialogFrame = Frame(fileDialogFrame) - baseSpriteLabel = Label(spriteDialogFrame, text='Link Sprite:') + spriteDialogFrame = Frame(romOptionsFrame) + spriteDialogFrame.grid(row=0, column=1) + baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:') spriteNameVar = StringVar() sprite = None @@ -168,17 +129,79 @@ def guiMain(args=None): def SpriteSelect(): SpriteSelector(mainWindow, set_sprite) - spriteSelectButton = Button(spriteDialogFrame, text='Open Sprite Picker', command=SpriteSelect) + spriteSelectButton = Button(spriteDialogFrame, text='...', command=SpriteSelect) baseSpriteLabel.pack(side=LEFT) spriteEntry.pack(side=LEFT) spriteSelectButton.pack(side=LEFT) - romDialogFrame.pack() - spriteDialogFrame.pack() + quickSwapVar = IntVar() + quickSwapCheckbutton = Checkbutton(romOptionsFrame, text="L/R Quickswapping", variable=quickSwapVar) + quickSwapCheckbutton.grid(row=1, column=0, sticky=E) - checkBoxFrame.pack() - fileDialogFrame.pack() + fastMenuFrame = Frame(romOptionsFrame) + fastMenuFrame.grid(row=1, column=1, sticky=E) + fastMenuLabel = Label(fastMenuFrame, text='Menu speed') + fastMenuLabel.pack(side=LEFT) + fastMenuVar = StringVar() + fastMenuVar.set('normal') + fastMenuOptionMenu = OptionMenu(fastMenuFrame, fastMenuVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') + fastMenuOptionMenu.pack(side=LEFT) + + heartcolorFrame = Frame(romOptionsFrame) + heartcolorFrame.grid(row=2, column=0, sticky=E) + heartcolorLabel = Label(heartcolorFrame, text='Heart color') + heartcolorLabel.pack(side=LEFT) + heartcolorVar = StringVar() + heartcolorVar.set('red') + heartcolorOptionMenu = OptionMenu(heartcolorFrame, heartcolorVar, 'red', 'blue', 'green', 'yellow', 'random') + heartcolorOptionMenu.pack(side=LEFT) + + heartbeepFrame = Frame(romOptionsFrame) + heartbeepFrame.grid(row=2, column=1, sticky=E) + heartbeepLabel = Label(heartbeepFrame, text='Heartbeep') + heartbeepLabel.pack(side=LEFT) + heartbeepVar = StringVar() + heartbeepVar.set('normal') + heartbeepOptionMenu = OptionMenu(heartbeepFrame, heartbeepVar, 'double', 'normal', 'half', 'quarter', 'off') + heartbeepOptionMenu.pack(side=LEFT) + + owPalettesFrame = Frame(romOptionsFrame) + owPalettesFrame.grid(row=3, column=0, sticky=E) + owPalettesLabel = Label(owPalettesFrame, text='Overworld palettes') + owPalettesLabel.pack(side=LEFT) + owPalettesVar = StringVar() + owPalettesVar.set('default') + owPalettesOptionMenu = OptionMenu(owPalettesFrame, owPalettesVar, 'default', 'random', 'blackout') + owPalettesOptionMenu.pack(side=LEFT) + + uwPalettesFrame = Frame(romOptionsFrame) + uwPalettesFrame.grid(row=3, column=1, sticky=E) + uwPalettesLabel = Label(uwPalettesFrame, text='Dungeon palettes') + uwPalettesLabel.pack(side=LEFT) + uwPalettesVar = StringVar() + uwPalettesVar.set('default') + uwPalettesOptionMenu = OptionMenu(uwPalettesFrame, uwPalettesVar, 'default', 'random', 'blackout') + uwPalettesOptionMenu.pack(side=LEFT) + + romDialogFrame = Frame(romOptionsFrame) + romDialogFrame.grid(row=4, column=0, columnspan=2, sticky=W+E) + + baseRomLabel = Label(romDialogFrame, text='Base Rom: ') + romVar = StringVar(value="Zelda no Densetsu - Kamigami no Triforce (Japan).sfc") + romEntry = Entry(romDialogFrame, textvariable=romVar) + + def RomSelect(): + rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")]) + romVar.set(rom) + romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect) + + baseRomLabel.pack(side=LEFT) + romEntry.pack(side=LEFT, expand=True, fill=X) + romSelectButton.pack(side=LEFT) + + checkBoxFrame.pack(side=TOP, anchor=W, padx=5, pady=10) + romOptionsFrame.pack(expand=True, fill=BOTH, padx=3) drowDownFrame = Frame(topFrame) @@ -300,18 +323,19 @@ def guiMain(args=None): algorithmFrame.pack(expand=True, anchor=E) shuffleFrame.pack(expand=True, anchor=E) - enemizerFrame = LabelFrame(randomizerWindow, text="Enemizer", padx=5, pady=5) + enemizerFrame = LabelFrame(randomizerWindow, text="Enemizer", padx=5, pady=2) enemizerFrame.columnconfigure(0, weight=1) enemizerFrame.columnconfigure(1, weight=1) enemizerFrame.columnconfigure(2, weight=1) + enemizerFrame.columnconfigure(3, weight=1) enemizerPathFrame = Frame(enemizerFrame) - enemizerPathFrame.grid(row=0, column=0, columnspan=3, sticky=W) + enemizerPathFrame.grid(row=0, column=0, columnspan=3, sticky=W+E, padx=3) enemizerCLIlabel = Label(enemizerPathFrame, text="EnemizerCLI path: ") enemizerCLIlabel.pack(side=LEFT) enemizerCLIpathVar = StringVar(value="EnemizerCLI/EnemizerCLI.Core") - enemizerCLIpathEntry = Entry(enemizerPathFrame, textvariable=enemizerCLIpathVar, width=80) - enemizerCLIpathEntry.pack(side=LEFT) + enemizerCLIpathEntry = Entry(enemizerPathFrame, textvariable=enemizerCLIpathVar) + enemizerCLIpathEntry.pack(side=LEFT, expand=True, fill=X) def EnemizerSelectPath(): path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")]) if path: @@ -319,18 +343,21 @@ def guiMain(args=None): enemizerCLIbrowseButton = Button(enemizerPathFrame, text='...', command=EnemizerSelectPath) enemizerCLIbrowseButton.pack(side=LEFT) - enemyShuffleVar = IntVar() - enemyShuffleButton = Checkbutton(enemizerFrame, text="Enemy shuffle", variable=enemyShuffleVar) - enemyShuffleButton.grid(row=1, column=0) - paletteShuffleVar = IntVar() - paletteShuffleButton = Checkbutton(enemizerFrame, text="Palette shuffle", variable=paletteShuffleVar) - paletteShuffleButton.grid(row=1, column=1) potShuffleVar = IntVar() potShuffleButton = Checkbutton(enemizerFrame, text="Pot shuffle", variable=potShuffleVar) - potShuffleButton.grid(row=1, column=2) + potShuffleButton.grid(row=0, column=3) + + enemizerEnemyFrame = Frame(enemizerFrame) + enemizerEnemyFrame.grid(row=1, column=0, pady=5) + enemizerEnemyLabel = Label(enemizerEnemyFrame, text='Enemy shuffle') + enemizerEnemyLabel.pack(side=LEFT) + enemyShuffleVar = StringVar() + enemyShuffleVar.set('none') + enemizerEnemyOption = OptionMenu(enemizerEnemyFrame, enemyShuffleVar, 'none', 'shuffled', 'chaos') + enemizerEnemyOption.pack(side=LEFT) enemizerBossFrame = Frame(enemizerFrame) - enemizerBossFrame.grid(row=2, column=0) + enemizerBossFrame.grid(row=1, column=1) enemizerBossLabel = Label(enemizerBossFrame, text='Boss shuffle') enemizerBossLabel.pack(side=LEFT) enemizerBossVar = StringVar() @@ -339,7 +366,7 @@ def guiMain(args=None): enemizerBossOption.pack(side=LEFT) enemizerDamageFrame = Frame(enemizerFrame) - enemizerDamageFrame.grid(row=2, column=1) + enemizerDamageFrame.grid(row=1, column=2) enemizerDamageLabel = Label(enemizerDamageFrame, text='Enemy damage') enemizerDamageLabel.pack(side=LEFT) enemizerDamageVar = StringVar() @@ -348,7 +375,7 @@ def guiMain(args=None): enemizerDamageOption.pack(side=LEFT) enemizerHealthFrame = Frame(enemizerFrame) - enemizerHealthFrame.grid(row=2, column=2) + enemizerHealthFrame.grid(row=1, column=3) enemizerHealthLabel = Label(enemizerHealthFrame, text='Enemy health') enemizerHealthLabel.pack(side=LEFT) enemizerHealthVar = StringVar() @@ -403,14 +430,15 @@ def guiMain(args=None): guiargs.retro = bool(retroVar.get()) guiargs.quickswap = bool(quickSwapVar.get()) guiargs.disablemusic = bool(disableMusicVar.get()) + guiargs.ow_palettes = owPalettesVar.get() + guiargs.uw_palettes = uwPalettesVar.get() guiargs.shuffleganon = bool(shuffleGanonVar.get()) guiargs.hints = bool(hintsVar.get()) guiargs.enemizercli = enemizerCLIpathVar.get() guiargs.shufflebosses = enemizerBossVar.get() - guiargs.shuffleenemies = 'chaos' if bool(enemyShuffleVar.get()) else 'none' + guiargs.shuffleenemies = enemyShuffleVar.get() guiargs.enemy_health = enemizerHealthVar.get() guiargs.enemy_damage = enemizerDamageVar.get() - guiargs.shufflepalette = bool(paletteShuffleVar.get()) guiargs.shufflepots = bool(potShuffleVar.get()) guiargs.custom = bool(customVar.get()) guiargs.customitemarray = [int(bowVar.get()), int(silverarrowVar.get()), int(boomerangVar.get()), int(magicboomerangVar.get()), int(hookshotVar.get()), int(mushroomVar.get()), int(magicpowderVar.get()), int(firerodVar.get()), @@ -534,6 +562,18 @@ def guiMain(args=None): fastMenuLabel2 = Label(fastMenuFrame2, text='Menu speed') fastMenuLabel2.pack(side=LEFT) + owPalettesFrame2 = Frame(drowDownFrame2) + owPalettesOptionMenu2 = OptionMenu(owPalettesFrame2, owPalettesVar, 'default', 'random', 'blackout') + owPalettesOptionMenu2.pack(side=RIGHT) + owPalettesLabel2 = Label(owPalettesFrame2, text='Overworld palettes') + owPalettesLabel2.pack(side=LEFT) + + uwPalettesFrame2 = Frame(drowDownFrame2) + uwPalettesOptionMenu2 = OptionMenu(uwPalettesFrame2, uwPalettesVar, 'default', 'random', 'blackout') + uwPalettesOptionMenu2.pack(side=RIGHT) + uwPalettesLabel2 = Label(uwPalettesFrame2, text='Dungeon palettes') + uwPalettesLabel2.pack(side=LEFT) + namesFrame2 = Frame(drowDownFrame2) namesLabel2 = Label(namesFrame2, text='Player names') namesVar2 = StringVar() @@ -545,6 +585,8 @@ def guiMain(args=None): heartbeepFrame2.pack(expand=True, anchor=E) heartcolorFrame2.pack(expand=True, anchor=E) fastMenuFrame2.pack(expand=True, anchor=E) + owPalettesFrame2.pack(expand=True, anchor=E) + uwPalettesFrame2.pack(expand=True, anchor=E) namesFrame2.pack(expand=True, anchor=E) bottomFrame2 = Frame(topFrame2) @@ -554,9 +596,12 @@ def guiMain(args=None): guiargs.heartbeep = heartbeepVar.get() guiargs.heartcolor = heartcolorVar.get() guiargs.fastmenu = fastMenuVar.get() + guiargs.ow_palettes = owPalettesVar.get() + guiargs.uw_palettes = uwPalettesVar.get() guiargs.quickswap = bool(quickSwapVar.get()) guiargs.disablemusic = bool(disableMusicVar.get()) guiargs.rom = romVar2.get() + guiargs.baserom = romVar.get() guiargs.sprite = sprite guiargs.names = namesEntry2.get() try: diff --git a/Main.py b/Main.py index 8e7ccfb4..f20940fb 100644 --- a/Main.py +++ b/Main.py @@ -152,7 +152,7 @@ def main(args, seed=None): for player in range(1, world.players + 1): use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none' or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' - or args.shufflepalette[player] or args.shufflepots[player]) + or args.shufflepots[player]) local_rom = None if args.jsonout: @@ -168,7 +168,7 @@ def main(args, seed=None): enemizer_patch = [] if use_enemizer and (args.enemizercli or not args.jsonout): - enemizer_patch = get_enemizer_patch(world, player, rom, args.rom, args.enemizercli, args.shufflepalette[player], args.shufflepots[player]) + enemizer_patch = get_enemizer_patch(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player]) if args.jsonout: jsonout[f'patch{player}'] = rom.patches @@ -185,7 +185,7 @@ def main(args, seed=None): for addr, values in get_race_rom_patches(rom).items(): rom.write_bytes(int(addr), values) - apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite, player_names) + apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite, args.ow_palettes[player], args.uw_palettes[player], player_names) mcsb_name = '' if all([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]]): diff --git a/Plando.py b/Plando.py index 3011a023..cf1fbf51 100755 --- a/Plando.py +++ b/Plando.py @@ -74,9 +74,9 @@ def main(args): sprite = None rom = LocalRom(args.rom) - patch_rom(world, 1, rom) + patch_rom(world, 1, rom, False) - apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite) + apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite, args.ow_palettes, args.uw_palettes) for textname, texttype, text in text_patches: if texttype == 'text': @@ -213,6 +213,8 @@ def start(): help='Select the rate at which the heart beep sound is played at low health.') parser.add_argument('--heartcolor', default='red', const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow'], 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('--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.') parser.add_argument('--plando', help='Filled out template to use for setting up the rom.') args = parser.parse_args() diff --git a/Rom.py b/Rom.py index adb462b8..8af1a23e 100644 --- a/Rom.py +++ b/Rom.py @@ -27,6 +27,7 @@ class JsonRom(object): def __init__(self): self.name = None + self.orig_buffer = None self.patches = {} self.addresses = [] @@ -72,10 +73,12 @@ class LocalRom(object): def __init__(self, file, patch=True): self.name = None + self.orig_buffer = None with open(file, 'rb') as stream: self.buffer = read_rom(stream) if patch: self.patch_base_rom() + self.orig_buffer = self.buffer.copy() def write_byte(self, address, value): self.buffer[address] = value @@ -161,7 +164,7 @@ def read_rom(stream): buffer = buffer[0x200:] return buffer -def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shufflepalette, shufflepots): +def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shufflepots): baserom_path = os.path.abspath(baserom_path) basepatch_path = os.path.abspath(local_path('data/base2current.json')) randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json')) @@ -197,10 +200,10 @@ def get_enemizer_patch(world, player, rom, baserom_path, enemizercli, shufflepal 'RandomizeBossDamageMinAmount': 0, 'RandomizeBossDamageMaxAmount': 200, 'RandomizeBossBehavior': False, - 'RandomizeDungeonPalettes': shufflepalette, + 'RandomizeDungeonPalettes': False, 'SetBlackoutMode': False, - 'RandomizeOverworldPalettes': shufflepalette, - 'RandomizeSpritePalettes': shufflepalette, + 'RandomizeOverworldPalettes': False, + 'RandomizeSpritePalettes': False, 'SetAdvancedSpritePalettes': False, 'PukeMode': False, 'NegativeMode': False, @@ -1278,7 +1281,7 @@ def hud_format_text(text): return output[:32] -def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, names = None): +def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, ow_palettes, uw_palettes, names = None): # enable instant item menu if fastmenu == 'instant': @@ -1439,6 +1442,18 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr if sprite is not None: write_sprite(rom, sprite) + default_ow_palettes(rom) + if ow_palettes == 'random': + randomize_ow_palettes(rom) + elif ow_palettes == 'blackout': + blackout_ow_palettes(rom) + + default_uw_palettes(rom) + if uw_palettes == 'random': + randomize_uw_palettes(rom) + elif uw_palettes == 'blackout': + blackout_uw_palettes(rom) + # set player names for player, name in names.items(): if 0 < player <= 64: @@ -1455,6 +1470,130 @@ def write_sprite(rom, sprite): rom.write_bytes(0xDD308, sprite.palette) rom.write_bytes(0xDEDF5, sprite.glove_palette) +def set_color(rom, address, color, shade): + r = round(min(color[0], 0xFF) * pow(0.8, shade) * 0x1F / 0xFF) + g = round(min(color[1], 0xFF) * pow(0.8, shade) * 0x1F / 0xFF) + b = round(min(color[2], 0xFF) * pow(0.8, shade) * 0x1F / 0xFF) + + rom.write_bytes(address, ((b << 10) | (g << 5) | (r << 0)).to_bytes(2, byteorder='little', signed=False)) + +def default_ow_palettes(rom): + if not rom.orig_buffer: + return + rom.write_bytes(0xDE604, rom.orig_buffer[0xDE604:0xDEBB4]) + + for address in [0x067FB4, 0x067F94, 0x067FC6, 0x067FE6, 0x067FE1, 0x05FEA9, 0x05FEB3]: + rom.write_bytes(address, rom.orig_buffer[address:address+2]) + +def randomize_ow_palettes(rom): + grass, grass2, grass3, dirt, dirt2, water, clouds, dwdirt,\ + dwgrass, dwwater, dwdmdirt, dwdmgrass, dwdmclouds1, dwdmclouds2 = [[random.randint(60, 215) for _ in range(3)] for _ in range(14)] + dwtree = [c + random.randint(-20, 10) for c in dwgrass] + treeleaf = [c + random.randint(-20, 10) for c in grass] + + patches = {0x067FB4: (grass, 0), 0x067F94: (grass, 0), 0x067FC6: (grass, 0), 0x067FE6: (grass, 0), 0x067FE1: (grass, 3), 0x05FEA9: (grass, 0), 0x05FEB3: (dwgrass, 1), + 0x0DD4AC: (grass, 2), 0x0DE6DE: (grass2, 2), 0x0DE6E0: (grass2, 1), 0x0DD4AE: (grass2, 1), 0x0DE9FA: (grass2, 1), 0x0DEA0E: (grass2, 1), 0x0DE9FE: (grass2, 0), + 0x0DD3D2: (grass2, 2), 0x0DE88C: (grass2, 2), 0x0DE8A8: (grass2, 2), 0x0DE9F8: (grass2, 2), 0x0DEA4E: (grass2, 2), 0x0DEAF6: (grass2, 2), 0x0DEB2E: (grass2, 2), 0x0DEB4A: (grass2, 2), + 0x0DE892: (grass, 1), 0x0DE886: (grass, 0), 0x0DE6D2: (grass, 0), 0x0DE6FA: (grass, 3), 0x0DE6FC: (grass, 0), 0x0DE6FE: (grass, 0), 0x0DE70A: (grass, 0), 0x0DE708: (grass, 2), 0x0DE70C: (grass, 1), + 0x0DE6D4: (dirt, 2), 0x0DE6CA: (dirt, 5), 0x0DE6CC: (dirt, 4), 0x0DE6CE: (dirt, 3), 0x0DE6E2: (dirt, 2), 0x0DE6D8: (dirt, 5), 0x0DE6DA: (dirt, 4), 0x0DE6DC: (dirt, 2), + 0x0DE6F0: (dirt, 2), 0x0DE6E6: (dirt, 5), 0x0DE6E8: (dirt, 4), 0x0DE6EA: (dirt, 2), 0x0DE6EC: (dirt, 4), 0x0DE6EE: (dirt, 2), + 0x0DE91E: (grass, 0), + 0x0DE920: (dirt, 2), 0x0DE916: (dirt, 3), 0x0DE934: (dirt, 3), + 0x0DE92C: (grass, 0), 0x0DE93A: (grass, 0), 0x0DE91C: (grass, 1), 0x0DE92A: (grass, 1), 0x0DEA1C: (grass, 0), 0x0DEA2A: (grass, 0), 0x0DEA30: (grass, 0), + 0x0DEA2E: (dirt, 5), + 0x0DE884: (grass, 3), 0x0DE8AE: (grass, 3), 0x0DE8BE: (grass, 3), 0x0DE8E4: (grass, 3), 0x0DE938: (grass, 3), 0x0DE9C4: (grass, 3), 0x0DE6D0: (grass, 4), + 0x0DE890: (treeleaf, 1), 0x0DE894: (treeleaf, 0), + 0x0DE924: (water, 3), 0x0DE668: (water, 3), 0x0DE66A: (water, 2), 0x0DE670: (water, 1), 0x0DE918: (water, 1), 0x0DE66C: (water, 0), 0x0DE91A: (water, 0), 0x0DE92E: (water, 1), 0x0DEA1A: (water, 1), 0x0DEA16: (water, 3), 0x0DEA10: (water, 4), + 0x0DE66E: (dirt, 3), 0x0DE672: (dirt, 2), 0x0DE932: (dirt, 4), 0x0DE936: (dirt, 2), 0x0DE93C: (dirt, 1), + 0x0DE756: (dirt2, 4), 0x0DE764: (dirt2, 4), 0x0DE772: (dirt2, 4), 0x0DE994: (dirt2, 4), 0x0DE9A2: (dirt2, 4), 0x0DE758: (dirt2, 3), 0x0DE766: (dirt2, 3), 0x0DE774: (dirt2, 3), + 0x0DE996: (dirt2, 3), 0x0DE9A4: (dirt2, 3), 0x0DE75A: (dirt2, 2), 0x0DE768: (dirt2, 2), 0x0DE776: (dirt2, 2), 0x0DE778: (dirt2, 2), 0x0DE998: (dirt2, 2), 0x0DE9A6: (dirt2, 2), + 0x0DE9AC: (dirt2, 1), 0x0DE99E: (dirt2, 1), 0x0DE760: (dirt2, 1), 0x0DE77A: (dirt2, 1), 0x0DE77C: (dirt2, 1), 0x0DE798: (dirt2, 1), 0x0DE980: (dirt2, 1), + 0x0DE75C: (grass3, 2), 0x0DE786: (grass3, 2), 0x0DE794: (grass3, 2), 0x0DE99A: (grass3, 2), 0x0DE75E: (grass3, 1), 0x0DE788: (grass3, 1), 0x0DE796: (grass3, 1), 0x0DE99C: (grass3, 1), + 0x0DE76A: (clouds, 2), 0x0DE9A8: (clouds, 2), 0x0DE76E: (clouds, 0), 0x0DE9AA: (clouds, 0), 0x0DE8DA: (clouds, 0), 0x0DE8D8: (clouds, 0), 0x0DE8D0: (clouds, 0), 0x0DE98C: (clouds, 2), 0x0DE990: (clouds, 0), + 0x0DEB34: (dwtree, 4), 0x0DEB30: (dwtree, 3), 0x0DEB32: (dwtree, 1), + 0x0DE710: (dwdirt, 5), 0x0DE71E: (dwdirt, 5), 0x0DE72C: (dwdirt, 5), 0x0DEAD6: (dwdirt, 5), 0x0DE712: (dwdirt, 4), 0x0DE720: (dwdirt, 4), 0x0DE72E: (dwdirt, 4), 0x0DE660: (dwdirt, 4), + 0x0DEAD8: (dwdirt, 4), 0x0DEADA: (dwdirt, 3), 0x0DE714: (dwdirt, 3), 0x0DE722: (dwdirt, 3), 0x0DE730: (dwdirt, 3), 0x0DE732: (dwdirt, 3), 0x0DE734: (dwdirt, 2), 0x0DE736: (dwdirt, 2), + 0x0DE728: (dwdirt, 2), 0x0DE71A: (dwdirt, 2), 0x0DE664: (dwdirt, 2), 0x0DEAE0: (dwdirt, 2), + 0x0DE716: (dwgrass, 3), 0x0DE740: (dwgrass, 3), 0x0DE74E: (dwgrass, 3), 0x0DEAC0: (dwgrass, 3), 0x0DEACE: (dwgrass, 3), 0x0DEADC: (dwgrass, 3), 0x0DEB24: (dwgrass, 3), 0x0DE752: (dwgrass, 2), + 0x0DE718: (dwgrass, 1), 0x0DE742: (dwgrass, 1), 0x0DE750: (dwgrass, 1), 0x0DEB26: (dwgrass, 1), 0x0DEAC2: (dwgrass, 1), 0x0DEAD0: (dwgrass, 1), 0x0DEADE: (dwgrass, 1), + 0x0DE65A: (dwwater, 5), 0x0DE65C: (dwwater, 3), 0x0DEAC8: (dwwater, 3), 0x0DEAD2: (dwwater, 2), 0x0DEABC: (dwwater, 2), 0x0DE662: (dwwater, 2), 0x0DE65E: (dwwater, 1), 0x0DEABE: (dwwater, 1), 0x0DEA98: (dwwater, 2), + 0x0DE79A: (dwdmdirt, 6), 0x0DE7A8: (dwdmdirt, 6), 0x0DE7B6: (dwdmdirt, 6), 0x0DEB60: (dwdmdirt, 6), 0x0DEB6E: (dwdmdirt, 6), 0x0DE93E: (dwdmdirt, 6), 0x0DE94C: (dwdmdirt, 6), 0x0DEBA6: (dwdmdirt, 6), + 0x0DE79C: (dwdmdirt, 4), 0x0DE7AA: (dwdmdirt, 4), 0x0DE7B8: (dwdmdirt, 4), 0x0DEB70: (dwdmdirt, 4), 0x0DEBA8: (dwdmdirt, 4), 0x0DEB72: (dwdmdirt, 3), 0x0DEB74: (dwdmdirt, 3), 0x0DE79E: (dwdmdirt, 3), 0x0DE7AC: (dwdmdirt, 3), 0x0DEBAA: (dwdmdirt, 3), 0x0DE7A0: (dwdmdirt, 3), + 0x0DE7BC: (dwdmgrass, 3), + 0x0DEBAC: (dwdmdirt, 2), 0x0DE7AE: (dwdmdirt, 2), 0x0DE7C2: (dwdmdirt, 2), 0x0DE7A6: (dwdmdirt, 2), 0x0DEB7A: (dwdmdirt, 2), 0x0DEB6C: (dwdmdirt, 2), 0x0DE7C0: (dwdmdirt, 2), + 0x0DE7A2: (dwdmgrass, 3), 0x0DE7BE: (dwdmgrass, 3), 0x0DE7CC: (dwdmgrass, 3), 0x0DE7DA: (dwdmgrass, 3), 0x0DEB6A: (dwdmgrass, 3), 0x0DE948: (dwdmgrass, 3), 0x0DE956: (dwdmgrass, 3), 0x0DE964: (dwdmgrass, 3), 0x0DE7CE: (dwdmgrass, 1), 0x0DE7A4: (dwdmgrass, 1), 0x0DEBA2: (dwdmgrass, 1), 0x0DEBB0: (dwdmgrass, 1), + 0x0DE644: (dwdmclouds1, 2), 0x0DEB84: (dwdmclouds1, 2), 0x0DE648: (dwdmclouds1, 1), 0x0DEB88: (dwdmclouds1, 1), + 0x0DEBAE: (dwdmclouds2, 2), 0x0DE7B0: (dwdmclouds2, 2), 0x0DE7B4: (dwdmclouds2, 0), 0x0DEB78: (dwdmclouds2, 0), 0x0DEBB2: (dwdmclouds2, 0) + } + for address, (color, shade) in patches.items(): + set_color(rom, address, color, shade) + +def blackout_ow_palettes(rom): + rom.write_bytes(0xDE604, [0] * 0xC4) + for i in range(0xDE6C8, 0xDE86C, 70): + rom.write_bytes(i, [0] * 64) + rom.write_bytes(i+66, [0] * 4) + rom.write_bytes(0xDE86C, [0] * 0x348) + + for address in [0x067FB4, 0x067F94, 0x067FC6, 0x067FE6, 0x067FE1, 0x05FEA9, 0x05FEB3]: + rom.write_bytes(address, [0,0]) + +def default_uw_palettes(rom): + if not rom.orig_buffer: + return + rom.write_bytes(0xDD734, rom.orig_buffer[0xDD734:0xDE544]) + +def randomize_uw_palettes(rom): + for dungeon in range(20): + wall, pot, chest, floor1, floor2, floor3 = [[random.randint(60, 240) for _ in range(3)] for _ in range(6)] + + for i in range(5): + shade = 10 - (i * 2) + set_color(rom, 0x0DD734 + (0xB4 * dungeon) + (i * 2), wall, shade) + set_color(rom, 0x0DD770 + (0xB4 * dungeon) + (i * 2), wall, shade) + set_color(rom, 0x0DD744 + (0xB4 * dungeon) + (i * 2), wall, shade) + if dungeon == 0: + set_color(rom, 0x0DD7CA + (0xB4 * dungeon) + (i * 2), wall, shade) + + if dungeon == 2: + set_color(rom, 0x0DD74E + (0xB4 * dungeon), wall, 3) + set_color(rom, 0x0DD750 + (0xB4 * dungeon), wall, 5) + set_color(rom, 0x0DD73E + (0xB4 * dungeon), wall, 3) + set_color(rom, 0x0DD740 + (0xB4 * dungeon), wall, 5) + + set_color(rom, 0x0DD7E4 + (0xB4 * dungeon), wall, 4) + set_color(rom, 0x0DD7E6 + (0xB4 * dungeon), wall, 2) + + set_color(rom, 0xDD7DA + (0xB4 * dungeon), wall, 10) + set_color(rom, 0xDD7DC + (0xB4 * dungeon), wall, 8) + + set_color(rom, 0x0DD75A + (0xB4 * dungeon), pot, 7) + set_color(rom, 0x0DD75C + (0xB4 * dungeon), pot, 1) + set_color(rom, 0x0DD75E + (0xB4 * dungeon), pot, 3) + + set_color(rom, 0x0DD76A + (0xB4 * dungeon), wall, 7) + set_color(rom, 0x0DD76C + (0xB4 * dungeon), wall, 2) + set_color(rom, 0x0DD76E + (0xB4 * dungeon), wall, 4) + + set_color(rom, 0x0DD7AE + (0xB4 * dungeon), chest, 2) + set_color(rom, 0x0DD7B0 + (0xB4 * dungeon), chest, 0) + + for i in range(3): + shade = 6 - (i * 2) + set_color(rom, 0x0DD764 + (0xB4 * dungeon) + (i * 2), floor1, shade) + set_color(rom, 0x0DD782 + (0xB4 * dungeon) + (i * 2), floor1, shade + 3) + + set_color(rom, 0x0DD7A0 + (0xB4 * dungeon) + (i * 2), floor2, shade) + set_color(rom, 0x0DD7BE + (0xB4 * dungeon) + (i * 2), floor2, shade + 3) + + set_color(rom, 0x0DD7E2 + (0xB4 * dungeon), floor3, 3) + set_color(rom, 0x0DD796 + (0xB4 * dungeon), floor3, 4) + +def blackout_uw_palettes(rom): + for i in range(0xDD734, 0xDE544, 180): + rom.write_bytes(i, [0] * 38) + rom.write_bytes(i+44, [0] * 76) + rom.write_bytes(i+136, [0] * 44) def write_string_to_rom(rom, target, string): address, maxbytes = text_addresses[target]