296 lines
10 KiB
Python
296 lines
10 KiB
Python
import argparse
|
|
import copy
|
|
import json
|
|
import os
|
|
import logging
|
|
import random
|
|
import textwrap
|
|
import shlex
|
|
import sys
|
|
|
|
import source.classes.constants as CONST
|
|
from source.classes.BabelFish import BabelFish
|
|
|
|
|
|
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
|
|
|
|
def _get_help_string(self, action):
|
|
return textwrap.dedent(action.help)
|
|
|
|
def parse_arguments(argv, no_defaults=False):
|
|
def defval(value):
|
|
return value if not no_defaults else None
|
|
|
|
# get settings
|
|
settings = get_settings()
|
|
|
|
lang = "en"
|
|
if argv is not None:
|
|
priority = get_args_priority(None, None, argv)
|
|
if "load" in priority:
|
|
priority = priority["load"]
|
|
if "lang" in priority:
|
|
lang = priority["lang"]
|
|
|
|
fish = BabelFish(lang=lang)
|
|
|
|
# we need to know how many players we have first
|
|
parser = argparse.ArgumentParser(add_help=False)
|
|
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
|
|
multiargs, _ = parser.parse_known_args(argv)
|
|
|
|
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
|
|
|
|
# get args
|
|
args = []
|
|
with open(os.path.join("resources","app","cli","args.json")) as argsFile:
|
|
args = json.load(argsFile)
|
|
for arg in args:
|
|
argdata = args[arg]
|
|
argname = "--" + arg
|
|
argatts = {}
|
|
argatts["help"] = "(default: %(default)s)"
|
|
if "action" in argdata:
|
|
argatts["action"] = argdata["action"]
|
|
if "choices" in argdata:
|
|
argatts["choices"] = argdata["choices"]
|
|
argatts["const"] = argdata["choices"][0]
|
|
argatts["default"] = argdata["choices"][0]
|
|
argatts["nargs"] = "?"
|
|
elif arg in settings:
|
|
argatts["default"] = defval(settings[arg] != 0) if "type" in argdata and argdata["type"] == "bool" else defval(settings[arg])
|
|
arghelp = fish.translate("cli","help",arg)
|
|
if "help" in argdata and argdata["help"] == "suppress":
|
|
argatts["help"] = argparse.SUPPRESS
|
|
elif not isinstance(arghelp,str):
|
|
argatts["help"] = '\n'.join(arghelp).replace("\\'","'")
|
|
parser.add_argument(argname,**argatts)
|
|
|
|
parser.add_argument('--seed', default=defval(int(settings["seed"]) if settings["seed"] != "" and settings["seed"] is not None else None), help="\n".join(fish.translate("cli","help","seed")), type=int)
|
|
parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help="\n".join(fish.translate("cli","help","count")), type=int)
|
|
parser.add_argument('--customitemarray', default={}, help=argparse.SUPPRESS)
|
|
|
|
# included for backwards compatibility
|
|
parser.add_argument('--beemizer', default=defval(settings["beemizer"]), type=lambda value: min(max(int(value), 0), 4))
|
|
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
|
|
parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1))
|
|
|
|
if multiargs.multi:
|
|
for player in range(1, multiargs.multi + 1):
|
|
parser.add_argument(f'--p{player}', default=defval(''), help=argparse.SUPPRESS)
|
|
|
|
ret = parser.parse_args(argv)
|
|
|
|
if ret.keysanity:
|
|
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = [True] * 4
|
|
|
|
if multiargs.multi:
|
|
defaults = copy.deepcopy(ret)
|
|
for player in range(1, multiargs.multi + 1):
|
|
playerargs = parse_arguments(shlex.split(getattr(ret,f"p{player}")), True)
|
|
|
|
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
|
'shuffle', 'door_shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid',
|
|
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
|
|
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
|
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
|
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep',
|
|
'remote_items']:
|
|
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
|
if player == 1:
|
|
setattr(ret, name, {1: value})
|
|
else:
|
|
getattr(ret, name)[player] = value
|
|
|
|
return ret
|
|
|
|
|
|
def get_settings():
|
|
# set default settings
|
|
settings = {
|
|
"lang": "en",
|
|
"retro": False,
|
|
"mode": "open",
|
|
"logic": "noglitches",
|
|
"goal": "ganon",
|
|
"crystals_gt": "7",
|
|
"crystals_ganon": "7",
|
|
"swords": "random",
|
|
"difficulty": "normal",
|
|
"item_functionality": "normal",
|
|
"timer": "none",
|
|
"progressive": "on",
|
|
"accessibility": "items",
|
|
"algorithm": "balanced",
|
|
|
|
"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,
|
|
"door_shuffle": "basic",
|
|
"experimental": 0,
|
|
"dungeon_counters": "default",
|
|
|
|
"multi": 1,
|
|
"names": "",
|
|
|
|
"hints": True,
|
|
"disablemusic": False,
|
|
"quickswap": False,
|
|
"heartcolor": "red",
|
|
"heartbeep": "normal",
|
|
"sprite": None,
|
|
"fastmenu": "normal",
|
|
"ow_palettes": "default",
|
|
"uw_palettes": "default",
|
|
|
|
"create_spoiler": False,
|
|
"skip_playthrough": False,
|
|
"suppress_rom": False,
|
|
"usestartinventory": False,
|
|
"custom": False,
|
|
"rom": os.path.join(".", "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"),
|
|
|
|
"seed": None,
|
|
"count": None,
|
|
"startinventory": "",
|
|
"beemizer": 0,
|
|
"remote_items": False,
|
|
"race": False,
|
|
"customitemarray": {
|
|
"bow": 0,
|
|
"progressivebow": 2,
|
|
"boomerang": 1,
|
|
"redmerang": 1,
|
|
"hookshot": 1,
|
|
"mushroom": 1,
|
|
"powder": 1,
|
|
"firerod": 1,
|
|
"icerod": 1,
|
|
"bombos": 1,
|
|
"ether": 1,
|
|
"quake": 1,
|
|
"lamp": 1,
|
|
"hammer": 1,
|
|
"shovel": 1,
|
|
"flute": 1,
|
|
"bugnet": 1,
|
|
"book": 1,
|
|
"bottle": 4,
|
|
"somaria": 1,
|
|
"byrna": 1,
|
|
"cape": 1,
|
|
"mirror": 1,
|
|
"boots": 1,
|
|
"powerglove": 0,
|
|
"titansmitt": 0,
|
|
"progressiveglove": 2,
|
|
"flippers": 1,
|
|
"pearl": 1,
|
|
"heartpiece": 24,
|
|
"heartcontainer": 10,
|
|
"sancheart": 1,
|
|
"sword1": 0,
|
|
"sword2": 0,
|
|
"sword3": 0,
|
|
"sword4": 0,
|
|
"progressivesword": 4,
|
|
"shield1": 0,
|
|
"shield2": 0,
|
|
"shield3": 0,
|
|
"progressiveshield": 3,
|
|
"mail2": 0,
|
|
"mail3": 0,
|
|
"progressivemail": 2,
|
|
"halfmagic": 1,
|
|
"quartermagic": 0,
|
|
"bombsplus5": 0,
|
|
"bombsplus10": 0,
|
|
"arrowsplus5": 0,
|
|
"arrowsplus10": 0,
|
|
"arrow1": 1,
|
|
"arrow10": 12,
|
|
"bomb1": 0,
|
|
"bomb3": 16,
|
|
"bomb10": 1,
|
|
"rupee1": 2,
|
|
"rupee5": 4,
|
|
"rupee20": 28,
|
|
"rupee50": 7,
|
|
"rupee100": 1,
|
|
"rupee300": 5,
|
|
"blueclock": 0,
|
|
"greenclock": 0,
|
|
"redclock": 0,
|
|
"silversupgrade": 0,
|
|
"generickeys": 0,
|
|
"triforcepieces": 0,
|
|
"triforcepiecesgoal": 0,
|
|
"triforce": 0,
|
|
"rupoor": 0,
|
|
"rupoorcost": 10
|
|
},
|
|
"randomSprite": False,
|
|
"outputpath": os.path.join("."),
|
|
"saveonexit": "ask",
|
|
"outputname": "",
|
|
"startinventoryarray": {}
|
|
}
|
|
|
|
if sys.platform.lower().find("windows"):
|
|
settings["enemizercli"] += ".exe"
|
|
|
|
# read saved settings file if it exists and set these
|
|
settings_path = os.path.join(".", "resources", "user", "settings.json")
|
|
if os.path.exists(settings_path):
|
|
with(open(settings_path)) as json_file:
|
|
data = json.load(json_file)
|
|
for k, v in data.items():
|
|
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
|
|
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) and 1 in v:
|
|
args["cli"][k] = v[1]
|
|
else:
|
|
args["cli"][k] = v
|
|
load_doesnt_have_key = k not in args["load"]
|
|
different_val = (k in args["load"] and k in args["cli"]) and (args["load"][k] != args["cli"][k])
|
|
cli_has_empty_dict = k in args["cli"] and isinstance(args["cli"][k], dict) and len(args["cli"][k]) == 0
|
|
if load_doesnt_have_key or different_val:
|
|
if not cli_has_empty_dict:
|
|
args["load"][k] = args["cli"][k]
|
|
|
|
return args
|