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(): 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 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('--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)