Merge branch 'DoorDev' into DoorDevUnstable

# Conflicts:
#	Main.py
#	RELEASENOTES.md
This commit is contained in:
randall.rupper
2021-03-05 10:43:05 -07:00
8 changed files with 103 additions and 89 deletions

1
.gitignore vendored
View File

@@ -36,3 +36,4 @@ get-pip.py
venv
test
data/base2current.json

View File

@@ -1377,6 +1377,11 @@ class Door(object):
else:
self.passage = False
def kind(self, world):
if self.roomIndex != -1 and self.doorListPos != -1:
return world.get_room(self.roomIndex, self.player).kind(self)
return None
def __eq__(self, other):
return isinstance(other, self.__class__) and self.name == other.name

95
CLI.py
View File

@@ -6,7 +6,6 @@ import textwrap
import shlex
import sys
import source.classes.constants as CONST
from source.classes.BabelFish import BabelFish
from Utils import update_deprecated_args
@@ -17,6 +16,7 @@ class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
def _get_help_string(self, action):
return textwrap.dedent(action.help)
def parse_cli(argv, no_defaults=False):
def defval(value):
return value if not no_defaults else None
@@ -28,44 +28,49 @@ def parse_cli(argv, no_defaults=False):
fish = BabelFish(lang=lang)
# we need to know how many players we have first
# also if we're loading our own settings file, we should do that now
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--settingsfile', help="input json file of settings", type=str)
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
multiargs, _ = parser.parse_known_args(argv)
if multiargs.settingsfile:
settings = apply_settings_file(settings, multiargs.settingsfile)
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"] = "?"
if arg in settings:
default = settings[arg]
if "type" in argdata and argdata["type"] == "bool":
default = settings[arg] != 0
argatts["default"] = defval(default)
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("\\'","'")
else:
argatts["help"] = arghelp + " " + argatts["help"]
parser.add_argument(argname,**argatts)
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"] = "?"
if arg in settings:
default = settings[arg]
if "type" in argdata and argdata["type"] == "bool":
default = settings[arg] != 0
argatts["default"] = defval(default)
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("\\'", "'")
else:
argatts["help"] = arghelp + " " + argatts["help"]
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('--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
@@ -73,6 +78,7 @@ def parse_cli(argv, no_defaults=False):
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
parser.add_argument('--securerandom', default=defval(settings["securerandom"]), action='store_true')
parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1))
parser.add_argument('--settingsfile', dest="filename", help="input json file of settings", type=str)
if multiargs.multi:
for player in range(1, multiargs.multi + 1):
@@ -86,7 +92,7 @@ def parse_cli(argv, no_defaults=False):
if multiargs.multi:
defaults = copy.deepcopy(ret)
for player in range(1, multiargs.multi + 1):
playerargs = parse_cli(shlex.split(getattr(ret,f"p{player}")), True)
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
@@ -104,6 +110,15 @@ def parse_cli(argv, no_defaults=False):
return ret
def apply_settings_file(settings, settings_path):
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
def parse_settings():
# set default settings
settings = {
@@ -160,7 +175,7 @@ def parse_settings():
"quickswap": False,
"heartcolor": "red",
"heartbeep": "normal",
"sprite": os.path.join(".","data","sprites","official","001.link.1.zspr"),
"sprite": os.path.join(".", "data", "sprites", "official", "001.link.1.zspr"),
"fastmenu": "normal",
"ow_palettes": "default",
"uw_palettes": "default",
@@ -266,17 +281,15 @@ def parse_settings():
# 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
settings = apply_settings_file(settings, settings_path)
return settings
# Priority fallback is:
# 1: CLI
# 2: Settings file
# 2: Settings file(s)
# 3: Canned defaults
def get_args_priority(settings_args, gui_args, cli_args):
args = {}
args["settings"] = parse_settings() if settings_args is None else settings_args
@@ -303,7 +316,7 @@ def get_args_priority(settings_args, gui_args, cli_args):
for k in vars(args["cli"]):
load_doesnt_have_key = k not in args["load"]
cli_val = cli[k]
if isinstance(cli_val,dict) and 1 in cli_val:
if isinstance(cli_val, dict) and 1 in cli_val:
cli_val = cli_val[1]
different_val = (k in args["load"] and k in cli) and (str(args["load"][k]) != str(cli_val))
cli_has_empty_dict = k in cli and isinstance(cli_val, dict) and len(cli_val) == 0
@@ -312,9 +325,9 @@ def get_args_priority(settings_args, gui_args, cli_args):
args["load"][k] = cli_val
newArgs = {}
for key in [ "settings", "gui", "cli", "load" ]:
for key in ["settings", "gui", "cli", "load"]:
if args[key]:
if isinstance(args[key],dict):
if isinstance(args[key], dict):
newArgs[key] = argparse.Namespace(**args[key])
else:
newArgs[key] = args[key]

View File

@@ -130,6 +130,7 @@ New item counter modified to show total
* 0.3.1.5-u
* Ganon hints fixed for shops
* Added support for a settings file so SahasrahBot and the main website can use it easier. (Thanks Synack)
* 0.3.1.4-u
* Fix for Blind when shuffled to TT and another dungeon
* Remove use of RaceRandom

View File

@@ -6,19 +6,24 @@ import sys
import xml.etree.ElementTree as ET
from collections import defaultdict
def int16_as_bytes(value):
value = value & 0xFFFF
return [value & 0xFF, (value >> 8) & 0xFF]
def int32_as_bytes(value):
value = value & 0xFFFFFFFF
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
def pc_to_snes(value):
return ((value<<1) & 0x7F0000)|(value & 0x7FFF)|0x8000
return ((value << 1) & 0x7F0000) | (value & 0x7FFF) | 0x8000
def snes_to_pc(value):
return ((value & 0x7F0000)>>1)|(value & 0x7FFF)
return ((value & 0x7F0000) >> 1) | (value & 0x7FFF)
def parse_player_names(names, players, teams):
names = [n for n in re.split(r'[, ]', names) if n]
@@ -32,9 +37,11 @@ def parse_player_names(names, players, teams):
names = names[players:]
return ret
def is_bundled():
return getattr(sys, 'frozen', False)
def local_path(path):
# just do stuff here and bail
return os.path.join(".", path)
@@ -44,51 +51,26 @@ def local_path(path):
if is_bundled():
# we are running in a bundle
local_path.cached_path = sys._MEIPASS # pylint: disable=protected-access,no-member
local_path.cached_path = sys._MEIPASS # pylint: disable=protected-access,no-member
else:
# we are running in a normal Python environment
local_path.cached_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(local_path.cached_path, path)
local_path.cached_path = None
def output_path(path):
# just do stuff here and bail
return os.path.join(".", path)
if output_path.cached_path is not None:
return os.path.join(output_path.cached_path, path)
if not is_bundled():
if output_path.cached_path is None:
output_path.cached_path = '.'
return os.path.join(output_path.cached_path, path)
else:
# has been packaged, so cannot use CWD for output.
if sys.platform == 'win32':
#windows
documents = os.path.join(os.path.expanduser("~"),"Documents")
elif sys.platform == 'darwin':
from AppKit import NSSearchPathForDirectoriesInDomains # pylint: disable=import-error
# http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains
NSDocumentDirectory = 9
NSUserDomainMask = 1
# True for expanding the tilde into a fully qualified path
documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0]
elif sys.platform.find("linux") or sys.platform.find("ubuntu") or sys.platform.find("unix"):
documents = os.path.join(os.path.expanduser("~"),"Documents")
else:
raise NotImplementedError('Not supported yet')
return os.path.join(output_path.cached_path, path)
output_path.cached_path = os.path.join(documents, 'ALttPDoorRandomizer')
if not os.path.exists(output_path.cached_path):
os.makedirs(output_path.cached_path)
if not os.path.join(output_path.cached_path, path):
os.makedirs(os.path.join(output_path.cached_path, path))
return os.path.join(output_path.cached_path, path)
output_path.cached_path = None
def open_file(filename):
if sys.platform == 'win32':
os.startfile(filename)
@@ -96,15 +78,17 @@ def open_file(filename):
open_command = 'open' if sys.platform == 'darwin' else 'xdg-open'
subprocess.call([open_command, filename])
def close_console():
if sys.platform == 'win32':
#windows
# windows
import ctypes.wintypes
try:
ctypes.windll.kernel32.FreeConsole()
except Exception:
pass
def make_new_base2current(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', new_rom='working.sfc'):
from collections import OrderedDict
import json
@@ -125,7 +109,7 @@ def make_new_base2current(old_rom='Zelda no Densetsu - Kamigami no Triforce (Jap
if offset - 1 in out_data:
out_data[offset-1].extend(out_data.pop(offset))
with open('data/base2current.json', 'wt') as outfile:
json.dump([{key:value} for key, value in out_data.items()], outfile, separators=(",", ":"))
json.dump([{key: value} for key, value in out_data.items()], outfile, separators=(",", ":"))
basemd5 = hashlib.md5()
basemd5.update(new_rom_data)
@@ -241,7 +225,6 @@ def room_palette_data(old_rom):
print(f'{hex(header_offset)}: {[hex(x) for x in rooms]}')
# Palette notes:
# HC: 0
# Sewer/Dungeon: 1
@@ -303,21 +286,22 @@ def print_wiki_doors_by_region(d_regions, world, player):
toprint += ('| '+'<br />'.join(strs_to_print))
toprint += "\n"
toprint += ('|}') + "\n"
with open(os.path.join(".","resources", "user", "regions-" + d + ".txt"),"w+") as f:
with open(os.path.join(".", "resources", "user", "regions-" + d + ".txt"), "w+") as f:
f.write(toprint)
def update_deprecated_args(args):
if args:
argVars = vars(args)
truthy = [ 1, True, "True", "true" ]
truthy = [1, True, "True", "true"]
# Hints default to TRUE
# Don't do: Yes
# Do: No
if "no_hints" in argVars:
src = "no_hints"
if isinstance(argVars["hints"],dict):
if isinstance(argVars["hints"], dict):
tmp = {}
for idx in range(1,len(argVars["hints"]) + 1):
for idx in range(1, len(argVars["hints"]) + 1):
tmp[idx] = argVars[src] not in truthy # tmp = !src
args.hints = tmp # dest = tmp
else:
@@ -326,9 +310,9 @@ def update_deprecated_args(args):
# Do: Yes
if "hints" in argVars:
src = "hints"
if isinstance(argVars["hints"],dict):
if isinstance(argVars["hints"], dict):
tmp = {}
for idx in range(1,len(argVars["hints"]) + 1):
for idx in range(1, len(argVars["hints"]) + 1):
tmp[idx] = argVars[src] not in truthy # tmp = !src
args.no_hints = tmp # dest = tmp
else:
@@ -376,6 +360,7 @@ def update_deprecated_args(args):
return args
def print_wiki_doors_by_room(d_regions, world, player):
for d, region_list in d_regions.items():
tile_map = {}
@@ -407,14 +392,15 @@ def print_wiki_doors_by_room(d_regions, world, player):
toprint += ('|-') + "\n"
toprint += ('! Door !! Room Side !! Requirement') + "\n"
for ext in region.exits:
ext_part = ext.name.replace(region.name,'')
ext_part = ext.name.replace(region.name, '')
ext_part = ext_part.strip()
toprint += ('{{DungeonRoomDoorList/Row|{{ROOTPAGENAME}}|{{SUBPAGENAME}}|' + ext_part + '|Side|}}') + "\n"
toprint += ('|}') + "\n"
toprint += ('') + "\n"
with open(os.path.join(".","resources", "user", "rooms-" + d + ".txt"),"w+") as f:
with open(os.path.join(".", "resources", "user", "rooms-" + d + ".txt"), "w+") as f:
f.write(toprint)
def print_xml_doors(d_regions, world, player):
root = ET.Element('root')
for d, region_list in d_regions.items():

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-slate

View File

@@ -7,6 +7,8 @@ import json
import logging
import os
from Utils import output_path
def adjust_page(top, parent, settings):
# Adjust page
self = ttk.Frame(parent)
@@ -90,6 +92,8 @@ def adjust_page(top, parent, settings):
# These are the options to Adjust
def adjustRom():
if output_path.cached_path is None:
output_path.cached_path = top.settings["outputpath"]
options = {
"heartbeep": "heartbeep",
"heartcolor": "heartcolor",

View File

@@ -146,10 +146,13 @@ def bottom_frame(self, parent, args=None):
self.widgets[widget].pieces["button"].pack(side=LEFT)
def open_output():
if args and args.outputpath:
open_file(output_path(args.outputpath))
else:
open_file(output_path(parent.settings["outputpath"]))
if output_path.cached_path is None:
if args and args.outputpath:
output_path.cached_path = args.outputpath
else:
output_path.cached_path = parent.settings["outputpath"]
open_file(output_path('.'))
## Output Button
# widget ID