Fix CLI precedence

This commit is contained in:
Mike A. Trethewey
2020-02-23 21:38:22 -08:00
parent 01e1e719db
commit 66c57e38b0
7 changed files with 189 additions and 177 deletions

96
CLI.py
View File

@@ -34,7 +34,7 @@ def parse_arguments(argv, no_defaults=False):
multiargs, _ = parser.parse_known_args(argv)
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('--create_spoiler', help='Output a Spoiler File', action='store_true')
parser.add_argument('--create_spoiler', default=defval(settings["create_spoiler"] != 0), help='Output a Spoiler File', action='store_true')
parser.add_argument('--logic', default=defval(settings["logic"]), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'nologic'],
help='''\
Select Enforcement of Item Requirements. (default: %(default)s)
@@ -236,8 +236,8 @@ def parse_arguments(argv, no_defaults=False):
and a few other little things make this more like Zelda-1.
''', action='store_true')
parser.add_argument('--startinventory', default=defval(settings["startinventory"]), help='Specifies a list of items that will be in your starting inventory (separated by commas)')
parser.add_argument('--usestartinventory', default=defval(settings["usestartinventory"] != 0), help='Not supported.')
parser.add_argument('--custom', default=defval(settings["custom"] != 0), help='Not supported.')
parser.add_argument('--customitemarray', default=defval(settings["customitemarray"] != 0), help='Not supported.')
parser.add_argument('--accessibility', default=defval(settings["accessibility"]), const='items', nargs='?', choices=['items', 'locations', 'none'], help='''\
Select Item/Location Accessibility. (default: %(default)s)
Items: You can reach all unique inventory items. No guarantees about
@@ -270,7 +270,7 @@ def parse_arguments(argv, no_defaults=False):
Alternatively, can be a ALttP Rom patched with a Link
sprite that will be extracted.
''')
parser.add_argument('--suppress_rom', help='Do not create an output rom file.', action='store_true')
parser.add_argument('--suppress_rom', default=defval(settings["suppress_rom"] != 0), help='Do not create an output rom file.', action='store_true')
parser.add_argument('--gui', help='Launch the GUI', action='store_true')
parser.add_argument('--jsonout', action='store_true', help='''\
Output .json patch to stdout instead of a patched rom. Used
@@ -324,10 +324,7 @@ def parse_arguments(argv, no_defaults=False):
def get_settings():
# set default settings
settings = {
"multi": 1,
"names": "",
"seed": None,
"count": 1,
"retro": False,
"mode": "open",
"logic": "noglitches",
"goal": "ganon",
@@ -340,41 +337,53 @@ def get_settings():
"progressive": "on",
"accessibility": "items",
"algorithm": "balanced",
"shuffle": "vanilla",
"door_shuffle": "basic",
"experimental": 0,
"dungeon_counters": "off",
"heartbeep": "normal",
"heartcolor": "red",
"fastmenu": "normal",
"create_spoiler": False,
"skip_playthrough": True,
"suppress_rom": False,
"openpyramid": False,
"shuffleganon": False,
"shuffle": "vanilla",
"shufflepots": False,
"shuffleenemies": "none",
"shufflebosses": "none",
"enemy_damage": "default",
"enemy_health": "default",
"enemizercli": os.path.join(".","EnemizerCLI","EnemizerCLI.Core"),
"mapshuffle": False,
"compassshuffle": False,
"keyshuffle": False,
"bigkeyshuffle": False,
"keysanity": False,
"retro": False,
"startinventory": "",
"quickswap": False,
"door_shuffle": "basic",
"experimental": 0,
"dungeon_counters": "off",
"multi": 1,
"names": "",
"hints": True,
"disablemusic": False,
"quickswap": False,
"heartcolor": "red",
"heartbeep": "normal",
"sprite": None,
"fastmenu": "normal",
"ow_palettes": "default",
"uw_palettes": "default",
"shuffleganon": True,
"hints": True,
"enemizercli": os.path.join(".","EnemizerCLI","EnemizerCLI.Core"),
"shufflebosses": "none",
"shuffleenemies": "none",
"enemy_health": "default",
"enemy_damage": "default",
"shufflepots": False,
"create_spoiler": False,
"skip_playthrough": True,
"suppress_rom": False,
"usestartinventory": False,
"custom": False,
"rom": os.path.join(".","Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"),
"seed": None,
"count": 1,
"startinventory": "",
"beemizer": 0,
"remote_items": False,
"race": False,
"custom": False,
"usestartinventory": False,
"customitemarray": {
"bow": 0,
"progressivebow": 2,
@@ -448,8 +457,6 @@ def get_settings():
"rupoor": 0,
"rupoorcost": 10
},
"rom": os.path.join(".","Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"),
"sprite": None,
"randomSprite": False,
"outputpath": os.path.join(".")
}
@@ -467,3 +474,28 @@ def get_settings():
for k,v in data.items():
settings[k] = v
return settings
def get_args_priority(settings_args, gui_args, cli_args):
args = {}
args["settings"] = get_settings() if settings_args is None else settings_args
args["gui"] = {} if gui_args is None else gui_args
args["cli"] = cli_args
args["load"] = args["settings"]
if args["gui"] is not None:
for k in args["gui"]:
if k not in args["load"] or args["load"][k] != args["gui"]:
args["load"][k] = args["gui"][k]
if args["cli"] is None:
args["cli"] = {}
cli = vars(parse_arguments(None))
for k,v in cli.items():
if isinstance(v,dict):
args["cli"][k] = v[1]
else:
args["cli"][k] = v
if k not in args["load"] or args["load"][k] != args["cli"]:
args["load"][k] = args["cli"][k]
return args

View File

@@ -59,7 +59,8 @@ def start():
seed = random.randint(0, 999999999)
for fail in failures:
logger.info('%s seed failed with: %s', fail[1], fail[0])
logger.info('Generation fail rate: %f%%', 100*len(failures)/args.count)
logger.info('Generation fail rate: %f%%', 100 * len(failures) / args.count)
logger.info('Generation success rate: %f%%', 100 * (args.count - len(failures)) / args.count)
else:
main(seed=args.seed, args=args)

35
Gui.py
View File

@@ -2,10 +2,10 @@
import json
import os
import sys
from tkinter import Tk, BOTTOM, TOP, StringVar, BooleanVar, X, BOTH, ttk
from tkinter import Tk, Button, BOTTOM, TOP, StringVar, BooleanVar, X, BOTH, RIGHT, ttk, messagebox
from argparse import Namespace
from CLI import get_settings
from CLI import get_settings, get_args_priority
from DungeonRandomizer import parse_arguments
from gui.adjust.overview import adjust_page
from gui.startinventory.overview import startinventory_page
@@ -36,28 +36,39 @@ def guiMain(args=None):
f.write(json.dumps(args, indent=2))
os.chmod(os.path.join(settings_path, "settings.json"),0o755)
# routine for exiting the app
def guiExit():
def save_settings_from_gui():
gui_args = vars(create_guiargs(self))
if self.randomSprite.get():
gui_args['sprite'] = 'random'
elif gui_args['sprite']:
gui_args['sprite'] = gui_args['sprite'].name
save_settings(gui_args)
messagebox.showinfo("Door Shuffle " + ESVersion,"Settings saved from GUI.")
# routine for exiting the app
def guiExit():
dosave = messagebox.askyesno("Door Shuffle " + ESVersion, "Save settings before exit?")
if dosave:
save_settings_from_gui()
sys.exit(0)
# make main window
# add program title & version number
mainWindow = Tk()
self = mainWindow
mainWindow.wm_title("Door Shuffle %s" % ESVersion)
mainWindow.protocol("WM_DELETE_WINDOW", guiExit) # intercept when user clicks the X
# set program icon
set_icon(mainWindow)
# get args
# getting Settings & CLI (no GUI built yet)
self.args = get_args_priority(None, None, None)
# get saved settings
self.settings = get_settings()
self.settings = self.args["settings"]
# make array for pages
self.pages = {}
@@ -73,7 +84,7 @@ def guiMain(args=None):
self.notebook.add(self.pages["randomizer"], text='Randomize')
self.notebook.add(self.pages["adjust"], text='Adjust')
self.notebook.add(self.pages["startinventory"], text='Starting Inventory')
self.notebook.add(self.pages["custom"], text='Custom')
self.notebook.add(self.pages["custom"], text='Custom Item Pool')
self.notebook.pack()
# randomizer controls
@@ -125,6 +136,10 @@ def guiMain(args=None):
# bottom of window: Open Output Directory, Open Documentation (if exists)
self.frames["bottom"] = bottom_frame(self, self, None)
## Save Settings Button
savesettingsButton = Button(self.frames["bottom"], text='Save Settings to File', command=save_settings_from_gui)
savesettingsButton.pack(side=RIGHT)
# set bottom frame to main window
self.frames["bottom"].pack(side=BOTTOM, fill=X, padx=5, pady=5)
@@ -141,7 +156,7 @@ def guiMain(args=None):
# Custom Controls
self.pages["custom"].content = custom_page(self,self.pages["custom"])
self.pages["custom"].content.pack(side=TOP, pady=(17,0))
self.pages["custom"].content.pack(side=TOP, fill=BOTH, expand=True)
def validation(P):
if str.isdigit(P) or P == "":
@@ -150,12 +165,12 @@ def guiMain(args=None):
return False
vcmd=(self.pages["custom"].content.register(validation), '%P')
# load args
loadcliargs(self, self.args["load"])
# load adjust settings into options
loadadjustargs(self, self.settings)
# load args from CLI into options
loadcliargs(self, args, self.settings)
mainWindow.mainloop()

View File

@@ -20,6 +20,11 @@ CUSTOMITEMS = [
"rupoorcost"
]
CANTSTARTWITH = [
"triforcepiecesgoal", "triforce", "rupoor",
"rupoorcost"
]
CUSTOMITEMLABELS = [
"Bow", "Progressive Bow", "Blue Boomerang", "Red Boomerang", "Hookshot",
"Mushroom", "Magic Powder", "Fire Rod", "Ice Rod", "Bombos",
@@ -41,3 +46,63 @@ CUSTOMITEMLABELS = [
"Small Key (Universal)", "Triforce Piece", "Triforce Piece Goal", "Triforce", "Rupoor",
"Rupoor Cost"
]
SETTINGSTOPROCESS = {
"randomizer": {
"item": {
"retro": "retro",
"worldstate": "mode",
"logiclevel": "logic",
"goal": "goal",
"crystals_gt": "crystals_gt",
"crystals_ganon": "crystals_ganon",
"weapons": "swords",
"itempool": "difficulty",
"itemfunction": "item_functionality",
"timer": "timer",
"progressives": "progressive",
"accessibility": "accessibility",
"sortingalgo": "algorithm"
},
"entrance": {
"openpyramid": "openpyramid",
"shuffleganon": "shuffleganon",
"entranceshuffle": "shuffle"
},
"enemizer": {
"potshuffle": "shufflepots",
"enemyshuffle": "shuffleenemies",
"bossshuffle": "shufflebosses",
"enemydamage": "enemy_damage",
"enemyhealth": "enemy_health"
},
"dungeon": {
"mapshuffle": "mapshuffle",
"compassshuffle": "compassshuffle",
"smallkeyshuffle": "keyshuffle",
"bigkeyshuffle": "bigkeyshuffle",
"dungeondoorshuffle": "door_shuffle",
"experimental": "experimental",
"dungeon_counters": "dungeon_counters"
},
"multiworld": {
"names": "names"
},
"gameoptions": {
"hints": "hints",
"nobgm": "disablemusic",
"quickswap": "quickswap",
"heartcolor": "heartcolor",
"heartbeep": "heartbeep",
"menuspeed": "fastmenu",
"owpalettes": "ow_palettes",
"uwpalettes": "uw_palettes"
},
"generation": {
"spoiler": "create_spoiler",
"suppressrom": "suppress_rom",
"usestartinventory": "usestartinventory",
"usecustompool": "custom"
}
}
}

View File

@@ -1,5 +1,6 @@
from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, Spinbox, E, W, LEFT, RIGHT, X
from argparse import Namespace
from functools import partial
import logging
import os
import random
@@ -79,11 +80,12 @@ def bottom_frame(self, parent, args=None):
openOutputButton = Button(self, text='Open Output Directory', command=open_output)
openOutputButton.pack(side=RIGHT)
## Documentation Button
if os.path.exists(local_path('README.html')):
def open_readme():
open_file(local_path('README.html'))
openReadmeButton = Button(self, text='Open Documentation', command=open_readme)
openReadmeButton.pack()
openReadmeButton.pack(side=RIGHT)
return self
@@ -93,63 +95,8 @@ def create_guiargs(parent):
# set up settings to gather
# Page::Subpage::GUI-id::param-id
options = {
"randomizer": {
"item": {
"retro": "retro",
"worldstate": "mode",
"logiclevel": "logic",
"goal": "goal",
"crystals_gt": "crystals_gt",
"crystals_ganon": "crystals_ganon",
"weapons": "swords",
"itempool": "difficulty",
"itemfunction": "item_functionality",
"timer": "timer",
"progressives": "progressive",
"accessibility": "accessibility",
"sortingalgo": "algorithm"
},
"entrance": {
"openpyramid": "openpyramid",
"shuffleganon": "shuffleganon",
"entranceshuffle": "shuffle"
},
"enemizer": {
"potshuffle": "shufflepots",
"enemyshuffle": "shuffleenemies",
"bossshuffle": "shufflebosses",
"enemydamage": "enemy_damage",
"enemyhealth": "enemy_health"
},
"dungeon": {
"mapshuffle": "mapshuffle",
"compassshuffle": "compassshuffle",
"smallkeyshuffle": "keyshuffle",
"bigkeyshuffle": "bigkeyshuffle",
"dungeondoorshuffle": "door_shuffle",
"experimental": "experimental",
"dungeon_counters": "dungeon_counters"
},
"multiworld": {
"names": "names"
},
"gameoptions": {
"hints": "hints",
"nobgm": "disablemusic",
"quickswap": "quickswap",
"heartcolor": "heartcolor",
"heartbeep": "heartbeep",
"menuspeed": "fastmenu",
"owpalettes": "ow_palettes",
"uwpalettes": "uw_palettes"
},
"generation": {
"spoiler": "create_spoiler",
"suppressrom": "suppress_rom"
}
}
}
options = CONST.SETTINGSTOPROCESS
for mainpage in options:
for subpage in options[mainpage]:
for widget in options[mainpage][subpage]:
@@ -184,7 +131,7 @@ def create_guiargs(parent):
guiargs.customitemarray = {}
guiargs.startinventoryarray = {}
for customitem in customitems:
if customitem not in ["triforcepiecesgoal", "rupoorcost"]:
if customitem not in ["triforcepiecesgoal", "triforce", "rupoor", "rupoorcost"]:
amount = int(parent.pages["startinventory"].content.startingWidgets[customitem].storageVar.get())
guiargs.startinventoryarray[customitem] = amount
for i in range(0, amount):

View File

@@ -1,96 +1,45 @@
from classes.SpriteSelector import SpriteSelector as spriteSelector
from gui.randomize.gameoptions import set_sprite
from Rom import Sprite
import classes.constants as CONST
def loadcliargs(gui, args, settings=None):
if args is not None:
for k, v in vars(args).items():
if type(v) is dict:
setattr(args, k, v[1]) # only get values for player 1 for now
# for k, v in vars(args).items():
# if type(v) is dict:
# setattr(args, k, v[1]) # only get values for player 1 for now
# load values from commandline args
# set up options to get
# Page::Subpage::GUI-id::param-id
options = {
"randomizer": {
"item": {
"retro": "retro",
"worldstate": "mode",
"logiclevel": "logic",
"goal": "goal",
"crystals_gt": "crystals_gt",
"crystals_ganon": "crystals_ganon",
"weapons": "swords",
"itempool": "difficulty",
"itemfunction": "item_functionality",
"timer": "timer",
"progressives": "progressive",
"accessibility": "accessibility",
"sortingalgo": "algorithm"
},
"entrance": {
"openpyramid": "openpyramid",
"shuffleganon": "shuffleganon",
"entranceshuffle": "shuffle"
},
"enemizer": {
"potshuffle": "shufflepots",
"enemyshuffle": "shuffleenemies",
"bossshuffle": "shufflebosses",
"enemydamage": "enemy_damage",
"enemyhealth": "enemy_health"
},
"dungeon": {
"mapshuffle": "mapshuffle",
"compassshuffle": "compassshuffle",
"smallkeyshuffle": "keyshuffle",
"bigkeyshuffle": "bigkeyshuffle",
"dungeondoorshuffle": "door_shuffle",
"experimental": "experimental"
},
"gameoptions": {
"hints": "hints",
"nobgm": "disablemusic",
"quickswap": "quickswap",
"heartcolor": "heartcolor",
"heartbeep": "heartbeep",
"menuspeed": "fastmenu",
"owpalettes": "ow_palettes",
"uwpalettes": "uw_palettes"
},
"generation": {
"spoiler": "create_spoiler",
"suppressrom": "suppress_rom"
}
}
}
options = CONST.SETTINGSTOPROCESS
for mainpage in options:
for subpage in options[mainpage]:
for widget in options[mainpage][subpage]:
arg = options[mainpage][subpage][widget]
gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(getattr(args, arg))
gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[arg])
if subpage == "gameoptions" and not widget == "hints":
hasSettings = settings is not None
hasWidget = ("adjust." + widget) in settings if hasSettings else None
if hasWidget is None:
gui.pages["adjust"].content.widgets[widget].storageVar.set(getattr(args, arg))
gui.pages["adjust"].content.widgets[widget].storageVar.set(args[arg])
gui.pages["randomizer"].pages["enemizer"].enemizerCLIpathVar.set(args.enemizercli)
gui.pages["randomizer"].pages["generation"].romVar.set(args.rom)
gui.pages["randomizer"].pages["enemizer"].enemizerCLIpathVar.set(args["enemizercli"])
gui.pages["randomizer"].pages["generation"].romVar.set(args["rom"])
if args.multi:
gui.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.set(str(args.multi))
if args.seed:
gui.frames["bottom"].seedVar.set(str(args.seed))
if args.count:
gui.frames["bottom"].widgets["generationcount"].storageVar.set(str(args.count))
gui.outputPath.set(args.outputpath)
if args["multi"]:
gui.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.set(str(args["multi"]))
if args["seed"]:
gui.frames["bottom"].seedVar.set(str(args["seed"]))
if args["count"]:
gui.frames["bottom"].widgets["generationcount"].storageVar.set(str(args["count"]))
gui.outputPath.set(args["outputpath"])
def sprite_setter(spriteObject):
gui.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteObject"] = spriteObject
if args.sprite is not None:
sprite_obj = args.sprite if isinstance(args.sprite, Sprite) else Sprite(args.sprite)
if args["sprite"] is not None:
sprite_obj = args.sprite if isinstance(args["sprite"], Sprite) else Sprite(args["sprite"])
r_sprite_flag = args.randomSprite if hasattr(args, 'randomSprite') else False
set_sprite(sprite_obj, r_sprite_flag, spriteSetter=sprite_setter,
spriteNameVar=gui.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteNameVar"],
@@ -98,9 +47,9 @@ def loadcliargs(gui, args, settings=None):
def sprite_setter_adj(spriteObject):
gui.pages["adjust"].content.sprite = spriteObject
if args.sprite is not None:
if args["sprite"] is not None:
sprite_obj = args.sprite if isinstance(args.sprite, Sprite) else Sprite(args.sprite)
r_sprite_flag = args.randomSprite if hasattr(args, 'randomSprite') else False
r_sprite_flag = args["randomSprite"] if hasattr(args, 'randomSprite') else False
set_sprite(sprite_obj, r_sprite_flag, spriteSetter=sprite_setter_adj,
spriteNameVar=gui.pages["adjust"].content.spriteNameVar2,
randomSpriteVar=gui.randomSprite)

View File

@@ -43,15 +43,18 @@ def startinventory_page(top,parent):
with open(os.path.join("resources","app","gui","custom","overview","widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns)
del myDict["itemList5"]["triforcepiecesgoal"]
del myDict["itemList5"]["rupoorcost"]
for key in CONST.CANTSTARTWITH:
for num in range(1, 5 + 1):
thisList = "itemList" + str(num)
if key in myDict[thisList]:
del myDict[thisList][key]
for framename,theseWidgets in myDict.items():
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets:
self.startingWidgets[key] = dictWidgets[key]
for key in CONST.CUSTOMITEMS:
if key not in ["triforcepiecesgoal", "rupoorcost"]:
if key not in CONST.CANTSTARTWITH:
val = 0
if key in top.settings["startinventoryarray"]:
val = top.settings["startinventoryarray"][key]