def adjust(args): start = time.clock() logger = logging.getLogger('') logger.info('Patching ROM.') if args.sprite is not None: if isinstance(args.sprite, Sprite): sprite = args.sprite else: sprite = Sprite(args.sprite) else: sprite = None outfilebase = os.path.basename(args.rom)[:-4] + '_adjusted' if os.stat(args.rom).st_size == 2097152 and os.path.splitext( args.rom)[-1].lower() == '.sfc': rom = LocalRom(args.rom, False) 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) rom.write_to_file(output_path('%s.sfc' % outfilebase)) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start) return args
def browse_for_sprite(self): sprite = filedialog.askopenfilename(filetypes=[("All Sprite Sources", ( ".zspr", ".spr", ".sfc", ".smc")), ("ZSprite files", ".zspr"), ("Sprite files", ".spr"), ("Rom Files", (".sfc", ".smc")), ("All Files", "*")]) try: self.callback(Sprite(sprite)) except Exception: self.callback(None) self.window.destroy()
def icon_section(self, frame_label, path, no_results_label): frame = LabelFrame(self.window, labelwidget=frame_label, padx=5, pady=5) canvas = Canvas(frame, borderwidth=0) y_scrollbar = Scrollbar(frame, orient="vertical", command=canvas.yview) y_scrollbar.pack(side="right", fill="y") content_frame = Frame(canvas) canvas.pack(side="left", fill="both", expand=True) canvas.create_window((4, 4), window=content_frame, anchor="nw") canvas.configure(yscrollcommand=y_scrollbar.set) def onFrameConfigure(canvas): """Reset the scroll region to encompass the inner frame""" canvas.configure(scrollregion=canvas.bbox("all")) content_frame.bind( "<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas)) frame.pack(side=TOP, fill=X) sprites = [] for file in glob(output_path(path)): sprites.append(Sprite(file)) sprites.sort(key=lambda s: str.lower(s.name or "").strip()) i = 0 for sprite in sprites: image = get_image_for_sprite(sprite) if image is None: continue self.all_sprites.append(sprite) button = Button(content_frame, image=image, command=lambda spr=sprite: self.select_sprite(spr)) ToolTips.register( button, sprite.name + ("\nBy: %s" % sprite.author_name if sprite.author_name else "")) button.image = image button.grid(row=i // 16, column=i % 16) i += 1 if i == 0: label = Label(content_frame, text=no_results_label) label.pack()
def start(): args = parse_arguments(None) if is_bundled() and len(sys.argv) == 1: # for the bundled builds, if we have no arguments, the user # probably wants the gui. Users of the bundled build who want the command line # interface shouuld specify at least one option, possibly setting a value to a # default if they like all the defaults from Gui import guiMain close_console() guiMain() sys.exit(0) # 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) sys.exit(1) if any([ sprite is not None and not os.path.isfile(sprite) and not Sprite.get_sprite_from_name(sprite) for sprite in args.sprite.values() ]): input( 'Could not find link sprite sheet at given location. \nPress Enter to exit.' ) sys.exit(1) # set up logger loglevel = { 'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG }[args.loglevel] logging.basicConfig(format='%(message)s', level=loglevel) if args.gui: from Gui import guiMain guiMain(args) elif args.count is not None: seed = args.seed for _ in range(args.count): main(seed=seed, args=args) seed = get_seed() else: main(seed=args.seed, args=args)
def main(args=None, callback=ERmain): if not args: args = mystery_argparse() seed = get_seed(args.seed) random.seed(seed) seedname = "M" + ( f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits)) print( f"Generating mystery for {args.multi} player{'s' if args.multi > 1 else ''}, {seedname} Seed {seed}" ) if args.race: random.seed() # reset to time-based random source weights_cache = {} if args.weights: try: weights_cache[args.weights] = get_weights(args.weights) except Exception as e: raise ValueError( f"File {args.weights} is destroyed. Please fix your yaml." ) from e print( f"Weights: {args.weights} >> " f"{get_choice('description', weights_cache[args.weights], 'No description specified')}" ) if args.meta: try: weights_cache[args.meta] = get_weights(args.meta) except Exception as e: raise ValueError( f"File {args.meta} is destroyed. Please fix your yaml.") from e meta_weights = weights_cache[args.meta] print( f"Meta: {args.meta} >> {get_choice('meta_description', meta_weights, 'No description specified')}" ) if args.samesettings: raise Exception("Cannot mix --samesettings with --meta") for player in range(1, args.multi + 1): path = getattr(args, f'p{player}') if path: try: if path not in weights_cache: weights_cache[path] = get_weights(path) print( f"P{player} Weights: {path} >> " f"{get_choice('description', weights_cache[path], 'No description specified')}" ) except Exception as e: raise ValueError( f"File {path} is destroyed. Please fix your yaml.") from e erargs = parse_arguments(['--multi', str(args.multi)]) erargs.seed = seed erargs.name = {x: "" for x in range(1, args.multi + 1) } # only so it can be overwrittin in mystery erargs.create_spoiler = args.create_spoiler erargs.create_diff = args.create_diff erargs.glitch_triforce = args.glitch_triforce erargs.race = args.race erargs.skip_playthrough = args.skip_playthrough erargs.outputname = seedname erargs.outputpath = args.outputpath erargs.teams = args.teams erargs.progression_balancing = {} # set up logger if args.loglevel: erargs.loglevel = args.loglevel loglevel = { 'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG }[erargs.loglevel] if args.log_output_path: import sys class LoggerWriter(object): def __init__(self, writer): self._writer = writer self._msg = '' def write(self, message): self._msg = self._msg + message while '\n' in self._msg: pos = self._msg.find('\n') self._writer(self._msg[:pos]) self._msg = self._msg[pos + 1:] def flush(self): if self._msg != '': self._writer(self._msg) self._msg = '' log = logging.getLogger("stderr") log.addHandler(logging.StreamHandler()) sys.stderr = LoggerWriter(log.error) os.makedirs(args.log_output_path, exist_ok=True) logging.basicConfig(format='%(message)s', level=loglevel, filename=os.path.join(args.log_output_path, f"{seed}.log")) else: logging.basicConfig(format='%(message)s', level=loglevel) if args.rom: erargs.rom = args.rom if args.enemizercli: erargs.enemizercli = args.enemizercli settings_cache = { k: (roll_settings(v, args.plando) if args.samesettings else None) for k, v in weights_cache.items() } player_path_cache = {} for player in range(1, args.multi + 1): player_path_cache[player] = getattr(args, f'p{player}') if getattr( args, f'p{player}') else args.weights if args.meta: for player, path in player_path_cache.items(): weights_cache[path].setdefault("meta_ignore", []) meta_weights = weights_cache[args.meta] for key in meta_weights: option = get_choice(key, meta_weights) if option is not None: for player, path in player_path_cache.items(): players_meta = weights_cache[path].get("meta_ignore", []) if key not in players_meta: weights_cache[path][key] = option elif type(players_meta) == dict and players_meta[ key] and option not in players_meta[key]: weights_cache[path][key] = option name_counter = Counter() for player in range(1, args.multi + 1): path = player_path_cache[player] if path: try: settings = settings_cache[path] if settings_cache[path] else \ roll_settings(weights_cache[path], args.plando) if settings.sprite and not os.path.isfile( settings.sprite) and not Sprite.get_sprite_from_name( settings.sprite): logging.warning( f"Warning: The chosen sprite, \"{settings.sprite}\", for yaml \"{path}\", does not exist." ) if args.pre_roll: import yaml if path == args.weights: settings.name = f"Player{player}" elif not settings.name: settings.name = os.path.splitext( os.path.split(path)[-1])[0] if "-" not in settings.shuffle and settings.shuffle != "vanilla": settings.shuffle += f"-{random.randint(0, 2 ** 64)}" pre_rolled = dict() pre_rolled["original_seed_number"] = seed pre_rolled["original_seed_name"] = seedname pre_rolled["pre_rolled"] = vars(settings).copy() if "plando_items" in pre_rolled["pre_rolled"]: pre_rolled["pre_rolled"]["plando_items"] = [ item.to_dict() for item in pre_rolled["pre_rolled"] ["plando_items"] ] if "plando_connections" in pre_rolled["pre_rolled"]: pre_rolled["pre_rolled"]["plando_connections"] = [ connection.to_dict() for connection in pre_rolled["pre_rolled"]["plando_connections"] ] with open( os.path.join( args.outputpath if args.outputpath else ".", f"{os.path.split(path)[-1].split('.')[0]}_pre_rolled.yaml" ), "wt") as f: yaml.dump(pre_rolled, f) for k, v in vars(settings).items(): if v is not None: getattr(erargs, k)[player] = v except Exception as e: raise ValueError( f"File {path} is destroyed. Please fix your yaml.") from e else: raise RuntimeError(f'No weights specified for player {player}') if path == args.weights: # if name came from the weights file, just use base player name erargs.name[player] = f"Player{player}" elif not erargs.name[ player]: # if name was not specified, generate it from filename erargs.name[player] = os.path.splitext(os.path.split(path)[-1])[0] erargs.name[player] = handle_name(erargs.name[player], player, name_counter) logging.info(erargs.name[player]) erargs.names = ",".join(erargs.name[i] for i in range(1, args.multi + 1)) del (erargs.name) if args.yaml_output: import yaml important = {} for option, player_settings in vars(erargs).items(): if type(player_settings) == dict: if all( type(value) != list for value in player_settings.values()): if len(player_settings.values()) > 1: important[option] = { player: value for player, value in player_settings.items() if player <= args.yaml_output } elif len(player_settings.values()) > 0: important[option] = player_settings[1] else: logging.debug( f"No player settings defined for option '{option}'" ) else: if player_settings != "": # is not empty name important[option] = player_settings else: logging.debug( f"No player settings defined for option '{option}'") if args.outputpath: os.makedirs(args.outputpath, exist_ok=True) with open( os.path.join(args.outputpath if args.outputpath else ".", f"mystery_result_{seed}.yaml"), "wt") as f: yaml.dump(important, f) erargs.skip_progression_balancing = { player: not balanced for player, balanced in erargs.progression_balancing.items() } del (erargs.progression_balancing) callback(erargs, seed)
def main(): parser = argparse.ArgumentParser( formatter_class=ArgumentDefaultsHelpFormatter) parser.add_argument('--rom', default='ER_base.sfc', help='Path to an ALttP 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='''\ Select the rate at which the menu opens and closes. (default: %(default)s) ''') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['double', 'normal', 'half', 'quarter', 'off'], help='''\ Select the rate at which the heart beep sound is played at low health. (default: %(default)s) ''') 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', 'puke', 'classic', 'grayscale', 'negative', 'dizzy', 'sick' ]) parser.add_argument('--link_palettes', default='default', choices=[ 'default', 'random', 'blackout', 'puke', 'classic', 'grayscale', 'negative', 'dizzy', 'sick' ]) parser.add_argument('--shield_palettes', default='default', choices=[ 'default', 'random', 'blackout', 'puke', 'classic', 'grayscale', 'negative', 'dizzy', 'sick' ]) parser.add_argument('--sword_palettes', default='default', choices=[ 'default', 'random', 'blackout', 'puke', 'classic', 'grayscale', 'negative', 'dizzy', 'sick' ]) parser.add_argument('--hud_palettes', default='default', choices=[ 'default', 'random', 'blackout', 'puke', 'classic', 'grayscale', 'negative', 'dizzy', 'sick' ]) parser.add_argument('--uw_palettes', default='default', choices=[ 'default', 'random', 'blackout', 'puke', 'classic', 'grayscale', 'negative', 'dizzy', 'sick' ]) 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, or 0x7078 (28792) bytes including palette data. Alternatively, can be a ALttP Rom patched with a Link sprite that will be extracted. ''') parser.add_argument('--names', default='', type=str) args = parser.parse_args() # ToDo: Validate files further than mere existance if not os.path.isfile(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) and not Sprite.get_sprite_from_name(args.sprite): input( 'Could not find link sprite sheet at given location. \nPress Enter to exit.' ) sys.exit(1) # set up logger loglevel = { 'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG }[args.loglevel] logging.basicConfig(format='%(message)s', level=loglevel) args, path = adjust(args=args) from Utils import persistent_store persistent_store("adjuster", "last_settings_2", args)
def use_default_link_sprite(self): self.callback(Sprite.default_link_sprite(), False) self.window.destroy()
def main(args, seed=None): start = time.clock() # initialize the world world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray) logger = logging.getLogger('') if seed is None: random.seed(None) world.seed = random.randint(0, 999999999) else: world.seed = int(seed) random.seed(world.seed) logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed) world.difficulty_requirements = difficulties[world.difficulty] create_regions(world) create_dungeons(world) logger.info('Shuffling the World about.') link_entrances(world) mark_light_world_regions(world) logger.info('Calculating Access Rules.') set_rules(world) logger.info('Generating Item Pool.') generate_itempool(world) logger.info('Placing Dungeon Items.') shuffled_locations = None if args.algorithm in ['balanced', 'vt26'] or args.keysanity: shuffled_locations = world.get_unfilled_locations() random.shuffle(shuffled_locations) fill_dungeons_restrictive(world, shuffled_locations) else: fill_dungeons(world) logger.info('Fill the world.') if args.algorithm == 'flood': flood_items(world) # different algo, biased towards early game progress items elif args.algorithm == 'vt21': distribute_items_cutoff(world, 1) elif args.algorithm == 'vt22': distribute_items_cutoff(world, 0.66) elif args.algorithm == 'freshness': distribute_items_staleness(world) elif args.algorithm == 'vt25': distribute_items_restrictive(world, 0) elif args.algorithm == 'vt26': distribute_items_restrictive(world, gt_filler(world), shuffled_locations) elif args.algorithm == 'balanced': distribute_items_restrictive(world, gt_filler(world)) logger.info('Calculating playthrough.') create_playthrough(world) logger.info('Patching ROM.') if args.sprite is not None: if isinstance(args.sprite, Sprite): sprite = args.sprite else: sprite = Sprite(args.sprite) else: sprite = None outfilebase = 'ER_%s_%s-%s-%s%s_%s-%s%s%s%s_%s' % (world.logic, world.difficulty, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", world.seed) if not args.suppress_rom: if args.jsonout: rom = JsonRom() else: rom = LocalRom(args.rom) patch_rom(world, rom, bytearray(logic_hash), args.heartbeep, args.heartcolor, sprite) if args.jsonout: print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()})) else: rom.write_to_file(args.jsonout or output_path('%s.sfc' % outfilebase)) if args.create_spoiler and not args.jsonout: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start) return world
def main(args): start_time = time.perf_counter() # initialize the world world = World(1, 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, False, None, 'none', False) logger = logging.getLogger('') hasher = hashlib.md5() with open(args.plando, 'rb') as plandofile: buf = plandofile.read() hasher.update(buf) world.seed = int(hasher.hexdigest(), 16) % 1000000000 random.seed(world.seed) logger.info('ALttP Plandomizer Version %s - Seed: %s\n\n', __version__, args.plando) world.difficulty_requirements = difficulties[world.difficulty] create_regions(world, 1) create_dungeons(world, 1) link_entrances(world, 1) logger.info('Calculating Access Rules.') set_rules(world, 1) logger.info('Fill the world.') text_patches = [] fill_world(world, args.plando, text_patches) if world.get_entrance( 'Dam', 1).connected_region.name != 'Dam' or world.get_entrance( 'Swamp Palace', 1).connected_region.name != 'Swamp Palace (Entrance)': world.swamp_patch_required[1] = True logger.info('Calculating playthrough.') try: create_playthrough(world) except RuntimeError: if args.ignore_unsolvable: pass else: raise logger.info('Patching ROM.') if args.sprite is not None: sprite = Sprite(args.sprite) else: sprite = None rom = LocalRom(args.rom) patch_rom(world, 1, rom, args.heartbeep, args.heartcolor, sprite) for textname, texttype, text in text_patches: if texttype == 'text': write_string_to_rom(rom, textname, text) #elif texttype == 'credit': # write_credits_string_to_rom(rom, textname, text) outfilebase = 'Plando_%s_%s' % (os.path.splitext( os.path.basename(args.plando))[0], world.seed) rom.write_to_file('%s.sfc' % outfilebase) if args.create_spoiler: world.spoiler.to_file('%s_Spoiler.txt' % outfilebase) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.perf_counter() - start_time) return world
def main(args, seed=None): start = time.perf_counter() # initialize the world world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.accessibility, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints) logger = logging.getLogger('') if seed is None: random.seed(None) world.seed = random.randint(0, 999999999) else: world.seed = int(seed) random.seed(world.seed) world.crystals_needed_for_ganon = random.randint( 0, 7) if args.crystals_ganon == 'random' else int(args.crystals_ganon) world.crystals_needed_for_gt = random.randint( 0, 7) if args.crystals_gt == 'random' else int(args.crystals_gt) world.rom_seeds = { player: random.randint(0, 999999999) for player in range(1, world.players + 1) } logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed) world.difficulty_requirements = difficulties[world.difficulty] if world.mode != 'inverted': for player in range(1, world.players + 1): create_regions(world, player) create_dungeons(world, player) else: for player in range(1, world.players + 1): create_inverted_regions(world, player) create_dungeons(world, player) logger.info('Shuffling the World about.') if world.mode != 'inverted': for player in range(1, world.players + 1): link_entrances(world, player) mark_light_world_regions(world) else: for player in range(1, world.players + 1): link_inverted_entrances(world, player) mark_dark_world_regions(world) logger.info('Generating Item Pool.') for player in range(1, world.players + 1): generate_itempool(world, player) logger.info('Calculating Access Rules.') for player in range(1, world.players + 1): set_rules(world, player) logger.info('Placing Dungeon Prizes.') fill_prizes(world) logger.info('Placing Dungeon Items.') shuffled_locations = None if args.algorithm in ['balanced', 'vt26'] or args.keysanity: shuffled_locations = world.get_unfilled_locations() random.shuffle(shuffled_locations) fill_dungeons_restrictive(world, shuffled_locations) else: fill_dungeons(world) logger.info('Fill the world.') if args.algorithm == 'flood': flood_items( world) # different algo, biased towards early game progress items elif args.algorithm == 'vt21': distribute_items_cutoff(world, 1) elif args.algorithm == 'vt22': distribute_items_cutoff(world, 0.66) elif args.algorithm == 'freshness': distribute_items_staleness(world) elif args.algorithm == 'vt25': distribute_items_restrictive(world, 0) elif args.algorithm == 'vt26': distribute_items_restrictive(world, gt_filler(world), shuffled_locations) elif args.algorithm == 'balanced': distribute_items_restrictive(world, gt_filler(world)) if world.players > 1: logger.info('Balancing multiworld progression.') balance_multiworld_progression(world) logger.info('Patching ROM.') if args.sprite is not None: if isinstance(args.sprite, Sprite): sprite = args.sprite else: sprite = Sprite(args.sprite) else: sprite = None outfilebase = 'ER_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s_%s' % ( world.logic, world.difficulty, world.difficulty_adjustments, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-nohints" if not world.hints else "", world.seed) use_enemizer = args.enemizercli and ( args.shufflebosses != 'none' or args.shuffleenemies or args.enemy_health != 'default' or args.enemy_health != 'default' or args.enemy_damage or args.shufflepalette or args.shufflepots) jsonout = {} if not args.suppress_rom: if world.players > 1: raise NotImplementedError( "Multiworld rom writes have not been implemented") else: player = 1 local_rom = None if args.jsonout: rom = JsonRom() else: if use_enemizer: local_rom = LocalRom(args.rom) rom = JsonRom() else: rom = LocalRom(args.rom) patch_rom(world, player, rom) enemizer_patch = [] if use_enemizer: enemizer_patch = get_enemizer_patch( world, player, rom, args.rom, args.enemizercli, args.shuffleenemies, args.enemy_health, args.enemy_damage, args.shufflepalette, args.shufflepots) if args.jsonout: jsonout['patch'] = rom.patches if use_enemizer: jsonout['enemizer' % player] = enemizer_patch else: if use_enemizer: local_rom.patch_enemizer( rom.patches, os.path.join(os.path.dirname(args.enemizercli), "enemizerBasePatch.json"), enemizer_patch) rom = local_rom apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite) rom.write_to_file(output_path('%s.sfc' % outfilebase)) if args.create_spoiler and not args.jsonout: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) if not args.skip_playthrough: logger.info('Calculating playthrough.') create_playthrough(world) if args.jsonout: print(json.dumps({**jsonout, 'spoiler': world.spoiler.to_json()})) elif args.create_spoiler and not args.skip_playthrough: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.perf_counter() - start) return world
if not args.i or not args.o: print('Invalid arguments provided. -i and -o are required.') exit() # Target directories input_dir = args.i output_dir = args.o # Get a list of all files in the input directory targetFiles = [ file for file in listdir(input_dir) if isfile(join(input_dir, file)) ] spriteData = {} for file in targetFiles: if file[-5:] != '.zspr': continue sprite = Sprite(join(input_dir, file)) spriteData[sprite.name] = file image = open(f'{output_dir}/{sprite.name}.gif', 'wb') image.write(get_image_for_sprite(sprite, True)) image.close() jsonFile = open(f'{output_dir}/spriteData.json', 'w') jsonFile.write(json.dumps(spriteData)) jsonFile.close()
def main(): parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter) parser.add_argument('--rom', default='ER_base.sfc', help='Path to an ALttP 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='''\ Select the rate at which the menu opens and closes. (default: %(default)s) ''') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') parser.add_argument('--triforcehud', default='hide_goal', const='hide_goal', nargs='?', choices=['normal', 'hide_goal', 'hide_required', 'hide_both'], help='''\ Hide the triforce hud in certain circumstances. hide_goal will hide the hud until finding a triforce piece, hide_required will hide the total amount needed to win (Both can be revealed when speaking to Murahalda) (default: %(default)s) ''') parser.add_argument('--enableflashing', help='Reenable flashing animations (unfriendly to epilepsy, always disabled in race roms)', action='store_false', dest="reduceflashing") parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['double', 'normal', 'half', 'quarter', 'off'], help='''\ Select the rate at which the heart beep sound is played at low health. (default: %(default)s) ''') 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','puke','classic','grayscale','negative','dizzy','sick']) parser.add_argument('--link_palettes', default='default', choices=['default', 'random', 'blackout','puke','classic','grayscale','negative','dizzy','sick']) parser.add_argument('--shield_palettes', default='default', choices=['default', 'random', 'blackout','puke','classic','grayscale','negative','dizzy','sick']) parser.add_argument('--sword_palettes', default='default', choices=['default', 'random', 'blackout','puke','classic','grayscale','negative','dizzy','sick']) parser.add_argument('--hud_palettes', default='default', choices=['default', 'random', 'blackout','puke','classic','grayscale','negative','dizzy','sick']) parser.add_argument('--uw_palettes', default='default', choices=['default', 'random', 'blackout','puke','classic','grayscale','negative','dizzy','sick']) 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, or 0x7078 (28792) bytes including palette data. Alternatively, can be a ALttP Rom patched with a Link sprite that will be extracted. ''') parser.add_argument('--names', default='', type=str) args = parser.parse_args() # set up logger loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[ args.loglevel] logging.basicConfig(format='%(message)s', level=loglevel) if not os.path.isfile(args.rom): adjustGUI() else: if args.sprite is not None and not os.path.isfile(args.sprite) and not Sprite.get_sprite_from_name(args.sprite): input('Could not find link sprite sheet at given location. \nPress Enter to exit.') sys.exit(1) args, path = adjust(args=args) from Utils import persistent_store if isinstance(args.sprite, Sprite): args.sprite = args.sprite.name persistent_store("adjuster", "last_settings_3", args)