From 30e8fd132cf26a13d3769be8bb809ea006de2262 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 02:44:11 -0800 Subject: [PATCH 01/93] Initial CI stuff --- .github/workflows/ci.yml | 70 ++++++++++ .../app/meta/manifests/pip_requirements.txt | 1 + resources/ci/__init__.py | 1 + resources/ci/common.py | 131 ++++++++++++++++++ resources/ci/get_upx.py | 42 ++++++ resources/ci/install.py | 27 ++++ resources/ci/prepare_binary.py | 40 ++++++ 7 files changed, 312 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 resources/app/meta/manifests/pip_requirements.txt create mode 100644 resources/ci/__init__.py create mode 100644 resources/ci/common.py create mode 100644 resources/ci/get_upx.py create mode 100644 resources/ci/install.py create mode 100644 resources/ci/prepare_binary.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..0a5f2823 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +# workflow name +name: Build + +# fire on +on: [ push, pull_request ] + +# stuff to do +jobs: + # Install & Build + # Set up environment + # Build + # Run build-gui.py + # Run build-dr.py + install-build: + name: Install/Build + # cycle through os list + runs-on: ${{ matrix.os-name }} + + # VM settings + # os & python versions + strategy: + matrix: + os-name: [ ubuntu-latest, ubuntu-16.04, macOS-latest, windows-latest ] + python-version: [ 3.7 ] +# needs: [ install-test ] + steps: + # checkout commit + - name: Checkout commit + uses: actions/checkout@v1 + # install python + - name: Install python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + architecture: "x64" + - run: | + python --version + # install dependencies via pip + - name: Install dependencies via pip + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/install.py + # try to get UPX + - name: Get UPX + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/get_upx.py + # run build-gui.py + - name: Build GUI + run: | + pip install pyinstaller + python ./build-gui.py + # run build-dr.py + - name: Build DungeonRandomizer + run: | + python ./build-dr.py + # prepare binary artifacts for later step + - name: Prepare Binary Artifacts + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/prepare_binary.py + # upload binary artifacts for later step + - name: Upload Binary Artifacts + uses: actions/upload-artifact@v1 + with: + name: binaries-${{ matrix.os-name }} + path: ../artifact diff --git a/resources/app/meta/manifests/pip_requirements.txt b/resources/app/meta/manifests/pip_requirements.txt new file mode 100644 index 00000000..ef376ca8 --- /dev/null +++ b/resources/app/meta/manifests/pip_requirements.txt @@ -0,0 +1 @@ +pyinstaller diff --git a/resources/ci/__init__.py b/resources/ci/__init__.py new file mode 100644 index 00000000..427ed90c --- /dev/null +++ b/resources/ci/__init__.py @@ -0,0 +1 @@ +#do nothing, just exist to make "common" package diff --git a/resources/ci/common.py b/resources/ci/common.py new file mode 100644 index 00000000..4e2cf50c --- /dev/null +++ b/resources/ci/common.py @@ -0,0 +1,131 @@ +import os # for env vars +import stat # file statistics + +# take number of bytes and convert to string with units measure +def convert_bytes(num): + for x in ["bytes","KB","MB","GB","TB","PB"]: + if num < 1024.0: + return "%3.1f %s" % (num,x) + num /= 1024.0 + +# get filesize of file at path +def file_size(file_path): + if os.path.isfile(file_path): + file_info = os.stat(file_path) + return convert_bytes(file_info.st_size) + +# prepare environment variables +def prepare_env(): + DEFAULT_EVENT = "event" + DEFAULT_REPO_SLUG = "miketrethewey/ALttPDoorRandomizer" + + env = {} + + # get app version + APP_VERSION = "" + APP_VERSION_FILE = "./resources/app/meta/manifests/app_version.txt" + if os.path.isfile(APP_VERSION_FILE): + with open(APP_VERSION_FILE,"r") as f: + APP_VERSION = f.readlines()[0].strip() + # ci data + env["CI_SYSTEM"] = os.getenv("CI_SYSTEM","") + # git data + env["BRANCH"] = os.getenv("TRAVIS_BRANCH","") + env["GITHUB_ACTOR"] = os.getenv("GITHUB_ACTOR","MegaMan.EXE") + env["GITHUB_SHA"] = os.getenv("GITHUB_SHA","") + env["GITHUB_SHA_SHORT"] = env["GITHUB_SHA"] + # commit data + env["COMMIT_ID"] = os.getenv("TRAVIS_COMMIT",os.getenv("GITHUB_SHA","")) + env["COMMIT_COMPARE"] = os.getenv("TRAVIS_COMMIT_RANGE","") + # event data + env["EVENT_MESSAGE"] = os.getenv("TRAVIS_COMMIT_MESSAGE","") + env["EVENT_LOG"] = os.getenv("GITHUB_EVENT_PATH","") + env["EVENT_TYPE"] = os.getenv("TRAVIS_EVENT_TYPE",os.getenv("GITHUB_EVENT_NAME",DEFAULT_EVENT)) + # repo data + env["REPO_SLUG"] = os.getenv("TRAVIS_REPO_SLUG",os.getenv("GITHUB_REPOSITORY",DEFAULT_REPO_SLUG)) + env["REPO_USERNAME"] = "" + env["REPO_NAME"] = "" + + # repo slug + if '/' in env["REPO_SLUG"]: + tmp = env["REPO_SLUG"].split('/') + env["REPO_USERNAME"] = tmp[0] + env["REPO_NAME"] = tmp[1] + + if not env["GITHUB_SHA"] == "": + env["GITHUB_SHA_SHORT"] = env["GITHUB_SHA"][:7] + + # ci data + env["BUILD_NUMBER"] = os.getenv("TRAVIS_BUILD_NUMBER",env["GITHUB_SHA_SHORT"]) + + GITHUB_TAG = os.getenv("TRAVIS_TAG",os.getenv("GITHUB_TAG","")) + OS_NAME = os.getenv("TRAVIS_OS_NAME",os.getenv("OS_NAME","")).replace("macOS","osx") + OS_DIST = os.getenv("TRAVIS_DIST","notset") + OS_VERSION = "" + + if '-' in OS_NAME: + OS_VERSION = OS_NAME[OS_NAME.find('-')+1:] + OS_NAME = OS_NAME[:OS_NAME.find('-')] + if OS_NAME == "linux" or OS_NAME == "ubuntu": + if OS_VERSION == "latest": + OS_VERSION = "bionic" + elif OS_VERSION == "16.04": + OS_VERSION = "xenial" + OS_DIST = OS_VERSION + + if OS_VERSION == "" and not OS_DIST == "" and not OS_DIST == "notset": + OS_VERSION = OS_DIST + + # if no tag + if GITHUB_TAG == "": + # if we haven't appended the build number, do it + if env["BUILD_NUMBER"] not in GITHUB_TAG: + GITHUB_TAG = APP_VERSION + # if the app version didn't have the build number, add it + # set to . + if env["BUILD_NUMBER"] not in GITHUB_TAG: + GITHUB_TAG += '.' + env["BUILD_NUMBER"] + + env["GITHUB_TAG"] = GITHUB_TAG + env["OS_NAME"] = OS_NAME + env["OS_DIST"] = OS_DIST + env["OS_VERSION"] = OS_VERSION + + return env + +# build filename based on metadata +def prepare_filename(BUILD_FILENAME): + env = prepare_env() + + DEST_FILENAME = "" + + # build the filename + if not BUILD_FILENAME == "": + os.chmod(BUILD_FILENAME,0o755) + fileparts = os.path.splitext(BUILD_FILENAME) + DEST_SLUG = fileparts[0] + DEST_EXTENSION = fileparts[1] + DEST_SLUG = DEST_SLUG + '-' + env["GITHUB_TAG"] + '-' + env["OS_NAME"] + if not env["OS_DIST"] == "" and not env["OS_DIST"] == "notset": + DEST_SLUG += '-' + env["OS_DIST"] + DEST_FILENAME = DEST_SLUG + DEST_EXTENSION + return DEST_FILENAME + +# find a binary file if it's executable +# failing that, assume it's over 10MB +def find_binary(listdir): + BUILD_FILENAMES = [] + executable = stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH + for filename in os.listdir(listdir): + if os.path.isfile(filename): + st = os.stat(filename) + mode = st.st_mode + big = st.st_size > (10 * 1024 * 1024) # 10MB + if (mode & executable) or big: + if "GUI" in filename or "DungeonRandomizer" in filename: + BUILD_FILENAMES.append(filename) + return BUILD_FILENAMES + +if __name__ == "__main__": + env = prepare_env() + print(env) diff --git a/resources/ci/get_upx.py b/resources/ci/get_upx.py new file mode 100644 index 00000000..8d71098e --- /dev/null +++ b/resources/ci/get_upx.py @@ -0,0 +1,42 @@ +import common +import os # for env vars +import sys # for path +import urllib.request # for downloads +from shutil import unpack_archive + +# only do stuff if we don't have a UPX folder + +if not os.path.isdir("./upx"): + # get env vars + env = common.prepare_env() + # set up download url + UPX_VERSION = os.getenv("UPX_VERSION") or "3.96" + UPX_SLUG = "" + UPX_FILE = "" + if "windows" in env["OS_NAME"]: + UPX_SLUG = "upx-" + UPX_VERSION + "-win64" + UPX_FILE = UPX_SLUG + ".zip" + else: + UPX_SLUG = "upx-" + UPX_VERSION + "-amd64_linux" + UPX_FILE = UPX_SLUG + ".tar.xz" + UPX_URL = "https://github.com/upx/upx/releases/download/v" + UPX_VERSION + '/' + UPX_FILE + + if "osx" not in env["OS_NAME"]: + + print("Getting UPX: " + UPX_FILE) + + with open("./" + UPX_FILE,"wb") as upx: + UPX_REQ = urllib.request.Request( + UPX_URL, + data=None + ) + UPX_REQ = urllib.request.urlopen(UPX_REQ) + UPX_DATA = UPX_REQ.read() + upx.write(UPX_DATA) + + unpack_archive(UPX_FILE,"./") + + os.rename("./" + UPX_SLUG,"./upx") + os.remove("./" + UPX_FILE) + +print("UPX should " + ("not " if not os.path.isdir("./upx") else "") + "be available.") diff --git a/resources/ci/install.py b/resources/ci/install.py new file mode 100644 index 00000000..8cd40df2 --- /dev/null +++ b/resources/ci/install.py @@ -0,0 +1,27 @@ +import common +import os # for env vars +import subprocess # do stuff at the shell level + +env = common.prepare_env() + +# get executables +# python +# linux/windows: python +# macosx: python3 +# pip +# linux/macosx: pip3 +# windows: pip +PYTHON_EXECUTABLE = "python3" if "osx" in env["OS_NAME"] else "python" +PIP_EXECUTABLE = "pip" if "windows" in env["OS_NAME"] else "pip3" +PIP_EXECUTABLE = "pip" if "osx" in env["OS_NAME"] and "actions" in env["CI_SYSTEM"] else PIP_EXECUTABLE + +# upgrade pip +subprocess.check_call([PYTHON_EXECUTABLE,"-m","pip","install","--upgrade","pip"]) + +# pip version +subprocess.check_call([PIP_EXECUTABLE,"--version"]) +# if pip3, install wheel +if PIP_EXECUTABLE == "pip3": + subprocess.check_call([PIP_EXECUTABLE,"install","-U","wheel"]) +# install listed dependencies +subprocess.check_call([PIP_EXECUTABLE,"install","-r","./resources/app/meta/manifests/pip_requirements.txt"]) diff --git a/resources/ci/prepare_binary.py b/resources/ci/prepare_binary.py new file mode 100644 index 00000000..583c7d7b --- /dev/null +++ b/resources/ci/prepare_binary.py @@ -0,0 +1,40 @@ +import distutils.dir_util # for copying trees +import os # for env vars +import stat # for file stats +import subprocess # do stuff at the shell level +import common +from shutil import copy, make_archive, move, rmtree # file manipulation + +env = common.prepare_env() + +# make dir to put the binary in +if not os.path.isdir(os.path.join("..","artifact")): + os.mkdir(os.path.join("..","artifact")) + +BUILD_FILENAME = "" + +# list executables +BUILD_FILENAME = common.find_binary('.') +if BUILD_FILENAME == "": + BUILD_FILENAME = common.find_binary(os.path.join("..","artifact")) + +if isinstance(BUILD_FILENAME,str): + BUILD_FILENAME = list(BUILD_FILENAME) + +BUILD_FILENAMES = BUILD_FILENAME + +for BUILD_FILENAME in BUILD_FILENAMES: + DEST_FILENAME = common.prepare_filename(BUILD_FILENAME) + + print("OS Name: " + env["OS_NAME"]) + print("OS Version: " + env["OS_VERSION"]) + print("Build Filename: " + BUILD_FILENAME) + print("Dest Filename: " + DEST_FILENAME) + if not BUILD_FILENAME == "": + print("Build Filesize: " + common.file_size(BUILD_FILENAME)) + + if not BUILD_FILENAME == "": + move( + os.path.join(".",BUILD_FILENAME), + os.path.join("..","artifact",BUILD_FILENAME) + ) From 8103981b0eb110036f06943aca6ac640e643a392 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 02:46:40 -0800 Subject: [PATCH 02/93] Fix syntax --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a5f2823..a6dc6e49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: python-version: ${{ matrix.python-version }} architecture: "x64" - run: | - python --version + python --version # install dependencies via pip - name: Install dependencies via pip env: From ae1f87143e23e98a464bae9fe0b093de6b0b9287 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 02:48:40 -0800 Subject: [PATCH 03/93] Move stuff to common folder --- resources/ci/__init__.py | 2 +- resources/ci/common/__init__.py | 1 + resources/ci/{ => common}/common.py | 0 resources/ci/{ => common}/get_upx.py | 0 resources/ci/{ => common}/install.py | 0 resources/ci/{ => common}/prepare_binary.py | 0 6 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 resources/ci/common/__init__.py rename resources/ci/{ => common}/common.py (100%) rename resources/ci/{ => common}/get_upx.py (100%) rename resources/ci/{ => common}/install.py (100%) rename resources/ci/{ => common}/prepare_binary.py (100%) diff --git a/resources/ci/__init__.py b/resources/ci/__init__.py index 427ed90c..1c6bfcb0 100644 --- a/resources/ci/__init__.py +++ b/resources/ci/__init__.py @@ -1 +1 @@ -#do nothing, just exist to make "common" package +#do nothing, just exist to make "ci" package diff --git a/resources/ci/common/__init__.py b/resources/ci/common/__init__.py new file mode 100644 index 00000000..427ed90c --- /dev/null +++ b/resources/ci/common/__init__.py @@ -0,0 +1 @@ +#do nothing, just exist to make "common" package diff --git a/resources/ci/common.py b/resources/ci/common/common.py similarity index 100% rename from resources/ci/common.py rename to resources/ci/common/common.py diff --git a/resources/ci/get_upx.py b/resources/ci/common/get_upx.py similarity index 100% rename from resources/ci/get_upx.py rename to resources/ci/common/get_upx.py diff --git a/resources/ci/install.py b/resources/ci/common/install.py similarity index 100% rename from resources/ci/install.py rename to resources/ci/common/install.py diff --git a/resources/ci/prepare_binary.py b/resources/ci/common/prepare_binary.py similarity index 100% rename from resources/ci/prepare_binary.py rename to resources/ci/common/prepare_binary.py From ab33c5c95f492b296e2562de8f5c4e784fd5ce52 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 02:50:40 -0800 Subject: [PATCH 04/93] Make consistent --- resources/ci/common/common.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 4e2cf50c..cc5c99d0 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -115,16 +115,16 @@ def prepare_filename(BUILD_FILENAME): # failing that, assume it's over 10MB def find_binary(listdir): BUILD_FILENAMES = [] - executable = stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH - for filename in os.listdir(listdir): - if os.path.isfile(filename): - st = os.stat(filename) - mode = st.st_mode - big = st.st_size > (10 * 1024 * 1024) # 10MB - if (mode & executable) or big: - if "GUI" in filename or "DungeonRandomizer" in filename: - BUILD_FILENAMES.append(filename) - return BUILD_FILENAMES + executable = stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH + for filename in os.listdir(listdir): + if os.path.isfile(filename): + st = os.stat(filename) + mode = st.st_mode + big = st.st_size > (10 * 1024 * 1024) # 10MB + if (mode & executable) or big: + if "GUI" in filename or "DungeonRandomizer" in filename: + BUILD_FILENAMES.append(filename) + return BUILD_FILENAMES if __name__ == "__main__": env = prepare_env() From 9c59dda0e08afafb677612e6580851cc01c0d93a Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 02:53:21 -0800 Subject: [PATCH 05/93] Meh, capitals --- resources/ci/common/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index cc5c99d0..9126fb24 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -122,7 +122,7 @@ def find_binary(listdir): mode = st.st_mode big = st.st_size > (10 * 1024 * 1024) # 10MB if (mode & executable) or big: - if "GUI" in filename or "DungeonRandomizer" in filename: + if "GUI" in filename or "Gui" in filename or "DungeonRandomizer" in filename: BUILD_FILENAMES.append(filename) return BUILD_FILENAMES From 28e2d83c54cbaddd6b6c3e6c48e7fa100f950612 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:07:08 -0800 Subject: [PATCH 06/93] Prepare for release --- .github/workflows/ci.yml | 61 +++++++++++++ resources/ci/common/git_clean.py | 14 +++ resources/ci/common/prepare_release.py | 113 +++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 resources/ci/common/git_clean.py create mode 100644 resources/ci/common/prepare_release.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6dc6e49..5089e758 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,3 +68,64 @@ jobs: with: name: binaries-${{ matrix.os-name }} path: ../artifact + + # Install & Preparing Release + # Set up environment + # Local Prepare Release action + install-prepare-release: + name: Install/Prepare Release + # cycle through os list + runs-on: ${{ matrix.os-name }} + + # VM settings + # os & python versions + strategy: + matrix: + # install/release on not xenial + os-name: [ ubuntu-latest, macOS-latest, windows-latest ] + python-version: [ 3.7 ] + + needs: [ install-build ] + steps: + # checkout commit + - name: Checkout commit + uses: actions/checkout@v1 + # install python + - name: Install Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + architecture: "x64" + - run: | + python --version + # install dependencies via pip + - name: Install Dependencies via pip + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/install.py + # download binary artifact + - name: Download Binary Artifact + uses: actions/download-artifact@v1 + with: + name: binary-${{ matrix.os-name }} + path: ./ + # Prepare AppVersion & Release + - name: Prepare AppVersion & Release + env: + OS_NAME: ${{ matrix.os-name }} + run: | +# python ./resources/ci/common/prepare_appversion.py + python ./resources/ci/common/prepare_release.py + # upload appversion artifact for later step +# - name: Upload AppVersion Artifact +# uses: actions/upload-artifact@v1 +# with: +# name: appversion-${{ matrix.os-name }} +# path: ./resources/app/meta/manifests/app_version.txt + # upload archive artifact for later step + - name: Upload Archive Artifact + uses: actions/upload-artifact@v1 + with: + name: archive-${{ matrix.os-name }} + path: ../deploy diff --git a/resources/ci/common/git_clean.py b/resources/ci/common/git_clean.py new file mode 100644 index 00000000..9b424283 --- /dev/null +++ b/resources/ci/common/git_clean.py @@ -0,0 +1,14 @@ +import subprocess # do stuff at the shell level + +def git_clean(): + # clean the git slate + subprocess.check_call([ + "git", # run a git command + "clean", # clean command + "-dfx", # d: directories, f: files, x: ignored files + "--exclude=.vscode", # keep vscode IDE files + "--exclude=.idea", # keep idea IDE files + "--exclude=*.json"]) # keep JSON files for that one time I just nuked all that I was working on, oops + +if __name__ == "__main__": + git_clean() diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py new file mode 100644 index 00000000..7a49f8e3 --- /dev/null +++ b/resources/ci/common/prepare_release.py @@ -0,0 +1,113 @@ +import distutils.dir_util # for copying trees +import os # for env vars +import stat # for file stats +import subprocess # do stuff at the shell level +import common +from git_clean import git_clean +from shutil import copy, make_archive, move, rmtree # file manipulation + +env = common.prepare_env() # get env vars + +# make temp dir to put binary in +if not os.path.isdir(os.path.join("..","artifact")): + os.mkdir(os.path.join("..","artifact")) + +# make temp dir for other stuff +if not os.path.isdir(os.path.join("..","build")): + os.mkdir(os.path.join("..","build")) + +# make dir to put the archive in +if not os.path.isdir(os.path.join("..","deploy")): + os.mkdir(os.path.join("..","deploy")) + +# make dirs for each os +for dirname in ["linux","macos","windows"]: + if not os.path.isdir(os.path.join("..","deploy",dirname)): + os.mkdir(os.path.join("..","deploy",dirname)) + +# sanity check permissions for working_dirs.json +dirpath = "." +for dirname in ["resources","user"]: + dirpath += '/' + dirname + os.chmod(dirpath,0o755) +# nuke travis file if it exists +if os.path.isfile(os.path.join(".",".travis.yml")): + os.remove(os.path.join(".",".travis.yml")) +# nuke test suite if it exists +if os.path.isdir(os.path.join(".","tests")): + distutils.dir_util.remove_tree(os.path.join(".","tests")) + +BUILD_FILENAME = "" +ZIP_FILENAME = "" + +# list executables +BUILD_FILENAME = common.find_binary(os.path.join(".")) +if BUILD_FILENAME == "": + BUILD_FILENAME = common.find_binary(os.path.join("..","artifact")) + +if isinstance(BUILD_FILENAME,str): + BUILD_FILENAME = list(BUILD_FILENAME) + +BUILD_FILENAMES = BUILD_FILENAME + +for BUILD_FILENAME in BUILD_FILENAMES: + if not BUILD_FILENAME == "": + if not "artifact" in BUILD_FILENAME: + # move the binary to temp folder + move( + os.path.join(".",BUILD_FILENAME), + os.path.join("..","artifact",BUILD_FILENAME) + ) + + # clean the git slate + git_clean() + + # mv dirs from source code + dirs = [os.path.join(".",".git"), os.path.join(".",".github"), os.path.join(".",".gitignore"), os.path.join(".","html"), os.path.join(".","resources","ci")] + for dir in dirs: + if os.path.isdir(dir): + move( + dir, + os.path.join("..","build",dir) + ) + + for BUILD_FILENAME in BUILD_FILENAMES: + if not "artifact" in BUILD_FILENAME: + # move the binary back + move( + os.path.join("..","artifact",BUILD_FILENAME), + os.path.join(".",BUILD_FILENAME) + ) + + # .zip if windows + # .tar.gz otherwise + ZIP_FILENAME = os.path.join("..","deploy",os.path.splitext(BUILD_FILENAME)[0]) + if env["OS_NAME"] == "windows": + make_archive(ZIP_FILENAME,"zip") + ZIP_FILENAME += ".zip" + else: + make_archive(ZIP_FILENAME,"gztar") + ZIP_FILENAME += ".tar.gz" + + # mv dirs back + for dir in dirs: + if os.path.isdir(os.path.join("..","build",dir)): + move( + os.path.join("..","build",dir), + os.path.join(".",dir) + ) + +for BUILD_FILENAME in BUILD_FILENAMES: + if not BUILD_FILENAME == "": + print("Build Filename: " + BUILD_FILENAME) + print("Build Filesize: " + common.file_size(BUILD_FILENAME)) + else: + print("No Build to prepare") + +if not ZIP_FILENAME == "": + print("Zip Filename: " + ZIP_FILENAME) + print("Zip Filesize: " + common.file_size(ZIP_FILENAME)) +else: + print("No Zip to prepare") + +print("Git tag: " + env["GITHUB_TAG"]) From 568fffc11f4c0f8d258bd6d1d35a44e9422e8562 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:08:05 -0800 Subject: [PATCH 07/93] Ignore AppVersion for now --- .github/workflows/ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5089e758..2364e80b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,14 +115,7 @@ jobs: env: OS_NAME: ${{ matrix.os-name }} run: | -# python ./resources/ci/common/prepare_appversion.py python ./resources/ci/common/prepare_release.py - # upload appversion artifact for later step -# - name: Upload AppVersion Artifact -# uses: actions/upload-artifact@v1 -# with: -# name: appversion-${{ matrix.os-name }} -# path: ./resources/app/meta/manifests/app_version.txt # upload archive artifact for later step - name: Upload Archive Artifact uses: actions/upload-artifact@v1 From 1bb638d58263582903973fc4a9cc67224c3a6165 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:11:34 -0800 Subject: [PATCH 08/93] Lol, plural --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2364e80b..1aeb5987 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,7 +108,7 @@ jobs: - name: Download Binary Artifact uses: actions/download-artifact@v1 with: - name: binary-${{ matrix.os-name }} + name: binaries-${{ matrix.os-name }} path: ./ # Prepare AppVersion & Release - name: Prepare AppVersion & Release From c2eaa43ed4fd43071252900296b12de7fbf15fc2 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:16:41 -0800 Subject: [PATCH 09/93] Create .gitkeep --- resources/user/.gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 resources/user/.gitkeep diff --git a/resources/user/.gitkeep b/resources/user/.gitkeep new file mode 100644 index 00000000..a96dac2e --- /dev/null +++ b/resources/user/.gitkeep @@ -0,0 +1 @@ +# do nothing, just exist to make "user" folder From 1b62ca2c3093697254cbb95ceb717dd9c068a408 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:21:35 -0800 Subject: [PATCH 10/93] Ignore python scripts --- resources/ci/common/common.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 9126fb24..8e68851d 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -118,12 +118,13 @@ def find_binary(listdir): executable = stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH for filename in os.listdir(listdir): if os.path.isfile(filename): - st = os.stat(filename) - mode = st.st_mode - big = st.st_size > (10 * 1024 * 1024) # 10MB - if (mode & executable) or big: - if "GUI" in filename or "Gui" in filename or "DungeonRandomizer" in filename: - BUILD_FILENAMES.append(filename) + if os.path.splitext(filename)[1] != ".py": + st = os.stat(filename) + mode = st.st_mode + big = st.st_size > (10 * 1024 * 1024) # 10MB + if (mode & executable) or big: + if "GUI" in filename or "Gui" in filename or "DungeonRandomizer" in filename: + BUILD_FILENAMES.append(filename) return BUILD_FILENAMES if __name__ == "__main__": From 0d45f20eaec773f19b490eb223bdb14eacb891f3 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:26:39 -0800 Subject: [PATCH 11/93] Debug --- resources/ci/common/prepare_release.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 7a49f8e3..0204b5c4 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -50,6 +50,8 @@ if isinstance(BUILD_FILENAME,str): BUILD_FILENAMES = BUILD_FILENAME +print(BUILD_FILENAMES) + for BUILD_FILENAME in BUILD_FILENAMES: if not BUILD_FILENAME == "": if not "artifact" in BUILD_FILENAME: From 5897cb0b801cdeff78d85be65c72bfffd1894fcf Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:35:32 -0800 Subject: [PATCH 12/93] Moar sanity checks --- Gui.spec | 3 +++ resources/ci/common/prepare_release.py | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Gui.spec b/Gui.spec index cd6de67d..7aef9c09 100644 --- a/Gui.spec +++ b/Gui.spec @@ -3,6 +3,9 @@ block_cipher = None console = True +if sys.platform.find("mac") or sys.platform.find("osx"): + console = False + def recurse_for_py_files(names_so_far): returnvalue = [] for name in os.listdir(os.path.join(*names_so_far)): diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 0204b5c4..86f229bd 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -75,11 +75,12 @@ for BUILD_FILENAME in BUILD_FILENAMES: for BUILD_FILENAME in BUILD_FILENAMES: if not "artifact" in BUILD_FILENAME: - # move the binary back - move( - os.path.join("..","artifact",BUILD_FILENAME), - os.path.join(".",BUILD_FILENAME) - ) + if os.path.isfile(os.path.join("..","artifact",BUILD_FILENAME)): + # move the binary back + move( + os.path.join("..","artifact",BUILD_FILENAME), + os.path.join(".",BUILD_FILENAME) + ) # .zip if windows # .tar.gz otherwise From 0fb4e47568592fc9cab8d4f5d0cf5bb471224b77 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:40:11 -0800 Subject: [PATCH 13/93] Import sys for mac check --- Gui.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gui.spec b/Gui.spec index 7aef9c09..9d02abdf 100644 --- a/Gui.spec +++ b/Gui.spec @@ -1,5 +1,7 @@ # -*- mode: python -*- +import sys + block_cipher = None console = True From cae638d7aae0f7ef64b6d31b06e5c4368baabf16 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:45:48 -0800 Subject: [PATCH 14/93] Do stuff if we've got filenames but only do it once --- resources/ci/common/prepare_release.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 86f229bd..98348da7 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -52,14 +52,15 @@ BUILD_FILENAMES = BUILD_FILENAME print(BUILD_FILENAMES) -for BUILD_FILENAME in BUILD_FILENAMES: - if not BUILD_FILENAME == "": - if not "artifact" in BUILD_FILENAME: - # move the binary to temp folder - move( - os.path.join(".",BUILD_FILENAME), - os.path.join("..","artifact",BUILD_FILENAME) - ) +if len(BUILD_FILENAMES) > 0: + for BUILD_FILENAME in BUILD_FILENAMES: + if not BUILD_FILENAME == "": + if not "artifact" in BUILD_FILENAME: + # move the binary to temp folder + move( + os.path.join(".",BUILD_FILENAME), + os.path.join("..","artifact",BUILD_FILENAME) + ) # clean the git slate git_clean() From f0d2e28923146002c1a46bf503bb4748e96eb517 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 03:58:58 -0800 Subject: [PATCH 15/93] Moar Mac sanity checks; rename archive --- build-dr.py | 3 ++- build-gui.py | 3 ++- resources/ci/common/prepare_release.py | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build-dr.py b/build-dr.py index 120028d9..06b2d510 100644 --- a/build-dr.py +++ b/build-dr.py @@ -1,6 +1,7 @@ import subprocess import os import shutil +import sys DEST_DIRECTORY = '.' @@ -9,7 +10,7 @@ if os.path.isdir("upx"): else: upx_string = "" -if os.path.isdir("build"): +if os.path.isdir("build") and not sys.platform.find("mac") and not sys.platform.find("osx"): shutil.rmtree("build") subprocess.run(" ".join(["pyinstaller DungeonRandomizer.spec ", diff --git a/build-gui.py b/build-gui.py index 3f63548d..35965d3e 100644 --- a/build-gui.py +++ b/build-gui.py @@ -1,6 +1,7 @@ import subprocess import os import shutil +import sys DEST_DIRECTORY = '.' @@ -9,7 +10,7 @@ if os.path.isdir("upx"): else: upx_string = "" -if os.path.isdir("build"): +if os.path.isdir("build") and not sys.platform.find("mac") and not sys.platform.find("osx"): shutil.rmtree("build") subprocess.run(" ".join(["pyinstaller Gui.spec ", diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 98348da7..5c8f776f 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -86,6 +86,7 @@ if len(BUILD_FILENAMES) > 0: # .zip if windows # .tar.gz otherwise ZIP_FILENAME = os.path.join("..","deploy",os.path.splitext(BUILD_FILENAME)[0]) + ZIP_FILENAME = "ALttPDoorRandomizer" if env["OS_NAME"] == "windows": make_archive(ZIP_FILENAME,"zip") ZIP_FILENAME += ".zip" From cd89bb5a950d128fec1fe3004defeb72a7195e8a Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 26 Feb 2020 04:04:09 -0800 Subject: [PATCH 16/93] Put Archive in correct folder --- resources/ci/common/prepare_release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 5c8f776f..802c9d16 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -86,7 +86,7 @@ if len(BUILD_FILENAMES) > 0: # .zip if windows # .tar.gz otherwise ZIP_FILENAME = os.path.join("..","deploy",os.path.splitext(BUILD_FILENAME)[0]) - ZIP_FILENAME = "ALttPDoorRandomizer" + ZIP_FILENAME = os.path.join("..","deploy","ALttPDoorRandomizer") if env["OS_NAME"] == "windows": make_archive(ZIP_FILENAME,"zip") ZIP_FILENAME += ".zip" From 0fab8c2450cb42592cd293206f787cc2740b030d Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 00:16:17 -0800 Subject: [PATCH 17/93] Break out Mac into multiple VMs See if this works to get both binaries for MacOSX --- .github/workflows/ci.yml | 136 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1aeb5987..e06be32f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: [ push, pull_request ] # stuff to do jobs: - # Install & Build + # Install & Build (not MacOSX) # Set up environment # Build # Run build-gui.py @@ -20,7 +20,7 @@ jobs: # os & python versions strategy: matrix: - os-name: [ ubuntu-latest, ubuntu-16.04, macOS-latest, windows-latest ] + os-name: [ ubuntu-latest, ubuntu-16.04, windows-latest ] python-version: [ 3.7 ] # needs: [ install-test ] steps: @@ -69,6 +69,121 @@ jobs: name: binaries-${{ matrix.os-name }} path: ../artifact + # Install & Build GUI (MacOSX) + # Set up environment + # Build + # Run build-gui.py + mac-install-build-gui: + name: Mac Install/Build GUI + # cycle through os list + runs-on: ${{ matrix.os-name }} + + # VM settings + # os & python versions + strategy: + matrix: + os-name: [ macOS-latest ] + python-version: [ 3.7 ] +# needs: [ install-test ] + steps: + # checkout commit + - name: Checkout commit + uses: actions/checkout@v1 + # install python + - name: Install python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + architecture: "x64" + - run: | + python --version + # install dependencies via pip + - name: Install dependencies via pip + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/install.py + # try to get UPX + - name: Get UPX + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/get_upx.py + # run build-gui.py + - name: Build GUI + run: | + pip install pyinstaller + python ./build-gui.py + # prepare binary artifacts for later step + - name: Prepare Binary Artifacts + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/prepare_binary.py + # upload binary artifacts for later step + - name: Upload Binary Artifacts + uses: actions/upload-artifact@v1 + with: + name: gui-macos + path: ../artifact + + # Install & Build DungeonRandomizer (MacOSX) + # Set up environment + # Build + # Run build-dr.py + mac-install-build-dr: + name: Mac Install/Build GUI + # cycle through os list + runs-on: ${{ matrix.os-name }} + + # VM settings + # os & python versions + strategy: + matrix: + os-name: [ macOS-latest ] + python-version: [ 3.7 ] +# needs: [ install-test ] + steps: + # checkout commit + - name: Checkout commit + uses: actions/checkout@v1 + # install python + - name: Install python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + architecture: "x64" + - run: | + python --version + # install dependencies via pip + - name: Install dependencies via pip + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/install.py + # try to get UPX + - name: Get UPX + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/get_upx.py + # run build-dr.py + - name: Build DungeonRandomizer + run: | + python ./build-dr.py + # prepare binary artifacts for later step + - name: Prepare Binary Artifacts + env: + OS_NAME: ${{ matrix.os-name }} + run: | + python ./resources/ci/common/prepare_binary.py + # upload binary artifacts for later step + - name: Upload Binary Artifacts + uses: actions/upload-artifact@v1 + with: + name: dr-macos + path: ../artifact + # Install & Preparing Release # Set up environment # Local Prepare Release action @@ -85,7 +200,7 @@ jobs: os-name: [ ubuntu-latest, macOS-latest, windows-latest ] python-version: [ 3.7 ] - needs: [ install-build ] + needs: [ install-build, mac-install-build-gui, mac-install-build-dr ] steps: # checkout commit - name: Checkout commit @@ -110,6 +225,21 @@ jobs: with: name: binaries-${{ matrix.os-name }} path: ./ + if: not contains(${{ matrix.os-name }},"macos") + # download binary artifact + - name: Download GUI Binary Artifact + uses: actions/download-artifact@v1 + with: + name: gui-macos + path: ./ + if: contains(${{ matrix.os-name }},"macos") + # download binary artifact + - name: Download DungeonRandomizer Binary Artifact + uses: actions/download-artifact@v1 + with: + name: dr-macos + path: ./ + if: contains(${{ matrix.os-name }},"macos") # Prepare AppVersion & Release - name: Prepare AppVersion & Release env: From 0a2e9cde3931029baeb8c6068d52a58ecf092ec8 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 00:19:10 -0800 Subject: [PATCH 18/93] Revert "Break out Mac into multiple VMs" This reverts commit 0fab8c2450cb42592cd293206f787cc2740b030d. --- .github/workflows/ci.yml | 136 +-------------------------------------- 1 file changed, 3 insertions(+), 133 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e06be32f..1aeb5987 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: [ push, pull_request ] # stuff to do jobs: - # Install & Build (not MacOSX) + # Install & Build # Set up environment # Build # Run build-gui.py @@ -20,7 +20,7 @@ jobs: # os & python versions strategy: matrix: - os-name: [ ubuntu-latest, ubuntu-16.04, windows-latest ] + os-name: [ ubuntu-latest, ubuntu-16.04, macOS-latest, windows-latest ] python-version: [ 3.7 ] # needs: [ install-test ] steps: @@ -69,121 +69,6 @@ jobs: name: binaries-${{ matrix.os-name }} path: ../artifact - # Install & Build GUI (MacOSX) - # Set up environment - # Build - # Run build-gui.py - mac-install-build-gui: - name: Mac Install/Build GUI - # cycle through os list - runs-on: ${{ matrix.os-name }} - - # VM settings - # os & python versions - strategy: - matrix: - os-name: [ macOS-latest ] - python-version: [ 3.7 ] -# needs: [ install-test ] - steps: - # checkout commit - - name: Checkout commit - uses: actions/checkout@v1 - # install python - - name: Install python - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - architecture: "x64" - - run: | - python --version - # install dependencies via pip - - name: Install dependencies via pip - env: - OS_NAME: ${{ matrix.os-name }} - run: | - python ./resources/ci/common/install.py - # try to get UPX - - name: Get UPX - env: - OS_NAME: ${{ matrix.os-name }} - run: | - python ./resources/ci/common/get_upx.py - # run build-gui.py - - name: Build GUI - run: | - pip install pyinstaller - python ./build-gui.py - # prepare binary artifacts for later step - - name: Prepare Binary Artifacts - env: - OS_NAME: ${{ matrix.os-name }} - run: | - python ./resources/ci/common/prepare_binary.py - # upload binary artifacts for later step - - name: Upload Binary Artifacts - uses: actions/upload-artifact@v1 - with: - name: gui-macos - path: ../artifact - - # Install & Build DungeonRandomizer (MacOSX) - # Set up environment - # Build - # Run build-dr.py - mac-install-build-dr: - name: Mac Install/Build GUI - # cycle through os list - runs-on: ${{ matrix.os-name }} - - # VM settings - # os & python versions - strategy: - matrix: - os-name: [ macOS-latest ] - python-version: [ 3.7 ] -# needs: [ install-test ] - steps: - # checkout commit - - name: Checkout commit - uses: actions/checkout@v1 - # install python - - name: Install python - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - architecture: "x64" - - run: | - python --version - # install dependencies via pip - - name: Install dependencies via pip - env: - OS_NAME: ${{ matrix.os-name }} - run: | - python ./resources/ci/common/install.py - # try to get UPX - - name: Get UPX - env: - OS_NAME: ${{ matrix.os-name }} - run: | - python ./resources/ci/common/get_upx.py - # run build-dr.py - - name: Build DungeonRandomizer - run: | - python ./build-dr.py - # prepare binary artifacts for later step - - name: Prepare Binary Artifacts - env: - OS_NAME: ${{ matrix.os-name }} - run: | - python ./resources/ci/common/prepare_binary.py - # upload binary artifacts for later step - - name: Upload Binary Artifacts - uses: actions/upload-artifact@v1 - with: - name: dr-macos - path: ../artifact - # Install & Preparing Release # Set up environment # Local Prepare Release action @@ -200,7 +85,7 @@ jobs: os-name: [ ubuntu-latest, macOS-latest, windows-latest ] python-version: [ 3.7 ] - needs: [ install-build, mac-install-build-gui, mac-install-build-dr ] + needs: [ install-build ] steps: # checkout commit - name: Checkout commit @@ -225,21 +110,6 @@ jobs: with: name: binaries-${{ matrix.os-name }} path: ./ - if: not contains(${{ matrix.os-name }},"macos") - # download binary artifact - - name: Download GUI Binary Artifact - uses: actions/download-artifact@v1 - with: - name: gui-macos - path: ./ - if: contains(${{ matrix.os-name }},"macos") - # download binary artifact - - name: Download DungeonRandomizer Binary Artifact - uses: actions/download-artifact@v1 - with: - name: dr-macos - path: ./ - if: contains(${{ matrix.os-name }},"macos") # Prepare AppVersion & Release - name: Prepare AppVersion & Release env: From f09346e5cb00f9e8b15a24f9c7a8ded7a6e208ae Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 00:40:35 -0800 Subject: [PATCH 19/93] Try to fix Mac again, write version number --- .github/workflows/ci.yml | 7 +++++++ resources/ci/common/install.py | 5 +++++ resources/ci/common/prepare_appversion.py | 20 ++++++++++++++++++++ resources/ci/common/prepare_binary.py | 3 +++ 4 files changed, 35 insertions(+) create mode 100644 resources/ci/common/prepare_appversion.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1aeb5987..557af1d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,7 +115,14 @@ jobs: env: OS_NAME: ${{ matrix.os-name }} run: | + python ./resources/ci/common/prepare_appversion.py python ./resources/ci/common/prepare_release.py + # upload appversion artifact for later step + - name: Upload AppVersion Artifact + uses: actions/upload-artifact@v1 + with: + name: appversion-${{ matrix.os-name }} + path: ./resources/app/meta/manifests/app_version.txt # upload archive artifact for later step - name: Upload Archive Artifact uses: actions/upload-artifact@v1 diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 8cd40df2..44118725 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -2,6 +2,11 @@ import common import os # for env vars import subprocess # do stuff at the shell level +from Main import __version__ as DRVersion + +with(open(os.path.join("resources","app","meta","manifests","app_version.txt"),"w+")) as f: + f.write(DRVersion) + env = common.prepare_env() # get executables diff --git a/resources/ci/common/prepare_appversion.py b/resources/ci/common/prepare_appversion.py new file mode 100644 index 00000000..bd26318e --- /dev/null +++ b/resources/ci/common/prepare_appversion.py @@ -0,0 +1,20 @@ +import common +import os # for env vars +from shutil import copy # file manipulation + +env = common.prepare_env() + +# set tag to app_version.txt +if not env["GITHUB_TAG"] == "": + with open(os.path.join(".","resources","app","meta","manifests","app_version.txt"),"w+") as f: + _ = f.read() + f.seek(0) + f.write(env["GITHUB_TAG"]) + f.truncate() + +if not os.path.isdir(os.path.join("..","build")): + os.mkdir(os.path.join("..","build")) +copy( + os.path.join(".","resources","app","meta","manifests","app_version.txt"), + os.path.join("..","build","app_version.txt") +) diff --git a/resources/ci/common/prepare_binary.py b/resources/ci/common/prepare_binary.py index 583c7d7b..bc4e0d09 100644 --- a/resources/ci/common/prepare_binary.py +++ b/resources/ci/common/prepare_binary.py @@ -7,6 +7,9 @@ from shutil import copy, make_archive, move, rmtree # file manipulation env = common.prepare_env() +if "OS_NAME" in env and env["OS_NAME"] == "osx": + subprocess.check_call(["ls","-l"]) + # make dir to put the binary in if not os.path.isdir(os.path.join("..","artifact")): os.mkdir(os.path.join("..","artifact")) From 1f8c0ddc59744259976d3b484ad38a63ea09fbf6 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 00:43:07 -0800 Subject: [PATCH 20/93] Buh? --- resources/ci/common/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 44118725..2a3d9178 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -2,7 +2,7 @@ import common import os # for env vars import subprocess # do stuff at the shell level -from Main import __version__ as DRVersion +from ......Main import __version__ as DRVersion with(open(os.path.join("resources","app","meta","manifests","app_version.txt"),"w+")) as f: f.write(DRVersion) From 2075dc773e6334e0ec9e5c80d8a02a31a44fabe9 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 00:47:17 -0800 Subject: [PATCH 21/93] Package management --- __init__.py | 1 + resources/__init__.py | 1 + resources/ci/__init__.py | 2 +- resources/ci/common/__init__.py | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 __init__.py create mode 100644 resources/__init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..41df9bfe --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make root package diff --git a/resources/__init__.py b/resources/__init__.py new file mode 100644 index 00000000..a1a6b28e --- /dev/null +++ b/resources/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "resources" package diff --git a/resources/ci/__init__.py b/resources/ci/__init__.py index 1c6bfcb0..04deec10 100644 --- a/resources/ci/__init__.py +++ b/resources/ci/__init__.py @@ -1 +1 @@ -#do nothing, just exist to make "ci" package +#do nothing, just exist to make "resources.ci" package diff --git a/resources/ci/common/__init__.py b/resources/ci/common/__init__.py index 427ed90c..34761450 100644 --- a/resources/ci/common/__init__.py +++ b/resources/ci/common/__init__.py @@ -1 +1 @@ -#do nothing, just exist to make "common" package +# do nothing, just exist to make "resources.ci.common" package From 9025d8204c12d4d3675495a5a4b5d48af8623e21 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 00:54:30 -0800 Subject: [PATCH 22/93] Up four --- resources/ci/common/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 2a3d9178..db7ef533 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -2,7 +2,7 @@ import common import os # for env vars import subprocess # do stuff at the shell level -from ......Main import __version__ as DRVersion +from ....Main import __version__ as DRVersion with(open(os.path.join("resources","app","meta","manifests","app_version.txt"),"w+")) as f: f.write(DRVersion) From 1b4d6a1ec4053f06f2cec47cd1a66c41a1580b58 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 00:59:33 -0800 Subject: [PATCH 23/93] Update install.py --- resources/ci/common/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index db7ef533..1fedfcaa 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -2,7 +2,7 @@ import common import os # for env vars import subprocess # do stuff at the shell level -from ....Main import __version__ as DRVersion +from .... Main import __version__ as DRVersion with(open(os.path.join("resources","app","meta","manifests","app_version.txt"),"w+")) as f: f.write(DRVersion) From a256dc1ae34a291115694b9dfc21df9bb2dab62c Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:00:43 -0800 Subject: [PATCH 24/93] Skin that cat later --- resources/ci/common/install.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 1fedfcaa..8cd40df2 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -2,11 +2,6 @@ import common import os # for env vars import subprocess # do stuff at the shell level -from .... Main import __version__ as DRVersion - -with(open(os.path.join("resources","app","meta","manifests","app_version.txt"),"w+")) as f: - f.write(DRVersion) - env = common.prepare_env() # get executables From 74f3e8954785d0616d7e191654be5e13be0d1963 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:10:49 -0800 Subject: [PATCH 25/93] Move GUI to Source folder to avoid conflicts --- CLI.py | 2 +- Gui.py | 24 +++++++++---------- ItemList.py | 2 +- classes/__init__.py | 1 - gui/__init__.py | 1 - gui/about/__init__.py | 1 - gui/adjust/__init__.py | 1 - gui/custom/__init__.py | 1 - gui/randomize/__init__.py | 1 - gui/startinventory/__init__.py | 1 - source/__init__.py | 1 + {classes => source/classes}/SpriteSelector.py | 0 source/classes/__init__.py | 1 + {classes => source/classes}/constants.py | 0 source/gui/__init__.py | 1 + source/gui/about/__init__.py | 1 + source/gui/adjust/__init__.py | 1 + {gui => source/gui}/adjust/overview.py | 4 ++-- {gui => source/gui}/bottom.py | 4 ++-- source/gui/custom/__init__.py | 1 + {gui => source/gui}/custom/overview.py | 4 ++-- {gui => source/gui}/loadcliargs.py | 6 ++--- source/gui/randomize/__init__.py | 1 + {gui => source/gui}/randomize/dungeon.py | 2 +- {gui => source/gui}/randomize/enemizer.py | 2 +- {gui => source/gui}/randomize/entrando.py | 2 +- {gui => source/gui}/randomize/gameoptions.py | 4 ++-- {gui => source/gui}/randomize/generation.py | 2 +- {gui => source/gui}/randomize/item.py | 2 +- {gui => source/gui}/randomize/multiworld.py | 2 +- source/gui/startinventory/__init__.py | 1 + .../gui}/startinventory/overview.py | 4 ++-- {gui => source/gui}/widgets.py | 0 33 files changed, 41 insertions(+), 40 deletions(-) delete mode 100644 classes/__init__.py delete mode 100644 gui/__init__.py delete mode 100644 gui/about/__init__.py delete mode 100644 gui/adjust/__init__.py delete mode 100644 gui/custom/__init__.py delete mode 100644 gui/randomize/__init__.py delete mode 100644 gui/startinventory/__init__.py create mode 100644 source/__init__.py rename {classes => source/classes}/SpriteSelector.py (100%) create mode 100644 source/classes/__init__.py rename {classes => source/classes}/constants.py (100%) create mode 100644 source/gui/__init__.py create mode 100644 source/gui/about/__init__.py create mode 100644 source/gui/adjust/__init__.py rename {gui => source/gui}/adjust/overview.py (97%) rename {gui => source/gui}/bottom.py (98%) create mode 100644 source/gui/custom/__init__.py rename {gui => source/gui}/custom/overview.py (95%) rename {gui => source/gui}/loadcliargs.py (95%) create mode 100644 source/gui/randomize/__init__.py rename {gui => source/gui}/randomize/dungeon.py (97%) rename {gui => source/gui}/randomize/enemizer.py (98%) rename {gui => source/gui}/randomize/entrando.py (96%) rename {gui => source/gui}/randomize/gameoptions.py (96%) rename {gui => source/gui}/randomize/generation.py (97%) rename {gui => source/gui}/randomize/item.py (96%) rename {gui => source/gui}/randomize/multiworld.py (97%) create mode 100644 source/gui/startinventory/__init__.py rename {gui => source/gui}/startinventory/overview.py (96%) rename {gui => source/gui}/widgets.py (100%) diff --git a/CLI.py b/CLI.py index 69a00b09..60391631 100644 --- a/CLI.py +++ b/CLI.py @@ -13,7 +13,7 @@ from Rom import get_sprite_from_name from Utils import is_bundled, close_console from Fill import FillError -import classes.constants as CONST +import source.classes.constants as CONST class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): diff --git a/Gui.py b/Gui.py index 40605db6..76215dce 100755 --- a/Gui.py +++ b/Gui.py @@ -7,18 +7,18 @@ from tkinter import Tk, Button, BOTTOM, TOP, StringVar, BooleanVar, X, BOTH, RIG from argparse import Namespace 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 -from gui.custom.overview import custom_page -from gui.loadcliargs import loadcliargs, loadadjustargs -from gui.randomize.item import item_page -from gui.randomize.entrando import entrando_page -from gui.randomize.enemizer import enemizer_page -from gui.randomize.dungeon import dungeon_page -from gui.randomize.multiworld import multiworld_page -from gui.randomize.gameoptions import gameoptions_page -from gui.randomize.generation import generation_page -from gui.bottom import bottom_frame, create_guiargs +from source.gui.adjust.overview import adjust_page +from source.gui.startinventory.overview import startinventory_page +from source.gui.custom.overview import custom_page +from source.gui.loadcliargs import loadcliargs, loadadjustargs +from source.gui.randomize.item import item_page +from source.gui.randomize.entrando import entrando_page +from source.gui.randomize.enemizer import enemizer_page +from source.gui.randomize.dungeon import dungeon_page +from source.gui.randomize.multiworld import multiworld_page +from source.gui.randomize.gameoptions import gameoptions_page +from source.gui.randomize.generation import generation_page +from source.gui.bottom import bottom_frame, create_guiargs from GuiUtils import set_icon from Main import __version__ as ESVersion from Rom import get_sprite_from_name diff --git a/ItemList.py b/ItemList.py index 971424fc..7c7684d0 100644 --- a/ItemList.py +++ b/ItemList.py @@ -9,7 +9,7 @@ from EntranceShuffle import connect_entrance from Fill import FillError, fill_restrictive from Items import ItemFactory -import classes.constants as CONST +import source.classes.constants as CONST #This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space. diff --git a/classes/__init__.py b/classes/__init__.py deleted file mode 100644 index a9d60931..00000000 --- a/classes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make "classes" package diff --git a/gui/__init__.py b/gui/__init__.py deleted file mode 100644 index 8c5232bc..00000000 --- a/gui/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make "gui" package diff --git a/gui/about/__init__.py b/gui/about/__init__.py deleted file mode 100644 index 1ad9a517..00000000 --- a/gui/about/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make "gui.about" package diff --git a/gui/adjust/__init__.py b/gui/adjust/__init__.py deleted file mode 100644 index 3e1ae764..00000000 --- a/gui/adjust/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make "gui.adjust" package diff --git a/gui/custom/__init__.py b/gui/custom/__init__.py deleted file mode 100644 index 33cec31c..00000000 --- a/gui/custom/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make "gui.custom" package diff --git a/gui/randomize/__init__.py b/gui/randomize/__init__.py deleted file mode 100644 index ecf3a271..00000000 --- a/gui/randomize/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make "gui.randomize" package diff --git a/gui/startinventory/__init__.py b/gui/startinventory/__init__.py deleted file mode 100644 index fa319bae..00000000 --- a/gui/startinventory/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make "gui.startinventory" package diff --git a/source/__init__.py b/source/__init__.py new file mode 100644 index 00000000..724252e9 --- /dev/null +++ b/source/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source" package diff --git a/classes/SpriteSelector.py b/source/classes/SpriteSelector.py similarity index 100% rename from classes/SpriteSelector.py rename to source/classes/SpriteSelector.py diff --git a/source/classes/__init__.py b/source/classes/__init__.py new file mode 100644 index 00000000..68fbbf63 --- /dev/null +++ b/source/classes/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source.classes" package diff --git a/classes/constants.py b/source/classes/constants.py similarity index 100% rename from classes/constants.py rename to source/classes/constants.py diff --git a/source/gui/__init__.py b/source/gui/__init__.py new file mode 100644 index 00000000..00601e6a --- /dev/null +++ b/source/gui/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source.gui" package diff --git a/source/gui/about/__init__.py b/source/gui/about/__init__.py new file mode 100644 index 00000000..5f70030c --- /dev/null +++ b/source/gui/about/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source.gui.about" package diff --git a/source/gui/adjust/__init__.py b/source/gui/adjust/__init__.py new file mode 100644 index 00000000..a838ecf0 --- /dev/null +++ b/source/gui/adjust/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source.gui.adjust" package diff --git a/gui/adjust/overview.py b/source/gui/adjust/overview.py similarity index 97% rename from gui/adjust/overview.py rename to source/gui/adjust/overview.py index 8e3a7851..b7fcb78f 100644 --- a/gui/adjust/overview.py +++ b/source/gui/adjust/overview.py @@ -1,8 +1,8 @@ from tkinter import ttk, filedialog, messagebox, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, OptionMenu, E, W, LEFT, RIGHT, X, BOTTOM from AdjusterMain import adjust from argparse import Namespace -from classes.SpriteSelector import SpriteSelector -import gui.widgets as widgets +from source.classes.SpriteSelector import SpriteSelector +import source.gui.widgets as widgets import json import logging import os diff --git a/gui/bottom.py b/source/gui/bottom.py similarity index 98% rename from gui/bottom.py rename to source/gui/bottom.py index 22d94d35..90fdc8c0 100644 --- a/gui/bottom.py +++ b/source/gui/bottom.py @@ -7,8 +7,8 @@ import random from CLI import parse_arguments, get_settings from Main import main from Utils import local_path, output_path, open_file -import classes.constants as CONST -import gui.widgets as widgets +import source.classes.constants as CONST +import source.gui.widgets as widgets def bottom_frame(self, parent, args=None): diff --git a/source/gui/custom/__init__.py b/source/gui/custom/__init__.py new file mode 100644 index 00000000..febb4d06 --- /dev/null +++ b/source/gui/custom/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source.gui.custom" package diff --git a/gui/custom/overview.py b/source/gui/custom/overview.py similarity index 95% rename from gui/custom/overview.py rename to source/gui/custom/overview.py index e7c7fa63..54c4f7f3 100644 --- a/gui/custom/overview.py +++ b/source/gui/custom/overview.py @@ -1,9 +1,9 @@ from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, LEFT, RIGHT, X, VERTICAL, Y -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os -import classes.constants as CONST +import source.classes.constants as CONST def custom_page(top,parent): # Custom Item Pool diff --git a/gui/loadcliargs.py b/source/gui/loadcliargs.py similarity index 95% rename from gui/loadcliargs.py rename to source/gui/loadcliargs.py index 14a8b007..462c5de6 100644 --- a/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -1,7 +1,7 @@ -from classes.SpriteSelector import SpriteSelector as spriteSelector -from gui.randomize.gameoptions import set_sprite +from source.classes.SpriteSelector import SpriteSelector as spriteSelector +from source.gui.randomize.gameoptions import set_sprite from Rom import Sprite -import classes.constants as CONST +import source.classes.constants as CONST def loadcliargs(gui, args, settings=None): if args is not None: diff --git a/source/gui/randomize/__init__.py b/source/gui/randomize/__init__.py new file mode 100644 index 00000000..564c2ab5 --- /dev/null +++ b/source/gui/randomize/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source.gui.randomize" package diff --git a/gui/randomize/dungeon.py b/source/gui/randomize/dungeon.py similarity index 97% rename from gui/randomize/dungeon.py rename to source/gui/randomize/dungeon.py index 1d575521..11dd1b58 100644 --- a/gui/randomize/dungeon.py +++ b/source/gui/randomize/dungeon.py @@ -1,5 +1,5 @@ from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os diff --git a/gui/randomize/enemizer.py b/source/gui/randomize/enemizer.py similarity index 98% rename from gui/randomize/enemizer.py rename to source/gui/randomize/enemizer.py index cb78281c..26aab8ec 100644 --- a/gui/randomize/enemizer.py +++ b/source/gui/randomize/enemizer.py @@ -1,6 +1,6 @@ import os from tkinter import ttk, filedialog, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, LabelFrame, OptionMenu, N, E, W, LEFT, RIGHT, BOTTOM, X -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os import webbrowser diff --git a/gui/randomize/entrando.py b/source/gui/randomize/entrando.py similarity index 96% rename from gui/randomize/entrando.py rename to source/gui/randomize/entrando.py index 3ad6bac4..e094dc3b 100644 --- a/gui/randomize/entrando.py +++ b/source/gui/randomize/entrando.py @@ -1,5 +1,5 @@ from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os diff --git a/gui/randomize/gameoptions.py b/source/gui/randomize/gameoptions.py similarity index 96% rename from gui/randomize/gameoptions.py rename to source/gui/randomize/gameoptions.py index eb107df4..3369f33f 100644 --- a/gui/randomize/gameoptions.py +++ b/source/gui/randomize/gameoptions.py @@ -1,7 +1,7 @@ from tkinter import ttk, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, OptionMenu, E, W, LEFT, RIGHT from functools import partial -import classes.SpriteSelector as spriteSelector -import gui.widgets as widgets +import source.classes.SpriteSelector as spriteSelector +import source.gui.widgets as widgets import json import os diff --git a/gui/randomize/generation.py b/source/gui/randomize/generation.py similarity index 97% rename from gui/randomize/generation.py rename to source/gui/randomize/generation.py index d5f5ce1c..725c0ad9 100644 --- a/gui/randomize/generation.py +++ b/source/gui/randomize/generation.py @@ -1,6 +1,6 @@ import os from tkinter import ttk, filedialog, IntVar, StringVar, Button, Checkbutton, Entry, Frame, Label, E, W, LEFT, RIGHT, X -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os diff --git a/gui/randomize/item.py b/source/gui/randomize/item.py similarity index 96% rename from gui/randomize/item.py rename to source/gui/randomize/item.py index f962d574..45714dfd 100644 --- a/gui/randomize/item.py +++ b/source/gui/randomize/item.py @@ -1,5 +1,5 @@ from tkinter import ttk, IntVar, StringVar, Checkbutton, Frame, Label, OptionMenu, E, W, LEFT, RIGHT -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os diff --git a/gui/randomize/multiworld.py b/source/gui/randomize/multiworld.py similarity index 97% rename from gui/randomize/multiworld.py rename to source/gui/randomize/multiworld.py index 646b02d1..3d67d38e 100644 --- a/gui/randomize/multiworld.py +++ b/source/gui/randomize/multiworld.py @@ -1,5 +1,5 @@ from tkinter import ttk, StringVar, Entry, Frame, Label, Spinbox, N, E, W, X, LEFT, RIGHT -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os diff --git a/source/gui/startinventory/__init__.py b/source/gui/startinventory/__init__.py new file mode 100644 index 00000000..024e33ee --- /dev/null +++ b/source/gui/startinventory/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source.gui.startinventory" package diff --git a/gui/startinventory/overview.py b/source/gui/startinventory/overview.py similarity index 96% rename from gui/startinventory/overview.py rename to source/gui/startinventory/overview.py index aaa601b3..46dd74dc 100644 --- a/gui/startinventory/overview.py +++ b/source/gui/startinventory/overview.py @@ -1,9 +1,9 @@ from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, LEFT, RIGHT, X, VERTICAL, Y -import gui.widgets as widgets +import source.gui.widgets as widgets import json import os -import classes.constants as CONST +import source.classes.constants as CONST def startinventory_page(top,parent): # Starting Inventory diff --git a/gui/widgets.py b/source/gui/widgets.py similarity index 100% rename from gui/widgets.py rename to source/gui/widgets.py From ee52953cca07afe75e55fda9b396e043c1de569c Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:17:04 -0800 Subject: [PATCH 26/93] Try App Version again --- .github/workflows/ci.yml | 1 + build-app_version.py | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 build-app_version.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 557af1d9..08fcd154 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,6 +115,7 @@ jobs: env: OS_NAME: ${{ matrix.os-name }} run: | + python ./build-app_version.py python ./resources/ci/common/prepare_appversion.py python ./resources/ci/common/prepare_release.py # upload appversion artifact for later step diff --git a/build-app_version.py b/build-app_version.py new file mode 100644 index 00000000..87660345 --- /dev/null +++ b/build-app_version.py @@ -0,0 +1,5 @@ +from Main import __version__ as DRVersion +import os + +with(open(os.path.join("resources","app","manifests","app_version.txt"))) as f: + f.write(DRVersion) From 637eb111b84c422d43d344137ddfaf632eea24f2 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:20:17 -0800 Subject: [PATCH 27/93] Open for writing --- build-app_version.py | 2 +- resources/ci/common/prepare_binary.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/build-app_version.py b/build-app_version.py index 87660345..110f2cf0 100644 --- a/build-app_version.py +++ b/build-app_version.py @@ -1,5 +1,5 @@ from Main import __version__ as DRVersion import os -with(open(os.path.join("resources","app","manifests","app_version.txt"))) as f: +with(open(os.path.join("resources","app","manifests","app_version.txt"),"w+")) as f: f.write(DRVersion) diff --git a/resources/ci/common/prepare_binary.py b/resources/ci/common/prepare_binary.py index bc4e0d09..583c7d7b 100644 --- a/resources/ci/common/prepare_binary.py +++ b/resources/ci/common/prepare_binary.py @@ -7,9 +7,6 @@ from shutil import copy, make_archive, move, rmtree # file manipulation env = common.prepare_env() -if "OS_NAME" in env and env["OS_NAME"] == "osx": - subprocess.check_call(["ls","-l"]) - # make dir to put the binary in if not os.path.isdir(os.path.join("..","artifact")): os.mkdir(os.path.join("..","artifact")) From aeeb5dba11a18f10484922842dc55fc8ef9dc9b2 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:25:47 -0800 Subject: [PATCH 28/93] Folders are dumb --- build-app_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-app_version.py b/build-app_version.py index 110f2cf0..6d63f3ca 100644 --- a/build-app_version.py +++ b/build-app_version.py @@ -1,5 +1,5 @@ from Main import __version__ as DRVersion import os -with(open(os.path.join("resources","app","manifests","app_version.txt"),"w+")) as f: +with(open(os.path.join("resources","app","meta","manifests","app_version.txt"),"w+")) as f: f.write(DRVersion) From 2e19a3205348c9575b305ec7d09982da71e22bd8 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:30:16 -0800 Subject: [PATCH 29/93] Don't git clean App Version files --- resources/ci/common/git_clean.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/ci/common/git_clean.py b/resources/ci/common/git_clean.py index 9b424283..6a8ca8fc 100644 --- a/resources/ci/common/git_clean.py +++ b/resources/ci/common/git_clean.py @@ -8,7 +8,8 @@ def git_clean(): "-dfx", # d: directories, f: files, x: ignored files "--exclude=.vscode", # keep vscode IDE files "--exclude=.idea", # keep idea IDE files - "--exclude=*.json"]) # keep JSON files for that one time I just nuked all that I was working on, oops + "--exclude=*.json", # keep JSON files for that one time I just nuked all that I was working on, oops + "--exclude=*app*version.*"]) # keep appversion files if __name__ == "__main__": git_clean() From cc6a675864f95054365c3561093de94e6f1b5536 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:44:07 -0800 Subject: [PATCH 30/93] Go big or go home --- .github/workflows/ci.yml | 117 +++++++++++++++++- DungeonRandomizer.spec | 3 +- Gui.spec | 3 +- .../app/meta/manifests/pip_requirements.txt | 1 - 4 files changed, 120 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08fcd154..3825f328 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,7 @@ jobs: OS_NAME: ${{ matrix.os-name }} run: | python ./resources/ci/common/install.py + pip install pyinstaller # try to get UPX - name: Get UPX env: @@ -50,7 +51,6 @@ jobs: # run build-gui.py - name: Build GUI run: | - pip install pyinstaller python ./build-gui.py # run build-dr.py - name: Build DungeonRandomizer @@ -130,3 +130,118 @@ jobs: with: name: archive-${{ matrix.os-name }} path: ../deploy + + # Deploy to GitHub Releases + # Release Name: ALttPDoorRandomizer v${GITHUB_TAG} + # Release Body: Inline content of RELEASENOTES.md + # Release Body: Fallback to URL to RELEASENOTES.md + # Release Files: ../deploy + deploy-release: + name: Deploy GHReleases + runs-on: ${{ matrix.os-name }} + + # VM settings + # os & python versions + strategy: + matrix: + # release only on bionic + os-name: [ ubuntu-latest ] + python-version: [ 3.7 ] + + needs: [ install-prepare-release ] + steps: + # checkout commit + - name: Checkout commit + uses: actions/checkout@v1 + - name: Install Dependencies via pip + run: | + python -m pip install pytz requests + # download appversion artifact + - name: Download AppVersion Artifact + uses: actions/download-artifact@v1 + with: + name: appversion-${{ matrix.os-name }} + path: ../build + # download ubuntu archive artifact + - name: Download Ubuntu Archive Artifact + uses: actions/download-artifact@v1 + with: + name: archive-ubuntu-latest + path: ../deploy/linux + # download macos archive artifact + - name: Download MacOS Archive Artifact + uses: actions/download-artifact@v1 + with: + name: archive-macOS-latest + path: ../deploy/macos + # download windows archive artifact + - name: Download Windows Archive Artifact + uses: actions/download-artifact@v1 + with: + name: archive-windows-latest + path: ../deploy/windows + # debug info + - name: Debug Info + id: debug_info +# shell: bash +# git tag ${GITHUB_TAG} +# git push origin ${GITHUB_TAG} + run: | + GITHUB_TAG="$(head -n 1 ../build/app_version.txt)" + echo "::set-output name=github_tag::$GITHUB_TAG" + GITHUB_TAG="v${GITHUB_TAG}" + RELEASE_NAME="ALttPDoorRandomizer ${GITHUB_TAG}" + echo "Release Name: ${RELEASE_NAME}" + echo "Git Tag: ${GITHUB_TAG}" + # read releasenotes + - name: Read RELEASENOTES + id: release_notes + run: | + body="$(cat RELEASENOTES.md)" + body="${body//'%'/'%25'}" + body="${body//$'\n'/'%0A'}" + body="${body//$'\r'/'%0D'}" + echo "::set-output name=body::$body" + # create a release + - name: Create a Release + id: create_release + uses: actions/create-release@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ steps.debug_info.outputs.github_tag }} + release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} + body: ${{ steps.release_notes.outputs.body }} + # upload linux archive asset + - name: Upload Linux Archive Asset + id: upload-linux-asset + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ../deploy/linux/ALttPDoorRandomizer.tar.gz + asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-bionic.tar.gz + asset_content_type: application/gzip + # upload macos archive asset + - name: Upload MacOS Archive Asset + id: upload-macos-asset + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ../deploy/macos/ALttPDoorRandomizer.tar.gz + asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-osx.tar.gz + asset_content_type: application/gzip + # upload windows archive asset + - name: Upload Windows Archive Asset + id: upload-windows-asset + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ../deploy/windows/ALttPDoorRandomizer.zip + asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-windows.zip + asset_content_type: application/zip diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index 7b8de387..13d67eb5 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -39,7 +39,8 @@ a = Analysis(['DungeonRandomizer.py'], excluded_binaries = [ 'VCRUNTIME140.dll', 'msvcp140.dll', - 'mfc140u.dll'] + 'mfc140u.dll', + 'ucrtbase.dll] a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries]) pyz = PYZ(a.pure, a.zipped_data, diff --git a/Gui.spec b/Gui.spec index 9d02abdf..783fbea9 100644 --- a/Gui.spec +++ b/Gui.spec @@ -44,7 +44,8 @@ a = Analysis(['Gui.py'], excluded_binaries = [ 'VCRUNTIME140.dll', 'msvcp140.dll', - 'mfc140u.dll'] + 'mfc140u.dll', + 'ucrtbase.dll] a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries]) pyz = PYZ(a.pure, a.zipped_data, diff --git a/resources/app/meta/manifests/pip_requirements.txt b/resources/app/meta/manifests/pip_requirements.txt index ef376ca8..e69de29b 100644 --- a/resources/app/meta/manifests/pip_requirements.txt +++ b/resources/app/meta/manifests/pip_requirements.txt @@ -1 +0,0 @@ -pyinstaller From 7f38ccaf748b94020d3578ffdfb47d13a6ed8bc9 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:45:07 -0800 Subject: [PATCH 31/93] Create RELEASENOTES.md --- RELEASENOTES.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 RELEASENOTES.md diff --git a/RELEASENOTES.md b/RELEASENOTES.md new file mode 100644 index 00000000..27c2b586 --- /dev/null +++ b/RELEASENOTES.md @@ -0,0 +1,4 @@ +# Features + +* Native GUI executables +* Native Dungeon Randomizer CLI executables From 089012b5940d60436bbfe7ec27ec7dc66783a52b Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:47:49 -0800 Subject: [PATCH 32/93] Strings are dumb --- DungeonRandomizer.spec | 2 +- Gui.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index 13d67eb5..9dfabd0a 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -40,7 +40,7 @@ excluded_binaries = [ 'VCRUNTIME140.dll', 'msvcp140.dll', 'mfc140u.dll', - 'ucrtbase.dll] + 'ucrtbase.dll'] a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries]) pyz = PYZ(a.pure, a.zipped_data, diff --git a/Gui.spec b/Gui.spec index 783fbea9..2e9bbd6d 100644 --- a/Gui.spec +++ b/Gui.spec @@ -45,7 +45,7 @@ excluded_binaries = [ 'VCRUNTIME140.dll', 'msvcp140.dll', 'mfc140u.dll', - 'ucrtbase.dll] + 'ucrtbase.dll'] a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries]) pyz = PYZ(a.pure, a.zipped_data, From 1b889845c384b7c08a5fbed75115e5b7138d463d Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 01:56:22 -0800 Subject: [PATCH 33/93] Not nearly as big as SpriteSomething --- resources/ci/common/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 8e68851d..2ef8dd64 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -121,7 +121,7 @@ def find_binary(listdir): if os.path.splitext(filename)[1] != ".py": st = os.stat(filename) mode = st.st_mode - big = st.st_size > (10 * 1024 * 1024) # 10MB + big = st.st_size > (4.7 * 1024 * 1024) # 10MB if (mode & executable) or big: if "GUI" in filename or "Gui" in filename or "DungeonRandomizer" in filename: BUILD_FILENAMES.append(filename) From 8e7da9467f4ffdec103d9d544ad68104078dbf55 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:06:35 -0800 Subject: [PATCH 34/93] Do PreRelease --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3825f328..77b2f49c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -212,6 +212,8 @@ jobs: tag_name: v${{ steps.debug_info.outputs.github_tag }} release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} body: ${{ steps.release_notes.outputs.body }} + draft: contains(${{ steps.debug_info.outputs.github_tag }},'p.') or contains(${{ steps.debug_info.outputs.github_tag }},'pre.') + prerelease: contains(${{ steps.debug_info.outputs.github_tag }},'p.') or contains(${{ steps.debug_info.outputs.github_tag }},'pre.') # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset From dbd727208a8336cfb9d8ae4d68d2744a7708f814 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:23:03 -0800 Subject: [PATCH 35/93] Pre/Release --- .github/workflows/ci.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77b2f49c..974f2c0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -202,6 +202,19 @@ jobs: body="${body//$'\n'/'%0A'}" body="${body//$'\r'/'%0D'}" echo "::set-output name=body::$body" + # create a prerelease + - name: Create a PreRelease + id: create_prerelease + uses: actions/create-release@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ steps.debug_info.outputs.github_tag }} + release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} + body: ${{ steps.release_notes.outputs.body }} + draft: true + prerelease: true + if: contains(${{ steps.debug_info.outputs.github_tag }},'p.') or contains(${{ steps.debug_info.outputs.github_tag }}, 'pre.') # create a release - name: Create a Release id: create_release @@ -212,8 +225,9 @@ jobs: tag_name: v${{ steps.debug_info.outputs.github_tag }} release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} body: ${{ steps.release_notes.outputs.body }} - draft: contains(${{ steps.debug_info.outputs.github_tag }},'p.') or contains(${{ steps.debug_info.outputs.github_tag }},'pre.') - prerelease: contains(${{ steps.debug_info.outputs.github_tag }},'p.') or contains(${{ steps.debug_info.outputs.github_tag }},'pre.') + draft: true + prerelease: false + if: not contains(${{ steps.debug_info.outputs.github_tag }},'p.') and not contains(${{ steps.debug_info.outputs.github_tag }}, 'pre.') # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset From bcd56da5c1837c050006dbf7fd853370d0139532 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:25:28 -0800 Subject: [PATCH 36/93] Try again --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 974f2c0a..9d66ef1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -214,7 +214,7 @@ jobs: body: ${{ steps.release_notes.outputs.body }} draft: true prerelease: true - if: contains(${{ steps.debug_info.outputs.github_tag }},'p.') or contains(${{ steps.debug_info.outputs.github_tag }}, 'pre.') + if: contains(steps.debug_info.outputs.github_tag,'p.') or contains(steps.debug_info.outputs.github_tag, 'pre.') # create a release - name: Create a Release id: create_release @@ -227,7 +227,7 @@ jobs: body: ${{ steps.release_notes.outputs.body }} draft: true prerelease: false - if: not contains(${{ steps.debug_info.outputs.github_tag }},'p.') and not contains(${{ steps.debug_info.outputs.github_tag }}, 'pre.') + if: not contains(steps.debug_info.outputs.github_tag,'p.') and not contains(steps.debug_info.outputs.github_tag, 'pre.') # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset From bd042072f5ff25924bb5037475e27ef642a311cd Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:27:42 -0800 Subject: [PATCH 37/93] Operators --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d66ef1e..4a5f210d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -214,7 +214,7 @@ jobs: body: ${{ steps.release_notes.outputs.body }} draft: true prerelease: true - if: contains(steps.debug_info.outputs.github_tag,'p.') or contains(steps.debug_info.outputs.github_tag, 'pre.') + if: contains(steps.debug_info.outputs.github_tag,'p.') || contains(steps.debug_info.outputs.github_tag, 'pre.') # create a release - name: Create a Release id: create_release @@ -227,7 +227,7 @@ jobs: body: ${{ steps.release_notes.outputs.body }} draft: true prerelease: false - if: not contains(steps.debug_info.outputs.github_tag,'p.') and not contains(steps.debug_info.outputs.github_tag, 'pre.') + if: (! contains(steps.debug_info.outputs.github_tag,'p.') && ! contains(steps.debug_info.outputs.github_tag, 'pre.')) # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset From e40c7e4332173e9b337ab252b34194777d824cd7 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:31:51 -0800 Subject: [PATCH 38/93] Can I cheat the ID? --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a5f210d..93c0f493 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -204,7 +204,7 @@ jobs: echo "::set-output name=body::$body" # create a prerelease - name: Create a PreRelease - id: create_prerelease + id: create_release uses: actions/create-release@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e42e50c5a75a094b07db9b5d03f5039354430547 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:33:21 -0800 Subject: [PATCH 39/93] Try conditional again? --- .github/workflows/ci.yml | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93c0f493..49237e26 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -202,8 +202,8 @@ jobs: body="${body//$'\n'/'%0A'}" body="${body//$'\r'/'%0D'}" echo "::set-output name=body::$body" - # create a prerelease - - name: Create a PreRelease + # create a pre/release + - name: Create a Pre/Release id: create_release uses: actions/create-release@master env: @@ -213,21 +213,7 @@ jobs: release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} body: ${{ steps.release_notes.outputs.body }} draft: true - prerelease: true - if: contains(steps.debug_info.outputs.github_tag,'p.') || contains(steps.debug_info.outputs.github_tag, 'pre.') - # create a release - - name: Create a Release - id: create_release - uses: actions/create-release@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: v${{ steps.debug_info.outputs.github_tag }} - release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} - body: ${{ steps.release_notes.outputs.body }} - draft: true - prerelease: false - if: (! contains(steps.debug_info.outputs.github_tag,'p.') && ! contains(steps.debug_info.outputs.github_tag, 'pre.')) + prerelease: contains(steps.debug_info.outputs.github_tag,'p.') || contains(steps.debug_info.outputs.github_tag, 'pre.') # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset From f1ad50e14bc83957562a4d9f9308fdcaa156de20 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:38:20 -0800 Subject: [PATCH 40/93] Test not a draft --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49237e26..4b49d489 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -212,7 +212,6 @@ jobs: tag_name: v${{ steps.debug_info.outputs.github_tag }} release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} body: ${{ steps.release_notes.outputs.body }} - draft: true prerelease: contains(steps.debug_info.outputs.github_tag,'p.') || contains(steps.debug_info.outputs.github_tag, 'pre.') # upload linux archive asset - name: Upload Linux Archive Asset From c5f08f7b278be39c3caf8924922de710bfb4abe1 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:43:38 -0800 Subject: [PATCH 41/93] Back to drafts --- .github/workflows/ci.yml | 2 +- DungeonRandomizer.spec | 3 +-- Gui.spec | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b49d489..9796cf5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -212,7 +212,7 @@ jobs: tag_name: v${{ steps.debug_info.outputs.github_tag }} release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} body: ${{ steps.release_notes.outputs.body }} - prerelease: contains(steps.debug_info.outputs.github_tag,'p.') || contains(steps.debug_info.outputs.github_tag, 'pre.') + draft: true # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index 9dfabd0a..7b8de387 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -39,8 +39,7 @@ a = Analysis(['DungeonRandomizer.py'], excluded_binaries = [ 'VCRUNTIME140.dll', 'msvcp140.dll', - 'mfc140u.dll', - 'ucrtbase.dll'] + 'mfc140u.dll'] a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries]) pyz = PYZ(a.pure, a.zipped_data, diff --git a/Gui.spec b/Gui.spec index 2e9bbd6d..9d02abdf 100644 --- a/Gui.spec +++ b/Gui.spec @@ -44,8 +44,7 @@ a = Analysis(['Gui.py'], excluded_binaries = [ 'VCRUNTIME140.dll', 'msvcp140.dll', - 'mfc140u.dll', - 'ucrtbase.dll'] + 'mfc140u.dll'] a.binaries = TOC([x for x in a.binaries if x[0] not in excluded_binaries]) pyz = PYZ(a.pure, a.zipped_data, From 4063b538d0091e8e1dd0ca64ba8007fff4cd56b5 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:50:43 -0800 Subject: [PATCH 42/93] Delete __init__.py --- __init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 41df9bfe..00000000 --- a/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# do nothing, just exist to make root package From 055ad6bd717ab789987f8c5bc50ea29cdcb2f6f6 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:51:42 -0800 Subject: [PATCH 43/93] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9796cf5b..a1bb6f8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,7 +148,7 @@ jobs: os-name: [ ubuntu-latest ] python-version: [ 3.7 ] - needs: [ install-prepare-release ] + needs: [ install-prepare-release.off ] steps: # checkout commit - name: Checkout commit From a46f84b3ae44cd1aed9c17e9f4f4a8ad035144c7 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 02:53:23 -0800 Subject: [PATCH 44/93] Update ci.yml --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1bb6f8b..db372478 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,7 +148,7 @@ jobs: os-name: [ ubuntu-latest ] python-version: [ 3.7 ] - needs: [ install-prepare-release.off ] + needs: [ install-prepare-release ] steps: # checkout commit - name: Checkout commit @@ -213,6 +213,7 @@ jobs: release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} body: ${{ steps.release_notes.outputs.body }} draft: true + if: false # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset @@ -224,6 +225,7 @@ jobs: asset_path: ../deploy/linux/ALttPDoorRandomizer.tar.gz asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-bionic.tar.gz asset_content_type: application/gzip + if: false # upload macos archive asset - name: Upload MacOS Archive Asset id: upload-macos-asset @@ -235,6 +237,7 @@ jobs: asset_path: ../deploy/macos/ALttPDoorRandomizer.tar.gz asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-osx.tar.gz asset_content_type: application/gzip + if: false # upload windows archive asset - name: Upload Windows Archive Asset id: upload-windows-asset @@ -246,3 +249,4 @@ jobs: asset_path: ../deploy/windows/ALttPDoorRandomizer.zip asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-windows.zip asset_content_type: application/zip + if: false From 54bc0aeae81b7d617d569de01a8765c9e29d2369 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 27 Feb 2020 03:05:28 -0800 Subject: [PATCH 45/93] Make a prerelease for now --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db372478..ff6d8368 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -213,7 +213,7 @@ jobs: release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} body: ${{ steps.release_notes.outputs.body }} draft: true - if: false + prerelease: true # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset @@ -225,7 +225,6 @@ jobs: asset_path: ../deploy/linux/ALttPDoorRandomizer.tar.gz asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-bionic.tar.gz asset_content_type: application/gzip - if: false # upload macos archive asset - name: Upload MacOS Archive Asset id: upload-macos-asset @@ -237,7 +236,6 @@ jobs: asset_path: ../deploy/macos/ALttPDoorRandomizer.tar.gz asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-osx.tar.gz asset_content_type: application/gzip - if: false # upload windows archive asset - name: Upload Windows Archive Asset id: upload-windows-asset @@ -249,4 +247,3 @@ jobs: asset_path: ../deploy/windows/ALttPDoorRandomizer.zip asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-windows.zip asset_content_type: application/zip - if: false From 61fbb0021772dce825d3f3cd7484daaf092cc7ae Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Sat, 29 Feb 2020 15:56:40 -0800 Subject: [PATCH 46/93] Prepare for new SpriteSomething URLs --- source/classes/SpriteSelector.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/classes/SpriteSelector.py b/source/classes/SpriteSelector.py index 7e527d05..486b0792 100644 --- a/source/classes/SpriteSelector.py +++ b/source/classes/SpriteSelector.py @@ -36,6 +36,7 @@ class SpriteSelector(object): def open_spritesomething_listing(_evt): webbrowser.open("https://artheau.github.io/SpriteSomething/?mode=zelda3/link") +# webbrowser.open("https://artheau.github.io/SpriteSomething/resources/app/snes/zelda3/link/sprites.html") official_frametitle = Frame(self.window) official_title_text = Label(official_frametitle, text="Official Sprites") From e9af70463a17e209dc89aed73ddbdbb0a4ddd271 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 3 Mar 2020 06:56:46 -0800 Subject: [PATCH 47/93] Bring closer to v18 --- source/classes/constants.py | 7 ++++--- source/gui/loadcliargs.py | 12 +++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/source/classes/constants.py b/source/classes/constants.py index 90434970..36bf0ec2 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -33,7 +33,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)", @@ -102,7 +102,8 @@ SETTINGSTOPROCESS = { "spoiler": "create_spoiler", "suppressrom": "suppress_rom", "usestartinventory": "usestartinventory", - "usecustompool": "custom" - } + "usecustompool": "custom", + "saveonexit": "saveonexit" + } } } diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 462c5de6..b62f5065 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -1,6 +1,6 @@ from source.classes.SpriteSelector import SpriteSelector as spriteSelector from source.gui.randomize.gameoptions import set_sprite -from Rom import Sprite +from Rom import Sprite, get_sprite_from_name import source.classes.constants as CONST def loadcliargs(gui, args, settings=None): @@ -39,18 +39,16 @@ def loadcliargs(gui, args, settings=None): 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"]) - r_sprite_flag = args.randomSprite if hasattr(args, 'randomSprite') else False - set_sprite(sprite_obj, r_sprite_flag, spriteSetter=sprite_setter, + sprite_obj = args.sprite if isinstance(args["sprite"], Sprite) else get_sprite_from_name(args["sprite"]) + set_sprite(sprite_obj, False, spriteSetter=sprite_setter, spriteNameVar=gui.pages["randomizer"].pages["gameoptions"].widgets["sprite"]["spriteNameVar"], randomSpriteVar=gui.randomSprite) def sprite_setter_adj(spriteObject): gui.pages["adjust"].content.sprite = spriteObject 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_adj, + sprite_obj = args.sprite if isinstance(args["sprite"], Sprite) else get_sprite_from_name(args["sprite"]) + set_sprite(sprite_obj, False, spriteSetter=sprite_setter_adj, spriteNameVar=gui.pages["adjust"].content.spriteNameVar2, randomSpriteVar=gui.randomSprite) From 4968e72a3b77a2c02fc99cd67de2788deb33fd73 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 3 Mar 2020 23:43:43 -0800 Subject: [PATCH 48/93] Document stuff, add a couple things Add Retro World State (Open & Retro on) Add SpriteSomething plug to sprite selector Fix Custom Item Pool loading to use disct instead of list --- BaseClasses.py | 4 +++ CLI.py | 9 +++--- Gui.py | 7 ++-- ItemList.py | 32 ++++++++++--------- Main.py | 5 +-- build-dr.py | 4 +++ build-gui.py | 4 +++ classes/SpriteSelector.py | 4 ++- classes/constants.py | 9 ++++-- gui/adjust/overview.py | 9 +++++- gui/bottom.py | 29 ++++++++++++++--- gui/custom/overview.py | 18 +++++++---- gui/loadcliargs.py | 21 ++++++++++++ gui/randomize/dungeon.py | 6 +++- gui/randomize/enemizer.py | 7 +++- gui/randomize/entrando.py | 7 +++- gui/randomize/gameoptions.py | 10 ++++-- gui/randomize/generation.py | 6 ++-- gui/randomize/item.py | 9 ++++-- gui/randomize/multiworld.py | 5 ++- gui/startinventory/overview.py | 11 +++++-- gui/widgets.py | 13 ++++++-- resources/app/gui/randomize/item/widgets.json | 3 +- 23 files changed, 179 insertions(+), 53 deletions(-) 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": { From 92cec430fff31cf5d5826732feb25ebe747991d4 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 00:54:42 -0800 Subject: [PATCH 49/93] See if this fixes this damn dll --- DungeonRandomizer.spec | 3 +++ Gui.spec | 3 +++ 2 files changed, 6 insertions(+) diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index 7b8de387..dff96ef5 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -35,6 +35,9 @@ a = Analysis(['DungeonRandomizer.py'], cipher=block_cipher, noarchive=False) +if sys.platform.find("windows"): + a.binaries.append("ucrtbase.dll") + # https://stackoverflow.com/questions/17034434/how-to-remove-exclude-modules-and-files-from-pyinstaller excluded_binaries = [ 'VCRUNTIME140.dll', diff --git a/Gui.spec b/Gui.spec index 9d02abdf..56404336 100644 --- a/Gui.spec +++ b/Gui.spec @@ -40,6 +40,9 @@ a = Analysis(['Gui.py'], cipher=block_cipher, noarchive=False) +if sys.platform.find("windows"): + a.binaries.append("ucrtbase.dll") + # https://stackoverflow.com/questions/17034434/how-to-remove-exclude-modules-and-files-from-pyinstaller excluded_binaries = [ 'VCRUNTIME140.dll', From 5b988818b7ad2d6fd7b07438c6d6180d738a7890 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:00:33 -0800 Subject: [PATCH 50/93] Add to analysis --- DungeonRandomizer.spec | 11 +++++++---- Gui.spec | 11 ++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index dff96ef5..4afcc6e8 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -1,5 +1,7 @@ # -*- mode: python -*- +import sys + block_cipher = None console = True @@ -21,10 +23,14 @@ def recurse_for_py_files(names_so_far): return returnvalue hiddenimports = [] +binaries = [] + +if sys.platform.find("windows"): + binaries.append("ucrtbase.dll") a = Analysis(['DungeonRandomizer.py'], pathex=[], - binaries=[], + binaries=binaries, datas=[], hiddenimports=hiddenimports, hookspath=[], @@ -35,9 +41,6 @@ a = Analysis(['DungeonRandomizer.py'], cipher=block_cipher, noarchive=False) -if sys.platform.find("windows"): - a.binaries.append("ucrtbase.dll") - # https://stackoverflow.com/questions/17034434/how-to-remove-exclude-modules-and-files-from-pyinstaller excluded_binaries = [ 'VCRUNTIME140.dll', diff --git a/Gui.spec b/Gui.spec index 56404336..b8bba397 100644 --- a/Gui.spec +++ b/Gui.spec @@ -26,10 +26,14 @@ def recurse_for_py_files(names_so_far): return returnvalue hiddenimports = [] +binaries = [] -a = Analysis(['Gui.py'], +if sys.platform.find("windows"): + binaries.append("ucrtbase.dll") + +a = Analysis(['DungeonRandomizer.py'], pathex=[], - binaries=[], + binaries=binaries, datas=[], hiddenimports=hiddenimports, hookspath=[], @@ -40,9 +44,6 @@ a = Analysis(['Gui.py'], cipher=block_cipher, noarchive=False) -if sys.platform.find("windows"): - a.binaries.append("ucrtbase.dll") - # https://stackoverflow.com/questions/17034434/how-to-remove-exclude-modules-and-files-from-pyinstaller excluded_binaries = [ 'VCRUNTIME140.dll', From dde662b2632bfd458d988b1eb379530d0c97c254 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:04:03 -0800 Subject: [PATCH 51/93] What's RTFM? --- DungeonRandomizer.spec | 2 +- Gui.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index 4afcc6e8..2afc0011 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -26,7 +26,7 @@ hiddenimports = [] binaries = [] if sys.platform.find("windows"): - binaries.append("ucrtbase.dll") + binaries.append("ucrtbase.dll",".") a = Analysis(['DungeonRandomizer.py'], pathex=[], diff --git a/Gui.spec b/Gui.spec index b8bba397..34960cda 100644 --- a/Gui.spec +++ b/Gui.spec @@ -29,7 +29,7 @@ hiddenimports = [] binaries = [] if sys.platform.find("windows"): - binaries.append("ucrtbase.dll") + binaries.append("ucrtbase.dll",".") a = Analysis(['DungeonRandomizer.py'], pathex=[], From b8bf4910e444a8e0cf0a5ec0b24f6472a6bef2c8 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:06:18 -0800 Subject: [PATCH 52/93] Make it Trouble, make it Tuple --- DungeonRandomizer.spec | 2 +- Gui.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index 2afc0011..4d0eaa00 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -26,7 +26,7 @@ hiddenimports = [] binaries = [] if sys.platform.find("windows"): - binaries.append("ucrtbase.dll",".") + binaries.append(("ucrtbase.dll",".")) a = Analysis(['DungeonRandomizer.py'], pathex=[], diff --git a/Gui.spec b/Gui.spec index 34960cda..1af65ffb 100644 --- a/Gui.spec +++ b/Gui.spec @@ -29,7 +29,7 @@ hiddenimports = [] binaries = [] if sys.platform.find("windows"): - binaries.append("ucrtbase.dll",".") + binaries.append(("ucrtbase.dll",".")) a = Analysis(['DungeonRandomizer.py'], pathex=[], From 27c685f2114cf54f76d72ec701fa2ee8c31dbe29 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:19:05 -0800 Subject: [PATCH 53/93] Meh, chase that rabbit later --- DungeonRandomizer.spec | 4 ++-- Gui.spec | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DungeonRandomizer.spec b/DungeonRandomizer.spec index 4d0eaa00..163e8413 100644 --- a/DungeonRandomizer.spec +++ b/DungeonRandomizer.spec @@ -25,8 +25,8 @@ def recurse_for_py_files(names_so_far): hiddenimports = [] binaries = [] -if sys.platform.find("windows"): - binaries.append(("ucrtbase.dll",".")) +#if sys.platform.find("windows"): +# binaries.append(("ucrtbase.dll",".")) a = Analysis(['DungeonRandomizer.py'], pathex=[], diff --git a/Gui.spec b/Gui.spec index 1af65ffb..a1b1a86c 100644 --- a/Gui.spec +++ b/Gui.spec @@ -28,8 +28,8 @@ def recurse_for_py_files(names_so_far): hiddenimports = [] binaries = [] -if sys.platform.find("windows"): - binaries.append(("ucrtbase.dll",".")) +#if sys.platform.find("windows"): +# binaries.append(("ucrtbase.dll",".")) a = Analysis(['DungeonRandomizer.py'], pathex=[], From 126a7793a48b18a1567c910fec286ec7e8c410ce Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:22:45 -0800 Subject: [PATCH 54/93] Try to fix Linux save path --- Utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Utils.py b/Utils.py index d616ef22..54b1a233 100644 --- a/Utils.py +++ b/Utils.py @@ -77,6 +77,8 @@ def output_path(path): NSUserDomainMask = 1 # True for expanding the tilde into a fully qualified path documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0] + elif sys.platform.find("linux"): + documents = os.path.join("~","Documents") else: raise NotImplementedError('Not supported yet') From 6476f96398f81c54821fc68f26d39886b655eb19 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:25:45 -0800 Subject: [PATCH 55/93] Ooh, Run IDs --- resources/ci/common/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 2ef8dd64..49f5da60 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -56,7 +56,7 @@ def prepare_env(): env["GITHUB_SHA_SHORT"] = env["GITHUB_SHA"][:7] # ci data - env["BUILD_NUMBER"] = os.getenv("TRAVIS_BUILD_NUMBER",env["GITHUB_SHA_SHORT"]) + env["BUILD_NUMBER"] = os.getenv("TRAVIS_BUILD_NUMBER",env["GITHUB_RUN_ID"]) GITHUB_TAG = os.getenv("TRAVIS_TAG",os.getenv("GITHUB_TAG","")) OS_NAME = os.getenv("TRAVIS_OS_NAME",os.getenv("OS_NAME","")).replace("macOS","osx") From d533a92e166ad805facbb9069bde993160e13e89 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:27:25 -0800 Subject: [PATCH 56/93] Meh --- resources/ci/common/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 49f5da60..6f793e21 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -33,6 +33,7 @@ def prepare_env(): env["BRANCH"] = os.getenv("TRAVIS_BRANCH","") env["GITHUB_ACTOR"] = os.getenv("GITHUB_ACTOR","MegaMan.EXE") env["GITHUB_SHA"] = os.getenv("GITHUB_SHA","") + env["GITHUB_RUN_ID"] = os.getenv("GITHUB_RUN_ID","") env["GITHUB_SHA_SHORT"] = env["GITHUB_SHA"] # commit data env["COMMIT_ID"] = os.getenv("TRAVIS_COMMIT",os.getenv("GITHUB_SHA","")) From b1d9633c152917c9fbb7e6b538e34585f62fc28d Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 01:38:41 -0800 Subject: [PATCH 57/93] Set exec flag --- resources/ci/common/prepare_release.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 802c9d16..668db67f 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -83,6 +83,10 @@ if len(BUILD_FILENAMES) > 0: os.path.join(".",BUILD_FILENAME) ) + # Make Linux/Mac binary executable + if "linux" in env["OS_NAME"] or "ubuntu" in env["OS_NAME"] or "mac" in env["OS_NAME"] or "osx" in env["OS_NAME"]: + os.chmod(BUILD_FILENAME,0o755) + # .zip if windows # .tar.gz otherwise ZIP_FILENAME = os.path.join("..","deploy",os.path.splitext(BUILD_FILENAME)[0]) From dd1a3d416f3dbd1d1d5f86aa6c67ed2116a4781e Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 02:06:08 -0800 Subject: [PATCH 58/93] chmod both --- resources/ci/common/prepare_release.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 668db67f..35c087b3 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -82,10 +82,9 @@ if len(BUILD_FILENAMES) > 0: os.path.join("..","artifact",BUILD_FILENAME), os.path.join(".",BUILD_FILENAME) ) - - # Make Linux/Mac binary executable - if "linux" in env["OS_NAME"] or "ubuntu" in env["OS_NAME"] or "mac" in env["OS_NAME"] or "osx" in env["OS_NAME"]: - os.chmod(BUILD_FILENAME,0o755) + # Make Linux/Mac binary executable + if "linux" in env["OS_NAME"] or "ubuntu" in env["OS_NAME"] or "mac" in env["OS_NAME"] or "osx" in env["OS_NAME"]: + os.chmod(os.path.join(".",BUILD_FILENAME),0o755) # .zip if windows # .tar.gz otherwise From 85587848d6996979a28f9f8695242be96aa451b4 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 02:22:32 -0800 Subject: [PATCH 59/93] Meh, shoot from hip --- Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utils.py b/Utils.py index 54b1a233..55c4d347 100644 --- a/Utils.py +++ b/Utils.py @@ -77,7 +77,7 @@ def output_path(path): NSUserDomainMask = 1 # True for expanding the tilde into a fully qualified path documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0] - elif sys.platform.find("linux"): + elif sys.platform.find("linux") or sys.platform.find("ubuntu") or sys.platform.find("unix"): documents = os.path.join("~","Documents") else: raise NotImplementedError('Not supported yet') From ccd48dfa4d9fe9877330fb80f9b98107c9c605e8 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 02:33:42 -0800 Subject: [PATCH 60/93] Sanity checks --- Utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Utils.py b/Utils.py index 55c4d347..c9612abc 100644 --- a/Utils.py +++ b/Utils.py @@ -84,7 +84,9 @@ def output_path(path): output_path.cached_path = os.path.join(documents, 'ALttPEntranceRandomizer') if not os.path.exists(output_path.cached_path): - os.mkdir(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 From c61bd00af63ec6fb4f0b7da93ee08a0d7ede51a6 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 4 Mar 2020 02:44:31 -0800 Subject: [PATCH 61/93] Update Utils.py --- Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utils.py b/Utils.py index c9612abc..c1a97ced 100644 --- a/Utils.py +++ b/Utils.py @@ -82,7 +82,7 @@ def output_path(path): else: raise NotImplementedError('Not supported yet') - output_path.cached_path = os.path.join(documents, 'ALttPEntranceRandomizer') + 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): From a424719fbde97b15a51bdc427eaa35fd08bc1c1c Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Sat, 7 Mar 2020 14:38:08 -0800 Subject: [PATCH 62/93] Wiki Doors --- .gitignore | 4 ++-- Main.py | 6 ++++-- Utils.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 91b22ab9..ed3e8193 100644 --- a/.gitignore +++ b/.gitignore @@ -19,8 +19,8 @@ RaceRom.py upx/ weights/ -settings.json -working_dirs.json +resources/user/* +!resources/user/.gitkeep *.exe diff --git a/Main.py b/Main.py index 0dd660b6..e8db9b73 100644 --- a/Main.py +++ b/Main.py @@ -19,10 +19,10 @@ from Doors import create_doors from DoorShuffle import link_doors from RoomData import create_rooms from Rules import set_rules -from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive +from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive, dungeon_regions from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression from ItemList import generate_itempool, difficulties, fill_prizes -from Utils import output_path, parse_player_names +from Utils import output_path, parse_player_names, print_wiki_doors_by_room __version__ = '0.0.18.3d' @@ -257,6 +257,8 @@ def main(args, seed=None): logger.info('Done. Enjoy.') logger.info('Total Time: %s', time.perf_counter() - start) +# print_wiki_doors_by_room(dungeon_regions,world,1) + return world def copy_world(world): diff --git a/Utils.py b/Utils.py index c1a97ced..6755279b 100644 --- a/Utils.py +++ b/Utils.py @@ -206,7 +206,7 @@ def read_entrance_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan) print(string) -def print_wiki_doors(d_regions, world, player): +def print_wiki_doors_by_region(d_regions, world, player): for d, region_list in d_regions.items(): tile_map = {} @@ -222,7 +222,7 @@ def print_wiki_doors(d_regions, world, player): if tile not in tile_map: tile_map[tile] = [] tile_map[tile].append(r) - print(d) + print('') print('{| class="wikitable"') print('|-') print('! Room') @@ -244,6 +244,44 @@ def print_wiki_doors(d_regions, world, player): print('| '+'
'.join(strs_to_print)) print('|}') +def print_wiki_doors_by_room(d_regions, world, player): + for d, region_list in d_regions.items(): + tile_map = {} + for region in region_list: + tile = None + r = world.get_region(region, player) + for ext in r.exits: + door = world.check_for_door(ext.name, player) + if door is not None and door.roomIndex != -1: + tile = door.roomIndex + break + if tile is not None: + if tile not in tile_map: + tile_map[tile] = [] + tile_map[tile].append(r) + toprint = "" + toprint += ('') + "\n" + for tile, region_list in tile_map.items(): + for region in region_list: + toprint += ('') + "\n" + toprint += ('{{Infobox dungeon room') + "\n" + toprint += ('| dungeon = {{ROOTPAGENAME}}') + "\n" + toprint += ('| supertile = ' + str(tile)) + "\n" + toprint += ('| tile = x') + "\n" + toprint += ('}}') + "\n" + toprint += ('') + "\n" + toprint += ('== Doors ==') + "\n" + toprint += ('{| class="wikitable"') + "\n" + toprint += ('|-') + "\n" + toprint += ('! Door !! Room Side !! Requirement') + "\n" + for ext in region.exits: + 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: + f.write(toprint) if __name__ == '__main__': pass From dce7dd39360983355d0fea2d693a41a633ccccba Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Sat, 7 Mar 2020 17:11:12 -0800 Subject: [PATCH 63/93] Implement shallow translation --- BaseClasses.py | 55 ++++++++++------ resources/app/meta/lang/en.json | 3 + source/classes/BabelFish.py | 109 ++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 resources/app/meta/lang/en.json create mode 100644 source/classes/BabelFish.py diff --git a/BaseClasses.py b/BaseClasses.py index e90b38da..bda6832d 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -4,6 +4,7 @@ import logging import json from collections import OrderedDict, deque, defaultdict +from source.classes.BabelFish import BabelFish from EntranceShuffle import door_addresses from _vendor.collections_extended import bag from Utils import int16_as_bytes @@ -71,6 +72,7 @@ class World(object): self.key_logic = {} self.pool_adjustment = {} self.key_layout = defaultdict(dict) + self.fish = BabelFish() for player in range(1, players + 1): # If World State is Retro, set to Open and set Retro flag @@ -1732,43 +1734,60 @@ class Spoiler(object): outfile.write('\n\nDoors:\n\n') outfile.write('\n'.join( ['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', - entry['entrance'], + self.world.fish.translate("meta","doors",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', - entry['exit'], + self.world.fish.translate("meta","doors",entry['exit']), '({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] == 'crossed' else '') for entry in self.doors.values()])) if self.doorTypes: + # doorNames: For some reason these come in combined, somehow need to split on the thing to translate + # doorTypes: Small Key, Bombable, Bonkable outfile.write('\n\nDoor Types:\n\n') - outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', entry['doorNames'], entry['type']) for entry in self.doorTypes.values()])) + outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta","doors",entry['doorNames']), self.world.fish.translate("meta","doorTypes",entry['type'])) for entry in self.doorTypes.values()])) if self.entrances: + # entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly outfile.write('\n\nEntrances:\n\n') - outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', entry['entrance'], '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', entry['exit']) for entry in self.entrances.values()])) + outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","entrances",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","entrances",entry['exit'])) for entry in self.entrances.values()])) outfile.write('\n\nMedallions:\n') for dungeon, medallion in self.medallions.items(): - outfile.write(f'\n{dungeon}: {medallion}') + outfile.write(f'\n{dungeon}: {medallion} Medallion') if self.startinventory: outfile.write('\n\nStarting Inventory:\n\n') outfile.write('\n'.join(self.startinventory)) - outfile.write('\n\nLocations:\n\n') - outfile.write('\n'.join(['%s: %s' % (location, item) for grouping in self.locations.values() for (location, item) in grouping.items()])) - outfile.write('\n\nShops:\n\n') - outfile.write('\n'.join("{} [{}]\n {}".format(shop['location'], shop['type'], "\n ".join(item for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops)) - outfile.write('\n\nPlaythrough:\n\n') - outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (location, item) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()])) - if self.unreachables: - outfile.write('\n\nUnreachable Items:\n\n') - outfile.write('\n'.join(['%s: %s' % (unreachable.item, unreachable) for unreachable in self.unreachables])) - outfile.write('\n\nPaths:\n\n') + # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name + # items: Item names + outfile.write('\n\nLocations:\n\n') + outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta","locations",location), self.world.fish.translate("meta","items",item)) for grouping in self.locations.values() for (location, item) in grouping.items()])) + + # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name + # items: Item names + outfile.write('\n\nShops:\n\n') + outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta","locations",shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta","items",item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops)) + + # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name + # items: Item names + outfile.write('\n\nPlaythrough:\n\n') + outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (self.world.fish.translate("meta","locations",location), self.world.fish.translate("meta","items",item)) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()])) + if self.unreachables: + # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name + # items: Item names + outfile.write('\n\nUnreachable Items:\n\n') + outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta","items",unreachable.item), self.world.fish.translate("meta","locations",unreachable)) for unreachable in self.unreachables])) + + # rooms: Change up room names; only if it's got no locations in it + # entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly + # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name + outfile.write('\n\nPaths:\n\n') path_listings = [] for location, path in sorted(self.paths.items()): path_lines = [] for region, exit in path: if exit is not None: - path_lines.append("{} -> {}".format(region, exit)) + path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit))) else: - path_lines.append(region) - path_listings.append("{}\n {}".format(location, "\n => ".join(path_lines))) + path_lines.append(self.world.fish.translate("meta","rooms",region)) + path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines))) outfile.write('\n'.join(path_listings)) diff --git a/resources/app/meta/lang/en.json b/resources/app/meta/lang/en.json new file mode 100644 index 00000000..0db3279e --- /dev/null +++ b/resources/app/meta/lang/en.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/source/classes/BabelFish.py b/source/classes/BabelFish.py new file mode 100644 index 00000000..972d3eac --- /dev/null +++ b/source/classes/BabelFish.py @@ -0,0 +1,109 @@ +import json +import locale +import os + +class BabelFish(): + def __init__(self,subpath=["resources","app","meta"],lang=None): + localization_string = locale.getdefaultlocale()[0] #get set localization + self.locale = localization_string[:2] if lang is None else lang #let caller override localization + self.langs = ["en"] #start with English + if(not self.locale == "en"): #add localization + self.langs.append(self.locale) + + self.lang_defns = {} #collect translations + self.add_translation_file() #start with default translation file + self.add_translation_file(["resources","user","meta"]) #add user translation file + + def add_translation_file(self,subpath=["resources","app","meta"]): + if not isinstance(subpath, list): + subpath = [subpath] + if "lang" not in subpath: + subpath.append("lang") #look in lang folder + subpath = os.path.join(*subpath) #put in path separators + key = subpath.split(os.sep) + for check in ["resources","app","user"]: + if check in key: + key.remove(check) + key = os.path.join(*key) #put in path separators + for lang in self.langs: + if not lang in self.lang_defns: + self.lang_defns[lang] = {} + langs_filename = os.path.join(subpath,lang + ".json") #get filename of translation file + if os.path.isfile(langs_filename): #if we've got a file + with open(langs_filename,encoding="utf-8") as f: #open it + self.lang_defns[lang][key[:key.rfind(os.sep)].replace(os.sep,'.')] = json.load(f) #save translation definitions + else: + print(langs_filename + " not found for translation!") + + def translate(self, domain="", key="", subkey=""): #three levels of keys + # start with nothing + display_text = "" + + # exits check for not exit first and then append Exit at end + # multiRooms check for not chest name first and then append chest name at end + specials = { + "exit": False, + "multiRoom": False + } + + # Domain + if os.sep in domain: + domain = domain.replace(os.sep,'.') +# display_text = domain + + # Operate on Key + if key != "": + if display_text != "": + display_text += '.' +# display_text += key + # Exits + if "exit" in key: + key = key.replace("exit","") + specials["exit"] = True + if "Exit" in key: + key = key.replace("Exit","") + specials["exit"] = True + # Locations + tmp = key.split(" - ") + if len(tmp) >= 2: + specials["multiRoom"] = tmp[len(tmp) - 1] + tmp.pop() + key = " - ".join(tmp) + key = key.strip() + + # Operate on Subkey + if subkey != "": + if display_text != "": + display_text += '.' + display_text += subkey + # Exits + if "exit" in subkey: + subkey = subkey.replace("exit","") + specials["exit"] = True + if "Exit" in subkey: + subkey = subkey.replace("Exit","") + specials["exit"] = True + # Locations + tmp = subkey.split(" - ") + if len(tmp) >= 2: + specials["multiRoom"] = tmp[len(tmp) - 1] + tmp.pop() + subkey = " - ".join(tmp) + subkey = subkey.strip() + + my_lang = self.lang_defns[self.locale] #handle for localization + en_lang = self.lang_defns["en"] #handle for English + + if domain in my_lang and key in my_lang[domain] and subkey in my_lang[domain][key] and not my_lang[domain][key][subkey] == "": #get localization first + display_text = my_lang[domain][key][subkey] + elif domain in en_lang and key in en_lang[domain] and subkey in en_lang[domain][key] and not en_lang[domain][key][subkey] == "": #gracefully degrade to English + display_text = en_lang[domain][key][subkey] + elif specials["exit"]: + specials["exit"] = False + + if specials["exit"]: + display_text += " Exit" + elif specials["multiRoom"] and specials["multiRoom"] not in display_text: + display_text += " - " + specials["multiRoom"] + + return display_text From 04404714d9c305862413097cdab2dacf23d3aeac Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Sun, 8 Mar 2020 15:52:05 -0700 Subject: [PATCH 64/93] Update wiki and ci stuff --- Main.py | 3 +- Utils.py | 29 +++++++------ resources/ci/common/common.py | 10 +++-- resources/ci/common/git_clean.py | 15 ++++--- resources/ci/common/prepare_release.py | 56 ++++++++++++++------------ 5 files changed, 66 insertions(+), 47 deletions(-) diff --git a/Main.py b/Main.py index 315ba5f5..e71dd8d3 100644 --- a/Main.py +++ b/Main.py @@ -22,7 +22,7 @@ from Rules import set_rules from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive, dungeon_regions from Fill import distribute_items_cutoff, distribute_items_staleness, distribute_items_restrictive, flood_items, balance_multiworld_progression from ItemList import generate_itempool, difficulties, fill_prizes -from Utils import output_path, parse_player_names, print_wiki_doors_by_room +from Utils import output_path, parse_player_names, print_wiki_doors_by_region, print_wiki_doors_by_room __version__ = '0.0.18.4d' @@ -258,6 +258,7 @@ def main(args, seed=None): logger.info('Total Time: %s', time.perf_counter() - start) # print_wiki_doors_by_room(dungeon_regions,world,1) +# print_wiki_doors_by_region(dungeon_regions,world,1) return world diff --git a/Utils.py b/Utils.py index 6755279b..f0dba062 100644 --- a/Utils.py +++ b/Utils.py @@ -207,7 +207,6 @@ def read_entrance_data(old_rom='Zelda no Densetsu - Kamigami no Triforce (Japan) def print_wiki_doors_by_region(d_regions, world, player): - for d, region_list in d_regions.items(): tile_map = {} for region in region_list: @@ -222,27 +221,31 @@ def print_wiki_doors_by_region(d_regions, world, player): if tile not in tile_map: tile_map[tile] = [] tile_map[tile].append(r) - print('') - print('{| class="wikitable"') - print('|-') - print('! Room') - print('! Supertile') - print('! Doors') + toprint = "" + toprint += ('') + "\n" + toprint += ('== Room List ==') + "\n" + toprint += "\n" + toprint += ('{| class="wikitable"') + "\n" + toprint += ('|-') + "\n" + toprint += ('! Room !! Supertile !! Doors') + "\n" for tile, region_list in tile_map.items(): tile_done = False for region in region_list: - print('|-') - print('| '+region.name) + toprint += ('|-') + "\n" + toprint += ('| {{Dungeon Room|{{PAGENAME}}|' + region.name + '}}') + "\n" if not tile_done: listlen = len(region_list) link = '| {{UnderworldMapLink|'+str(tile)+'}}' - print(link if listlen < 2 else '| rowspan = '+str(listlen)+' '+link) + toprint += (link if listlen < 2 else '| rowspan = '+str(listlen)+' '+link) + "\n" tile_done = True strs_to_print = [] for ext in region.exits: - strs_to_print.append(ext.name) - print('| '+'
'.join(strs_to_print)) - print('|}') + strs_to_print.append('{{Dungeon Door|{{PAGENAME}}|' + ext.name + '}}') + toprint += ('| '+'
'.join(strs_to_print)) + toprint += "\n" + toprint += ('|}') + "\n" + with open(os.path.join(".","resources", "user", "regions-" + d + ".txt"),"w+") as f: + f.write(toprint) def print_wiki_doors_by_room(d_regions, world, player): for d, region_list in d_regions.items(): diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 6f793e21..8f2a4f51 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -115,6 +115,9 @@ def prepare_filename(BUILD_FILENAME): # find a binary file if it's executable # failing that, assume it's over 10MB def find_binary(listdir): + FILENAME_CHECKS = [ "Gui", "DungeonRandomizer" ] + FILESIZE_CHECK = (10 * 1024 * 1024) # 10MB + BUILD_FILENAMES = [] executable = stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH for filename in os.listdir(listdir): @@ -122,10 +125,11 @@ def find_binary(listdir): if os.path.splitext(filename)[1] != ".py": st = os.stat(filename) mode = st.st_mode - big = st.st_size > (4.7 * 1024 * 1024) # 10MB + big = st.st_size > FILESIZE_CHECK if (mode & executable) or big: - if "GUI" in filename or "Gui" in filename or "DungeonRandomizer" in filename: - BUILD_FILENAMES.append(filename) + for check in FILENAME_CHECKS: + if check in filename: + BUILD_FILENAMES.append(filename) return BUILD_FILENAMES if __name__ == "__main__": diff --git a/resources/ci/common/git_clean.py b/resources/ci/common/git_clean.py index 6a8ca8fc..7c2e2cfa 100644 --- a/resources/ci/common/git_clean.py +++ b/resources/ci/common/git_clean.py @@ -1,15 +1,20 @@ import subprocess # do stuff at the shell level def git_clean(): + excludes = [ + ".vscode", # vscode IDE files + ".idea", # idea IDE files + "*.json", # keep JSON files for that one time I just nuked all that I was working on, oops + "*app*version.*" # keep appversion files + ] + excludes = ['--exclude={0}'.format(exclude) for exclude in excludes] + # clean the git slate - subprocess.check_call([ + subprocess.check_call([ "git", # run a git command "clean", # clean command "-dfx", # d: directories, f: files, x: ignored files - "--exclude=.vscode", # keep vscode IDE files - "--exclude=.idea", # keep idea IDE files - "--exclude=*.json", # keep JSON files for that one time I just nuked all that I was working on, oops - "--exclude=*app*version.*"]) # keep appversion files + *excludes]) if __name__ == "__main__": git_clean() diff --git a/resources/ci/common/prepare_release.py b/resources/ci/common/prepare_release.py index 35c087b3..5e32b410 100644 --- a/resources/ci/common/prepare_release.py +++ b/resources/ci/common/prepare_release.py @@ -8,17 +8,14 @@ from shutil import copy, make_archive, move, rmtree # file manipulation env = common.prepare_env() # get env vars -# make temp dir to put binary in -if not os.path.isdir(os.path.join("..","artifact")): - os.mkdir(os.path.join("..","artifact")) - -# make temp dir for other stuff -if not os.path.isdir(os.path.join("..","build")): - os.mkdir(os.path.join("..","build")) - -# make dir to put the archive in -if not os.path.isdir(os.path.join("..","deploy")): - os.mkdir(os.path.join("..","deploy")) +dirs = [ + os.path.join("..", "artifact"), # temp dir for binary + os.path.join("..", "build"), # temp dir for other stuff + os.path.join("..", "deploy") # dir for archive +] +for dirname in dirs: + if not os.path.isdir(dirname): + os.makedirs(dirname) # make dirs for each os for dirname in ["linux","macos","windows"]: @@ -27,12 +24,16 @@ for dirname in ["linux","macos","windows"]: # sanity check permissions for working_dirs.json dirpath = "." -for dirname in ["resources","user"]: - dirpath += '/' + dirname - os.chmod(dirpath,0o755) +for dirname in ["resources","user","meta","manifests"]: + dirpath += os.path.join(dirpath,dirname) + if os.path.isdir(dirpath): + os.chmod(dirpath,0o755) + # nuke travis file if it exists -if os.path.isfile(os.path.join(".",".travis.yml")): - os.remove(os.path.join(".",".travis.yml")) +for travis in [ os.path.join(".", ".travis.yml"), os.path.join(".", ".travis.off") ]: + if os.path.isfile(travis): + os.remove(travis) + # nuke test suite if it exists if os.path.isdir(os.path.join(".","tests")): distutils.dir_util.remove_tree(os.path.join(".","tests")) @@ -66,12 +67,18 @@ if len(BUILD_FILENAMES) > 0: git_clean() # mv dirs from source code - dirs = [os.path.join(".",".git"), os.path.join(".",".github"), os.path.join(".",".gitignore"), os.path.join(".","html"), os.path.join(".","resources","ci")] - for dir in dirs: - if os.path.isdir(dir): + dirs = [ + os.path.join(".",".git"), + os.path.join(".",".github"), + os.path.join(".",".gitignore"), + os.path.join(".","html"), + os.path.join(".","resources","ci") + ] + for dirname in dirs: + if os.path.isdir(dirname): move( - dir, - os.path.join("..","build",dir) + dirname, + os.path.join("..", "build", dirname) ) for BUILD_FILENAME in BUILD_FILENAMES: @@ -88,8 +95,7 @@ if len(BUILD_FILENAMES) > 0: # .zip if windows # .tar.gz otherwise - ZIP_FILENAME = os.path.join("..","deploy",os.path.splitext(BUILD_FILENAME)[0]) - ZIP_FILENAME = os.path.join("..","deploy","ALttPDoorRandomizer") + ZIP_FILENAME = os.path.join("..","deploy",env["REPO_NAME"]) if len(BUILD_FILENAMES) > 1 else os.path.join("..","deploy",os.path.splitext(BUILD_FILENAME)[0]) if env["OS_NAME"] == "windows": make_archive(ZIP_FILENAME,"zip") ZIP_FILENAME += ".zip" @@ -110,12 +116,12 @@ for BUILD_FILENAME in BUILD_FILENAMES: print("Build Filename: " + BUILD_FILENAME) print("Build Filesize: " + common.file_size(BUILD_FILENAME)) else: - print("No Build to prepare") + print("No Build to prepare: " + BUILD_FILENAME) if not ZIP_FILENAME == "": print("Zip Filename: " + ZIP_FILENAME) print("Zip Filesize: " + common.file_size(ZIP_FILENAME)) else: - print("No Zip to prepare") + print("No Zip to prepare: " + ZIP_FILENAME) print("Git tag: " + env["GITHUB_TAG"]) From 5bb0a0da267d104cad05aac98e29e7bcf3cba310 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Sun, 8 Mar 2020 15:59:24 -0700 Subject: [PATCH 65/93] Check for 6MB --- resources/ci/common/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 8f2a4f51..e8b871a7 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -113,10 +113,10 @@ def prepare_filename(BUILD_FILENAME): return DEST_FILENAME # find a binary file if it's executable -# failing that, assume it's over 10MB +# failing that, assume it's over 6MB def find_binary(listdir): FILENAME_CHECKS = [ "Gui", "DungeonRandomizer" ] - FILESIZE_CHECK = (10 * 1024 * 1024) # 10MB + FILESIZE_CHECK = (6 * 1024 * 1024) # 6MB BUILD_FILENAMES = [] executable = stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH From b25e46b16090c641df765c54b663a2d735083b8a Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 9 Mar 2020 01:11:48 -0700 Subject: [PATCH 66/93] Factor out CLI text --- CLI.py | 431 ++++++++------------------------- resources/app/cli/args.json | 312 ++++++++++++++++++++++++ resources/app/cli/lang/en.json | 224 +++++++++++++++++ source/classes/BabelFish.py | 1 + 4 files changed, 642 insertions(+), 326 deletions(-) create mode 100644 resources/app/cli/args.json create mode 100644 resources/app/cli/lang/en.json diff --git a/CLI.py b/CLI.py index ada43572..5b8d25f9 100644 --- a/CLI.py +++ b/CLI.py @@ -11,6 +11,7 @@ import sys from Main import main import source.classes.constants as CONST +from source.classes.BabelFish import BabelFish class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): @@ -25,271 +26,48 @@ def parse_arguments(argv, no_defaults=False): # get settings settings = get_settings() + fish = BabelFish() + # 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) - 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) - No Glitches: - Minor Glitches: May require Fake Flippers, Bunny Revival - and Dark Room Navigation. - No Logic: Distribute items without regard for - item requirements. - ''') - parser.add_argument('--mode', default=defval(settings["mode"]), const='open', nargs='?', choices=['standard', 'open', 'inverted'], - help='''\ - Select game mode. (default: %(default)s) - Open: World starts with Zelda rescued. - Standard: Fixes Hyrule Castle Secret Entrance and Front Door - but may lead to weird rain state issues if you exit - through the Hyrule Castle side exits before rescuing - Zelda in a full shuffle. - Inverted: Starting locations are Dark Sanctuary in West Dark - World or at Link's House, which is shuffled freely. - Requires the moon pearl to be Link in the Light World - instead of a bunny. - ''') - parser.add_argument('--swords', default=defval(settings["swords"]), const='random', nargs='?', choices= ['random', 'assured', 'swordless', 'vanilla'], - help='''\ - Select sword placement. (default: %(default)s) - Random: All swords placed randomly. - Assured: Start game with a sword already. - Swordless: No swords. Curtains in Skull Woods and Agahnim\'s - Tower are removed, Agahnim\'s Tower barrier can be - destroyed with hammer. Misery Mire and Turtle Rock - can be opened without a sword. Hammer damages Ganon. - Ether and Bombos Tablet can be activated with Hammer - (and Book). Bombos pads have been added in Ice - Palace, to allow for an alternative to firerod. - Vanilla: Swords are in vanilla locations. - ''') - parser.add_argument('--goal', default=defval(settings["goal"]), const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], - help='''\ - Select completion goal. (default: %(default)s) - Ganon: Collect all crystals, beat Agahnim 2 then - defeat Ganon. - Crystals: Collect all crystals then defeat Ganon. - Pedestal: Places the Triforce at the Master Sword Pedestal. - All Dungeons: Collect all crystals, pendants, beat both - Agahnim fights and then defeat Ganon. - Triforce Hunt: Places 30 Triforce Pieces in the world, collect - 20 of them to beat the game. - ''') - parser.add_argument('--difficulty', default=defval(settings["difficulty"]), const='normal', nargs='?', choices=['normal', 'hard', 'expert'], - help='''\ - Select game difficulty. Affects available itempool. (default: %(default)s) - Normal: Normal difficulty. - Hard: A harder setting with less equipment and reduced health. - Expert: A harder yet setting with minimum equipment and health. - ''') - parser.add_argument('--item_functionality', default=defval(settings["item_functionality"]), const='normal', nargs='?', choices=['normal', 'hard', 'expert'], - help='''\ - Select limits on item functionality to increase difficulty. (default: %(default)s) - Normal: Normal functionality. - Hard: Reduced functionality. - Expert: Greatly reduced functionality. - ''') - parser.add_argument('--timer', default=defval(settings["timer"]), const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'], - help='''\ - Select game timer setting. Affects available itempool. (default: %(default)s) - None: No timer. - Display: Displays a timer but does not affect - the itempool. - Timed: Starts with clock at zero. Green Clocks - subtract 4 minutes (Total: 20), Blue Clocks - subtract 2 minutes (Total: 10), Red Clocks add - 2 minutes (Total: 10). Winner is player with - lowest time at the end. - Timed OHKO: Starts clock at 10 minutes. Green Clocks add - 5 minutes (Total: 25). As long as clock is at 0, - Link will die in one hit. - OHKO: Like Timed OHKO, but no clock items are present - and the clock is permenantly at zero. - Timed Countdown: Starts with clock at 40 minutes. Same clocks as - Timed mode. If time runs out, you lose (but can - still keep playing). - ''') - parser.add_argument('--progressive', default=defval(settings["progressive"]), const='normal', nargs='?', choices=['on', 'off', 'random'], - help='''\ - Select progressive equipment setting. Affects available itempool. (default: %(default)s) - On: Swords, Shields, Armor, and Gloves will - all be progressive equipment. Each subsequent - item of the same type the player finds will - upgrade that piece of equipment by one stage. - Off: Swords, Shields, Armor, and Gloves will not - be progressive equipment. Higher level items may - be found at any time. Downgrades are not possible. - Random: Swords, Shields, Armor, and Gloves will, per - category, be randomly progressive or not. - Link will die in one hit. - ''') - parser.add_argument('--algorithm', default=defval(settings["algorithm"]), const='balanced', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'], - help='''\ - Select item filling algorithm. (default: %(default)s - balanced: vt26 derivative that aims to strike a balance between - the overworld heavy vt25 and the dungeon heavy vt26 - algorithm. - vt26: Shuffle items and place them in a random location - that it is not impossible to be in. This includes - dungeon keys and items. - vt25: Shuffle items and place them in a random location - that it is not impossible to be in. - vt21: Unbiased in its selection, but has tendency to put - Ice Rod in Turtle Rock. - vt22: Drops off stale locations after 1/3 of progress - items were placed to try to circumvent vt21\'s - shortcomings. - Freshness: Keep track of stale locations (ones that cannot be - reached yet) and decrease likeliness of selecting - them the more often they were found unreachable. - Flood: Push out items starting from Link\'s House and - slightly biased to placing progression items with - less restrictions. - ''') - parser.add_argument('--shuffle', default=defval(settings["shuffle"]), const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple'], - help='''\ - Select Entrance Shuffling Algorithm. (default: %(default)s) - Full: Mix cave and dungeon entrances freely while limiting - multi-entrance caves to one world. - Simple: Shuffle Dungeon Entrances/Exits between each other - and keep all 4-entrance dungeons confined to one - location. All caves outside of death mountain are - shuffled in pairs and matched by original type. - Restricted: Use Dungeons shuffling from Simple but freely - connect remaining entrances. - Crossed: Mix cave and dungeon entrances freely while allowing - caves to cross between worlds. - Insanity: Decouple entrances and exits from each other and - shuffle them freely. Caves that used to be single - entrance will still exit to the same location from - which they are entered. - Vanilla: All entrances are in the same locations they were - in the base game. - Legacy shuffles preserve behavior from older versions of the - entrance randomizer including significant technical limitations. - The dungeon variants only mix up dungeons and keep the rest of - the overworld vanilla. - ''') - parser.add_argument('--door_shuffle', default=defval(settings["door_shuffle"]), const='vanilla', nargs='?', choices=['vanilla', 'basic', 'crossed'], - help='''\ - Select Door Shuffling Algorithm. (default: %(default)s) - Basic: Doors are mixed within a single dungeon. - (Not yet implemented) - Crossed: Doors are mixed between all dungeons. - (Not yet implemented) - Vanilla: All doors are connected the same way they were in the - 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', 'default']) - parser.add_argument('--crystals_ganon', default=defval(settings["crystals_ganon"]), const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'], - help='''\ - How many crystals are needed to defeat ganon. Any other - requirements for ganon for the selected goal still apply. - This setting does not apply when the all dungeons goal is - selected. (default: %(default)s) - Random: Picks a random value between 0 and 7 (inclusive). - 0-7: Number of crystals needed - ''') - parser.add_argument('--crystals_gt', default=defval(settings["crystals_gt"]), const='7', nargs='?', choices=['random', '0', '1', '2', '3', '4', '5', '6', '7'], - help='''\ - How many crystals are needed to open GT. For inverted mode - this applies to the castle tower door instead. (default: %(default)s) - Random: Picks a random value between 0 and 7 (inclusive). - 0-7: Number of crystals needed - ''') - parser.add_argument('--openpyramid', default=defval(settings["openpyramid"] != 0), help='''\ - Pre-opens the pyramid hole, this removes the Agahnim 2 requirement for it - ''', action='store_true') - parser.add_argument('--rom', default=defval(settings["rom"]), help='Path to an ALttP JAP(1.0) rom to use as a base.') - parser.add_argument('--loglevel', default=defval('info'), const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.') - parser.add_argument('--seed', default=defval(int(settings["seed"]) if settings["seed"] != "" and settings["seed"] is not None else None), help='Define seed number to generate.', type=int) - parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else None), help='''\ - Use to batch generate multiple seeds with same settings. - If --seed is provided, it will be used for the first seed, then - used to derive the next seed (i.e. generating 10 seeds with - --seed given will produce the same 10 (different) roms each - time). - ''', type=int) - parser.add_argument('--fastmenu', default=defval(settings["fastmenu"]), const='normal', nargs='?', choices=['normal', 'instant', 'double', 'triple', 'quadruple', 'half'], - help='''\ - Select the rate at which the menu opens and closes. - (default: %(default)s) - ''') - parser.add_argument('--quickswap', default=defval(settings["quickswap"] != 0), help='Enable quick item swapping with L and R.', action='store_true') - parser.add_argument('--disablemusic', default=defval(settings["disablemusic"] != 0), help='Disables game music.', action='store_true') - parser.add_argument('--mapshuffle', default=defval(settings["mapshuffle"] != 0), help='Maps are no longer restricted to their dungeons, but can be anywhere', action='store_true') - parser.add_argument('--compassshuffle', default=defval(settings["compassshuffle"] != 0), help='Compasses are no longer restricted to their dungeons, but can be anywhere', action='store_true') - parser.add_argument('--keyshuffle', default=defval(settings["keyshuffle"] != 0), help='Small Keys are no longer restricted to their dungeons, but can be anywhere', action='store_true') - parser.add_argument('--bigkeyshuffle', default=defval(settings["bigkeyshuffle"] != 0), help='Big Keys are no longer restricted to their dungeons, but can be anywhere', action='store_true') - parser.add_argument('--keysanity', default=defval(settings["keysanity"] != 0), help=argparse.SUPPRESS, action='store_true') - parser.add_argument('--retro', default=defval(settings["retro"] != 0), help='''\ - Keys are universal, shooting arrows costs rupees, - 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={}, 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 - reaching all locations or all keys. - Locations: You will be able to reach every location in the game. - None: You will be able to reach enough locations to beat the game. - ''') - parser.add_argument('--hints', default=defval(settings["hints"] != 0), help='''\ - Make telepathic tiles and storytellers give helpful hints. - ''', action='store_true') + + # 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('--shuffleganon', help=argparse.SUPPRESS, action='store_true', default=defval(settings["shuffleganon"] != 0)) - parser.add_argument('--no-shuffleganon', help='''\ - If set, the Pyramid Hole and Ganon's Tower are not - included entrance shuffle pool. - ''', action='store_false', dest='shuffleganon') - parser.add_argument('--heartbeep', default=defval(settings["heartbeep"]), const='normal', nargs='?', choices=['double', 'normal', 'half', 'quarter', 'off'], - help='''\ - Select the rate at which the heart beep sound is played at - low health. (default: %(default)s) - ''') - parser.add_argument('--heartcolor', default=defval(settings["heartcolor"]), const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow', 'random'], - help='Select the color of Link\'s heart meter. (default: %(default)s)') - parser.add_argument('--ow_palettes', default=defval(settings["ow_palettes"]), choices=['default', 'random', 'blackout']) - parser.add_argument('--uw_palettes', default=defval(settings["uw_palettes"]), choices=['default', 'random', 'blackout']) - parser.add_argument('--sprite', default=defval(settings["sprite"]), help='''\ - Path to a sprite sheet to use for Link. Needs to be in - binary format and have a length of 0x7000 (28672) bytes, - or 0x7078 (28792) bytes including palette data. - Alternatively, can be a ALttP Rom patched with a Link - sprite that will be extracted. - ''') - 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 - for VT site integration, do not use otherwise. - ''') - parser.add_argument('--skip_playthrough', action='store_true', default=defval(settings["skip_playthrough"] != 0)) - parser.add_argument('--enemizercli', default=defval(settings["enemizercli"])) - parser.add_argument('--shufflebosses', default=defval(settings["shufflebosses"]), choices=['none', 'basic', 'normal', 'chaos']) - parser.add_argument('--shuffleenemies', default=defval(settings["shuffleenemies"]), choices=['none', 'shuffled', 'chaos']) - parser.add_argument('--enemy_health', default=defval(settings["enemy_health"]), choices=['default', 'easy', 'normal', 'hard', 'expert']) - parser.add_argument('--enemy_damage', default=defval(settings["enemy_damage"]), choices=['default', 'shuffled', 'chaos']) - parser.add_argument('--shufflepots', default=defval(settings["shufflepots"] != 0), action='store_true') parser.add_argument('--beemizer', default=defval(settings["beemizer"]), type=lambda value: min(max(int(value), 0), 4)) - parser.add_argument('--remote_items', default=defval(settings["remote_items"] != 0), action='store_true') parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255)) - parser.add_argument('--names', default=defval(settings["names"])) parser.add_argument('--teams', default=defval(1), type=lambda value: max(int(value), 1)) - parser.add_argument('--outputpath', default=defval(settings["outputpath"])) - parser.add_argument('--race', default=defval(settings["race"] != 0), action='store_true') - parser.add_argument('--saveonexit', default=defval(settings["saveonexit"]), choices=['never', 'ask', 'always']) - parser.add_argument('--outputname') if multiargs.multi: for player in range(1, multiargs.multi + 1): @@ -386,80 +164,81 @@ def get_settings(): "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 - }, + "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": {} } diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json new file mode 100644 index 00000000..aaaff7ae --- /dev/null +++ b/resources/app/cli/args.json @@ -0,0 +1,312 @@ +{ + "create_spoiler": { + "action": "store_true", + "type": "bool" + }, + "logic": { + "choices": [ + "noglitches", + "minorglitches", + "nologic" + ] + }, + "mode": { + "choices": [ + "open", + "standard", + "inverted", + "retro" + ] + }, + "swords": { + "choices": [ + "random", + "assured", + "swordless", + "vanilla" + ] + }, + "goal": { + "choices": [ + "ganon", + "pedestal", + "dungeons", + "triforcehunt", + "crystals" + ] + }, + "difficulty": { + "choices": [ + "normal", + "hard", + "expert" + ] + }, + "item_functionality": { + "choices": [ + "normal", + "hard", + "expert" + ] + }, + "timer": { + "choices": [ + "none", + "display", + "timed", + "timed-ohko", + "ohko", + "timed-countdown" + ] + }, + "progressive": { + "choices": [ + "on", + "off", + "random" + ] + }, + "algorithm": { + "choices": [ + "balanced", + "freshness", + "flood", + "vt21", + "vt22", + "vt25", + "vt26" + ] + }, + "shuffle": { + "choices": [ + "full", + "vanilla", + "simple", + "restricted", + "crossed", + "insanity", + "restricted_legacy", + "full_legacy", + "madness_legacy", + "insanity_legacy", + "dungeonsfull", + "dungeonssimple" + ] + }, + "door_shuffle": { + "choices": [ + "basic", + "crossed", + "vanilla" + ] + }, + "experimental": { + "action": "store_true", + "type": "bool" + }, + "dungeon_counters": { + "choices": [ + "off", + "on", + "pickup", + "default" + ] + }, + "crystals_ganon": { + "choices": [ + 7, 6, 5, 4, 3, 2, 1, 0, "random" + ] + }, + "crystals_gt": { + "choices": [ + 7, 6, 5, 4, 3, 2, 1, 0, "random" + ] + }, + "openpyramid": { + "action": "store_true", + "type": "bool" + }, + "rom": {}, + "loglevel": { + "choices": [ + "info", + "error", + "warning", + "debug" + ] + }, + "fastmenu": { + "choices": [ + "normal", + "instant", + "double", + "triple", + "quadruple", + "half" + ] + }, + "quickswap": { + "action": "store_true", + "type": "bool" + }, + "disablemusic": { + "action": "store_true", + "type": "bool" + }, + "mapshuffle": { + "action": "store_true", + "type": "bool" + }, + "compassshuffle": { + "action": "store_true", + "type": "bool" + }, + "keyshuffle": { + "action": "store_true", + "type": "bool" + }, + "bigkeyshuffle": { + "action": "store_true", + "type": "bool" + }, + "keysanity": { + "action": "store_true", + "type": "bool", + "help": "suppress" + }, + "retro": { + "action": "store_true", + "type": "bool" + }, + "startinventory": {}, + "usestartinventory": { + "type": "bool" + }, + "custom": { + "type": "bool", + "help": "suppress" + }, + "accessibility": { + "choices": [ + "items", + "locations", + "none" + ] + }, + "hints": { + "action": "store_true", + "type": "bool" + }, + "shuffleganon": { + "action": "store_true", + "type": "bool", + "help": "suppress" + }, + "no-shuffleganon": { + "action": "store_false", + "dest": "shuffleganon" + }, + "heartbeep": { + "choices": [ + "normal", + "double", + "half", + "quarter", + "off" + ] + }, + "heartcolor": { + "choices": [ + "red", + "blue", + "green", + "yellow", + "random" + ] + }, + "ow_palettes": { + "choices": [ + "default", + "random", + "blackout" + ] + }, + "uw_palettes": { + "choices": [ + "default", + "random", + "blackout" + ] + }, + "sprite": {}, + "suppress_rom": { + "action": "store_true", + "type": "bool" + }, + "gui": { + "action": "store_true" + }, + "jsonout": { + "action": "store_true" + }, + "skip_playthrough": { + "action": "store_true", + "type": "bool" + }, + "enemizercli": { + "setting": "enemizercli" + }, + "shufflebosses": { + "choices": [ + "none", + "basic", + "normal", + "chaos" + ] + }, + "shuffleenemies": { + "choices": [ + "none", + "shuffled", + "chaos" + ] + }, + "enemy_health": { + "choices": [ + "default", + "easy", + "normal", + "hard", + "expert" + ] + }, + "enemy_damage": { + "choices": [ + "default", + "shuffled", + "chaos" + ] + }, + "shufflepots": { + "action": "store_true", + "type": "bool" + }, + "remote_items": { + "action": "store_true", + "type": "bool" + }, + "names": {}, + "outputpath": {}, + "race": { + "action": "store_true", + "type": "bool" + }, + "saveonexit": { + "choices": [ + "never", + "ask", + "always" + ] + }, + "outputname": {} +} diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json new file mode 100644 index 00000000..2b1df5c9 --- /dev/null +++ b/resources/app/cli/lang/en.json @@ -0,0 +1,224 @@ +{ + "help": { + "create_spoiler": [ "Output a Spoiler File" ], + "logic": [ + "Select Enforcement of Item Requirements. (default: %(default)s)", + "No Glitches: No Glitch knowledge required.", + "Minor Glitches: May require Fake Flippers, Bunny Revival", + " and Dark Room Navigation.", + "No Logic: Distribute items without regard for", + " item requirements." + ], + "mode": [ + "Select game mode. (default: %(default)s)", + "Open: World starts with Zelda rescued.", + "Standard: Fixes Hyrule Castle Secret Entrance and Front Door", + " but may lead to weird rain state issues if you exit", + " through the Hyrule Castle side exits before rescuing", + " Zelda in a full shuffle.", + "Inverted: Starting locations are Dark Sanctuary in West Dark", + " World or at Link's House, which is shuffled freely.", + " Requires the moon pearl to be Link in the Light World", + " instead of a bunny.", + "Retro: Keys are universal, shooting arrows costs rupees,", + " and a few other little things make this more like Zelda-1." + ], + "swords": [ + "Select sword placement. (default: %(default)s)", + "Random: All swords placed randomly.", + "Assured: Start game with a sword already.", + "Swordless: No swords. Curtains in Skull Woods and Agahnim\\'s", + " Tower are removed, Agahnim\\'s Tower barrier can be", + " destroyed with hammer. Misery Mire and Turtle Rock", + " can be opened without a sword. Hammer damages Ganon.", + " Ether and Bombos Tablet can be activated with Hammer", + " (and Book). Bombos pads have been added in Ice", + " Palace, to allow for an alternative to firerod.", + "Vanilla: Swords are in vanilla locations." + ], + "goal": [ + "Select completion goal. (default: %(default)s)", + "Ganon: Collect all crystals, beat Agahnim 2 then", + " defeat Ganon.", + "Crystals: Collect all crystals then defeat Ganon.", + "Pedestal: Places the Triforce at the Master Sword Pedestal.", + "All Dungeons: Collect all crystals, pendants, beat both", + " Agahnim fights and then defeat Ganon.", + "Triforce Hunt: Places 30 Triforce Pieces in the world, collect", + " 20 of them to beat the game." + ], + "difficulty": [ + "Select game difficulty. Affects available itempool. (default: %(default)s)", + "Normal: Normal difficulty.", + "Hard: A harder setting with less equipment and reduced health.", + "Expert: A harder yet setting with minimum equipment and health." + ], + "item_functionality": [ + "Select limits on item functionality to increase difficulty. (default: %(default)s)", + "Normal: Normal functionality.", + "Hard: Reduced functionality.", + "Expert: Greatly reduced functionality." + ], + "timer": [ + "Select game timer setting. Affects available itempool. (default: %(default)s)", + "None: No timer.", + "Display: Displays a timer but does not affect", + " the itempool.", + "Timed: Starts with clock at zero. Green Clocks", + " subtract 4 minutes (Total: 20), Blue Clocks", + " subtract 2 minutes (Total: 10), Red Clocks add", + " 2 minutes (Total: 10). Winner is player with", + " lowest time at the end.", + "Timed OHKO: Starts clock at 10 minutes. Green Clocks add", + " 5 minutes (Total: 25). As long as clock is at 0,", + " Link will die in one hit.", + "OHKO: Like Timed OHKO, but no clock items are present", + " and the clock is permenantly at zero.", + "Timed Countdown:Starts with clock at 40 minutes. Same clocks as", + " Timed mode. If time runs out, you lose (but can", + " still keep playing)." + ], + "progressive": [ + "Select progressive equipment setting. Affects available itempool. (default: %(default)s)", + "On: Swords, Shields, Armor, and Gloves will", + " all be progressive equipment. Each subsequent", + " item of the same type the player finds will", + " upgrade that piece of equipment by one stage.", + "Off: Swords, Shields, Armor, and Gloves will not", + " be progressive equipment. Higher level items may", + " be found at any time. Downgrades are not possible.", + "Random: Swords, Shields, Armor, and Gloves will, per", + " category, be randomly progressive or not.", + " Link will die in one hit." + ], + "algorithm": [ + "Select item filling algorithm. (default: %(default)s)", + "balanced: vt26 derivative that aims to strike a balance between", + " the overworld heavy vt25 and the dungeon heavy vt26", + " algorithm.", + "vt26: Shuffle items and place them in a random location", + " that it is not impossible to be in. This includes", + " dungeon keys and items.", + "vt25: Shuffle items and place them in a random location", + " that it is not impossible to be in.", + "vt21: Unbiased in its selection, but has tendency to put", + " Ice Rod in Turtle Rock.", + "vt22: Drops off stale locations after 1/3 of progress", + " items were placed to try to circumvent vt21\\'s", + " shortcomings.", + "Freshness: Keep track of stale locations (ones that cannot be", + " reached yet) and decrease likeliness of selecting", + " them the more often they were found unreachable.", + "Flood: Push out items starting from Link\\'s House and", + " slightly biased to placing progression items with", + " less restrictions." + ], + "shuffle": [ + "Select Entrance Shuffling Algorithm. (default: %(default)s)", + "Full: Mix cave and dungeon entrances freely while limiting", + " multi-entrance caves to one world.", + "Simple: Shuffle Dungeon Entrances/Exits between each other", + " and keep all 4-entrance dungeons confined to one", + " location. All caves outside of death mountain are", + " shuffled in pairs and matched by original type.", + "Restricted: Use Dungeons shuffling from Simple but freely", + " connect remaining entrances.", + "Crossed: Mix cave and dungeon entrances freely while allowing", + " caves to cross between worlds.", + "Insanity: Decouple entrances and exits from each other and", + " shuffle them freely. Caves that used to be single", + " entrance will still exit to the same location from", + " which they are entered.", + "Vanilla: All entrances are in the same locations they were", + " in the base game.", + "Legacy shuffles preserve behavior from older versions of the", + "entrance randomizer including significant technical limitations.", + "The dungeon variants only mix up dungeons and keep the rest of", + "the overworld vanilla." + ], + "door_shuffle": [ + "Select Door Shuffling Algorithm. (default: %(default)s)", + "Basic: Doors are mixed within a single dungeon.", + "Crossed: Doors are mixed between all dungeons.", + "Vanilla: All doors are connected the same way they were in the", + " base game." + ], + "experimental": [ "Enable experimental features. (default: %(default)s)" ], + "dungeon_counters": [ "Enable dungeon chest counters. (default: %(default)s)" ], + "crystals_ganon": [ + "How many crystals are needed to defeat ganon. Any other", + "requirements for ganon for the selected goal still apply.", + "This setting does not apply when the all dungeons goal is", + "selected. (default: %(default)s)", + "Random: Picks a random value between 0 and 7 (inclusive).", + "0-7: Number of crystals needed" + ], + "crystals_gt": [ + "How many crystals are needed to open GT. For inverted mode", + "this applies to the castle tower door instead. (default: %(default)s)", + "Random: Picks a random value between 0 and 7 (inclusive).", + "0-7: Number of crystals needed" + ], + "openpyramid": [ "Pre-opens the pyramid hole, this removes the Agahnim 2 requirement for it. (default: %(default)s)" ], + "rom": [ + "Path to an ALttP JP (1.0) rom to use as a base." , + "(default: %(default)s)" + ], + "loglevel": [ "Select level of logging for output. (default: %(default)s)" ], + "seed": [ "Define seed number to generate." ], + "count": [ + "Use to batch generate multiple seeds with same settings.", + "If --seed is provided, it will be used for the first seed, then", + "used to derive the next seed (i.e. generating %(default)s seed(s) with", + "--seed given will produce the same %(default)s (different) rom(s) each", + "time)." + ], + "fastmenu": [ + "Select the rate at which the menu opens and closes. (default: %(default)s)" + ], + "quickswap": [ "Enable quick item swapping with L and R. (default: %(default)s)" ], + "disablemusic": [ "Disables game music including MSU-1. (default: %(default)s)" ], + "mapshuffle": [ "Maps are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], + "compassshuffle": [ "Compasses are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], + "keyshuffle": [ "Small Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], + "bigkeyshuffle": [ "Big Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], + "retro": [ + "Keys are universal, shooting arrows costs rupees,", + "and a few other little things make this more like Zelda-1. (default: %(default)s)" + ], + "startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ], + "usestartinventory": [ "Toggle usage of Starting Inventory." ], + "custom": [ "Not supported." ], + "customitemarray": [ "Not supported." ], + "accessibility": [ + "Select Item/Location Accessibility. (default: %(default)s)", + "Items: You can reach all unique inventory items. No guarantees about", + " reaching all locations or all keys.", + "Locations: You will be able to reach every location in the game.", + "None: You will be able to reach enough locations to beat the game." + ], + "hints": [ "Make telepathic tiles and storytellers give helpful hints. (default: %(default)s)" ], + "no-shuffleganon": [ + "If set, the Pyramid Hole and Ganon's Tower are not", + "included entrance shuffle pool. (default: %(default)s)" + ], + "heartbeep": [ + "Select the rate at which the heart beep sound is played at", + "low health. (default: %(default)s)" + ], + "heartcolor": [ "Select the color of Link\\'s heart meter. (default: %(default)s)" ], + "sprite": [ + "Path to a sprite sheet to use for Link. Needs to be in", + "binary format and have a length of 0x7000 (28672) bytes,", + "or 0x7078 (28792) bytes including palette data.", + "Alternatively, can be a ALttP Rom patched with a Link", + "sprite that will be extracted." + ], + "suppress_rom": [ "Do not create an output rom file. (default: %(default)s)" ], + "gui": [ "Launch the GUI. (default: %(default)s)" ], + "jsonout": [ + "Output .json patch to stdout instead of a patched rom. Used", + "for VT site integration, do not use otherwise. (default: %(default)s)" + ] + } +} diff --git a/source/classes/BabelFish.py b/source/classes/BabelFish.py index 972d3eac..d2b52765 100644 --- a/source/classes/BabelFish.py +++ b/source/classes/BabelFish.py @@ -12,6 +12,7 @@ class BabelFish(): self.lang_defns = {} #collect translations self.add_translation_file() #start with default translation file + self.add_translation_file(["resources","app","cli"]) #add help translation file self.add_translation_file(["resources","user","meta"]) #add user translation file def add_translation_file(self,subpath=["resources","app","meta"]): From ca7fc4d0f6b3fb4aed3d6ad06cefd7b63b31e426 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 9 Mar 2020 02:25:25 -0700 Subject: [PATCH 67/93] Hack in Spanish --- CLI.py | 3 +- DoorShuffle.py | 31 ++++++++++++---- DungeonGenerator.py | 38 ++++++++++++++++--- DungeonRandomizer.py | 13 +++++-- Main.py | 68 ++++++++++++++++++++++++---------- resources/app/cli/args.json | 1 + resources/app/cli/lang/en.json | 41 ++++++++++++++++++++ resources/app/cli/lang/es.json | 25 +++++++++++++ source/classes/BabelFish.py | 3 +- 9 files changed, 186 insertions(+), 37 deletions(-) create mode 100644 resources/app/cli/lang/es.json diff --git a/CLI.py b/CLI.py index 5b8d25f9..96d3be21 100644 --- a/CLI.py +++ b/CLI.py @@ -26,7 +26,7 @@ def parse_arguments(argv, no_defaults=False): # get settings settings = get_settings() - fish = BabelFish() + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") # we need to know how many players we have first parser = argparse.ArgumentParser(add_help=False) @@ -102,6 +102,7 @@ def parse_arguments(argv, no_defaults=False): def get_settings(): # set default settings settings = { + "lang": "en", "retro": False, "mode": "open", "logic": "noglitches", diff --git a/DoorShuffle.py b/DoorShuffle.py index 8bd93efc..7b084fa4 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -5,6 +5,9 @@ import operator as op import time from enum import unique, Flag +from source.classes.BabelFish import BabelFish +import CLI as cli + from functools import reduce from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier from Regions import key_only_locations @@ -305,6 +308,9 @@ def pair_existing_key_doors(world, player, door_a, door_b): # paired_door.pair = False def within_dungeon(world, player): + settings = cli.get_settings() + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + fix_big_key_doors_with_ugly_smalls(world, player) overworld_prep(world, player) entrances_map, potentials, connections = determine_entrance_list(world, player) @@ -326,7 +332,7 @@ def within_dungeon(world, player): start = time.process_time() for builder in world.dungeon_layouts[player].values(): shuffle_key_doors(builder, world, player) - logging.getLogger('').info('Key door shuffle time: %s', time.process_time()-start) + logging.getLogger('').info('%s: %s', fish.translate("cli","cli","keydoor.shuffle.time"), time.process_time()-start) smooth_door_pairs(world, player) @@ -347,6 +353,11 @@ def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map) def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player): + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + entrances_map, potentials, connections = connections_tuple enabled_entrances = {} sector_queue = deque(dungeon_builders.values()) @@ -366,7 +377,7 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_ sector_queue.append(builder) last_key = builder.name else: - logging.getLogger('').info('Generating dungeon: %s', builder.name) + logging.getLogger('').info('%s: %s', fish.translate("cli","cli","generating.dungeon"), builder.name) ds = generate_dungeon(builder, origin_list_sans_drops, split_dungeon, world, player) find_new_entrances(ds, connections, potentials, enabled_entrances, world, player) ds.name = name @@ -501,7 +512,7 @@ def shuffle_dungeon(world, player, start_region_names, dungeon_region_names): for door in get_doors(world, world.get_region(name, player), player): ugly_regions[door.name] = 0 available_doors.append(door) - + # Loop until all available doors are used while len(available_doors) > 0: # Pick a random available door to connect, prioritizing ones that aren't blocked. @@ -713,6 +724,9 @@ def cross_dungeon(world, player): def assign_cross_keys(dungeon_builders, world, player): + settings = cli.get_settings() + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + start = time.process_time() total_keys = remaining = 29 total_candidates = 0 @@ -792,7 +806,7 @@ def assign_cross_keys(dungeon_builders, world, player): dungeon.small_keys = [] else: dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys - logging.getLogger('').info('Cross Dungeon: Key door shuffle time: %s', time.process_time()-start) + logging.getLogger('').info('%s: %s', fish.translate("cli","cli","keydoor.shuffle.time.crossed"), time.process_time()-start) def reassign_boss(boss_region, boss_key, builder, gt, world, player): @@ -948,15 +962,18 @@ def calc_used_dungeon_items(builder): def find_valid_combination(builder, start_regions, world, player, drop_keys=True): + settings = cli.get_settings() + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + logger = logging.getLogger('') - logger.info('Shuffling Key doors for %s', builder.name) + logger.info('%s %s', fish.translate("cli","cli","shuffling.keydoors"), builder.name) # find valid combination of candidates if len(builder.candidates) < builder.key_doors_num: if not drop_keys: logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num) return False builder.key_doors_num = len(builder.candidates) # reduce number of key doors - logger.info('Lowering key door count because not enough candidates: %s', builder.name) + logger.info('%s: %s', fish.translate("cli","cli","lowering.keys.candidates"), builder.name) combinations = ncr(len(builder.candidates), builder.key_doors_num) itr = 0 start = time.process_time() @@ -976,7 +993,7 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True if not drop_keys: logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num) return False - logger.info('Lowering key door count because no valid layouts: %s', builder.name) + logger.info('%s: %s', fish.translate("cli","cli","lowering.keys.layouts"), builder.name) builder.key_doors_num -= 1 if builder.key_doors_num < 0: raise Exception('Bad dungeon %s - 0 key doors not valid' % builder.name) diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 5dd74be4..25f15e36 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -8,6 +8,9 @@ from functools import reduce import operator as op from typing import List +from source.classes.BabelFish import BabelFish +import CLI as cli + from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, Sector, PolSlot, flooded_keys from Regions import key_only_locations, dungeon_events, flooded_keys_reverse from Dungeons import dungeon_regions @@ -1094,6 +1097,11 @@ def simple_dungeon_builder(name, sector_list): def create_dungeon_builders(all_sectors, world, player, dungeon_entrances=None): + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + logger = logging.getLogger('') logger.info('Shuffling Dungeon Sectors') if dungeon_entrances is None: @@ -1149,7 +1157,7 @@ def create_dungeon_builders(all_sectors, world, player, dungeon_entrances=None): # polarity: if not global_pole.is_valid(dungeon_map): raise NeutralizingException('Either free location/crystal assignment is already globally invalid - lazy dev check this earlier!') - logger.info('-Balancing Doors') + logger.info(fish.translate("cli","cli","balance.doors")) assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger) # the rest assign_the_rest(dungeon_map, neutral_sectors, global_pole) @@ -1435,8 +1443,13 @@ def sum_polarity(sector_list): def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger): + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + # step 1: fix polarity connection issues - logger.info('--Basic Traversal') + logger.info(fish.translate("cli","cli","basic.traversal")) unconnected_builders = identify_polarity_issues(dungeon_map) while len(unconnected_builders) > 0: for name, builder in unconnected_builders.items(): @@ -1524,10 +1537,15 @@ def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger def polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger): + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + builder_order = list(dungeon_map.values()) random.shuffle(builder_order) for builder in builder_order: - logger.info('--Balancing %s', builder.name) + logger.info('%s %s', fish.translate("cli","cli","balancing"), builder.name) while not builder.polarity().is_neutral(): candidates = find_neutralizing_candidates(builder, polarized_sectors) valid, sectors = False, None @@ -1832,8 +1850,13 @@ def assign_the_rest(dungeon_map, neutral_sectors, global_pole): def split_dungeon_builder(builder, split_list): + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + logger = logging.getLogger('') - logger.info('Splitting Up Desert/Skull') + logger.info(fish.translate("cli","cli","splitting.up") + ' ' + 'Desert/Skull') candidate_sectors = dict.fromkeys(builder.sectors) global_pole = GlobalPolarity(candidate_sectors) @@ -1848,6 +1871,11 @@ def split_dungeon_builder(builder, split_list): def balance_split(candidate_sectors, dungeon_map, global_pole): + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + logger = logging.getLogger('') # categorize sectors crystal_switches, crystal_barriers, neutral_sectors, polarized_sectors = categorize_sectors(candidate_sectors) @@ -1860,7 +1888,7 @@ def balance_split(candidate_sectors, dungeon_map, global_pole): # blue barriers assign_crystal_barrier_sectors(dungeon_map, crystal_barriers, global_pole) # polarity: - logger.info('-Re-balancing ' + next(iter(dungeon_map.keys())) + ' et al') + logger.info(fish.translate("cli","cli","re-balancing") + ' ' + next(iter(dungeon_map.keys())) + ' et al') assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger) # the rest assign_the_rest(dungeon_map, neutral_sectors, global_pole) diff --git a/DungeonRandomizer.py b/DungeonRandomizer.py index 64a0e2d7..a66a7238 100755 --- a/DungeonRandomizer.py +++ b/DungeonRandomizer.py @@ -8,7 +8,9 @@ import textwrap import shlex import sys -from CLI import parse_arguments +from source.classes.BabelFish import BabelFish + +from CLI import parse_arguments, get_args_priority from Main import main from Rom import get_sprite_from_name from Utils import is_bundled, close_console @@ -17,6 +19,11 @@ from Fill import FillError def start(): args = parse_arguments(None) + settings = get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + 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 @@ -52,10 +59,10 @@ def start(): for _ in range(args.count): try: main(seed=seed, args=args) - logger.info('Finished run %s', _+1) + logger.info('%s %s', fish.translate("cli","cli","finished.run"), _+1) except (FillError, Exception, RuntimeError) as err: failures.append((err, seed)) - logger.warning('Generation failed: %s', err) + logger.warning('%s: %s', fish.translate("cli","cli","generation.failed"), err) seed = random.randint(0, 999999999) for fail in failures: logger.info('%s seed failed with: %s', fail[1], fail[0]) diff --git a/Main.py b/Main.py index 315ba5f5..33ca7548 100644 --- a/Main.py +++ b/Main.py @@ -8,6 +8,9 @@ import random import time import zlib +from source.classes.BabelFish import BabelFish +import CLI as cli + from BaseClasses import World, CollectionState, Item, Region, Location, Shop from Items import ItemFactory from KeyDoorShuffle import validate_key_placement @@ -34,6 +37,11 @@ def main(args, seed=None): start = time.perf_counter() + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + # initialize the world world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints) logger = logging.getLogger('') @@ -62,7 +70,14 @@ def main(args, seed=None): world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} - logger.info('ALttP Door Randomizer Version %s - Seed: %s\n', __version__, world.seed) + logger.info( + '%s %s %s - %s: %s\n', + fish.translate("cli","cli","app.title"), + fish.translate("cli","cli","version"), + __version__, + fish.translate("cli","cli","seed"), + world.seed + ) parsed_names = parse_player_names(args.names, world.players, args.teams) world.teams = len(parsed_names) @@ -95,7 +110,7 @@ def main(args, seed=None): create_rooms(world, player) create_dungeons(world, player) - logger.info('Shuffling the World about.') + logger.info(fish.translate("cli","cli","shuffling.world")) for player in range(1, world.players + 1): if world.mode[player] != 'inverted': @@ -103,7 +118,7 @@ def main(args, seed=None): else: link_inverted_entrances(world, player) - logger.info('Shuffling dungeons') + logger.info(fish.translate("cli","cli","shuffling.dungeons")) for player in range(1, world.players + 1): link_doors(world, player) @@ -111,21 +126,21 @@ def main(args, seed=None): mark_light_world_regions(world, player) else: mark_dark_world_regions(world, player) - logger.info('Generating Item Pool.') + logger.info(fish.translate("cli","cli","generating.itempool")) for player in range(1, world.players + 1): generate_itempool(world, player) - logger.info('Calculating Access Rules.') + logger.info(fish.translate("cli","cli","calc.access.rules")) for player in range(1, world.players + 1): set_rules(world, player) - logger.info('Placing Dungeon Prizes.') + logger.info(fish.translate("cli","cli","placing.dungeon.prizes")) fill_prizes(world) - logger.info('Placing Dungeon Items.') + logger.info(fish.translate("cli","cli","placing.dungeon.items")) shuffled_locations = None if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) + @@ -139,9 +154,17 @@ def main(args, seed=None): for player in range(1, world.players+1): for key_layout in world.key_layout[player].values(): if not validate_key_placement(key_layout, world, player): - raise RuntimeError("Keylock detected: %s (Player %d)" % (key_layout.sector.name, player)) + raise RuntimeError( + "%s: %s (%s %d)" % + ( + fish.translate("cli","cli","keylock.detected"), + key_layout.sector.name, + fish.translate("cli","cli","player"), + player + ) + ) - logger.info('Fill the world.') + logger.info(fish.translate("cli","cli","fill.world")) if args.algorithm == 'flood': flood_items(world) # different algo, biased towards early game progress items @@ -160,14 +183,14 @@ def main(args, seed=None): distribute_items_restrictive(world, True) if world.players > 1: - logger.info('Balancing multiworld progression.') + logger.info(fish.translate("cli","cli","balance.multiworld")) balance_multiworld_progression(world) # if we only check for beatable, we can do this sanity check first before creating the rom if not world.can_beat_game(): - raise RuntimeError('Cannot beat game. Something went terribly wrong here!') + raise RuntimeError(fish.translate("cli","cli","cannot.beat.game")) - logger.info('Patching ROM.') + logger.info(fish.translate("cli","cli","patching.rom")) outfilebase = 'DR_%s' % (args.outputname if args.outputname else world.seed) @@ -191,8 +214,8 @@ def main(args, seed=None): if not args.jsonout: rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) else: - logging.warning("EnemizerCLI not found at:" + args.enemizercli) - logging.warning("No Enemizer options will be applied until this is resolved.") + logging.warning(fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli) + logging.warning(fish.translate("cli","cli","enemizer.nothing.applied")) if args.race: patch_race_rom(rom) @@ -246,7 +269,7 @@ def main(args, seed=None): world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) if not args.skip_playthrough: - logger.info('Calculating playthrough.') + logger.info(fish.translate("cli","cli","calc.playthrough")) create_playthrough(world) if args.jsonout: @@ -254,8 +277,8 @@ def main(args, seed=None): elif args.create_spoiler and not args.skip_playthrough: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) - logger.info('Done. Enjoy.') - logger.info('Total Time: %s', time.perf_counter() - start) + logger.info(fish.translate("cli","cli","done")) + logger.info('%s: %s', fish.translate("cli","cli","total.time"), time.perf_counter() - start) # print_wiki_doors_by_room(dungeon_regions,world,1) @@ -391,6 +414,11 @@ def copy_dynamic_regions_and_locations(world, ret): def create_playthrough(world): + settings = cli.get_args_priority(None, None, None) + if "load" in settings: + settings = settings["load"] + fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + # create a copy as we will modify it old_world = world world = copy_world(world) @@ -401,7 +429,7 @@ def create_playthrough(world): collection_spheres = [] state = CollectionState(world) sphere_candidates = list(prog_locations) - logging.getLogger('').debug('Building up collection spheres.') + logging.getLogger('').debug(fish.translate("cli","cli","building.collection.spheres")) while sphere_candidates: state.sweep_for_events(key_only=True) state.sweep_for_crystal_access() @@ -424,7 +452,7 @@ def create_playthrough(world): if not sphere: logging.getLogger('').debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates]) if any([world.accessibility[location.item.player] != 'none' for location in sphere_candidates]): - raise RuntimeError('Not all progression items reachable. Something went terribly wrong here.') + raise RuntimeError(fish.translate("cli","cli","cannot.reach.progression")) else: old_world.spoiler.unreachables = sphere_candidates.copy() break @@ -478,7 +506,7 @@ def create_playthrough(world): logging.getLogger('').debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(required_locations)) if not sphere: - raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') + raise RuntimeError(fish.translate("cli","cli","cannot.reach.required")) # store the required locations for statistical analysis old_world.required_locations = [(location.name, location.player) for sphere in collection_spheres for location in sphere] diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index aaaff7ae..fb54383d 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -1,4 +1,5 @@ { + "lang": {}, "create_spoiler": { "action": "store_true", "type": "bool" diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 2b1df5c9..90b641d7 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -1,5 +1,46 @@ { + "cli": { + "app.title": "ALttP Door Randomizer", + "version": "Version", + "seed": "Seed", + "player": "Player", + "shuffling.world": "Shuffling the World about", + "shuffling.dungeons": "Shuffling dungeons", + "basic.traversal": "--Basic Traversal", + "generating.dungeon": "Generating dungeon", + "shuffling.keydoors": "Shuffling Key doors for", + "lowering.keys.candidates": "Lowering key door count because not enough candidates", + "lowering.keys.layouts": "Lowering key door count because no valid layouts", + "keydoor.shuffle.time": "Key door shuffle time", + "keydoor.shuffle.time.crossed": "Cross Dungeon: Key door shuffle time", + "generating.itempool": "Generating Item Pool", + "calc.access.rules": "Calculating Access Rules", + "placing.dungeon.prizes": "Placing Dungeon Prizes", + "placing.dungeon.items": "Placing Dungeon Items", + "keylock.detected": "Keylock detected", + "fill.world": "Fill the world", + "balance.doors": "-Balancing Doors", + "re-balancing": "-Re-balancing", + "balancing": "--Balancing", + "splitting.up": "Splitting Up", + "balance.multiworld": "Balancing multiworld progression", + "cannot.beat.game": "Cannot beat game! Something went terribly wrong here!", + "cannot.reach.progression": "Not all progression items reachable. Something went terribly wrong here.", + "cannot.reach.required": "Not all required items reachable. Something went terribly wrong here.", + "patching.rom": "Patching ROM", + "calc.playthrough": "Calculating playthrough", + "done": "Done. Enjoy.", + "total.time": "Total Time", + "finished.run": "Finished run", + "generation.failed": "Generation failed", + "generation.fail.rate": "Generation fail rate", + "generation.success.rate": "Generation success rate", + "enemizer.not.found": "Enemizer not found at", + "enemizer.nothing.applied": "No Enemizer options will be applied until this is resolved.", + "building.collection.spheres": "Building up collection spheres" + }, "help": { + "lang": [ "App Language, if available, defaults to English" ], "create_spoiler": [ "Output a Spoiler File" ], "logic": [ "Select Enforcement of Item Requirements. (default: %(default)s)", diff --git a/resources/app/cli/lang/es.json b/resources/app/cli/lang/es.json new file mode 100644 index 00000000..fe9f3173 --- /dev/null +++ b/resources/app/cli/lang/es.json @@ -0,0 +1,25 @@ +{ + "cli": { + "app.title": "ALttP Puerta Aleatorizador", + "version": "Versión", + "seed": "Número", + "player": "Player", + "shuffling.world": "Barajando el Mundo", + "shuffling.dungeons": "Barajando Mazmorras", + "balance.doors": "-Equilibriando Puertas", + "re-balancing": "-Reequilibriando", + "balancing": "--Equilibriando", + "splitting.up": "División", + "basic.traversal": "--Recorrido Básico", + "generating.dungeon": "Generando mazmorra", + "shuffling.keydoors": "Barajando Puertas Clave para", + "placing.dungeon.prizes": "Placing Dungeon Prizes", + "placing.dungeon.items": "Placing Dungeon Items", + "keylock.detected": "Bloqueo de Teclas detectado", + "fill.world": "Llenar el Mundo", + "patching.rom": "Parchear ROM", + "calc.playthrough": "Cálculo de Juego", + "generation.failed": "Generación Fallida", + "enemizer.not.found": "Enemizer no encontrado en" + } +} \ No newline at end of file diff --git a/source/classes/BabelFish.py b/source/classes/BabelFish.py index d2b52765..4a2897d0 100644 --- a/source/classes/BabelFish.py +++ b/source/classes/BabelFish.py @@ -34,7 +34,8 @@ class BabelFish(): with open(langs_filename,encoding="utf-8") as f: #open it self.lang_defns[lang][key[:key.rfind(os.sep)].replace(os.sep,'.')] = json.load(f) #save translation definitions else: - print(langs_filename + " not found for translation!") + pass +# print(langs_filename + " not found for translation!") def translate(self, domain="", key="", subkey=""): #three levels of keys # start with nothing From 37a1b70f3bd8bb1193fd2f5f31ba946d80121389 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 9 Mar 2020 03:53:08 -0700 Subject: [PATCH 68/93] Pass the fish better --- CLI.py | 12 ++++++--- DoorShuffle.py | 35 +++++++------------------ DungeonGenerator.py | 46 +++++++------------------------- DungeonRandomizer.py | 15 ++++++----- Main.py | 62 ++++++++++++++++++-------------------------- 5 files changed, 60 insertions(+), 110 deletions(-) diff --git a/CLI.py b/CLI.py index 96d3be21..0497069a 100644 --- a/CLI.py +++ b/CLI.py @@ -8,8 +8,6 @@ import textwrap import shlex import sys -from Main import main - import source.classes.constants as CONST from source.classes.BabelFish import BabelFish @@ -26,7 +24,15 @@ def parse_arguments(argv, no_defaults=False): # get settings settings = get_settings() - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") + 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) diff --git a/DoorShuffle.py b/DoorShuffle.py index 7b084fa4..249f9c20 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -5,9 +5,6 @@ import operator as op import time from enum import unique, Flag -from source.classes.BabelFish import BabelFish -import CLI as cli - from functools import reduce from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier from Regions import key_only_locations @@ -308,9 +305,6 @@ def pair_existing_key_doors(world, player, door_a, door_b): # paired_door.pair = False def within_dungeon(world, player): - settings = cli.get_settings() - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - fix_big_key_doors_with_ugly_smalls(world, player) overworld_prep(world, player) entrances_map, potentials, connections = determine_entrance_list(world, player) @@ -322,7 +316,7 @@ def within_dungeon(world, player): dungeon_builders[key] = simple_dungeon_builder(key, sector_list) dungeon_builders[key].entrance_list = list(entrances_map[key]) recombinant_builders = {} - handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map) + handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, world.fish) main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player) paths = determine_required_paths(world, player) @@ -332,15 +326,15 @@ def within_dungeon(world, player): start = time.process_time() for builder in world.dungeon_layouts[player].values(): shuffle_key_doors(builder, world, player) - logging.getLogger('').info('%s: %s', fish.translate("cli","cli","keydoor.shuffle.time"), time.process_time()-start) + logging.getLogger('').info('%s: %s', world.fish.translate("cli","cli","keydoor.shuffle.time"), time.process_time()-start) smooth_door_pairs(world, player) -def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map): +def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, fish): for name, split_list in split_region_starts.items(): builder = dungeon_builders.pop(name) recombinant_builders[name] = builder - split_builders = split_dungeon_builder(builder, split_list) + split_builders = split_dungeon_builder(builder, split_list, fish) dungeon_builders.update(split_builders) for sub_name, split_entrances in split_list.items(): sub_builder = dungeon_builders[name+' '+sub_name] @@ -353,11 +347,6 @@ def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map) def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player): - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - entrances_map, potentials, connections = connections_tuple enabled_entrances = {} sector_queue = deque(dungeon_builders.values()) @@ -377,7 +366,7 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_ sector_queue.append(builder) last_key = builder.name else: - logging.getLogger('').info('%s: %s', fish.translate("cli","cli","generating.dungeon"), builder.name) + logging.getLogger('').info('%s: %s', world.fish.translate("cli","cli","generating.dungeon"), builder.name) ds = generate_dungeon(builder, origin_list_sans_drops, split_dungeon, world, player) find_new_entrances(ds, connections, potentials, enabled_entrances, world, player) ds.name = name @@ -724,9 +713,6 @@ def cross_dungeon(world, player): def assign_cross_keys(dungeon_builders, world, player): - settings = cli.get_settings() - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - start = time.process_time() total_keys = remaining = 29 total_candidates = 0 @@ -806,7 +792,7 @@ def assign_cross_keys(dungeon_builders, world, player): dungeon.small_keys = [] else: dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys - logging.getLogger('').info('%s: %s', fish.translate("cli","cli","keydoor.shuffle.time.crossed"), time.process_time()-start) + logging.getLogger('').info('%s: %s', world.fish.translate("cli","cli","keydoor.shuffle.time.crossed"), time.process_time()-start) def reassign_boss(boss_region, boss_key, builder, gt, world, player): @@ -962,18 +948,15 @@ def calc_used_dungeon_items(builder): def find_valid_combination(builder, start_regions, world, player, drop_keys=True): - settings = cli.get_settings() - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - logger = logging.getLogger('') - logger.info('%s %s', fish.translate("cli","cli","shuffling.keydoors"), builder.name) + logger.info('%s %s', world.fish.translate("cli","cli","shuffling.keydoors"), builder.name) # find valid combination of candidates if len(builder.candidates) < builder.key_doors_num: if not drop_keys: logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num) return False builder.key_doors_num = len(builder.candidates) # reduce number of key doors - logger.info('%s: %s', fish.translate("cli","cli","lowering.keys.candidates"), builder.name) + logger.info('%s: %s', world.fish.translate("cli","cli","lowering.keys.candidates"), builder.name) combinations = ncr(len(builder.candidates), builder.key_doors_num) itr = 0 start = time.process_time() @@ -993,7 +976,7 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True if not drop_keys: logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num) return False - logger.info('%s: %s', fish.translate("cli","cli","lowering.keys.layouts"), builder.name) + logger.info('%s: %s', world.fish.translate("cli","cli","lowering.keys.layouts"), builder.name) builder.key_doors_num -= 1 if builder.key_doors_num < 0: raise Exception('Bad dungeon %s - 0 key doors not valid' % builder.name) diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 25f15e36..2c009437 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -8,9 +8,6 @@ from functools import reduce import operator as op from typing import List -from source.classes.BabelFish import BabelFish -import CLI as cli - from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, Sector, PolSlot, flooded_keys from Regions import key_only_locations, dungeon_events, flooded_keys_reverse from Dungeons import dungeon_regions @@ -1097,11 +1094,6 @@ def simple_dungeon_builder(name, sector_list): def create_dungeon_builders(all_sectors, world, player, dungeon_entrances=None): - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - logger = logging.getLogger('') logger.info('Shuffling Dungeon Sectors') if dungeon_entrances is None: @@ -1157,8 +1149,8 @@ def create_dungeon_builders(all_sectors, world, player, dungeon_entrances=None): # polarity: if not global_pole.is_valid(dungeon_map): raise NeutralizingException('Either free location/crystal assignment is already globally invalid - lazy dev check this earlier!') - logger.info(fish.translate("cli","cli","balance.doors")) - assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger) + logger.info(world.fish.translate("cli","cli","balance.doors")) + assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger, world.fish) # the rest assign_the_rest(dungeon_map, neutral_sectors, global_pole) return dungeon_map @@ -1442,12 +1434,7 @@ def sum_polarity(sector_list): return pol -def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger): - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - +def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger, fish): # step 1: fix polarity connection issues logger.info(fish.translate("cli","cli","basic.traversal")) unconnected_builders = identify_polarity_issues(dungeon_map) @@ -1487,7 +1474,7 @@ def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger problem_builders = identify_simple_branching_issues(problem_builders) # step 3: fix neutrality issues - polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger) + polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger, fish) # step 4: fix dead ends again neutral_choices: List[List] = neutralize_the_rest(polarized_sectors) @@ -1536,12 +1523,7 @@ def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger tries += 1 -def polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger): - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - +def polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger, fish): builder_order = list(dungeon_map.values()) random.shuffle(builder_order) for builder in builder_order: @@ -1849,12 +1831,7 @@ def assign_the_rest(dungeon_map, neutral_sectors, global_pole): assign_sector(sector_list[i], builder, neutral_sectors, global_pole) -def split_dungeon_builder(builder, split_list): - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - +def split_dungeon_builder(builder, split_list, fish): logger = logging.getLogger('') logger.info(fish.translate("cli","cli","splitting.up") + ' ' + 'Desert/Skull') candidate_sectors = dict.fromkeys(builder.sectors) @@ -1867,15 +1844,10 @@ def split_dungeon_builder(builder, split_list): sub_builder.all_entrances = split_entrances for r_name in split_entrances: assign_sector(find_sector(r_name, candidate_sectors), sub_builder, candidate_sectors, global_pole) - return balance_split(candidate_sectors, dungeon_map, global_pole) + return balance_split(candidate_sectors, dungeon_map, global_pole, fish) -def balance_split(candidate_sectors, dungeon_map, global_pole): - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - +def balance_split(candidate_sectors, dungeon_map, global_pole, fish): logger = logging.getLogger('') # categorize sectors crystal_switches, crystal_barriers, neutral_sectors, polarized_sectors = categorize_sectors(candidate_sectors) @@ -1889,7 +1861,7 @@ def balance_split(candidate_sectors, dungeon_map, global_pole): assign_crystal_barrier_sectors(dungeon_map, crystal_barriers, global_pole) # polarity: logger.info(fish.translate("cli","cli","re-balancing") + ' ' + next(iter(dungeon_map.keys())) + ' et al') - assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger) + assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger, fish) # the rest assign_the_rest(dungeon_map, neutral_sectors, global_pole) return dungeon_map diff --git a/DungeonRandomizer.py b/DungeonRandomizer.py index a66a7238..4a0c04e3 100755 --- a/DungeonRandomizer.py +++ b/DungeonRandomizer.py @@ -19,11 +19,6 @@ from Fill import FillError def start(): args = parse_arguments(None) - settings = get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - 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 @@ -49,6 +44,12 @@ def start(): loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[args.loglevel] logging.basicConfig(format='%(message)s', level=loglevel) + settings = get_args_priority(None, None, None) + lang = "en" + if "load" in settings and "lang" in settings["load"]: + lang = settings["load"]["lang"] + fish = BabelFish(lang=lang) + if args.gui: from Gui import guiMain guiMain(args) @@ -58,7 +59,7 @@ def start(): logger = logging.getLogger('') for _ in range(args.count): try: - main(seed=seed, args=args) + main(seed=seed, args=args, fish=fish) logger.info('%s %s', fish.translate("cli","cli","finished.run"), _+1) except (FillError, Exception, RuntimeError) as err: failures.append((err, seed)) @@ -73,7 +74,7 @@ def start(): logger.info('Generation fail rate: ' + str(fail_rate[0] ).rjust(3, " ") + '.' + str(fail_rate[1] ).ljust(6, '0') + '%') logger.info('Generation success rate: ' + str(success_rate[0]).rjust(3, " ") + '.' + str(success_rate[1]).ljust(6, '0') + '%') else: - main(seed=args.seed, args=args) + main(seed=args.seed, args=args, fish=fish) if __name__ == '__main__': diff --git a/Main.py b/Main.py index 33ca7548..b34518fe 100644 --- a/Main.py +++ b/Main.py @@ -8,9 +8,6 @@ import random import time import zlib -from source.classes.BabelFish import BabelFish -import CLI as cli - from BaseClasses import World, CollectionState, Item, Region, Location, Shop from Items import ItemFactory from KeyDoorShuffle import validate_key_placement @@ -30,18 +27,13 @@ from Utils import output_path, parse_player_names, print_wiki_doors_by_room __version__ = '0.0.18.4d' -def main(args, seed=None): +def main(args, seed=None, fish=None): if args.outputpath: os.makedirs(args.outputpath, exist_ok=True) output_path.cached_path = args.outputpath start = time.perf_counter() - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - # initialize the world world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints) logger = logging.getLogger('') @@ -67,15 +59,16 @@ def main(args, seed=None): world.beemizer = args.beemizer.copy() world.experimental = args.experimental.copy() world.dungeon_counters = args.dungeon_counters.copy() + world.fish = fish world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} logger.info( '%s %s %s - %s: %s\n', - fish.translate("cli","cli","app.title"), - fish.translate("cli","cli","version"), + world.fish.translate("cli","cli","app.title"), + world.fish.translate("cli","cli","version"), __version__, - fish.translate("cli","cli","seed"), + world.fish.translate("cli","cli","seed"), world.seed ) @@ -110,7 +103,7 @@ def main(args, seed=None): create_rooms(world, player) create_dungeons(world, player) - logger.info(fish.translate("cli","cli","shuffling.world")) + logger.info(world.fish.translate("cli","cli","shuffling.world")) for player in range(1, world.players + 1): if world.mode[player] != 'inverted': @@ -118,7 +111,7 @@ def main(args, seed=None): else: link_inverted_entrances(world, player) - logger.info(fish.translate("cli","cli","shuffling.dungeons")) + logger.info(world.fish.translate("cli","cli","shuffling.dungeons")) for player in range(1, world.players + 1): link_doors(world, player) @@ -126,21 +119,21 @@ def main(args, seed=None): mark_light_world_regions(world, player) else: mark_dark_world_regions(world, player) - logger.info(fish.translate("cli","cli","generating.itempool")) + logger.info(world.fish.translate("cli","cli","generating.itempool")) for player in range(1, world.players + 1): generate_itempool(world, player) - logger.info(fish.translate("cli","cli","calc.access.rules")) + logger.info(world.fish.translate("cli","cli","calc.access.rules")) for player in range(1, world.players + 1): set_rules(world, player) - logger.info(fish.translate("cli","cli","placing.dungeon.prizes")) + logger.info(world.fish.translate("cli","cli","placing.dungeon.prizes")) fill_prizes(world) - logger.info(fish.translate("cli","cli","placing.dungeon.items")) + logger.info(world.fish.translate("cli","cli","placing.dungeon.items")) shuffled_locations = None if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) + @@ -157,14 +150,14 @@ def main(args, seed=None): raise RuntimeError( "%s: %s (%s %d)" % ( - fish.translate("cli","cli","keylock.detected"), + world.fish.translate("cli","cli","keylock.detected"), key_layout.sector.name, - fish.translate("cli","cli","player"), + world.fish.translate("cli","cli","player"), player ) ) - logger.info(fish.translate("cli","cli","fill.world")) + logger.info(world.fish.translate("cli","cli","fill.world")) if args.algorithm == 'flood': flood_items(world) # different algo, biased towards early game progress items @@ -183,14 +176,14 @@ def main(args, seed=None): distribute_items_restrictive(world, True) if world.players > 1: - logger.info(fish.translate("cli","cli","balance.multiworld")) + logger.info(world.fish.translate("cli","cli","balance.multiworld")) balance_multiworld_progression(world) # if we only check for beatable, we can do this sanity check first before creating the rom if not world.can_beat_game(): - raise RuntimeError(fish.translate("cli","cli","cannot.beat.game")) + raise RuntimeError(world.fish.translate("cli","cli","cannot.beat.game")) - logger.info(fish.translate("cli","cli","patching.rom")) + logger.info(world.fish.translate("cli","cli","patching.rom")) outfilebase = 'DR_%s' % (args.outputname if args.outputname else world.seed) @@ -214,8 +207,8 @@ def main(args, seed=None): if not args.jsonout: rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) else: - logging.warning(fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli) - logging.warning(fish.translate("cli","cli","enemizer.nothing.applied")) + logging.warning(world.fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli) + logging.warning(world.fish.translate("cli","cli","enemizer.nothing.applied")) if args.race: patch_race_rom(rom) @@ -269,7 +262,7 @@ def main(args, seed=None): world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) if not args.skip_playthrough: - logger.info(fish.translate("cli","cli","calc.playthrough")) + logger.info(world.fish.translate("cli","cli","calc.playthrough")) create_playthrough(world) if args.jsonout: @@ -277,8 +270,8 @@ def main(args, seed=None): elif args.create_spoiler and not args.skip_playthrough: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) - logger.info(fish.translate("cli","cli","done")) - logger.info('%s: %s', fish.translate("cli","cli","total.time"), time.perf_counter() - start) + logger.info(world.fish.translate("cli","cli","done")) + logger.info('%s: %s', world.fish.translate("cli","cli","total.time"), time.perf_counter() - start) # print_wiki_doors_by_room(dungeon_regions,world,1) @@ -414,11 +407,6 @@ def copy_dynamic_regions_and_locations(world, ret): def create_playthrough(world): - settings = cli.get_args_priority(None, None, None) - if "load" in settings: - settings = settings["load"] - fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en") - # create a copy as we will modify it old_world = world world = copy_world(world) @@ -429,7 +417,7 @@ def create_playthrough(world): collection_spheres = [] state = CollectionState(world) sphere_candidates = list(prog_locations) - logging.getLogger('').debug(fish.translate("cli","cli","building.collection.spheres")) + logging.getLogger('').debug(world.fish.translate("cli","cli","building.collection.spheres")) while sphere_candidates: state.sweep_for_events(key_only=True) state.sweep_for_crystal_access() @@ -452,7 +440,7 @@ def create_playthrough(world): if not sphere: logging.getLogger('').debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates]) if any([world.accessibility[location.item.player] != 'none' for location in sphere_candidates]): - raise RuntimeError(fish.translate("cli","cli","cannot.reach.progression")) + raise RuntimeError(world.fish.translate("cli","cli","cannot.reach.progression")) else: old_world.spoiler.unreachables = sphere_candidates.copy() break @@ -506,7 +494,7 @@ def create_playthrough(world): logging.getLogger('').debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(required_locations)) if not sphere: - raise RuntimeError(fish.translate("cli","cli","cannot.reach.required")) + raise RuntimeError(world.fish.translate("cli","cli","cannot.reach.required")) # store the required locations for statistical analysis old_world.required_locations = [(location.name, location.player) for sphere in collection_spheres for location in sphere] From ff51821701a2b28c192ad5cc641eba579068514b Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 9 Mar 2020 04:51:57 -0700 Subject: [PATCH 69/93] Moar translation German Refactor some English/Spanish --- Main.py | 13 +++++-------- resources/app/cli/lang/de.json | 27 ++++++++++++++++++++++++++ resources/app/cli/lang/en.json | 13 ++++++++++--- resources/app/cli/lang/es.json | 35 +++++++++++++++++++++------------- 4 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 resources/app/cli/lang/de.json diff --git a/Main.py b/Main.py index b34518fe..652ea35e 100644 --- a/Main.py +++ b/Main.py @@ -64,11 +64,8 @@ def main(args, seed=None, fish=None): world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} logger.info( - '%s %s %s - %s: %s\n', - world.fish.translate("cli","cli","app.title"), - world.fish.translate("cli","cli","version"), + world.fish.translate("cli","cli","app.title") + "\n", __version__, - world.fish.translate("cli","cli","seed"), world.seed ) @@ -271,7 +268,7 @@ def main(args, seed=None, fish=None): world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info(world.fish.translate("cli","cli","done")) - logger.info('%s: %s', world.fish.translate("cli","cli","total.time"), time.perf_counter() - start) + logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start) # print_wiki_doors_by_room(dungeon_regions,world,1) @@ -436,9 +433,9 @@ def create_playthrough(world): state_cache.append(state.copy()) - logging.getLogger('').debug('Calculated sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(prog_locations)) + logging.getLogger('').debug(world.fish.translate("cli","cli","building.calculating.spheres"), len(collection_spheres), len(sphere), len(prog_locations)) if not sphere: - logging.getLogger('').debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates]) + logging.getLogger('').debug(world.fish.translate("cli","cli","cannot.reach.items"), [world.fish.translate("cli","cli","cannot.reach.item") % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates]) if any([world.accessibility[location.item.player] != 'none' for location in sphere_candidates]): raise RuntimeError(world.fish.translate("cli","cli","cannot.reach.progression")) else: @@ -492,7 +489,7 @@ def create_playthrough(world): collection_spheres.append(sphere) - logging.getLogger('').debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(required_locations)) + logging.getLogger('').debug(world.fish.translate("cli","cli","building.final.spheres"), len(collection_spheres), len(sphere), len(required_locations)) if not sphere: raise RuntimeError(world.fish.translate("cli","cli","cannot.reach.required")) diff --git a/resources/app/cli/lang/de.json b/resources/app/cli/lang/de.json new file mode 100644 index 00000000..07936374 --- /dev/null +++ b/resources/app/cli/lang/de.json @@ -0,0 +1,27 @@ +{ + "cli": { + "app.title": "ALttP Tür Randomisier Version %s - Nummer: %d", + "shuffling.world": "Welt wird durchmischt.", + "generating.itempool": "Generier Gegenstandsbasis.", + "calc.access.rules": "Berechne Zugriffsregeln.", + "placing.dungeon.prizes": "Platziere Verliespreise.", + "placing.dungeon.items": "Platziere Verliesgegenstände.", + "fill.world": "Fülle die Welt.", + "balance.multiworld": "Gleiche Multiwelt-Fortschritt aus.", + "patching.rom": "Patche ROM.", + "calc.playthrough": "Berechne Durschpiellösung.", + "done": "Fertig. Viel Spaß.", + "total.time": "Gesamtzeit: %s", + "building.collection.spheres": "Baue Sammelbereiche auf.", + "building.calculating.spheres": "Berechneter Bereich %i, beinhaltet %i von %i Progressionsgegenständen.", + "cannot.reach.items": "Die folgenden Gegenstände können nicht erreicht werden: %s", + "cannot.reach.item": "%s (Spieler %d) in %s (Spieler %d)", + "check.item.location": "Prüfe ob %s (Spieler %d) benötigt wird um das Spiel zu schlagen.", + "check.item.location.true": "Ja, Gegenstand wird benötigt um das Spiel zu schlagen.", + "check.item.location.false": "Nein, Gegenstand wird nicht benötigt um das Spiel zu schlagen.", + "building.final.spheres": "Berechneter Finalbereich %i, beinhaltet, %i von %i Progressionsgegenständen.", + "cannot.beat.game": "Spiel is nicht schlagbar.", + "cannot.reach.progression": "Nicht alle Progressionsgegenstände erreichbar.", + "cannot.reach.required": "Nitch alle benötigten Gegenstände erreichbar." + } +} diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 90b641d7..53ef9599 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -1,6 +1,6 @@ { "cli": { - "app.title": "ALttP Door Randomizer", + "app.title": "ALttP Door Randomizer Version %s - Seed: %d", "version": "Version", "seed": "Seed", "player": "Player", @@ -25,19 +25,26 @@ "splitting.up": "Splitting Up", "balance.multiworld": "Balancing multiworld progression", "cannot.beat.game": "Cannot beat game! Something went terribly wrong here!", + "cannot.reach.items": "The following items could not be reached: %s", + "cannot.reach.item": "%s (Player %d) at %s (Player %d)", + "check.item.location": "Checking if %s (Player %d) is required to beat the game.", + "check.item.location.true": "Yes, item is required.", + "check.item.location.false": "No, item is not required.", "cannot.reach.progression": "Not all progression items reachable. Something went terribly wrong here.", "cannot.reach.required": "Not all required items reachable. Something went terribly wrong here.", "patching.rom": "Patching ROM", "calc.playthrough": "Calculating playthrough", "done": "Done. Enjoy.", - "total.time": "Total Time", + "total.time": "Total Time: %s", "finished.run": "Finished run", "generation.failed": "Generation failed", "generation.fail.rate": "Generation fail rate", "generation.success.rate": "Generation success rate", "enemizer.not.found": "Enemizer not found at", "enemizer.nothing.applied": "No Enemizer options will be applied until this is resolved.", - "building.collection.spheres": "Building up collection spheres" + "building.collection.spheres": "Building up collection spheres", + "building.calculating.spheres": "Calculated sphere %i, containing %i of %i progress items.", + "building.final.spheres": "Calculated final sphere %i, containing %i of %i progress items." }, "help": { "lang": [ "App Language, if available, defaults to English" ], diff --git a/resources/app/cli/lang/es.json b/resources/app/cli/lang/es.json index fe9f3173..9dcdc2c0 100644 --- a/resources/app/cli/lang/es.json +++ b/resources/app/cli/lang/es.json @@ -1,25 +1,34 @@ { "cli": { - "app.title": "ALttP Puerta Aleatorizador", - "version": "Versión", - "seed": "Número", - "player": "Player", + "app.title": "ALttP Puerta Aleatorizador Versión %s - Número: %d", + "player": "Jugador", "shuffling.world": "Barajando el Mundo", "shuffling.dungeons": "Barajando Mazmorras", + "basic.traversal": "--Recorrido Básico", + "generating.dungeon": "Generando mazmorra", + "shuffling.keydoors": "Barajando Puertas Clave para", + + "keylock.detected": "Bloqueo de Teclas detectado", + "fill.world": "Llenar el Mundo", "balance.doors": "-Equilibriando Puertas", "re-balancing": "-Reequilibriando", "balancing": "--Equilibriando", "splitting.up": "División", - "basic.traversal": "--Recorrido Básico", - "generating.dungeon": "Generando mazmorra", - "shuffling.keydoors": "Barajando Puertas Clave para", - "placing.dungeon.prizes": "Placing Dungeon Prizes", - "placing.dungeon.items": "Placing Dungeon Items", - "keylock.detected": "Bloqueo de Teclas detectado", - "fill.world": "Llenar el Mundo", + + "cannot.beat.game": "No se puede vencer el juego. Algo salió terriblemente mal.", + "cannot.reach.items": "No se pudo llegar a los siguientes elementos: %s", + "cannot.reach.item": "%s (Jugador %d) at %s (Jugador %d)", + "check.item.location": "Comprobar si se requiere que %s (Jugador %d) gane el juego.", + "check.item.location.true": "Sí, se requiere artículo.", + "check.item.location.false": "No, no se requiere artículo.", + "patching.rom": "Parchear ROM", "calc.playthrough": "Cálculo de Juego", "generation.failed": "Generación Fallida", - "enemizer.not.found": "Enemizer no encontrado en" + "enemizer.not.found": "Enemizer no encontrado en", + + "building.collection.spheres": "Construyendo esferas de recolección.", + "building.calculating.spheres": "Esfera calculada %i, que contiene %i de %i elementos de progreso.", + "building.final.spheres": "Esfera final calculada %i, que contiene %i de %i elementos de progreso." } -} \ No newline at end of file +} From ceceee68df545b810ff44a67b0889db0a65d01ee Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 9 Mar 2020 19:10:40 -0700 Subject: [PATCH 70/93] Strip Enemizer Foundation for GUI translation --- resources/app/gui/lang/en.json | 271 +++++++++++++++++++++++++++++++ source/classes/BabelFish.py | 1 + source/classes/Empty.py | 3 + source/gui/loadcliargs.py | 55 ++++++- source/gui/randomize/enemizer.py | 31 ++-- source/gui/widgets.py | 6 +- 6 files changed, 342 insertions(+), 25 deletions(-) create mode 100644 resources/app/gui/lang/en.json create mode 100644 source/classes/Empty.py diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json new file mode 100644 index 00000000..86786378 --- /dev/null +++ b/resources/app/gui/lang/en.json @@ -0,0 +1,271 @@ +{ + "gui": { + "adjust.nobgm": "Disable Music & MSU-1", + "adjust.quickswap": "L/R Quickswapping", + + "adjust.heartcolor": "Heart Color", + "adjust.heartcolor.red": "Red", + "adjust.heartcolor.blue": "Blue", + "adjust.heartcolor.green": "Green", + "adjust.heartcolor.yellow": "Yellow", + "adjust.heartcolor.random": "Random", + + "adjust.heartbeep": "Heart Beep sound rate", + "adjust.heartbeep.double": "Double", + "adjust.heartbeep.normal": "Normal", + "adjust.heartbeep.half": "Half", + "adjust.heartbeep.quarter": "Quarter", + "adjust.heartbeep.off": "Off", + + "adjust.menuspeed": "Menu Speed", + "adjust.menuspeed.instant": "Instant", + "adjust.menuspeed.quadruple": "Quadruple", + "adjust.menuspeed.triple": "Triple", + "adjust.menuspeed.double": "Double", + "adjust.menuspeed.normal": "Normal", + "adjust.menuspeed.half": "Half", + + "adjust.owpalettes": "Overworld Palettes", + "adjust.owpalettes.default": "Default", + "adjust.owpalettes.random": "Random", + "adjust.owpalettes.blackout": "Blackout", + + "adjust.uwpalettes": "Underworld Palettes", + "adjust.uwpalettes.default": "Default", + "adjust.uwpalettes.random": "Random", + "adjust.uwpalettes.blackout": "Blackout", + + "adjust.sprite": "Sprite:", + "adjust.sprite.unchanged": "(unchanged)", + + "adjust.rom": "Rom to adjust: ", + "adjust.rom.filetypes": "Rom Files", + "adjust.rom.opendialog": "Select Rom", + "adjust.rom.go": "Adjust Rom", + "adjust.rom.dialog.error": "Error while patching", + "adjust.rom.dialog.success": "Success", + "adjust.rom.dialog.success.message": "Rom patched successfully.", + + + "randomizer.dungeon.keysanity": "Shuffle: ", + "randomizer.dungeon.mapshuffle": "Maps", + "randomizer.dungeon.compassshuffle": "Compasses", + "randomizer.dungeon.smallkeyshuffle": "Small Keys", + "randomizer.dungeon.bigkeyshuffle": "Big Keys", + + "randomizer.dungeon.dungeondoorshuffle": "Dungeon Door Shuffle", + "randomizer.dungeon.dungeondoorshuffle.vanilla": "Vanilla", + "randomizer.dungeon.dungeondoorshuffle.basic": "Basic", + "randomizer.dungeon.dungeondoorshuffle.crossed": "Crossed", + + "randomizer.dungeon.experimental": "Enable Experimental Features", + + "randomizer.dungeon.dungeon_counters": "Dungeon Chest Counters", + "randomizer.dungeon.dungeon_counters.default": "Auto", + "randomizer.dungeon.dungeon_counters.off": "Off", + "randomizer.dungeon.dungeon_counters.on": "On", + "randomizer.dungeon.dungeon_counters.pickup": "On Compass Pickup", + + + "randomizer.enemizer.potshuffle": "Pot Shuffle", + + "randomizer.enemizer.enemyshuffle": "Enemy Shuffle", + "randomizer.enemizer.enemyshuffle.none": "Vanilla", + "randomizer.enemizer.enemyshuffle.shuffled": "Shuffled", + "randomizer.enemizer.enemyshuffle.chaos": "Chaos", + + "randomizer.enemizer.bossshuffle": "Boss Shuffle", + "randomizer.enemizer.bossshuffle.none": "Vanilla", + "randomizer.enemizer.bossshuffle.basic": "Basic", + "randomizer.enemizer.bossshuffle.shuffled": "Shuffled", + "randomizer.enemizer.bossshuffle.chaos": "Chaos", + + "randomizer.enemizer.enemydamage": "Enemy Damage", + "randomizer.enemizer.enemydamage.default": "Vanilla", + "randomizer.enemizer.enemydamage.shuffled": "Shuffled", + "randomizer.enemizer.enemydamage.choas": "Chaos", + + "randomizer.enemizer.enemyhealth": "Enemy Health", + "randomizer.enemizer.enemyhealth.default": "Vanilla", + "randomizer.enemizer.enemyhealth.easy": "Easy", + "randomizer.enemizer.enemyhealth.normal": "Normal", + "randomizer.enemizer.enemyhealth.hard": "Hard", + "randomizer.enemizer.enemyhealth.expert": "Expert", + + "randomizer.enemizer.enemizercli": "EnemizerCLI path: ", + "randomizer.enemizer.enemizercli.online": "(get online)", + + + "randomizer.entrance.openpyramid": "Pre-open Pyramid Hole", + "randomizer.entrance.shuffleganon": "Include Ganon's Tower and Pyramid Hole in shuffle pool", + + "randomizer.entrance.entranceshuffle": "Entrance Shuffle", + "randomizer.entrance.entranceshuffle.vanilla": "Vanilla", + "randomizer.entrance.entranceshuffle.simple": "Simple", + "randomizer.entrance.entranceshuffle.restricted": "Restricted", + "randomizer.entrance.entranceshuffle.full": "Full", + "randomizer.entrance.entranceshuffle.crossed": "Crossed", + "randomizer.entrance.entranceshuffle.insanity": "Insanity", + "randomizer.entrance.entranceshuffle.restricted_legacy": "Restricted (Legacy)", + "randomizer.entrance.entranceshuffle.full_legacy": "Full (Legacy)", + "randomizer.entrance.entranceshuffle.madness_legacy": "Madness (Legacy)", + "randomizer.entrance.entranceshuffle.insanity_legacy": "Insanity (Legacy)", + "randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full", + "randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple", + + + "randomizer.gameoptions.hints": "Include Helpful Hints", + "randomizer.gameoptions.nobgm": "Disable Music & MSU-1", + "randomizer.gameoptions.quickswap": "L/R Quickswapping", + + "randomizer.gameoptions.heartcolor": "Heart Color", + "randomizer.gameoptions.heartcolor.red": "Red", + "randomizer.gameoptions.heartcolor.blue": "Blue", + "randomizer.gameoptions.heartcolor.green": "Green", + "randomizer.gameoptions.heartcolor.yellow": "Yellow", + "randomizer.gameoptions.heartcolor.random": "Random", + + "randomizer.gameoptions.heartbeep": "Heart Beep sound rate", + "randomizer.gameoptions.heartbeep.double": "Double", + "randomizer.gameoptions.heartbeep.normal": "Normal", + "randomizer.gameoptions.heartbeep.half": "Half", + "randomizer.gameoptions.heartbeep.quarter": "Quarter", + "randomizer.gameoptions.heartbeep.off": "Off", + + "randomizer.gameoptions.menuspeed": "Menu Speed", + "randomizer.gameoptions.menuspeed.instant": "Instant", + "randomizer.gameoptions.menuspeed.quadruple": "Quadruple", + "randomizer.gameoptions.menuspeed.triple": "Triple", + "randomizer.gameoptions.menuspeed.double": "Double", + "randomizer.gameoptions.menuspeed.normal": "Normal", + "randomizer.gameoptions.menuspeed.half": "Half", + + "randomizer.gameoptions.owpalettes": "Overworld Palettes", + "randomizer.gameoptions.owpalettes.default": "Default", + "randomizer.gameoptions.owpalettes.random": "Random", + "randomizer.gameoptions.owpalettes.blackout": "Blackout", + + "randomizer.gameoptions.uwpalettes": "Underworld Palettes", + "randomizer.gameoptions.uwpalettes.default": "Default", + "randomizer.gameoptions.uwpalettes.random": "Random", + "randomizer.gameoptions.uwpalettes.blackout": "Blackout", + + "randomizer.gameoptions.sprite": "Sprite:", + "randomizer.gameoptions.sprite.unchanged": "(unchanged)", + + + "randomizer.generation.spoiler": "Create Spoiler Log", + "randomizer.generation.suppressrom": "Do not create patched ROM", + "randomizer.generation.usestartinventory": "Use starting inventory", + "randomizer.generation.usecustompool": "Use custom item pool", + + "randomizer.generation.saveonexit": "Save Settings on Exit", + "randomizer.generation.saveonexit.ask": "Ask Me", + "randomizer.generation.saveonexit.always": "Always", + "randomizer.generation.saveonexit.never": "Never", + + "randomizer.generation.rom": "Base Rom: ", + "randomizer.generation.rom.filetypes": "Rom Files", + "randomizer.generation.rom.opendialog": "Select Rom", + + + "randomizer.item.retro": "Retro mode (universal keys)", + + "randomizer.item.worldstate": "World State", + "randomizer.item.worldstate.standard": "Standard", + "randomizer.item.worldstate.open": "Open", + "randomizer.item.worldstate.inverted": "Inverted", + "randomizer.item.worldstate.retro": "Retro", + + "randomizer.item.logiclevel": "Logic Level", + "randomizer.item.logiclevel.noglitches": "No Glitches", + "randomizer.item.logiclevel.minorglitches": "Minor Glitches", + "randomizer.item.logiclevel.nologic": "No Logic", + + "randomizer.item.goal": "Goal", + "randomizer.item.goal.ganon": "Defeat Ganon", + "randomizer.item.goal.pedestal": "Master Sword Pedestal", + "randomizer.item.goal.dungeons": "All Dungeons", + "randomizer.item.goal.triforcehunt": "Triforce Hunt", + "randomizer.item.goal.crystals": "Crystals", + + "randomizer.item.crystals_gt": "Crystals to open GT", + "randomizer.item.crystals_gt.0": "0", + "randomizer.item.crystals_gt.1": "1", + "randomizer.item.crystals_gt.2": "2", + "randomizer.item.crystals_gt.3": "3", + "randomizer.item.crystals_gt.4": "4", + "randomizer.item.crystals_gt.5": "5", + "randomizer.item.crystals_gt.6": "6", + "randomizer.item.crystals_gt.7": "7", + "randomizer.item.crystals_gt.random": "Random", + + "randomizer.item.crystals_ganon": "Crystals to harm Ganon", + "randomizer.item.crystals_ganon.0": "0", + "randomizer.item.crystals_ganon.1": "1", + "randomizer.item.crystals_ganon.2": "2", + "randomizer.item.crystals_ganon.3": "3", + "randomizer.item.crystals_ganon.4": "4", + "randomizer.item.crystals_ganon.5": "5", + "randomizer.item.crystals_ganon.6": "6", + "randomizer.item.crystals_ganon.7": "7", + "randomizer.item.crystals_ganon.random": "Random", + + "randomizer.item.weapons": "Weapons", + "randomizer.item.weapons.random": "Randomized", + "randomizer.item.weapons.assured": "Assured", + "randomizer.item.weapons.swordless": "Swordless", + "randomizer.item.weapons.vanilla": "Vanilla", + + "randomizer.item.itempool": "Item Pool", + "randomizer.item.itempool.normal": "Normal", + "randomizer.item.itempool.hard": "Hard", + "randomizer.item.itempool.expert": "Expert", + + "randomizer.item.itemfunction": "Item Functionality", + "randomizer.item.itemfunction.normal": "Normal", + "randomizer.item.itemfunction.hard": "Hard", + "randomizer.item.itemfunction.expert": "Expert", + + "randomizer.item.timer": "Timer Setting", + "randomizer.item.timer.none": "No Timer", + "randomizer.item.timer.display": "Stopwatch", + "randomizer.item.timer.timed": "Timed", + "randomizer.item.timer.timed-ohko": "Timed OHKO", + "randomizer.item.timer.ohko": "OHKO", + "randomizer.item.timer.timed-countdown": "Timed Countdown", + + "randomizer.item.progressives": "Progressive Items", + "randomizer.item.progressives.on": "On", + "randomizer.item.progressives.off": "Off", + "randomizer.item.progressives.random": "Random", + + "randomizer.item.accessibility": "Accessibility", + "randomizer.item.accessibility.items": "100% Inventory", + "randomizer.item.accessibility.locations": "100% Locations", + "randomizer.item.accessibility.none": "Beatable", + + "randomizer.item.sortingalgo": "Item Sorting", + "randomizer.item.sortingalgo.freshness": "Freshness", + "randomizer.item.sortingalgo.flood": "Flood", + "randomizer.item.sortingalgo.vt21": "VT8.21", + "randomizer.item.sortingalgo.vt22": "VT8.22", + "randomizer.item.sortingalgo.vt25": "VT8.25", + "randomizer.item.sortingalgo.vt26": "VT8.26", + "randomizer.item.sortingalgo.balanced": "Balanced", + + + "randomizer.multiworld.worlds": "Worlds", + "randomizer.multiworld.names": "Player names", + + + "bottom.seed": "Seed #", + "bottom.generationcount": "Count", + "bottom.go": "Generate Patched Rom", + "bottom.dialog.error": "Error while creating seed", + "bottom.dialog.success": "Success", + "bottom.dialog.success.message": "Rom created successfully.", + "bottom.outputdir": "Open Output Directory", + "bottom.docs": "Open Documentation" + } +} diff --git a/source/classes/BabelFish.py b/source/classes/BabelFish.py index 4a2897d0..81d24acc 100644 --- a/source/classes/BabelFish.py +++ b/source/classes/BabelFish.py @@ -13,6 +13,7 @@ class BabelFish(): self.lang_defns = {} #collect translations self.add_translation_file() #start with default translation file self.add_translation_file(["resources","app","cli"]) #add help translation file + self.add_translation_file(["resources","app","gui"]) #add gui label translation file self.add_translation_file(["resources","user","meta"]) #add user translation file def add_translation_file(self,subpath=["resources","app","meta"]): diff --git a/source/classes/Empty.py b/source/classes/Empty.py new file mode 100644 index 00000000..a22a92d1 --- /dev/null +++ b/source/classes/Empty.py @@ -0,0 +1,3 @@ +# Need a dummy class +class Empty(): + pass diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index b807b599..8e7be63d 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -2,10 +2,13 @@ from source.classes.SpriteSelector import SpriteSelector as spriteSelector from source.gui.randomize.gameoptions import set_sprite from Rom import Sprite, get_sprite_from_name import source.classes.constants as CONST +from source.classes.BabelFish import BabelFish +from source.classes.Empty import Empty # Load args/settings for most tabs def loadcliargs(gui, args, settings=None): if args is not None: + fish = BabelFish() # 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 @@ -23,6 +26,13 @@ def loadcliargs(gui, args, settings=None): for widget in options[mainpage][subpage]: # Get the value and set it arg = options[mainpage][subpage][widget] + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + if hasattr(gui.pages[mainpage].pages[subpage].widgets[widget],"type"): + type = gui.pages[mainpage].pages[subpage].widgets[widget].type + if type == "checkbox": + gui.pages[mainpage].pages[subpage].widgets[widget].checkbox.configure(text=label) + elif type == "selectbox": + gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) 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": @@ -30,27 +40,56 @@ def loadcliargs(gui, args, settings=None): # Check if we've got the widget in Adjust settings hasSettings = settings is not None hasWidget = ("adjust." + widget) in settings if hasSettings else None + label = fish.translate("gui","gui","adjust." + widget) + if ("adjust." + widget) in label: + label = fish.translate("gui","gui","randomizer.gameoptions." + widget) + if hasattr(gui.pages["adjust"].content.widgets[widget],"type"): + type = gui.pages["adjust"].content.widgets[widget].type + if type == "checkbox": + gui.pages["adjust"].content.widgets[widget].checkbox.configure(text=label) + elif type == "selectbox": + gui.pages["adjust"].content.widgets[widget].label.configure(text=label) 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"]) + mainpage = "randomizer" + subpage = "enemizer" + widget = "enemizercli" + setting = "enemizercli" + gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[setting]) + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + ".online") + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["online"].label.configure(text=label) # Get baserom path - gui.pages["randomizer"].pages["generation"].romVar.set(args["rom"]) + mainpage = "randomizer" + subpage = "generation" + setting = "rom" + gui.pages[mainpage].pages[subpage].romVar.set(args[setting]) # Get Multiworld Worlds count - if args["multi"]: - gui.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.set(str(args["multi"])) + mainpage = "randomizer" + subpage = "multiworld" + widget = "worlds" + setting = "multi" + if args[setting]: + gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(str(args[setting])) # Get Seed ID - if args["seed"]: - gui.frames["bottom"].seedVar.set(str(args["seed"])) + mainpage = "bottom" + setting = "seed" + if args[setting]: + gui.frames[mainpage].seedVar.set(str(args[setting])) # Get number of generations to run - if args["count"]: - gui.frames["bottom"].widgets["generationcount"].storageVar.set(str(args["count"])) + mainpage = "bottom" + widget = "generationcount" + setting = "count" + if args[setting]: + gui.frames[mainpage].widgets[widget].storageVar.set(str(args[setting])) # Get output path gui.outputPath.set(args["outputpath"]) diff --git a/source/gui/randomize/enemizer.py b/source/gui/randomize/enemizer.py index 8c92bd48..dd16bbaa 100644 --- a/source/gui/randomize/enemizer.py +++ b/source/gui/randomize/enemizer.py @@ -3,6 +3,7 @@ import source.gui.widgets as widgets import json import os import webbrowser +from source.classes.Empty import Empty def enemizer_page(parent,settings): def open_enemizer_download(_evt): @@ -46,22 +47,26 @@ def enemizer_page(parent,settings): ## 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) - enemizerURL = Label(enemizerPathFrame, text="(get online)", fg="blue", cursor="hand2") - enemizerURL.pack(side=LEFT) - enemizerURL.bind("", open_enemizer_download) - self.enemizerCLIpathVar = StringVar(value=settings["enemizercli"]) - enemizerCLIpathEntry = Entry(enemizerPathFrame, textvariable=self.enemizerCLIpathVar) - enemizerCLIpathEntry.pack(side=LEFT, fill=X, expand=True) + widget = "enemizercli" + self.widgets[widget] = Empty() + self.widgets[widget].pieces = {} + self.widgets[widget].pieces["frame"] = Frame(self.frames["bottomEnemizerFrame"]) + self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text="EnemizerCLI path: ") + self.widgets[widget].pieces["frame"].label.pack(side=LEFT) + self.widgets[widget].pieces["online"] = Empty() + self.widgets[widget].pieces["online"].label = Label(self.widgets[widget].pieces["frame"], text="(get online)", fg="blue", cursor="hand2") + self.widgets[widget].pieces["online"].label.pack(side=LEFT) + self.widgets[widget].pieces["online"].label.bind("", open_enemizer_download) + self.widgets[widget].storageVar = StringVar(value=settings["enemizercli"]) + self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar) + self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True) def EnemizerSelectPath(): path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")], initialdir=os.path.join(".")) if path: - self.enemizerCLIpathVar.set(path) + self.widgets[widget].storageVar.set(path) settings["enemizercli"] = path - enemizerCLIbrowseButton = Button(enemizerPathFrame, text='...', command=EnemizerSelectPath) - enemizerCLIbrowseButton.pack(side=LEFT) - enemizerPathFrame.pack(fill=X) + self.widgets[widget].pieces["opendialog"] = Button(self.widgets[widget].pieces["frame"], text='...', command=EnemizerSelectPath) + self.widgets[widget].pieces["opendialog"].pack(side=LEFT) + self.widgets[widget].pieces["frame"].pack(fill=X) return self,settings diff --git a/source/gui/widgets.py b/source/gui/widgets.py index 95e7b1af..eed19272 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -1,8 +1,5 @@ from tkinter import Checkbutton, Entry, Frame, IntVar, Label, OptionMenu, Spinbox, StringVar, RIGHT, X - -# Need a dummy class -class Empty(): - pass +from source.classes.Empty import Empty # Override Spinbox to include mousewheel support for changing value class mySpinbox(Spinbox): @@ -144,6 +141,7 @@ def make_widget_from_dict(self, defn, parent): managerAttrs = defn["managerAttrs"] if "managerAttrs" in defn else None options = defn["options"] if "options" in defn else None widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options) + widget.type = type return widget # Make a set of generic widgets from a dict From 3f282fe15cc892900debd089058ed40278854dbe Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 9 Mar 2020 20:40:24 -0700 Subject: [PATCH 71/93] Strip Generation --- resources/app/gui/lang/en.json | 9 +++++---- source/classes/BabelFish.py | 8 ++++---- source/gui/loadcliargs.py | 13 ++++++++++++- source/gui/randomize/generation.py | 27 ++++++++++++++++----------- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 86786378..07a6641f 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -39,8 +39,8 @@ "adjust.sprite.unchanged": "(unchanged)", "adjust.rom": "Rom to adjust: ", - "adjust.rom.filetypes": "Rom Files", - "adjust.rom.opendialog": "Select Rom", + "adjust.rom.romfiles": "Rom Files", + "adjust.rom.button": "Select Rom", "adjust.rom.go": "Adjust Rom", "adjust.rom.dialog.error": "Error while patching", "adjust.rom.dialog.success": "Success", @@ -165,8 +165,9 @@ "randomizer.generation.saveonexit.never": "Never", "randomizer.generation.rom": "Base Rom: ", - "randomizer.generation.rom.filetypes": "Rom Files", - "randomizer.generation.rom.opendialog": "Select Rom", + "randomizer.generation.rom.button": "Select Rom", + "randomizer.generation.rom.dialog.romfiles": "Rom Files", + "randomizer.generation.rom.dialog.allfiles": "All Files", "randomizer.item.retro": "Retro mode (universal keys)", diff --git a/source/classes/BabelFish.py b/source/classes/BabelFish.py index 81d24acc..3be79c09 100644 --- a/source/classes/BabelFish.py +++ b/source/classes/BabelFish.py @@ -60,10 +60,10 @@ class BabelFish(): display_text += '.' # display_text += key # Exits - if "exit" in key: + if "exit" in key and "gui" not in domain: key = key.replace("exit","") specials["exit"] = True - if "Exit" in key: + if "Exit" in key and "gui" not in domain: key = key.replace("Exit","") specials["exit"] = True # Locations @@ -80,10 +80,10 @@ class BabelFish(): display_text += '.' display_text += subkey # Exits - if "exit" in subkey: + if "exit" in subkey and "gui" not in domain: subkey = subkey.replace("exit","") specials["exit"] = True - if "Exit" in subkey: + if "Exit" in subkey and "gui" not in domain: subkey = subkey.replace("Exit","") specials["exit"] = True # Locations diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 8e7be63d..c8ac481f 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -58,17 +58,28 @@ def loadcliargs(gui, args, settings=None): subpage = "enemizer" widget = "enemizercli" setting = "enemizercli" + # set storagevar gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[setting]) + # set textbox/frame label label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) + # set get from web label label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + ".online") gui.pages[mainpage].pages[subpage].widgets[widget].pieces["online"].label.configure(text=label) # Get baserom path mainpage = "randomizer" subpage = "generation" + widget = "rom" setting = "rom" - gui.pages[mainpage].pages[subpage].romVar.set(args[setting]) + # set storagevar + gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[setting]) + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) + # set button label + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + ".button") + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["button"].configure(text=label) # Get Multiworld Worlds count mainpage = "randomizer" diff --git a/source/gui/randomize/generation.py b/source/gui/randomize/generation.py index 376fc564..47c221d0 100644 --- a/source/gui/randomize/generation.py +++ b/source/gui/randomize/generation.py @@ -2,6 +2,7 @@ from tkinter import ttk, filedialog, StringVar, Button, Entry, Frame, Label, E, import source.gui.widgets as widgets import json import os +from source.classes.Empty import Empty def generation_page(parent,settings): # Generation Setup @@ -28,20 +29,24 @@ def generation_page(parent,settings): 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() - romEntry = Entry(baseRomFrame, textvariable=self.romVar) - self.romVar.set(settings["rom"]) + widget = "rom" + self.widgets[widget] = Empty() + self.widgets[widget].pieces = {} + self.widgets[widget].pieces["frame"] = Frame(self.frames["baserom"]) + self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text='Base Rom: ') + self.widgets[widget].storageVar = StringVar() + self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar) + self.widgets[widget].storageVar.set(settings["rom"]) + # FIXME: Translate these def RomSelect(): rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")], initialdir=os.path.join(".")) - self.romVar.set(rom) - romSelectButton = Button(baseRomFrame, text='Select Rom', command=RomSelect) + self.widgets[widget].storageVar.set(rom) + self.widgets[widget].pieces["button"] = Button(self.widgets[widget].pieces["frame"], text='Select Rom', command=RomSelect) - baseRomLabel.pack(side=LEFT) - romEntry.pack(side=LEFT, fill=X, expand=True) - romSelectButton.pack(side=LEFT) - baseRomFrame.pack(fill=X) + self.widgets[widget].pieces["frame"].label.pack(side=LEFT) + self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True) + self.widgets[widget].pieces["button"].pack(side=LEFT) + self.widgets[widget].pieces["frame"].pack(fill=X) return self,settings From 014aa3519c368574bdbbd2d3764029f988a543fd Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 01:44:48 -0700 Subject: [PATCH 72/93] Strip Multiworld --- source/gui/loadcliargs.py | 14 +++++++++++ source/gui/randomize/enemizer.py | 17 +++++++++++++ source/gui/randomize/generation.py | 14 +++++++++++ source/gui/randomize/multiworld.py | 38 ++++++++++++++++++++++-------- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index c8ac481f..f8a52483 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -33,6 +33,8 @@ def loadcliargs(gui, args, settings=None): gui.pages[mainpage].pages[subpage].widgets[widget].checkbox.configure(text=label) elif type == "selectbox": gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) + elif type == "spinbox": + gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) 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": @@ -86,9 +88,21 @@ def loadcliargs(gui, args, settings=None): subpage = "multiworld" widget = "worlds" setting = "multi" + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) if args[setting]: + # set storagevar gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(str(args[setting])) + # Set Multiworld Names + mainpage = "randomizer" + subpage = "multiworld" + widget = "names" + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) + # Get Seed ID mainpage = "bottom" setting = "seed" diff --git a/source/gui/randomize/enemizer.py b/source/gui/randomize/enemizer.py index dd16bbaa..f6ba1846 100644 --- a/source/gui/randomize/enemizer.py +++ b/source/gui/randomize/enemizer.py @@ -47,26 +47,43 @@ def enemizer_page(parent,settings): ## Enemizer CLI Path # This one's more-complicated, build it and stuff it + # widget ID widget = "enemizercli" + + # Empty object self.widgets[widget] = Empty() + # pieces self.widgets[widget].pieces = {} + + # frame self.widgets[widget].pieces["frame"] = Frame(self.frames["bottomEnemizerFrame"]) + # frame: label self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text="EnemizerCLI path: ") self.widgets[widget].pieces["frame"].label.pack(side=LEFT) + + # get app online self.widgets[widget].pieces["online"] = Empty() + # get app online: label self.widgets[widget].pieces["online"].label = Label(self.widgets[widget].pieces["frame"], text="(get online)", fg="blue", cursor="hand2") self.widgets[widget].pieces["online"].label.pack(side=LEFT) + # get app online: open browser self.widgets[widget].pieces["online"].label.bind("", open_enemizer_download) + # storage var self.widgets[widget].storageVar = StringVar(value=settings["enemizercli"]) + # textbox self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar) self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True) + def EnemizerSelectPath(): path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")], initialdir=os.path.join(".")) if path: self.widgets[widget].storageVar.set(path) settings["enemizercli"] = path + # dialog button self.widgets[widget].pieces["opendialog"] = Button(self.widgets[widget].pieces["frame"], text='...', command=EnemizerSelectPath) self.widgets[widget].pieces["opendialog"].pack(side=LEFT) + + # frame: pack self.widgets[widget].pieces["frame"].pack(fill=X) return self,settings diff --git a/source/gui/randomize/generation.py b/source/gui/randomize/generation.py index 47c221d0..58eb9b29 100644 --- a/source/gui/randomize/generation.py +++ b/source/gui/randomize/generation.py @@ -29,12 +29,21 @@ def generation_page(parent,settings): self.frames["baserom"].pack(anchor=W, fill=X) ## Locate base ROM # This one's more-complicated, build it and stuff it + # widget ID widget = "rom" + + # Empty object self.widgets[widget] = Empty() + # pieces self.widgets[widget].pieces = {} + + # frame self.widgets[widget].pieces["frame"] = Frame(self.frames["baserom"]) + # frame: label self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text='Base Rom: ') + # storage var self.widgets[widget].storageVar = StringVar() + # textbox self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar) self.widgets[widget].storageVar.set(settings["rom"]) @@ -42,11 +51,16 @@ def generation_page(parent,settings): def RomSelect(): rom = filedialog.askopenfilename(filetypes=[("Rom Files", (".sfc", ".smc")), ("All Files", "*")], initialdir=os.path.join(".")) self.widgets[widget].storageVar.set(rom) + # dialog button self.widgets[widget].pieces["button"] = Button(self.widgets[widget].pieces["frame"], text='Select Rom', command=RomSelect) + # frame label: pack self.widgets[widget].pieces["frame"].label.pack(side=LEFT) + # textbox: pack self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True) + # button: pack self.widgets[widget].pieces["button"].pack(side=LEFT) + # frame: pack self.widgets[widget].pieces["frame"].pack(fill=X) return self,settings diff --git a/source/gui/randomize/multiworld.py b/source/gui/randomize/multiworld.py index c36854ed..d1b69380 100644 --- a/source/gui/randomize/multiworld.py +++ b/source/gui/randomize/multiworld.py @@ -2,6 +2,7 @@ from tkinter import ttk, StringVar, Entry, Frame, Label, N, E, W, X, LEFT import source.gui.widgets as widgets import json import os +from source.classes.Empty import Empty def multiworld_page(parent,settings): # Multiworld @@ -26,16 +27,33 @@ def multiworld_page(parent,settings): ## 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') - self.widgets[key].storageVar = StringVar(value=settings["names"]) + # widget ID + widget = "names" + + # Empty object + self.widgets[widget] = Empty() + # pieces + self.widgets[widget].pieces = {} + + # frame + self.widgets[widget].pieces["frame"] = Frame(self.frames["widgets"]) + # frame: label + self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text='Player names') + # storage var + self.widgets[widget].storageVar = StringVar(value=settings["names"]) + + # FIXME: Got some strange behavior here; both Entry-like objects react to mousewheel on Spinbox def saveMultiNames(caller,_,mode): - settings["names"] = self.widgets[key].storageVar.get() - self.widgets[key].storageVar.trace_add("write",saveMultiNames) - self.widgets[key].textbox = Entry(self.widgets[key], textvariable=self.widgets[key].storageVar) - self.widgets[key].label.pack(side=LEFT) - self.widgets[key].textbox.pack(side=LEFT, fill=X, expand=True) - self.widgets[key].pack(anchor=N, fill=X, expand=True) + settings["names"] = self.widgets[widget].storageVar.get() + self.widgets[widget].storageVar.trace_add("write",saveMultiNames) + # textbox + self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[key].storageVar) + + # frame label: pack + self.widgets[widget].pieces["frame"].label.pack(side=LEFT, anchor=N) + # textbox: pack + self.widgets[widget].pieces["textbox"].pack(side=LEFT, anchor=N, fill=X, expand=True) + # frame: pack + self.widgets[widget].pieces["frame"].pack(side=LEFT, anchor=N, fill=X, expand=True) return self,settings From 58bb9fffba94cae1069b5c4bb3180f421e79c819 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 02:57:20 -0700 Subject: [PATCH 73/93] Strip Bottom --- Gui.py | 8 ++++ source/gui/bottom.py | 94 ++++++++++++++++++++++++++++++--------- source/gui/loadcliargs.py | 32 ++++++++++++- 3 files changed, 111 insertions(+), 23 deletions(-) diff --git a/Gui.py b/Gui.py index c2f871a5..4c4c2959 100755 --- a/Gui.py +++ b/Gui.py @@ -21,6 +21,8 @@ from source.gui.bottom import bottom_frame, create_guiargs from GuiUtils import set_icon from Main import __version__ as ESVersion +from source.classes.BabelFish import BabelFish + def guiMain(args=None): # Save settings to file @@ -143,6 +145,12 @@ def guiMain(args=None): # add randomizer notebook to main window self.pages["randomizer"].notebook.pack() + settings = get_args_priority(None, None, None) + lang = "en" + if "load" in settings and "lang" in settings["load"]: + lang = settings["load"]["lang"] + self.fish = BabelFish(lang=lang) + # bottom of window: Open Output Directory, Open Documentation (if exists) self.frames["bottom"] = bottom_frame(self, self, None) ## Save Settings Button diff --git a/source/gui/bottom.py b/source/gui/bottom.py index aeb26c94..54b859ec 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -8,6 +8,7 @@ from Main import main from Utils import local_path, output_path, open_file import source.classes.constants as CONST import source.gui.widgets as widgets +from source.classes.Empty import Empty def bottom_frame(self, parent, args=None): @@ -17,19 +18,33 @@ def bottom_frame(self, parent, args=None): # Bottom Frame options self.widgets = {} - seedCountFrame = Frame(self) - seedCountFrame.pack() - ## Seed # - seedLabel = Label(self, text='Seed #') + # Seed input + # widget ID + widget = "seed" + + # Empty object + self.widgets[widget] = Empty() + # pieces + self.widgets[widget].pieces = {} + + # frame + self.widgets[widget].pieces["frame"] = Frame(self) + # frame: label + self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text="Seed #") + self.widgets[widget].pieces["frame"].label.pack(side=LEFT) + # storagevar savedSeed = parent.settings["seed"] - self.seedVar = StringVar(value=savedSeed) + self.widgets[widget].storageVar = StringVar(value=savedSeed) + # textbox + self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], width=15, textvariable=self.widgets[widget].storageVar) + self.widgets[widget].pieces["textbox"].pack(side=LEFT) + def saveSeed(caller,_,mode): savedSeed = self.seedVar.get() parent.settings["seed"] = int(savedSeed) if savedSeed.isdigit() else None - self.seedVar.trace_add("write",saveSeed) - seedEntry = Entry(self, width=15, textvariable=self.seedVar) - seedLabel.pack(side=LEFT) - seedEntry.pack(side=LEFT) + self.widgets[widget].storageVar.trace_add("write",saveSeed) + # frame: pack + self.widgets[widget].pieces["frame"].pack(side=LEFT) ## Number of Generation attempts key = "generationcount" @@ -56,10 +71,10 @@ def bottom_frame(self, parent, args=None): if guiargs.count is not None: seed = guiargs.seed for _ in range(guiargs.count): - main(seed=seed, args=guiargs) + main(seed=seed, args=guiargs, fish=parent.fish) seed = random.randint(0, 999999999) else: - main(seed=guiargs.seed, args=guiargs) + main(seed=guiargs.seed, args=guiargs, fish=parent.fish) except Exception as e: logging.exception(e) messagebox.showerror(title="Error while creating seed", message=str(e)) @@ -67,8 +82,18 @@ def bottom_frame(self, parent, args=None): messagebox.showinfo(title="Success", message="Rom patched successfully") ## Generate Button - generateButton = Button(self, text='Generate Patched Rom', command=generateRom) - generateButton.pack(side=LEFT) + # widget ID + widget = "go" + + # Empty object + self.widgets[widget] = Empty() + # pieces + self.widgets[widget].pieces = {} + + # button + self.widgets[widget].pieces["button"] = Button(self, text='Generate Patched Rom', command=generateRom) + # button: pack + self.widgets[widget].pieces["button"].pack(side=LEFT) def open_output(): if args and args.outputpath: @@ -76,15 +101,38 @@ def bottom_frame(self, parent, args=None): else: open_file(output_path(parent.settings["outputpath"])) - openOutputButton = Button(self, text='Open Output Directory', command=open_output) - openOutputButton.pack(side=RIGHT) + ## Output Button + # widget ID + widget = "outputdir" + + # Empty object + self.widgets[widget] = Empty() + # pieces + self.widgets[widget].pieces = {} + + # storagevar + self.widgets[widget].storageVar = StringVar(value=parent.settings["outputpath"]) + + # button + self.widgets[widget].pieces["button"] = Button(self, text='Open Output Directory', command=open_output) + # button: pack + self.widgets[widget].pieces["button"].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(side=RIGHT) + # widget ID + widget = "docs" + + # Empty object + self.widgets[widget] = Empty() + # pieces + self.widgets[widget].pieces = {} + # button + self.widgets[widget].pieces["button"] = Button(self, text='Open Documentation', command=open_readme) + # button: pack + self.widgets[widget].pieces["button"].pack(side=RIGHT) return self @@ -107,22 +155,26 @@ def create_guiargs(parent): setattr(guiargs, arg, parent.pages[mainpage].pages[subpage].widgets[widget].storageVar.get()) # Get EnemizerCLI setting - guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].enemizerCLIpathVar.get() + guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].widgets["enemizercli"].storageVar.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() + guiargs.rom = parent.pages["randomizer"].pages["generation"].widgets["rom"].storageVar.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 + guiargs.seed = None + if parent.frames["bottom"].widgets["seed"].storageVar.get(): + guiargs.seed = int(parent.frames["bottom"].widgets["seed"].storageVar.get()) # 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 + guiargs.count = 1 + if parent.frames["bottom"].widgets["generationcount"].storageVar.get(): + guiargs.count = int(parent.frames["bottom"].widgets["generationcount"].storageVar.get()) # Get Adjust settings adjustargs = { diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index f8a52483..84cb3e10 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -105,9 +105,13 @@ def loadcliargs(gui, args, settings=None): # Get Seed ID mainpage = "bottom" + widget = "seed" setting = "seed" if args[setting]: - gui.frames[mainpage].seedVar.set(str(args[setting])) + gui.frames[mainpage].widgets[widget].storageVar.set(str(args[setting])) + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + widget) + gui.frames[mainpage].widgets[widget].pieces["frame"].label.configure(text=label) # Get number of generations to run mainpage = "bottom" @@ -115,9 +119,33 @@ def loadcliargs(gui, args, settings=None): setting = "count" if args[setting]: gui.frames[mainpage].widgets[widget].storageVar.set(str(args[setting])) + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + widget) + gui.frames[mainpage].widgets[widget].label.configure(text=label) + # Set Generate button + mainpage = "bottom" + widget = "go" + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + widget) + gui.frames[mainpage].widgets[widget].pieces["button"].configure(text=label) + + # Set Output Directory button + mainpage = "bottom" + widget = "outputdir" + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + widget) + gui.frames[mainpage].widgets[widget].pieces["button"].configure(text=label) # Get output path - gui.outputPath.set(args["outputpath"]) + gui.frames[mainpage].widgets[widget].storageVar.set(args["outputpath"]) + + # Set Output Directory button + mainpage = "bottom" + widget = "docs" + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + widget) + if widget in gui.frames[mainpage].widgets: + gui.frames[mainpage].widgets[widget].pieces["button"].configure(text=label) # Figure out Sprite Selection def sprite_setter(spriteObject): From e901f0f1f5ab7350b665189f8e581b9607566a50 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 17:23:23 -0700 Subject: [PATCH 74/93] Ugh, I think I did it --- Gui.py | 9 +- resources/app/gui/lang/en.json | 18 +- .../app/gui/randomize/dungeon/widgets.json | 26 +-- .../app/gui/randomize/enemizer/widgets.json | 46 ++--- .../app/gui/randomize/entrando/widgets.json | 28 +-- .../gui/randomize/gameoptions/widgets.json | 70 ++++---- .../gui/randomize/generation/checkboxes.json | 10 +- resources/app/gui/randomize/item/widgets.json | 170 +++++++++--------- source/classes/constants.py | 6 + source/gui/bottom.py | 24 ++- source/gui/loadcliargs.py | 108 ++++++----- source/gui/widgets.py | 78 ++++++-- 12 files changed, 339 insertions(+), 254 deletions(-) diff --git a/Gui.py b/Gui.py index 4c4c2959..5093ba5e 100755 --- a/Gui.py +++ b/Gui.py @@ -22,6 +22,7 @@ from GuiUtils import set_icon from Main import __version__ as ESVersion from source.classes.BabelFish import BabelFish +from source.classes.Empty import Empty def guiMain(args=None): @@ -152,13 +153,15 @@ def guiMain(args=None): self.fish = BabelFish(lang=lang) # bottom of window: Open Output Directory, Open Documentation (if exists) - self.frames["bottom"] = bottom_frame(self, self, None) + self.pages["bottom"] = Empty() + self.pages["bottom"].pages = {} + self.pages["bottom"].pages["content"] = bottom_frame(self, self, None) ## Save Settings Button - savesettingsButton = Button(self.frames["bottom"], text='Save Settings to File', command=lambda: save_settings_from_gui(True)) + savesettingsButton = Button(self.pages["bottom"].pages["content"], text='Save Settings to File', command=lambda: save_settings_from_gui(True)) savesettingsButton.pack(side=RIGHT) # set bottom frame to main window - self.frames["bottom"].pack(side=BOTTOM, fill=X, padx=5, pady=5) + self.pages["bottom"].pages["content"].pack(side=BOTTOM, fill=X, padx=5, pady=5) self.outputPath = StringVar() self.randomSprite = BooleanVar() diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 07a6641f..2fc10054 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -83,7 +83,7 @@ "randomizer.enemizer.enemydamage": "Enemy Damage", "randomizer.enemizer.enemydamage.default": "Vanilla", "randomizer.enemizer.enemydamage.shuffled": "Shuffled", - "randomizer.enemizer.enemydamage.choas": "Chaos", + "randomizer.enemizer.enemydamage.chaos": "Chaos", "randomizer.enemizer.enemyhealth": "Enemy Health", "randomizer.enemizer.enemyhealth.default": "Vanilla", @@ -260,13 +260,13 @@ "randomizer.multiworld.names": "Player names", - "bottom.seed": "Seed #", - "bottom.generationcount": "Count", - "bottom.go": "Generate Patched Rom", - "bottom.dialog.error": "Error while creating seed", - "bottom.dialog.success": "Success", - "bottom.dialog.success.message": "Rom created successfully.", - "bottom.outputdir": "Open Output Directory", - "bottom.docs": "Open Documentation" + "bottom.content.seed": "Seed #", + "bottom.content.generationcount": "Count", + "bottom.content.go": "Generate Patched Rom", + "bottom.content.dialog.error": "Error while creating seed", + "bottom.content.dialog.success": "Success", + "bottom.content.dialog.success.message": "Rom created successfully.", + "bottom.content.outputdir": "Open Output Directory", + "bottom.content.docs": "Open Documentation" } } diff --git a/resources/app/gui/randomize/dungeon/widgets.json b/resources/app/gui/randomize/dungeon/widgets.json index 295f3098..be8973d8 100644 --- a/resources/app/gui/randomize/dungeon/widgets.json +++ b/resources/app/gui/randomize/dungeon/widgets.json @@ -11,13 +11,13 @@ "selectbox": { "side": "right" }, - "default": "Basic" + "default": "basic" }, - "options": { - "Vanilla": "vanilla", - "Basic": "basic", - "Crossed": "crossed" - } + "options": [ + "vanilla", + "basic", + "crossed" + ] }, "experimental": { "type": "checkbox", @@ -37,13 +37,13 @@ "selectbox": { "side": "right" }, - "default": "Auto" + "default": "default" }, - "options": { - "Auto": "default", - "Off": "off", - "On": "on", - "On Compass Pickup": "pickup" - } + "options": [ + "default", + "off", + "on", + "pickup" + ] } } diff --git a/resources/app/gui/randomize/enemizer/widgets.json b/resources/app/gui/randomize/enemizer/widgets.json index 4095ab5b..af18a221 100644 --- a/resources/app/gui/randomize/enemizer/widgets.json +++ b/resources/app/gui/randomize/enemizer/widgets.json @@ -21,11 +21,11 @@ "side": "right" } }, - "options": { - "Vanilla": "none", - "Shuffled": "shuffled", - "Chaos": "chaos" - } + "options": [ + "none", + "shuffled", + "chaos" + ] }, "bossshuffle": { "type": "selectbox", @@ -40,12 +40,12 @@ "side": "right" } }, - "options": { - "Vanilla": "none", - "Basic": "basic", - "Shuffled": "shuffled", - "Chaos": "chaos" - } + "options": [ + "none", + "basic", + "shuffled", + "chaos" + ] } }, "rightEnemizerFrame": { @@ -62,11 +62,11 @@ "side": "right" } }, - "options": { - "Vanilla": "default", - "Shuffled": "shuffled", - "Chaos": "chaos" - } + "options": [ + "default", + "shuffled", + "chaos" + ] }, "enemyhealth": { "type": "selectbox", @@ -81,13 +81,13 @@ "side": "right" } }, - "options": { - "Vanilla": "default", - "Easy": "easy", - "Normal": "normal", - "Hard": "hard", - "Expert": "expert" - } + "options": [ + "default", + "easy", + "normal", + "hard", + "expert" + ] } } } \ No newline at end of file diff --git a/resources/app/gui/randomize/entrando/widgets.json b/resources/app/gui/randomize/entrando/widgets.json index 1a3ae127..f1c8aad6 100644 --- a/resources/app/gui/randomize/entrando/widgets.json +++ b/resources/app/gui/randomize/entrando/widgets.json @@ -21,20 +21,20 @@ "label": { "side": "left" }, "selectbox": { "side": "right" } }, - "options": { - "Vanilla": "vanilla", - "Simple": "simple", - "Restricted": "restricted", - "Full": "full", - "Crossed": "crossed", - "Insanity": "insanity", - "Restricted (Legacy)": "restricted_legacy", - "Full (Legacy)": "full_legacy", - "Madness (Legacy)": "madness_legacy", - "Insanity (Legacy)": "insanity_legacy", - "Dungeons + Full": "dungeonsfull", - "Dungeons + Simple": "dungeonssimple" - } + "options": [ + "vanilla", + "simple", + "restricted", + "full", + "crossed", + "insanity", + "restricted_legacy", + "full_legacy", + "madness_legacy", + "insanity_legacy", + "dungeonsfull", + "dungeonssimple" + ] } } } diff --git a/resources/app/gui/randomize/gameoptions/widgets.json b/resources/app/gui/randomize/gameoptions/widgets.json index 0a0a11ce..f84ba3ee 100644 --- a/resources/app/gui/randomize/gameoptions/widgets.json +++ b/resources/app/gui/randomize/gameoptions/widgets.json @@ -18,7 +18,7 @@ "label": { "text": "L/R Quickswapping" } - } + } }, "leftRomOptionsFrame": { "heartcolor": { @@ -34,13 +34,13 @@ "side": "right" } }, - "options": { - "Red": "red", - "Blue": "blue", - "Green": "green", - "Yellow": "yellow", - "Random": "random" - } + "options": [ + "red", + "blue", + "green", + "yellow", + "random" + ] }, "heartbeep": { "type": "selectbox", @@ -54,15 +54,15 @@ "selectbox": { "side": "right" }, - "default": "Normal" + "default": "normal" }, - "options": { - "Double": "double", - "Normal": "normal", - "Half": "half", - "Quarter": "quarter", - "Off": "off" - } + "options": [ + "double", + "normal", + "half", + "quarter", + "off" + ] } }, "rightRomOptionsFrame": { @@ -78,16 +78,16 @@ "selectbox": { "side": "right" }, - "default": "Normal" + "default": "normal" }, - "options": { - "Instant": "instant", - "Quadruple": "quadruple", - "Triple": "triple", - "Double": "double", - "Normal": "normal", - "Half": "half" - } + "options": [ + "instant", + "quadruple", + "triple", + "double", + "normal", + "half" + ] }, "owpalettes": { "type": "selectbox", @@ -102,11 +102,11 @@ "side": "right" } }, - "options": { - "Default": "default", - "Random": "random", - "Blackout": "blackout" - } + "options": [ + "default", + "random", + "blackout" + ] }, "uwpalettes": { "type": "selectbox", @@ -121,11 +121,11 @@ "side": "right" } }, - "options": { - "Default": "default", - "Random": "random", - "Blackout": "blackout" - } + "options": [ + "default", + "random", + "blackout" + ] } } } diff --git a/resources/app/gui/randomize/generation/checkboxes.json b/resources/app/gui/randomize/generation/checkboxes.json index b7228c78..43693b72 100644 --- a/resources/app/gui/randomize/generation/checkboxes.json +++ b/resources/app/gui/randomize/generation/checkboxes.json @@ -36,10 +36,10 @@ "side": "right" } }, - "options": { - "Ask Me": "ask", - "Always": "always", - "Never": "never" - } + "options": [ + "ask", + "always", + "never" + ] } } diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index f63cffe7..96dcaa99 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -20,14 +20,14 @@ "selectbox": { "side": "right" }, - "default": "Open" + "default": "open" }, - "options": { - "Standard": "standard", - "Open": "open", - "Inverted": "inverted", - "Retro": "retro" - } + "options": [ + "standard", + "open", + "inverted", + "retro" + ] }, "logiclevel": { "type": "selectbox", @@ -42,11 +42,11 @@ "side": "right" } }, - "options": { - "No Glitches": "noglitches", - "Minor Glitches": "minorglitches", - "No Logic": "nologic" - } + "options": [ + "noglitches", + "minorglitches", + "nologic" + ] }, "goal": { "type": "selectbox", @@ -61,13 +61,13 @@ "side": "right" } }, - "options": { - "Defeat Ganon": "ganon", - "Master Sword Pedestal": "pedestal", - "All Dungeons": "dungeons", - "Triforce Hunt": "triforcehunt", - "Crystals": "crystals" - } + "options": [ + "ganon", + "pedestal", + "dungeons", + "triforcehunt", + "crystals" + ] }, "crystals_gt": { "type": "selectbox", @@ -82,17 +82,17 @@ "side": "right" } }, - "options": { - "0": "0", - "1": "1", - "2": "2", - "3": "3", - "4": "4", - "5": "5", - "6": "6", - "7": "7", - "Random": "random" - } + "options": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "random" + ] }, "crystals_ganon": { "type": "selectbox", @@ -107,17 +107,17 @@ "side": "right" } }, - "options": { - "0": "0", - "1": "1", - "2": "2", - "3": "3", - "4": "4", - "5": "5", - "6": "6", - "7": "7", - "Random": "random" - } + "options": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "random" + ] }, "weapons": { "type": "selectbox", @@ -132,12 +132,12 @@ "side": "right" } }, - "options": { - "Randomized": "random", - "Assured": "assured", - "Swordless": "swordless", - "Vanilla": "vanilla" - } + "options": [ + "random", + "assured", + "swordless", + "vanilla" + ] } }, "rightItemFrame": { @@ -154,11 +154,11 @@ "side": "right" } }, - "options": { - "Normal": "normal", - "Hard": "hard", - "Expert": "expert" - } + "options": [ + "normal", + "hard", + "expert" + ] }, "itemfunction": { "type": "selectbox", @@ -173,11 +173,11 @@ "side": "right" } }, - "options": { - "Normal": "normal", - "Hard": "hard", - "Expert": "expert" - } + "options": [ + "normal", + "hard", + "expert" + ] }, "timer": { "type": "selectbox", @@ -192,14 +192,14 @@ "side": "right" } }, - "options": { - "No Timer": "none", - "Stopwatch": "display", - "Timed": "timed", - "Timed OHKO": "timed-ohko", - "OHKO": "ohko", - "Timed Countdown": "timed-countdown" - } + "options": [ + "none", + "display", + "timed", + "timed-ohko", + "ohko", + "timed-countdown" + ] }, "progressives": { "type": "selectbox", @@ -214,11 +214,11 @@ "side": "right" } }, - "options": { - "On": "on", - "Off": "off", - "Random": "random" - } + "options": [ + "on", + "off", + "random" + ] }, "accessibility": { "type": "selectbox", @@ -233,11 +233,11 @@ "side": "right" } }, - "options": { - "100% Inventory": "items", - "100% Locations": "locations", - "Beatable": "none" - } + "options": [ + "items", + "locations", + "none" + ] }, "sortingalgo": { "type": "selectbox", @@ -251,17 +251,17 @@ "selectbox": { "side": "right" }, - "default": "Balanced" + "default": "balanced" }, - "options": { - "Freshness": "freshness", - "Flood": "flood", - "VT8.21": "vt21", - "VT8.22": "vt22", - "VT8.25": "vt25", - "VT8.26": "vt26", - "Balanced": "balanced" - } + "options": [ + "freshness", + "flood", + "vt21", + "vt22", + "vt25", + "vt26", + "balanced" + ] } } } diff --git a/source/classes/constants.py b/source/classes/constants.py index 8277cd3c..9e52ebc6 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -110,5 +110,11 @@ SETTINGSTOPROCESS = { "usecustompool": "custom", "saveonexit": "saveonexit" } + }, + "bottom": { + "content": { + "seed": "seed", + "generationcount": "count" + } } } diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 54b859ec..56cd0e9a 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -36,11 +36,12 @@ def bottom_frame(self, parent, args=None): savedSeed = parent.settings["seed"] self.widgets[widget].storageVar = StringVar(value=savedSeed) # textbox + self.widgets[widget].type = "textbox" self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], width=15, textvariable=self.widgets[widget].storageVar) self.widgets[widget].pieces["textbox"].pack(side=LEFT) def saveSeed(caller,_,mode): - savedSeed = self.seedVar.get() + savedSeed = self.widgets["seed"].storageVar.get() parent.settings["seed"] = int(savedSeed) if savedSeed.isdigit() else None self.widgets[widget].storageVar.trace_add("write",saveSeed) # frame: pack @@ -91,6 +92,7 @@ def bottom_frame(self, parent, args=None): self.widgets[widget].pieces = {} # button + self.widgets[widget].type = "button" self.widgets[widget].pieces["button"] = Button(self, text='Generate Patched Rom', command=generateRom) # button: pack self.widgets[widget].pieces["button"].pack(side=LEFT) @@ -114,22 +116,26 @@ def bottom_frame(self, parent, args=None): self.widgets[widget].storageVar = StringVar(value=parent.settings["outputpath"]) # button + self.widgets[widget].type = "button" self.widgets[widget].pieces["button"] = Button(self, text='Open Output Directory', command=open_output) # button: pack self.widgets[widget].pieces["button"].pack(side=RIGHT) ## Documentation Button + # widget ID + widget = "docs" + + # Empty object + self.widgets[widget] = Empty() + # pieces + self.widgets[widget].pieces = {} + # button + self.widgets[widget].type = "button" + self.widgets[widget].selectbox = Empty() + self.widgets[widget].selectbox.storageVar = Empty() if os.path.exists(local_path('README.html')): def open_readme(): open_file(local_path('README.html')) - # widget ID - widget = "docs" - - # Empty object - self.widgets[widget] = Empty() - # pieces - self.widgets[widget].pieces = {} - # button self.widgets[widget].pieces["button"] = Button(self, text='Open Documentation', command=open_readme) # button: pack self.widgets[widget].pieces["button"].pack(side=RIGHT) diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 84cb3e10..172db9b3 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -24,36 +24,46 @@ def loadcliargs(gui, args, settings=None): 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] - label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) - if hasattr(gui.pages[mainpage].pages[subpage].widgets[widget],"type"): - type = gui.pages[mainpage].pages[subpage].widgets[widget].type - if type == "checkbox": - gui.pages[mainpage].pages[subpage].widgets[widget].checkbox.configure(text=label) - elif type == "selectbox": - gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) - elif type == "spinbox": - gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) - 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 - label = fish.translate("gui","gui","adjust." + widget) - if ("adjust." + widget) in label: - label = fish.translate("gui","gui","randomizer.gameoptions." + widget) - if hasattr(gui.pages["adjust"].content.widgets[widget],"type"): - type = gui.pages["adjust"].content.widgets[widget].type - if type == "checkbox": - gui.pages["adjust"].content.widgets[widget].checkbox.configure(text=label) - elif type == "selectbox": - gui.pages["adjust"].content.widgets[widget].label.configure(text=label) - 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]) + if widget in gui.pages[mainpage].pages[subpage].widgets: + thisType = "" + # Get the value and set it + arg = options[mainpage][subpage][widget] + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + if hasattr(gui.pages[mainpage].pages[subpage].widgets[widget],"type"): + thisType = gui.pages[mainpage].pages[subpage].widgets[widget].type + if thisType == "checkbox": + gui.pages[mainpage].pages[subpage].widgets[widget].checkbox.configure(text=label) + elif thisType == "selectbox": + theseOptions = gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options + gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) + i = 0 + for value in theseOptions["values"]: + gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options["labels"][i] = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + '.' + value) + i += 1 + for i in range(0, len(theseOptions["values"])): + gui.pages[mainpage].pages[subpage].widgets[widget].selectbox["menu"].entryconfigure(i, label=theseOptions["labels"][i]) + gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options = theseOptions + elif thisType == "spinbox": + gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) + 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 + label = fish.translate("gui","gui","adjust." + widget) + if ("adjust." + widget) in label: + label = fish.translate("gui","gui","randomizer.gameoptions." + widget) + if hasattr(gui.pages["adjust"].content.widgets[widget],"type"): + type = gui.pages["adjust"].content.widgets[widget].type + if type == "checkbox": + gui.pages["adjust"].content.widgets[widget].checkbox.configure(text=label) + elif type == "selectbox": + gui.pages["adjust"].content.widgets[widget].label.configure(text=label) + 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 mainpage = "randomizer" @@ -105,47 +115,53 @@ def loadcliargs(gui, args, settings=None): # Get Seed ID mainpage = "bottom" + subpage = "content" widget = "seed" setting = "seed" if args[setting]: - gui.frames[mainpage].widgets[widget].storageVar.set(str(args[setting])) + gui.pages[mainpage].widgets[widget].storageVar.set(str(args[setting])) # set textbox/frame label - label = fish.translate("gui","gui",mainpage + '.' + widget) - gui.frames[mainpage].widgets[widget].pieces["frame"].label.configure(text=label) + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) # Get number of generations to run mainpage = "bottom" + subpage = "content" widget = "generationcount" setting = "count" if args[setting]: - gui.frames[mainpage].widgets[widget].storageVar.set(str(args[setting])) + gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(str(args[setting])) # set textbox/frame label - label = fish.translate("gui","gui",mainpage + '.' + widget) - gui.frames[mainpage].widgets[widget].label.configure(text=label) + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) # Set Generate button mainpage = "bottom" + subpage = "content" widget = "go" # set textbox/frame label - label = fish.translate("gui","gui",mainpage + '.' + widget) - gui.frames[mainpage].widgets[widget].pieces["button"].configure(text=label) + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["button"].configure(text=label) # Set Output Directory button mainpage = "bottom" + subpage = "content" widget = "outputdir" # set textbox/frame label - label = fish.translate("gui","gui",mainpage + '.' + widget) - gui.frames[mainpage].widgets[widget].pieces["button"].configure(text=label) + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["button"].configure(text=label) # Get output path - gui.frames[mainpage].widgets[widget].storageVar.set(args["outputpath"]) + gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args["outputpath"]) - # Set Output Directory button + # Set Documentation button mainpage = "bottom" + subpage = "content" widget = "docs" - # set textbox/frame label - label = fish.translate("gui","gui",mainpage + '.' + widget) - if widget in gui.frames[mainpage].widgets: - gui.frames[mainpage].widgets[widget].pieces["button"].configure(text=label) + if widget in gui.pages[mainpage].pages[subpage].widgets: + if "button" in gui.pages[mainpage].pages[subpage].widgets[widget].pieces: + # set textbox/frame label + label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) + gui.pages[mainpage].pages[subpage].widgets[widget].pieces["button"].configure(text=label) # Figure out Sprite Selection def sprite_setter(spriteObject): diff --git a/source/gui/widgets.py b/source/gui/widgets.py index eed19272..7697328f 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -28,27 +28,81 @@ def make_checkbox(self, parent, label, storageVar, manager, managerAttrs): # 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()]) - def change_selected(*args): - keys = options.keys() - vals = options.values() - keysList = list(keys) - valsList = list(vals) - self.labelVar.set(keysList[valsList.index(str(self.storageVar.get()))]) self = Frame(parent, name="selectframe-" + label.lower()) - self.storageVar = storageVar - self.storageVar.trace_add("write",change_selected) + + labels = options + + if isinstance(options,dict): + labels = options.keys() + self.labelVar = StringVar() + self.storageVar = storageVar + self.selectbox = OptionMenu(self, self.labelVar, *labels) + self.selectbox.options = {} + + if isinstance(options,dict): + self.selectbox.options["labels"] = list(options.keys()) + self.selectbox.options["values"] = list(options.values()) + else: + self.selectbox.options["labels"] = ["" for i in range(0,len(options))] + self.selectbox.options["values"] = options + + def change_thing(thing, *args): + labels = self.selectbox.options["labels"] + values = self.selectbox.options["values"] + check = "" + lbl = "" + val = "" + idx = 0 + + if thing == "storage": + check = self.labelVar.get() + elif thing == "label": + check = self.storageVar.get() + + if check in labels: + idx = labels.index(check) + if check in values: + idx = values.index(check) + + lbl = labels[idx] + val = values[idx] + + if thing == "storage": + self.storageVar.set(val) + elif thing == "label": + self.labelVar.set(lbl) + self.selectbox["menu"].entryconfigure(idx,label=lbl) + self.selectbox.configure(state="active") + + + def change_storage(*args): + change_thing("storage", *args) + def change_selected(*args): + change_thing("label", *args) + + self.storageVar.trace_add("write",change_selected) self.labelVar.trace_add("write",change_storage) self.label = Label(self, text=label) + if managerAttrs is not None and "label" in managerAttrs: self.label.pack(managerAttrs["label"]) else: self.label.pack() - self.selectbox = OptionMenu(self, self.labelVar, *options.keys()) + self.selectbox.config(width=20) - self.labelVar.set(managerAttrs["default"] if "default" in managerAttrs else list(options.keys())[0]) + idx = 0 + default = self.selectbox.options["values"][idx] + if "default" in managerAttrs: + default = managerAttrs["default"] + labels = self.selectbox.options["labels"] + values = self.selectbox.options["values"] + if default in values: + idx = values.index(default) + default = labels[idx] + self.labelVar.set(default) + self.selectbox["menu"].entryconfigure(idx,label=default) + if managerAttrs is not None and "selectbox" in managerAttrs: self.selectbox.pack(managerAttrs["selectbox"]) else: From a80602517d11b26fafa61883c5bd30fad40521c8 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 17:37:15 -0700 Subject: [PATCH 75/93] Fix generation --- source/gui/bottom.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 56cd0e9a..6d1459d2 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -174,13 +174,15 @@ def create_guiargs(parent): # Get Seed ID guiargs.seed = None - if parent.frames["bottom"].widgets["seed"].storageVar.get(): - guiargs.seed = int(parent.frames["bottom"].widgets["seed"].storageVar.get()) + if parent.pages["bottom"].pages["content"].widgets["seed"].storageVar.get(): + guiargs.seed = parent.pages["bottom"].pages["content"].widgets["seed"].storageVar.get() + if guiargs.seed == "None": + guiargs.seed = None # Get number of generations to run guiargs.count = 1 - if parent.frames["bottom"].widgets["generationcount"].storageVar.get(): - guiargs.count = int(parent.frames["bottom"].widgets["generationcount"].storageVar.get()) + if parent.pages["bottom"].pages["content"].widgets["generationcount"].storageVar.get(): + guiargs.count = int(parent.pages["bottom"].pages["content"].widgets["generationcount"].storageVar.get()) # Get Adjust settings adjustargs = { From 3338e968223b530558b6be43e73329e69f7c5278 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 17:46:58 -0700 Subject: [PATCH 76/93] Add seed to end of output log per generation --- Main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Main.py b/Main.py index 652ea35e..42146027 100644 --- a/Main.py +++ b/Main.py @@ -268,6 +268,7 @@ def main(args, seed=None, fish=None): world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info(world.fish.translate("cli","cli","done")) + logger.info(world.fish.translate("cli","cli","seed") + ": %d", world.seed) logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start) # print_wiki_doors_by_room(dungeon_regions,world,1) From e2943b21dc9af82a9439fa6d89d89becb449dd44 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 18:21:36 -0700 Subject: [PATCH 77/93] Fix Seed & Player Names --- CLI.py | 4 ++-- source/gui/bottom.py | 4 +--- source/gui/loadcliargs.py | 4 +++- source/gui/randomize/multiworld.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CLI.py b/CLI.py index 0497069a..46d375ce 100644 --- a/CLI.py +++ b/CLI.py @@ -163,8 +163,8 @@ def get_settings(): "custom": False, "rom": os.path.join(".", "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"), - "seed": None, - "count": None, + "seed": "", + "count": 1, "startinventory": "", "beemizer": 0, "remote_items": False, diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 6d1459d2..f1da09b9 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -173,11 +173,9 @@ def create_guiargs(parent): guiargs.custom = bool(parent.pages["randomizer"].pages["generation"].widgets["usecustompool"].storageVar.get()) # Get Seed ID - guiargs.seed = None + guiargs.seed = "" if parent.pages["bottom"].pages["content"].widgets["seed"].storageVar.get(): guiargs.seed = parent.pages["bottom"].pages["content"].widgets["seed"].storageVar.get() - if guiargs.seed == "None": - guiargs.seed = None # Get number of generations to run guiargs.count = 1 diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 172db9b3..a528fa50 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -28,6 +28,8 @@ def loadcliargs(gui, args, settings=None): thisType = "" # Get the value and set it arg = options[mainpage][subpage][widget] + if args[arg] == None: + args[arg] = "" label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) if hasattr(gui.pages[mainpage].pages[subpage].widgets[widget],"type"): thisType = gui.pages[mainpage].pages[subpage].widgets[widget].type @@ -119,7 +121,7 @@ def loadcliargs(gui, args, settings=None): widget = "seed" setting = "seed" if args[setting]: - gui.pages[mainpage].widgets[widget].storageVar.set(str(args[setting])) + gui.pages[mainpage].widgets[widget].storageVar.set(args[setting]) # set textbox/frame label label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) diff --git a/source/gui/randomize/multiworld.py b/source/gui/randomize/multiworld.py index d1b69380..369c1b6d 100644 --- a/source/gui/randomize/multiworld.py +++ b/source/gui/randomize/multiworld.py @@ -44,10 +44,10 @@ def multiworld_page(parent,settings): # FIXME: Got some strange behavior here; both Entry-like objects react to mousewheel on Spinbox def saveMultiNames(caller,_,mode): - settings["names"] = self.widgets[widget].storageVar.get() + settings["names"] = self.widgets["names"].storageVar.get() self.widgets[widget].storageVar.trace_add("write",saveMultiNames) # textbox - self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[key].storageVar) + self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar) # frame label: pack self.widgets[widget].pieces["frame"].label.pack(side=LEFT, anchor=N) From 83d9398a19f3c18148c2f7dad26fa2ab6035464e Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 18:29:23 -0700 Subject: [PATCH 78/93] Reorder args --- resources/app/cli/args.json | 6 +++--- source/gui/widgets.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index fb54383d..f95f3a1d 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -80,10 +80,10 @@ }, "shuffle": { "choices": [ - "full", "vanilla", "simple", "restricted", + "full", "crossed", "insanity", "restricted_legacy", @@ -304,9 +304,9 @@ }, "saveonexit": { "choices": [ - "never", "ask", - "always" + "always", + "never" ] }, "outputname": {} diff --git a/source/gui/widgets.py b/source/gui/widgets.py index 7697328f..f5512c89 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -99,9 +99,9 @@ def make_selectbox(self, parent, label, options, storageVar, manager, managerAtt values = self.selectbox.options["values"] if default in values: idx = values.index(default) - default = labels[idx] - self.labelVar.set(default) - self.selectbox["menu"].entryconfigure(idx,label=default) + self.labelVar.set(labels[idx]) + self.storageVar.set(values[idx]) + self.selectbox["menu"].entryconfigure(idx,label=labels[idx]) if managerAttrs is not None and "selectbox" in managerAttrs: self.selectbox.pack(managerAttrs["selectbox"]) From 79c6047415b29770ee2c180174172a7baa2fc3ed Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 18:47:16 -0700 Subject: [PATCH 79/93] Fix Seed again --- source/gui/bottom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index f1da09b9..b55090ea 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -173,7 +173,7 @@ def create_guiargs(parent): guiargs.custom = bool(parent.pages["randomizer"].pages["generation"].widgets["usecustompool"].storageVar.get()) # Get Seed ID - guiargs.seed = "" + guiargs.seed = None if parent.pages["bottom"].pages["content"].widgets["seed"].storageVar.get(): guiargs.seed = parent.pages["bottom"].pages["content"].widgets["seed"].storageVar.get() From 721896d5a867a092de4598f71399bfc562dcd76a Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 18:58:54 -0700 Subject: [PATCH 80/93] Forgot to pass the fish --- DoorShuffle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 249f9c20..47e2384b 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -671,7 +671,7 @@ def cross_dungeon(world, player): key_name = dungeon_keys[builder.name] if loc.name != 'Hyrule Castle - Big Key Drop' else dungeon_bigs[builder.name] loc.forced_item = loc.item = ItemFactory(key_name, player) recombinant_builders = {} - handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map) + handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map, world.fish) main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player) From 0c1dc363358298c00fb0981fcd08a26427c1e385 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 21:31:42 -0700 Subject: [PATCH 81/93] Move MW to Bottom --- Gui.py | 6 +++--- resources/app/gui/lang/en.json | 6 ++---- source/classes/constants.py | 4 +--- source/gui/bottom.py | 5 +++++ source/gui/loadcliargs.py | 8 ++++---- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Gui.py b/Gui.py index 5093ba5e..ebe39f33 100755 --- a/Gui.py +++ b/Gui.py @@ -14,7 +14,7 @@ from source.gui.randomize.item import item_page from source.gui.randomize.entrando import entrando_page from source.gui.randomize.enemizer import enemizer_page from source.gui.randomize.dungeon import dungeon_page -from source.gui.randomize.multiworld import multiworld_page +#from source.gui.randomize.multiworld import multiworld_page from source.gui.randomize.gameoptions import gameoptions_page from source.gui.randomize.generation import generation_page from source.gui.bottom import bottom_frame, create_guiargs @@ -132,8 +132,8 @@ def guiMain(args=None): self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["dungeon"], text="Dungeon Shuffle") # Multiworld - self.pages["randomizer"].pages["multiworld"],self.settings = multiworld_page(self.pages["randomizer"].notebook,self.settings) - self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["multiworld"], text="Multiworld") +# self.pages["randomizer"].pages["multiworld"],self.settings = multiworld_page(self.pages["randomizer"].notebook,self.settings) +# self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["multiworld"], text="Multiworld") # Game Options self.pages["randomizer"].pages["gameoptions"] = gameoptions_page(self, self.pages["randomizer"].notebook) diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 2fc10054..fcb28072 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -256,10 +256,8 @@ "randomizer.item.sortingalgo.balanced": "Balanced", - "randomizer.multiworld.worlds": "Worlds", - "randomizer.multiworld.names": "Player names", - - + "bottom.content.worlds": "Worlds", + "bottom.content.names": "Player names", "bottom.content.seed": "Seed #", "bottom.content.generationcount": "Count", "bottom.content.go": "Generate Patched Rom", diff --git a/source/classes/constants.py b/source/classes/constants.py index 9e52ebc6..93064e7d 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -90,9 +90,6 @@ SETTINGSTOPROCESS = { "experimental": "experimental", "dungeon_counters": "dungeon_counters" }, - "multiworld": { - "names": "names" - }, "gameoptions": { "hints": "hints", "nobgm": "disablemusic", @@ -113,6 +110,7 @@ SETTINGSTOPROCESS = { }, "bottom": { "content": { + "names": "names", "seed": "seed", "generationcount": "count" } diff --git a/source/gui/bottom.py b/source/gui/bottom.py index b55090ea..a4f1ce1f 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -7,6 +7,7 @@ from CLI import parse_arguments from Main import main from Utils import local_path, output_path, open_file import source.classes.constants as CONST +from source.gui.randomize.multiworld import multiworld_page import source.gui.widgets as widgets from source.classes.Empty import Empty @@ -18,6 +19,10 @@ def bottom_frame(self, parent, args=None): # Bottom Frame options self.widgets = {} + mw,_ = multiworld_page(self, parent.settings) + mw.pack(fill=X, expand=True) + self.widgets = mw.widgets + # Seed input # widget ID widget = "seed" diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index a528fa50..6d8ae3b5 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -96,8 +96,8 @@ def loadcliargs(gui, args, settings=None): gui.pages[mainpage].pages[subpage].widgets[widget].pieces["button"].configure(text=label) # Get Multiworld Worlds count - mainpage = "randomizer" - subpage = "multiworld" + mainpage = "bottom" + subpage = "content" widget = "worlds" setting = "multi" # set textbox/frame label @@ -108,8 +108,8 @@ def loadcliargs(gui, args, settings=None): gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(str(args[setting])) # Set Multiworld Names - mainpage = "randomizer" - subpage = "multiworld" + mainpage = "bottom" + subpage = "content" widget = "names" # set textbox/frame label label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) From e485b39842d91c6b1d00caac85002d37b423a65a Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 21:32:54 -0700 Subject: [PATCH 82/93] Different Notes --- RELEASENOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 27c2b586..cd41b205 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,4 +1,6 @@ # Features +## Door Randomizer + * Native GUI executables * Native Dungeon Randomizer CLI executables From 50a960114a3a3d6e1b7ec864e47cb9f0a73de76d Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 21:48:15 -0700 Subject: [PATCH 83/93] Fix Worlds --- source/gui/bottom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index a4f1ce1f..63eec2f9 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -169,7 +169,7 @@ def create_guiargs(parent): guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].widgets["enemizercli"].storageVar.get() # Get Multiworld Worlds count - guiargs.multi = int(parent.pages["randomizer"].pages["multiworld"].widgets["worlds"].storageVar.get()) + guiargs.multi = int(parent.pages["bottom"].pages["content"].widgets["worlds"].storageVar.get()) # Get baserom path guiargs.rom = parent.pages["randomizer"].pages["generation"].widgets["rom"].storageVar.get() From f69d86eca6277ea968725265b138e7aae8ec2401 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 10 Mar 2020 22:10:56 -0700 Subject: [PATCH 84/93] Test Linux user folder --- Utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Utils.py b/Utils.py index f0dba062..8376d5fb 100644 --- a/Utils.py +++ b/Utils.py @@ -78,7 +78,9 @@ def output_path(path): # 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("~","Documents") + documents = os.path.expanduser("~") + print(documents) + documents = os.path.join(documents,"Documents") else: raise NotImplementedError('Not supported yet') From 0cb7eaa417f62b2ebd177338308231f5e2ea077c Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 12 Mar 2020 03:31:39 -0700 Subject: [PATCH 85/93] Slim down args --- Utils.py | 4 +- resources/app/cli/args.json | 6 +- resources/app/cli/lang/en.json | 6 +- .../app/gui/randomize/dungeon/keysanity.json | 28 +--- .../app/gui/randomize/dungeon/widgets.json | 25 +-- .../app/gui/randomize/enemizer/widgets.json | 53 +----- .../app/gui/randomize/entrando/widgets.json | 23 +-- .../gui/randomize/gameoptions/widgets.json | 70 +------- .../gui/randomize/generation/checkboxes.json | 47 +----- .../app/gui/randomize/generation/widgets.json | 10 ++ resources/app/gui/randomize/item/widgets.json | 153 +----------------- .../app/gui/randomize/multiworld/widgets.json | 15 +- source/gui/loadcliargs.py | 2 +- source/gui/randomize/generation.py | 11 ++ source/gui/widgets.py | 29 ++-- 15 files changed, 65 insertions(+), 417 deletions(-) create mode 100644 resources/app/gui/randomize/generation/widgets.json diff --git a/Utils.py b/Utils.py index 8376d5fb..43cae912 100644 --- a/Utils.py +++ b/Utils.py @@ -78,9 +78,7 @@ def output_path(path): # 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.expanduser("~") - print(documents) - documents = os.path.join(documents,"Documents") + documents = os.path.join(os.path.expanduser("~"),"Documents") else: raise NotImplementedError('Not supported yet') diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index f95f3a1d..b991620f 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -200,12 +200,12 @@ }, "shuffleganon": { "action": "store_true", - "type": "bool", - "help": "suppress" + "type": "bool" }, "no-shuffleganon": { "action": "store_false", - "dest": "shuffleganon" + "dest": "shuffleganon", + "help": "suppress" }, "heartbeep": { "choices": [ diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 53ef9599..fdd58163 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -246,9 +246,9 @@ "None: You will be able to reach enough locations to beat the game." ], "hints": [ "Make telepathic tiles and storytellers give helpful hints. (default: %(default)s)" ], - "no-shuffleganon": [ - "If set, the Pyramid Hole and Ganon's Tower are not", - "included entrance shuffle pool. (default: %(default)s)" + "shuffleganon": [ + "Include the Ganon's Tower and Pyramid Hole in the", + "entrance shuffle pool. (default: %(default)s)" ], "heartbeep": [ "Select the rate at which the heart beep sound is played at", diff --git a/resources/app/gui/randomize/dungeon/keysanity.json b/resources/app/gui/randomize/dungeon/keysanity.json index 1be4c3fd..29efc0e4 100644 --- a/resources/app/gui/randomize/dungeon/keysanity.json +++ b/resources/app/gui/randomize/dungeon/keysanity.json @@ -1,26 +1,6 @@ { - "mapshuffle": { - "type": "checkbox", - "label": { - "text": "Maps" - } - }, - "compassshuffle": { - "type": "checkbox", - "label": { - "text": "Compasses" - } - }, - "smallkeyshuffle": { - "type": "checkbox", - "label": { - "text": "Small Keys" - } - }, - "bigkeyshuffle": { - "type": "checkbox", - "label": { - "text": "Big Keys" - } - } + "mapshuffle": { "type": "checkbox" }, + "compassshuffle": { "type": "checkbox" }, + "smallkeyshuffle": { "type": "checkbox" }, + "bigkeyshuffle": { "type": "checkbox" } } diff --git a/resources/app/gui/randomize/dungeon/widgets.json b/resources/app/gui/randomize/dungeon/widgets.json index be8973d8..91ef1f3e 100644 --- a/resources/app/gui/randomize/dungeon/widgets.json +++ b/resources/app/gui/randomize/dungeon/widgets.json @@ -1,16 +1,7 @@ { "dungeondoorshuffle": { "type": "selectbox", - "label": { - "text": "Dungeon Door Shuffle" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "basic" }, "options": [ @@ -19,24 +10,10 @@ "crossed" ] }, - "experimental": { - "type": "checkbox", - "label": { - "text": "Enable Experimental Features" - } - }, + "experimental": { "type": "checkbox" }, "dungeon_counters": { "type": "selectbox", - "label": { - "text": "Dungeon Chest Counters" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "default" }, "options": [ diff --git a/resources/app/gui/randomize/enemizer/widgets.json b/resources/app/gui/randomize/enemizer/widgets.json index af18a221..8103e8cb 100644 --- a/resources/app/gui/randomize/enemizer/widgets.json +++ b/resources/app/gui/randomize/enemizer/widgets.json @@ -1,26 +1,10 @@ { "checkboxes": { - "potshuffle": { - "type": "checkbox", - "label": { - "text": "Pot Shuffle" - } - } + "potshuffle": { "type": "checkbox" } }, "leftEnemizerFrame": { "enemyshuffle": { "type": "selectbox", - "label": { - "text": "Enemy Shuffle" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "none", "shuffled", @@ -29,17 +13,6 @@ }, "bossshuffle": { "type": "selectbox", - "label": { - "text": "Boss Shuffle" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "none", "basic", @@ -51,17 +24,6 @@ "rightEnemizerFrame": { "enemydamage": { "type": "selectbox", - "label": { - "text": "Enemy Damage" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "default", "shuffled", @@ -70,17 +32,6 @@ }, "enemyhealth": { "type": "selectbox", - "label": { - "text": "Enemy Health" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "default", "easy", @@ -90,4 +41,4 @@ ] } } -} \ No newline at end of file +} diff --git a/resources/app/gui/randomize/entrando/widgets.json b/resources/app/gui/randomize/entrando/widgets.json index f1c8aad6..e46ee36e 100644 --- a/resources/app/gui/randomize/entrando/widgets.json +++ b/resources/app/gui/randomize/entrando/widgets.json @@ -1,26 +1,9 @@ { "widgets": { - "openpyramid": { - "type": "checkbox", - "label": { - "text": "Pre-open Pyramid Hole" - } - }, - "shuffleganon": { - "type": "checkbox", - "label": { - "text": "Include Ganon's Tower and Pyramid Hole in shuffle pool" - } - }, - "entranceshuffle": { + "openpyramid": { "type": "checkbox" }, + "shuffleganon": { "type": "checkbox" }, + "entranceshuffle": { "type": "selectbox", - "label": { - "text": "Entrance Shuffle" - }, - "managerAttrs": { - "label": { "side": "left" }, - "selectbox": { "side": "right" } - }, "options": [ "vanilla", "simple", diff --git a/resources/app/gui/randomize/gameoptions/widgets.json b/resources/app/gui/randomize/gameoptions/widgets.json index f84ba3ee..ac187f86 100644 --- a/resources/app/gui/randomize/gameoptions/widgets.json +++ b/resources/app/gui/randomize/gameoptions/widgets.json @@ -1,39 +1,15 @@ { - "checkboxes": { + "checkboxes": { "hints": { "type": "checkbox", - "label": { - "text": "Include Helpful Hints" - }, "default": "true" }, - "nobgm": { - "type": "checkbox", - "label": { - "text": "Disable Music & MSU-1" - } - }, - "quickswap": { - "type": "checkbox", - "label": { - "text": "L/R Quickswapping" - } - } + "nobgm": { "type": "checkbox" }, + "quickswap": { "type": "checkbox" } }, "leftRomOptionsFrame": { "heartcolor": { "type": "selectbox", - "label": { - "text": "Heart Color" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "red", "blue", @@ -44,16 +20,7 @@ }, "heartbeep": { "type": "selectbox", - "label": { - "text": "Heart Beep sound rate" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "normal" }, "options": [ @@ -68,16 +35,7 @@ "rightRomOptionsFrame": { "menuspeed": { "type": "selectbox", - "label": { - "text": "Menu Speed" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "normal" }, "options": [ @@ -91,17 +49,6 @@ }, "owpalettes": { "type": "selectbox", - "label": { - "text": "Overworld Palettes" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "default", "random", @@ -110,17 +57,6 @@ }, "uwpalettes": { "type": "selectbox", - "label": { - "text": "Underworld Palettes" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "default", "random", diff --git a/resources/app/gui/randomize/generation/checkboxes.json b/resources/app/gui/randomize/generation/checkboxes.json index 43693b72..d609e866 100644 --- a/resources/app/gui/randomize/generation/checkboxes.json +++ b/resources/app/gui/randomize/generation/checkboxes.json @@ -1,45 +1,6 @@ { - "spoiler": { - "type": "checkbox", - "label": { - "text": "Create Spoiler Log" - } - }, - "suppressrom": { - "type": "checkbox", - "label": { - "text": "Do not create patched ROM" - } - }, - "usestartinventory": { - "type": "checkbox", - "label": { - "text": "Use starting inventory" - } - }, - "usecustompool": { - "type": "checkbox", - "label": { - "text": "Use custom item pool" - } - }, - "saveonexit": { - "type": "selectbox", - "label": { - "text": "Save Settings on Exit" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, - "options": [ - "ask", - "always", - "never" - ] - } + "spoiler": { "type": "checkbox" }, + "suppressrom": { "type": "checkbox" }, + "usestartinventory": { "type": "checkbox" }, + "usecustompool": { "type": "checkbox" } } diff --git a/resources/app/gui/randomize/generation/widgets.json b/resources/app/gui/randomize/generation/widgets.json new file mode 100644 index 00000000..d22de592 --- /dev/null +++ b/resources/app/gui/randomize/generation/widgets.json @@ -0,0 +1,10 @@ +{ + "saveonexit": { + "type": "selectbox", + "options": [ + "ask", + "always", + "never" + ] + } +} diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 96dcaa99..6a3ebbaa 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -1,25 +1,11 @@ { "checkboxes": { - "retro": { - "type": "checkbox", - "label": { - "text": "Retro mode (universal keys)" - } - } + "retro": { "type": "checkbox" } }, "leftItemFrame": { "worldstate": { "type": "selectbox", - "label": { - "text": "World State" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "open" }, "options": [ @@ -31,17 +17,6 @@ }, "logiclevel": { "type": "selectbox", - "label": { - "text": "Logic Level" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "noglitches", "minorglitches", @@ -50,17 +25,6 @@ }, "goal": { "type": "selectbox", - "label": { - "text": "Goal" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "ganon", "pedestal", @@ -71,67 +35,20 @@ }, "crystals_gt": { "type": "selectbox", - "label": { - "text": "Crystals to open GT" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", + 0, 1, 2, 3, 4, 5, 6, 7, "random" ] }, "crystals_ganon": { "type": "selectbox", - "label": { - "text": "Crystals to harm Ganon" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", + 0, 1, 2, 3, 4, 5, 6, 7, "random" ] }, "weapons": { "type": "selectbox", - "label": { - "text": "Weapons" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "random", "assured", @@ -143,17 +60,6 @@ "rightItemFrame": { "itempool": { "type": "selectbox", - "label": { - "text": "Item Pool" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "normal", "hard", @@ -162,17 +68,6 @@ }, "itemfunction": { "type": "selectbox", - "label": { - "text": "Item Functionality" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "normal", "hard", @@ -181,17 +76,6 @@ }, "timer": { "type": "selectbox", - "label": { - "text": "Timer Setting" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "none", "display", @@ -203,17 +87,6 @@ }, "progressives": { "type": "selectbox", - "label": { - "text": "Progressive Items" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "on", "off", @@ -222,17 +95,6 @@ }, "accessibility": { "type": "selectbox", - "label": { - "text": "Accessibility" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": [ "items", "locations", @@ -241,16 +103,7 @@ }, "sortingalgo": { "type": "selectbox", - "label": { - "text": "Item Sorting" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "balanced" }, "options": [ diff --git a/resources/app/gui/randomize/multiworld/widgets.json b/resources/app/gui/randomize/multiworld/widgets.json index b943c017..4038bed7 100644 --- a/resources/app/gui/randomize/multiworld/widgets.json +++ b/resources/app/gui/randomize/multiworld/widgets.json @@ -1,16 +1,3 @@ { - "worlds": { - "type": "spinbox", - "label": { - "text": "Worlds" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "spinbox": { - "side": "right" - } - } - } + "worlds": { "type": "spinbox" } } diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 6d8ae3b5..6db02220 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -40,7 +40,7 @@ def loadcliargs(gui, args, settings=None): gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) i = 0 for value in theseOptions["values"]: - gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options["labels"][i] = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + '.' + value) + gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options["labels"][i] = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + '.' + str(value)) i += 1 for i in range(0, len(theseOptions["values"])): gui.pages[mainpage].pages[subpage].widgets[widget].selectbox["menu"].entryconfigure(i, label=theseOptions["labels"][i]) diff --git a/source/gui/randomize/generation.py b/source/gui/randomize/generation.py index 58eb9b29..7bdd5cd8 100644 --- a/source/gui/randomize/generation.py +++ b/source/gui/randomize/generation.py @@ -25,6 +25,17 @@ def generation_page(parent,settings): self.widgets[key] = dictWidgets[key] self.widgets[key].pack(anchor=W) + self.frames["widgets"] = Frame(self) + self.frames["widgets"].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","widgets.json")) as items: + myDict = json.load(items) + dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"]) + for key in dictWidgets: + self.widgets[key] = dictWidgets[key] + self.widgets[key].pack(anchor=W) + self.frames["baserom"] = Frame(self) self.frames["baserom"].pack(anchor=W, fill=X) ## Locate base ROM diff --git a/source/gui/widgets.py b/source/gui/widgets.py index f5512c89..c96dca02 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -1,4 +1,4 @@ -from tkinter import Checkbutton, Entry, Frame, IntVar, Label, OptionMenu, Spinbox, StringVar, RIGHT, X +from tkinter import Checkbutton, Entry, Frame, IntVar, Label, OptionMenu, Spinbox, StringVar, LEFT, RIGHT, X from source.classes.Empty import Empty # Override Spinbox to include mousewheel support for changing value @@ -17,9 +17,9 @@ class mySpinbox(Spinbox): # Make a Checkbutton with a label def make_checkbox(self, parent, label, storageVar, manager, managerAttrs): - self = Frame(parent, name="checkframe-" + label.lower()) + self = Frame(parent) self.storageVar = storageVar - self.checkbox = Checkbutton(self, text=label, variable=self.storageVar, name="checkbox-" + label.lower()) + self.checkbox = Checkbutton(self, text=label, variable=self.storageVar) if managerAttrs is not None: self.checkbox.pack(managerAttrs) else: @@ -28,7 +28,7 @@ def make_checkbox(self, parent, label, storageVar, manager, managerAttrs): # Make an OptionMenu with a label and pretty option labels def make_selectbox(self, parent, label, options, storageVar, manager, managerAttrs): - self = Frame(parent, name="selectframe-" + label.lower()) + self = Frame(parent) labels = options @@ -88,48 +88,49 @@ def make_selectbox(self, parent, label, options, storageVar, manager, managerAtt if managerAttrs is not None and "label" in managerAttrs: self.label.pack(managerAttrs["label"]) else: - self.label.pack() + self.label.pack(side=LEFT) self.selectbox.config(width=20) idx = 0 default = self.selectbox.options["values"][idx] - if "default" in managerAttrs: + if managerAttrs is not None and "default" in managerAttrs: default = managerAttrs["default"] labels = self.selectbox.options["labels"] values = self.selectbox.options["values"] if default in values: idx = values.index(default) - self.labelVar.set(labels[idx]) + if not labels[idx] == "": + self.labelVar.set(labels[idx]) + self.selectbox["menu"].entryconfigure(idx,label=labels[idx]) self.storageVar.set(values[idx]) - self.selectbox["menu"].entryconfigure(idx,label=labels[idx]) if managerAttrs is not None and "selectbox" in managerAttrs: self.selectbox.pack(managerAttrs["selectbox"]) else: - self.selectbox.pack() + self.selectbox.pack(side=RIGHT) 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 = Frame(parent) self.storageVar = storageVar self.label = Label(self, text=label) if managerAttrs is not None and "label" in managerAttrs: self.label.pack(managerAttrs["label"]) else: - self.label.pack() + self.label.pack(side=LEFT) fromNum = 1 toNum = 100 - if "spinbox" in managerAttrs: + if managerAttrs is not None and "spinbox" in managerAttrs: if "from" in managerAttrs: fromNum = managerAttrs["spinbox"]["from"] if "to" in managerAttrs: toNum = managerAttrs["spinbox"]["to"] - self.spinbox = mySpinbox(self, from_=fromNum, to=toNum, width=5, textvariable=self.storageVar, name="spinbox-" + label.lower()) + self.spinbox = mySpinbox(self, from_=fromNum, to=toNum, width=5, textvariable=self.storageVar) if managerAttrs is not None and "spinbox" in managerAttrs: self.spinbox.pack(managerAttrs["spinbox"]) else: - self.spinbox.pack() + self.spinbox.pack(side=RIGHT) return self # Make an Entry box with a label From 755c401da11b3ff6997f85e8b9602185021f1c3d Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 12 Mar 2020 03:50:19 -0700 Subject: [PATCH 86/93] Slim down adjust & custom --- .../app/gui/adjust/overview/widgets.json | 65 +----- .../app/gui/custom/overview/widgets.json | 210 ------------------ source/gui/widgets.py | 2 + 3 files changed, 4 insertions(+), 273 deletions(-) diff --git a/resources/app/gui/adjust/overview/widgets.json b/resources/app/gui/adjust/overview/widgets.json index 30fe2445..87d5421a 100644 --- a/resources/app/gui/adjust/overview/widgets.json +++ b/resources/app/gui/adjust/overview/widgets.json @@ -1,32 +1,11 @@ { "checkboxes": { - "nobgm": { - "type": "checkbox", - "label": { - "text": "Disable Music & MSU-1" - } - }, - "quickswap": { - "type": "checkbox", - "label": { - "text": "L/R Quickswapping" - } - } + "nobgm": { "type": "checkbox" }, + "quickswap": { "type": "checkbox" } }, "leftAdjustFrame": { "heartcolor": { "type": "selectbox", - "label": { - "text": "Heart Color" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": { "Red": "red", "Blue": "blue", @@ -37,16 +16,7 @@ }, "heartbeep": { "type": "selectbox", - "label": { - "text": "Heart Beep sound rate" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "Normal" }, "options": { @@ -61,16 +31,7 @@ "rightAdjustFrame": { "menuspeed": { "type": "selectbox", - "label": { - "text": "Menu Speed" - }, "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - }, "default": "Normal" }, "options": { @@ -84,17 +45,6 @@ }, "owpalettes": { "type": "selectbox", - "label": { - "text": "Overworld Palettes" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": { "Default": "default", "Random": "random", @@ -103,17 +53,6 @@ }, "uwpalettes": { "type": "selectbox", - "label": { - "text": "Underworld Palettes" - }, - "managerAttrs": { - "label": { - "side": "left" - }, - "selectbox": { - "side": "right" - } - }, "options": { "Default": "default", "Random": "random", diff --git a/resources/app/gui/custom/overview/widgets.json b/resources/app/gui/custom/overview/widgets.json index faaa9637..24f9428e 100644 --- a/resources/app/gui/custom/overview/widgets.json +++ b/resources/app/gui/custom/overview/widgets.json @@ -7,9 +7,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -20,9 +17,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 2 } }, @@ -46,9 +40,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -59,9 +50,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -72,9 +60,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -85,9 +70,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -98,9 +80,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -111,9 +90,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -124,9 +100,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -137,9 +110,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -150,9 +120,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -163,9 +130,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -176,9 +140,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -189,9 +150,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } } @@ -204,9 +162,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -217,9 +172,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -230,9 +182,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -243,9 +192,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 4 } }, @@ -256,9 +202,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -269,9 +212,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -282,9 +222,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -295,9 +232,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -308,9 +242,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -321,9 +252,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -334,9 +262,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -347,9 +272,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 2 } }, @@ -360,9 +282,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -373,9 +292,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -386,9 +302,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 24 } } @@ -401,9 +314,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 10 } }, @@ -414,9 +324,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -427,9 +334,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -440,9 +344,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -453,9 +354,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -466,9 +364,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -479,9 +374,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 4 } }, @@ -492,9 +384,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -505,9 +394,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -518,9 +404,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -531,9 +414,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 3 } }, @@ -544,9 +424,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -557,9 +434,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -570,9 +444,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 2 } }, @@ -583,9 +454,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } } @@ -598,9 +466,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -611,9 +476,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -624,9 +486,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -637,9 +496,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -650,9 +506,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -663,9 +516,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -676,9 +526,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 12 } }, @@ -689,9 +536,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -702,9 +546,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 16 } }, @@ -715,9 +556,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } }, @@ -728,9 +566,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 2 } }, @@ -741,9 +576,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 4 } }, @@ -754,9 +586,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 28 } }, @@ -767,9 +596,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 7 } }, @@ -780,9 +606,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 1 } } @@ -795,9 +618,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 5 } }, @@ -808,9 +628,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -821,9 +638,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -834,9 +648,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -847,9 +658,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -860,9 +668,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -873,9 +678,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -886,9 +688,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -899,9 +698,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -912,9 +708,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 0 } }, @@ -925,9 +718,6 @@ }, "manager": "grid", "managerAttrs": { - "label": { - "sticky": "w" - }, "default": 10 } } diff --git a/source/gui/widgets.py b/source/gui/widgets.py index c96dca02..068bf076 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -146,6 +146,8 @@ def make_textbox(self, parent, label, storageVar, manager, managerAttrs): # grid if manager == "grid": widget.label.grid(managerAttrs["label"] if managerAttrs is not None and "label" in managerAttrs else None, row=parent.thisRow, column=parent.thisCol) + if managerAttrs is not None and "label" not in managerAttrs: + widget.label.grid_configure(sticky="w") parent.thisCol += 1 widget.textbox.grid(managerAttrs["textbox"] if managerAttrs is not None and "textbox" in managerAttrs else None, row=parent.thisRow, column=parent.thisCol) parent.thisRow += 1 From fc8ebad37f4d067f8ed237ba2e13b427641ec9da Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 12 Mar 2020 04:05:26 -0700 Subject: [PATCH 87/93] Promote default --- .../app/gui/adjust/overview/widgets.json | 8 +- .../app/gui/custom/overview/widgets.json | 289 +++++------------- .../app/gui/randomize/dungeon/widgets.json | 8 +- .../gui/randomize/gameoptions/widgets.json | 8 +- resources/app/gui/randomize/item/widgets.json | 8 +- source/gui/widgets.py | 12 + 6 files changed, 92 insertions(+), 241 deletions(-) diff --git a/resources/app/gui/adjust/overview/widgets.json b/resources/app/gui/adjust/overview/widgets.json index 87d5421a..f56bfd08 100644 --- a/resources/app/gui/adjust/overview/widgets.json +++ b/resources/app/gui/adjust/overview/widgets.json @@ -16,9 +16,7 @@ }, "heartbeep": { "type": "selectbox", - "managerAttrs": { - "default": "Normal" - }, + "default": "Normal", "options": { "Double": "double", "Normal": "normal", @@ -31,9 +29,7 @@ "rightAdjustFrame": { "menuspeed": { "type": "selectbox", - "managerAttrs": { - "default": "Normal" - }, + "default": "Normal", "options": { "Instant": "instant", "Quadruple": "quadruple", diff --git a/resources/app/gui/custom/overview/widgets.json b/resources/app/gui/custom/overview/widgets.json index 24f9428e..ee7be421 100644 --- a/resources/app/gui/custom/overview/widgets.json +++ b/resources/app/gui/custom/overview/widgets.json @@ -6,9 +6,7 @@ "text": "Bow" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "progressivebow": { "type": "textbox", @@ -16,9 +14,7 @@ "text": "Progressive Bow" }, "manager": "grid", - "managerAttrs": { - "default": 2 - } + "default": 2 }, "boomerang": { "type": "textbox", @@ -26,12 +22,7 @@ "text": "Blue Boomerang" }, "manager": "grid", - "managerAttrs": { - "label": { - "sticky": "w" - }, - "default": 1 - } + "default": 1 }, "redmerang": { "type": "textbox", @@ -39,9 +30,7 @@ "text": "Red Boomerang" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "hookshot": { "type": "textbox", @@ -49,9 +38,7 @@ "text": "Hookshot" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "mushroom": { "type": "textbox", @@ -59,9 +46,7 @@ "text": "Mushroom" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "powder": { "type": "textbox", @@ -69,9 +54,7 @@ "text": "Magic Powder" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "firerod": { "type": "textbox", @@ -79,9 +62,7 @@ "text": "Fire Rod" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "icerod": { "type": "textbox", @@ -89,9 +70,7 @@ "text": "Ice Rod" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "bombos": { "type": "textbox", @@ -99,9 +78,7 @@ "text": "Bombos" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "ether": { "type": "textbox", @@ -109,9 +86,7 @@ "text": "Ether" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "quake": { "type": "textbox", @@ -119,9 +94,7 @@ "text": "Quake" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "lamp": { "type": "textbox", @@ -129,9 +102,7 @@ "text": "Lamp" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "hammer": { "type": "textbox", @@ -139,9 +110,7 @@ "text": "Hammer" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "shovel": { "type": "textbox", @@ -149,9 +118,7 @@ "text": "Shovel" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 } }, "itemList2": { @@ -161,9 +128,7 @@ "text": "Flute" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "bugnet": { "type": "textbox", @@ -171,9 +136,7 @@ "text": "Bug Net" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "book": { "type": "textbox", @@ -181,9 +144,7 @@ "text": "Book" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "bottle": { "type": "textbox", @@ -191,9 +152,7 @@ "text": "Bottle" }, "manager": "grid", - "managerAttrs": { - "default": 4 - } + "default": 4 }, "somaria": { "type": "textbox", @@ -201,9 +160,7 @@ "text": "Cane of Somaria" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "byrna": { "type": "textbox", @@ -211,9 +168,7 @@ "text": "Cane of Byrna" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "cape": { "type": "textbox", @@ -221,9 +176,7 @@ "text": "Magic Cape" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "mirror": { "type": "textbox", @@ -231,9 +184,7 @@ "text": "Magic Mirror" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "boots": { "type": "textbox", @@ -241,9 +192,7 @@ "text": "Pegasus Boots" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "powerglove": { "type": "textbox", @@ -251,9 +200,7 @@ "text": "Power Glove" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "titansmitt": { "type": "textbox", @@ -261,9 +208,7 @@ "text": "Titan's Mitt" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "progressiveglove": { "type": "textbox", @@ -271,9 +216,7 @@ "text": "Progressive Glove" }, "manager": "grid", - "managerAttrs": { - "default": 2 - } + "default": 2 }, "flippers": { "type": "textbox", @@ -281,9 +224,7 @@ "text": "Flippers" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "pearl": { "type": "textbox", @@ -291,9 +232,7 @@ "text": "Moon Pearl" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "heartpiece": { "type": "textbox", @@ -301,9 +240,7 @@ "text": "Piece of Heart" }, "manager": "grid", - "managerAttrs": { - "default": 24 - } + "default": 24 } }, "itemList3": { @@ -313,9 +250,7 @@ "text": "Heart Container" }, "manager": "grid", - "managerAttrs": { - "default": 10 - } + "default": 10 }, "sancheart": { "type": "textbox", @@ -323,9 +258,7 @@ "text": "Sanctuary Heart" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "sword1": { "type": "textbox", @@ -333,9 +266,7 @@ "text": "Fighters' Sword" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "sword2": { "type": "textbox", @@ -343,9 +274,7 @@ "text": "Master Sword" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "sword3": { "type": "textbox", @@ -353,9 +282,7 @@ "text": "Tempered Sword" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "sword4": { "type": "textbox", @@ -363,9 +290,7 @@ "text": "Golden Sword" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "progressivesword": { "type": "textbox", @@ -373,9 +298,7 @@ "text": "Progressive Sword" }, "manager": "grid", - "managerAttrs": { - "default": 4 - } + "default": 4 }, "shield1": { "type": "textbox", @@ -383,9 +306,7 @@ "text": "Fighters' Shield" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "shield2": { "type": "textbox", @@ -393,9 +314,7 @@ "text": "Fire Shield" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "shield3": { "type": "textbox", @@ -403,9 +322,7 @@ "text": "Mirror Shield" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "progressiveshield": { "type": "textbox", @@ -413,9 +330,7 @@ "text": "Progressive Shield" }, "manager": "grid", - "managerAttrs": { - "default": 3 - } + "default": 3 }, "mail2": { "type": "textbox", @@ -423,9 +338,7 @@ "text": "Blue Mail" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "mail3": { "type": "textbox", @@ -433,9 +346,7 @@ "text": "Red Mail" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "progressivemail": { "type": "textbox", @@ -443,9 +354,7 @@ "text": "Progressive Mail" }, "manager": "grid", - "managerAttrs": { - "default": 2 - } + "default": 2 }, "halfmagic": { "type": "textbox", @@ -453,9 +362,7 @@ "text": "Half Magic" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 } }, "itemList4": { @@ -465,9 +372,7 @@ "text": "Quarter Magic" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "bombsplus5": { "type": "textbox", @@ -475,9 +380,7 @@ "text": "Bomb Cap +5" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "bombsplus10": { "type": "textbox", @@ -485,9 +388,7 @@ "text": "Bomb Cap +10" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "arrowsplus5": { "type": "textbox", @@ -495,9 +396,7 @@ "text": "Arrow Cap +5" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "arrowsplus10": { "type": "textbox", @@ -505,9 +404,7 @@ "text": "Arrow Cap +10" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "arrow1": { "type": "textbox", @@ -515,9 +412,7 @@ "text": "Arrow (1)" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "arrow10": { "type": "textbox", @@ -525,9 +420,7 @@ "text": "Arrow (10)" }, "manager": "grid", - "managerAttrs": { - "default": 12 - } + "default": 12 }, "bomb1": { "type": "textbox", @@ -535,9 +428,7 @@ "text": "Bomb (1)" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "bomb3": { "type": "textbox", @@ -545,9 +436,7 @@ "text": "Bomb (3)" }, "manager": "grid", - "managerAttrs": { - "default": 16 - } + "default": 16 }, "bomb10": { "type": "textbox", @@ -555,9 +444,7 @@ "text": "Bomb (10)" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 }, "rupee1": { "type": "textbox", @@ -565,9 +452,7 @@ "text": "Rupee (1)" }, "manager": "grid", - "managerAttrs": { - "default": 2 - } + "default": 2 }, "rupee5": { "type": "textbox", @@ -575,9 +460,7 @@ "text": "Rupee (5)" }, "manager": "grid", - "managerAttrs": { - "default": 4 - } + "default": 4 }, "rupee20": { "type": "textbox", @@ -585,9 +468,7 @@ "text": "Rupee (20)" }, "manager": "grid", - "managerAttrs": { - "default": 28 - } + "default": 28 }, "rupee50": { "type": "textbox", @@ -595,9 +476,7 @@ "text": "Rupee (50)" }, "manager": "grid", - "managerAttrs": { - "default": 7 - } + "default": 7 }, "rupee100": { "type": "textbox", @@ -605,9 +484,7 @@ "text": "Rupee (100)" }, "manager": "grid", - "managerAttrs": { - "default": 1 - } + "default": 1 } }, "itemList5": { @@ -617,9 +494,7 @@ "text": "Rupee (300)" }, "manager": "grid", - "managerAttrs": { - "default": 5 - } + "default": 5 }, "blueclock": { "type": "textbox", @@ -627,9 +502,7 @@ "text": "Blue Clock" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "greenclock": { "type": "textbox", @@ -637,9 +510,7 @@ "text": "Green Clock" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "redclock": { "type": "textbox", @@ -647,9 +518,7 @@ "text": "Red Clock" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "silversupgrade": { "type": "textbox", @@ -657,9 +526,7 @@ "text": "Silver Arrows Upgrade" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "generickeys": { "type": "textbox", @@ -667,9 +534,7 @@ "text": "Generic Keys" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "triforcepieces": { "type": "textbox", @@ -677,9 +542,7 @@ "text": "Triforce Pieces" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "triforcepiecesgoal": { "type": "textbox", @@ -687,9 +550,7 @@ "text": "Triforce Pieces Goal" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "triforce": { "type": "textbox", @@ -697,9 +558,7 @@ "text": "Triforce (win game)" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "rupoor": { "type": "textbox", @@ -707,9 +566,7 @@ "text": "Rupoor" }, "manager": "grid", - "managerAttrs": { - "default": 0 - } + "default": 0 }, "rupoorcost": { "type": "textbox", @@ -717,9 +574,7 @@ "text": "Rupoor Cost" }, "manager": "grid", - "managerAttrs": { - "default": 10 - } + "default": 10 } } -} \ No newline at end of file +} diff --git a/resources/app/gui/randomize/dungeon/widgets.json b/resources/app/gui/randomize/dungeon/widgets.json index 91ef1f3e..3cbd7962 100644 --- a/resources/app/gui/randomize/dungeon/widgets.json +++ b/resources/app/gui/randomize/dungeon/widgets.json @@ -1,9 +1,7 @@ { "dungeondoorshuffle": { "type": "selectbox", - "managerAttrs": { - "default": "basic" - }, + "default": "basic", "options": [ "vanilla", "basic", @@ -13,9 +11,7 @@ "experimental": { "type": "checkbox" }, "dungeon_counters": { "type": "selectbox", - "managerAttrs": { - "default": "default" - }, + "default": "default", "options": [ "default", "off", diff --git a/resources/app/gui/randomize/gameoptions/widgets.json b/resources/app/gui/randomize/gameoptions/widgets.json index ac187f86..815f72b1 100644 --- a/resources/app/gui/randomize/gameoptions/widgets.json +++ b/resources/app/gui/randomize/gameoptions/widgets.json @@ -20,9 +20,7 @@ }, "heartbeep": { "type": "selectbox", - "managerAttrs": { - "default": "normal" - }, + "default": "normal", "options": [ "double", "normal", @@ -35,9 +33,7 @@ "rightRomOptionsFrame": { "menuspeed": { "type": "selectbox", - "managerAttrs": { - "default": "normal" - }, + "default": "normal", "options": [ "instant", "quadruple", diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 6a3ebbaa..d485cdb8 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -5,9 +5,7 @@ "leftItemFrame": { "worldstate": { "type": "selectbox", - "managerAttrs": { - "default": "open" - }, + "default": "open", "options": [ "standard", "open", @@ -103,9 +101,7 @@ }, "sortingalgo": { "type": "selectbox", - "managerAttrs": { - "default": "balanced" - }, + "default": "balanced", "options": [ "freshness", "flood", diff --git a/source/gui/widgets.py b/source/gui/widgets.py index 068bf076..d3915c6f 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -19,6 +19,12 @@ class mySpinbox(Spinbox): def make_checkbox(self, parent, label, storageVar, manager, managerAttrs): self = Frame(parent) self.storageVar = storageVar + if managerAttrs is not None and "default" in managerAttrs: + if managerAttrs["default"] == "true" or managerAttrs["default"] == True: + self.storageVar.set(True) + elif managerAttrs["default"] == "false" or managerAttrs["default"] == False: + self.storageVar.set(False) + del managerAttrs["default"] self.checkbox = Checkbutton(self, text=label, variable=self.storageVar) if managerAttrs is not None: self.checkbox.pack(managerAttrs) @@ -197,6 +203,12 @@ def make_widget_from_dict(self, defn, parent): manager = defn["manager"] if "manager" in defn else None managerAttrs = defn["managerAttrs"] if "managerAttrs" in defn else None options = defn["options"] if "options" in defn else None + + if managerAttrs is None and "default" in defn: + managerAttrs = {} + if "default" in defn: + managerAttrs["default"] = defn["default"] + widget = make_widget(self, type, parent, label, None, manager, managerAttrs, options) widget.type = type return widget From 4b0f30a85f650225edc44b37063f8cd9ec0c1ad7 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 12 Mar 2020 16:16:15 -0700 Subject: [PATCH 88/93] Implement prelim enums --- resources/app/gui/lang/en.json | 6 +- .../app/gui/randomize/dungeon/keysanity.json | 10 +- .../app/gui/randomize/dungeon/widgets.json | 40 ++++--- .../gui/randomize/generation/checkboxes.json | 10 +- .../app/gui/randomize/generation/widgets.json | 16 +-- .../app/gui/randomize/multiworld/widgets.json | 4 +- .../app/meta/manifests/pip_requirements.txt | 1 + source/classes/BabelFish.py | 4 +- test-options.py | 109 ++++++++++++++++++ 9 files changed, 160 insertions(+), 40 deletions(-) create mode 100644 test-options.py diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index fcb28072..d67963e5 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -155,9 +155,9 @@ "randomizer.generation.spoiler": "Create Spoiler Log", - "randomizer.generation.suppressrom": "Do not create patched ROM", - "randomizer.generation.usestartinventory": "Use starting inventory", - "randomizer.generation.usecustompool": "Use custom item pool", + "randomizer.generation.suppressrom": "Do Not create Patched ROM", + "randomizer.generation.usestartinventory": "Use Starting Inventory", + "randomizer.generation.usecustompool": "Use Custom Item Pool", "randomizer.generation.saveonexit": "Save Settings on Exit", "randomizer.generation.saveonexit.ask": "Ask Me", diff --git a/resources/app/gui/randomize/dungeon/keysanity.json b/resources/app/gui/randomize/dungeon/keysanity.json index 29efc0e4..49a17237 100644 --- a/resources/app/gui/randomize/dungeon/keysanity.json +++ b/resources/app/gui/randomize/dungeon/keysanity.json @@ -1,6 +1,8 @@ { - "mapshuffle": { "type": "checkbox" }, - "compassshuffle": { "type": "checkbox" }, - "smallkeyshuffle": { "type": "checkbox" }, - "bigkeyshuffle": { "type": "checkbox" } + "keysanity": { + "mapshuffle": { "type": "checkbox" }, + "compassshuffle": { "type": "checkbox" }, + "smallkeyshuffle": { "type": "checkbox" }, + "bigkeyshuffle": { "type": "checkbox" } + } } diff --git a/resources/app/gui/randomize/dungeon/widgets.json b/resources/app/gui/randomize/dungeon/widgets.json index 3cbd7962..04082822 100644 --- a/resources/app/gui/randomize/dungeon/widgets.json +++ b/resources/app/gui/randomize/dungeon/widgets.json @@ -1,22 +1,24 @@ { - "dungeondoorshuffle": { - "type": "selectbox", - "default": "basic", - "options": [ - "vanilla", - "basic", - "crossed" - ] - }, - "experimental": { "type": "checkbox" }, - "dungeon_counters": { - "type": "selectbox", - "default": "default", - "options": [ - "default", - "off", - "on", - "pickup" - ] + "widgets": { + "dungeondoorshuffle": { + "type": "selectbox", + "default": "basic", + "options": [ + "vanilla", + "basic", + "crossed" + ] + }, + "experimental": { "type": "checkbox" }, + "dungeon_counters": { + "type": "selectbox", + "default": "default", + "options": [ + "default", + "off", + "on", + "pickup" + ] + } } } diff --git a/resources/app/gui/randomize/generation/checkboxes.json b/resources/app/gui/randomize/generation/checkboxes.json index d609e866..5839e0d4 100644 --- a/resources/app/gui/randomize/generation/checkboxes.json +++ b/resources/app/gui/randomize/generation/checkboxes.json @@ -1,6 +1,8 @@ { - "spoiler": { "type": "checkbox" }, - "suppressrom": { "type": "checkbox" }, - "usestartinventory": { "type": "checkbox" }, - "usecustompool": { "type": "checkbox" } + "checkboxes": { + "spoiler": { "type": "checkbox" }, + "suppressrom": { "type": "checkbox" }, + "usestartinventory": { "type": "checkbox" }, + "usecustompool": { "type": "checkbox" } + } } diff --git a/resources/app/gui/randomize/generation/widgets.json b/resources/app/gui/randomize/generation/widgets.json index d22de592..22f9decc 100644 --- a/resources/app/gui/randomize/generation/widgets.json +++ b/resources/app/gui/randomize/generation/widgets.json @@ -1,10 +1,12 @@ { - "saveonexit": { - "type": "selectbox", - "options": [ - "ask", - "always", - "never" - ] + "widgets": { + "saveonexit": { + "type": "selectbox", + "options": [ + "ask", + "always", + "never" + ] + } } } diff --git a/resources/app/gui/randomize/multiworld/widgets.json b/resources/app/gui/randomize/multiworld/widgets.json index 4038bed7..327b9434 100644 --- a/resources/app/gui/randomize/multiworld/widgets.json +++ b/resources/app/gui/randomize/multiworld/widgets.json @@ -1,3 +1,5 @@ { - "worlds": { "type": "spinbox" } + "widgets": { + "worlds": { "type": "spinbox" } + } } diff --git a/resources/app/meta/manifests/pip_requirements.txt b/resources/app/meta/manifests/pip_requirements.txt index e69de29b..93e06fa8 100644 --- a/resources/app/meta/manifests/pip_requirements.txt +++ b/resources/app/meta/manifests/pip_requirements.txt @@ -0,0 +1 @@ +aenum diff --git a/source/classes/BabelFish.py b/source/classes/BabelFish.py index 3be79c09..40b6463f 100644 --- a/source/classes/BabelFish.py +++ b/source/classes/BabelFish.py @@ -38,7 +38,7 @@ class BabelFish(): pass # print(langs_filename + " not found for translation!") - def translate(self, domain="", key="", subkey=""): #three levels of keys + def translate(self, domain="", key="", subkey="", uselang=None): #three levels of keys # start with nothing display_text = "" @@ -94,7 +94,7 @@ class BabelFish(): subkey = " - ".join(tmp) subkey = subkey.strip() - my_lang = self.lang_defns[self.locale] #handle for localization + my_lang = self.lang_defns[uselang if uselang is not None else self.locale ] #handle for localization en_lang = self.lang_defns["en"] #handle for English if domain in my_lang and key in my_lang[domain] and subkey in my_lang[domain][key] and not my_lang[domain][key][subkey] == "": #get localization first diff --git a/test-options.py b/test-options.py new file mode 100644 index 00000000..60386860 --- /dev/null +++ b/test-options.py @@ -0,0 +1,109 @@ +from __future__ import annotations +from aenum import Enum, IntEnum, extend_enum +from source.classes.BabelFish import BabelFish +import json +import os + +fish = BabelFish(lang="en") + +def tokenize(token): + for search,replace in ( + ('(', ""), + (')',""), + ("'", ""), + ('-'," "), + ('/',""), + ("\\","") + ): + token = token.replace(search, replace) + tokens = token.split(" ") + i = 0 + for check in tokens: + if check.lower() == check: + tokens[i] = "" + i += 1 + return " ".join(tokens).replace(" ","") + +class Toggle(IntEnum): + Off = 0 + On = 1 + + @classmethod + def from_text(cls, text: str) -> Toggle: + if text.lower() in {"off", "0", "false", "none", "null"}: + return Toggle.Off + else: + return Toggle.On + +class Choice(IntEnum): + @classmethod + def from_text(cls, text: str) -> Choice: + for option in cls: + if option.name == text.upper(): + return option + raise KeyError( + 'KeyError: Could not find option "%s" for "%s", known options are %s' % + ( + text, + cls.__name__, + ", ".join(option.name for option in cls) + ) + ) + +def create_choice(option_name,option_vals): + option = type(option_name,(Choice,),{}) + for name in option_vals: + extend_enum(option,str(name).upper(),len(option)) + return option + +def load_options(filepath): + theseCompiled = {} + with open(filepath) as widgetsDefn: + filepath = filepath.split(os.sep) + domain = filepath[3] + key = filepath[4] + theseOptions = json.load(widgetsDefn) + for section in theseOptions: + widgets = theseOptions[section] + for widget in widgets: + thisWidget = widgets[widget] + if domain == "randomize": + domain = "randomizer" + if key == "entrando": + key = "entrance" + fish_key = domain + '.' + key + '.' + widget + option_name = tokenize(fish.translate("gui","gui",fish_key,"en")) + if thisWidget["type"] == "checkbox": + theseCompiled[option_name] = Toggle + elif thisWidget["type"] == "selectbox": + option_vals = thisWidget["options"] + theseCompiled[option_name] = create_choice(option_name,option_vals) + return theseCompiled + + +if __name__ == "__main__": + import argparse + + compiledOptions = {} + notebooks = { + "randomize": [ "dungeon", "enemizer", "entrando", "gameoptions", "generation", "item", "multiworld" ] + } + for notebook in notebooks: + for page in notebooks[notebook]: + for filename in ["keysanity","checkboxes","widgets"]: + defn = os.path.join("resources", "app", "gui", notebook, page, filename + ".json") + if os.path.isfile(defn): + compiledOptions.update(load_options(defn)) + + test = argparse.Namespace() + test.logic = compiledOptions["LogicLevel"].from_text("nologic") + test.mapshuffle = compiledOptions["Maps"].from_text("ON") + try: + test.logic = compiledOptions["LogicLevel"].from_text("overworldglitches") + except KeyError as e: + print(e) + if test.mapshuffle: + print("Map Shuffle is on") + print(test) + for option in compiledOptions: + print("%s: %s" % (option, list(compiledOptions[option]))) From f28b1d15c66d9ca9a2436840ad825819b4f8c4e4 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 12 Mar 2020 16:47:39 -0700 Subject: [PATCH 89/93] Fix widget loader --- source/gui/randomize/dungeon.py | 2 ++ source/gui/randomize/generation.py | 2 ++ source/gui/randomize/multiworld.py | 1 + 3 files changed, 5 insertions(+) diff --git a/source/gui/randomize/dungeon.py b/source/gui/randomize/dungeon.py index 1d36c021..01985982 100644 --- a/source/gui/randomize/dungeon.py +++ b/source/gui/randomize/dungeon.py @@ -24,6 +24,7 @@ def dungeon_page(parent): # 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) + myDict = myDict["keysanity"] dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["keysanity"]) for key in dictWidgets: self.widgets[key] = dictWidgets[key] @@ -34,6 +35,7 @@ def dungeon_page(parent): self.frames["widgets"].pack(anchor=W) with open(os.path.join("resources","app","gui","randomize","dungeon","widgets.json")) as dungeonWidgets: myDict = json.load(dungeonWidgets) + myDict = myDict["widgets"] dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"]) for key in dictWidgets: self.widgets[key] = dictWidgets[key] diff --git a/source/gui/randomize/generation.py b/source/gui/randomize/generation.py index 7bdd5cd8..f358c864 100644 --- a/source/gui/randomize/generation.py +++ b/source/gui/randomize/generation.py @@ -20,6 +20,7 @@ def generation_page(parent,settings): # 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) + myDict = myDict["checkboxes"] dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["checkboxes"]) for key in dictWidgets: self.widgets[key] = dictWidgets[key] @@ -31,6 +32,7 @@ def generation_page(parent,settings): # Defns include frame name, widget type, widget options, widget placement attributes with open(os.path.join("resources","app","gui","randomize","generation","widgets.json")) as items: myDict = json.load(items) + myDict = myDict["widgets"] dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"]) for key in dictWidgets: self.widgets[key] = dictWidgets[key] diff --git a/source/gui/randomize/multiworld.py b/source/gui/randomize/multiworld.py index 369c1b6d..493f31b5 100644 --- a/source/gui/randomize/multiworld.py +++ b/source/gui/randomize/multiworld.py @@ -20,6 +20,7 @@ def multiworld_page(parent,settings): # 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) + myDict = myDict["widgets"] dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"]) for key in dictWidgets: self.widgets[key] = dictWidgets[key] From 8513b7f270ff890e4b41c742df08de4c1aa8d816 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Sun, 22 Mar 2020 02:53:40 -0700 Subject: [PATCH 90/93] Fix var loading again Update Deprecated Args Parse CLI Parse Settings Fix defaults Fix priority Add Enemizer error Fix Custom Item Array again Make output more verbose Fix double-negative options --- CLI.py | 69 ++++++++++++------- DungeonRandomizer.py | 14 ++-- Gui.py | 16 ++--- ItemList.py | 10 +-- Main.py | 26 ++++--- Mystery.py | 4 +- Rom.py | 10 +-- Utils.py | 32 +++++++++ resources/app/cli/args.json | 40 +++++++---- resources/app/cli/lang/en.json | 8 ++- resources/app/gui/lang/en.json | 3 +- .../gui/randomize/generation/checkboxes.json | 3 +- source/classes/constants.py | 3 +- source/gui/bottom.py | 23 +++++-- source/gui/loadcliargs.py | 9 ++- 15 files changed, 185 insertions(+), 85 deletions(-) diff --git a/CLI.py b/CLI.py index 0565f570..32879689 100644 --- a/CLI.py +++ b/CLI.py @@ -11,27 +11,22 @@ import sys import source.classes.constants as CONST from source.classes.BabelFish import BabelFish +from Utils import update_deprecated_args + class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter): def _get_help_string(self, action): return textwrap.dedent(action.help) -def parse_arguments(argv, no_defaults=False): +def parse_cli(argv, no_defaults=False): def defval(value): return value if not no_defaults else None # get settings - settings = get_settings() + settings = parse_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 @@ -57,13 +52,18 @@ def parse_arguments(argv, no_defaults=False): 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]) + 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) @@ -87,7 +87,7 @@ def parse_arguments(argv, no_defaults=False): 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) + playerargs = parse_cli(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', @@ -105,7 +105,7 @@ def parse_arguments(argv, no_defaults=False): return ret -def get_settings(): +def parse_settings(): # set default settings settings = { "lang": "en", @@ -151,14 +151,16 @@ def get_settings(): "quickswap": False, "heartcolor": "red", "heartbeep": "normal", - "sprite": None, + "sprite": os.path.join(".","data","sprites","official","001.link.1.zspr"), "fastmenu": "normal", "ow_palettes": "default", "uw_palettes": "default", "create_spoiler": False, "skip_playthrough": False, + "calc_playthrough": True, "suppress_rom": False, + "create_rom": True, "usestartinventory": False, "custom": False, "rom": os.path.join(".", "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"), @@ -267,8 +269,8 @@ def get_settings(): # 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["settings"] = parse_settings() if settings_args is None else settings_args + args["gui"] = gui_args args["cli"] = cli_args args["load"] = args["settings"] @@ -279,17 +281,38 @@ def get_args_priority(settings_args, gui_args, cli_args): if args["cli"] is None: args["cli"] = {} - cli = vars(parse_arguments(None)) + cli = vars(parse_cli(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] + args["cli"] = argparse.Namespace(**args["cli"]) + + cli = vars(args["cli"]) + 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: + 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 + if load_doesnt_have_key or different_val: + if not cli_has_empty_dict: + args["load"][k] = cli_val + + newArgs = {} + for key in [ "settings", "gui", "cli", "load" ]: + if args[key]: + if isinstance(args[key],dict): + newArgs[key] = argparse.Namespace(**args[key]) + else: + newArgs[key] = args[key] + + newArgs[key] = update_deprecated_args(newArgs[key]) + else: + newArgs[key] = args[key] + + args = newArgs return args diff --git a/DungeonRandomizer.py b/DungeonRandomizer.py index 4a0c04e3..ca008a96 100755 --- a/DungeonRandomizer.py +++ b/DungeonRandomizer.py @@ -10,14 +10,14 @@ import sys from source.classes.BabelFish import BabelFish -from CLI import parse_arguments, get_args_priority -from Main import main +from CLI import parse_cli, get_args_priority +from Main import main, EnemizerError from Rom import get_sprite_from_name from Utils import is_bundled, close_console from Fill import FillError def start(): - args = parse_arguments(None) + args = parse_cli(None) if is_bundled() and len(sys.argv) == 1: # for the bundled builds, if we have no arguments, the user @@ -44,10 +44,10 @@ def start(): loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[args.loglevel] logging.basicConfig(format='%(message)s', level=loglevel) - settings = get_args_priority(None, None, None) + priority = get_args_priority(None, None, args) lang = "en" - if "load" in settings and "lang" in settings["load"]: - lang = settings["load"]["lang"] + if "load" in priority and "lang" in priority["load"]: + lang = priority["load"].lang fish = BabelFish(lang=lang) if args.gui: @@ -61,7 +61,7 @@ def start(): try: main(seed=seed, args=args, fish=fish) logger.info('%s %s', fish.translate("cli","cli","finished.run"), _+1) - except (FillError, Exception, RuntimeError) as err: + except (FillError, EnemizerError, Exception, RuntimeError) as err: failures.append((err, seed)) logger.warning('%s: %s', fish.translate("cli","cli","generation.failed"), err) seed = random.randint(0, 999999999) diff --git a/Gui.py b/Gui.py index ebe39f33..66fd4999 100755 --- a/Gui.py +++ b/Gui.py @@ -5,7 +5,7 @@ import sys from tkinter import Tk, Button, BOTTOM, TOP, StringVar, BooleanVar, X, BOTH, RIGHT, ttk, messagebox from CLI import get_args_priority -from DungeonRandomizer import parse_arguments +from DungeonRandomizer import parse_cli from source.gui.adjust.overview import adjust_page from source.gui.startinventory.overview import startinventory_page from source.gui.custom.overview import custom_page @@ -77,9 +77,13 @@ def guiMain(args=None): # get args # getting Settings & CLI (no GUI built yet) self.args = get_args_priority(None, None, None) + lang = "en" + if "load" in self.args and "lang" in self.args["load"]: + lang = self.args["load"].lang + self.fish = BabelFish(lang=lang) # get saved settings - self.settings = self.args["settings"] + self.settings = vars(self.args["settings"]) # make array for pages self.pages = {} @@ -146,12 +150,6 @@ def guiMain(args=None): # add randomizer notebook to main window self.pages["randomizer"].notebook.pack() - settings = get_args_priority(None, None, None) - lang = "en" - if "load" in settings and "lang" in settings["load"]: - lang = settings["load"]["lang"] - self.fish = BabelFish(lang=lang) - # bottom of window: Open Output Directory, Open Documentation (if exists) self.pages["bottom"] = Empty() self.pages["bottom"].pages = {} @@ -196,5 +194,5 @@ def guiMain(args=None): if __name__ == '__main__': - args = parse_arguments(None) + args = parse_cli(None) guiMain(args) diff --git a/ItemList.py b/ItemList.py index ffb6579e..27c0b384 100644 --- a/ItemList.py +++ b/ItemList.py @@ -258,7 +258,7 @@ def generate_itempool(world, player): # set up item pool if world.custom: (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.customitemarray) - world.rupoor_cost = min(world.customitemarray["rupoorcost"], 9999) + world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) else: (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.doorShuffle[player]) @@ -322,9 +322,9 @@ def generate_itempool(world, player): # logic has some branches where having 4 hearts is one possible requirement (of several alternatives) # rather than making all hearts/heart pieces progression items (which slows down generation considerably) # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode) - if world.difficulty[player] in ['normal', 'hard'] and not (world.custom and world.customitemarray["heartcontainer"] == 0): + if world.difficulty[player] in ['normal', 'hard'] and not (world.custom and world.customitemarray[player]["heartcontainer"] == 0): [item for item in items if item.name == 'Boss Heart Container'][0].advancement = True - elif world.difficulty[player] in ['expert'] and not (world.custom and world.customitemarray["heartpiece"] < 4): + elif world.difficulty[player] in ['expert'] and not (world.custom and world.customitemarray[player]["heartpiece"] < 4): adv_heart_pieces = [item for item in items if item.name == 'Piece of Heart'][0:4] for hp in adv_heart_pieces: hp.advancement = True @@ -601,6 +601,8 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray): + if isinstance(customitemarray,dict) and 1 in customitemarray: + customitemarray = customitemarray[1] pool = [] placed_items = {} precollected_items = [] @@ -698,7 +700,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode if itemtotal < total_items_to_place: nothings = total_items_to_place - itemtotal - print("Placing " + str(nothings) + " Nothings") +# print("Placing " + str(nothings) + " Nothings") pool.extend(['Nothing'] * nothings) return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) diff --git a/Main.py b/Main.py index 6ccb1719..407af108 100644 --- a/Main.py +++ b/Main.py @@ -26,6 +26,8 @@ from Utils import output_path, parse_player_names, print_wiki_doors_by_region, p __version__ = '0.0.18.6d' +class EnemizerError(RuntimeError): + pass def main(args, seed=None, fish=None): if args.outputpath: @@ -180,13 +182,12 @@ def main(args, seed=None, fish=None): if not world.can_beat_game(): raise RuntimeError(world.fish.translate("cli","cli","cannot.beat.game")) - logger.info(world.fish.translate("cli","cli","patching.rom")) - outfilebase = 'DR_%s' % (args.outputname if args.outputname else world.seed) rom_names = [] jsonout = {} if not args.suppress_rom: + logger.info(world.fish.translate("cli","cli","patching.rom")) for team in range(world.teams): for player in range(1, world.players + 1): sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit' @@ -199,13 +200,17 @@ def main(args, seed=None, fish=None): patch_rom(world, rom, player, team, use_enemizer) if use_enemizer and (args.enemizercli or not args.jsonout): + if args.rom and not(os.path.isfile(args.rom)): + raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom) if os.path.exists(args.enemizercli): patch_enemizer(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player], sprite_random_on_hit) if not args.jsonout: rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) else: - logging.warning(world.fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli) - logging.warning(world.fish.translate("cli","cli","enemizer.nothing.applied")) + enemizerMsg = world.fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli + "\n" + enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied") + logging.warning(enemizerMsg) + raise EnemizerError(enemizerMsg) if args.race: patch_race_rom(rom) @@ -255,19 +260,24 @@ def main(args, seed=None, fish=None): with open(output_path('%s_multidata' % outfilebase), 'wb') as f: f.write(multidata) - if args.create_spoiler and not args.jsonout: - world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) - if not args.skip_playthrough: logger.info(world.fish.translate("cli","cli","calc.playthrough")) create_playthrough(world) if args.jsonout: print(json.dumps({**jsonout, 'spoiler': world.spoiler.to_json()})) - elif args.create_spoiler and not args.skip_playthrough: + elif args.create_spoiler: + logger.info(world.fish.translate("cli","cli","patching.spoiler")) world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) + YES = world.fish.translate("cli","cli","yes") + NO = world.fish.translate("cli","cli","no") + logger.info("") logger.info(world.fish.translate("cli","cli","done")) + logger.info("") + logger.info(world.fish.translate("cli","cli","made.rom") % (YES if (args.create_rom) else NO)) + logger.info(world.fish.translate("cli","cli","made.playthrough") % (YES if (args.calc_playthrough) else NO)) + logger.info(world.fish.translate("cli","cli","made.spoiler") % (YES if (not args.jsonout and args.create_spoiler) else NO)) logger.info(world.fish.translate("cli","cli","seed") + ": %d", world.seed) logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start) diff --git a/Mystery.py b/Mystery.py index d175be3e..f42123fd 100644 --- a/Mystery.py +++ b/Mystery.py @@ -5,7 +5,7 @@ import urllib.request import urllib.parse import re -from DungeonRandomizer import parse_arguments +from DungeonRandomizer import parse_cli from Main import main as DRMain def parse_yaml(txt): @@ -71,7 +71,7 @@ def main(): weights_cache[path] = get_weights(path) print(f"P{player} Weights: {path} >> {weights_cache[path]['description']}") - erargs = parse_arguments(['--multi', str(args.multi)]) + erargs = parse_cli(['--multi', str(args.multi)]) erargs.seed = seed erargs.names = args.names erargs.create_spoiler = args.create_spoiler diff --git a/Rom.py b/Rom.py index 7f74a53f..b2344e7f 100644 --- a/Rom.py +++ b/Rom.py @@ -78,6 +78,8 @@ class LocalRom(object): self.name = name self.hash = hash self.orig_buffer = None + if not os.path.isfile(file): + raise RuntimeError("Could not find valid local base rom for patching at expected path %s." % args.rom) with open(file, 'rb') as stream: self.buffer = read_rom(stream) if patch: @@ -759,10 +761,10 @@ def patch_rom(world, rom, player, team, enemized): difficulty.progressive_shield_limit, overflow_replacement, difficulty.progressive_armor_limit, overflow_replacement, difficulty.progressive_bottle_limit, overflow_replacement]) - + #Work around for json patch ordering issues - write bow limit separately so that it is replaced in the patch rom.write_bytes(0x180098, [difficulty.progressive_bow_limit, overflow_replacement]) - + if difficulty.progressive_bow_limit < 2 and world.swords == 'swordless': rom.write_bytes(0x180098, [2, overflow_replacement]) rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon @@ -2089,9 +2091,9 @@ def patch_shuffled_dark_sanc(world, rom, player): dark_sanc_entrance = str(world.get_region('Inverted Dark Sanctuary', player).entrances[0].name) room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[dark_sanc_entrance][1] door_index = door_addresses[str(dark_sanc_entrance)][0] - + rom.write_byte(0x180241, 0x01) - rom.write_byte(0x180248, door_index + 1) + rom.write_byte(0x180248, door_index + 1) write_int16(rom, 0x180250, room_id) rom.write_byte(0x180252, ow_area) write_int16s(rom, 0x180253, [vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x]) diff --git a/Utils.py b/Utils.py index 72d02161..63670d78 100644 --- a/Utils.py +++ b/Utils.py @@ -248,6 +248,38 @@ def print_wiki_doors_by_region(d_regions, world, player): with open(os.path.join(".","resources", "user", "regions-" + d + ".txt"),"w+") as f: f.write(toprint) +def update_deprecated_args(args): + argVars = vars(args) + truthy = [ 1, True, "True", "true" ] + # Don't do: Yes + # Do: No + if "suppress_rom" in argVars: + args.create_rom = args.suppress_rom not in truthy + # Don't do: No + # Do: Yes + if "create_rom" in argVars: + args.suppress_rom = not args.create_rom in truthy + + # Don't do: Yes + # Do: No + if "no_shuffleganon" in argVars: + args.shuffleganon = not args.no_shuffleganon in truthy + # Don't do: No + # Do: Yes + if "shuffleganon" in argVars: + args.no_shuffleganon = not args.shuffleganon in truthy + + # Don't do: Yes + # Do: No + if "skip_playthrough" in argVars: + args.calc_playthrough = not args.skip_playthrough in truthy + # Don't do: No + # Do: Yes + if "calc_playthrough" in argVars: + args.skip_playthrough = not args.calc_playthrough in truthy + + return args + def print_wiki_doors_by_room(d_regions, world, player): for d, region_list in d_regions.items(): tile_map = {} diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index b991620f..cc639ab5 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -107,10 +107,10 @@ }, "dungeon_counters": { "choices": [ + "default", "off", "on", - "pickup", - "default" + "pickup" ] }, "crystals_ganon": { @@ -198,15 +198,6 @@ "action": "store_true", "type": "bool" }, - "shuffleganon": { - "action": "store_true", - "type": "bool" - }, - "no-shuffleganon": { - "action": "store_false", - "dest": "shuffleganon", - "help": "suppress" - }, "heartbeep": { "choices": [ "normal", @@ -240,20 +231,39 @@ ] }, "sprite": {}, + "create_rom": { + "action": "store_false", + "type": "bool" + }, "suppress_rom": { "action": "store_true", + "dest": "create_rom", + "help": "suppress" + }, + "shuffleganon": { + "action": "store_false", "type": "bool" }, + "no_shuffleganon": { + "action": "store_true", + "dest": "shuffleganon", + "help": "suppress" + }, + "calc_playthrough": { + "action": "store_false", + "type": "bool" + }, + "skip_playthrough": { + "action": "store_true", + "dest": "calc_playthrough", + "help": "suppress" + }, "gui": { "action": "store_true" }, "jsonout": { "action": "store_true" }, - "skip_playthrough": { - "action": "store_true", - "type": "bool" - }, "enemizercli": { "setting": "enemizercli" }, diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index fdd58163..9c6519a4 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -1,5 +1,7 @@ { "cli": { + "yes": "Yes", + "no": "No", "app.title": "ALttP Door Randomizer Version %s - Seed: %d", "version": "Version", "seed": "Seed", @@ -33,7 +35,11 @@ "cannot.reach.progression": "Not all progression items reachable. Something went terribly wrong here.", "cannot.reach.required": "Not all required items reachable. Something went terribly wrong here.", "patching.rom": "Patching ROM", + "patching.spoiler": "Creating Spoiler", "calc.playthrough": "Calculating playthrough", + "made.rom": "Patched ROM: %s", + "made.playthrough": "Printed Playthrough: %s", + "made.spoiler": "Printed Spoiler: %s", "done": "Done. Enjoy.", "total.time": "Total Time: %s", "finished.run": "Finished run", @@ -262,7 +268,7 @@ "Alternatively, can be a ALttP Rom patched with a Link", "sprite that will be extracted." ], - "suppress_rom": [ "Do not create an output rom file. (default: %(default)s)" ], + "create_rom": [ "Create an output rom file. (default: %(default)s)" ], "gui": [ "Launch the GUI. (default: %(default)s)" ], "jsonout": [ "Output .json patch to stdout instead of a patched rom. Used", diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index d67963e5..da499b03 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -155,7 +155,8 @@ "randomizer.generation.spoiler": "Create Spoiler Log", - "randomizer.generation.suppressrom": "Do Not create Patched ROM", + "randomizer.generation.createrom": "Create Patched ROM", + "randomizer.generation.calcplaythrough": "Calculate Playthrough", "randomizer.generation.usestartinventory": "Use Starting Inventory", "randomizer.generation.usecustompool": "Use Custom Item Pool", diff --git a/resources/app/gui/randomize/generation/checkboxes.json b/resources/app/gui/randomize/generation/checkboxes.json index 5839e0d4..db020e6d 100644 --- a/resources/app/gui/randomize/generation/checkboxes.json +++ b/resources/app/gui/randomize/generation/checkboxes.json @@ -1,7 +1,8 @@ { "checkboxes": { "spoiler": { "type": "checkbox" }, - "suppressrom": { "type": "checkbox" }, + "createrom": { "type": "checkbox" }, + "calcplaythrough": { "type": "checkbox" }, "usestartinventory": { "type": "checkbox" }, "usecustompool": { "type": "checkbox" } } diff --git a/source/classes/constants.py b/source/classes/constants.py index 93064e7d..e8145f66 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -102,7 +102,8 @@ SETTINGSTOPROCESS = { }, "generation": { "spoiler": "create_spoiler", - "suppressrom": "suppress_rom", + "createrom": "create_rom", + "calcplaythrough": "calc_playthrough", "usestartinventory": "usestartinventory", "usecustompool": "custom", "saveonexit": "saveonexit" diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 63eec2f9..cb01b471 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -3,9 +3,10 @@ from argparse import Namespace import logging import os import random -from CLI import parse_arguments -from Main import main -from Utils import local_path, output_path, open_file +from CLI import parse_cli +from Fill import FillError +from Main import main, EnemizerError +from Utils import local_path, output_path, open_file, update_deprecated_args import source.classes.constants as CONST from source.gui.randomize.multiworld import multiworld_page import source.gui.widgets as widgets @@ -68,7 +69,7 @@ def bottom_frame(self, parent, args=None): def generateRom(): guiargs = create_guiargs(parent) # get default values for missing parameters - for k,v in vars(parse_arguments(['--multi', str(guiargs.multi)])).items(): + for k,v in vars(parse_cli(['--multi', str(guiargs.multi)])).items(): if k not in vars(guiargs): setattr(guiargs, k, v) elif type(v) is dict: # use same settings for every player @@ -81,11 +82,18 @@ def bottom_frame(self, parent, args=None): seed = random.randint(0, 999999999) else: main(seed=guiargs.seed, args=guiargs, fish=parent.fish) - except Exception as e: + except (FillError, EnemizerError, Exception, RuntimeError) as e: logging.exception(e) messagebox.showerror(title="Error while creating seed", message=str(e)) else: - messagebox.showinfo(title="Success", message="Rom patched successfully") + YES = parent.fish.translate("cli","cli","yes") + NO = parent.fish.translate("cli","cli","no") + successMsg = "" + successMsg += (parent.fish.translate("cli","cli","made.rom").strip() % (YES if (guiargs.create_rom) else NO)) + "\n" + successMsg += (parent.fish.translate("cli","cli","made.playthrough").strip() % (YES if (guiargs.calc_playthrough) else NO)) + "\n" + successMsg += (parent.fish.translate("cli","cli","made.spoiler").strip() % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + + messagebox.showinfo(title="Success", message=successMsg) ## Generate Button # widget ID @@ -226,4 +234,7 @@ def create_guiargs(parent): # Get output path guiargs.outputpath = parent.outputPath.get() + + guiargs = update_deprecated_args(guiargs) + return guiargs diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 6db02220..83342892 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -1,6 +1,7 @@ from source.classes.SpriteSelector import SpriteSelector as spriteSelector from source.gui.randomize.gameoptions import set_sprite from Rom import Sprite, get_sprite_from_name +from Utils import update_deprecated_args import source.classes.constants as CONST from source.classes.BabelFish import BabelFish from source.classes.Empty import Empty @@ -8,10 +9,12 @@ from source.classes.Empty import Empty # Load args/settings for most tabs def loadcliargs(gui, args, settings=None): if args is not None: + args = update_deprecated_args(args) + args = vars(args) fish = BabelFish() -# 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 args.items(): + if isinstance(v,dict) and 1 in v: + setattr(args, k, v[1]) # only get values for player 1 for now # load values from commandline args # set up options to get From 215a4d039f26233c4213e668ffbd670e9ce89424 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Sun, 22 Mar 2020 16:32:15 -0700 Subject: [PATCH 91/93] Pull in fixes from Multiworld --- Fill.py | 2 +- Main.py | 4 +++ Rom.py | 2 +- resources/app/cli/lang/en.json | 2 +- source/gui/bottom.py | 64 ++++++++++++++++++++++++---------- source/gui/loadcliargs.py | 2 +- 6 files changed, 54 insertions(+), 22 deletions(-) diff --git a/Fill.py b/Fill.py index 69a774bb..014db619 100644 --- a/Fill.py +++ b/Fill.py @@ -392,7 +392,7 @@ def balance_multiworld_progression(world): threshold = max(reachable_locations_count.values()) - 20 balancing_players = [player for player, reachables in reachable_locations_count.items() if reachables < threshold] - if balancing_players: + if balancing_players is not None and len(balancing_players) > 0: balancing_state = state.copy() balancing_unchecked_locations = unchecked_locations.copy() balancing_reachables = reachable_locations_count.copy() diff --git a/Main.py b/Main.py index 407af108..35300de9 100644 --- a/Main.py +++ b/Main.py @@ -191,6 +191,7 @@ def main(args, seed=None, fish=None): for team in range(world.teams): for player in range(1, world.players + 1): sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit' + enemized = False use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none' or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or args.shufflepots[player] or sprite_random_on_hit) @@ -204,6 +205,7 @@ def main(args, seed=None, fish=None): raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom) if os.path.exists(args.enemizercli): patch_enemizer(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player], sprite_random_on_hit) + enemized = True if not args.jsonout: rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) else: @@ -212,6 +214,8 @@ def main(args, seed=None, fish=None): logging.warning(enemizerMsg) raise EnemizerError(enemizerMsg) + patch_rom(world, rom, player, team, enemized) + if args.race: patch_race_rom(rom) diff --git a/Rom.py b/Rom.py index b2344e7f..db84aefd 100644 --- a/Rom.py +++ b/Rom.py @@ -79,7 +79,7 @@ class LocalRom(object): self.hash = hash self.orig_buffer = None if not os.path.isfile(file): - raise RuntimeError("Could not find valid local base rom for patching at expected path %s." % args.rom) + raise RuntimeError("Could not find valid local base rom for patching at expected path %s." % file) with open(file, 'rb') as stream: self.buffer = read_rom(stream) if patch: diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 9c6519a4..6edf5496 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -36,7 +36,7 @@ "cannot.reach.required": "Not all required items reachable. Something went terribly wrong here.", "patching.rom": "Patching ROM", "patching.spoiler": "Creating Spoiler", - "calc.playthrough": "Calculating playthrough", + "calc.playthrough": "Calculating Playthrough", "made.rom": "Patched ROM: %s", "made.playthrough": "Printed Playthrough: %s", "made.spoiler": "Printed Spoiler: %s", diff --git a/source/gui/bottom.py b/source/gui/bottom.py index cb01b471..6daa4e30 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -3,6 +3,7 @@ from argparse import Namespace import logging import os import random +import re from CLI import parse_cli from Fill import FillError from Main import main, EnemizerError @@ -74,26 +75,53 @@ def bottom_frame(self, parent, args=None): setattr(guiargs, k, v) elif type(v) is dict: # use same settings for every player setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, guiargs.multi + 1)}) - try: - if guiargs.count is not None: - seed = guiargs.seed - for _ in range(guiargs.count): - main(seed=seed, args=guiargs, fish=parent.fish) - seed = random.randint(0, 999999999) + argsDump = vars(guiargs) + hasEnemizer = "enemizercli" in argsDump and os.path.isfile(argsDump["enemizercli"]) + needEnemizer = False + if not hasEnemizer: + falsey = [ "none", "default", "vanilla", False, 0 ] + for enemizerOption in [ "shufflepots", "shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health" ]: + if enemizerOption in argsDump: + if isinstance(argsDump[enemizerOption], dict): + for playerID,playerSetting in argsDump[enemizerOption].items(): + if not playerSetting in falsey: + needEnemizer = True + elif not argsDump[enemizerOption] in falsey: + needEnemizer = True + seeds = [] + if not needEnemizer or (needEnemizer and hasEnemizer): + try: + if guiargs.count is not None: + seed = guiargs.seed + for _ in range(guiargs.count): + seeds.append(seed) + main(seed=seed, args=guiargs, fish=parent.fish) + seed = random.randint(0, 999999999) + else: + seeds.append(guiargs.seed) + main(seed=guiargs.seed, args=guiargs, fish=parent.fish) + except (FillError, EnemizerError, Exception, RuntimeError) as e: + logging.exception(e) + messagebox.showerror(title="Error while creating seed", message=str(e)) else: - main(seed=guiargs.seed, args=guiargs, fish=parent.fish) - except (FillError, EnemizerError, Exception, RuntimeError) as e: - logging.exception(e) - messagebox.showerror(title="Error while creating seed", message=str(e)) - else: - YES = parent.fish.translate("cli","cli","yes") - NO = parent.fish.translate("cli","cli","no") - successMsg = "" - successMsg += (parent.fish.translate("cli","cli","made.rom").strip() % (YES if (guiargs.create_rom) else NO)) + "\n" - successMsg += (parent.fish.translate("cli","cli","made.playthrough").strip() % (YES if (guiargs.calc_playthrough) else NO)) + "\n" - successMsg += (parent.fish.translate("cli","cli","made.spoiler").strip() % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + YES = parent.fish.translate("cli","cli","yes") + NO = parent.fish.translate("cli","cli","no") + successMsg = "" + made = {} + for k in [ "rom", "playthrough", "spoiler" ]: + made[k] = parent.fish.translate("cli","cli","made." + k) + for k in made: + v = made[k] + pattern = "([\w]+)(:)([\s]+)(.*)" + m = re.search(pattern,made[k]) + made[k] = m.group(1) + m.group(2) + ' ' + m.group(4) + successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n" + successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n" + successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n" + # FIXME: English + successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(seeds))) - messagebox.showinfo(title="Success", message=successMsg) + messagebox.showinfo(title="Success", message=successMsg) ## Generate Button # widget ID diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index 83342892..31658549 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -124,7 +124,7 @@ def loadcliargs(gui, args, settings=None): widget = "seed" setting = "seed" if args[setting]: - gui.pages[mainpage].widgets[widget].storageVar.set(args[setting]) + gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[setting]) # set textbox/frame label label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) From 4fc15e84fc35ed7cb3a04b64f907dccd936b4fb8 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 23 Mar 2020 14:15:17 -0600 Subject: [PATCH 92/93] Bump version Set shuffleganon to default to True --- CLI.py | 2 +- Main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CLI.py b/CLI.py index 32879689..eec8327a 100644 --- a/CLI.py +++ b/CLI.py @@ -124,7 +124,7 @@ def parse_settings(): "algorithm": "balanced", "openpyramid": False, - "shuffleganon": False, + "shuffleganon": True, "shuffle": "vanilla", "shufflepots": False, diff --git a/Main.py b/Main.py index c8ba18aa..5085c935 100644 --- a/Main.py +++ b/Main.py @@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute from ItemList import generate_itempool, difficulties, fill_prizes from Utils import output_path, parse_player_names, print_wiki_doors_by_region, print_wiki_doors_by_room -__version__ = '0.0.19dev' +__version__ = '0.0.20dev' class EnemizerError(RuntimeError): pass From 0b13db754d234f384b8c87ad3983299d2624c88f Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 23 Mar 2020 15:40:56 -0600 Subject: [PATCH 93/93] Fixing some seed reporting --- DungeonRandomizer.py | 3 ++- source/gui/bottom.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/DungeonRandomizer.py b/DungeonRandomizer.py index ca008a96..15e73f85 100755 --- a/DungeonRandomizer.py +++ b/DungeonRandomizer.py @@ -53,7 +53,8 @@ def start(): if args.gui: from Gui import guiMain guiMain(args) - elif args.count is not None: + elif args.count is not None and args.count > 1: + random.seed(None) seed = args.seed or random.randint(0, 999999999) failures = [] logger = logging.getLogger('') diff --git a/source/gui/bottom.py b/source/gui/bottom.py index 6daa4e30..218f7785 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -91,14 +91,19 @@ def bottom_frame(self, parent, args=None): seeds = [] if not needEnemizer or (needEnemizer and hasEnemizer): try: - if guiargs.count is not None: + if guiargs.count is not None and guiargs.seed: seed = guiargs.seed for _ in range(guiargs.count): seeds.append(seed) main(seed=seed, args=guiargs, fish=parent.fish) seed = random.randint(0, 999999999) else: - seeds.append(guiargs.seed) + if guiargs.seed: + seeds.append(guiargs.seed) + else: + random.seed(None) + guiargs.seed = random.randint(0, 999999999) + seeds.append(guiargs.seed) main(seed=guiargs.seed, args=guiargs, fish=parent.fish) except (FillError, EnemizerError, Exception, RuntimeError) as e: logging.exception(e) @@ -119,7 +124,7 @@ def bottom_frame(self, parent, args=None): successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n" successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n" # FIXME: English - successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(seeds))) + successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds))) messagebox.showinfo(title="Success", message=successMsg)