mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 23:33:22 -07:00
Compare commits
1 Commits
NewSoupVi-
...
NewSoupVi-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68f90571fd |
@@ -1264,6 +1264,10 @@ class Item:
|
|||||||
def trap(self) -> bool:
|
def trap(self) -> bool:
|
||||||
return ItemClassification.trap in self.classification
|
return ItemClassification.trap in self.classification
|
||||||
|
|
||||||
|
@property
|
||||||
|
def excludable(self) -> bool:
|
||||||
|
return not (self.advancement or self.useful)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def flags(self) -> int:
|
def flags(self) -> int:
|
||||||
return self.classification.as_flag()
|
return self.classification.as_flag()
|
||||||
|
|||||||
4
Utils.py
4
Utils.py
@@ -423,7 +423,7 @@ class RestrictedUnpickler(pickle.Unpickler):
|
|||||||
if module == "NetUtils" and name in {"NetworkItem", "ClientStatus", "Hint", "SlotType", "NetworkSlot"}:
|
if module == "NetUtils" and name in {"NetworkItem", "ClientStatus", "Hint", "SlotType", "NetworkSlot"}:
|
||||||
return getattr(self.net_utils_module, name)
|
return getattr(self.net_utils_module, name)
|
||||||
# Options and Plando are unpickled by WebHost -> Generate
|
# Options and Plando are unpickled by WebHost -> Generate
|
||||||
if module == "worlds.generic" and name == "PlandoItem":
|
if module == "worlds.generic" and name in {"PlandoItem", "PlandoConnection"}:
|
||||||
if not self.generic_properties_module:
|
if not self.generic_properties_module:
|
||||||
self.generic_properties_module = importlib.import_module("worlds.generic")
|
self.generic_properties_module = importlib.import_module("worlds.generic")
|
||||||
return getattr(self.generic_properties_module, name)
|
return getattr(self.generic_properties_module, name)
|
||||||
@@ -434,7 +434,7 @@ class RestrictedUnpickler(pickle.Unpickler):
|
|||||||
else:
|
else:
|
||||||
mod = importlib.import_module(module)
|
mod = importlib.import_module(module)
|
||||||
obj = getattr(mod, name)
|
obj = getattr(mod, name)
|
||||||
if issubclass(obj, (self.options_module.Option, self.options_module.PlandoConnection)):
|
if issubclass(obj, self.options_module.Option):
|
||||||
return obj
|
return obj
|
||||||
# Forbid everything else.
|
# Forbid everything else.
|
||||||
raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden")
|
raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden")
|
||||||
|
|||||||
98
setup.py
98
setup.py
@@ -5,6 +5,7 @@ import platform
|
|||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
import typing
|
||||||
import warnings
|
import warnings
|
||||||
import zipfile
|
import zipfile
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@@ -13,14 +14,14 @@ import json
|
|||||||
import threading
|
import threading
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from collections.abc import Iterable
|
||||||
from hashlib import sha3_512
|
from hashlib import sha3_512
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
|
|
||||||
|
|
||||||
|
|
||||||
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
|
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
|
||||||
requirement = 'cx-Freeze==7.2.0'
|
|
||||||
try:
|
try:
|
||||||
|
requirement = 'cx-Freeze==7.2.0'
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
try:
|
try:
|
||||||
pkg_resources.require(requirement)
|
pkg_resources.require(requirement)
|
||||||
@@ -29,7 +30,7 @@ try:
|
|||||||
install_cx_freeze = True
|
install_cx_freeze = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
install_cx_freeze = True
|
install_cx_freeze = True
|
||||||
pkg_resources = None # type: ignore[assignment]
|
pkg_resources = None # type: ignore [assignment]
|
||||||
|
|
||||||
if install_cx_freeze:
|
if install_cx_freeze:
|
||||||
# check if pip is available
|
# check if pip is available
|
||||||
@@ -60,7 +61,7 @@ from Cython.Build import cythonize
|
|||||||
|
|
||||||
|
|
||||||
# On Python < 3.10 LogicMixin is not currently supported.
|
# On Python < 3.10 LogicMixin is not currently supported.
|
||||||
non_apworlds: Set[str] = {
|
non_apworlds: set = {
|
||||||
"A Link to the Past",
|
"A Link to the Past",
|
||||||
"Adventure",
|
"Adventure",
|
||||||
"ArchipIDLE",
|
"ArchipIDLE",
|
||||||
@@ -83,7 +84,7 @@ non_apworlds: Set[str] = {
|
|||||||
if sys.version_info < (3,10):
|
if sys.version_info < (3,10):
|
||||||
non_apworlds.add("Hollow Knight")
|
non_apworlds.add("Hollow Knight")
|
||||||
|
|
||||||
def download_SNI() -> None:
|
def download_SNI():
|
||||||
print("Updating SNI")
|
print("Updating SNI")
|
||||||
machine_to_go = {
|
machine_to_go = {
|
||||||
"x86_64": "amd64",
|
"x86_64": "amd64",
|
||||||
@@ -113,8 +114,8 @@ def download_SNI() -> None:
|
|||||||
if source_url and source_url.endswith(".zip"):
|
if source_url and source_url.endswith(".zip"):
|
||||||
with urllib.request.urlopen(source_url) as download:
|
with urllib.request.urlopen(source_url) as download:
|
||||||
with zipfile.ZipFile(io.BytesIO(download.read()), "r") as zf:
|
with zipfile.ZipFile(io.BytesIO(download.read()), "r") as zf:
|
||||||
for zf_member in zf.infolist():
|
for member in zf.infolist():
|
||||||
zf.extract(zf_member, path="SNI")
|
zf.extract(member, path="SNI")
|
||||||
print(f"Downloaded SNI from {source_url}")
|
print(f"Downloaded SNI from {source_url}")
|
||||||
|
|
||||||
elif source_url and (source_url.endswith(".tar.xz") or source_url.endswith(".tar.gz")):
|
elif source_url and (source_url.endswith(".tar.xz") or source_url.endswith(".tar.gz")):
|
||||||
@@ -128,13 +129,11 @@ def download_SNI() -> None:
|
|||||||
raise ValueError(f"Unexpected file '{member.name}' in {source_url}")
|
raise ValueError(f"Unexpected file '{member.name}' in {source_url}")
|
||||||
elif member.isdir() and not sni_dir:
|
elif member.isdir() and not sni_dir:
|
||||||
sni_dir = member.name
|
sni_dir = member.name
|
||||||
elif member.isfile() and not sni_dir or sni_dir and not member.name.startswith(sni_dir):
|
elif member.isfile() and not sni_dir or not member.name.startswith(sni_dir):
|
||||||
raise ValueError(f"Expected folder before '{member.name}' in {source_url}")
|
raise ValueError(f"Expected folder before '{member.name}' in {source_url}")
|
||||||
elif member.isfile() and sni_dir:
|
elif member.isfile() and sni_dir:
|
||||||
tf.extract(member)
|
tf.extract(member)
|
||||||
# sadly SNI is in its own folder on non-windows, so we need to rename
|
# sadly SNI is in its own folder on non-windows, so we need to rename
|
||||||
if not sni_dir:
|
|
||||||
raise ValueError("Did not find SNI in archive")
|
|
||||||
shutil.rmtree("SNI", True)
|
shutil.rmtree("SNI", True)
|
||||||
os.rename(sni_dir, "SNI")
|
os.rename(sni_dir, "SNI")
|
||||||
print(f"Downloaded SNI from {source_url}")
|
print(f"Downloaded SNI from {source_url}")
|
||||||
@@ -146,7 +145,7 @@ def download_SNI() -> None:
|
|||||||
print(f"No SNI found for system spec {platform_name} {machine_name}")
|
print(f"No SNI found for system spec {platform_name} {machine_name}")
|
||||||
|
|
||||||
|
|
||||||
signtool: Optional[str]
|
signtool: typing.Optional[str]
|
||||||
if os.path.exists("X:/pw.txt"):
|
if os.path.exists("X:/pw.txt"):
|
||||||
print("Using signtool")
|
print("Using signtool")
|
||||||
with open("X:/pw.txt", encoding="utf-8-sig") as f:
|
with open("X:/pw.txt", encoding="utf-8-sig") as f:
|
||||||
@@ -198,13 +197,13 @@ extra_data = ["LICENSE", "data", "EnemizerCLI", "SNI"]
|
|||||||
extra_libs = ["libssl.so", "libcrypto.so"] if is_linux else []
|
extra_libs = ["libssl.so", "libcrypto.so"] if is_linux else []
|
||||||
|
|
||||||
|
|
||||||
def remove_sprites_from_folder(folder: Path) -> None:
|
def remove_sprites_from_folder(folder):
|
||||||
for file in os.listdir(folder):
|
for file in os.listdir(folder):
|
||||||
if file != ".gitignore":
|
if file != ".gitignore":
|
||||||
os.remove(folder / file)
|
os.remove(folder / file)
|
||||||
|
|
||||||
|
|
||||||
def _threaded_hash(filepath: Union[str, Path]) -> str:
|
def _threaded_hash(filepath):
|
||||||
hasher = sha3_512()
|
hasher = sha3_512()
|
||||||
hasher.update(open(filepath, "rb").read())
|
hasher.update(open(filepath, "rb").read())
|
||||||
return base64.b85encode(hasher.digest()).decode()
|
return base64.b85encode(hasher.digest()).decode()
|
||||||
@@ -218,11 +217,11 @@ class BuildCommand(setuptools.command.build.build):
|
|||||||
yes: bool
|
yes: bool
|
||||||
last_yes: bool = False # used by sub commands of build
|
last_yes: bool = False # used by sub commands of build
|
||||||
|
|
||||||
def initialize_options(self) -> None:
|
def initialize_options(self):
|
||||||
super().initialize_options()
|
super().initialize_options()
|
||||||
type(self).last_yes = self.yes = False
|
type(self).last_yes = self.yes = False
|
||||||
|
|
||||||
def finalize_options(self) -> None:
|
def finalize_options(self):
|
||||||
super().finalize_options()
|
super().finalize_options()
|
||||||
type(self).last_yes = self.yes
|
type(self).last_yes = self.yes
|
||||||
|
|
||||||
@@ -234,27 +233,27 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
|||||||
('extra-data=', None, 'Additional files to add.'),
|
('extra-data=', None, 'Additional files to add.'),
|
||||||
]
|
]
|
||||||
yes: bool
|
yes: bool
|
||||||
extra_data: Iterable[str]
|
extra_data: Iterable # [any] not available in 3.8
|
||||||
extra_libs: Iterable[str] # work around broken include_files
|
extra_libs: Iterable # work around broken include_files
|
||||||
|
|
||||||
buildfolder: Path
|
buildfolder: Path
|
||||||
libfolder: Path
|
libfolder: Path
|
||||||
library: Path
|
library: Path
|
||||||
buildtime: datetime.datetime
|
buildtime: datetime.datetime
|
||||||
|
|
||||||
def initialize_options(self) -> None:
|
def initialize_options(self):
|
||||||
super().initialize_options()
|
super().initialize_options()
|
||||||
self.yes = BuildCommand.last_yes
|
self.yes = BuildCommand.last_yes
|
||||||
self.extra_data = []
|
self.extra_data = []
|
||||||
self.extra_libs = []
|
self.extra_libs = []
|
||||||
|
|
||||||
def finalize_options(self) -> None:
|
def finalize_options(self):
|
||||||
super().finalize_options()
|
super().finalize_options()
|
||||||
self.buildfolder = self.build_exe
|
self.buildfolder = self.build_exe
|
||||||
self.libfolder = Path(self.buildfolder, "lib")
|
self.libfolder = Path(self.buildfolder, "lib")
|
||||||
self.library = Path(self.libfolder, "library.zip")
|
self.library = Path(self.libfolder, "library.zip")
|
||||||
|
|
||||||
def installfile(self, path: Path, subpath: Optional[Union[str, Path]] = None, keep_content: bool = False) -> None:
|
def installfile(self, path, subpath=None, keep_content: bool = False):
|
||||||
folder = self.buildfolder
|
folder = self.buildfolder
|
||||||
if subpath:
|
if subpath:
|
||||||
folder /= subpath
|
folder /= subpath
|
||||||
@@ -269,7 +268,7 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
|||||||
else:
|
else:
|
||||||
print('Warning,', path, 'not found')
|
print('Warning,', path, 'not found')
|
||||||
|
|
||||||
def create_manifest(self, create_hashes: bool = False) -> None:
|
def create_manifest(self, create_hashes=False):
|
||||||
# Since the setup is now split into components and the manifest is not,
|
# Since the setup is now split into components and the manifest is not,
|
||||||
# it makes most sense to just remove the hashes for now. Not aware of anyone using them.
|
# it makes most sense to just remove the hashes for now. Not aware of anyone using them.
|
||||||
hashes = {}
|
hashes = {}
|
||||||
@@ -291,7 +290,7 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
|||||||
json.dump(manifest, open(manifestpath, "wt"), indent=4)
|
json.dump(manifest, open(manifestpath, "wt"), indent=4)
|
||||||
print("Created Manifest")
|
print("Created Manifest")
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self):
|
||||||
# start downloading sni asap
|
# start downloading sni asap
|
||||||
sni_thread = threading.Thread(target=download_SNI, name="SNI Downloader")
|
sni_thread = threading.Thread(target=download_SNI, name="SNI Downloader")
|
||||||
sni_thread.start()
|
sni_thread.start()
|
||||||
@@ -342,7 +341,7 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
|||||||
|
|
||||||
# post build steps
|
# post build steps
|
||||||
if is_windows: # kivy_deps is win32 only, linux picks them up automatically
|
if is_windows: # kivy_deps is win32 only, linux picks them up automatically
|
||||||
from kivy_deps import sdl2, glew # type: ignore
|
from kivy_deps import sdl2, glew
|
||||||
for folder in sdl2.dep_bins + glew.dep_bins:
|
for folder in sdl2.dep_bins + glew.dep_bins:
|
||||||
shutil.copytree(folder, self.libfolder, dirs_exist_ok=True)
|
shutil.copytree(folder, self.libfolder, dirs_exist_ok=True)
|
||||||
print(f"copying {folder} -> {self.libfolder}")
|
print(f"copying {folder} -> {self.libfolder}")
|
||||||
@@ -363,7 +362,7 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
|||||||
self.installfile(Path(data))
|
self.installfile(Path(data))
|
||||||
|
|
||||||
# kivi data files
|
# kivi data files
|
||||||
import kivy # type: ignore[import-untyped]
|
import kivy
|
||||||
shutil.copytree(os.path.join(os.path.dirname(kivy.__file__), "data"),
|
shutil.copytree(os.path.join(os.path.dirname(kivy.__file__), "data"),
|
||||||
self.buildfolder / "data",
|
self.buildfolder / "data",
|
||||||
dirs_exist_ok=True)
|
dirs_exist_ok=True)
|
||||||
@@ -373,7 +372,7 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
|||||||
from worlds.AutoWorld import AutoWorldRegister
|
from worlds.AutoWorld import AutoWorldRegister
|
||||||
assert not non_apworlds - set(AutoWorldRegister.world_types), \
|
assert not non_apworlds - set(AutoWorldRegister.world_types), \
|
||||||
f"Unknown world {non_apworlds - set(AutoWorldRegister.world_types)} designated for .apworld"
|
f"Unknown world {non_apworlds - set(AutoWorldRegister.world_types)} designated for .apworld"
|
||||||
folders_to_remove: List[str] = []
|
folders_to_remove: typing.List[str] = []
|
||||||
disabled_worlds_folder = "worlds_disabled"
|
disabled_worlds_folder = "worlds_disabled"
|
||||||
for entry in os.listdir(disabled_worlds_folder):
|
for entry in os.listdir(disabled_worlds_folder):
|
||||||
if os.path.isdir(os.path.join(disabled_worlds_folder, entry)):
|
if os.path.isdir(os.path.join(disabled_worlds_folder, entry)):
|
||||||
@@ -394,7 +393,7 @@ class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
|
|||||||
shutil.rmtree(world_directory)
|
shutil.rmtree(world_directory)
|
||||||
shutil.copyfile("meta.yaml", self.buildfolder / "Players" / "Templates" / "meta.yaml")
|
shutil.copyfile("meta.yaml", self.buildfolder / "Players" / "Templates" / "meta.yaml")
|
||||||
try:
|
try:
|
||||||
from maseya import z3pr # type: ignore[import-untyped]
|
from maseya import z3pr
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("Maseya Palette Shuffle not found, skipping data files.")
|
print("Maseya Palette Shuffle not found, skipping data files.")
|
||||||
else:
|
else:
|
||||||
@@ -445,16 +444,16 @@ class AppImageCommand(setuptools.Command):
|
|||||||
("app-exec=", None, "The application to run inside the image."),
|
("app-exec=", None, "The application to run inside the image."),
|
||||||
("yes", "y", 'Answer "yes" to all questions.'),
|
("yes", "y", 'Answer "yes" to all questions.'),
|
||||||
]
|
]
|
||||||
build_folder: Optional[Path]
|
build_folder: typing.Optional[Path]
|
||||||
dist_file: Optional[Path]
|
dist_file: typing.Optional[Path]
|
||||||
app_dir: Optional[Path]
|
app_dir: typing.Optional[Path]
|
||||||
app_name: str
|
app_name: str
|
||||||
app_exec: Optional[Path]
|
app_exec: typing.Optional[Path]
|
||||||
app_icon: Optional[Path] # source file
|
app_icon: typing.Optional[Path] # source file
|
||||||
app_id: str # lower case name, used for icon and .desktop
|
app_id: str # lower case name, used for icon and .desktop
|
||||||
yes: bool
|
yes: bool
|
||||||
|
|
||||||
def write_desktop(self) -> None:
|
def write_desktop(self):
|
||||||
assert self.app_dir, "Invalid app_dir"
|
assert self.app_dir, "Invalid app_dir"
|
||||||
desktop_filename = self.app_dir / f"{self.app_id}.desktop"
|
desktop_filename = self.app_dir / f"{self.app_id}.desktop"
|
||||||
with open(desktop_filename, 'w', encoding="utf-8") as f:
|
with open(desktop_filename, 'w', encoding="utf-8") as f:
|
||||||
@@ -469,7 +468,7 @@ class AppImageCommand(setuptools.Command):
|
|||||||
)))
|
)))
|
||||||
desktop_filename.chmod(0o755)
|
desktop_filename.chmod(0o755)
|
||||||
|
|
||||||
def write_launcher(self, default_exe: Path) -> None:
|
def write_launcher(self, default_exe: Path):
|
||||||
assert self.app_dir, "Invalid app_dir"
|
assert self.app_dir, "Invalid app_dir"
|
||||||
launcher_filename = self.app_dir / "AppRun"
|
launcher_filename = self.app_dir / "AppRun"
|
||||||
with open(launcher_filename, 'w', encoding="utf-8") as f:
|
with open(launcher_filename, 'w', encoding="utf-8") as f:
|
||||||
@@ -492,7 +491,7 @@ $APPDIR/$exe "$@"
|
|||||||
""")
|
""")
|
||||||
launcher_filename.chmod(0o755)
|
launcher_filename.chmod(0o755)
|
||||||
|
|
||||||
def install_icon(self, src: Path, name: Optional[str] = None, symlink: Optional[Path] = None) -> None:
|
def install_icon(self, src: Path, name: typing.Optional[str] = None, symlink: typing.Optional[Path] = None):
|
||||||
assert self.app_dir, "Invalid app_dir"
|
assert self.app_dir, "Invalid app_dir"
|
||||||
try:
|
try:
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -514,8 +513,7 @@ $APPDIR/$exe "$@"
|
|||||||
if symlink:
|
if symlink:
|
||||||
symlink.symlink_to(dest_file.relative_to(symlink.parent))
|
symlink.symlink_to(dest_file.relative_to(symlink.parent))
|
||||||
|
|
||||||
def initialize_options(self) -> None:
|
def initialize_options(self):
|
||||||
assert self.distribution.metadata.name
|
|
||||||
self.build_folder = None
|
self.build_folder = None
|
||||||
self.app_dir = None
|
self.app_dir = None
|
||||||
self.app_name = self.distribution.metadata.name
|
self.app_name = self.distribution.metadata.name
|
||||||
@@ -529,22 +527,17 @@ $APPDIR/$exe "$@"
|
|||||||
))
|
))
|
||||||
self.yes = False
|
self.yes = False
|
||||||
|
|
||||||
def finalize_options(self) -> None:
|
def finalize_options(self):
|
||||||
assert self.build_folder
|
|
||||||
if not self.app_dir:
|
if not self.app_dir:
|
||||||
self.app_dir = self.build_folder.parent / "AppDir"
|
self.app_dir = self.build_folder.parent / "AppDir"
|
||||||
self.app_id = self.app_name.lower()
|
self.app_id = self.app_name.lower()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self):
|
||||||
assert self.build_folder and self.dist_file, "Command not properly set up"
|
|
||||||
assert (
|
|
||||||
self.app_icon and self.app_id and self.app_dir and self.app_exec and self.app_name
|
|
||||||
), "AppImageCommand not properly set up"
|
|
||||||
self.dist_file.parent.mkdir(parents=True, exist_ok=True)
|
self.dist_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
if self.app_dir.is_dir():
|
if self.app_dir.is_dir():
|
||||||
shutil.rmtree(self.app_dir)
|
shutil.rmtree(self.app_dir)
|
||||||
self.app_dir.mkdir(parents=True)
|
self.app_dir.mkdir(parents=True)
|
||||||
opt_dir = self.app_dir / "opt" / self.app_name
|
opt_dir = self.app_dir / "opt" / self.distribution.metadata.name
|
||||||
shutil.copytree(self.build_folder, opt_dir)
|
shutil.copytree(self.build_folder, opt_dir)
|
||||||
root_icon = self.app_dir / f'{self.app_id}{self.app_icon.suffix}'
|
root_icon = self.app_dir / f'{self.app_id}{self.app_icon.suffix}'
|
||||||
self.install_icon(self.app_icon, self.app_id, symlink=root_icon)
|
self.install_icon(self.app_icon, self.app_id, symlink=root_icon)
|
||||||
@@ -555,7 +548,7 @@ $APPDIR/$exe "$@"
|
|||||||
subprocess.call(f'ARCH={build_arch} ./appimagetool -n "{self.app_dir}" "{self.dist_file}"', shell=True)
|
subprocess.call(f'ARCH={build_arch} ./appimagetool -n "{self.app_dir}" "{self.dist_file}"', shell=True)
|
||||||
|
|
||||||
|
|
||||||
def find_libs(*args: str) -> Sequence[Tuple[str, str]]:
|
def find_libs(*args: str) -> typing.Sequence[typing.Tuple[str, str]]:
|
||||||
"""Try to find system libraries to be included."""
|
"""Try to find system libraries to be included."""
|
||||||
if not args:
|
if not args:
|
||||||
return []
|
return []
|
||||||
@@ -563,7 +556,7 @@ def find_libs(*args: str) -> Sequence[Tuple[str, str]]:
|
|||||||
arch = build_arch.replace('_', '-')
|
arch = build_arch.replace('_', '-')
|
||||||
libc = 'libc6' # we currently don't support musl
|
libc = 'libc6' # we currently don't support musl
|
||||||
|
|
||||||
def parse(line: str) -> Tuple[Tuple[str, str, str], str]:
|
def parse(line):
|
||||||
lib, path = line.strip().split(' => ')
|
lib, path = line.strip().split(' => ')
|
||||||
lib, typ = lib.split(' ', 1)
|
lib, typ = lib.split(' ', 1)
|
||||||
for test_arch in ('x86-64', 'i386', 'aarch64'):
|
for test_arch in ('x86-64', 'i386', 'aarch64'):
|
||||||
@@ -584,29 +577,26 @@ def find_libs(*args: str) -> Sequence[Tuple[str, str]]:
|
|||||||
ldconfig = shutil.which("ldconfig")
|
ldconfig = shutil.which("ldconfig")
|
||||||
assert ldconfig, "Make sure ldconfig is in PATH"
|
assert ldconfig, "Make sure ldconfig is in PATH"
|
||||||
data = subprocess.run([ldconfig, "-p"], capture_output=True, text=True).stdout.split("\n")[1:]
|
data = subprocess.run([ldconfig, "-p"], capture_output=True, text=True).stdout.split("\n")[1:]
|
||||||
find_libs.cache = { # type: ignore[attr-defined]
|
find_libs.cache = { # type: ignore [attr-defined]
|
||||||
k: v for k, v in (parse(line) for line in data if "=>" in line)
|
k: v for k, v in (parse(line) for line in data if "=>" in line)
|
||||||
}
|
}
|
||||||
|
|
||||||
def find_lib(lib: str, arch: str, libc: str) -> Optional[str]:
|
def find_lib(lib, arch, libc):
|
||||||
cache: Dict[Tuple[str, str, str], str] = getattr(find_libs, "cache")
|
for k, v in find_libs.cache.items():
|
||||||
for k, v in cache.items():
|
|
||||||
if k == (lib, arch, libc):
|
if k == (lib, arch, libc):
|
||||||
return v
|
return v
|
||||||
for k, v, in cache.items():
|
for k, v, in find_libs.cache.items():
|
||||||
if k[0].startswith(lib) and k[1] == arch and k[2] == libc:
|
if k[0].startswith(lib) and k[1] == arch and k[2] == libc:
|
||||||
return v
|
return v
|
||||||
return None
|
return None
|
||||||
|
|
||||||
res: List[Tuple[str, str]] = []
|
res = []
|
||||||
for arg in args:
|
for arg in args:
|
||||||
# try exact match, empty libc, empty arch, empty arch and libc
|
# try exact match, empty libc, empty arch, empty arch and libc
|
||||||
file = find_lib(arg, arch, libc)
|
file = find_lib(arg, arch, libc)
|
||||||
file = file or find_lib(arg, arch, '')
|
file = file or find_lib(arg, arch, '')
|
||||||
file = file or find_lib(arg, '', libc)
|
file = file or find_lib(arg, '', libc)
|
||||||
file = file or find_lib(arg, '', '')
|
file = file or find_lib(arg, '', '')
|
||||||
if not file:
|
|
||||||
raise ValueError(f"Could not find lib {arg}")
|
|
||||||
# resolve symlinks
|
# resolve symlinks
|
||||||
for n in range(0, 5):
|
for n in range(0, 5):
|
||||||
res.append((file, os.path.join('lib', os.path.basename(file))))
|
res.append((file, os.path.join('lib', os.path.basename(file))))
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from dataclasses import make_dataclass
|
|||||||
from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, Optional, Set, TextIO, Tuple,
|
from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, Optional, Set, TextIO, Tuple,
|
||||||
TYPE_CHECKING, Type, Union)
|
TYPE_CHECKING, Type, Union)
|
||||||
|
|
||||||
from Options import item_and_loc_options, ItemsAccessibility, OptionGroup, PerGameCommonOptions
|
from Options import item_and_loc_options, OptionGroup, PerGameCommonOptions
|
||||||
from BaseClasses import CollectionState
|
from BaseClasses import CollectionState
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -480,7 +480,6 @@ class World(metaclass=AutoWorldRegister):
|
|||||||
group = cls(multiworld, new_player_id)
|
group = cls(multiworld, new_player_id)
|
||||||
group.options = cls.options_dataclass(**{option_key: option.from_any(option.default)
|
group.options = cls.options_dataclass(**{option_key: option.from_any(option.default)
|
||||||
for option_key, option in cls.options_dataclass.type_hints.items()})
|
for option_key, option in cls.options_dataclass.type_hints.items()})
|
||||||
group.options.accessibility = ItemsAccessibility(ItemsAccessibility.option_items)
|
|
||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ option_docstrings = {
|
|||||||
"RandomizeLoreTablets": "Randomize Lore items into the itempool, one per Lore Tablet, and place randomized item "
|
"RandomizeLoreTablets": "Randomize Lore items into the itempool, one per Lore Tablet, and place randomized item "
|
||||||
"grants on the tablets themselves.\n You must still read the tablet to get the item.",
|
"grants on the tablets themselves.\n You must still read the tablet to get the item.",
|
||||||
"PreciseMovement": "Places skips into logic which require extremely precise player movement, possibly without "
|
"PreciseMovement": "Places skips into logic which require extremely precise player movement, possibly without "
|
||||||
"movement skills such as\n dash or claw.",
|
"movement skills such as\n dash or hook.",
|
||||||
"ProficientCombat": "Places skips into logic which require proficient combat, possibly with limited items.",
|
"ProficientCombat": "Places skips into logic which require proficient combat, possibly with limited items.",
|
||||||
"BackgroundObjectPogos": "Places skips into logic for locations which are reachable via pogoing off of "
|
"BackgroundObjectPogos": "Places skips into logic for locations which are reachable via pogoing off of "
|
||||||
"background objects.",
|
"background objects.",
|
||||||
|
|||||||
@@ -274,12 +274,6 @@ class TunicWorld(World):
|
|||||||
if items_to_create[page] > 0:
|
if items_to_create[page] > 0:
|
||||||
tunic_items.append(self.create_item(page, ItemClassification.useful))
|
tunic_items.append(self.create_item(page, ItemClassification.useful))
|
||||||
items_to_create[page] = 0
|
items_to_create[page] = 0
|
||||||
# if ice grapple logic is on, probably really want icebolt
|
|
||||||
elif self.options.ice_grappling:
|
|
||||||
page = "Pages 52-53 (Icebolt)"
|
|
||||||
if items_to_create[page] > 0:
|
|
||||||
tunic_items.append(self.create_item(page, ItemClassification.progression | ItemClassification.useful))
|
|
||||||
items_to_create[page] = 0
|
|
||||||
|
|
||||||
if self.options.maskless:
|
if self.options.maskless:
|
||||||
tunic_items.append(self.create_item("Scavenger Mask", ItemClassification.useful))
|
tunic_items.append(self.create_item("Scavenger Mask", ItemClassification.useful))
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from typing import Dict, List, Set, NamedTuple
|
from typing import Dict, List, Set, NamedTuple
|
||||||
from BaseClasses import ItemClassification as IC
|
from BaseClasses import ItemClassification
|
||||||
|
|
||||||
|
|
||||||
class TunicItemData(NamedTuple):
|
class TunicItemData(NamedTuple):
|
||||||
classification: IC
|
classification: ItemClassification
|
||||||
quantity_in_item_pool: int
|
quantity_in_item_pool: int
|
||||||
item_id_offset: int
|
item_id_offset: int
|
||||||
item_group: str = ""
|
item_group: str = ""
|
||||||
@@ -13,157 +13,157 @@ class TunicItemData(NamedTuple):
|
|||||||
item_base_id = 509342400
|
item_base_id = 509342400
|
||||||
|
|
||||||
item_table: Dict[str, TunicItemData] = {
|
item_table: Dict[str, TunicItemData] = {
|
||||||
"Firecracker x2": TunicItemData(IC.filler, 3, 0, "Bombs"),
|
"Firecracker x2": TunicItemData(ItemClassification.filler, 3, 0, "Bombs"),
|
||||||
"Firecracker x3": TunicItemData(IC.filler, 3, 1, "Bombs"),
|
"Firecracker x3": TunicItemData(ItemClassification.filler, 3, 1, "Bombs"),
|
||||||
"Firecracker x4": TunicItemData(IC.filler, 3, 2, "Bombs"),
|
"Firecracker x4": TunicItemData(ItemClassification.filler, 3, 2, "Bombs"),
|
||||||
"Firecracker x5": TunicItemData(IC.filler, 1, 3, "Bombs"),
|
"Firecracker x5": TunicItemData(ItemClassification.filler, 1, 3, "Bombs"),
|
||||||
"Firecracker x6": TunicItemData(IC.filler, 2, 4, "Bombs"),
|
"Firecracker x6": TunicItemData(ItemClassification.filler, 2, 4, "Bombs"),
|
||||||
"Fire Bomb x2": TunicItemData(IC.filler, 2, 5, "Bombs"),
|
"Fire Bomb x2": TunicItemData(ItemClassification.filler, 2, 5, "Bombs"),
|
||||||
"Fire Bomb x3": TunicItemData(IC.filler, 1, 6, "Bombs"),
|
"Fire Bomb x3": TunicItemData(ItemClassification.filler, 1, 6, "Bombs"),
|
||||||
"Ice Bomb x2": TunicItemData(IC.filler, 2, 7, "Bombs"),
|
"Ice Bomb x2": TunicItemData(ItemClassification.filler, 2, 7, "Bombs"),
|
||||||
"Ice Bomb x3": TunicItemData(IC.filler, 2, 8, "Bombs"),
|
"Ice Bomb x3": TunicItemData(ItemClassification.filler, 2, 8, "Bombs"),
|
||||||
"Ice Bomb x5": TunicItemData(IC.filler, 1, 9, "Bombs"),
|
"Ice Bomb x5": TunicItemData(ItemClassification.filler, 1, 9, "Bombs"),
|
||||||
"Lure": TunicItemData(IC.filler, 4, 10, "Consumables"),
|
"Lure": TunicItemData(ItemClassification.filler, 4, 10, "Consumables"),
|
||||||
"Lure x2": TunicItemData(IC.filler, 1, 11, "Consumables"),
|
"Lure x2": TunicItemData(ItemClassification.filler, 1, 11, "Consumables"),
|
||||||
"Pepper x2": TunicItemData(IC.filler, 4, 12, "Consumables"),
|
"Pepper x2": TunicItemData(ItemClassification.filler, 4, 12, "Consumables"),
|
||||||
"Ivy x3": TunicItemData(IC.filler, 2, 13, "Consumables"),
|
"Ivy x3": TunicItemData(ItemClassification.filler, 2, 13, "Consumables"),
|
||||||
"Effigy": TunicItemData(IC.useful, 12, 14, "Money"),
|
"Effigy": TunicItemData(ItemClassification.useful, 12, 14, "Money"),
|
||||||
"HP Berry": TunicItemData(IC.filler, 2, 15, "Consumables"),
|
"HP Berry": TunicItemData(ItemClassification.filler, 2, 15, "Consumables"),
|
||||||
"HP Berry x2": TunicItemData(IC.filler, 4, 16, "Consumables"),
|
"HP Berry x2": TunicItemData(ItemClassification.filler, 4, 16, "Consumables"),
|
||||||
"HP Berry x3": TunicItemData(IC.filler, 2, 17, "Consumables"),
|
"HP Berry x3": TunicItemData(ItemClassification.filler, 2, 17, "Consumables"),
|
||||||
"MP Berry": TunicItemData(IC.filler, 4, 18, "Consumables"),
|
"MP Berry": TunicItemData(ItemClassification.filler, 4, 18, "Consumables"),
|
||||||
"MP Berry x2": TunicItemData(IC.filler, 2, 19, "Consumables"),
|
"MP Berry x2": TunicItemData(ItemClassification.filler, 2, 19, "Consumables"),
|
||||||
"MP Berry x3": TunicItemData(IC.filler, 7, 20, "Consumables"),
|
"MP Berry x3": TunicItemData(ItemClassification.filler, 7, 20, "Consumables"),
|
||||||
"Fairy": TunicItemData(IC.progression, 20, 21),
|
"Fairy": TunicItemData(ItemClassification.progression, 20, 21),
|
||||||
"Stick": TunicItemData(IC.progression | IC.useful, 1, 22, "Weapons"),
|
"Stick": TunicItemData(ItemClassification.progression, 1, 22, "Weapons"),
|
||||||
"Sword": TunicItemData(IC.progression | IC.useful, 3, 23, "Weapons"),
|
"Sword": TunicItemData(ItemClassification.progression, 3, 23, "Weapons"),
|
||||||
"Sword Upgrade": TunicItemData(IC.progression | IC.useful, 4, 24, "Weapons"),
|
"Sword Upgrade": TunicItemData(ItemClassification.progression, 4, 24, "Weapons"),
|
||||||
"Magic Wand": TunicItemData(IC.progression | IC.useful, 1, 25, "Weapons"),
|
"Magic Wand": TunicItemData(ItemClassification.progression, 1, 25, "Weapons"),
|
||||||
"Magic Dagger": TunicItemData(IC.progression | IC.useful, 1, 26),
|
"Magic Dagger": TunicItemData(ItemClassification.progression, 1, 26),
|
||||||
"Magic Orb": TunicItemData(IC.progression | IC.useful, 1, 27),
|
"Magic Orb": TunicItemData(ItemClassification.progression, 1, 27),
|
||||||
"Hero's Laurels": TunicItemData(IC.progression | IC.useful, 1, 28),
|
"Hero's Laurels": TunicItemData(ItemClassification.progression, 1, 28),
|
||||||
"Lantern": TunicItemData(IC.progression, 1, 29),
|
"Lantern": TunicItemData(ItemClassification.progression, 1, 29),
|
||||||
"Gun": TunicItemData(IC.progression | IC.useful, 1, 30, "Weapons"),
|
"Gun": TunicItemData(ItemClassification.progression, 1, 30, "Weapons"),
|
||||||
"Shield": TunicItemData(IC.useful, 1, 31),
|
"Shield": TunicItemData(ItemClassification.useful, 1, 31),
|
||||||
"Dath Stone": TunicItemData(IC.useful, 1, 32),
|
"Dath Stone": TunicItemData(ItemClassification.useful, 1, 32),
|
||||||
"Hourglass": TunicItemData(IC.useful, 1, 33),
|
"Hourglass": TunicItemData(ItemClassification.useful, 1, 33),
|
||||||
"Old House Key": TunicItemData(IC.progression, 1, 34, "Keys"),
|
"Old House Key": TunicItemData(ItemClassification.progression, 1, 34, "Keys"),
|
||||||
"Key": TunicItemData(IC.progression, 2, 35, "Keys"),
|
"Key": TunicItemData(ItemClassification.progression, 2, 35, "Keys"),
|
||||||
"Fortress Vault Key": TunicItemData(IC.progression, 1, 36, "Keys"),
|
"Fortress Vault Key": TunicItemData(ItemClassification.progression, 1, 36, "Keys"),
|
||||||
"Flask Shard": TunicItemData(IC.useful, 12, 37),
|
"Flask Shard": TunicItemData(ItemClassification.useful, 12, 37),
|
||||||
"Potion Flask": TunicItemData(IC.useful, 5, 38, "Flask"),
|
"Potion Flask": TunicItemData(ItemClassification.useful, 5, 38, "Flask"),
|
||||||
"Golden Coin": TunicItemData(IC.progression, 17, 39),
|
"Golden Coin": TunicItemData(ItemClassification.progression, 17, 39),
|
||||||
"Card Slot": TunicItemData(IC.useful, 4, 40),
|
"Card Slot": TunicItemData(ItemClassification.useful, 4, 40),
|
||||||
"Red Questagon": TunicItemData(IC.progression_skip_balancing, 1, 41, "Hexagons"),
|
"Red Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 1, 41, "Hexagons"),
|
||||||
"Green Questagon": TunicItemData(IC.progression_skip_balancing, 1, 42, "Hexagons"),
|
"Green Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 1, 42, "Hexagons"),
|
||||||
"Blue Questagon": TunicItemData(IC.progression_skip_balancing, 1, 43, "Hexagons"),
|
"Blue Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 1, 43, "Hexagons"),
|
||||||
"Gold Questagon": TunicItemData(IC.progression_skip_balancing, 0, 44, "Hexagons"),
|
"Gold Questagon": TunicItemData(ItemClassification.progression_skip_balancing, 0, 44, "Hexagons"),
|
||||||
"ATT Offering": TunicItemData(IC.useful, 4, 45, "Offerings"),
|
"ATT Offering": TunicItemData(ItemClassification.useful, 4, 45, "Offerings"),
|
||||||
"DEF Offering": TunicItemData(IC.useful, 4, 46, "Offerings"),
|
"DEF Offering": TunicItemData(ItemClassification.useful, 4, 46, "Offerings"),
|
||||||
"Potion Offering": TunicItemData(IC.useful, 3, 47, "Offerings"),
|
"Potion Offering": TunicItemData(ItemClassification.useful, 3, 47, "Offerings"),
|
||||||
"HP Offering": TunicItemData(IC.useful, 6, 48, "Offerings"),
|
"HP Offering": TunicItemData(ItemClassification.useful, 6, 48, "Offerings"),
|
||||||
"MP Offering": TunicItemData(IC.useful, 3, 49, "Offerings"),
|
"MP Offering": TunicItemData(ItemClassification.useful, 3, 49, "Offerings"),
|
||||||
"SP Offering": TunicItemData(IC.useful, 2, 50, "Offerings"),
|
"SP Offering": TunicItemData(ItemClassification.useful, 2, 50, "Offerings"),
|
||||||
"Hero Relic - ATT": TunicItemData(IC.progression_skip_balancing, 1, 51, "Hero Relics"),
|
"Hero Relic - ATT": TunicItemData(ItemClassification.progression_skip_balancing, 1, 51, "Hero Relics"),
|
||||||
"Hero Relic - DEF": TunicItemData(IC.progression_skip_balancing, 1, 52, "Hero Relics"),
|
"Hero Relic - DEF": TunicItemData(ItemClassification.progression_skip_balancing, 1, 52, "Hero Relics"),
|
||||||
"Hero Relic - HP": TunicItemData(IC.progression_skip_balancing, 1, 53, "Hero Relics"),
|
"Hero Relic - HP": TunicItemData(ItemClassification.progression_skip_balancing, 1, 53, "Hero Relics"),
|
||||||
"Hero Relic - MP": TunicItemData(IC.progression_skip_balancing, 1, 54, "Hero Relics"),
|
"Hero Relic - MP": TunicItemData(ItemClassification.progression_skip_balancing, 1, 54, "Hero Relics"),
|
||||||
"Hero Relic - POTION": TunicItemData(IC.progression_skip_balancing, 1, 55, "Hero Relics"),
|
"Hero Relic - POTION": TunicItemData(ItemClassification.progression_skip_balancing, 1, 55, "Hero Relics"),
|
||||||
"Hero Relic - SP": TunicItemData(IC.progression_skip_balancing, 1, 56, "Hero Relics"),
|
"Hero Relic - SP": TunicItemData(ItemClassification.progression_skip_balancing, 1, 56, "Hero Relics"),
|
||||||
"Orange Peril Ring": TunicItemData(IC.useful, 1, 57, "Cards"),
|
"Orange Peril Ring": TunicItemData(ItemClassification.useful, 1, 57, "Cards"),
|
||||||
"Tincture": TunicItemData(IC.useful, 1, 58, "Cards"),
|
"Tincture": TunicItemData(ItemClassification.useful, 1, 58, "Cards"),
|
||||||
"Scavenger Mask": TunicItemData(IC.progression, 1, 59, "Cards"),
|
"Scavenger Mask": TunicItemData(ItemClassification.progression, 1, 59, "Cards"),
|
||||||
"Cyan Peril Ring": TunicItemData(IC.useful, 1, 60, "Cards"),
|
"Cyan Peril Ring": TunicItemData(ItemClassification.useful, 1, 60, "Cards"),
|
||||||
"Bracer": TunicItemData(IC.useful, 1, 61, "Cards"),
|
"Bracer": TunicItemData(ItemClassification.useful, 1, 61, "Cards"),
|
||||||
"Dagger Strap": TunicItemData(IC.useful, 1, 62, "Cards"),
|
"Dagger Strap": TunicItemData(ItemClassification.useful, 1, 62, "Cards"),
|
||||||
"Inverted Ash": TunicItemData(IC.useful, 1, 63, "Cards"),
|
"Inverted Ash": TunicItemData(ItemClassification.useful, 1, 63, "Cards"),
|
||||||
"Lucky Cup": TunicItemData(IC.useful, 1, 64, "Cards"),
|
"Lucky Cup": TunicItemData(ItemClassification.useful, 1, 64, "Cards"),
|
||||||
"Magic Echo": TunicItemData(IC.useful, 1, 65, "Cards"),
|
"Magic Echo": TunicItemData(ItemClassification.useful, 1, 65, "Cards"),
|
||||||
"Anklet": TunicItemData(IC.useful, 1, 66, "Cards"),
|
"Anklet": TunicItemData(ItemClassification.useful, 1, 66, "Cards"),
|
||||||
"Muffling Bell": TunicItemData(IC.useful, 1, 67, "Cards"),
|
"Muffling Bell": TunicItemData(ItemClassification.useful, 1, 67, "Cards"),
|
||||||
"Glass Cannon": TunicItemData(IC.useful, 1, 68, "Cards"),
|
"Glass Cannon": TunicItemData(ItemClassification.useful, 1, 68, "Cards"),
|
||||||
"Perfume": TunicItemData(IC.useful, 1, 69, "Cards"),
|
"Perfume": TunicItemData(ItemClassification.useful, 1, 69, "Cards"),
|
||||||
"Louder Echo": TunicItemData(IC.useful, 1, 70, "Cards"),
|
"Louder Echo": TunicItemData(ItemClassification.useful, 1, 70, "Cards"),
|
||||||
"Aura's Gem": TunicItemData(IC.useful, 1, 71, "Cards"),
|
"Aura's Gem": TunicItemData(ItemClassification.useful, 1, 71, "Cards"),
|
||||||
"Bone Card": TunicItemData(IC.useful, 1, 72, "Cards"),
|
"Bone Card": TunicItemData(ItemClassification.useful, 1, 72, "Cards"),
|
||||||
"Mr Mayor": TunicItemData(IC.useful, 1, 73, "Golden Treasures"),
|
"Mr Mayor": TunicItemData(ItemClassification.useful, 1, 73, "Golden Treasures"),
|
||||||
"Secret Legend": TunicItemData(IC.useful, 1, 74, "Golden Treasures"),
|
"Secret Legend": TunicItemData(ItemClassification.useful, 1, 74, "Golden Treasures"),
|
||||||
"Sacred Geometry": TunicItemData(IC.useful, 1, 75, "Golden Treasures"),
|
"Sacred Geometry": TunicItemData(ItemClassification.useful, 1, 75, "Golden Treasures"),
|
||||||
"Vintage": TunicItemData(IC.useful, 1, 76, "Golden Treasures"),
|
"Vintage": TunicItemData(ItemClassification.useful, 1, 76, "Golden Treasures"),
|
||||||
"Just Some Pals": TunicItemData(IC.useful, 1, 77, "Golden Treasures"),
|
"Just Some Pals": TunicItemData(ItemClassification.useful, 1, 77, "Golden Treasures"),
|
||||||
"Regal Weasel": TunicItemData(IC.useful, 1, 78, "Golden Treasures"),
|
"Regal Weasel": TunicItemData(ItemClassification.useful, 1, 78, "Golden Treasures"),
|
||||||
"Spring Falls": TunicItemData(IC.useful, 1, 79, "Golden Treasures"),
|
"Spring Falls": TunicItemData(ItemClassification.useful, 1, 79, "Golden Treasures"),
|
||||||
"Power Up": TunicItemData(IC.useful, 1, 80, "Golden Treasures"),
|
"Power Up": TunicItemData(ItemClassification.useful, 1, 80, "Golden Treasures"),
|
||||||
"Back To Work": TunicItemData(IC.useful, 1, 81, "Golden Treasures"),
|
"Back To Work": TunicItemData(ItemClassification.useful, 1, 81, "Golden Treasures"),
|
||||||
"Phonomath": TunicItemData(IC.useful, 1, 82, "Golden Treasures"),
|
"Phonomath": TunicItemData(ItemClassification.useful, 1, 82, "Golden Treasures"),
|
||||||
"Dusty": TunicItemData(IC.useful, 1, 83, "Golden Treasures"),
|
"Dusty": TunicItemData(ItemClassification.useful, 1, 83, "Golden Treasures"),
|
||||||
"Forever Friend": TunicItemData(IC.useful, 1, 84, "Golden Treasures"),
|
"Forever Friend": TunicItemData(ItemClassification.useful, 1, 84, "Golden Treasures"),
|
||||||
"Fool Trap": TunicItemData(IC.trap, 0, 85),
|
"Fool Trap": TunicItemData(ItemClassification.trap, 0, 85),
|
||||||
"Money x1": TunicItemData(IC.filler, 3, 86, "Money"),
|
"Money x1": TunicItemData(ItemClassification.filler, 3, 86, "Money"),
|
||||||
"Money x10": TunicItemData(IC.filler, 1, 87, "Money"),
|
"Money x10": TunicItemData(ItemClassification.filler, 1, 87, "Money"),
|
||||||
"Money x15": TunicItemData(IC.filler, 10, 88, "Money"),
|
"Money x15": TunicItemData(ItemClassification.filler, 10, 88, "Money"),
|
||||||
"Money x16": TunicItemData(IC.filler, 1, 89, "Money"),
|
"Money x16": TunicItemData(ItemClassification.filler, 1, 89, "Money"),
|
||||||
"Money x20": TunicItemData(IC.filler, 17, 90, "Money"),
|
"Money x20": TunicItemData(ItemClassification.filler, 17, 90, "Money"),
|
||||||
"Money x25": TunicItemData(IC.filler, 14, 91, "Money"),
|
"Money x25": TunicItemData(ItemClassification.filler, 14, 91, "Money"),
|
||||||
"Money x30": TunicItemData(IC.filler, 4, 92, "Money"),
|
"Money x30": TunicItemData(ItemClassification.filler, 4, 92, "Money"),
|
||||||
"Money x32": TunicItemData(IC.filler, 4, 93, "Money"),
|
"Money x32": TunicItemData(ItemClassification.filler, 4, 93, "Money"),
|
||||||
"Money x40": TunicItemData(IC.filler, 3, 94, "Money"),
|
"Money x40": TunicItemData(ItemClassification.filler, 3, 94, "Money"),
|
||||||
"Money x48": TunicItemData(IC.filler, 1, 95, "Money"),
|
"Money x48": TunicItemData(ItemClassification.filler, 1, 95, "Money"),
|
||||||
"Money x50": TunicItemData(IC.filler, 7, 96, "Money"),
|
"Money x50": TunicItemData(ItemClassification.filler, 7, 96, "Money"),
|
||||||
"Money x64": TunicItemData(IC.filler, 1, 97, "Money"),
|
"Money x64": TunicItemData(ItemClassification.filler, 1, 97, "Money"),
|
||||||
"Money x100": TunicItemData(IC.filler, 5, 98, "Money"),
|
"Money x100": TunicItemData(ItemClassification.filler, 5, 98, "Money"),
|
||||||
"Money x128": TunicItemData(IC.useful, 3, 99, "Money"),
|
"Money x128": TunicItemData(ItemClassification.useful, 3, 99, "Money"),
|
||||||
"Money x200": TunicItemData(IC.useful, 1, 100, "Money"),
|
"Money x200": TunicItemData(ItemClassification.useful, 1, 100, "Money"),
|
||||||
"Money x255": TunicItemData(IC.useful, 1, 101, "Money"),
|
"Money x255": TunicItemData(ItemClassification.useful, 1, 101, "Money"),
|
||||||
"Pages 0-1": TunicItemData(IC.useful, 1, 102, "Pages"),
|
"Pages 0-1": TunicItemData(ItemClassification.useful, 1, 102, "Pages"),
|
||||||
"Pages 2-3": TunicItemData(IC.useful, 1, 103, "Pages"),
|
"Pages 2-3": TunicItemData(ItemClassification.useful, 1, 103, "Pages"),
|
||||||
"Pages 4-5": TunicItemData(IC.useful, 1, 104, "Pages"),
|
"Pages 4-5": TunicItemData(ItemClassification.useful, 1, 104, "Pages"),
|
||||||
"Pages 6-7": TunicItemData(IC.useful, 1, 105, "Pages"),
|
"Pages 6-7": TunicItemData(ItemClassification.useful, 1, 105, "Pages"),
|
||||||
"Pages 8-9": TunicItemData(IC.useful, 1, 106, "Pages"),
|
"Pages 8-9": TunicItemData(ItemClassification.useful, 1, 106, "Pages"),
|
||||||
"Pages 10-11": TunicItemData(IC.useful, 1, 107, "Pages"),
|
"Pages 10-11": TunicItemData(ItemClassification.useful, 1, 107, "Pages"),
|
||||||
"Pages 12-13": TunicItemData(IC.useful, 1, 108, "Pages"),
|
"Pages 12-13": TunicItemData(ItemClassification.useful, 1, 108, "Pages"),
|
||||||
"Pages 14-15": TunicItemData(IC.useful, 1, 109, "Pages"),
|
"Pages 14-15": TunicItemData(ItemClassification.useful, 1, 109, "Pages"),
|
||||||
"Pages 16-17": TunicItemData(IC.useful, 1, 110, "Pages"),
|
"Pages 16-17": TunicItemData(ItemClassification.useful, 1, 110, "Pages"),
|
||||||
"Pages 18-19": TunicItemData(IC.useful, 1, 111, "Pages"),
|
"Pages 18-19": TunicItemData(ItemClassification.useful, 1, 111, "Pages"),
|
||||||
"Pages 20-21": TunicItemData(IC.useful, 1, 112, "Pages"),
|
"Pages 20-21": TunicItemData(ItemClassification.useful, 1, 112, "Pages"),
|
||||||
"Pages 22-23": TunicItemData(IC.useful, 1, 113, "Pages"),
|
"Pages 22-23": TunicItemData(ItemClassification.useful, 1, 113, "Pages"),
|
||||||
"Pages 24-25 (Prayer)": TunicItemData(IC.progression | IC.useful, 1, 114, "Pages"),
|
"Pages 24-25 (Prayer)": TunicItemData(ItemClassification.progression, 1, 114, "Pages"),
|
||||||
"Pages 26-27": TunicItemData(IC.useful, 1, 115, "Pages"),
|
"Pages 26-27": TunicItemData(ItemClassification.useful, 1, 115, "Pages"),
|
||||||
"Pages 28-29": TunicItemData(IC.useful, 1, 116, "Pages"),
|
"Pages 28-29": TunicItemData(ItemClassification.useful, 1, 116, "Pages"),
|
||||||
"Pages 30-31": TunicItemData(IC.useful, 1, 117, "Pages"),
|
"Pages 30-31": TunicItemData(ItemClassification.useful, 1, 117, "Pages"),
|
||||||
"Pages 32-33": TunicItemData(IC.useful, 1, 118, "Pages"),
|
"Pages 32-33": TunicItemData(ItemClassification.useful, 1, 118, "Pages"),
|
||||||
"Pages 34-35": TunicItemData(IC.useful, 1, 119, "Pages"),
|
"Pages 34-35": TunicItemData(ItemClassification.useful, 1, 119, "Pages"),
|
||||||
"Pages 36-37": TunicItemData(IC.useful, 1, 120, "Pages"),
|
"Pages 36-37": TunicItemData(ItemClassification.useful, 1, 120, "Pages"),
|
||||||
"Pages 38-39": TunicItemData(IC.useful, 1, 121, "Pages"),
|
"Pages 38-39": TunicItemData(ItemClassification.useful, 1, 121, "Pages"),
|
||||||
"Pages 40-41": TunicItemData(IC.useful, 1, 122, "Pages"),
|
"Pages 40-41": TunicItemData(ItemClassification.useful, 1, 122, "Pages"),
|
||||||
"Pages 42-43 (Holy Cross)": TunicItemData(IC.progression | IC.useful, 1, 123, "Pages"),
|
"Pages 42-43 (Holy Cross)": TunicItemData(ItemClassification.progression, 1, 123, "Pages"),
|
||||||
"Pages 44-45": TunicItemData(IC.useful, 1, 124, "Pages"),
|
"Pages 44-45": TunicItemData(ItemClassification.useful, 1, 124, "Pages"),
|
||||||
"Pages 46-47": TunicItemData(IC.useful, 1, 125, "Pages"),
|
"Pages 46-47": TunicItemData(ItemClassification.useful, 1, 125, "Pages"),
|
||||||
"Pages 48-49": TunicItemData(IC.useful, 1, 126, "Pages"),
|
"Pages 48-49": TunicItemData(ItemClassification.useful, 1, 126, "Pages"),
|
||||||
"Pages 50-51": TunicItemData(IC.useful, 1, 127, "Pages"),
|
"Pages 50-51": TunicItemData(ItemClassification.useful, 1, 127, "Pages"),
|
||||||
"Pages 52-53 (Icebolt)": TunicItemData(IC.progression, 1, 128, "Pages"),
|
"Pages 52-53 (Icebolt)": TunicItemData(ItemClassification.progression, 1, 128, "Pages"),
|
||||||
"Pages 54-55": TunicItemData(IC.useful, 1, 129, "Pages"),
|
"Pages 54-55": TunicItemData(ItemClassification.useful, 1, 129, "Pages"),
|
||||||
"Ladders near Weathervane": TunicItemData(IC.progression, 0, 130, "Ladders"),
|
"Ladders near Weathervane": TunicItemData(ItemClassification.progression, 0, 130, "Ladders"),
|
||||||
"Ladders near Overworld Checkpoint": TunicItemData(IC.progression, 0, 131, "Ladders"),
|
"Ladders near Overworld Checkpoint": TunicItemData(ItemClassification.progression, 0, 131, "Ladders"),
|
||||||
"Ladders near Patrol Cave": TunicItemData(IC.progression, 0, 132, "Ladders"),
|
"Ladders near Patrol Cave": TunicItemData(ItemClassification.progression, 0, 132, "Ladders"),
|
||||||
"Ladder near Temple Rafters": TunicItemData(IC.progression, 0, 133, "Ladders"),
|
"Ladder near Temple Rafters": TunicItemData(ItemClassification.progression, 0, 133, "Ladders"),
|
||||||
"Ladders near Dark Tomb": TunicItemData(IC.progression, 0, 134, "Ladders"),
|
"Ladders near Dark Tomb": TunicItemData(ItemClassification.progression, 0, 134, "Ladders"),
|
||||||
"Ladder to Quarry": TunicItemData(IC.progression, 0, 135, "Ladders"),
|
"Ladder to Quarry": TunicItemData(ItemClassification.progression, 0, 135, "Ladders"),
|
||||||
"Ladders to West Bell": TunicItemData(IC.progression, 0, 136, "Ladders"),
|
"Ladders to West Bell": TunicItemData(ItemClassification.progression, 0, 136, "Ladders"),
|
||||||
"Ladders in Overworld Town": TunicItemData(IC.progression, 0, 137, "Ladders"),
|
"Ladders in Overworld Town": TunicItemData(ItemClassification.progression, 0, 137, "Ladders"),
|
||||||
"Ladder to Ruined Atoll": TunicItemData(IC.progression, 0, 138, "Ladders"),
|
"Ladder to Ruined Atoll": TunicItemData(ItemClassification.progression, 0, 138, "Ladders"),
|
||||||
"Ladder to Swamp": TunicItemData(IC.progression, 0, 139, "Ladders"),
|
"Ladder to Swamp": TunicItemData(ItemClassification.progression, 0, 139, "Ladders"),
|
||||||
"Ladders in Well": TunicItemData(IC.progression, 0, 140, "Ladders"),
|
"Ladders in Well": TunicItemData(ItemClassification.progression, 0, 140, "Ladders"),
|
||||||
"Ladder in Dark Tomb": TunicItemData(IC.progression, 0, 141, "Ladders"),
|
"Ladder in Dark Tomb": TunicItemData(ItemClassification.progression, 0, 141, "Ladders"),
|
||||||
"Ladder to East Forest": TunicItemData(IC.progression, 0, 142, "Ladders"),
|
"Ladder to East Forest": TunicItemData(ItemClassification.progression, 0, 142, "Ladders"),
|
||||||
"Ladders to Lower Forest": TunicItemData(IC.progression, 0, 143, "Ladders"),
|
"Ladders to Lower Forest": TunicItemData(ItemClassification.progression, 0, 143, "Ladders"),
|
||||||
"Ladder to Beneath the Vault": TunicItemData(IC.progression, 0, 144, "Ladders"),
|
"Ladder to Beneath the Vault": TunicItemData(ItemClassification.progression, 0, 144, "Ladders"),
|
||||||
"Ladders in Hourglass Cave": TunicItemData(IC.progression, 0, 145, "Ladders"),
|
"Ladders in Hourglass Cave": TunicItemData(ItemClassification.progression, 0, 145, "Ladders"),
|
||||||
"Ladders in South Atoll": TunicItemData(IC.progression, 0, 146, "Ladders"),
|
"Ladders in South Atoll": TunicItemData(ItemClassification.progression, 0, 146, "Ladders"),
|
||||||
"Ladders to Frog's Domain": TunicItemData(IC.progression, 0, 147, "Ladders"),
|
"Ladders to Frog's Domain": TunicItemData(ItemClassification.progression, 0, 147, "Ladders"),
|
||||||
"Ladders in Library": TunicItemData(IC.progression, 0, 148, "Ladders"),
|
"Ladders in Library": TunicItemData(ItemClassification.progression, 0, 148, "Ladders"),
|
||||||
"Ladders in Lower Quarry": TunicItemData(IC.progression, 0, 149, "Ladders"),
|
"Ladders in Lower Quarry": TunicItemData(ItemClassification.progression, 0, 149, "Ladders"),
|
||||||
"Ladders in Swamp": TunicItemData(IC.progression, 0, 150, "Ladders"),
|
"Ladders in Swamp": TunicItemData(ItemClassification.progression, 0, 150, "Ladders"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# items to be replaced by fool traps
|
# items to be replaced by fool traps
|
||||||
@@ -208,7 +208,7 @@ slot_data_item_names = [
|
|||||||
|
|
||||||
item_name_to_id: Dict[str, int] = {name: item_base_id + data.item_id_offset for name, data in item_table.items()}
|
item_name_to_id: Dict[str, int] = {name: item_base_id + data.item_id_offset for name, data in item_table.items()}
|
||||||
|
|
||||||
filler_items: List[str] = [name for name, data in item_table.items() if data.classification == IC.filler]
|
filler_items: List[str] = [name for name, data in item_table.items() if data.classification == ItemClassification.filler]
|
||||||
|
|
||||||
|
|
||||||
def get_item_group(item_name: str) -> str:
|
def get_item_group(item_name: str) -> str:
|
||||||
|
|||||||
@@ -101,15 +101,14 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
|
|||||||
return yachtdice_cache[player][tup]
|
return yachtdice_cache[player][tup]
|
||||||
|
|
||||||
# sort categories because for the step multiplier, you will want low-scoring categories first
|
# sort categories because for the step multiplier, you will want low-scoring categories first
|
||||||
# to avoid errors with order changing when obtaining rolls, we order assuming 4 rolls
|
categories.sort(key=lambda category: category.mean_score(num_dice, num_rolls))
|
||||||
categories.sort(key=lambda category: category.mean_score(num_dice, 4))
|
|
||||||
|
|
||||||
# function to add two discrete distribution.
|
# function to add two discrete distribution.
|
||||||
# defaultdict is a dict where you don't need to check if an id is present, you can just use += (lot faster)
|
# defaultdict is a dict where you don't need to check if an id is present, you can just use += (lot faster)
|
||||||
def add_distributions(dist1, dist2):
|
def add_distributions(dist1, dist2):
|
||||||
combined_dist = defaultdict(float)
|
combined_dist = defaultdict(float)
|
||||||
for val2, prob2 in dist2.items():
|
for val1, prob1 in dist1.items():
|
||||||
for val1, prob1 in dist1.items():
|
for val2, prob2 in dist2.items():
|
||||||
combined_dist[val1 + val2] += prob1 * prob2
|
combined_dist[val1 + val2] += prob1 * prob2
|
||||||
return dict(combined_dist)
|
return dict(combined_dist)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@ class YachtDiceWorld(World):
|
|||||||
|
|
||||||
item_name_groups = item_groups
|
item_name_groups = item_groups
|
||||||
|
|
||||||
ap_world_version = "2.1.4"
|
ap_world_version = "2.1.3"
|
||||||
|
|
||||||
def _get_yachtdice_data(self):
|
def _get_yachtdice_data(self):
|
||||||
return {
|
return {
|
||||||
@@ -468,7 +468,7 @@ class YachtDiceWorld(World):
|
|||||||
menu.exits.append(connection)
|
menu.exits.append(connection)
|
||||||
connection.connect(board)
|
connection.connect(board)
|
||||||
self.multiworld.regions += [menu, board]
|
self.multiworld.regions += [menu, board]
|
||||||
|
|
||||||
def get_filler_item_name(self) -> str:
|
def get_filler_item_name(self) -> str:
|
||||||
return "Good RNG"
|
return "Good RNG"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user