diff --git a/BaseClasses.py b/BaseClasses.py index ff001247..cb47063a 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -73,6 +73,10 @@ class World(object): self.key_layout = defaultdict(dict) for player in range(1, players + 1): + # If World State is Retro, set to Open and set Retro flag + if self.mode[player] == "retro": + self.mode[player] = "open" + self.retro[player] = True def set_player_attr(attr, val): self.__dict__.setdefault(attr, {})[player] = val set_player_attr('_region_cache', {}) diff --git a/CLI.py b/CLI.py index b3bbe78b..0d5105fa 100644 --- a/CLI.py +++ b/CLI.py @@ -9,8 +9,6 @@ import shlex import sys from Main import main -from Utils import is_bundled, close_console -from Fill import FillError import classes.constants as CONST @@ -185,7 +183,7 @@ def parse_arguments(argv, no_defaults=False): Crossed: Doors are mixed between all dungeons. (Not yet implemented) Vanilla: All doors are connected the same way they were in the - base game. + base game. ''') parser.add_argument('--experimental', default=defval(settings["experimental"] != 0), help='Enable experimental features', action='store_true') parser.add_argument('--dungeon_counters', default=defval(settings["dungeon_counters"]), help='Enable dungeon chest counters', const='off', nargs='?', choices=['off', 'on', 'pickup']) @@ -477,7 +475,10 @@ def get_settings(): settings[k] = v return settings - +# Priority fallback is: +# 1: CLI +# 2: Settings file +# 3: Canned defaults def get_args_priority(settings_args, gui_args, cli_args): args = {} args["settings"] = get_settings() if settings_args is None else settings_args diff --git a/Gui.py b/Gui.py index 632b9a43..5b9d7eb1 100755 --- a/Gui.py +++ b/Gui.py @@ -4,8 +4,7 @@ import os import sys from tkinter import Tk, Button, BOTTOM, TOP, StringVar, BooleanVar, X, BOTH, RIGHT, ttk, messagebox -from argparse import Namespace -from CLI import get_settings, get_args_priority +from CLI import get_args_priority from DungeonRandomizer import parse_arguments from gui.adjust.overview import adjust_page from gui.startinventory.overview import startinventory_page @@ -24,6 +23,7 @@ from Main import __version__ as ESVersion def guiMain(args=None): + # Save settings to file def save_settings(args): user_resources_path = os.path.join(".", "resources", "user") settings_path = os.path.join(user_resources_path) @@ -35,6 +35,7 @@ def guiMain(args=None): f.write(json.dumps(args, indent=2)) os.chmod(os.path.join(settings_path, "settings.json"),0o755) + # Save settings from GUI def save_settings_from_gui(confirm): gui_args = vars(create_guiargs(self)) if self.randomSprite.get(): @@ -83,6 +84,7 @@ def guiMain(args=None): # make array for frames self.frames = {} + # make pages for each section self.notebook = ttk.Notebook(self) self.pages["randomizer"] = ttk.Frame(self.notebook) self.pages["adjust"] = ttk.Frame(self.notebook) @@ -178,6 +180,7 @@ def guiMain(args=None): # load adjust settings into options loadadjustargs(self, self.settings) + # run main window mainWindow.mainloop() diff --git a/ItemList.py b/ItemList.py index 42e48179..f07ecdf5 100644 --- a/ItemList.py +++ b/ItemList.py @@ -126,6 +126,7 @@ difficulties = { ), } +# Translate between Mike's label array and YAML/JSON keys def get_custom_array_key(item): label_switcher = { "silverarrow": "silversupgrade", @@ -264,10 +265,10 @@ def generate_itempool(world, player): if player in world.pool_adjustment.keys(): amt = world.pool_adjustment[player] if amt < 0: - for i in range(0, amt): + for _ in range(0, amt): pool.remove('Rupees (20)') elif amt > 0: - for i in range(0, amt): + for _ in range(0, amt): pool.append('Rupees (20)') for item in precollected_items: @@ -707,24 +708,25 @@ def test(): for difficulty in ['normal', 'hard', 'expert']: for goal in ['ganon', 'triforcehunt', 'pedestal']: for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']: - for mode in ['open', 'standard', 'inverted']: + for mode in ['open', 'standard', 'inverted', 'retro']: for swords in ['random', 'assured', 'swordless', 'vanilla']: for progressive in ['on', 'off']: for shuffle in ['full', 'insanity_legacy']: for retro in [True, False]: - out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro) - count = len(out[0]) + len(out[1]) + for door_shuffle in ['basic', 'crossed', 'vanilla']: + out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, door_shuffle) + count = len(out[0]) + len(out[1]) - correct_count = total_items_to_place - if goal == 'pedestal' and swords != 'vanilla': - # pedestal goals generate one extra item - correct_count += 1 - if retro: - correct_count += 28 - try: - assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro)) - except AssertionError as e: - print(e) + correct_count = total_items_to_place + if goal == 'pedestal' and swords != 'vanilla': + # pedestal goals generate one extra item + correct_count += 1 + if retro: + correct_count += 28 + try: + assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro)) + except AssertionError as e: + print(e) if __name__ == '__main__': test() diff --git a/Main.py b/Main.py index 255a4290..dd3cbbf3 100644 --- a/Main.py +++ b/Main.py @@ -77,7 +77,8 @@ def main(args, seed=None): world.difficulty_requirements[player] = difficulties[world.difficulty[player]] if world.mode[player] == 'standard' and world.enemy_shuffle[player] != 'none': - world.escape_assist[player].append('bombs') # enemized escape assumes infinite bombs available and will likely be unbeatable without it + if hasattr(world,"escape_assist") and player in world.escape_assist: + world.escape_assist[player].append('bombs') # enemized escape assumes infinite bombs available and will likely be unbeatable without it for tok in filter(None, args.startinventory[player].split(',')): item = ItemFactory(tok.strip(), player) @@ -383,7 +384,7 @@ def copy_dynamic_regions_and_locations(world, ret): new_loc.always_allow = location.always_allow new_loc.item_rule = location.item_rule new_reg.locations.append(new_loc) - + ret.clear_location_cache() diff --git a/build-dr.py b/build-dr.py index 120028d9..6959b08d 100644 --- a/build-dr.py +++ b/build-dr.py @@ -2,16 +2,20 @@ import subprocess import os import shutil +# Destination is current dir DEST_DIRECTORY = '.' +# Check for UPX if os.path.isdir("upx"): upx_string = "--upx-dir=upx" else: upx_string = "" +# Nuke Build dir if os.path.isdir("build"): shutil.rmtree("build") +# Run pyinstaller for DungeonRandomizer subprocess.run(" ".join(["pyinstaller DungeonRandomizer.spec ", upx_string, "-y ", diff --git a/build-gui.py b/build-gui.py index 3f63548d..33c295b4 100644 --- a/build-gui.py +++ b/build-gui.py @@ -2,16 +2,20 @@ import subprocess import os import shutil +# Destination is current dir DEST_DIRECTORY = '.' +# Check for UPX if os.path.isdir("upx"): upx_string = "--upx-dir=upx" else: upx_string = "" +# Nuke Build dir if os.path.isdir("build"): shutil.rmtree("build") +# Run pyinstaller for Gui subprocess.run(" ".join(["pyinstaller Gui.spec ", upx_string, "-y ", diff --git a/classes/SpriteSelector.py b/classes/SpriteSelector.py index 7e527d05..405770a2 100644 --- a/classes/SpriteSelector.py +++ b/classes/SpriteSelector.py @@ -1,4 +1,4 @@ -from tkinter import filedialog, messagebox, Button, Canvas, Label, LabelFrame, Frame, PhotoImage, Scrollbar, Toplevel, ALL, NSEW, LEFT, BOTTOM, X, RIGHT, TOP, HORIZONTAL, EW, NS +from tkinter import filedialog, messagebox, Button, Canvas, Label, LabelFrame, Frame, PhotoImage, Scrollbar, Toplevel, ALL, LEFT, BOTTOM, X, RIGHT, TOP, EW, NS from glob import glob import json import os @@ -34,6 +34,7 @@ class SpriteSelector(object): def open_unofficial_sprite_dir(_evt): open_file(self.unofficial_sprite_dir) + # Open SpriteSomething directory for Link sprites def open_spritesomething_listing(_evt): webbrowser.open("https://artheau.github.io/SpriteSomething/?mode=zelda3/link") @@ -50,6 +51,7 @@ class SpriteSelector(object): unofficial_title_text.pack(side=LEFT) unofficial_title_link.pack(side=LEFT) unofficial_title_link.bind("", open_unofficial_sprite_dir) + # Include hyperlink to SpriteSomething directory for Link sprites spritesomething_title_link = Label(unofficial_frametitle, text="(SpriteSomething)", fg="blue", cursor="hand2") spritesomething_title_link.pack(side=LEFT) spritesomething_title_link.bind("", open_spritesomething_listing) diff --git a/classes/constants.py b/classes/constants.py index b822628f..8277cd3c 100644 --- a/classes/constants.py +++ b/classes/constants.py @@ -1,3 +1,4 @@ +# Ordered list of items in Custom Item Pool page and Starting Inventory page CUSTOMITEMS = [ "bow", "progressivebow", "boomerang", "redmerang", "hookshot", "mushroom", "powder", "firerod", "icerod", "bombos", @@ -20,11 +21,13 @@ CUSTOMITEMS = [ "rupoorcost" ] +# These can't be in the Starting Inventory page CANTSTARTWITH = [ "triforcepiecesgoal", "triforce", "rupoor", "rupoorcost" ] +# In the same order as CUSTOMITEMS, these are Pretty Labels for each option CUSTOMITEMLABELS = [ "Bow", "Progressive Bow", "Blue Boomerang", "Red Boomerang", "Hookshot", "Mushroom", "Magic Powder", "Fire Rod", "Ice Rod", "Bombos", @@ -33,7 +36,7 @@ CUSTOMITEMLABELS = [ "Ocarina", "Bug Catching Net", "Book of Mudora", "Bottle", "Cane of Somaria", "Cane of Byrna", "Magic Cape", "Magic Mirror", "Pegasus Boots", "Power Glove", "Titans Mitts", "Progressive Glove", "Flippers", "Moon Pearl", "Piece of Heart", - + "Boss Heart Container", "Sanctuary Heart Container", "Fighter Sword", "Master Sword", "Tempered Sword", "Golden Sword", "Progressive Sword", "Blue Shield", "Red Shield", "Mirror Shield", "Progressive Shield", "Blue Mail", "Red Mail", "Progressive Armor", "Magic Upgrade (1/2)", @@ -47,6 +50,8 @@ CUSTOMITEMLABELS = [ "Rupoor Cost" ] +# Stuff on each page to save, according to internal names as defined by the widgets definitions +# and how it eventually translates to YAML/JSON weight files SETTINGSTOPROCESS = { "randomizer": { "item": { @@ -104,6 +109,6 @@ SETTINGSTOPROCESS = { "usestartinventory": "usestartinventory", "usecustompool": "custom", "saveonexit": "saveonexit" - } + } } } diff --git a/gui/adjust/overview.py b/gui/adjust/overview.py index 8e3a7851..3c6dec73 100644 --- a/gui/adjust/overview.py +++ b/gui/adjust/overview.py @@ -1,4 +1,4 @@ -from tkinter import ttk, filedialog, messagebox, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, OptionMenu, E, W, LEFT, RIGHT, X, BOTTOM +from tkinter import ttk, filedialog, messagebox, StringVar, Button, Entry, Frame, Label, E, W, LEFT, RIGHT, X, BOTTOM from AdjusterMain import adjust from argparse import Namespace from classes.SpriteSelector import SpriteSelector @@ -19,6 +19,7 @@ def adjust_page(top, parent, settings): self.frames["checkboxes"] = Frame(self) self.frames["checkboxes"].pack(anchor=W) + # Adjust option frames self.frames["selectOptionsFrame"] = Frame(self) self.frames["leftAdjustFrame"] = Frame(self.frames["selectOptionsFrame"]) self.frames["rightAdjustFrame"] = Frame(self.frames["selectOptionsFrame"]) @@ -28,6 +29,8 @@ def adjust_page(top, parent, settings): self.frames["rightAdjustFrame"].pack(side=RIGHT) self.frames["bottomAdjustFrame"].pack(fill=X) + # Load Adjust option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes with open(os.path.join("resources","app","gui","adjust","overview","widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for framename,theseWidgets in myDict.items(): @@ -40,6 +43,7 @@ def adjust_page(top, parent, settings): self.widgets[key].pack(packAttrs) # Sprite Selection + # This one's more-complicated, build it and stuff it self.spriteNameVar2 = StringVar() spriteDialogFrame2 = Frame(self.frames["leftAdjustFrame"]) baseSpriteLabel2 = Label(spriteDialogFrame2, text='Sprite:') @@ -65,6 +69,8 @@ def adjust_page(top, parent, settings): spriteSelectButton2.pack(side=LEFT) spriteDialogFrame2.pack(anchor=E) + # Path to game file to Adjust + # This one's more-complicated, build it and stuff it adjustRomFrame = Frame(self.frames["bottomAdjustFrame"]) adjustRomLabel = Label(adjustRomFrame, text='Rom to adjust: ') self.romVar2 = StringVar(value=settings["rom"]) @@ -82,6 +88,7 @@ def adjust_page(top, parent, settings): romSelectButton2.pack(side=LEFT) adjustRomFrame.pack(fill=X) + # These are the options to Adjust def adjustRom(): options = { "heartbeep": "heartbeep", diff --git a/gui/bottom.py b/gui/bottom.py index 22d94d35..e2e40f43 100644 --- a/gui/bottom.py +++ b/gui/bottom.py @@ -1,10 +1,9 @@ -from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, Spinbox, E, W, LEFT, RIGHT, X +from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, E, W, LEFT, RIGHT, X from argparse import Namespace -from functools import partial import logging import os import random -from CLI import parse_arguments, get_settings +from CLI import parse_arguments from Main import main from Utils import local_path, output_path, open_file import classes.constants as CONST @@ -97,22 +96,35 @@ def create_guiargs(parent): # Page::Subpage::GUI-id::param-id options = CONST.SETTINGSTOPROCESS + # Cycle through each page for mainpage in options: + # Cycle through each subpage (in case of Item Randomizer) for subpage in options[mainpage]: + # Cycle through each widget for widget in options[mainpage][subpage]: + # Get the value and set it arg = options[mainpage][subpage][widget] setattr(guiargs, arg, parent.pages[mainpage].pages[subpage].widgets[widget].storageVar.get()) + # Get EnemizerCLI setting guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].enemizerCLIpathVar.get() + # Get Multiworld Worlds count guiargs.multi = int(parent.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.get()) + # Get baserom path guiargs.rom = parent.pages["randomizer"].pages["generation"].romVar.get() + + # Get if we're using the Custom Item Pool guiargs.custom = bool(parent.pages["randomizer"].pages["generation"].widgets["usecustompool"].storageVar.get()) + # Get Seed ID guiargs.seed = int(parent.frames["bottom"].seedVar.get()) if parent.frames["bottom"].seedVar.get() else None + + # Get number of generations to run guiargs.count = int(parent.frames["bottom"].widgets["generationcount"].storageVar.get()) if parent.frames["bottom"].widgets["generationcount"].storageVar.get() != '1' else None + # Get Adjust settings adjustargs = { "nobgm": "disablemusic", "quickswap": "quickswap", @@ -126,22 +138,29 @@ def create_guiargs(parent): internal = adjustargs[adjustarg] setattr(guiargs,"adjust." + internal, parent.pages["adjust"].content.widgets[adjustarg].storageVar.get()) + # Get Custom Items and Starting Inventory Items customitems = CONST.CUSTOMITEMS guiargs.startinventory = [] guiargs.customitemarray = {} guiargs.startinventoryarray = {} for customitem in customitems: - if customitem not in ["triforcepiecesgoal", "triforce", "rupoor", "rupoorcost"]: + if customitem not in CONST.CANTSTARTWITH: + # Starting Inventory is a CSV amount = int(parent.pages["startinventory"].content.startingWidgets[customitem].storageVar.get()) guiargs.startinventoryarray[customitem] = amount - for i in range(0, amount): + for _ in range(0, amount): label = CONST.CUSTOMITEMLABELS[customitems.index(customitem)] guiargs.startinventory.append(label) + # Custom Item Pool is a dict of ints guiargs.customitemarray[customitem] = int(parent.pages["custom"].content.customWidgets[customitem].storageVar.get()) + # Starting Inventory is a CSV guiargs.startinventory = ','.join(guiargs.startinventory) + # Get Sprite Selection (set or random) guiargs.sprite = parent.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteObject"] guiargs.randomSprite = parent.randomSprite.get() + + # Get output path guiargs.outputpath = parent.outputPath.get() return guiargs diff --git a/gui/custom/overview.py b/gui/custom/overview.py index d6a290ef..53a65d03 100644 --- a/gui/custom/overview.py +++ b/gui/custom/overview.py @@ -1,25 +1,27 @@ -from tkinter import ttk, Frame, N, LEFT, VERTICAL, Y +from tkinter import ttk, Frame, N, E, W, LEFT, X, VERTICAL, Y import gui.widgets as widgets import json import os import classes.constants as CONST - -def custom_page(top, parent): +def custom_page(top,parent): # Custom Item Pool self = ttk.Frame(parent) + # Create uniform list columns def create_list_frame(parent, framename): parent.frames[framename] = Frame(parent) parent.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N) parent.frames[framename].thisRow = 0 parent.frames[framename].thisCol = 0 + # Create a vertical rule to help with splitting columns visually def create_vertical_rule(num=1): - for i in range(0,num): + for _ in range(0,num): ttk.Separator(self, orient=VERTICAL).pack(side=LEFT, anchor=N, fill=Y) + # This was in here, I have no idea what it was but I left it just in case: MikeT def validation(P): if str.isdigit(P) or P == "": return True @@ -32,6 +34,7 @@ def custom_page(top, parent): # Custom Item Pool option sections self.frames = {} + # Create 5 columns with 2 vertical rules in between each create_list_frame(self, "itemList1") create_vertical_rule(2) create_list_frame(self, "itemList2") @@ -42,6 +45,8 @@ def custom_page(top, parent): create_vertical_rule(2) create_list_frame(self, "itemList5") + # Load Custom option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes with open(os.path.join("resources", "app", "gui", "custom", "overview", "widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for framename,theseWidgets in myDict.items(): @@ -49,7 +54,8 @@ def custom_page(top, parent): for key in dictWidgets: self.customWidgets[key] = dictWidgets[key] - for i, key in enumerate(CONST.CUSTOMITEMS): - self.customWidgets[key].storageVar.set(top.settings["customitemarray"][i]) + # Load Custom Item Pool settings from settings file + for key in CONST.CUSTOMITEMS: + self.customWidgets[key].storageVar.set(top.settings["customitemarray"][key]) return self diff --git a/gui/loadcliargs.py b/gui/loadcliargs.py index 846daaa8..446fbb09 100644 --- a/gui/loadcliargs.py +++ b/gui/loadcliargs.py @@ -3,6 +3,7 @@ from gui.randomize.gameoptions import set_sprite from Rom import Sprite, get_sprite_from_name import classes.constants as CONST +# Load args/settings for most tabs def loadcliargs(gui, args, settings=None): if args is not None: # for k, v in vars(args).items(): @@ -14,28 +15,47 @@ def loadcliargs(gui, args, settings=None): # Page::Subpage::GUI-id::param-id options = CONST.SETTINGSTOPROCESS + # Cycle through each page for mainpage in options: + # Cycle through each subpage (in case of Item Randomizer) for subpage in options[mainpage]: + # Cycle through each widget for widget in options[mainpage][subpage]: + # Get the value and set it arg = options[mainpage][subpage][widget] gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[arg]) + # If we're on the Game Options page and it's not about Hints if subpage == "gameoptions" and not widget == "hints": + # Check if we've got settings + # Check if we've got the widget in Adjust settings hasSettings = settings is not None hasWidget = ("adjust." + widget) in settings if hasSettings else None if hasWidget is None: + # If we've got a Game Options val and we don't have an Adjust val, use the Game Options val gui.pages["adjust"].content.widgets[widget].storageVar.set(args[arg]) + # Get EnemizerCLI setting gui.pages["randomizer"].pages["enemizer"].enemizerCLIpathVar.set(args["enemizercli"]) + + # Get baserom path gui.pages["randomizer"].pages["generation"].romVar.set(args["rom"]) + # Get Multiworld Worlds count if args["multi"]: gui.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.set(str(args["multi"])) + + # Get Seed ID if args["seed"]: gui.frames["bottom"].seedVar.set(str(args["seed"])) + + # Get number of generations to run if args["count"]: gui.frames["bottom"].widgets["generationcount"].storageVar.set(str(args["count"])) + + # Get output path gui.outputPath.set(args["outputpath"]) + # Figure out Sprite Selection def sprite_setter(spriteObject): gui.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteObject"] = spriteObject if args["sprite"] is not None: @@ -52,6 +72,7 @@ def loadcliargs(gui, args, settings=None): spriteNameVar=gui.pages["adjust"].content.spriteNameVar2, randomSpriteVar=gui.randomSprite) +# Load args/settings for Adjust tab def loadadjustargs(gui, settings): options = { "adjust": { diff --git a/gui/randomize/dungeon.py b/gui/randomize/dungeon.py index 1d575521..3b5742f1 100644 --- a/gui/randomize/dungeon.py +++ b/gui/randomize/dungeon.py @@ -1,4 +1,4 @@ -from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT +from tkinter import ttk, Frame, Label, E, W, LEFT, RIGHT import gui.widgets as widgets import json import os @@ -19,6 +19,9 @@ def dungeon_page(parent): mscbLabel = Label(self.frames["keysanity"], text="Shuffle: ") mscbLabel.pack(side=LEFT) + # Load Dungeon Shuffle option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes + # This first set goes in the Keysanity frame with open(os.path.join("resources","app","gui","randomize","dungeon","keysanity.json")) as keysanityItems: myDict = json.load(keysanityItems) dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["keysanity"]) @@ -26,6 +29,7 @@ def dungeon_page(parent): self.widgets[key] = dictWidgets[key] self.widgets[key].pack(side=LEFT) + # These get split left & right self.frames["widgets"] = Frame(self) self.frames["widgets"].pack(anchor=W) with open(os.path.join("resources","app","gui","randomize","dungeon","widgets.json")) as dungeonWidgets: diff --git a/gui/randomize/enemizer.py b/gui/randomize/enemizer.py index cb78281c..f6e5dd14 100644 --- a/gui/randomize/enemizer.py +++ b/gui/randomize/enemizer.py @@ -1,5 +1,5 @@ import os -from tkinter import ttk, filedialog, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, LabelFrame, OptionMenu, N, E, W, LEFT, RIGHT, BOTTOM, X +from tkinter import ttk, filedialog, StringVar, Button, Entry, Frame, Label, N, E, W, LEFT, RIGHT, BOTTOM, X import gui.widgets as widgets import json import os @@ -18,6 +18,7 @@ def enemizer_page(parent,settings): # Enemizer option sections self.frames = {} + # Enemizer option frames self.frames["checkboxes"] = Frame(self) self.frames["checkboxes"].pack(anchor=W) @@ -30,6 +31,9 @@ def enemizer_page(parent,settings): self.frames["rightEnemizerFrame"].pack(side=RIGHT) self.frames["bottomEnemizerFrame"].pack(fill=X) + # Load Enemizer option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes + # These get split left & right with open(os.path.join("resources","app","gui","randomize","enemizer","widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for framename,theseWidgets in myDict.items(): @@ -42,6 +46,7 @@ def enemizer_page(parent,settings): self.widgets[key].pack(packAttrs) ## Enemizer CLI Path + # This one's more-complicated, build it and stuff it enemizerPathFrame = Frame(self.frames["bottomEnemizerFrame"]) enemizerCLIlabel = Label(enemizerPathFrame, text="EnemizerCLI path: ") enemizerCLIlabel.pack(side=LEFT) diff --git a/gui/randomize/entrando.py b/gui/randomize/entrando.py index 3ad6bac4..0ef256b8 100644 --- a/gui/randomize/entrando.py +++ b/gui/randomize/entrando.py @@ -1,4 +1,4 @@ -from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT +from tkinter import ttk, Frame, E, W, LEFT, RIGHT import gui.widgets as widgets import json import os @@ -15,6 +15,11 @@ def entrando_page(parent): self.frames["widgets"] = Frame(self) self.frames["widgets"].pack(anchor=W) + # Load Entrance Randomizer option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes + # Checkboxes go West + # Everything else goes East + # They also get split left & right with open(os.path.join("resources","app","gui","randomize","entrando","widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for framename,theseWidgets in myDict.items(): diff --git a/gui/randomize/gameoptions.py b/gui/randomize/gameoptions.py index eb107df4..4ebcf204 100644 --- a/gui/randomize/gameoptions.py +++ b/gui/randomize/gameoptions.py @@ -1,4 +1,4 @@ -from tkinter import ttk, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, OptionMenu, E, W, LEFT, RIGHT +from tkinter import ttk, StringVar, Button, Entry, Frame, Label, E, W, LEFT, RIGHT from functools import partial import classes.SpriteSelector as spriteSelector import gui.widgets as widgets @@ -17,11 +17,17 @@ def gameoptions_page(top, parent): self.frames["checkboxes"] = Frame(self) self.frames["checkboxes"].pack(anchor=W) + # Game Options frames self.frames["leftRomOptionsFrame"] = Frame(self) self.frames["rightRomOptionsFrame"] = Frame(self) self.frames["leftRomOptionsFrame"].pack(side=LEFT) self.frames["rightRomOptionsFrame"].pack(side=RIGHT) + # Load Game Options widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes + # Checkboxes go West + # Everything else goes East + # They also get split left & right with open(os.path.join("resources","app","gui","randomize","gameoptions","widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for framename,theseWidgets in myDict.items(): @@ -34,6 +40,7 @@ def gameoptions_page(top, parent): self.widgets[key].pack(packAttrs) ## Sprite selection + # This one's more-complicated, build it and stuff it spriteDialogFrame = Frame(self.frames["leftRomOptionsFrame"]) baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:') @@ -75,4 +82,3 @@ def set_sprite(sprite_param, random_sprite=False, spriteSetter=None, spriteNameV spriteNameVar.set(sprite_param.name) if randomSpriteVar: randomSpriteVar.set(random_sprite) - diff --git a/gui/randomize/generation.py b/gui/randomize/generation.py index d5f5ce1c..a76f7aed 100644 --- a/gui/randomize/generation.py +++ b/gui/randomize/generation.py @@ -1,5 +1,4 @@ -import os -from tkinter import ttk, filedialog, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, E, W, LEFT, RIGHT, X +from tkinter import ttk, filedialog, StringVar, Button, Entry, Frame, Label, E, W, LEFT, X import gui.widgets as widgets import json import os @@ -16,6 +15,8 @@ def generation_page(parent,settings): self.frames["checkboxes"] = Frame(self) self.frames["checkboxes"].pack(anchor=W) + # Load Generation Setup option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes with open(os.path.join("resources","app","gui","randomize","generation","checkboxes.json")) as checkboxes: myDict = json.load(checkboxes) dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["checkboxes"]) @@ -26,6 +27,7 @@ def generation_page(parent,settings): self.frames["baserom"] = Frame(self) self.frames["baserom"].pack(anchor=W, fill=X) ## Locate base ROM + # This one's more-complicated, build it and stuff it baseRomFrame = Frame(self.frames["baserom"]) baseRomLabel = Label(baseRomFrame, text='Base Rom: ') self.romVar = StringVar() diff --git a/gui/randomize/item.py b/gui/randomize/item.py index f962d574..046cfc9e 100644 --- a/gui/randomize/item.py +++ b/gui/randomize/item.py @@ -1,8 +1,8 @@ -from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT +from tkinter import ttk, Frame, E, W, LEFT, RIGHT import gui.widgets as widgets import json import os - + def item_page(parent): # Item Randomizer self = ttk.Frame(parent) @@ -13,6 +13,7 @@ def item_page(parent): # Item Randomizer option sections self.frames = {} + # Item Randomizer option frames self.frames["checkboxes"] = Frame(self) self.frames["checkboxes"].pack(anchor=W) @@ -21,6 +22,10 @@ def item_page(parent): self.frames["leftItemFrame"].pack(side=LEFT) self.frames["rightItemFrame"].pack(side=RIGHT) + # Load Item Randomizer option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes + # Checkboxes go West + # Everything else goes East with open(os.path.join("resources","app","gui","randomize","item","widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for framename,theseWidgets in myDict.items(): diff --git a/gui/randomize/multiworld.py b/gui/randomize/multiworld.py index 646b02d1..acdc27af 100644 --- a/gui/randomize/multiworld.py +++ b/gui/randomize/multiworld.py @@ -1,4 +1,4 @@ -from tkinter import ttk, StringVar, Entry, Frame, Label, Spinbox, N, E, W, X, LEFT, RIGHT +from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, X, LEFT import gui.widgets as widgets import json import os @@ -15,6 +15,8 @@ def multiworld_page(parent,settings): self.frames["widgets"] = Frame(self) self.frames["widgets"].pack(anchor=W, fill=X) + # Load Multiworld option widgets as defined by JSON file + # Defns include frame name, widget type, widget options, widget placement attributes with open(os.path.join("resources","app","gui","randomize","multiworld","widgets.json")) as multiworldItems: myDict = json.load(multiworldItems) dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"]) @@ -23,6 +25,7 @@ def multiworld_page(parent,settings): self.widgets[key].pack(side=LEFT, anchor=N) ## List of Player Names + # This one's more-complicated, build it and stuff it key = "names" self.widgets[key] = Frame(self.frames["widgets"]) self.widgets[key].label = Label(self.widgets[key], text='Player names') diff --git a/gui/startinventory/overview.py b/gui/startinventory/overview.py index aaa601b3..b6eb7575 100644 --- a/gui/startinventory/overview.py +++ b/gui/startinventory/overview.py @@ -1,4 +1,4 @@ -from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, LEFT, RIGHT, X, VERTICAL, Y +from tkinter import ttk, Frame, N, E, W, LEFT, X, VERTICAL, Y import gui.widgets as widgets import json import os @@ -9,16 +9,19 @@ def startinventory_page(top,parent): # Starting Inventory self = ttk.Frame(parent) + # Create uniform list columns def create_list_frame(parent, framename): parent.frames[framename] = Frame(parent) parent.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N) parent.frames[framename].thisRow = 0 parent.frames[framename].thisCol = 0 + # Create a vertical rule to help with splitting columns visually def create_vertical_rule(num=1): - for i in range(0,num): + for _ in range(0,num): ttk.Separator(self, orient=VERTICAL).pack(side=LEFT, anchor=N, fill=Y) + # This was in Custom Item Pool, I have no idea what it was but I left it just in case: MikeT def validation(P): if str.isdigit(P) or P == "": return True @@ -31,6 +34,7 @@ def startinventory_page(top,parent): # Starting Inventory option sections self.frames = {} + # Create 5 columns with 2 vertical rules in between each create_list_frame(self,"itemList1") create_vertical_rule(2) create_list_frame(self,"itemList2") @@ -41,6 +45,8 @@ def startinventory_page(top,parent): create_vertical_rule(2) create_list_frame(self,"itemList5") + # Load Starting Inventory option widgets as defined by JSON file, ignoring the ones to be excluded + # Defns include frame name, widget type, widget options, widget placement attributes with open(os.path.join("resources","app","gui","custom","overview","widgets.json")) as widgetDefns: myDict = json.load(widgetDefns) for key in CONST.CANTSTARTWITH: @@ -53,6 +59,7 @@ def startinventory_page(top,parent): for key in dictWidgets: self.startingWidgets[key] = dictWidgets[key] + # Load Custom Starting Inventory settings from settings file, ignoring ones to be excluded for key in CONST.CUSTOMITEMS: if key not in CONST.CANTSTARTWITH: val = 0 diff --git a/gui/widgets.py b/gui/widgets.py index b4d15557..95e7b1af 100644 --- a/gui/widgets.py +++ b/gui/widgets.py @@ -1,8 +1,10 @@ from tkinter import Checkbutton, Entry, Frame, IntVar, Label, OptionMenu, Spinbox, StringVar, RIGHT, X +# Need a dummy class class Empty(): pass +# Override Spinbox to include mousewheel support for changing value class mySpinbox(Spinbox): def __init__(self, *args, **kwargs): Spinbox.__init__(self, *args, **kwargs) @@ -16,6 +18,7 @@ class mySpinbox(Spinbox): elif event.num == 4 or event.delta == 120: self.invoke('buttonup') +# Make a Checkbutton with a label def make_checkbox(self, parent, label, storageVar, manager, managerAttrs): self = Frame(parent, name="checkframe-" + label.lower()) self.storageVar = storageVar @@ -26,6 +29,7 @@ def make_checkbox(self, parent, label, storageVar, manager, managerAttrs): self.checkbox.pack() return self +# Make an OptionMenu with a label and pretty option labels def make_selectbox(self, parent, label, options, storageVar, manager, managerAttrs): def change_storage(*args): self.storageVar.set(options[self.labelVar.get()]) @@ -54,6 +58,7 @@ def make_selectbox(self, parent, label, options, storageVar, manager, managerAtt self.selectbox.pack() return self +# Make a Spinbox with a label, limit 1-100 def make_spinbox(self, parent, label, storageVar, manager, managerAttrs): self = Frame(parent, name="spinframe-" + label.lower()) self.storageVar = storageVar @@ -76,6 +81,8 @@ def make_spinbox(self, parent, label, storageVar, manager, managerAttrs): self.spinbox.pack() return self +# Make an Entry box with a label +# Support for Grid or Pack so that the Custom Item Pool & Starting Inventory pages don't look ugly def make_textbox(self, parent, label, storageVar, manager, managerAttrs): widget = Empty() widget.storageVar = storageVar @@ -98,7 +105,7 @@ def make_textbox(self, parent, label, storageVar, manager, managerAttrs): widget.textbox.pack(managerAttrs["textbox"] if managerAttrs is not None and "textbox" in managerAttrs else None) return widget - +# Make a generic widget def make_widget(self, type, parent, label, storageVar=None, manager=None, managerAttrs=dict(), options=None): widget = None if manager is None: @@ -129,6 +136,7 @@ def make_widget(self, type, parent, label, storageVar=None, manager=None, manage widget.type = type return widget +# Make a generic widget from a dict def make_widget_from_dict(self, defn, parent): type = defn["type"] if "type" in defn else None label = defn["label"]["text"] if "label" in defn and "text" in defn["label"] else "" @@ -138,8 +146,9 @@ def make_widget_from_dict(self, defn, parent): widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options) return widget +# Make a set of generic widgets from a dict def make_widgets_from_dict(self, defns, parent): widgets = {} for key,defn in defns.items(): widgets[key] = make_widget_from_dict(self, defn, parent) - return widgets + return widgets diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 0c427418..f63cffe7 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -25,7 +25,8 @@ "options": { "Standard": "standard", "Open": "open", - "Inverted": "inverted" + "Inverted": "inverted", + "Retro": "retro" } }, "logiclevel": {