Ejemplo n.º 1
0
    def __init__(self, options, *, seed=None):
        self.seed = seed
        if self.seed is None:
            self.seed = os.urandom(16)
        self.rnd = random.Random(self.seed)
        if options.race:
            self.rnd.random()  # Just pull 1 random number so race seeds are different then from normal seeds but still stable.
        if options.multiworld:
            self.__logic = logic.MultiworldLogic(options, self.rnd)
        else:
            self.__logic = logic.Logic(options, self.rnd)

        if not options.keysanity or options.forwardfactor:
            item_placer = ForwardItemPlacer(self.__logic, options.forwardfactor)
        else:
            item_placer = RandomItemPlacer(self.__logic)

        if options.plan:
            assert options.multiworld is None
            self.readPlan(options.plan)

        for item, count in self.readItemPool(options, item_placer).items():
            if count > 0:
                item_placer.addItem(item, count)
        item_placer.run(self.rnd)

        if options.goal == "random":
            options.goal = self.rnd.randint(-1, 8)

        if options.multiworld:
            z = None
            if options.output_filename is not None:
                z = zipfile.ZipFile(options.output_filename, "w")
            for n in range(options.multiworld):
                rom = generator.generateRom(options.multiworld_options[n], self.seed, self.__logic, multiworld=n)
                fname = "LADXR_Multiworld_%d_%d.gbc" % (options.multiworld, n + 1)
                if z:
                    handle = z.open(fname, "w")
                    rom.save(handle, name="LADXR")
                    handle.close()
                else:
                    rom.save(fname, name="LADXR")
                if options.spoilerformat != "none" and not options.race:
                    extension = "json" if options.spoilerformat == "json" else "txt"
                    sfname = "LADXR_Multiworld_%d_%d.%s" % (options.multiworld, n + 1, extension)
                    log = spoilerLog.SpoilerLog(options, rom)
                    log.output(sfname, z)
        else:
            rom = generator.generateRom(options, self.seed, self.__logic)
            filename = options.output_filename
            if filename is None:
                filename = "LADXR_%s.gbc" % (binascii.hexlify(self.seed).decode("ascii").upper())
            rom.save(filename, name="LADXR")

            if options.spoilerformat != "none" and not options.race:
                log = spoilerLog.SpoilerLog(options, rom)
                log.output(options.spoiler_filename)
Ejemplo n.º 2
0
def main(mainargs=None):
    import argparse
    import sys

    parser = argparse.ArgumentParser(description='Randomize!')
    parser.add_argument('input_filename',
                        metavar='input rom',
                        type=str,
                        help="Rom file to use as input.")
    parser.add_argument(
        '-o',
        '--output',
        dest="output_filename",
        metavar='output rom',
        type=str,
        required=False,
        help="Output filename to use. If not specified [seed].gbc is used.")
    parser.add_argument('--dump',
                        dest="dump",
                        type=str,
                        nargs="*",
                        help="Dump the logic of the given rom (spoilers!)")
    parser.add_argument(
        '--spoilerformat',
        dest="spoilerformat",
        choices=["none", "console", "text", "json"],
        default="none",
        help="Sets the output format for the generated seed's spoiler log")
    parser.add_argument(
        '--spoilerfilename',
        dest="spoiler_filename",
        type=str,
        required=False,
        help=
        "Output filename to use for the spoiler log.  If not specified, LADXR_[seed].txt/json is used."
    )
    parser.add_argument(
        '--test',
        dest="test",
        action="store_true",
        help="Test the logic of the given rom, without showing anything.")
    parser.add_argument('-s',
                        '--seed',
                        dest="seed",
                        type=str,
                        required=False,
                        help="Generate the specified seed")
    parser.add_argument(
        '--romdebugmode',
        dest="romdebugmode",
        action="store_true",
        help=
        "Patch the rom so that debug mode is enabled, this creates a default save with most items and unlocks some debug features."
    )
    parser.add_argument('--exportmap',
                        dest="exportmap",
                        action="store_true",
                        help="Export the map (many graphical mistakes)")
    parser.add_argument('--emptyplan',
                        dest="emptyplan",
                        type=str,
                        required=False,
                        help="Write an unfilled plan file")
    parser.add_argument(
        '--timeout',
        type=float,
        required=False,
        help="Timeout generating the seed after the specified number of seconds"
    )
    parser.add_argument(
        '--logdirectory',
        dest="log_directory",
        type=str,
        required=False,
        help=
        "Directory to write the JSON log file. Generated independently from the spoiler log and omitted by default."
    )

    # Flags that effect gameplay
    parser.add_argument('--plan',
                        dest="plan",
                        metavar='plandomizer',
                        type=str,
                        required=False,
                        help="Read an item placement plan")
    parser.add_argument(
        '--race',
        dest="race",
        nargs="?",
        default=False,
        const=True,
        help=
        "Enable race mode. This generates a rom from which the spoiler log cannot be dumped and the seed cannot be extracted."
    )
    parser.add_argument(
        '--logic',
        dest="logic",
        choices=["casual", "normal", "hard", "glitched", "hell"],
        help="Which level of logic is required.")
    parser.add_argument('--multiworld',
                        dest="multiworld",
                        type=int,
                        required=False,
                        help="Generates multiple roms for a multiworld setup.")
    parser.add_argument(
        '--multiworld-config',
        dest="multiworld_config",
        action="append",
        required=False,
        help=
        "Set configuration for a multiworld player, supply multiple times for settings per player"
    )
    parser.add_argument(
        '--forwardfactor',
        dest="forwardfactor",
        type=float,
        required=False,
        help=
        "Forward item weight adjustment factor, lower values generate more rear heavy seeds while higher values generate front heavy seeds. Default is 0.5."
    )
    parser.add_argument('--heartpiece',
                        dest="heartpiece",
                        action="store_true",
                        help="Enables randomization of heart pieces.")
    parser.add_argument(
        '--seashells',
        dest="seashells",
        action="store_true",
        help=
        "Enables seashells mode, which randomizes the secret sea shells hiding in the ground/trees. (chest are always randomized)"
    )
    parser.add_argument(
        '--heartcontainers',
        dest="heartcontainers",
        action="store_true",
        help=
        "Enables heartcontainer mode, which randomizes the heart containers dropped by bosses."
    )
    parser.add_argument('--instruments',
                        dest="instruments",
                        action="store_true",
                        help="Shuffle the instruments in the item pool.")
    parser.add_argument(
        '--owlstatues',
        dest="owlstatues",
        choices=['none', 'dungeon', 'overworld', 'both'],
        default='none',
        help=
        "Give the owl statues in dungeons or on the overworld items as well, instead of showing the normal hints"
    )
    parser.add_argument(
        '--dungeon-items',
        dest="dungeon_items",
        choices=['standard', 'localkeys', 'localnightmarekey', 'keysanity'],
        default='standard',
        help=
        "Sets what gets done with dungeon items, if they are in their own dungeon or not."
    )
    parser.add_argument('--randomstartlocation',
                        dest="randomstartlocation",
                        action="store_true",
                        help="Place your starting house at a random location.")
    parser.add_argument(
        '--dungeonshuffle',
        dest="dungeonshuffle",
        action="store_true",
        help="Enable dungeon shuffle, puts dungeons on different spots.")
    parser.add_argument(
        '--entranceshuffle',
        dest="entranceshuffle",
        choices=["none", "simple", "advanced", "expert", "insanity"],
        default="none",
        help="Enable entrance shuffle, shuffles around overworld entrances.")
    parser.add_argument(
        '--boss',
        dest="boss",
        choices=["default", "shuffle", "random"],
        default="default",
        help="Enable boss shuffle, swaps around dungeon bosses.")
    parser.add_argument('--miniboss',
                        dest="miniboss",
                        choices=["default", "shuffle", "random"],
                        default="default",
                        help="Shuffle the minibosses or just randomize them.")
    parser.add_argument('--doubletrouble',
                        dest="doubletrouble",
                        action="store_true",
                        help="...")
    parser.add_argument('--witch',
                        dest="witch",
                        action="store_true",
                        help="Enables witch and toadstool in the item pool.")
    parser.add_argument(
        '--hpmode',
        dest="hpmode",
        choices=['default', 'inverted', '1'],
        default='default',
        help=
        "Set the HP gamplay mode. Inverted causes health containers to take HP instead of give it and you start with more health. 1 sets your starting health to just 1 hearth."
    )
    parser.add_argument(
        '--boomerang',
        dest="boomerang",
        choices=['default', 'trade', 'gift'],
        default='default',
        help=
        "Put the boomerang and the trade with the boomerang in the item pool")
    parser.add_argument('--steal',
                        dest="steal",
                        choices=['never', 'always', 'default'],
                        default='always',
                        help="Configure when to allow stealing from the shop.")
    parser.add_argument(
        '--hard-mode',
        dest="hardMode",
        action="store_true",
        help=
        "Make the game a bit harder, less health from drops, bombs damage yourself, and less iframes."
    )
    parser.add_argument(
        '--goal',
        dest="goal",
        type=goal,
        default='8',
        help=
        "Configure the instrument goal for this rom: any number between -1 (open egg) and 8, a range (e.g. 4-7), 'random', or 'raft' / 'seashells' for special goals."
    )
    parser.add_argument(
        '--accessibility',
        dest="accessibility_rule",
        choices=['all', 'goal'],
        help=
        "Switches between making sure all locations are reachable or only the goal is reachable"
    )
    parser.add_argument(
        '--bowwow',
        dest="bowwow",
        choices=['normal', 'always', 'swordless'],
        default='normal',
        help=
        "Enables 'good boy mode', where BowWow is allowed on all screens and can damage bosses and more enemies."
    )
    parser.add_argument(
        '--pool',
        dest="itempool",
        choices=['normal', 'casual', 'pain', 'keyup'],
        default='normal',
        help="Sets up different item pools, for easier or harder gameplay.")
    parser.add_argument(
        '--overworld',
        dest="overworld",
        choices=['normal', 'dungeondive'],
        default='normal',
        help=
        "Allows switching to the dungeondive overworld, where there are only dungeons."
    )
    parser.add_argument('--pymod',
                        dest="pymod",
                        action='append',
                        help="Load python code mods.")

    # Just aestetic flags
    parser.add_argument('--gfxmod',
                        dest="gfxmod",
                        action='append',
                        help="Load graphical mods.")
    parser.add_argument(
        '--remove-flashing-lights',
        dest="removeFlashingLights",
        action="store_true",
        help=
        "Remove the flashing light effects from mamu, the shopkeeper and madbatter."
    )
    parser.add_argument(
        '--quickswap',
        dest="quickswap",
        choices=['none', 'a', 'b'],
        default='none',
        help=
        "Configure quickswap for A or B button (select key swaps, no longer opens map)"
    )
    parser.add_argument(
        '--textmode',
        dest="textmode",
        choices=['default', 'fast', 'none'],
        default='default',
        help=
        "Default just keeps text normal, fast makes text appear twice as fast, and none removes all text from the game."
    )
    parser.add_argument(
        '--nag-messages',
        dest="removeNagMessages",
        action="store_false",
        help=
        "Enable the nag messages on touching stones and crystals. By default they are removed."
    )
    parser.add_argument('--lowhpbeep',
                        dest="lowhpbeep",
                        choices=['default', 'slow', 'none'],
                        default='slow',
                        help="Slows or disables the low health beeping sound")
    parser.add_argument('--linkspalette',
                        dest="linkspalette",
                        type=int,
                        default=None,
                        help="Force the palette of link")
    parser.add_argument('--music',
                        dest="music",
                        choices=['default', 'random', 'off'],
                        default='default',
                        help="Randomizes or disable the music")

    args = parser.parse_args(mainargs)
    if args.multiworld is not None:
        args.multiworld_options = [args] * args.multiworld
        if args.multiworld_config is not None:
            for index, settings_string in enumerate(args.multiworld_config):
                args.multiworld_options[index] = parser.parse_args(
                    [args.input_filename] + shlex.split(settings_string),
                    namespace=argparse.Namespace(**vars(args)))

    if args.timeout is not None:
        import threading
        import time
        import os

        def timeoutFunction():
            time.sleep(args.timeout)
            print("TIMEOUT")
            sys.stdout.flush()
            os._exit(1)

        threading.Thread(target=timeoutFunction, daemon=True).start()

    if args.exportmap:
        import mapexport
        print("Loading: %s" % (args.input_filename))
        rom = ROMWithTables(args.input_filename)
        mapexport.MapExport(rom)
        sys.exit(0)

    if args.emptyplan:
        import locations.items
        f = open(args.emptyplan, "wt")
        f.write(";Plandomizer data\n;Items: %s\n" % (", ".join(
            map(lambda n: getattr(locations.items, n),
                filter(lambda n: not n.startswith("__"), dir(
                    locations.items))))))
        f.write(";Modify the item pool:\n")
        f.write(";Pool:SWORD:+5\n")
        f.write(";Pool:RUPEES_50:-5\n")
        import worldSetup
        iteminfo_list = logic.Logic(
            args, world_setup=worldSetup.WorldSetup()).iteminfo_list
        for ii in sorted(iteminfo_list,
                         key=lambda n:
                         (n.location.dungeon
                          if n.location.dungeon else -1, repr(n.metadata))):
            if len(ii.OPTIONS) > 1:
                f.write(";%r\n" % (ii.metadata))
                f.write("Location:%s: \n" % (ii.nameId))
        sys.exit(0)

    if args.dump is not None or args.test:
        print("Loading: %s" % (args.input_filename))
        roms = [ROMWithTables(f) for f in [args.input_filename] + args.dump]

        if args.spoilerformat == "none":
            args.spoilerformat = "console"

        try:
            log = spoilerLog.SpoilerLog(args, roms)
            log.output(args.spoiler_filename)
            sys.exit(0)
        except spoilerLog.RaceRomException:
            print("Cannot read spoiler log for race rom")
            sys.exit(1)

    if args.seed:
        try:
            args.seed = binascii.unhexlify(args.seed)
        except binascii.Error:
            args.seed = args.seed.encode("ascii")

    retry_count = 0
    while True:
        try:
            r = randomizer.Randomizer(args, seed=args.seed)
            seed = binascii.hexlify(r.seed).decode("ascii").upper()
            break
        except randomizer.Error:
            if args.seed is not None:
                print("Specified seed does not produce a valid result.")
                sys.exit(1)
            retry_count += 1
            if retry_count > 100:
                print("Randomization keeps failing, abort!")
                sys.exit(1)
            print("Failed, trying again: %d" % (retry_count))

    print("Seed: %s" % (seed))
Ejemplo n.º 3
0
    def __init__(self, options, *, seed=None):
        self.seed = seed
        self.plan = None
        if self.seed is None:
            self.seed = os.urandom(16)
        self.rnd = random.Random(self.seed)
        if options.race:
            self.rnd.random(
            )  # Just pull 1 random number so race seeds are different then from normal seeds but still stable.
        if isinstance(options.goal, range):
            options.goal = self.rnd.randint(min(options.goal),
                                            max(options.goal))
        if options.plan:
            assert options.multiworld is None
            self.plan = Plan(options.plan)

        if options.multiworld:
            self.__logic = logic.MultiworldLogic(options, self.rnd)
        else:
            for n in range(
                    1000
            ):  # Try the world setup in case entrance randomization generates unsolvable logic
                world_setup = WorldSetup()
                world_setup.randomize(options, self.rnd)
                self.__logic = logic.Logic(options, world_setup=world_setup)
                if options.entranceshuffle not in (
                        "advanced", "expert", "insanity") or len(
                            self.__logic.iteminfo_list) == sum(
                                itempool.ItemPool(options,
                                                  self.rnd).toDict().values()):
                    break

        if self.plan:
            for ii in self.__logic.iteminfo_list:
                item = self.plan.forced_items.get(ii.nameId.upper(), None)
                if isinstance(item, list):
                    ii.OPTIONS = item
                else:
                    ii.forced_item = item

        if options.multiworld:
            item_placer = MultiworldItemPlacer(
                self.__logic,
                options.forwardfactor if options.forwardfactor else 0.5,
                options.accessibility_rule, options.multiworld)
        elif options.dungeon_items in ('standard',
                                       'localkeys') or options.forwardfactor:
            item_placer = ForwardItemPlacer(self.__logic,
                                            options.forwardfactor,
                                            options.accessibility_rule)
        else:
            item_placer = RandomItemPlacer(self.__logic,
                                           options.accessibility_rule)
        for item, count in self.readItemPool(options, item_placer).items():
            if count > 0:
                item_placer.addItem(item, count)
        item_placer.run(self.rnd)

        if options.multiworld:
            z = None
            if options.output_filename is not None:
                z = zipfile.ZipFile(options.output_filename, "w")
                z.write(
                    os.path.join(os.path.dirname(__file__),
                                 "multiworld/bizhawkConnector.lua"),
                    "bizhawkConnector.lua")
                z.write(
                    os.path.join(os.path.dirname(__file__),
                                 "multiworld/socket.dll"), "socket.dll")
            roms = []
            for n in range(options.multiworld):
                rom = generator.generateRom(options.multiworld_options[n],
                                            self.seed,
                                            self.__logic,
                                            rnd=self.rnd,
                                            multiworld=n)
                fname = "LADXR_Multiworld_%d_%d.gbc" % (options.multiworld,
                                                        n + 1)
                if z:
                    handle = z.open(fname, "w")
                    rom.save(handle, name="LADXR")
                    handle.close()
                else:
                    rom.save(fname, name="LADXR")
                roms.append(rom)
            if (options.spoilerformat != "none"
                    or options.log_directory) and not options.race:
                log = spoilerLog.SpoilerLog(options, roms)
                if options.log_directory:
                    filename = "LADXR_Multiworld_%d_%s_%s.json" % (
                        options.multiworld,
                        datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), log.seed)
                    log_path = os.path.join(options.log_directory, filename)
                    log.outputJson(log_path)
                if options.spoilerformat != "none":
                    extension = "json" if options.spoilerformat == "json" else "txt"
                    sfname = "LADXR_Multiworld_%d.%s" % (options.multiworld,
                                                         extension)
                    log.output(sfname, z)
        else:
            rom = generator.generateRom(options,
                                        self.seed,
                                        self.__logic,
                                        rnd=self.rnd)
            filename = options.output_filename
            if filename is None:
                filename = "LADXR_%s.gbc" % (binascii.hexlify(
                    self.seed).decode("ascii").upper())
            rom.save(filename, name="LADXR")

            if (options.spoilerformat != "none"
                    or options.log_directory) and not options.race:
                log = spoilerLog.SpoilerLog(options, [rom])
                if options.log_directory:
                    filename = "LADXR_%s_%s.json" % (
                        datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), log.seed)
                    log_path = os.path.join(options.log_directory, filename)
                    log.outputJson(log_path)
                if options.spoilerformat != "none":
                    log.output(options.spoiler_filename)
Ejemplo n.º 4
0
def main(mainargs=None):
    import argparse
    import sys

    parser = argparse.ArgumentParser(description='Randomize!')
    parser.add_argument('input_filename', metavar='input rom', type=str,
        help="Rom file to use as input.")
    parser.add_argument('-o', '--output', dest="output_filename", metavar='output rom', type=str, required=False,
        help="Output filename to use. If not specified [seed].gbc is used.")
    parser.add_argument('--dump', dest="dump", action="store_true",
        help="Dump the logic of the given rom (spoilers!)")
    parser.add_argument('--spoilerformat', dest="spoilerformat", choices=["none", "console", "text", "json"], default="none",
        help="Sets the output format for the generated seed's spoiler log")
    parser.add_argument('--spoilerfilename', dest="spoiler_filename", type=str, required=False,
        help="Output filename to use for the spoiler log.  If not specified, LADXR_[seed].txt/json is used.")
    parser.add_argument('--test', dest="test", action="store_true",
        help="Test the logic of the given rom, without showing anything.")
    parser.add_argument('-s', '--seed', dest="seed", type=str, required=False,
        help="Generate the specified seed")
    parser.add_argument('--romdebugmode', dest="romdebugmode", action="store_true",
        help="Patch the rom so that debug mode is enabled, this creates a default save with most items and unlocks some debug features.")
    parser.add_argument('--exportmap', dest="exportmap", action="store_true",
        help="Export the map (many graphical mistakes)")
    parser.add_argument('--emptyplan', dest="emptyplan", type=str, required=False,
        help="Write an unfilled plan file")
    parser.add_argument('--timeout', type=float, required=False,
        help="Timeout generating the seed after the specified number of seconds")

    # Flags that effect gameplay
    parser.add_argument('--plan', dest="plan", metavar='plandomizer', type=str, required=False,
        help="Read an item placement plan")
    parser.add_argument('--race', dest="race", action="store_true",
        help="Enable race mode. This generates a rom from which the spoiler log cannot be dumped and the seed cannot be extracted.")
    parser.add_argument('--logic', dest="logic", choices=["normal", "hard", "glitched", "hell"],
        help="Which level of logic is required.")
    parser.add_argument('--multiworld', dest="multiworld", type=int, required=False,
        help="Generates multiple roms for a multiworld setup.")
    parser.add_argument('--multiworld-config', dest="multiworld_config", action="append", required=False,
        help="Set configuration for a multiworld player, supply multiple times for settings per player")
    parser.add_argument('--forwardfactor', dest="forwardfactor", type=float, required=False,
        help="Forward item weight adjustment factor, lower values generate more rear heavy seeds while higher values generate front heavy seeds. Default is 0.5.")
    parser.add_argument('--heartpiece', dest="heartpiece", action="store_true",
        help="Enables randomization of heart pieces.")
    parser.add_argument('--seashells', dest="seashells", action="store_true",
        help="Enables seashells mode, which randomizes the secret sea shells hiding in the ground/trees. (chest are always randomized)")
    parser.add_argument('--heartcontainers', dest="heartcontainers", action="store_true",
        help="Enables heartcontainer mode, which randomizes the heart containers dropped by bosses.")
    parser.add_argument('--owlstatues', dest="owlstatues", choices=['none', 'dungeon', 'overworld', 'both'],
        help="Give the owl statues in dungeons or on the overworld items as well, instead of showing the normal hints")
    parser.add_argument('--keysanity', dest="keysanity", action="store_true",
        help="Enables keysanity mode, which shuffles all dungeon items outside dungeons as well.")
    parser.add_argument('--dungeonshuffle', dest="dungeonshuffle", action="store_true",
        help="Enable dungeon shuffle, puts dungeons on different spots.")
    parser.add_argument('--bossshuffle', dest="bossshuffle", action="store_true",
        help="Enable boss shuffle, swaps around dungeon bosses.")
    parser.add_argument('--witch', dest="witch", action="store_true",
        help="Enables witch and toadstool in the item pool.")
    parser.add_argument('--hpmode', dest="hpmode", choices=['default', 'inverted', '1'], default='default',
        help="Set the HP gamplay mode. Inverted causes health containers to take HP instead of give it and you start with more health. 1 sets your starting health to just 1 hearth.")
    parser.add_argument('--boomerang', dest="boomerang", choices=['default', 'trade', 'gift'], default='default',
        help="Put the boomerang and the trade with the boomerang in the item pool")
    parser.add_argument('--steal', dest="steal", choices=['never', 'always', 'default'], default='always',
        help="Configure when to allow stealing from the shop.")
    parser.add_argument('--hard-mode', dest="hardMode", action="store_true",
        help="Make the game a bit harder, less health from drops, bombs damage yourself, and less iframes.")
    parser.add_argument('--goal', dest="goal", choices=['-1', '0', '1', '2', '3', '4', '5', '6', '7', '8', 'random', 'raft'],
        help="Configure the instrument goal for this rom, anything between 0 and 8.")
    parser.add_argument('--bowwow', dest="bowwow", choices=['normal', 'always', 'swordless'], default='normal',
        help="Enables 'good boy mode', where BowWow is allowed on all screens and can damage bosses and more enemies.")
    parser.add_argument('--pool', dest="itempool", choices=['normal', 'casual', 'pain', 'keyup'], default='normal',
        help="Sets up different item pools, for easier or harder gameplay.")

    # Just aestetic flags
    parser.add_argument('--gfxmod', dest="gfxmod", action='append',
        help="Load graphical mods.")
    parser.add_argument('--quickswap', dest="quickswap", choices=['none', 'a', 'b'], default='none',
        help="Configure quickswap for A or B button (select key swaps, no longer opens map)")
    parser.add_argument('--textmode', dest="textmode", choices=['default', 'fast', 'none'], default='default',
        help="Default just keeps text normal, fast makes text appear twice as fast, and none removes all text from the game.")
    parser.add_argument('--nag-messages', dest="removeNagMessages", action="store_false",
        help="Enable the nag messages on touching stones and crystals. By default they are removed.")
    parser.add_argument('--lowhpbeep', dest="lowhpbeep", choices=['default', 'slow', 'none'], default='slow',
        help="Slows or disables the low health beeping sound")
    parser.add_argument('--linkspalette', dest="linkspalette", type=int, default=None,
        help="Force the palette of link")

    args = parser.parse_args(mainargs)
    if args.multiworld is not None:
        args.multiworld_options = [args] * args.multiworld
        if args.multiworld_config is not None:
            for index, settings_string in enumerate(args.multiworld_config):
                args.multiworld_options[index] = parser.parse_args([args.input_filename] + shlex.split(settings_string))

    if args.timeout is not None:
        import threading
        import time
        import os
        def timeoutFunction():
            time.sleep(args.timeout)
            print("TIMEOUT")
            sys.stdout.flush()
            os._exit(1)
        threading.Thread(target=timeoutFunction, daemon=True).start()

    if args.exportmap:
        import mapexport
        print("Loading: %s" % (args.input_filename))
        rom = ROMWithTables(args.input_filename)
        mapexport.MapExport(rom)
        sys.exit(0)

    if args.emptyplan:
        import checkMetadata
        import locations.items
        f = open(args.emptyplan, "wt")
        f.write(";Plandomizer data\n;Items: %s\n" % (", ".join(map(lambda n: getattr(locations.items, n), filter(lambda n: not n.startswith("__"), dir(locations.items))))))
        for key in dir(locations.items):
            f.write("")
        for name, data in sorted(checkMetadata.checkMetadataTable.items(), key=lambda n: str(n[1])):
            if name is not "None":
                f.write(";%s\n" % (data))
                f.write("%s: \n" % (name))
        sys.exit(0)

    if args.dump or args.test:
        print("Loading: %s" % (args.input_filename))
        rom = ROMWithTables(args.input_filename)

        if args.spoilerformat == "none":
            args.spoilerformat = "console"

        try:
            log = spoilerLog.SpoilerLog(args, rom)
            log.output(args.spoiler_filename)
            sys.exit(0)
        except spoilerLog.RaceRomException:
            print("Cannot read spoiler log for race rom")
            sys.exit(1)

    if args.seed:
        try:
            args.seed = binascii.unhexlify(args.seed)
        except binascii.Error:
            args.seed = args.seed.encode("ascii")

    retry_count = 0
    while True:
        try:
            r = randomizer.Randomizer(args, seed=args.seed)
            seed = binascii.hexlify(r.seed).decode("ascii").upper()
            break
        except randomizer.Error:
            if args.seed is not None:
                print("Specified seed does not produce a valid result.")
                sys.exit(1)
            retry_count += 1
            if retry_count > 100:
                print("Randomization keeps failing, abort!")
                sys.exit(1)
            print("Failed, trying again: %d" % (retry_count))

    print("Seed: %s" % (seed))
Ejemplo n.º 5
0
    def __init__(self, options, *, seed=None):
        self.seed = seed
        self.plan = None
        if self.seed is None:
            self.seed = os.urandom(16)
        self.rnd = random.Random(self.seed)
        if options.race:
            self.rnd.random(
            )  # Just pull 1 random number so race seeds are different then from normal seeds but still stable.
        if options.goal == "random":
            options.goal = self.rnd.randint(-1, 8)
        if options.plan:
            assert options.multiworld is None
            self.plan = Plan(options.plan)

        if options.multiworld:
            self.__logic = logic.MultiworldLogic(options, self.rnd)
        else:
            world_setup = WorldSetup()
            world_setup.randomize(options, self.rnd)
            self.__logic = logic.Logic(options, world_setup=world_setup)

        if self.plan:
            for ii in self.__logic.iteminfo_list:
                item = self.plan.forced_items.get(ii.nameId.upper(), None)
                if isinstance(item, list):
                    ii.OPTIONS = item
                else:
                    ii.forced_item = item

        if not options.keysanity or options.forwardfactor:
            item_placer = ForwardItemPlacer(self.__logic,
                                            options.forwardfactor,
                                            options.accessibility_rule)
        else:
            item_placer = RandomItemPlacer(self.__logic,
                                           options.accessibility_rule)
        for item, count in self.readItemPool(options, item_placer).items():
            if count > 0:
                item_placer.addItem(item, count)
        item_placer.run(self.rnd)

        if options.multiworld:
            z = None
            if options.output_filename is not None:
                z = zipfile.ZipFile(options.output_filename, "w")
            for n in range(options.multiworld):
                rom = generator.generateRom(options.multiworld_options[n],
                                            self.seed,
                                            self.__logic,
                                            multiworld=n)
                fname = "LADXR_Multiworld_%d_%d.gbc" % (options.multiworld,
                                                        n + 1)
                if z:
                    handle = z.open(fname, "w")
                    rom.save(handle, name="LADXR")
                    handle.close()
                else:
                    rom.save(fname, name="LADXR")
                if (options.spoilerformat != "none"
                        or options.log_directory) and not options.race:
                    log = spoilerLog.SpoilerLog(options, rom)
                    if options.log_directory:
                        filename = "LADXR_Multiworld_%d_%d_%s_%s.json" % (
                            options.multiworld, n + 1,
                            datetime.now().strftime("%Y-%m-%d_%H-%M-%S"),
                            log.seed)
                        log_path = os.path.join(options.log_directory,
                                                filename)
                        log.outputJson(log_path)
                    if options.spoilerformat != "none":
                        extension = "json" if options.spoilerformat == "json" else "txt"
                        sfname = "LADXR_Multiworld_%d_%d.%s" % (
                            options.multiworld, n + 1, extension)
                        log.output(sfname, z)
        else:
            rom = generator.generateRom(options, self.seed, self.__logic)
            filename = options.output_filename
            if filename is None:
                filename = "LADXR_%s.gbc" % (binascii.hexlify(
                    self.seed).decode("ascii").upper())
            rom.save(filename, name="LADXR")

            if (options.spoilerformat != "none"
                    or options.log_directory) and not options.race:
                log = spoilerLog.SpoilerLog(options, rom)
                if options.log_directory:
                    filename = "LADXR_%s_%s.json" % (
                        datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), log.seed)
                    log_path = os.path.join(options.log_directory, filename)
                    log.outputJson(log_path)
                if options.spoilerformat != "none":
                    log.output(options.spoiler_filename)