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
Example #2
0
 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()
Example #3
0
    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()
Example #4
0
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)
Example #5
0
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)
Example #6
0
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)
Example #7
0
 def use_default_link_sprite(self):
     self.callback(Sprite.default_link_sprite(), False)
     self.window.destroy()
Example #8
0
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
Example #10
0
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
Example #11
0
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)