def get_settings_from_command_line_args():
    parser = argparse.ArgumentParser(
        formatter_class=ArgumentDefaultsHelpFormatter)

    parser.add_argument('--gui', help='Launch the GUI', action='store_true')
    parser.add_argument('--loglevel',
                        default='info',
                        const='info',
                        nargs='?',
                        choices=['error', 'info', 'warning', 'debug'],
                        help='Select level of logging for output.')
    parser.add_argument(
        '--settings_string',
        help=
        'Provide sharable settings using a settings string. This will override all flags that it specifies.'
    )
    parser.add_argument(
        '--convert_settings',
        help=
        'Only convert the specified settings to a settings string. If a settings string is specified output the used settings instead.',
        action='store_true')
    parser.add_argument(
        '--settings',
        help='Use the specified settings file to use for generation')
    parser.add_argument('--seed', help='Generate the specified seed.')

    args = parser.parse_args()

    if args.settings is None:
        settingsFile = local_path('settings.sav')
    else:
        settingsFile = local_path(args.settings)

    try:
        with open(settingsFile) as f:
            settings = Settings(json.load(f))
    except Exception as ex:
        if args.settings is None:
            settings = Settings({})
        else:
            raise ex

    if args.settings_string is not None:
        settings.update_with_settings_string(args.settings_string)

    if args.seed is not None:
        settings.update_seed(args.seed)

    if args.convert_settings:
        if args.settings_string is not None:
            print(settings.get_settings_display())
        else:
            print(settings.get_settings_string())
        sys.exit(0)

    return settings, args.gui, args.loglevel
Exemple #2
0
    def __init__(self, settings, patch=True):
        self.__last_address = None

        file = settings.rom
        decomp_file = 'ZOOTDEC.z64'

        os.chdir(local_path())

        with open(data_path('generated/symbols.json'), 'r') as stream:
            symbols = json.load(stream)
            self.symbols = {
                name: int(addr, 16)
                for name, addr in symbols.items()
            }

        if file == '':
            # if not specified, try to read from the previously decompressed rom
            file = decomp_file
            try:
                self.read_rom(file)
            except FileNotFoundError:
                # could not find the decompressed rom either
                raise FileNotFoundError('Must specify path to base ROM')
        else:
            self.read_rom(file)

        # decompress rom, or check if it's already decompressed
        self.decompress_rom_file(file, decomp_file)

        # Add file to maximum size
        self.buffer.extend(bytearray([0x00] * (0x4000000 - len(self.buffer))))
        self.original = copy.copy(self.buffer)
        self.changed_address = {}
        self.changed_dma = {}
        self.force_patch = []
Exemple #3
0
    def __init__(self, settings, patch=True):
        self.last_address = None

        file = settings.rom
        decomp_file = 'ZOOTDEC.z64'

        os.chdir(local_path())

        with open(data_path('symbols.json'), 'r') as stream:
            symbols = json.load(stream)
            self.symbols = {
                name: int(addr, 16)
                for name, addr in symbols.items()
            }

        try:
            # Read decompressed file if it exists
            self.read_rom(decomp_file)
            # This is mainly for validation testing, but just in case...
            self.decompress_rom_file(decomp_file, decomp_file)
        except Exception as ex:
            # No decompressed file, instead read Input ROM
            self.read_rom(file)
            self.decompress_rom_file(file, decomp_file)

        # Add file to maximum size
        self.buffer.extend(bytearray([0x00] * (0x4000000 - len(self.buffer))))
Exemple #4
0
def start():

    settings, gui, args_loglevel, no_log_file = get_settings_from_command_line_args(
    )

    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
        close_console()
        guiMain()
        sys.exit(0)

    # 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)

    logger = logging.getLogger('')

    if not no_log_file:
        ts = time.time()
        st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H-%M-%S')
        log_dir = local_path('Logs')
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)
        log_path = os.path.join(log_dir, '%s.log' % st)
        log_file = logging.FileHandler(log_path)
        log_file.setFormatter(
            logging.Formatter('[%(asctime)s] %(message)s', datefmt='%H:%M:%S'))
        logger.addHandler(log_file)

    if not settings.check_version:
        try:
            version_error = check_version(settings.checked_version)
        except VersionError as e:
            logger.warning(str(e))

    try:
        if gui:
            guiMain(settings)
        elif settings.cosmetics_only:
            cosmetic_patch(settings)
        elif settings.patch_file != '':
            from_patch_file(settings)
        elif settings.count != None and settings.count > 1:
            orig_seed = settings.seed
            for i in range(settings.count):
                settings.update_seed(orig_seed + '-' + str(i))
                main(settings)
        else:
            main(settings)
    except Exception as ex:
        logger.exception(ex)
 def save_presets():
     presets_file = local_path('presets.sav')
     with open(presets_file, 'w') as outfile:
         preset_json = {
             name: preset
             for name, preset in presets.items() if not preset.get('locked')
         }
         json.dump(preset_json, outfile, indent=4)
Exemple #6
0
    def __init__(self, file=None):
        super().__init__([])

        self.original = None
        self.changed_address = {}
        self.changed_dma = {}
        self.force_patch = []

        if file is None:
            return

        decomp_file = local_path('ZOOTDEC.z64')

        os.chdir(local_path())

        with open(data_path('generated/symbols.json'), 'r') as stream:
            symbols = json.load(stream)
            self.symbols = {
                name: int(addr, 16)
                for name, addr in symbols.items()
            }

        if file == '':
            # if not specified, try to read from the previously decompressed rom
            file = decomp_file
            try:
                self.read_rom(file)
            except FileNotFoundError:
                # could not find the decompressed rom either
                raise FileNotFoundError('Must specify path to base ROM')
        else:
            self.read_rom(file)

        # decompress rom, or check if it's already decompressed
        self.decompress_rom_file(file, decomp_file)

        # Add file to maximum size
        self.buffer.extend(bytearray([0x00] * (0x4000000 - len(self.buffer))))
        self.original = self.copy()

        # Add version number to header.
        self.write_bytes(0x35, get_version_bytes(__version__))
        self.force_patch.extend([0x35, 0x36, 0x37])
Exemple #7
0
def update_sprites_lttp():
    from tkinter import Tk
    from LttPAdjuster import get_image_for_sprite
    from LttPAdjuster import BackgroundTaskProgress
    from LttPAdjuster import BackgroundTaskProgressNullWindow
    from LttPAdjuster import update_sprites

    # Target directories
    input_dir = user_path("data", "sprites", "alttpr")
    output_dir = local_path("WebHostLib", "static",
                            "generated")  # TODO: move to user_path

    os.makedirs(os.path.join(output_dir, "sprites"), exist_ok=True)
    # update sprites through gui.py's functions
    done = threading.Event()
    try:
        top = Tk()
    except:
        task = BackgroundTaskProgressNullWindow(
            update_sprites, lambda successful, resultmessage: done.set())
    else:
        top.withdraw()
        task = BackgroundTaskProgress(
            top, update_sprites, "Updating Sprites",
            lambda succesful, resultmessage: done.set())
    while not done.isSet():
        task.do_events()

    spriteData = []

    for file in os.listdir(input_dir):
        sprite = Sprite(os.path.join(input_dir, file))

        if not sprite.name:
            print("Warning:", file, "has no name.")
            sprite.name = file.split(".", 1)[0]
        if sprite.valid:
            with open(
                    os.path.join(output_dir, "sprites",
                                 f"{os.path.splitext(file)[0]}.gif"),
                    'wb') as image:
                image.write(get_image_for_sprite(sprite, True))
            spriteData.append({
                "file": file,
                "author": sprite.author_name,
                "name": sprite.name
            })
        else:
            print(file, "dropped, as it has no valid sprite data.")
    spriteData.sort(key=lambda entry: entry["name"])
    with open(f'{output_dir}/spriteData.json', 'w') as file:
        json.dump({"sprites": spriteData}, file, indent=1)
    return spriteData
Exemple #8
0
def start():

    settings, gui, args_loglevel, no_log_file = get_settings_from_command_line_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)

    logger = logging.getLogger('')

    if not no_log_file:
        ts = time.time()
        st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H-%M-%S')
        log_dir = local_path('Logs')
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)
        log_path = os.path.join(log_dir, '%s.log' % st)
        log_file = logging.FileHandler(log_path)
        log_file.setFormatter(
            logging.Formatter('[%(asctime)s] %(message)s', datefmt='%H:%M:%S'))
        logger.addHandler(log_file)

    if not settings.check_version:
        try:
            check_version(settings.checked_version)
        except VersionError as e:
            logger.warning(str(e))

    try:
        if gui:
            guiMain(settings)
        elif settings.cosmetics_only:
            cosmetic_patch(settings)
        elif settings.patch_file != '':
            from_patch_file(settings)
        elif settings.count != None and settings.count > 1:
            orig_seed = settings.seed
            for i in range(settings.count):
                settings.update_seed(orig_seed + '-' + str(i))
                main(settings)
        else:
            main(settings)
    except Exception as ex:
        logger.exception(ex)
        sys.exit(1)
Exemple #9
0
def get_exe(component: Union[str, Component]) -> Optional[Sequence[str]]:
    if isinstance(component, str):
        name = component
        component = None
        if name.startswith('Archipelago'):
            name = name[11:]
        if name.endswith('.exe'):
            name = name[:-4]
        if name.endswith('.py'):
            name = name[:-3]
        if not name:
            return None
        for c in components:
            if c.script_name == name or c.frozen_name == f'Archipelago{name}':
                component = c
                break
        if not component:
            return None
    if is_frozen():
        suffix = '.exe' if is_windows else ''
        return [local_path(f'{component.frozen_name}{suffix}')]
    else:
        return [sys.executable, local_path(f'{component.script_name}.py')]
Exemple #10
0
def guiMain():
    try:
        version_check("Node", "8.0.0", "https://nodejs.org/en/download/")
        version_check("NPM", "3.5.2", "https://nodejs.org/en/download/")
    except VersionError as ex:
        print(ex.args[0])
        webbrowser.open(ex.args[1])
        return

    web_version = '--web' in sys.argv
    if '--skip-settingslist' not in sys.argv:
        CreateJSON(data_path('generated/settings_list.json'), web_version)

    args = ["node", "run.js", "release", "python", sys.executable]
    subprocess.Popen(args,shell=False,cwd=local_path("GUI"))
Exemple #11
0
    def __init__(self, settings, patch=True):
        self.last_address = None

        file = settings.rom
        decomp_file = 'ZOOTDEC.z64'

        os.chdir(os.path.dirname(os.path.realpath(__file__)))
        #os.chdir(output_path(os.path.dirname(os.path.realpath(__file__))))

        with open(local_path('data/symbols.json'), 'r') as stream:
            symbols = json.load(stream)
            self.symbols = { name: int(addr, 16) for name, addr in symbols.items() }
        self.read_rom(file)
        self.decompress_rom_file(file, decomp_file)

        # Add file to maximum size
        self.buffer.extend(bytearray([0x00] * (0x4000000 - len(self.buffer))))
    def __init__(self, settings, patch=True):
        self.__last_address = None

        file = settings.rom
        decomp_file = 'ZOOTDEC.z64'

        os.chdir(local_path())

        with open(data_path('generated/symbols.json'), 'r') as stream:
            symbols = json.load(stream)
            self.symbols = { name: int(addr, 16) for name, addr in symbols.items() }

        self.read_rom(file)
        self.decompress_rom_file(file, decomp_file)

        # Add file to maximum size
        self.buffer.extend(bytearray([0x00] * (0x4000000 - len(self.buffer))))
        self.original = copy.copy(self.buffer)
        self.changed_address = {}
        self.changed_dma = {}
Exemple #13
0
def guiMain(args=None):
    mainWindow = Tk()
    mainWindow.wm_title("OoT Randomizer %s" % ESVersion)

    set_icon(mainWindow)

    notebook = ttk.Notebook(mainWindow)
    randomizerWindow = ttk.Frame(notebook)
    adjustWindow = ttk.Frame(notebook)
    customWindow = ttk.Frame(notebook)
    notebook.add(randomizerWindow, text='Randomize')
    notebook.pack()

    # Shared Controls

    farBottomFrame = Frame(mainWindow)

    def open_output():
        open_file(output_path(''))

    openOutputButton = Button(farBottomFrame, text='Open Output Directory', command=open_output)

    if os.path.exists(local_path('README.html')):
        def open_readme():
            open_file(local_path('README.html'))
        openReadmeButton = Button(farBottomFrame, text='Open Documentation', command=open_readme)
        openReadmeButton.pack(side=LEFT)

    farBottomFrame.pack(side=BOTTOM, fill=X, padx=5, pady=5)

    # randomizer controls

    topFrame = Frame(randomizerWindow)
    rightHalfFrame = Frame(topFrame)
    checkBoxFrame = Frame(rightHalfFrame)

    createSpoilerVar = IntVar()
    createSpoilerCheckbutton = Checkbutton(checkBoxFrame, text="Create Spoiler Log", variable=createSpoilerVar)
    suppressRomVar = IntVar()
    suppressRomCheckbutton = Checkbutton(checkBoxFrame, text="Do not create patched Rom", variable=suppressRomVar)
    compressRomVar = IntVar()
    compressRomCheckbutton = Checkbutton(checkBoxFrame, text="Compress patched Rom", variable=compressRomVar)
    openForestVar = IntVar()
    openForestCheckbutton = Checkbutton(checkBoxFrame, text="Open Forest", variable=openForestVar)
    openDoorVar = IntVar()
    openDoorCheckbutton = Checkbutton(checkBoxFrame, text="Open Door of Time", variable=openDoorVar)
    dungeonItemsVar = IntVar()
    dungeonItemsCheckbutton = Checkbutton(checkBoxFrame, text="Place Dungeon Items (Compasses/Maps)", onvalue=0, offvalue=1, variable=dungeonItemsVar)
    beatableOnlyVar = IntVar()
    beatableOnlyCheckbutton = Checkbutton(checkBoxFrame, text="Only ensure seed is beatable, not all items must be reachable", variable=beatableOnlyVar)
    hintsVar = IntVar()
    hintsCheckbutton = Checkbutton(checkBoxFrame, text="Gossip Stone Hints with Stone of Agony", variable=hintsVar)

    createSpoilerCheckbutton.pack(expand=True, anchor=W)
    suppressRomCheckbutton.pack(expand=True, anchor=W)
    compressRomCheckbutton.pack(expand=True, anchor=W)
    openForestCheckbutton.pack(expand=True, anchor=W)
    openDoorCheckbutton.pack(expand=True, anchor=W)
    dungeonItemsCheckbutton.pack(expand=True, anchor=W)
    beatableOnlyCheckbutton.pack(expand=True, anchor=W)
    hintsCheckbutton.pack(expand=True, anchor=W)

    fileDialogFrame = Frame(rightHalfFrame)

    romDialogFrame = Frame(fileDialogFrame)
    baseRomLabel = Label(romDialogFrame, text='Base Rom')
    romVar = StringVar()
    romEntry = Entry(romDialogFrame, textvariable=romVar)

    def RomSelect():
        rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".z64", ".n64")), ("All Files", "*")])
        romVar.set(rom)
    romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect)

    baseRomLabel.pack(side=LEFT)
    romEntry.pack(side=LEFT)
    romSelectButton.pack(side=LEFT)

    romDialogFrame.pack()

    checkBoxFrame.pack()
    fileDialogFrame.pack()

    drowDownFrame = Frame(topFrame)


    bridgeFrame = Frame(drowDownFrame)
    bridgeVar = StringVar()
    bridgeVar.set('medallions')
    bridgeOptionMenu = OptionMenu(bridgeFrame, bridgeVar, 'medallions', 'vanilla', 'dungeons', 'open')
    bridgeOptionMenu.pack(side=RIGHT)
    bridgeLabel = Label(bridgeFrame, text='Rainbow Bridge Requirement')
    bridgeLabel.pack(side=LEFT)

    bridgeFrame.pack(expand=True, anchor=E)

    bottomFrame = Frame(randomizerWindow)

    seedLabel = Label(bottomFrame, text='Seed #')
    seedVar = StringVar()
    seedEntry = Entry(bottomFrame, textvariable=seedVar)
    countLabel = Label(bottomFrame, text='Count')
    countVar = StringVar()
    countSpinbox = Spinbox(bottomFrame, from_=1, to=100, textvariable=countVar)

    def generateRom():
        guiargs = Namespace
        guiargs.seed = int(seedVar.get()) if seedVar.get() else None
        guiargs.count = int(countVar.get()) if countVar.get() != '1' else None
        guiargs.bridge = bridgeVar.get()
        guiargs.create_spoiler = bool(createSpoilerVar.get())
        guiargs.suppress_rom = bool(suppressRomVar.get())
        guiargs.compress_rom = bool(compressRomVar.get())
        guiargs.open_forest = bool(openForestVar.get())
        guiargs.open_door_of_time = bool(openDoorVar.get())
        guiargs.nodungeonitems = bool(dungeonItemsVar.get())
        guiargs.beatableonly = bool(beatableOnlyVar.get())
        guiargs.hints = bool(hintsVar.get())
        guiargs.rom = romVar.get()
        try:
            if guiargs.count is not None:
                seed = guiargs.seed
                for _ in range(guiargs.count):
                    main(seed=seed, args=guiargs)
                    seed = random.randint(0, 999999999)
            else:
                main(seed=guiargs.seed, args=guiargs)
        except Exception as e:
            messagebox.showerror(title="Error while creating seed", message=str(e))
        else:
            messagebox.showinfo(title="Success", message="Rom patched successfully")

    generateButton = Button(bottomFrame, text='Generate Patched Rom', command=generateRom)

    seedLabel.pack(side=LEFT)
    seedEntry.pack(side=LEFT)
    countLabel.pack(side=LEFT, padx=(5, 0))
    countSpinbox.pack(side=LEFT)
    generateButton.pack(side=LEFT, padx=(5, 0))

    openOutputButton.pack(side=RIGHT)

    drowDownFrame.pack(side=LEFT)
    rightHalfFrame.pack(side=RIGHT)
    topFrame.pack(side=TOP)
    bottomFrame.pack(side=BOTTOM)

    if args is not None:
        # load values from commandline args
        createSpoilerVar.set(int(args.create_spoiler))
        suppressRomVar.set(int(args.suppress_rom))
        compressRomVar.set(int(args.compress_rom))
        if args.nodungeonitems:
            dungeonItemsVar.set(int(not args.nodungeonitems))
        openForestVar.set(int(args.open_forest))
        openDoorVar.set(int(args.open_door_of_time))
        beatableOnlyVar.set(int(args.beatableonly))
        hintsVar.set(int(args.hints))
        if args.count:
            countVar.set(str(args.count))
        if args.seed:
            seedVar.set(str(args.seed))
        bridgeVar.set(args.bridge)
        romVar.set(args.rom)

    mainWindow.mainloop()
Exemple #14
0
def guiMain(settings=None):
    frames = {}

    mainWindow = Tk()
    mainWindow.wm_title("OoT Randomizer %s" % ESVersion)

    set_icon(mainWindow)

    notebook = ttk.Notebook(mainWindow)
    frames['rom_tab'] = ttk.Frame(notebook)
    frames['rules_tab'] = ttk.Frame(notebook)
    frames['logic_tab'] = ttk.Frame(notebook)
    frames['other_tab'] = ttk.Frame(notebook)
    frames['aesthetic_tab'] = ttk.Frame(notebook)
    adjustWindow = ttk.Frame(notebook)
    customWindow = ttk.Frame(notebook)
    notebook.add(frames['rom_tab'], text='ROM Options')
    notebook.add(frames['rules_tab'], text='Main Rules')
    notebook.add(frames['logic_tab'], text='Detailed Logic')
    notebook.add(frames['other_tab'], text='Other')
    notebook.add(frames['aesthetic_tab'], text='Aesthetics')

    # Shared Controls   

    # adds a LabelFrame containing a list of radio buttons based on the given data
    # returns the label_frame, and a variable associated with it
    def MakeRadioList(parent, data):
        # create a frame to hold the radio buttons
        lable_frame = LabelFrame(parent, text=data["name"], labelanchor=NW)
        # create a variable to hold the result of the user's decision
        radio_var = StringVar(value=data["default"]);
        # setup orientation
        side = TOP
        anchor = W
        if "horizontal" in data and data["horizontal"]:
            side = LEFT
            anchor = N
        # add the radio buttons
        for option in data["options"]:
            radio_button = Radiobutton(lable_frame, text=option["description"], value=option["value"], variable=radio_var,
                                       justify=LEFT, wraplength=data["wraplength"])
            radio_button.pack(expand=True, side=side, anchor=anchor)
        # return the frame so it can be packed, and the var so it can be used
        return (lable_frame, radio_var)

    #######################
    # randomizer controls #
    #######################

    # hold the results of the user's decisions here
    guivars = {}

    # hierarchy
    ############

    #Rules Tab
    frames['open']   = LabelFrame(frames['rules_tab'], text='Open',   labelanchor=NW)
    frames['logic']  = LabelFrame(frames['rules_tab'], text='Logic',  labelanchor=NW)

    # Logic tab
    frames['rewards'] = LabelFrame(frames['logic_tab'], text='Remove Specific Locations', labelanchor=NW)
    frames['tricks']  = LabelFrame(frames['logic_tab'], text='Specific expected tricks', labelanchor=NW)

    #Other Tab
    frames['convenience'] = LabelFrame(frames['other_tab'], text='Speed Ups', labelanchor=NW)
    frames['other']       = LabelFrame(frames['other_tab'], text='Misc',      labelanchor=NW)

    #Aesthetics tab
    frames['tuniccolor'] = LabelFrame(frames['aesthetic_tab'], text='Tunic Color', labelanchor=NW)
    frames['navicolor']       = LabelFrame(frames['aesthetic_tab'], text='Navi Color',  labelanchor=NW)
    frames['lowhp']      = LabelFrame(frames['aesthetic_tab'], text='Low HP SFX',  labelanchor=NW)


    # shared
    settingsFrame = Frame(mainWindow)
    settings_string_var = StringVar()
    settingsEntry = Entry(settingsFrame, textvariable=settings_string_var)

    def show_settings(event=None):
        settings = guivars_to_settings(guivars)
        settings_string_var.set( settings.get_settings_string() )

        # Update any dependencies
        for info in setting_infos:
            if info.gui_params and 'dependency' in info.gui_params:
                dep_met = True
                for dep_var, dep_val in info.gui_params['dependency'].items():
                    if guivars[dep_var].get() != dep_val:
                        dep_met = False

                if widgets[info.name].winfo_class() == 'Frame':
                    for child in widgets[info.name].winfo_children():
                        child.configure(state= 'normal' if dep_met else 'disabled')
                else:
                    widgets[info.name].config(state = 'normal' if dep_met else 'disabled')


    def import_settings(event=None):
        try:
            settings = guivars_to_settings(guivars)
            text = settings_string_var.get().upper()
            settings.seed = guivars['seed'].get()
            settings.update_with_settings_string(text)
            settings_to_guivars(settings, guivars)
        except Exception as e:
            messagebox.showerror(title="Error", message="Invalid settings string")

    label = Label(settingsFrame, text="Settings String")
    importSettingsButton = Button(settingsFrame, text='Import Settings String', command=import_settings)
    label.pack(side=LEFT, anchor=W, padx=5)
    settingsEntry.pack(side=LEFT, anchor=W)
    importSettingsButton.pack(side=LEFT, anchor=W, padx=5)



    fileDialogFrame = Frame(frames['rom_tab'])

    romDialogFrame = Frame(fileDialogFrame)
    baseRomLabel = Label(romDialogFrame, text='Base Rom')
    guivars['rom'] = StringVar(value='ZOOTDEC.z64')
    romEntry = Entry(romDialogFrame, textvariable=guivars['rom'], width=40)

    def RomSelect():
        rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".z64", ".n64")), ("All Files", "*")])
        if rom != '':
            guivars['rom'].set(rom)
    romSelectButton = Button(romDialogFrame, text='Select Rom', command=RomSelect, width=10)

    baseRomLabel.pack(side=LEFT, padx=(40,0))
    romEntry.pack(side=LEFT, padx=3)
    romSelectButton.pack(side=LEFT)

    romDialogFrame.pack()

    fileDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(5,1))

    def open_output():
        open_file(output_path(''))
    
    def output_dir_select():
        rom = filedialog.askdirectory(initialdir = default_output_path(guivars['output_dir'].get()))
        if rom != '':
            guivars['output_dir'].set(rom)

    outputDialogFrame = Frame(frames['rom_tab'])
    outputDirLabel = Label(outputDialogFrame, text='Output Directory')
    guivars['output_dir'] = StringVar(value='')
    outputDirEntry = Entry(outputDialogFrame, textvariable=guivars['output_dir'], width=40)
    outputDirButton = Button(outputDialogFrame, text='Select Dir', command=output_dir_select, width=10)
    outputDirLabel.pack(side=LEFT, padx=(3,0))
    outputDirEntry.pack(side=LEFT, padx=3)
    outputDirButton.pack(side=LEFT)
    outputDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(5,1))

    if os.path.exists(local_path('README.html')):
        def open_readme():
            open_file(local_path('README.html'))
        openReadmeButton = Button(outputDialogFrame, text='Open Documentation', command=open_readme)
        openReadmeButton.pack(side=LEFT, padx=5)

    outputDialogFrame.pack(side=TOP, anchor=W, pady=3)

    countDialogFrame = Frame(frames['rom_tab'])
    countLabel = Label(countDialogFrame, text='Generation Count')
    guivars['count'] = StringVar()
    countSpinbox = Spinbox(countDialogFrame, from_=1, to=100, textvariable=guivars['count'], width=3)

    countLabel.pack(side=LEFT)
    countSpinbox.pack(side=LEFT, padx=2)
    countDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(1,1))

    multiworldFrame = LabelFrame(frames['rom_tab'], text='Multi-World Generation')
    countLabel = Label(multiworldFrame, wraplength=300, justify=LEFT, text='This is used for co-op generations. Increasing World Count will drastically increase the generation time. For more information see https://github.com/TestRunnerSRL/bizhawk-co-op')
    countLabel.pack(side=TOP, anchor=W, padx=5, pady=(1,1))


    worldCountFrame = Frame(multiworldFrame)
    countLabel = Label(worldCountFrame, text='World Count')
    guivars['world_count'] = StringVar()
    countSpinbox = Spinbox(worldCountFrame, from_=1, to=100, textvariable=guivars['world_count'], width=3)

    countLabel.pack(side=LEFT)
    countSpinbox.pack(side=LEFT, padx=2)
    worldCountFrame.pack(side=LEFT, anchor=N, padx=5, pady=(1,1))

    playerNumFrame = Frame(multiworldFrame)
    countLabel = Label(playerNumFrame, text='Player Number')
    guivars['player_num'] = StringVar()
    countSpinbox = Spinbox(playerNumFrame, from_=1, to=100, textvariable=guivars['player_num'], width=3)

    countLabel.pack(side=LEFT)
    countSpinbox.pack(side=LEFT, padx=2)
    playerNumFrame.pack(side=LEFT, anchor=N, padx=5, pady=(1,1))
    multiworldFrame.pack(side=TOP, anchor=W, padx=5, pady=(1,1))




    # build gui
    ############

    widgets = {}

    for info in setting_infos:
        if info.gui_params:
            if info.gui_params['widget'] == 'Checkbutton':
                # determine the initial value of the checkbox
                default_value = 1 if info.gui_params['default'] == "checked" else 0
                # create a variable to access the box's state
                guivars[info.name] = IntVar(value=default_value)
                # create the checkbox
                widgets[info.name] = Checkbutton(frames[info.gui_params['group']], text=info.gui_params['text'], variable=guivars[info.name], justify=LEFT, wraplength=200, command=show_settings)
                widgets[info.name].pack(expand=False, anchor=W)
            elif info.gui_params['widget'] == 'Combobox':
                # create the variable to store the user's decision
                guivars[info.name] = StringVar(value=info.gui_params['default'])
                # create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])
                # dropdown = OptionMenu(widgets[info.name], guivars[info.name], *(info['options']))
                if isinstance(info.gui_params['options'], list):
                    info.gui_params['options'] = dict(zip(info.gui_params['options'], info.gui_params['options']))
                dropdown = ttk.Combobox(widgets[info.name], textvariable=guivars[info.name], values=list(info.gui_params['options'].keys()), state='readonly', width=30)
                dropdown.bind("<<ComboboxSelected>>", show_settings)
                dropdown.pack(side=BOTTOM, anchor=W)
                # label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name], text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # pack the frame
                widgets[info.name].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)
            elif info.gui_params['widget'] == 'Scale':
                # create the variable to store the user's decision
                guivars[info.name] = IntVar(value=info.gui_params['default'])
                # create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])
                # dropdown = OptionMenu(widgets[info.name], guivars[info.name], *(info['options']))
                minval = 'min' in info.gui_params and info.gui_params['min'] or 0
                maxval = 'max' in info.gui_params and info.gui_params['max'] or 100
                stepval = 'step' in info.gui_params and info.gui_params['step'] or 1


                scale = Scale(widgets[info.name], variable=guivars[info.name], from_=minval, to=maxval, tickinterval=stepval, resolution=stepval, showvalue=0, orient=HORIZONTAL, sliderlength=15, length=200, command=show_settings)
                scale.pack(side=BOTTOM, anchor=W)
                # label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name], text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # pack the frame
                widgets[info.name].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)
            elif info.gui_params['widget'] == 'Entry':
                # create the variable to store the user's decision
                guivars[info.name] = StringVar(value=info.gui_params['default'])
                # create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])

                entry = Entry(widgets[info.name], textvariable=guivars[info.name], width=30)
                entry.pack(side=BOTTOM, anchor=W)
                # label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name], text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # pack the frame
                widgets[info.name].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)


    # pack the hierarchy

    frames['open'].pack(  fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1) )
    frames['logic'].pack( fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1) )

    # Logic tab
    frames['rewards'].pack(fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1) )
    frames['tricks'].pack( fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1) )

    #Other Tab
    frames['convenience'].pack(fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1) )
    frames['other'].pack(      fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1) )

    #Aesthetics tab
    frames['navicolor'].pack( fill=BOTH, expand=True, anchor=N, side=RIGHT, pady=(5,1) )
    frames['tuniccolor'].pack(fill=BOTH, expand=True, anchor=W, side=TOP, pady=(5,1) )
    frames['lowhp'].pack(     fill=BOTH, expand=True, anchor=W, side=BOTTOM, pady=(5,1) )

    
    notebook.pack(fill=BOTH, expand=True, padx=5, pady=5)



    # didn't refactor the rest, sorry


    # create the option menu

    settingsFrame.pack(fill=BOTH, anchor=W, padx=5, pady=(10,0))

    def generateRom():
        settings = guivars_to_settings(guivars)

        try:
            if settings.count is not None:
                orig_seed = settings.seed
                for i in range(settings.count):
                    settings.update_seed(orig_seed + '-' + str(i))
                    main(settings)
            else:
                main(settings)
        except Exception as e:
            messagebox.showerror(title="Error while creating seed", message=str(e))
        else:
            messagebox.showinfo(title="Success", message="Rom patched successfully")

    generateSeedFrame = Frame(mainWindow)
    generateButton = Button(generateSeedFrame, text='Generate Patched Rom', command=generateRom)

    seedLabel = Label(generateSeedFrame, text='Seed')
    guivars['seed'] = StringVar()
    seedEntry = Entry(generateSeedFrame, textvariable=guivars['seed'])
    seedLabel.pack(side=LEFT)
    seedEntry.pack(side=LEFT)
    generateButton.pack(side=LEFT, padx=(5, 0))

    generateSeedFrame.pack(side=BOTTOM, anchor=W, padx=5, pady=10)

    if settings is not None:
        # load values from commandline args
        settings_to_guivars(settings, guivars)
    else:
        # try to load saved settings
        try:
            with open('settings.sav') as f:
                settings = Settings( json.load(f) )
                settings.update_seed("")
                settings_to_guivars(settings, guivars)
        except:
            pass

    show_settings()

    mainWindow.mainloop()

    # save settings on close
    with open('settings.sav', 'w') as outfile:
        settings = guivars_to_settings(guivars)
        json.dump(settings.__dict__, outfile)
Exemple #15
0
 def local_unofficial_sprite_dir(self):
     return local_path("data/sprites/unofficial")
Exemple #16
0
def patch_rom(world, rom):
    with open(local_path('data/rom_patch.txt'), 'r') as stream:
        for line in stream:
            address, value = [int(x, 16) for x in line.split(',')]
            rom.write_byte(address, value)

    # Write Randomizer title screen logo
    with open(local_path('data/title.bin'), 'rb') as stream:
        titleBytes = stream.read()
        rom.write_bytes(0x01795300, titleBytes)

    # will be populated with data to be written to initial save
    # see initial_save.asm and config.asm for more details on specifics
    # or just use the following functions to add an entry to the table
    initial_save_table = []

    # will set the bits of value to the offset in the save (or'ing them with what is already there)
    def write_bits_to_save(offset, value, filter=None):
        nonlocal initial_save_table

        if filter and not filter(value):
            return

        initial_save_table += [(offset & 0xFF00) >> 8, offset & 0xFF, 0x00,
                               value]

    # will overwrite the byte at offset with the given value
    def write_byte_to_save(offset, value, filter=None):
        nonlocal initial_save_table

        if filter and not filter(value):
            return

        initial_save_table += [(offset & 0xFF00) >> 8, offset & 0xFF, 0x01,
                               value]

    # will overwrite the byte at offset with the given value
    def write_bytes_to_save(offset, bytes, filter=None):
        for i, value in enumerate(bytes):
            write_byte_to_save(offset + i, value, filter)

    # will overwrite the byte at offset with the given value
    def write_save_table(rom):
        nonlocal initial_save_table

        table_len = len(initial_save_table)
        if table_len > 0x400:
            raise Exception(
                "The Initial Save Table has exceeded it's maximum capacity: 0x%03X/0x400"
                % table_len)
        rom.write_bytes(0x3481800, initial_save_table)

    # Initial Save Data
    patch_files(rom, mq_scenes)

    # Load Message and Shop Data
    messages = read_messages(rom)
    shop_items = read_shop_items(rom, shop_item_file.start + 0x1DEC)
    remove_unused_messages(messages)

    # Sets hooks for gossip stone changes
    if world.hints != 'none':
        writeGossipStoneHintsHints(world, messages)

    # TODO: change to Majora text? if there's a reason to do that
    # build silly ganon lines
    buildGanonText(world, messages)

    # TODO: Update for MM
    # Write item overrides
    override_table = get_override_table(world)
    rom.write_bytes(0x3481000, sum(override_table, []))
    rom.write_byte(0x03481C00, world.id + 1)  # Write player ID

    # TODO: figure out and find these overrides for MM
    # Revert Song Get Override Injection
    if not world.shuffle_song_items:
        # general get song
        rom.write_int32(0xAE5DF8, 0x240200FF)
        rom.write_int32(0xAE5E04, 0xAD0F00A4)
        # requiem of spirit
        rom.write_int32s(0xAC9ABC, [0x3C010001, 0x00300821])
        # sun song
        rom.write_int32(0xE09F68, 0x8C6F00A4)
        rom.write_int32(0xE09F74, 0x01CFC024)
        rom.write_int32(0xE09FB0, 0x240F0001)
        # epona
        rom.write_int32(0xD7E77C, 0x8C4900A4)
        rom.write_int32(0xD7E784, 0x8D088C24)
        rom.write_int32s(0xD7E8D4, [0x8DCE8C24, 0x8C4F00A4])
        rom.write_int32s(0xD7E140, [0x8DCE8C24, 0x8C6F00A4])
        rom.write_int32(0xD7EBBC, 0x14410008)
        rom.write_int32(0xD7EC1C, 0x17010010)
        # song of time
        rom.write_int32(0xDB532C, 0x24050003)

    # TODO: Find appropriate address to do this in MM
    # Set default targeting option to Hold
    if world.default_targeting == 'hold':
        rom.write_byte(0xB71E6D, 0x01)

    # Set OHKO mode
    if world.difficulty == 'ohko':
        rom.write_int32(0xAE80A8, 0xA4A00030)  # sh  zero,48(a1)
        rom.write_int32(0xAE80B4, 0x06000003)  # bltz s0, +0003

    # Patch songs and boss rewards
    for location in world.get_locations():
        item = location.item
        itemid = copy.copy(item.code)
        locationaddress = location.address
        secondaryaddress = location.address2

        if itemid is None or location.address is None:
            continue

        if location.type == 'Song' and not world.shuffle_song_items:
            rom.write_byte(locationaddress, itemid[0])
            itemid[0] = itemid[0] + 0x0D
            rom.write_byte(secondaryaddress, itemid[0])
            if location.name == 'Impa at Castle':
                impa_fix = 0x65 - itemid[1]
                rom.write_byte(0xD12ECB, impa_fix)
                rom.write_byte(0x2E8E931, item_data[item.name])  #Fix text box
            elif location.name == 'Song from Malon':
                if item.name == 'Suns Song':
                    rom.write_byte(locationaddress, itemid[0])
                malon_fix = 0x8C34 - (itemid[1] * 4)
                malon_fix_high = malon_fix >> 8
                malon_fix_low = malon_fix & 0x00FF
                rom.write_bytes(0xD7E142, [malon_fix_high, malon_fix_low])
                rom.write_bytes(
                    0xD7E8D6, [malon_fix_high, malon_fix_low]
                )  # I really don't like hardcoding these addresses, but for now.....
                rom.write_bytes(0xD7E786, [malon_fix_high, malon_fix_low])
                rom.write_byte(0x29BECB9, item_data[item.name])  #Fix text box
            elif location.name == 'Song from Composer Grave':
                sun_fix = 0x8C34 - (itemid[1] * 4)
                sun_fix_high = sun_fix >> 8
                sun_fix_low = sun_fix & 0x00FF
                rom.write_bytes(0xE09F66, [sun_fix_high, sun_fix_low])
                rom.write_byte(0x332A87D, item_data[item.name])  #Fix text box
            elif location.name == 'Song from Saria':
                saria_fix = 0x65 - itemid[1]
                rom.write_byte(0xE2A02B, saria_fix)
                rom.write_byte(0x20B1DBD, item_data[item.name])  #Fix text box
            elif location.name == 'Song from Ocarina of Time':
                rom.write_byte(0x252FC95, item_data[item.name])  #Fix text box
            elif location.name == 'Song at Windmill':
                windmill_fix = 0x65 - itemid[1]
                rom.write_byte(0xE42ABF, windmill_fix)
                rom.write_byte(0x3041091, item_data[item.name])  #Fix text box
            elif location.name == 'Sheik Forest Song':
                minuet_fix = 0x65 - itemid[1]
                rom.write_byte(0xC7BAA3, minuet_fix)
                rom.write_byte(0x20B0815, item_data[item.name])  #Fix text box
            elif location.name == 'Sheik at Temple':
                prelude_fix = 0x65 - itemid[1]
                rom.write_byte(0xC805EF, prelude_fix)
                rom.write_byte(0x2531335, item_data[item.name])  #Fix text box
            elif location.name == 'Sheik in Crater':
                bolero_fix = 0x65 - itemid[1]
                rom.write_byte(0xC7BC57, bolero_fix)
                rom.write_byte(0x224D7FD, item_data[item.name])  #Fix text box
            elif location.name == 'Sheik in Ice Cavern':
                serenade_fix = 0x65 - itemid[1]
                rom.write_byte(0xC7BD77, serenade_fix)
                rom.write_byte(0x2BEC895, item_data[item.name])  #Fix text box
            elif location.name == 'Sheik in Kakariko':
                nocturne_fix = 0x65 - itemid[1]
                rom.write_byte(0xAC9A5B, nocturne_fix)
                rom.write_byte(0x2000FED, item_data[item.name])  #Fix text box
            elif location.name == 'Sheik at Colossus':
                rom.write_byte(0x218C589, item_data[item.name])  #Fix text box
        elif location.type == 'Boss':
            rom.write_byte(locationaddress, itemid)
            rom.write_byte(secondaryaddress, item_data[item.name][2])

    # add a cheaper bombchu pack to the bombchu shop
    # describe
    update_message_by_id(
        messages, 0x80FE,
        '\x08\x05\x41Bombchu   (5 pieces)   60 Rupees\x01\x05\x40This looks like a toy mouse, but\x01it\'s actually a self-propelled time\x01bomb!\x09\x0A',
        0x03)
    # purchase
    update_message_by_id(
        messages, 0x80FF,
        '\x08Bombchu    5 Pieces    60 Rupees\x01\x01\x1B\x05\x42Buy\x01Don\'t buy\x05\x40\x09',
        0x03)
    rbl_bombchu = shop_items[0x0018]
    rbl_bombchu.price = 60
    rbl_bombchu.pieces = 5
    rbl_bombchu.get_item_id = 0x006A
    rbl_bombchu.description_message = 0x80FE
    rbl_bombchu.purchase_message = 0x80FF

    # TODO: Find these for MM Shops
    '''
    shop_objs = place_shop_items(rom, world, shop_items, messages,
        world.get_region('Kokiri Shop').locations, True)
    shop_objs |= {0x00FC, 0x00B2, 0x0101, 0x0102, 0x00FD, 0x00C5} # Shop objects
    rom.write_byte(0x2587029, len(shop_objs))
    rom.write_int32(0x258702C, 0x0300F600)
    rom.write_int16s(0x2596600, list(shop_objs))
    '''

    # Update grotto id data
    set_grotto_id_data(rom)

    if world.shuffle_smallkeys == 'remove' or world.shuffle_bosskeys == 'remove':
        locked_doors = get_locked_doors(rom, world)
        for _, [door_byte, door_bits] in locked_doors.items():
            write_bits_to_save(door_byte, door_bits)

    # Update chest type sizes
    if world.correct_chest_sizes:
        update_chest_sizes(rom, override_table)

    unused_comment = '''
    # TODO: Update these messages to reflect MM
    # give dungeon items the correct messages
    message_patch_for_dungeon_items(messages, shop_items, world)
    if world.shuffle_mapcompass == 'keysanity' and world.enhance_map_compass:
        reward_list = {'Kokiri Emerald':   "\x05\x42Kokiri Emerald\x05\x40",
                       'Goron Ruby':       "\x05\x41Goron Ruby\x05\x40",
                       'Zora Sapphire':    "\x05\x43Zora Sapphire\x05\x40",
                       'Forest Medallion': "\x05\x42Forest Medallion\x05\x40",
                       'Fire Medallion':   "\x05\x41Fire Medallion\x05\x40",
                       'Water Medallion':  "\x05\x43Water Medallion\x05\x40",
                       'Spirit Medallion': "\x05\x46Spirit Medallion\x05\x40",
                       'Shadow Medallion': "\x05\x45Shadow Medallion\x05\x40",
                       'Light Medallion':  "\x05\x44Light Medallion\x05\x40"
        }
        dungeon_list = {'DT':   ("the \x05\x42Deku Tree", 'Queen Gohma', 0x62, 0x88),
                        'DC':   ("\x05\x41Dodongo\'s Cavern", 'King Dodongo', 0x63, 0x89),
                        'JB':   ("\x05\x43Jabu Jabu\'s Belly", 'Barinade', 0x64, 0x8a),
                        'FoT':  ("the \x05\x42Forest Temple", 'Phantom Ganon', 0x65, 0x8b),
                        'FiT':  ("the \x05\x41Fire Temple", 'Volvagia', 0x7c, 0x8c),
                        'WT':   ("the \x05\x43Water Temple", 'Morpha', 0x7d, 0x8e),
                        'SpT':  ("the \x05\x46Spirit Temple", 'Twinrova', 0x7e, 0x8f),
                        'IC':   ("the \x05\x44Ice Cavern", None, 0x87, 0x92),
                        'BW':   ("the \x05\x45Bottom of the Well", None, 0xa2, 0xa5),
                        'ShT':   ("the \x05\x45Shadow Temple", 'Bongo Bongo', 0x7f, 0xa3),
        }
        for dungeon in world.dungeon_mq:
            if dungeon in ['GTG', 'GC']:
                pass
            elif dungeon in ['BW', 'IC']:
                dungeon_name, boss_name, compass_id, map_id = dungeon_list[dungeon]
                if world.world_count > 1:
                    map_message = "\x13\x76\x08\x05\x42\x0F\x05\x40 found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name)
                else:
                    map_message = "\x13\x76\x08You found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x01It\'s %s!\x09" % (dungeon_name, "masterful" if world.dungeon_mq[dungeon] else "ordinary")

                if world.quest == 'mixed':
                    update_message_by_id(messages, map_id, map_message)
            else:
                dungeon_name, boss_name, compass_id, map_id = dungeon_list[dungeon]
                dungeon_reward = reward_list[world.get_location(boss_name).item.name]
                if world.world_count > 1:
                    compass_message = "\x13\x75\x08\x05\x42\x0F\x05\x40 found the \x05\x41Compass\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name)
                else:
                    compass_message = "\x13\x75\x08You found the \x05\x41Compass\x05\x40\x01for %s\x05\x40!\x01It holds the %s!\x09" % (dungeon_name, dungeon_reward)
                update_message_by_id(messages, compass_id, compass_message)
                if world.quest == 'mixed':
                    if world.world_count > 1:
                        map_message = "\x13\x76\x08\x05\x42\x0F\x05\x40 found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x09" % (dungeon_name)
                    else:
                        map_message = "\x13\x76\x08You found the \x05\x41Dungeon Map\x05\x40\x01for %s\x05\x40!\x01It\'s %s!\x09" % (dungeon_name, "masterful" if world.dungeon_mq[dungeon] else "ordinary")
                    update_message_by_id(messages, map_id, map_message)
        else:
            # Set hints for boss reward shuffle # TODO: Find correct thing in MM
            # rom.write_bytes(0xE2ADB2, [0x70, 0x7A])
            # rom.write_bytes(0xE2ADB6, [0x70, 0x57])
            buildBossRewardHints(world, messages)
        '''

    # add song messages
    add_song_messages(messages, world)

    # reduce item message lengths
    update_item_messages(messages, world)

    # TODO: create a third wallet for MM
    # Add 3rd Wallet Upgrade
    # rom.write_int16(0xB6D57E, 0x0003)
    # rom.write_int16(0xB6EC52, 999)
    # tycoon_message = "\x08\x13\x57You got a \x05\x43Tycoon's Wallet\x05\x40!\x01Now you can hold\x01up to \x05\x46999\x05\x40 \x05\x46Rupees\x05\x40."
    if world.world_count > 1:
        tycoon_message = make_player_message(tycoon_message)
    update_message_by_id(messages, 0x00F8, tycoon_message, 0x23)

    repack_messages(rom, messages)
    write_shop_items(rom, shop_item_file.start + 0x1DEC, shop_items)

    # text shuffle
    if world.text_shuffle == 'except_hints':
        shuffle_messages(rom, except_hints=True)
    elif world.text_shuffle == 'complete':
        shuffle_messages(rom, except_hints=False)

    # output a text dump, for testing...
    #with open('keysanity_' + str(world.seed) + '_dump.txt', 'w', encoding='utf-16') as f:
    #     messages = read_messages(rom)
    #     f.write('item_message_strings = {\n')
    #     for m in messages:
    #        f.write("\t0x%04X: \"%s\",\n" % (m.id, m.get_python_string()))
    #     f.write('}\n')

    if world.ocarina_songs:
        replace_songs(rom, scarecrow_song)

    # actually write the save table to rom
    write_save_table(rom)

    # patch music
    if world.background_music == 'random':
        randomize_music(rom)
    elif world.background_music == 'off':
        disable_music(rom)

    # re-seed for aesthetic effects. They shouldn't be affected by the generation seed
    random.seed()

    # patch tunic colors
    # Custom color tunic stuff
    Tunics = []
    Tunics.append(0x00B6DA38)  # Kokiri Tunic
    Tunics.append(0x00B6DA3B)  # Goron Tunic
    Tunics.append(0x00B6DA3E)  # Zora Tunic
    colorList = get_tunic_colors()
    randomColors = random_choices(colorList, k=3)

    for i in range(len(Tunics)):
        # get the color option
        thisColor = world.tunic_colors[i]
        # handle true random
        randColor = [
            random.getrandbits(8),
            random.getrandbits(8),
            random.getrandbits(8)
        ]
        if thisColor == 'Completely Random':
            color = randColor
        else:
            # handle random
            if world.tunic_colors[i] == 'Random Choice':
                color = TunicColors[randomColors[i]]
            # grab the color from the list
            elif thisColor in TunicColors:
                color = TunicColors[thisColor]
            # build color from hex code
            else:
                color = list(int(thisColor[i:i + 2], 16) for i in (0, 2, 4))
        rom.write_bytes(Tunics[i], color)

    # patch navi colors
    Navi = []
    Navi.append([0x00B5E184])  # Default
    Navi.append([0x00B5E19C, 0x00B5E1BC])  # Enemy, Boss
    Navi.append([0x00B5E194])  # NPC
    Navi.append([
        0x00B5E174, 0x00B5E17C, 0x00B5E18C, 0x00B5E1A4, 0x00B5E1AC, 0x00B5E1B4,
        0x00B5E1C4, 0x00B5E1CC, 0x00B5E1D4
    ])  # Everything else
    naviList = get_navi_colors()
    randomColors = random_choices(naviList, k=4)

    for i in range(len(Navi)):
        # do everything in the inner loop so that "true random" changes even for subcategories
        for j in range(len(Navi[i])):
            # get the color option
            thisColor = world.navi_colors[i]
            # handle true random
            randColor = [
                random.getrandbits(8),
                random.getrandbits(8),
                random.getrandbits(8), 0xFF,
                random.getrandbits(8),
                random.getrandbits(8),
                random.getrandbits(8), 0x00
            ]
            if thisColor == 'Completely Random':
                color = randColor
            else:
                # handle random
                if world.navi_colors[i] == 'Random Choice':
                    color = NaviColors[randomColors[i]]
                # grab the color from the list
                elif thisColor in NaviColors:
                    color = NaviColors[thisColor]
                # build color from hex code
                else:
                    color = list(
                        int(thisColor[i:i + 2], 16) for i in (0, 2, 4))
                    color = color + [0xFF] + color + [0x00]
            rom.write_bytes(Navi[i][j], color)

    #Navi hints
    NaviHint = []
    NaviHint.append([0xAE7EF2, 0xC26C7E])  #Overworld Hint
    NaviHint.append([0xAE7EC6])  #Enemy Target Hint
    naviHintSFXList = [
        'Default', 'Notification', 'Rupee', 'Timer', 'Tamborine',
        'Recovery Heart', 'Carrot Refill', 'Navi - Hey!', 'Navi - Random',
        'Zelda - Gasp', 'Cluck', 'Mweep!', 'None'
    ]
    randomNaviHintSFX = random_choices(naviHintSFXList, k=2)

    for i in range(len(NaviHint)):
        for j in range(len(NaviHint[i])):
            thisNaviHintSFX = world.navi_hint_sounds[i]
            if thisNaviHintSFX == 'Random Choice':
                thisNaviHintSFX = randomNaviHintSFX[i]
            if thisNaviHintSFX == 'Notification':
                naviHintSFX = [0x48, 0x20]
            elif thisNaviHintSFX == 'Rupee':
                naviHintSFX = [0x48, 0x03]
            elif thisNaviHintSFX == 'Timer':
                naviHintSFX = [0x48, 0x1A]
            elif thisNaviHintSFX == 'Tamborine':
                naviHintSFX = [0x48, 0x42]
            elif thisNaviHintSFX == 'Recovery Heart':
                naviHintSFX = [0x48, 0x0B]
            elif thisNaviHintSFX == 'Carrot Refill':
                naviHintSFX = [0x48, 0x45]
            elif thisNaviHintSFX == 'Navi - Hey!':
                naviHintSFX = [0x68, 0x5F]
            elif thisNaviHintSFX == 'Navi - Random':
                naviHintSFX = [0x68, 0x43]
            elif thisNaviHintSFX == 'Zelda - Gasp':
                naviHintSFX = [0x68, 0x79]
            elif thisNaviHintSFX == 'Cluck':
                naviHintSFX = [0x28, 0x12]
            elif thisNaviHintSFX == 'Mweep!':
                naviHintSFX = [0x68, 0x7A]
            elif thisNaviHintSFX == 'None':
                naviHintSFX = [0x00, 0x00]
            if thisNaviHintSFX != 'Default':
                rom.write_bytes(NaviHint[i][j], naviHintSFX)

    #Low health beep
    healthSFXList = [
        'Default', 'Softer Beep', 'Rupee', 'Timer', 'Tamborine',
        'Recovery Heart', 'Carrot Refill', 'Navi - Hey!', 'Zelda - Gasp',
        'Cluck', 'Mweep!', 'None'
    ]
    randomSFX = random.choice(healthSFXList)
    address = 0xADBA1A

    if world.healthSFX == 'Random Choice':
        thisHealthSFX = randomSFX
    else:
        thisHealthSFX = world.healthSFX
    if thisHealthSFX == 'Default':
        healthSFX = [0x48, 0x1B]
    elif thisHealthSFX == 'Softer Beep':
        healthSFX = [0x48, 0x04]
    elif thisHealthSFX == 'Rupee':
        healthSFX = [0x48, 0x03]
    elif thisHealthSFX == 'Timer':
        healthSFX = [0x48, 0x1A]
    elif thisHealthSFX == 'Tamborine':
        healthSFX = [0x48, 0x42]
    elif thisHealthSFX == 'Recovery Heart':
        healthSFX = [0x48, 0x0B]
    elif thisHealthSFX == 'Carrot Refill':
        healthSFX = [0x48, 0x45]
    elif thisHealthSFX == 'Navi - Hey!':
        healthSFX = [0x68, 0x5F]
    elif thisHealthSFX == 'Zelda - Gasp':
        healthSFX = [0x68, 0x79]
    elif thisHealthSFX == 'Cluck':
        healthSFX = [0x28, 0x12]
    elif thisHealthSFX == 'Mweep!':
        healthSFX = [0x68, 0x7A]
    elif thisHealthSFX == 'None':
        healthSFX = [0x00, 0x00, 0x00, 0x00]
        address = 0xADBA14
    rom.write_bytes(address, healthSFX)

    return rom
Exemple #17
0
              'OoTClient',
              file_identifier=SuffixIdentifier('.apz5')),
    Component('OoT Adjuster', 'OoTAdjuster'),
    # FF1
    Component('FF1 Client', 'FF1Client'),
    # ChecksFinder
    Component('ChecksFinder Client', 'ChecksFinderClient'),
    # Starcraft 2
    Component('Starcraft 2 Client', 'Starcraft2Client'),
    # Functions
    Component('Open host.yaml', func=open_host_yaml),
    Component('Open Patch', func=open_patch),
    Component('Browse Files', func=browse_files),
)
icon_paths = {
    'icon': local_path('data', 'icon.ico' if is_windows else 'icon.png'),
    'mcicon': local_path('data', 'mcicon.ico')
}


def identify(path: Union[None, str]):
    if path is None:
        return None, None, None
    for component in components:
        if component.handles_file(path):
            return path, component.script_name, component
    return (None, None, None) if '/' in path or '\\' in path else (None, path,
                                                                   None)


def get_exe(component: Union[str, Component]) -> Optional[Sequence[str]]:
Exemple #18
0
def get_json():
    with open(local_path('data/mqu.json'), 'r') as stream:
        data = json.load(stream)
    return data
Exemple #19
0
def guiMain(settings=None):
    frames = {}

    mainWindow = Tk()
    mainWindow.wm_title("OoT Randomizer %s" % ESVersion)
    mainWindow.resizable(False, False)

    set_icon(mainWindow)

    notebook = ttk.Notebook(mainWindow)
    frames['rom_tab'] = ttk.Frame(notebook)
    frames['rules_tab'] = ttk.Frame(notebook)
    frames['logic_tab'] = ttk.Frame(notebook)
    frames['other_tab'] = ttk.Frame(notebook)
    frames['cosmetic_tab'] = ttk.Frame(notebook)
    frames['SFX_tab'] = ttk.Frame(notebook)
    frames['cosmetic_tab_left'] = Frame(frames['cosmetic_tab'])
    frames['cosmetic_tab_right'] = Frame(frames['cosmetic_tab'])
    notebook.add(frames['rom_tab'], text='ROM Options')
    notebook.add(frames['rules_tab'], text='Main Rules')
    notebook.add(frames['logic_tab'], text='Detailed Logic')
    notebook.add(frames['other_tab'], text='Other')
    notebook.add(frames['cosmetic_tab'], text='Cosmetic')
    notebook.add(frames['SFX_tab'], text='SFX')

    #######################
    # Randomizer controls #
    #######################

    # Hold the results of the user's decisions here
    guivars = {}
    widgets = {}
    presets = {}

    # Hierarchy
    ############

    #Rules Tab
    frames['open']        = LabelFrame(frames['rules_tab'],          text='Open',              labelanchor=NW)
    frames['world']       = LabelFrame(frames['rules_tab'],          text='World',             labelanchor=NW)
    frames['shuffle']     = LabelFrame(frames['rules_tab'],          text='Shuffle',           labelanchor=NW)

    # Logic tab
    frames['checks']      = LabelFrame(frames['logic_tab'],          text='Adult Trade Sequence', labelanchor=NW)
    frames['tricks']      = LabelFrame(frames['logic_tab'],          text='Lens of Truth',     labelanchor=NW)

    #Other Tab
    frames['convenience'] = LabelFrame(frames['other_tab'],          text='Timesavers',        labelanchor=NW)
    frames['other']       = LabelFrame(frames['other_tab'],          text='Misc',              labelanchor=NW)

    #Cosmetic tab
    frames['cosmetic']    = LabelFrame(frames['cosmetic_tab_left'],  text='General',           labelanchor=NW)
    frames['sword_trails']= LabelFrame(frames['cosmetic_tab_left'],  text='Sword Trail Colors',labelanchor=NW)
    frames['ui_colors']=    LabelFrame(frames['cosmetic_tab_left'], text='UI Colors',         labelanchor=NW)
    frames['tunic_colors']= LabelFrame(frames['cosmetic_tab_right'], text='Tunic Colors',      labelanchor=NW)
    frames['navi_colors']=  LabelFrame(frames['cosmetic_tab_right'], text='Navi Colors',       labelanchor=NW)
    frames['gauntlet_colors']= LabelFrame(frames['cosmetic_tab_right'], text='Gauntlet Colors', labelanchor=NW)

    #Cosmetic tab
    frames['sfx']         = LabelFrame(frames['SFX_tab'],            text='General',           labelanchor=NW)
    frames['menu_sfx']    = LabelFrame(frames['SFX_tab'],            text='Menu',              labelanchor=NW)
    frames['npc_sfx']     = LabelFrame(frames['SFX_tab'],            text='NPC',               labelanchor=NW)


    # Shared
    def toggle_widget(widget, enabled):
        widget_type = widget.winfo_class()
        if widget_type == 'Frame' or widget_type == 'TFrame' or widget_type == 'Labelframe':
            if widget_type == 'Labelframe':
                widget.configure(fg='Black'if enabled else 'Grey')
            for child in widget.winfo_children():
                toggle_widget(child, enabled)
        else:
            if widget_type == 'TCombobox':
                widget.configure(state= 'readonly' if enabled else 'disabled')
            else:
                widget.configure(state= 'normal' if enabled else 'disabled')

            if widget_type == 'Scale':
                widget.configure(fg='Black'if enabled else 'Grey')

    def show_settings(*event):
        settings = guivars_to_settings(guivars)
        settings_string_var.set( settings.get_settings_string() )

        # Update any dependencies
        for info in setting_infos:
            dep_met = settings.check_dependency(info.name)

            if info.name in widgets:
                toggle_widget(widgets[info.name], dep_met)

            if info.type == list:
                widgets[info.name].delete(0, END)
                widgets[info.name].insert(0, *guivars[info.name])

            if info.type != list and info.name in guivars and guivars[info.name].get() == 'Custom Color':
                color = colorchooser.askcolor()
                if color == (None, None):
                    color = ((0,0,0),'#000000')
                guivars[info.name].set('Custom (' + color[1] + ')')

            if info.type != list and info.name in guivars and guivars[info.name].get() == 'Custom Navi Color':
                innerColor = colorchooser.askcolor(title='Pick an Inner Core color.')
                if innerColor == (None, None):
                    innerColor = ((0,0,0),'#000000')
                outerColor = colorchooser.askcolor(title='Pick an Outer Glow color.')
                if outerColor == (None, None):
                    outerColor = ((0,0,0),'#000000')
                guivars[info.name].set('Custom (%s %s)' % (innerColor[1], outerColor[1]))

        update_generation_type()


    versionCheckFrame = Frame(frames['rom_tab'])
    versionCheckFrame.pack(side=BOTTOM, anchor=NW, fill=X)

    fileDialogFrame = Frame(frames['rom_tab'])

    romDialogFrame = Frame(fileDialogFrame)
    baseRomLabel = Label(romDialogFrame, text='Base ROM')
    guivars['rom'] = StringVar(value='')
    romEntry = Entry(romDialogFrame, textvariable=guivars['rom'], width=50)

    def RomSelect():
        rom = filedialog.askopenfilename(filetypes=[("ROM Files", (".z64", ".n64")), ("All Files", "*")])
        if rom != '':
            guivars['rom'].set(rom)
    romSelectButton = Button(romDialogFrame, text='Browse', command=RomSelect, width=10)

    baseRomLabel.pack(side=LEFT, padx=(34,0))
    romEntry.pack(side=LEFT, padx=3)
    romSelectButton.pack(side=LEFT)

    romDialogFrame.pack()

    fileDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(5,1))

    def output_dir_select():
        rom = filedialog.askdirectory(initialdir = default_output_path(guivars['output_dir'].get()))
        if rom != '':
            guivars['output_dir'].set(rom)

    outputDialogFrame = Frame(frames['rom_tab'])
    outputDirLabel = Label(outputDialogFrame, text='Output Directory')
    guivars['output_dir'] = StringVar(value='')
    outputDirEntry = Entry(outputDialogFrame, textvariable=guivars['output_dir'], width=50)
    outputDirButton = Button(outputDialogFrame, text='Browse', command=output_dir_select, width=10)
    outputDirLabel.pack(side=LEFT, padx=(3,0))
    outputDirEntry.pack(side=LEFT, padx=3)
    outputDirButton.pack(side=LEFT)
    outputDialogFrame.pack(side=TOP, anchor=W, pady=3)

    distFileDialogFrame = Frame(frames['rom_tab'])
    distFileLabel = Label(distFileDialogFrame, text='Distribution File')
    guivars['distribution_file'] = StringVar(value='')
    distFileEntry = Entry(distFileDialogFrame, textvariable=guivars['distribution_file'], width=50)

    def DistFileSelect():
        distFile = filedialog.askopenfilename(filetypes=[("JSON Files", (".json")), ("All Files", "*")])
        if distFile != '':
            guivars['distribution_file'].set(distFile)
    distFileSelectButton = Button(distFileDialogFrame, text='Browse', command=DistFileSelect, width=10)

    distFileLabel.pack(side=LEFT, padx=(9,0))
    distFileEntry.pack(side=LEFT, padx=3)
    distFileSelectButton.pack(side=LEFT)

    distFileDialogFrame.pack(side=TOP, anchor=W, pady=3)

    countDialogFrame = Frame(frames['rom_tab'])
    countLabel = Label(countDialogFrame, text='Generation Count')
    guivars['count'] = StringVar()
    widgets['count'] = Spinbox(countDialogFrame, from_=1, to=100, textvariable=guivars['count'], width=3)

    def open_readme():
        open_file('https://wiki.ootrandomizer.com/index.php?title=Main_Page')
    openReadmeButton = Button(countDialogFrame, text='Open Wiki Page', command=open_readme)
    openReadmeButton.pack(side=RIGHT, padx=5)

    def open_output():
        open_file(default_output_path(guivars['output_dir'].get()))
    openOutputButton = Button(countDialogFrame, text='Open Output Directory', command=open_output)
    openOutputButton.pack(side=RIGHT, padx=5)

    countLabel.pack(side=LEFT)
    widgets['count'].pack(side=LEFT, padx=2)
    countDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(1,1))

    # Build gui
    ############

    for info in setting_infos:
        if 'group' in info.gui_params:
            if info.gui_params['widget'] == 'Checkbutton':
                # Determine the initial value of the checkbox
                default_value = 1 if info.choices[info.default] == 'checked' else 0
                # Create a variable to access the box's state
                guivars[info.name] = IntVar(value=default_value)
                # Create the checkbox
                widgets[info.name] = Checkbutton(frames[info.gui_params['group']], text=info.gui_params['text'], variable=guivars[info.name], justify=LEFT, wraplength=220, command=show_settings)
                widgets[info.name].pack(expand=False, anchor=W)
            elif info.gui_params['widget'] == 'Combobox':
                # Create the variable to store the user's decision
                guivars[info.name] = StringVar(value=info.choices[info.default])
                # Create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])
                dropdown = ttk.Combobox(widgets[info.name], textvariable=guivars[info.name], values=list(map(lambda choice: info.choices[choice], info.choice_list)), state='readonly', width=36)
                dropdown.bind("<<ComboboxSelected>>", show_settings)
                dropdown.pack(side=BOTTOM, anchor=W)
                # Label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name], text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # Pack the frame
                widgets[info.name].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)
            elif info.gui_params['widget'] == 'Radiobutton':
                # Create the variable to store the user's decision
                guivars[info.name] = StringVar(value=info.choices[info.default])
                # Create the option menu
                widgets[info.name] = LabelFrame(frames[info.gui_params['group']], text=info.gui_params.get('text', info.name), labelanchor=NW)
                # Setup orientation
                side = TOP
                anchor = W
                if "horizontal" in info.gui_params and info.gui_params["horizontal"]:
                    side = LEFT
                    anchor = N
                # Add the radio buttons
                for option in map(lambda choice: info.choices[choice], info.choice_list):
                    radio_button = Radiobutton(widgets[info.name], text=option, value=option, variable=guivars[info.name], justify=LEFT, wraplength=220, indicatoron=False, command=show_settings)
                    radio_button.pack(expand=True, side=side, anchor=anchor)
                # Pack the frame
                widgets[info.name].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)
            elif info.gui_params['widget'] == 'Scale':
                # Create the variable to store the user's decision
                guivars[info.name] = IntVar(value=info.choices[info.default])
                # Create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])
                minval = info.gui_params.get('min', 0)
                maxval = info.gui_params.get('max', 100)
                stepval = info.gui_params.get('step', 1)
                scale = Scale(widgets[info.name], variable=guivars[info.name], from_=minval, to=maxval, tickinterval=stepval, resolution=stepval, showvalue=0, orient=HORIZONTAL, sliderlength=15, length=235, command=show_settings)
                scale.pack(side=BOTTOM, anchor=W)
                # Label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name], text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # Pack the frame
                widgets[info.name].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)
            elif info.gui_params['widget'] == 'Entry':
                # Create the variable to store the user's decision
                guivars[info.name] = StringVar(value=info.default)
                # Create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])

                if 'validate' in info.gui_params:
                    entry = ValidatingEntry(widgets[info.name], command=show_settings, validate=info.gui_params['validate'], textvariable=guivars[info.name], width=35)
                else:
                    entry = Entry(widgets[info.name], textvariable=guivars[info.name], width=36)
                entry.pack(side=BOTTOM, anchor=W)
                # Label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name], text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # Pack the frame
                widgets[info.name].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)
            elif info.gui_params['widget'] == 'SearchBox' or info.gui_params['widget'] == 'FilteredSearchBox':
                filtered = (info.gui_params['widget'] == 'FilteredSearchBox')
                search_frame = LabelFrame(frames[info.gui_params['group']], text=info.gui_params.get('text', info.name), labelanchor=NW)

                if filtered:
                    filter_frame = Frame(search_frame)
                    widgets[info.name + '_filterlabel'] = Label(filter_frame, text="Filter: ")
                    widgets[info.name + '_filterlabel'].pack(side=LEFT, anchor=W)
                    widgets[info.name + '_entry'] = SearchBox(search_frame, list(map(lambda choice: info.choices[choice], info.choice_list)), width=78)
                    widgets[info.name + '_filter'] = SearchBoxFilterControl(filter_frame, widgets[info.name + '_entry'], info.gui_params['filterdata'], width=50)
                    widgets[info.name + '_filter'].pack(expand=False, side=LEFT, anchor=W)
                    filter_frame.pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)
                    widgets[info.name + '_entry'].pack(expand=False, side=TOP, anchor=W)
                else:
                    widgets[info.name + '_entry'] = SearchBox(search_frame, list(map(lambda choice: info.choices[choice], info.choice_list)), width=78)
                    widgets[info.name + '_entry'].pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)

                list_frame = Frame(search_frame)
                scrollbar = Scrollbar(list_frame, orient=VERTICAL)
                widgets[info.name] = Listbox(list_frame, width=78, height=7, yscrollcommand=scrollbar.set)
                guivars[info.name] = list(info.default)
                scrollbar.config(command=widgets[info.name].yview)
                scrollbar.pack(side=RIGHT, fill=Y)
                widgets[info.name].pack(side=LEFT)
                list_frame.pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)

                if 'entry_tooltip' in info.gui_params:
                    ToolTips.register(widgets[info.name + '_entry'], info.gui_params['entry_tooltip'])
                    if filtered:
                        ToolTips.register(widgets[info.name + '_filter'], info.gui_params['entry_tooltip'])
                if 'list_tooltip' in info.gui_params:
                    ToolTips.register(widgets[info.name], info.gui_params['list_tooltip'])

                def get_lambda(function, *args):
                    return lambda: function(*args)

                def add_list_selected(name):
                    new_location = widgets[name +'_entry'].get()
                    if new_location in widgets[name +'_entry'].options and new_location not in widgets[name].get(0, END):
                        widgets[name].insert(END, new_location)
                        guivars[name].append(new_location)
                    show_settings()

                def remove_list_selected(name):
                    location = widgets[name].get(ACTIVE)
                    widgets[name].delete(ACTIVE)
                    guivars[name].remove(location)
                    show_settings()

                def add_list_all(name):
                    for new_location in widgets[name + '_entry'].options:
                        if new_location not in widgets[name].get(0, END):
                            widgets[name].insert(END, new_location)
                            guivars[name].append(new_location)
                    show_settings()

                def remove_list_all(name):
                    items = list(widgets[name].get(0, END))
                    widgets[name].delete(0, END)
                    guivars[name] = []
                    for item in (x for x in items if x not in widgets[name + '_entry'].options):
                        widgets[name].insert(END, item)
                        guivars[name].append(item)
                    show_settings()
                    
                def clear_list_all(name):
                    widgets[name].delete(0, END)
                    guivars[name] = []
                    show_settings()

                list_button_frame = Frame(search_frame)
                list_add = Button(list_button_frame, width=10, text='Add', command=get_lambda(add_list_selected, info.name))
                list_add.pack(side=LEFT, anchor=N, padx=3, pady=3)
                list_remove = Button(list_button_frame, width=10, text='Remove', command=get_lambda(remove_list_selected, info.name))
                list_remove.pack(side=LEFT, anchor=N, padx=3, pady=3)

                list_add = Button(list_button_frame, width=10, text='All', command=get_lambda(add_list_all, info.name))
                list_add.pack(side=LEFT, anchor=N, padx=3, pady=3)
                list_remove = Button(list_button_frame, width=10, text='None', command=get_lambda(remove_list_all, info.name))
                list_remove.pack(side=LEFT, anchor=N, padx=3, pady=3)
                if filtered:
                    list_clear = Button(list_button_frame, width=10, text='Clear', command=get_lambda(clear_list_all, info.name))
                    list_clear.pack(side=LEFT, anchor=N, padx=3, pady=3)

                list_button_frame.pack(expand=False, side=TOP, padx=3, pady=3)

                # pack the frame
                search_frame.pack(expand=False, side=TOP, anchor=W, padx=3, pady=3)


            if 'tooltip' in info.gui_params:
                ToolTips.register(widgets[info.name], info.gui_params['tooltip'])


    # Pack the hierarchy
    frames['shuffle'].pack(fill=BOTH,  expand=True, anchor=N, side=RIGHT,  pady=(5,1))
    frames['open'].pack(   fill=BOTH,  expand=True, anchor=W, side=TOP,    pady=(5,1))
    frames['world'].pack(  fill=BOTH,  expand=True, anchor=W, side=BOTTOM, pady=(5,1))

    # Logic tab
    frames['checks'].pack(fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1))
    frames['tricks'].pack(fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1))

    # Other Tab
    frames['convenience'].pack(fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1))
    frames['other'].pack(      fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1))

    # Cosmetics tab
    frames['cosmetic'].pack(          fill=BOTH, expand=True, anchor=W, side=TOP)
    frames['cosmetic_tab_left'].pack( fill=BOTH, expand=True, anchor=W, side=LEFT)
    frames['cosmetic_tab_right'].pack(fill=BOTH, expand=True, anchor=W, side=RIGHT)

    # Cosmetics tab - Left Side
    frames['sword_trails'].pack(   fill=BOTH, expand=True, anchor=W, side=TOP)
    frames['ui_colors'].pack(      fill=BOTH, expand=True, anchor=W, side=BOTTOM)

    # Cosmetics tab - Right Side
    frames['tunic_colors'].pack(fill=BOTH, expand=True, anchor=N, side=TOP)
    frames['navi_colors'].pack( fill=BOTH, expand=True, anchor=W, side=TOP)
    frames['gauntlet_colors'].pack(fill=BOTH, expand=True, anchor=W, side=BOTTOM)


    #SFX tab
    frames['sfx'].pack(          fill=BOTH, expand=True, anchor=N, side=LEFT, pady=(5,1))
    frames['menu_sfx'].pack( fill=BOTH, expand=False, anchor=W, side=TOP, pady=(5,1))
    frames['npc_sfx'].pack(fill=BOTH, expand=True, anchor=W, side=BOTTOM, pady=(5,1))

    notebook.pack(fill=BOTH, expand=True, padx=5, pady=5)


    #Multi-World
    widgets['multiworld'] = LabelFrame(frames['rom_tab'], text='Multi-World Generation')
    countLabel = Label(
            widgets['multiworld'],
            wraplength=250,
            justify=LEFT,
            text='''This is used for co-op generations. \
                    Increasing Player Count will drastically \
                    increase the generation time. \
                    \nFor more information, see: \
                 '''
            )
    hyperLabel = Label(widgets['multiworld'], wraplength=250, justify=LEFT, text='https://github.com/TestRunnerSRL/\nbizhawk-co-op\n', fg='blue', cursor='hand2')
    hyperLabel.bind("<Button-1>", lambda event: webbrowser.open_new(r"https://github.com/TestRunnerSRL/bizhawk-co-op"))
    countLabel.pack(side=TOP, anchor=W, padx=5, pady=0)
    hyperLabel.pack(side=TOP, anchor=W, padx=5, pady=0)

    worldCountFrame = Frame(widgets['multiworld'])
    countLabel = Label(worldCountFrame, text='Player Count')
    guivars['world_count'] = StringVar()
    widgets['world_count'] = Spinbox(worldCountFrame, from_=1, to=255, textvariable=guivars['world_count'], width=3)
    guivars['world_count'].trace('w', show_settings)
    countLabel.pack(side=LEFT)
    widgets['world_count'].pack(side=LEFT, padx=2)
    worldCountFrame.pack(side=LEFT, anchor=N, padx=10, pady=(1,5))

    playerNumFrame = Frame(widgets['multiworld'])
    countLabel = Label(playerNumFrame, text='Player ID')
    guivars['player_num'] = StringVar()
    widgets['player_num'] = Spinbox(playerNumFrame, from_=1, to=255, textvariable=guivars['player_num'], width=3)
    countLabel.pack(side=LEFT)
    widgets['player_num'].pack(side=LEFT, padx=2)
    ToolTips.register(widgets['player_num'], 'Generate for specific Player.')
    playerNumFrame.pack(side=LEFT, anchor=N, padx=10, pady=(1,5))

    widgets['multiworld'].pack(side=LEFT, fill=BOTH, anchor=NW, padx=5, pady=5)


    # Settings Presets Functions
    def import_setting_preset():
        if guivars['settings_preset'].get() == '[New Preset]':
            messagebox.showerror("Invalid Preset", "You must select an existing preset!")
            return

        # Get cosmetic settings
        old_settings = guivars_to_settings(guivars)
        new_settings = {setting.name: old_settings.__dict__[setting.name] for setting in
                            filter(lambda s: not (s.shared and s.bitwidth > 0), setting_infos)}

        preset = presets[guivars['settings_preset'].get()]
        new_settings.update(preset)

        settings = Settings(new_settings)
        settings.seed = guivars['seed'].get()

        settings_to_guivars(settings, guivars)
        show_settings()


    def add_settings_preset():
        preset_name = guivars['settings_preset'].get()
        if preset_name == '[New Preset]':
            preset_name = simpledialog.askstring("New Preset", "Enter a new preset name:")
            if not preset_name or preset_name in presets or preset_name == '[New Preset]':
                messagebox.showerror("Invalid Preset", "You must enter a new preset name!")
                return
        elif presets[preset_name].get('locked', False):
            messagebox.showerror("Invalid Preset", "You cannot modify a locked preset!")
            return
        else:
            if messagebox.askquestion("Overwrite Preset", 'Are you sure you want to overwrite the "%s" preset?' % preset_name) != 'yes':
                return

        settings = guivars_to_settings(guivars)
        preset = {setting.name: settings.__dict__[setting.name] for setting in
            filter(lambda s: s.shared and s.bitwidth > 0, setting_infos)}

        presets[preset_name] = preset
        guivars['settings_preset'].set(preset_name)
        update_preset_dropdown()
        save_presets()


    def remove_setting_preset():
        preset_name = guivars['settings_preset'].get()
        if preset_name == '[New Preset]':
            messagebox.showerror("Invalid Preset", "You must select an existing preset!")
            return
        elif presets[preset_name].get('locked', False):
            messagebox.showerror("Invalid Preset", "You cannot modify a locked preset!")
            return

        confirm = messagebox.askquestion('Remove Setting Preset', 'Are you sure you want to remove the setting preset "%s"?' % preset_name)
        if confirm != 'yes':
            return

        del presets[preset_name]
        guivars['settings_preset'].set('[New Preset]')
        update_preset_dropdown()
        save_presets()


    def update_preset_dropdown():
        widgets['settings_preset']['values'] = ['[New Preset]'] + list(presets.keys())


    def save_presets():
        presets_file = local_path('presets.sav')
        with open(presets_file, 'w') as outfile:
            preset_json = {name: preset for name,preset in presets.items() if not preset.get('locked')}
            json.dump(preset_json, outfile, indent=4)


    # Settings Presets
    widgets['settings_presets'] = LabelFrame(frames['rom_tab'], text='Settings Presets')
    countLabel = Label(
            widgets['settings_presets'],
            wraplength=250,
            justify=LEFT,
            text='''Presets are settings that can be saved\
                    and loaded from. Loading a preset\
                    will overwrite all settings that affect\
                    the seed.\
                    \n\
                 '''
            )
    countLabel.pack(side=TOP, anchor=W, padx=5, pady=0)

    selectPresetFrame = Frame(widgets['settings_presets'])
    guivars['settings_preset'] = StringVar(value='[New Preset]')
    widgets['settings_preset'] = ttk.Combobox(selectPresetFrame, textvariable=guivars['settings_preset'], values=['[New Preset]'], state='readonly', width=33)
    widgets['settings_preset'].pack(side=BOTTOM, anchor=W)
    ToolTips.register(widgets['settings_preset'], 'Select a setting preset to apply.')
    widgets['settings_preset'].pack(side=LEFT, padx=(5, 0))
    selectPresetFrame.pack(side=TOP, anchor=W, padx=5, pady=(1,5))

    buttonPresetFrame = Frame(widgets['settings_presets'])
    importPresetButton = Button(buttonPresetFrame, text='Load', width=9, command=import_setting_preset)
    addPresetButton = Button(buttonPresetFrame, text='Save', width=9, command=add_settings_preset)
    removePresetButton = Button(buttonPresetFrame, text='Remove', width=9, command=remove_setting_preset)
    importPresetButton.pack(side=LEFT, anchor=W, padx=2)
    addPresetButton.pack(side=LEFT, anchor=W, padx=2)
    removePresetButton.pack(side=LEFT, anchor=W, padx=2)
    buttonPresetFrame.pack(side=TOP, anchor=W, padx=5, pady=(1,5))

    widgets['settings_presets'].pack(side=RIGHT, fill=BOTH, anchor=NW, padx=5, pady=5)


    # Create the generation menu
    def update_generation_type(event=None):
        settings = guivars_to_settings(guivars)
        if generation_notebook.tab(generation_notebook.select())['text'] == 'Generate From Seed':
            notebook.tab(1, state="normal")
            if guivars['logic_rules'].get() == 'Glitchless':
                notebook.tab(2, state="normal")
            else:
                notebook.tab(2, state="disabled")
            notebook.tab(3, state="normal")
            notebook.tab(4, state="normal")
            notebook.tab(5, state="normal")
            toggle_widget(widgets['world_count'], settings.check_dependency('world_count'))
            toggle_widget(widgets['create_spoiler'], settings.check_dependency('create_spoiler'))
            toggle_widget(widgets['count'], settings.check_dependency('count'))
            toggle_widget(widgets['settings_presets'], True)
        else:
            notebook.tab(1, state="disabled")
            notebook.tab(2, state="disabled")
            notebook.tab(3, state="disabled")
            if guivars['repatch_cosmetics'].get():
                notebook.tab(4, state="normal")
                notebook.tab(5, state="normal")
                toggle_widget(widgets['create_cosmetics_log'], settings.check_dependency('create_cosmetics_log'))
            else:
                notebook.tab(4, state="disabled")
                notebook.tab(5, state="disabled")
                toggle_widget(widgets['create_cosmetics_log'], False)
            toggle_widget(widgets['world_count'], False)
            toggle_widget(widgets['create_spoiler'], False)
            toggle_widget(widgets['count'], False)
            toggle_widget(widgets['settings_presets'], False)



    generation_notebook = ttk.Notebook(mainWindow)
    frames['gen_from_seed'] = ttk.Frame(generation_notebook)
    frames['gen_from_file'] = ttk.Frame(generation_notebook)
    generation_notebook.add(frames['gen_from_seed'], text='Generate From Seed')
    generation_notebook.add(frames['gen_from_file'], text='Generate From File')
    generation_notebook.bind("<<NotebookTabChanged>>", show_settings)

    # From seed tab
    def import_settings(event=None):
        try:
            settings = guivars_to_settings(guivars)
            text = settings_string_var.get().upper()
            settings.seed = guivars['seed'].get()
            settings.update_with_settings_string(text)
            settings_to_guivars(settings, guivars)
            show_settings()
        except Exception as e:
            messagebox.showerror(title="Error", message="Invalid settings string")

    def copy_settings(event=None):
        mainWindow.clipboard_clear()
        new_clip = settings_string_var.get().upper()
        mainWindow.clipboard_append(new_clip)
        mainWindow.update()

    settingsFrame = Frame(frames['gen_from_seed'])
    settings_string_var = StringVar()
    widgets['setting_string'] = Entry(settingsFrame, textvariable=settings_string_var, width=44)

    label = Label(settingsFrame, text="Settings String", width=13, anchor=E)
    widgets['copy_settings'] = Button(settingsFrame, text='Copy', width=5, command=copy_settings)
    widgets['import_settings'] = Button(settingsFrame, text='Import', width=7, command=import_settings)
    label.pack(side=LEFT, anchor=W, padx=5)
    widgets['setting_string'].pack(side=LEFT, anchor=W)
    widgets['copy_settings'].pack(side=LEFT, anchor=W, padx=(5, 0))
    widgets['import_settings'].pack(side=LEFT, anchor=W)

    settingsFrame.pack(fill=BOTH, anchor=W, padx=5, pady=(10,0))

    def multiple_run(settings, window):
        orig_seed = settings.seed
        for i in range(settings.count):
            settings.update_seed(orig_seed + '-' + str(i))
            window.update_title("Generating Seed %s...%d/%d" % (settings.seed, i+1, settings.count))
            main(settings, window)

    def generateRom():
        settings = guivars_to_settings(guivars)
        if settings.count:
            BackgroundTaskProgress(mainWindow, "Generating Seed %s..." % settings.seed, multiple_run, settings)
        else:
            BackgroundTaskProgress(mainWindow, "Generating Seed %s..." % settings.seed, main, settings)

    generateSeedFrame = Frame(frames['gen_from_seed'])
    seedLabel = Label(generateSeedFrame, text='Seed', width=13, anchor=E)
    generateButton = Button(generateSeedFrame, text='Generate!', width=14, command=generateRom)
    guivars['seed'] = StringVar()
    widgets['seed'] = Entry(generateSeedFrame, textvariable=guivars['seed'], width=44)
    seedLabel.pack(side=LEFT, padx=5)
    widgets['seed'].pack(side=LEFT)
    generateButton.pack(side=LEFT, padx=(5, 0))

    generateSeedFrame.pack(side=BOTTOM, anchor=W, padx=5, pady=10)

    # From file tab
    patchDialogFrame = Frame(frames['gen_from_file'])

    patchFileLabel = Label(patchDialogFrame, text='Patch File')
    guivars['patch_file'] = StringVar(value='')
    patchEntry = Entry(patchDialogFrame, textvariable=guivars['patch_file'], width=52)

    def PatchSelect():
        patch_file = filedialog.askopenfilename(filetypes=[("Patch File Archive", "*.zpfz *.zpf"), ("All Files", "*")])
        if patch_file != '':
            guivars['patch_file'].set(patch_file)
    patchSelectButton = Button(patchDialogFrame, text='Select File', command=PatchSelect, width=14)

    patchFileLabel.pack(side=LEFT, padx=(5,0))
    patchEntry.pack(side=LEFT, padx=3)
    patchSelectButton.pack(side=LEFT)

    patchDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(10,5))

    def generateFromFile():
        settings = guivars_to_settings(guivars)
        BackgroundTaskProgress(mainWindow, "Generating From File %s..." % os.path.basename(settings.patch_file), from_patch_file, settings)

    patchCosmeticsAndGenerateFrame = Frame(frames['gen_from_file'])
    guivars['repatch_cosmetics'] = IntVar()
    widgets['repatch_cosmetics'] = Checkbutton(patchCosmeticsAndGenerateFrame, text='Update Cosmetics', variable=guivars['repatch_cosmetics'], justify=LEFT, wraplength=220, command=show_settings)
    widgets['repatch_cosmetics'].pack(side=LEFT, padx=5, anchor=W)

    generateFileButton = Button(patchCosmeticsAndGenerateFrame, text='Generate!', width=14, command=generateFromFile)
    generateFileButton.pack(side=RIGHT, anchor=E)
    patchCosmeticsAndGenerateFrame.pack(side=BOTTOM, fill=BOTH, expand=True, pady=(0,10), padx=(0, 10))

    generation_notebook.pack(fill=BOTH, expand=True, padx=5, pady=5)


    guivars['checked_version'] = StringVar()
    guivars['cosmetics_only'] = IntVar()

    if settings is not None:
        # Load values from commandline args
        settings_to_guivars(settings, guivars)
    else:
        # Try to load saved settings
        settingsFile = local_path('settings.sav')
        try:
            with open(settingsFile) as f:
                settings = Settings( json.load(f) )
        except:
            settings = Settings({})
        settings_to_guivars(settings, guivars)
        guivars['seed'].set("")

        presets = {}
        for file in [data_path('presets_default.json')] \
                  + [local_path(f) for f in os.listdir(local_path()) if f.startswith('presets_') and f.endswith('.sav')] \
                  + [local_path('presets.sav')]:
            try:
                with open(file) as f:
                    presets_temp = json.load(f)
                    if file != local_path('presets.sav'):
                        for preset in presets_temp.values():
                            preset['locked'] = True
                    presets.update(presets_temp)
            except:
                pass
        update_preset_dropdown()

    show_settings()

    def gui_check_version():
        task = BackgroundTask(mainWindow, check_version, guivars['checked_version'].get())
        while task.running:
            mainWindow.update()

        if task.status:
            versionCheckLabel = LabelFrame(versionCheckFrame, text="New Version Available!")
            versionCheckText = Label(versionCheckLabel, justify=LEFT, text=task.status[(task.status.find(' ')+1):])
            versionCheckLink = Label(versionCheckLabel, justify=LEFT, text='Click here and download the current version.', fg='blue', cursor='hand2')
            versionCheckLink.bind("<Button-1>", lambda event: webbrowser.open_new(r"https://github.com/TestRunnerSRL/OoT-Randomizer/tree/Dev"))
            versionCheckText.pack(anchor=NW, padx=5, pady=0)
            versionCheckLink.pack(anchor=NW, padx=5, pady=0)
            versionCheckLabel.pack(anchor=NW, fill=X, expand="yes", padx=5, pady=5)

    mainWindow.after(1000, gui_check_version)
    mainWindow.mainloop()

    # Save settings on close
    settings_file = local_path('settings.sav')
    with open(settings_file, 'w') as outfile:
        settings = guivars_to_settings(guivars)
        del settings.__dict__["distribution"]
        del settings.__dict__["seed"]
        del settings.__dict__["numeric_seed"]
        del settings.__dict__["check_version"]
        if "locked" in settings.__dict__:
            del settings.__dict__["locked"]
        json.dump(settings.__dict__, outfile, indent=4)
    
    save_presets()
Exemple #20
0
def set_icon(window):
    logo = tk.PhotoImage(file=local_path('data', 'icon.png'))
    window.tk.call('wm', 'iconphoto', window._w, logo)
Exemple #21
0
def guiMain(args=None):
    mainWindow = Tk()
    mainWindow.wm_title("OoT Randomizer %s" % ESVersion)

    set_icon(mainWindow)

    notebook = ttk.Notebook(mainWindow)
    randomizerWindow = ttk.Frame(notebook)
    adjustWindow = ttk.Frame(notebook)
    customWindow = ttk.Frame(notebook)
    notebook.add(randomizerWindow, text='Randomize')
    notebook.pack()

    # Shared Controls

    farBottomFrame = Frame(mainWindow)

    def open_output():
        open_file(output_path(''))

    openOutputButton = Button(farBottomFrame,
                              text='Open Output Directory',
                              command=open_output)

    if os.path.exists(local_path('README.html')):

        def open_readme():
            open_file(local_path('README.html'))

        openReadmeButton = Button(farBottomFrame,
                                  text='Open Documentation',
                                  command=open_readme)
        openReadmeButton.pack(side=LEFT)

    farBottomFrame.pack(side=BOTTOM, fill=X, padx=5, pady=5)

    # randomizer controls

    topFrame = Frame(randomizerWindow)
    rightHalfFrame = Frame(topFrame)
    checkBoxFrame = Frame(rightHalfFrame)

    createSpoilerVar = IntVar()
    createSpoilerCheckbutton = Checkbutton(
        checkBoxFrame,
        text="Create Spoiler Log (affects item layout)",
        variable=createSpoilerVar)
    suppressRomVar = IntVar()
    suppressRomCheckbutton = Checkbutton(checkBoxFrame,
                                         text="Do not create patched Rom",
                                         variable=suppressRomVar)
    compressRomVar = IntVar()
    compressRomCheckbutton = Checkbutton(checkBoxFrame,
                                         text="Compress patched Rom",
                                         variable=compressRomVar)
    openForestVar = IntVar()
    openForestCheckbutton = Checkbutton(checkBoxFrame,
                                        text="Open Forest",
                                        variable=openForestVar)
    openDoorVar = IntVar()
    openDoorCheckbutton = Checkbutton(checkBoxFrame,
                                      text="Open Door of Time",
                                      variable=openDoorVar)
    fastGanonVar = IntVar()
    fastGanonCheckbutton = Checkbutton(checkBoxFrame,
                                       text="Skip most of Ganon's Castle",
                                       variable=fastGanonVar)
    dungeonItemsVar = IntVar()
    dungeonItemsCheckbutton = Checkbutton(
        checkBoxFrame,
        text="Place Dungeon Items (Compasses/Maps)",
        onvalue=0,
        offvalue=1,
        variable=dungeonItemsVar)
    beatableOnlyVar = IntVar()
    beatableOnlyCheckbutton = Checkbutton(
        checkBoxFrame,
        text="Only ensure seed is beatable, not all items must be reachable",
        variable=beatableOnlyVar)
    hintsVar = IntVar()
    hintsCheckbutton = Checkbutton(
        checkBoxFrame,
        text="Gossip Stone Hints with Stone of Agony",
        variable=hintsVar)

    createSpoilerCheckbutton.pack(expand=True, anchor=W)
    suppressRomCheckbutton.pack(expand=True, anchor=W)
    compressRomCheckbutton.pack(expand=True, anchor=W)
    openForestCheckbutton.pack(expand=True, anchor=W)
    openDoorCheckbutton.pack(expand=True, anchor=W)
    fastGanonCheckbutton.pack(expand=True, anchor=W)
    dungeonItemsCheckbutton.pack(expand=True, anchor=W)
    beatableOnlyCheckbutton.pack(expand=True, anchor=W)
    hintsCheckbutton.pack(expand=True, anchor=W)

    fileDialogFrame = Frame(rightHalfFrame)

    romDialogFrame = Frame(fileDialogFrame)
    baseRomLabel = Label(romDialogFrame, text='Base Rom')
    romVar = StringVar()
    romEntry = Entry(romDialogFrame, textvariable=romVar)

    def RomSelect():
        rom = filedialog.askopenfilename(
            filetypes=[("Rom Files", (".z64", ".n64")), ("All Files", "*")])
        romVar.set(rom)

    romSelectButton = Button(romDialogFrame,
                             text='Select Rom',
                             command=RomSelect)

    baseRomLabel.pack(side=LEFT)
    romEntry.pack(side=LEFT)
    romSelectButton.pack(side=LEFT)

    romDialogFrame.pack()

    checkBoxFrame.pack()
    fileDialogFrame.pack()

    dropDownFrame = Frame(topFrame)

    bridgeFrame = Frame(dropDownFrame)
    bridgeVar = StringVar()
    bridgeVar.set('medallions')
    bridgeOptionMenu = OptionMenu(bridgeFrame, bridgeVar, 'medallions',
                                  'vanilla', 'dungeons', 'open')
    bridgeOptionMenu.pack(side=RIGHT)
    bridgeLabel = Label(bridgeFrame, text='Rainbow Bridge Requirement')
    bridgeLabel.pack(side=LEFT)

    colorVars = []
    colorVars.append(StringVar())
    colorVars.append(StringVar())
    colorVars.append(StringVar())
    colorVars[0].set('Kokiri Green')
    colorVars[1].set('Goron Red')
    colorVars[2].set('Zora Blue')

    kokiriFrame = Frame(dropDownFrame)
    kokiriOptionMenu = OptionMenu(kokiriFrame, colorVars[0], 'Kokiri Green',
                                  'Goron Red', 'Zora Blue', 'Black', 'White',
                                  'Purple', 'Yellow', 'Orange', 'Pink', 'Gray',
                                  'Brown', 'Gold', 'Silver', 'Beige', 'Teal',
                                  'Royal Blue', 'Sonic Blue', 'Blood Red',
                                  'Blood Orange', 'NES Green', 'Dark Green',
                                  'Random', 'True Random')
    kokiriOptionMenu.pack(side=RIGHT)
    kokiriLabel = Label(kokiriFrame, text='Kokiri Tunic Color')
    kokiriLabel.pack(side=LEFT)

    goronFrame = Frame(dropDownFrame)
    goronOptionMenu = OptionMenu(goronFrame, colorVars[1], 'Kokiri Green',
                                 'Goron Red', 'Zora Blue', 'Black', 'White',
                                 'Purple', 'Yellow', 'Orange', 'Pink', 'Gray',
                                 'Brown', 'Gold', 'Silver', 'Beige', 'Teal',
                                 'Royal Blue', 'Sonic Blue', 'Blood Red',
                                 'Blood Orange', 'NES Green', 'Dark Green',
                                 'Random', 'True Random')
    goronOptionMenu.pack(side=RIGHT)
    goronLabel = Label(goronFrame, text='Goron Tunic Color')
    goronLabel.pack(side=LEFT)

    zoraFrame = Frame(dropDownFrame)
    zoraOptionMenu = OptionMenu(zoraFrame, colorVars[2], 'Kokiri Green',
                                'Goron Red', 'Zora Blue', 'Black', 'White',
                                'Purple', 'Yellow', 'Orange', 'Pink', 'Gray',
                                'Brown', 'Gold', 'Silver', 'Beige', 'Teal',
                                'Royal Blue', 'Sonic Blue', 'Blood Red',
                                'Blood Orange', 'NES Green', 'Dark Green',
                                'Random', 'True Random')
    zoraOptionMenu.pack(side=RIGHT)
    zoraLabel = Label(zoraFrame, text='Zora Tunic Color')
    zoraLabel.pack(side=LEFT)

    lowHealthSFXVar = StringVar()
    lowHealthSFXVar.set('Default')

    lowHealthSFXFrame = Frame(dropDownFrame)
    lowHealthSFXOptionMenu = OptionMenu(lowHealthSFXFrame, lowHealthSFXVar,
                                        'Default', 'Softer Beep', 'Rupee',
                                        'Timer', 'Tamborine', 'Recovery Heart',
                                        'Carrot Refill', 'Navi - Hey!',
                                        'Zelda - Gasp', 'Cluck', 'Mweep!',
                                        'Random', 'None')
    lowHealthSFXOptionMenu.pack(side=RIGHT)
    lowHealthSFXLabel = Label(lowHealthSFXFrame, text='Low Health SFX')
    lowHealthSFXLabel.pack(side=LEFT)

    bridgeFrame.pack(expand=True, anchor=E)
    kokiriFrame.pack(expand=True, anchor=E)
    goronFrame.pack(expand=True, anchor=E)
    zoraFrame.pack(expand=True, anchor=E)
    lowHealthSFXFrame.pack(expand=True, anchor=E)

    bottomFrame = Frame(randomizerWindow)

    seedLabel = Label(bottomFrame, text='Seed #')
    seedVar = StringVar()
    seedEntry = Entry(bottomFrame, textvariable=seedVar)
    countLabel = Label(bottomFrame, text='Count')
    countVar = StringVar()
    countSpinbox = Spinbox(bottomFrame, from_=1, to=100, textvariable=countVar)

    def generateRom():
        guiargs = Namespace
        guiargs.seed = int(seedVar.get()) if seedVar.get() else None
        guiargs.count = int(countVar.get()) if countVar.get() != '1' else None
        guiargs.bridge = bridgeVar.get()
        guiargs.kokiricolor = colorVars[0].get()
        guiargs.goroncolor = colorVars[1].get()
        guiargs.zoracolor = colorVars[2].get()
        guiargs.healthSFX = lowHealthSFXVar.get()
        guiargs.create_spoiler = bool(createSpoilerVar.get())
        guiargs.suppress_rom = bool(suppressRomVar.get())
        guiargs.compress_rom = bool(compressRomVar.get())
        guiargs.open_forest = bool(openForestVar.get())
        guiargs.open_door_of_time = bool(openDoorVar.get())
        guiargs.fast_ganon = bool(fastGanonVar.get())
        guiargs.nodungeonitems = bool(dungeonItemsVar.get())
        guiargs.beatableonly = bool(beatableOnlyVar.get())
        guiargs.hints = bool(hintsVar.get())
        guiargs.rom = romVar.get()
        try:
            if guiargs.count is not None:
                seed = guiargs.seed
                for _ in range(guiargs.count):
                    main(seed=seed, args=guiargs)
                    seed = random.randint(0, 999999999)
            else:
                main(seed=guiargs.seed, args=guiargs)
        except Exception as e:
            messagebox.showerror(title="Error while creating seed",
                                 message=str(e))
        else:
            messagebox.showinfo(title="Success",
                                message="Rom patched successfully")

    generateButton = Button(bottomFrame,
                            text='Generate Patched Rom',
                            command=generateRom)

    seedLabel.pack(side=LEFT)
    seedEntry.pack(side=LEFT)
    countLabel.pack(side=LEFT, padx=(5, 0))
    countSpinbox.pack(side=LEFT)
    generateButton.pack(side=LEFT, padx=(5, 0))

    openOutputButton.pack(side=RIGHT)

    dropDownFrame.pack(side=LEFT)
    rightHalfFrame.pack(side=RIGHT)
    topFrame.pack(side=TOP)
    bottomFrame.pack(side=BOTTOM)

    if args is not None:
        # load values from commandline args
        createSpoilerVar.set(int(args.create_spoiler))
        suppressRomVar.set(int(args.suppress_rom))
        compressRomVar.set(int(args.compress_rom))
        if args.nodungeonitems:
            dungeonItemsVar.set(int(not args.nodungeonitems))
        openForestVar.set(int(args.open_forest))
        openDoorVar.set(int(args.open_door_of_time))
        fastGanonVar.set(int(args.fast_ganon))
        beatableOnlyVar.set(int(args.beatableonly))
        hintsVar.set(int(args.hints))
        if args.count:
            countVar.set(str(args.count))
        if args.seed:
            seedVar.set(str(args.seed))
        bridgeVar.set(args.bridge)
        colorVars[0].set(args.kokiricolor)
        colorVars[1].set(args.goroncolor)
        colorVars[2].set(args.zoracolor)
        lowHealthSFXVar.set(args.healthSFX)
        romVar.set(args.rom)

    mainWindow.mainloop()
Exemple #22
0
def get_settings_from_command_line_args():
    parser = argparse.ArgumentParser(
        formatter_class=ArgumentDefaultsHelpFormatter)

    parser.add_argument('--gui', help='Launch the GUI', action='store_true')
    parser.add_argument('--loglevel',
                        default='info',
                        const='info',
                        nargs='?',
                        choices=['error', 'info', 'warning', 'debug'],
                        help='Select level of logging for output.')
    parser.add_argument(
        '--settings_string',
        help=
        'Provide sharable settings using a settings string. This will override all flags that it specifies.'
    )
    parser.add_argument(
        '--convert_settings',
        help=
        'Only convert the specified settings to a settings string. If a settings string is specified output the used settings instead.',
        action='store_true')
    parser.add_argument(
        '--settings',
        help='Use the specified settings file to use for generation')
    parser.add_argument(
        '--settings_preset',
        help=
        "Use the given preset for base settings. Anything defined in the --settings file or the --settings_string will override the preset."
    )
    parser.add_argument('--seed', help='Generate the specified seed.')
    parser.add_argument('--no_log',
                        help='Suppresses the generation of a log file.',
                        action='store_true')
    parser.add_argument(
        '--output_settings',
        help=
        'Always outputs a settings.json file even when spoiler is enabled.',
        action='store_true')

    args = parser.parse_args()
    settings_base = {}
    if args.settings_preset:
        presetsFiles = [data_path('presets_default.json')] + sorted(
            os.path.join(data_path('Presets'), fn)
            for fn in os.listdir(data_path('Presets')) if fn.endswith('.json'))
        for fn in presetsFiles:
            with open(fn, encoding='utf-8') as f:
                presets = json.load(f)
                if args.settings_preset in presets:
                    settings_base.update(presets[args.settings_preset])
                    break
        else:
            sys.stderr.write(
                f'ERROR:No preset found with name {args.settings_preset!r}\n')
            sys.exit(1)

    if args.settings == '-':
        settings_base.update(json.loads(sys.stdin.read()))
    elif args.settings or not settings_base:  # avoid implicitly using settings.sav with presets
        settingsFile = local_path(args.settings or 'settings.sav')

        try:
            with open(settingsFile, encoding='utf-8') as f:
                settings_base.update(json.load(f))
        except Exception as ex:
            if args.settings is not None:
                raise ex

    settings = Settings(settings_base)

    settings.output_settings = args.output_settings

    if args.settings_string is not None:
        settings.update_with_settings_string(args.settings_string)

    if args.seed is not None:
        settings.update_seed(args.seed)

    if args.convert_settings:
        if args.settings_string is not None:
            print(json.dumps(settings.to_json()))
        else:
            print(settings.get_settings_string())
        sys.exit(0)

    return settings, args.gui, args.loglevel, args.no_log
Exemple #23
0
 def open_readme():
     open_file(local_path('README.html'))
Exemple #24
0
def set_icon(window):
    er16 = tk.PhotoImage(file=local_path('data/oot16.gif'))
    er32 = tk.PhotoImage(file=local_path('data/oot32.gif'))
    er48 = tk.PhotoImage(file=local_path('data/oot32.gif'))
    window.tk.call('wm', 'iconphoto', window._w, er16, er32, er48)  # pylint: disable=protected-access
Exemple #25
0
def guiMain(settings=None):
    frames = {}

    mainWindow = Tk()
    mainWindow.wm_title("OoT Randomizer %s" % ESVersion)
    mainWindow.resizable(False, False)

    set_icon(mainWindow)

    notebook = ttk.Notebook(mainWindow)
    frames['rom_tab'] = ttk.Frame(notebook)
    frames['rules_tab'] = ttk.Frame(notebook)
    frames['logic_tab'] = ttk.Frame(notebook)
    frames['other_tab'] = ttk.Frame(notebook)
    frames['aesthetic_tab'] = ttk.Frame(notebook)
    frames['aesthetic_tab_left'] = Frame(frames['aesthetic_tab'])
    frames['aesthetic_tab_right'] = Frame(frames['aesthetic_tab'])
    adjustWindow = ttk.Frame(notebook)
    customWindow = ttk.Frame(notebook)
    notebook.add(frames['rom_tab'], text='ROM Options')
    notebook.add(frames['rules_tab'], text='Main Rules')
    notebook.add(frames['logic_tab'], text='Detailed Logic')
    notebook.add(frames['other_tab'], text='Other')
    notebook.add(frames['aesthetic_tab'], text='Cosmetics')

    #######################
    # randomizer controls #
    #######################

    # hold the results of the user's decisions here
    guivars = {}

    # hierarchy
    ############

    #Rules Tab
    frames['open'] = LabelFrame(frames['rules_tab'],
                                text='Open',
                                labelanchor=NW)
    frames['world'] = LabelFrame(frames['rules_tab'],
                                 text='World',
                                 labelanchor=NW)
    frames['logic'] = LabelFrame(frames['rules_tab'],
                                 text='Shuffle',
                                 labelanchor=NW)

    # Logic tab
    frames['rewards'] = LabelFrame(frames['logic_tab'],
                                   text='Remove Specific Locations',
                                   labelanchor=NW)
    frames['tricks'] = LabelFrame(frames['logic_tab'],
                                  text='Specific expected tricks',
                                  labelanchor=NW)

    #Other Tab
    frames['convenience'] = LabelFrame(frames['other_tab'],
                                       text='Speed Ups',
                                       labelanchor=NW)
    frames['other'] = LabelFrame(frames['other_tab'],
                                 text='Misc',
                                 labelanchor=NW)

    #Aesthetics tab
    frames['cosmetics'] = LabelFrame(frames['aesthetic_tab'],
                                     text='General',
                                     labelanchor=NW)
    frames['tuniccolor'] = LabelFrame(frames['aesthetic_tab_left'],
                                      text='Tunic Color',
                                      labelanchor=NW)
    frames['navicolor'] = LabelFrame(frames['aesthetic_tab_right'],
                                     text='Navi Color',
                                     labelanchor=NW)
    frames['lowhp'] = LabelFrame(frames['aesthetic_tab_left'],
                                 text='Low HP SFX',
                                 labelanchor=NW)
    frames['navihint'] = LabelFrame(frames['aesthetic_tab_right'],
                                    text='Navi SFX',
                                    labelanchor=NW)

    # shared
    settingsFrame = Frame(mainWindow)
    settings_string_var = StringVar()
    settingsEntry = Entry(settingsFrame,
                          textvariable=settings_string_var,
                          width=25)

    def show_settings(event=None):
        settings = guivars_to_settings(guivars)
        settings_string_var.set(settings.get_settings_string())

        # Update any dependencies
        for info in setting_infos:
            if info.gui_params and 'dependency' in info.gui_params:
                dep_met = info.gui_params['dependency'](guivars)

                if widgets[info.name].winfo_class() == 'Frame':
                    for child in widgets[info.name].winfo_children():
                        if child.winfo_class() == 'TCombobox':
                            child.configure(
                                state='readonly' if dep_met else 'disabled')
                        else:
                            child.configure(
                                state='normal' if dep_met else 'disabled')

                        if child.winfo_class() == 'Scale':
                            child.configure(fg='Black' if dep_met else 'Grey')
                else:
                    if widgets[info.name].winfo_class() == 'TCombobox':
                        widgets[info.name].configure(
                            state='readonly' if dep_met else 'disabled')
                    else:
                        widgets[info.name].configure(
                            state='normal' if dep_met else 'disabled')

                    if widgets[info.name].winfo_class() == 'Scale':
                        widgets[info.name].configure(
                            fg='Black' if dep_met else 'Grey')

            if info.name in guivars and guivars[
                    info.name].get() == 'Custom Color':
                color = askcolor()
                if color == (None, None):
                    color = ((0, 0, 0), '#000000')
                guivars[info.name].set('Custom (' + color[1] + ')')

    def show_settings_special(event=None):
        if guivars['logic_tricks'].get():
            widgets['logic_man_on_roof'].select()
            widgets['logic_child_deadhand'].select()
            widgets['logic_dc_jump'].select()
            widgets['logic_windmill_poh'].select()
            widgets['logic_crater_bean_poh_with_hovers'].select()
            widgets['logic_zora_with_cucco'].select()
            widgets['logic_fewer_tunic_requirements'].select()
        else:
            widgets['logic_man_on_roof'].deselect()
            widgets['logic_child_deadhand'].deselect()
            widgets['logic_dc_jump'].deselect()
            widgets['logic_windmill_poh'].deselect()
            widgets['logic_crater_bean_poh_with_hovers'].deselect()
            widgets['logic_zora_with_cucco'].deselect()
            widgets['logic_fewer_tunic_requirements'].deselect()
        settings = guivars_to_settings(guivars)
        settings_string_var.set(settings.get_settings_string())

    def import_settings(event=None):
        try:
            settings = guivars_to_settings(guivars)
            text = settings_string_var.get().upper()
            settings.seed = guivars['seed'].get()
            settings.update_with_settings_string(text)
            settings_to_guivars(settings, guivars)
        except Exception as e:
            messagebox.showerror(title="Error",
                                 message="Invalid settings string")

    label = Label(settingsFrame, text="Settings String")
    importSettingsButton = Button(settingsFrame,
                                  text='Import Settings String',
                                  command=import_settings)
    label.pack(side=LEFT, anchor=W, padx=5)
    settingsEntry.pack(side=LEFT, anchor=W)
    importSettingsButton.pack(side=LEFT, anchor=W, padx=5)

    fileDialogFrame = Frame(frames['rom_tab'])

    romDialogFrame = Frame(fileDialogFrame)
    baseRomLabel = Label(romDialogFrame, text='Base ROM')
    guivars['rom'] = StringVar(value='ZOOTDEC.z64')
    romEntry = Entry(romDialogFrame, textvariable=guivars['rom'], width=40)

    def RomSelect():
        rom = filedialog.askopenfilename(
            filetypes=[("ROM Files", (".z64", ".n64")), ("All Files", "*")])
        if rom != '':
            guivars['rom'].set(rom)

    romSelectButton = Button(romDialogFrame,
                             text='Select ROM',
                             command=RomSelect,
                             width=10)

    baseRomLabel.pack(side=LEFT, padx=(38, 0))
    romEntry.pack(side=LEFT, padx=3)
    romSelectButton.pack(side=LEFT)

    romDialogFrame.pack()

    fileDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(5, 1))

    def open_output():
        open_file(output_path(''))

    def output_dir_select():
        rom = filedialog.askdirectory(
            initialdir=default_output_path(guivars['output_dir'].get()))
        if rom != '':
            guivars['output_dir'].set(rom)

    outputDialogFrame = Frame(frames['rom_tab'])
    outputDirLabel = Label(outputDialogFrame, text='Output Directory')
    guivars['output_dir'] = StringVar(value='')
    outputDirEntry = Entry(outputDialogFrame,
                           textvariable=guivars['output_dir'],
                           width=40)
    outputDirButton = Button(outputDialogFrame,
                             text='Select Dir',
                             command=output_dir_select,
                             width=10)
    outputDirLabel.pack(side=LEFT, padx=(3, 0))
    outputDirEntry.pack(side=LEFT, padx=3)
    outputDirButton.pack(side=LEFT)
    outputDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(5, 1))

    if os.path.exists(local_path('README.html')):

        def open_readme():
            open_file(local_path('README.html'))

        openReadmeButton = Button(outputDialogFrame,
                                  text='Open Documentation',
                                  command=open_readme)
        openReadmeButton.pack(side=LEFT, padx=5)

    outputDialogFrame.pack(side=TOP, anchor=W, pady=3)

    countDialogFrame = Frame(frames['rom_tab'])
    countLabel = Label(countDialogFrame, text='Generation Count')
    guivars['count'] = StringVar()
    countSpinbox = Spinbox(countDialogFrame,
                           from_=1,
                           to=100,
                           textvariable=guivars['count'],
                           width=3)

    countLabel.pack(side=LEFT)
    countSpinbox.pack(side=LEFT, padx=2)
    countDialogFrame.pack(side=TOP, anchor=W, padx=5, pady=(1, 1))

    # build gui
    ############

    widgets = {}

    for info in setting_infos:
        if info.gui_params:
            if info.gui_params['widget'] == 'Checkbutton':
                # determine the initial value of the checkbox
                default_value = 1 if info.gui_params[
                    'default'] == "checked" else 0
                # create a variable to access the box's state
                guivars[info.name] = IntVar(value=default_value)
                # create the checkbox
                widgets[info.name] = Checkbutton(
                    frames[info.gui_params['group']],
                    text=info.gui_params['text'],
                    variable=guivars[info.name],
                    justify=LEFT,
                    wraplength=190,
                    command=show_settings)
                widgets[info.name].pack(expand=False, anchor=W)
            if info.gui_params['widget'] == 'SpecialCheckbutton':
                # determine the initial value of the checkbox
                default_value = 1 if info.gui_params[
                    'default'] == "checked" else 0
                # create a variable to access the box's state
                guivars[info.name] = IntVar(value=default_value)
                # create the checkbox
                widgets[info.name] = Checkbutton(
                    frames[info.gui_params['group']],
                    text=info.gui_params['text'],
                    variable=guivars[info.name],
                    justify=LEFT,
                    wraplength=190,
                    command=show_settings_special)
                widgets[info.name].pack(expand=False, anchor=W)
            elif info.gui_params['widget'] == 'Combobox':
                # create the variable to store the user's decision
                guivars[info.name] = StringVar(
                    value=info.gui_params['default'])
                # create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])
                # dropdown = OptionMenu(widgets[info.name], guivars[info.name], *(info['options']))
                if isinstance(info.gui_params['options'], list):
                    info.gui_params['options'] = dict(
                        zip(info.gui_params['options'],
                            info.gui_params['options']))
                dropdown = ttk.Combobox(widgets[info.name],
                                        textvariable=guivars[info.name],
                                        values=list(
                                            info.gui_params['options'].keys()),
                                        state='readonly',
                                        width=30)
                dropdown.bind("<<ComboboxSelected>>", show_settings)
                dropdown.pack(side=BOTTOM, anchor=W)
                # label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name],
                                  text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # pack the frame
                widgets[info.name].pack(expand=False,
                                        side=TOP,
                                        anchor=W,
                                        padx=3,
                                        pady=3)
            elif info.gui_params['widget'] == 'Radiobutton':
                # create the variable to store the user's decision
                guivars[info.name] = StringVar(
                    value=info.gui_params['default'])
                # create the option menu
                widgets[info.name] = LabelFrame(
                    frames[info.gui_params['group']],
                    text=info.gui_params['text']
                    if 'text' in info.gui_params else info["name"],
                    labelanchor=NW)
                if isinstance(info.gui_params['options'], list):
                    info.gui_params['options'] = dict(
                        zip(info.gui_params['options'],
                            info.gui_params['options']))
                # setup orientation
                side = TOP
                anchor = W
                if "horizontal" in info.gui_params and info.gui_params[
                        "horizontal"]:
                    side = LEFT
                    anchor = N
                # add the radio buttons
                for option in info.gui_params["options"]:
                    radio_button = Radiobutton(widgets[info.name],
                                               text=option,
                                               value=option,
                                               variable=guivars[info.name],
                                               justify=LEFT,
                                               wraplength=190,
                                               indicatoron=False,
                                               command=show_settings)
                    radio_button.pack(expand=True, side=side, anchor=anchor)
                # pack the frame
                widgets[info.name].pack(expand=False,
                                        side=TOP,
                                        anchor=W,
                                        padx=3,
                                        pady=3)
            elif info.gui_params['widget'] == 'Scale':
                # create the variable to store the user's decision
                guivars[info.name] = IntVar(value=info.gui_params['default'])
                # create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])
                # dropdown = OptionMenu(widgets[info.name], guivars[info.name], *(info['options']))
                minval = 'min' in info.gui_params and info.gui_params[
                    'min'] or 0
                maxval = 'max' in info.gui_params and info.gui_params[
                    'max'] or 100
                stepval = 'step' in info.gui_params and info.gui_params[
                    'step'] or 1
                scale = Scale(widgets[info.name],
                              variable=guivars[info.name],
                              from_=minval,
                              to=maxval,
                              tickinterval=stepval,
                              resolution=stepval,
                              showvalue=0,
                              orient=HORIZONTAL,
                              sliderlength=15,
                              length=200,
                              command=show_settings)
                scale.pack(side=BOTTOM, anchor=W)
                # label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name],
                                  text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # pack the frame
                widgets[info.name].pack(expand=False,
                                        side=TOP,
                                        anchor=W,
                                        padx=3,
                                        pady=3)
            elif info.gui_params['widget'] == 'Entry':
                # create the variable to store the user's decision
                guivars[info.name] = StringVar(
                    value=info.gui_params['default'])
                # create the option menu
                widgets[info.name] = Frame(frames[info.gui_params['group']])

                entry = Entry(widgets[info.name],
                              textvariable=guivars[info.name],
                              width=30)
                entry.pack(side=BOTTOM, anchor=W)
                # label the option
                if 'text' in info.gui_params:
                    label = Label(widgets[info.name],
                                  text=info.gui_params['text'])
                    label.pack(side=LEFT, anchor=W, padx=5)
                # pack the frame
                widgets[info.name].pack(expand=False,
                                        side=TOP,
                                        anchor=W,
                                        padx=3,
                                        pady=3)

            if 'tooltip' in info.gui_params:
                ToolTips.register(widgets[info.name],
                                  info.gui_params['tooltip'])

    # pack the hierarchy

    frames['logic'].pack(fill=BOTH,
                         expand=True,
                         anchor=N,
                         side=RIGHT,
                         pady=(5, 1))
    frames['open'].pack(fill=BOTH,
                        expand=True,
                        anchor=W,
                        side=TOP,
                        pady=(5, 1))
    frames['world'].pack(fill=BOTH,
                         expand=True,
                         anchor=W,
                         side=BOTTOM,
                         pady=(5, 1))

    # Logic tab
    frames['rewards'].pack(fill=BOTH,
                           expand=True,
                           anchor=N,
                           side=LEFT,
                           pady=(5, 1))
    frames['tricks'].pack(fill=BOTH,
                          expand=True,
                          anchor=N,
                          side=LEFT,
                          pady=(5, 1))

    #Other Tab
    frames['convenience'].pack(fill=BOTH,
                               expand=True,
                               anchor=N,
                               side=LEFT,
                               pady=(5, 1))
    frames['other'].pack(fill=BOTH,
                         expand=True,
                         anchor=N,
                         side=LEFT,
                         pady=(5, 1))

    #Aesthetics tab
    frames['cosmetics'].pack(fill=BOTH, expand=True, anchor=W, side=TOP)
    frames['aesthetic_tab_left'].pack(fill=BOTH,
                                      expand=True,
                                      anchor=W,
                                      side=LEFT)
    frames['aesthetic_tab_right'].pack(fill=BOTH,
                                       expand=True,
                                       anchor=W,
                                       side=RIGHT)

    #Aesthetics tab - Left Side
    frames['tuniccolor'].pack(fill=BOTH,
                              expand=True,
                              anchor=W,
                              side=TOP,
                              pady=(5, 1))
    frames['lowhp'].pack(fill=BOTH,
                         expand=True,
                         anchor=W,
                         side=TOP,
                         pady=(5, 1))

    #Aesthetics tab - Right Side
    frames['navicolor'].pack(fill=BOTH,
                             expand=True,
                             anchor=W,
                             side=TOP,
                             pady=(5, 1))
    frames['navihint'].pack(fill=BOTH,
                            expand=True,
                            anchor=W,
                            side=TOP,
                            pady=(5, 1))

    notebook.pack(fill=BOTH, expand=True, padx=5, pady=5)

    multiworldFrame = LabelFrame(frames['rom_tab'],
                                 text='Multi-World Generation')
    countLabel = Label(
        multiworldFrame,
        wraplength=350,
        justify=LEFT,
        text=
        'This is used for co-op generations. Increasing Player Count will drastically increase the generation time. For more information see:'
    )
    hyperLabel = Label(multiworldFrame,
                       wraplength=350,
                       justify=LEFT,
                       text='https://github.com/TestRunnerSRL/bizhawk-co-op',
                       fg='blue',
                       cursor='hand2')
    hyperLabel.bind(
        "<Button-1>", lambda event: webbrowser.open_new(
            r"https://github.com/TestRunnerSRL/bizhawk-co-op"))
    countLabel.pack(side=TOP, anchor=W, padx=5, pady=0)
    hyperLabel.pack(side=TOP, anchor=W, padx=5, pady=0)

    worldCountFrame = Frame(multiworldFrame)
    countLabel = Label(worldCountFrame, text='Player Count')
    guivars['world_count'] = StringVar()
    countSpinbox = Spinbox(worldCountFrame,
                           from_=1,
                           to=100,
                           textvariable=guivars['world_count'],
                           width=3)

    countLabel.pack(side=LEFT)
    countSpinbox.pack(side=LEFT, padx=2)
    worldCountFrame.pack(side=LEFT, anchor=N, padx=10, pady=(1, 5))

    playerNumFrame = Frame(multiworldFrame)
    countLabel = Label(playerNumFrame, text='Player ID')
    guivars['player_num'] = StringVar()
    countSpinbox = Spinbox(playerNumFrame,
                           from_=1,
                           to=100,
                           textvariable=guivars['player_num'],
                           width=3)

    countLabel.pack(side=LEFT)
    countSpinbox.pack(side=LEFT, padx=2)
    playerNumFrame.pack(side=LEFT, anchor=N, padx=10, pady=(1, 5))
    multiworldFrame.pack(side=TOP, anchor=W, padx=5, pady=(1, 1))

    # didn't refactor the rest, sorry

    # create the option menu

    settingsFrame.pack(fill=BOTH, anchor=W, padx=5, pady=(10, 0))

    def multiple_run(settings, window):
        orig_seed = settings.seed
        for i in range(settings.count):
            settings.update_seed(orig_seed + '-' + str(i))
            window.update_title("Generating Seed...%d/%d" %
                                (i + 1, settings.count))
            main(settings, window)

    def generateRom():
        settings = guivars_to_settings(guivars)
        if settings.count is not None:
            BackgroundTaskProgress(mainWindow, "Generating Seed...",
                                   multiple_run, settings)
        else:
            BackgroundTaskProgress(mainWindow, "Generating Seed...", main,
                                   settings)

    generateSeedFrame = Frame(mainWindow)
    generateButton = Button(generateSeedFrame,
                            text='Generate Patched Rom',
                            command=generateRom)

    seedLabel = Label(generateSeedFrame, text='Seed')
    guivars['seed'] = StringVar()
    seedEntry = Entry(generateSeedFrame,
                      textvariable=guivars['seed'],
                      width=25)
    seedLabel.pack(side=LEFT, padx=(55, 5))
    seedEntry.pack(side=LEFT)
    generateButton.pack(side=LEFT, padx=(5, 0))

    generateSeedFrame.pack(side=BOTTOM, anchor=W, padx=5, pady=10)

    guivars['checked_version'] = StringVar()

    if settings is not None:
        # load values from commandline args
        settings_to_guivars(settings, guivars)
    else:
        # try to load saved settings
        try:
            settingsFile = os.path.join(
                os.path.dirname(os.path.abspath(__file__)), 'settings.sav')
            with open(settingsFile) as f:
                settings = Settings(json.load(f))
                settings.update_seed("")
                settings_to_guivars(settings, guivars)
        except:
            pass

    show_settings()

    def gui_check_version():
        task = BackgroundTask(mainWindow, check_version,
                              guivars['checked_version'].get())
        while task.running:
            mainWindow.update()

        if task.status:
            dialog = Dialog(mainWindow,
                            title="Version Error",
                            question=task.status,
                            oktext='Don\'t show again',
                            canceltext='OK')
            if dialog.result:
                guivars['checked_version'].set(ESVersion)

    mainWindow.after(1000, gui_check_version)
    mainWindow.mainloop()

    # save settings on close
    with open('settings.sav', 'w') as outfile:
        settings = guivars_to_settings(guivars)
        json.dump(settings.__dict__, outfile)