Skip to content

Commit

Permalink
Merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
PoryGone committed Mar 5, 2024
2 parents ac9409d + 644f759 commit 5bb6288
Show file tree
Hide file tree
Showing 74 changed files with 9,645 additions and 214 deletions.
15 changes: 12 additions & 3 deletions BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,14 +717,23 @@ def can_reach(self,
assert isinstance(player, int), "can_reach: player is required if spot is str"
# try to resolve a name
if resolution_hint == 'Location':
spot = self.multiworld.get_location(spot, player)
return self.can_reach_location(spot, player)
elif resolution_hint == 'Entrance':
spot = self.multiworld.get_entrance(spot, player)
return self.can_reach_entrance(spot, player)
else:
# default to Region
spot = self.multiworld.get_region(spot, player)
return self.can_reach_region(spot, player)
return spot.can_reach(self)

def can_reach_location(self, spot: str, player: int) -> bool:
return self.multiworld.get_location(spot, player).can_reach(self)

def can_reach_entrance(self, spot: str, player: int) -> bool:
return self.multiworld.get_entrance(spot, player).can_reach(self)

def can_reach_region(self, spot: str, player: int) -> bool:
return self.multiworld.get_region(spot, player).can_reach(self)

def sweep_for_events(self, key_only: bool = False, locations: Optional[Iterable[Location]] = None) -> None:
if locations is None:
locations = self.multiworld.get_filled_locations()
Expand Down
32 changes: 19 additions & 13 deletions Options.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
from __future__ import annotations

import abc
import logging
from copy import deepcopy
from dataclasses import dataclass
import functools
import logging
import math
import numbers
import random
import typing
from copy import deepcopy
from dataclasses import dataclass

from schema import And, Optional, Or, Schema

from Utils import get_fuzzy_results
from Utils import get_fuzzy_results, is_iterable_of_str

if typing.TYPE_CHECKING:
from BaseClasses import PlandoOptions
Expand Down Expand Up @@ -59,6 +58,7 @@ def __new__(mcs, name, bases, attrs):
def verify(self, *args, **kwargs) -> None:
for f in verifiers:
f(self, *args, **kwargs)

attrs["verify"] = verify
else:
assert verifiers, "class Option is supposed to implement def verify"
Expand Down Expand Up @@ -183,6 +183,7 @@ def get_option_name(cls, value: str) -> str:

class NumericOption(Option[int], numbers.Integral, abc.ABC):
default = 0

# note: some of the `typing.Any`` here is a result of unresolved issue in python standards
# `int` is not a `numbers.Integral` according to the official typestubs
# (even though isinstance(5, numbers.Integral) == True)
Expand Down Expand Up @@ -598,7 +599,7 @@ def verify(self, world: typing.Type[World], player_name: str, plando_options: "P
if isinstance(self.value, int):
return
from BaseClasses import PlandoOptions
if not(PlandoOptions.bosses & plando_options):
if not (PlandoOptions.bosses & plando_options):
# plando is disabled but plando options were given so pull the option and change it to an int
option = self.value.split(";")[-1]
self.value = self.options[option]
Expand Down Expand Up @@ -727,7 +728,7 @@ def __new__(cls, value: int) -> SpecialRange:
"Consider switching to NamedRange, which supports all use-cases of SpecialRange, and more. In "
"NamedRange, range_start specifies the lower end of the regular range, while special values can be "
"placed anywhere (below, inside, or above the regular range).")
return super().__new__(cls, value)
return super().__new__(cls)

@classmethod
def weighted_range(cls, text) -> Range:
Expand Down Expand Up @@ -765,7 +766,7 @@ class VerifyKeys(metaclass=FreezeValidKeys):
value: typing.Any

@classmethod
def verify_keys(cls, data: typing.List[str]):
def verify_keys(cls, data: typing.Iterable[str]) -> None:
if cls.valid_keys:
data = set(data)
dataset = set(word.casefold() for word in data) if cls.valid_keys_casefold else set(data)
Expand Down Expand Up @@ -843,11 +844,11 @@ class OptionList(Option[typing.List[typing.Any]], VerifyKeys):
# If only unique entries are needed and input order of elements does not matter, OptionSet should be used instead.
# Not a docstring so it doesn't get grabbed by the options system.

default: typing.List[typing.Any] = []
default: typing.Union[typing.List[typing.Any], typing.Tuple[typing.Any, ...]] = ()
supports_weighting = False

def __init__(self, value: typing.List[typing.Any]):
self.value = deepcopy(value)
def __init__(self, value: typing.Iterable[str]):
self.value = list(deepcopy(value))
super(OptionList, self).__init__()

@classmethod
Expand All @@ -856,7 +857,7 @@ def from_text(cls, text: str):

@classmethod
def from_any(cls, data: typing.Any):
if type(data) == list:
if is_iterable_of_str(data):
cls.verify_keys(data)
return cls(data)
return cls.from_text(str(data))
Expand All @@ -882,7 +883,7 @@ def from_text(cls, text: str):

@classmethod
def from_any(cls, data: typing.Any):
if isinstance(data, (list, set, frozenset)):
if is_iterable_of_str(data):
cls.verify_keys(data)
return cls(data)
return cls.from_text(str(data))
Expand Down Expand Up @@ -932,7 +933,7 @@ def __new__(mcs,
bases: typing.Tuple[type, ...],
attrs: typing.Dict[str, typing.Any]) -> "OptionsMetaProperty":
for attr_type in attrs.values():
assert not isinstance(attr_type, AssembleOptions),\
assert not isinstance(attr_type, AssembleOptions), \
f"Options for {name} should be type hinted on the class, not assigned"
return super().__new__(mcs, name, bases, attrs)

Expand Down Expand Up @@ -1110,6 +1111,11 @@ class PerGameCommonOptions(CommonOptions):
item_links: ItemLinks


@dataclass
class DeathLinkMixin:
death_link: DeathLink


def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True):
import os

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Currently, the following games are supported:
* Landstalker: The Treasures of King Nole
* Final Fantasy Mystic Quest
* TUNIC
* Kirby's Dream Land 3
* Celeste 64

For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Expand Down
11 changes: 11 additions & 0 deletions Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from argparse import Namespace
from settings import Settings, get_settings
from typing import BinaryIO, Coroutine, Optional, Set, Dict, Any, Union
from typing_extensions import TypeGuard
from yaml import load, load_all, dump

try:
Expand Down Expand Up @@ -966,3 +967,13 @@ def __bool__(self):

def __len__(self):
return sum(len(iterable) for iterable in self.iterable)


def is_iterable_of_str(obj: object) -> TypeGuard[typing.Iterable[str]]:
""" but not a `str` (because technically, `str` is `Iterable[str]`) """
if isinstance(obj, str):
return False
if not isinstance(obj, typing.Iterable):
return False
obj_it: typing.Iterable[object] = obj
return all(isinstance(v, str) for v in obj_it)
3 changes: 3 additions & 0 deletions docs/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
# Hylics 2
/worlds/hylics2/ @TRPG0

# Kirby's Dream Land 3
/worlds/kdl3/ @Silvris

# Kingdom Hearts 2
/worlds/kh2/ @JaredWeakStrike

Expand Down
12 changes: 11 additions & 1 deletion docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@ It is recommended that automated github actions are turned on in your fork to ha
You can turn them on here:
![Github actions example](./img/github-actions-example.png)

Other than these requests, we tend to judge code on a case by case basis.
* **When reviewing PRs, please leave a message about what was done.**
We don't have full test coverage, so manual testing can help.
For code changes that could affect multiple worlds or that could have changes in unexpected code paths, manual testing
or checking if all code paths are covered by automated tests is desired. The original author may not have been able
to test all possibly affected worlds, or didn't know it would affect another world. In such cases, it is helpful to
state which games or settings were rolled, if any.
Please also tell us if you looked at code, just did functional testing, did both, or did neither.
If testing the PR depends on other PRs, please state what you merged into what for testing.
We cannot determine what "LGTM" means without additional context, so that should not be the norm.

Other than these requests, we tend to judge code on a case-by-case basis.

For contribution to the website, please refer to the [WebHost README](/WebHostLib/README.md).

Expand Down
4 changes: 4 additions & 0 deletions docs/settings api.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ Path to a single file. Automatically resolves as user_path:
Source folder or AP install path on Windows. ~/Archipelago for the AppImage.
Will open a file browser if the file is missing when in GUI mode.

If the file is used in the world's `generate_output`, make sure to add a `stage_assert_generate` that checks if the
file is available, otherwise generation may fail at the very end.
See also [world api.md](https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/world%20api.md#generation).

#### class method validate(cls, path: str)

Override this and raise ValueError if validation fails.
Expand Down
5 changes: 5 additions & 0 deletions inno_setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ Root: HKCR; Subkey: "{#MyAppName}l2acpatch"; ValueData: "Arc
Root: HKCR; Subkey: "{#MyAppName}l2acpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}l2acpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: "";

Root: HKCR; Subkey: ".apkdl3"; ValueData: "{#MyAppName}kdl3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}kdl3patch"; ValueData: "Archipelago Kirby's Dream Land 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}kdl3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}kdl3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni

Root: HKCR; Subkey: ".apmc"; ValueData: "{#MyAppName}mcdata"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archipelago Minecraft Data"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mcdata\DefaultIcon"; ValueData: "{app}\ArchipelagoMinecraftClient.exe,0"; ValueType: string; ValueName: "";
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ certifi>=2023.11.17
cython>=3.0.8
cymem>=2.0.8
orjson>=3.9.10
typing-extensions>=4.7.0
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
"Super Mario 64",
"VVVVVV",
"Wargroove",
"Zillion",
}

# LogicMixin is broken before 3.10 import revamp
Expand Down
2 changes: 1 addition & 1 deletion typings/kivy/graphics/texture.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class FillType_Drawable:


class Texture:
pass
size: FillType_Vec
17 changes: 16 additions & 1 deletion worlds/Files.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ def get_handler(file: str) -> Optional[AutoPatchRegister]:
current_patch_version: int = 5


class InvalidDataError(Exception):
"""
Since games can override `read_contents` in APContainer,
this is to report problems in that process.
"""


class APContainer:
"""A zipfile containing at least archipelago.json"""
version: int = current_patch_version
Expand Down Expand Up @@ -89,7 +96,15 @@ def read(self, file: Optional[Union[str, BinaryIO]] = None) -> None:
with zipfile.ZipFile(zip_file, "r") as zf:
if file:
self.path = zf.filename
self.read_contents(zf)
try:
self.read_contents(zf)
except Exception as e:
message = ""
if len(e.args):
arg0 = e.args[0]
if isinstance(arg0, str):
message = f"{arg0} - "
raise InvalidDataError(f"{message}This might be the incorrect world version for this file") from e

def read_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
with opened_zipfile.open("archipelago.json", "r") as f:
Expand Down
2 changes: 1 addition & 1 deletion worlds/LauncherComponents.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def launch_textclient():
# SNI
Component('SNI Client', 'SNIClient',
file_identifier=SuffixIdentifier('.apz3', '.apm3', '.apsoe', '.aplttp', '.apsm', '.apsmz3', '.apdkc3',
'.apsmw', '.apl2ac')),
'.apsmw', '.apl2ac', '.apkdl3')),
Component('Links Awakening DX Client', 'LinksAwakeningClient',
file_identifier=SuffixIdentifier('.apladx')),
Component('LttP Adjuster', 'LttPAdjuster'),
Expand Down
Loading

0 comments on commit 5bb6288

Please sign in to comment.