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
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 = []
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))))
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)
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])
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
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)
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')]
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"))
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 = {}
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()
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)
def local_unofficial_sprite_dir(self): return local_path("data/sprites/unofficial")
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
'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]]:
def get_json(): with open(local_path('data/mqu.json'), 'r') as stream: data = json.load(stream) return data
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()
def set_icon(window): logo = tk.PhotoImage(file=local_path('data', 'icon.png')) window.tk.call('wm', 'iconphoto', window._w, logo)
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()
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
def open_readme(): open_file(local_path('README.html'))
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
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)