SoE: update to pyevermizer v0.48.0 (#3050)
This commit is contained in:
parent
c97215e0e7
commit
5d9d4ed9f1
|
@ -13,7 +13,7 @@ from Utils import output_path
|
|||
from worlds.AutoWorld import WebWorld, World
|
||||
from worlds.generic.Rules import add_item_rule, set_rule
|
||||
from .logic import SoEPlayerLogic
|
||||
from .options import Difficulty, EnergyCore, SoEOptions
|
||||
from .options import Difficulty, EnergyCore, Sniffamizer, SniffIngredients, SoEOptions
|
||||
from .patch import SoEDeltaPatch, get_base_rom_path
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
|
@ -64,20 +64,28 @@ _id_offset: typing.Dict[int, int] = {
|
|||
pyevermizer.CHECK_BOSS: _id_base + 50, # bosses 64050..6499
|
||||
pyevermizer.CHECK_GOURD: _id_base + 100, # gourds 64100..64399
|
||||
pyevermizer.CHECK_NPC: _id_base + 400, # npc 64400..64499
|
||||
# TODO: sniff 64500..64799
|
||||
# blank 64500..64799
|
||||
pyevermizer.CHECK_EXTRA: _id_base + 800, # extra items 64800..64899
|
||||
pyevermizer.CHECK_TRAP: _id_base + 900, # trap 64900..64999
|
||||
pyevermizer.CHECK_SNIFF: _id_base + 1000 # sniff 65000..65592
|
||||
}
|
||||
|
||||
# cache native evermizer items and locations
|
||||
_items = pyevermizer.get_items()
|
||||
_sniff_items = pyevermizer.get_sniff_items() # optional, not part of the default location pool
|
||||
_traps = pyevermizer.get_traps()
|
||||
_extras = pyevermizer.get_extra_items() # items that are not placed by default
|
||||
_locations = pyevermizer.get_locations()
|
||||
_sniff_locations = pyevermizer.get_sniff_locations() # optional, not part of the default location pool
|
||||
# fix up texts for AP
|
||||
for _loc in _locations:
|
||||
if _loc.type == pyevermizer.CHECK_GOURD:
|
||||
_loc.name = f'{_loc.name} #{_loc.index}'
|
||||
_loc.name = f"{_loc.name} #{_loc.index}"
|
||||
for _loc in _sniff_locations:
|
||||
if _loc.type == pyevermizer.CHECK_SNIFF:
|
||||
_loc.name = f"{_loc.name} Sniff #{_loc.index}"
|
||||
del _loc
|
||||
|
||||
# item helpers
|
||||
_ingredients = (
|
||||
'Wax', 'Water', 'Vinegar', 'Root', 'Oil', 'Mushroom', 'Mud Pepper', 'Meteorite', 'Limestone', 'Iron',
|
||||
|
@ -97,7 +105,7 @@ def _match_item_name(item: pyevermizer.Item, substr: str) -> bool:
|
|||
def _get_location_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[int, pyevermizer.Location]]:
|
||||
name_to_id = {}
|
||||
id_to_raw = {}
|
||||
for loc in _locations:
|
||||
for loc in itertools.chain(_locations, _sniff_locations):
|
||||
ap_id = _id_offset[loc.type] + loc.index
|
||||
id_to_raw[ap_id] = loc
|
||||
name_to_id[loc.name] = ap_id
|
||||
|
@ -108,7 +116,7 @@ def _get_location_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[i
|
|||
def _get_item_mapping() -> typing.Tuple[typing.Dict[str, int], typing.Dict[int, pyevermizer.Item]]:
|
||||
name_to_id = {}
|
||||
id_to_raw = {}
|
||||
for item in itertools.chain(_items, _extras, _traps):
|
||||
for item in itertools.chain(_items, _sniff_items, _extras, _traps):
|
||||
if item.name in name_to_id:
|
||||
continue
|
||||
ap_id = _id_offset[item.type] + item.index
|
||||
|
@ -168,9 +176,9 @@ class SoEWorld(World):
|
|||
options: SoEOptions
|
||||
settings: typing.ClassVar[SoESettings]
|
||||
topology_present = False
|
||||
data_version = 4
|
||||
data_version = 5
|
||||
web = SoEWebWorld()
|
||||
required_client_version = (0, 3, 5)
|
||||
required_client_version = (0, 4, 4)
|
||||
|
||||
item_name_to_id, item_id_to_raw = _get_item_mapping()
|
||||
location_name_to_id, location_id_to_raw = _get_location_mapping()
|
||||
|
@ -238,16 +246,26 @@ class SoEWorld(World):
|
|||
spheres.setdefault(get_sphere_index(loc), {}).setdefault(loc.type, []).append(
|
||||
SoELocation(self.player, loc.name, self.location_name_to_id[loc.name], ingame,
|
||||
loc.difficulty > max_difficulty))
|
||||
# extend pool if feature and setting enabled
|
||||
if hasattr(Sniffamizer, "option_everywhere") and self.options.sniffamizer == Sniffamizer.option_everywhere:
|
||||
for loc in _sniff_locations:
|
||||
spheres.setdefault(get_sphere_index(loc), {}).setdefault(loc.type, []).append(
|
||||
SoELocation(self.player, loc.name, self.location_name_to_id[loc.name], ingame,
|
||||
loc.difficulty > max_difficulty))
|
||||
|
||||
# location balancing data
|
||||
trash_fills: typing.Dict[int, typing.Dict[int, typing.Tuple[int, int, int, int]]] = {
|
||||
0: {pyevermizer.CHECK_GOURD: (20, 40, 40, 40)}, # remove up to 40 gourds from sphere 1
|
||||
1: {pyevermizer.CHECK_GOURD: (70, 90, 90, 90)}, # remove up to 90 gourds from sphere 2
|
||||
0: {pyevermizer.CHECK_GOURD: (20, 40, 40, 40), # remove up to 40 gourds from sphere 1
|
||||
pyevermizer.CHECK_SNIFF: (100, 130, 130, 130)}, # remove up to 130 sniff spots from sphere 1
|
||||
1: {pyevermizer.CHECK_GOURD: (70, 90, 90, 90), # remove up to 90 gourds from sphere 2
|
||||
pyevermizer.CHECK_SNIFF: (160, 200, 200, 200)}, # remove up to 200 sniff spots from sphere 2
|
||||
}
|
||||
|
||||
# mark some as excluded based on numbers above
|
||||
for trash_sphere, fills in trash_fills.items():
|
||||
for typ, counts in fills.items():
|
||||
if typ not in spheres[trash_sphere]:
|
||||
continue # e.g. player does not have sniff locations
|
||||
count = counts[self.options.difficulty.value]
|
||||
for location in self.random.sample(spheres[trash_sphere][typ], count):
|
||||
assert location.name != "Energy Core #285", "Error in sphere generation"
|
||||
|
@ -299,6 +317,15 @@ class SoEWorld(World):
|
|||
# remove one pair of wings that will be placed in generate_basic
|
||||
items.remove(self.create_item("Wings"))
|
||||
|
||||
# extend pool if feature and setting enabled
|
||||
if hasattr(Sniffamizer, "option_everywhere") and self.options.sniffamizer == Sniffamizer.option_everywhere:
|
||||
if self.options.sniff_ingredients == SniffIngredients.option_vanilla_ingredients:
|
||||
# vanilla ingredients
|
||||
items += list(map(lambda item: self.create_item(item), _sniff_items))
|
||||
else:
|
||||
# random ingredients
|
||||
items += [self.create_item(self.get_filler_item_name()) for _ in _sniff_items]
|
||||
|
||||
def is_ingredient(item: pyevermizer.Item) -> bool:
|
||||
for ingredient in _ingredients:
|
||||
if _match_item_name(item, ingredient):
|
||||
|
@ -345,7 +372,12 @@ class SoEWorld(World):
|
|||
set_rule(self.multiworld.get_location('Done', self.player),
|
||||
lambda state: self.logic.has(state, pyevermizer.P_FINAL_BOSS))
|
||||
set_rule(self.multiworld.get_entrance('New Game', self.player), lambda state: True)
|
||||
for loc in _locations:
|
||||
locations: typing.Iterable[pyevermizer.Location]
|
||||
if hasattr(Sniffamizer, "option_everywhere") and self.options.sniffamizer == Sniffamizer.option_everywhere:
|
||||
locations = itertools.chain(_locations, _sniff_locations)
|
||||
else:
|
||||
locations = _locations
|
||||
for loc in locations:
|
||||
location = self.multiworld.get_location(loc.name, self.player)
|
||||
set_rule(location, self.make_rule(loc.requires))
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import typing
|
||||
from itertools import chain
|
||||
from typing import Callable, Set
|
||||
|
||||
from . import pyevermizer
|
||||
|
@ -11,10 +12,12 @@ if typing.TYPE_CHECKING:
|
|||
|
||||
# TODO: resolve/flatten/expand rules to get rid of recursion below where possible
|
||||
# Logic.rules are all rules including locations, excluding those with no progress (i.e. locations that only drop items)
|
||||
rules = [rule for rule in pyevermizer.get_logic() if len(rule.provides) > 0]
|
||||
rules = pyevermizer.get_logic()
|
||||
# Logic.items are all items and extra items excluding non-progression items and duplicates
|
||||
# NOTE: we are skipping sniff items here because none of them is supposed to provide progression
|
||||
item_names: Set[str] = set()
|
||||
items = [item for item in filter(lambda item: item.progression, pyevermizer.get_items() + pyevermizer.get_extra_items())
|
||||
items = [item for item in filter(lambda item: item.progression, # type: ignore[arg-type]
|
||||
chain(pyevermizer.get_items(), pyevermizer.get_extra_items()))
|
||||
if item.name not in item_names and not item_names.add(item.name)] # type: ignore[func-returns-value]
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from dataclasses import dataclass, fields
|
||||
from datetime import datetime
|
||||
from typing import Any, ClassVar, cast, Dict, Iterator, List, Tuple, Protocol
|
||||
|
||||
from Options import AssembleOptions, Choice, DeathLink, DefaultOnToggle, Option, PerGameCommonOptions, \
|
||||
|
@ -158,13 +159,30 @@ class Ingredienizer(EvermizerFlags, OffOnFullChoice):
|
|||
flags = ['i', '', 'I']
|
||||
|
||||
|
||||
class Sniffamizer(EvermizerFlags, OffOnFullChoice):
|
||||
"""On Shuffles, Full randomizes drops in sniff locations"""
|
||||
class Sniffamizer(EvermizerFlags, Choice):
|
||||
"""
|
||||
Off: all vanilla items in sniff spots
|
||||
Shuffle: sniff items shuffled into random sniff spots
|
||||
"""
|
||||
display_name = "Sniffamizer"
|
||||
option_off = 0
|
||||
option_shuffle = 1
|
||||
if datetime.today().year > 2024 or datetime.today().month > 3:
|
||||
option_everywhere = 2
|
||||
__doc__ = __doc__ + " Everywhere: add sniff spots to multiworld pool"
|
||||
alias_true = 1
|
||||
default = 1
|
||||
flags = ['s', '', 'S']
|
||||
|
||||
|
||||
class SniffIngredients(EvermizerFlag, Choice):
|
||||
"""Select which items should be used as sniff items"""
|
||||
display_name = "Sniff Ingredients"
|
||||
option_vanilla_ingredients = 0
|
||||
option_random_ingredients = 1
|
||||
flag = 'v'
|
||||
|
||||
|
||||
class Callbeadamizer(EvermizerFlags, OffOnFullChoice):
|
||||
"""On Shuffles call bead characters, Full shuffles individual spells"""
|
||||
display_name = "Callbeadamizer"
|
||||
|
@ -207,7 +225,7 @@ class ItemChanceMeta(AssembleOptions):
|
|||
attrs["display_name"] = f"{attrs['item_name']} Chance"
|
||||
attrs["range_start"] = 0
|
||||
attrs["range_end"] = 100
|
||||
cls = super(ItemChanceMeta, mcs).__new__(mcs, name, bases, attrs)
|
||||
cls = super(ItemChanceMeta, mcs).__new__(mcs, name, bases, attrs) # type: ignore[no-untyped-call]
|
||||
return cast(ItemChanceMeta, cls)
|
||||
|
||||
|
||||
|
@ -268,6 +286,7 @@ class SoEOptions(PerGameCommonOptions):
|
|||
short_boss_rush: ShortBossRush
|
||||
ingredienizer: Ingredienizer
|
||||
sniffamizer: Sniffamizer
|
||||
sniff_ingredients: SniffIngredients
|
||||
callbeadamizer: Callbeadamizer
|
||||
musicmizer: Musicmizer
|
||||
doggomizer: Doggomizer
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
pyevermizer==0.46.1 \
|
||||
--hash=sha256:9fd71b5e4af26a5dd24a9cbf5320bf0111eef80320613401a1c03011b1515806 \
|
||||
--hash=sha256:23f553ed0509d9a238b2832f775e0b5abd7741b38ab60d388294ee8a7b96c5fb \
|
||||
--hash=sha256:7189b67766418a3e7e6c683f09c5e758aa1a5c24316dd9b714984bac099c4b75 \
|
||||
--hash=sha256:befa930711e63d5d5892f67fd888b2e65e746363e74599c53e71ecefb90ae16a \
|
||||
--hash=sha256:202933ce21e0f33859537bf3800d9a626c70262a9490962e3f450171758507ca \
|
||||
--hash=sha256:c20ca69311c696528e1122ebc7d33775ee971f538c0e3e05dd3bfd4de10b82d4 \
|
||||
--hash=sha256:74dc689a771ae5ffcd5257e763f571ee890e3e87bdb208233b7f451522c00d66 \
|
||||
--hash=sha256:072296baef464daeb6304cf58827dcbae441ad0803039aee1c0caa10d56e0674 \
|
||||
--hash=sha256:7921baf20d52d92d6aeb674125963c335b61abb7e1298bde4baf069d11a2d05e \
|
||||
--hash=sha256:ca098034a84007038c2bff004582e6e6ac2fa9cc8b9251301d25d7e2adcee6da \
|
||||
--hash=sha256:22ddb29823c19be9b15e1b3627db1babfe08b486aede7d5cc463a0a1ae4c75d8 \
|
||||
--hash=sha256:bf1c441b49026d9000166be6e2f63fc351a3fda170aa3fdf18d44d5e5d044640 \
|
||||
--hash=sha256:9710aa7957b4b1f14392006237eb95803acf27897377df3e85395f057f4316b9 \
|
||||
--hash=sha256:8feb676c198bee17ab991ee015828345ac3f87c27dfdb3061d92d1fe47c184b4 \
|
||||
--hash=sha256:597026dede72178ff3627a4eb3315de8444461c7f0f856f5773993c3f9790c53 \
|
||||
--hash=sha256:70f9b964bdfb5191e8f264644c5d1af3041c66fe15261df8a99b3d719dc680d6 \
|
||||
--hash=sha256:74655c0353ffb6cda30485091d0917ce703b128cd824b612b3110a85c79a93d0 \
|
||||
--hash=sha256:0e9c74d105d4ec3af12404e85bb8776931c043657add19f798ee69465f92b999 \
|
||||
--hash=sha256:d3c13446d3d482b9cce61ac73b38effd26fcdcf7f693a405868d3aaaa4d18ca6 \
|
||||
--hash=sha256:371ac3360640ef439a5920ddfe11a34e9d2e546ed886bb8c9ed312611f9f4655 \
|
||||
--hash=sha256:6e5cf63b036f24d2ae4375a88df8d0bc93208352939521d1fcac3c829ef2c363 \
|
||||
--hash=sha256:edf28f5c4d1950d17343adf6d8d40d12c7e982d1e39535d55f7915e122cd8b0e \
|
||||
--hash=sha256:b5ef6f3b4e04f677c296f60f7f4c320ac22cd5bc09c05574460116c8641c801a \
|
||||
--hash=sha256:dd651f66720af4abe2ddae29944e299a57ff91e6fca1739e6dc1f8fd7a8c2b39 \
|
||||
--hash=sha256:4e278f5f72c27f9703bce5514d2fead8c00361caac03e94b0bf9ad8a144f1eeb \
|
||||
--hash=sha256:38f36ea1f545b835c3ecd6e081685a233ac2e3cf0eec8916adc92e4d791098a6 \
|
||||
--hash=sha256:0a2e58ed6e7c42f006cc17d32cec1f432f01b3fe490e24d71471b36e0d0d8742 \
|
||||
--hash=sha256:c1b658db76240596c03571c60635abe953f36fb55b363202971831c2872ea9a0 \
|
||||
--hash=sha256:deb5a84a6a56325eb6701336cdbf70f72adaaeab33cbe953d0e551ecf2592f20 \
|
||||
--hash=sha256:b1425c793e0825f58b3726e7afebaf5a296c07cb0d28580d0ee93dbe10dcdf63 \
|
||||
--hash=sha256:11995fb4dfd14b5c359591baee2a864c5814650ba0084524d4ea0466edfaf029 \
|
||||
--hash=sha256:5d2120b5c93ae322fe2a85d48e3eab4168a19e974a880908f1ac291c0300940f \
|
||||
--hash=sha256:254912ea4bfaaffb0abe366e73bd9ecde622677d6afaf2ce8a0c330df99fefd9 \
|
||||
--hash=sha256:540d8e4525f0b5255c1554b4589089dc58e15df22f343e9545ea00f7012efa07 \
|
||||
--hash=sha256:f69b8ebded7eed181fabe30deabae89fd10c41964f38abb26b19664bbe55c1ae
|
||||
pyevermizer==0.48.0 \
|
||||
--hash=sha256:069ce348e480e04fd6208cfd0f789c600b18d7c34b5272375b95823be191ed57 \
|
||||
--hash=sha256:58164dddaba2f340b0a8b4f39605e9dac46d8b0ffb16120e2e57bef2bfc1d683 \
|
||||
--hash=sha256:115dd09d38a10f11d4629b340dfd75e2ba4089a1ff9e9748a11619829e02c876 \
|
||||
--hash=sha256:b5e79cfe721e75cd7dec306b5eecd6385ce059e31ef7523ba7f677e22161ec6f \
|
||||
--hash=sha256:382882fa9d641b9969a6c3ed89449a814bdabcb6b17b558872d95008a6cc908b \
|
||||
--hash=sha256:92f67700e9132064a90858d391dd0b8fb111aff6dfd472befed57772d89ae567 \
|
||||
--hash=sha256:fe4c453b7dbd5aa834b81f9a7aedb949a605455650b938b8b304d8e5a7edcbf7 \
|
||||
--hash=sha256:c6bdbc45daf73818f763ed59ad079f16494593395d806f772dd62605c722b3e9 \
|
||||
--hash=sha256:bb09f45448fdfd28566ae6fcc38c35a6632f4c31a9de2483848f6ce17b2359b5 \
|
||||
--hash=sha256:00a8b9014744bd1528d0d39c33ede7c0d1713ad797a331cebb33d377a5bc1064 \
|
||||
--hash=sha256:64ee69edc0a7d3b3caded78f2e46975f9beaff1ff8feaf29b87da44c45f38d7d \
|
||||
--hash=sha256:9211bdb1313e9f4869ed5bdc61f3831d39679bd08bb4087f1c1e5475d9e3018b \
|
||||
--hash=sha256:4a57821e422a1d75fe3307931a78db7a65e76955f8e401c4b347db6570390d09 \
|
||||
--hash=sha256:04670cee0a0b913f24d2b9a1e771781560e2485bda31e6cd372a08421cf85cfa \
|
||||
--hash=sha256:971fe77d0a20a1db984020ad253b613d0983f5e23ff22cba60ee5ac00d8128de \
|
||||
--hash=sha256:127265fdb49f718f54706bf15604af1cec23590afd00d423089dea4331dcfc61 \
|
||||
--hash=sha256:d47576360337c1a23f424cd49944a8d68fc4f3338e00719c9f89972c84604bef \
|
||||
--hash=sha256:879659603e51130a0de8d9885d815a2fa1df8bd6cebe6d520d1c6002302adfdb \
|
||||
--hash=sha256:6a91bfc53dd130db6424adf8ac97a1133e97b4157ed00f889d8cbd26a2a4b340 \
|
||||
--hash=sha256:f3bf35fc5eef4cda49d2de77339fc201dd3206660a3dc15db005625b15bb806c \
|
||||
--hash=sha256:e7c8d5bf59a3c16db20411bc5d8e9c9087a30b6b4edf1b5ed9f4c013291427e4 \
|
||||
--hash=sha256:054a4d84ffe75448d41e88e1e0642ef719eb6111be5fe608e71e27a558c59069 \
|
||||
--hash=sha256:e6f141ca367469c69ba7fbf65836c479ec6672c598cfcb6b39e8098c60d346bc \
|
||||
--hash=sha256:6e65eb88f0c1ff4acde1c13b24ce649b0fe3d1d3916d02d96836c781a5022571 \
|
||||
--hash=sha256:e61e8f476b6da809cf38912755ed8bb009665f589e913eb8df877e9fa763024b \
|
||||
--hash=sha256:7e7c5484c0a2e3da6064de3f73d8d988d6703db58ab0be4730cbbf1a82319237 \
|
||||
--hash=sha256:9033b954e5f4878fd94af6d2056c78e3316115521fb1c24a4416d5cbf2ad66ad \
|
||||
--hash=sha256:824c623fff8ae4da176306c458ad63ad16a06a495a16db700665eca3c115924f \
|
||||
--hash=sha256:8e31031409a8386c6a63b79d480393481badb3ba29f32ff7a0db2b4abed20ac8 \
|
||||
--hash=sha256:7dbb7bb13e1e94f69f7ccdbcf4d35776424555fce5af1ca29d0256f91fdf087a \
|
||||
--hash=sha256:3a24e331b259407b6912d6e0738aa8a675831db3b7493fcf54dc17cb0cb80d37 \
|
||||
--hash=sha256:fdda06662a994271e96633cba100dd92b2fcd524acef8b2f664d1aaa14503cbd \
|
||||
--hash=sha256:0f0fc81bef3dbb78ba6a7622dd4296f23c59825968a0bb0448beb16eb3397cc2 \
|
||||
--hash=sha256:e07cbef776a7468669211546887357cc88e9afcf1578b23a4a4f2480517b15d9 \
|
||||
--hash=sha256:e442212695bdf60e455673b7b9dd83a5d4b830d714376477093d2c9054d92832
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from test.bases import WorldTestBase
|
||||
from typing import Iterable
|
||||
from .. import SoEWorld
|
||||
|
||||
|
||||
class SoETestBase(WorldTestBase):
|
||||
game = "Secret of Evermore"
|
||||
world: SoEWorld
|
||||
|
||||
def assertLocationReachability(self, reachable: Iterable[str] = (), unreachable: Iterable[str] = (),
|
||||
satisfied: bool = True) -> None:
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
import typing
|
||||
from unittest import TestCase, skipUnless
|
||||
|
||||
from . import SoETestBase
|
||||
from .. import pyevermizer
|
||||
from ..options import Sniffamizer
|
||||
|
||||
|
||||
class TestCount(TestCase):
|
||||
"""
|
||||
Test that counts line up for sniff spots
|
||||
"""
|
||||
|
||||
def test_compare_counts(self) -> None:
|
||||
self.assertEqual(len(pyevermizer.get_sniff_locations()), len(pyevermizer.get_sniff_items()),
|
||||
"Sniff locations and sniff items don't line up")
|
||||
|
||||
|
||||
class Bases:
|
||||
# class in class to avoid running tests for helper class
|
||||
class TestSniffamizerLocal(SoETestBase):
|
||||
"""
|
||||
Test that provided options do not add sniff items or locations
|
||||
"""
|
||||
def test_no_sniff_items(self) -> None:
|
||||
self.assertLess(len(self.multiworld.itempool), 500,
|
||||
"Unexpected number of items")
|
||||
for item in self.multiworld.itempool:
|
||||
if item.code is not None:
|
||||
self.assertLess(item.code, 65000,
|
||||
"Unexpected item type")
|
||||
|
||||
def test_no_sniff_locations(self) -> None:
|
||||
location_count = sum(1 for location in self.multiworld.get_locations(self.player) if location.item is None)
|
||||
self.assertLess(location_count, 500,
|
||||
"Unexpected number of locations")
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.address is not None:
|
||||
self.assertLess(location.address, 65000,
|
||||
"Unexpected location type")
|
||||
self.assertEqual(location_count, len(self.multiworld.itempool),
|
||||
"Locations and item counts do not line up")
|
||||
|
||||
class TestSniffamizerPool(SoETestBase):
|
||||
"""
|
||||
Test that provided options add sniff items and locations
|
||||
"""
|
||||
def test_sniff_items(self) -> None:
|
||||
self.assertGreater(len(self.multiworld.itempool), 500,
|
||||
"Unexpected number of items")
|
||||
|
||||
def test_sniff_locations(self) -> None:
|
||||
location_count = sum(1 for location in self.multiworld.get_locations(self.player) if location.item is None)
|
||||
self.assertGreater(location_count, 500,
|
||||
"Unexpected number of locations")
|
||||
self.assertTrue(any(location.address is not None and location.address >= 65000
|
||||
for location in self.multiworld.get_locations(self.player)),
|
||||
"No sniff locations")
|
||||
self.assertEqual(location_count, len(self.multiworld.itempool),
|
||||
"Locations and item counts do not line up")
|
||||
|
||||
|
||||
class TestSniffamizerShuffle(Bases.TestSniffamizerLocal):
|
||||
"""
|
||||
Test that shuffle does not add extra items or locations
|
||||
"""
|
||||
options: typing.Dict[str, typing.Any] = {
|
||||
"sniffamizer": "shuffle"
|
||||
}
|
||||
|
||||
def test_flags(self) -> None:
|
||||
# default -> no flags
|
||||
flags = self.world.options.flags
|
||||
self.assertNotIn("s", flags)
|
||||
self.assertNotIn("S", flags)
|
||||
self.assertNotIn("v", flags)
|
||||
|
||||
|
||||
@skipUnless(hasattr(Sniffamizer, "option_everywhere"), "Feature disabled")
|
||||
class TestSniffamizerEverywhereVanilla(Bases.TestSniffamizerPool):
|
||||
"""
|
||||
Test that everywhere + vanilla ingredients does add extra items and locations
|
||||
"""
|
||||
options: typing.Dict[str, typing.Any] = {
|
||||
"sniffamizer": "everywhere",
|
||||
"sniff_ingredients": "vanilla_ingredients",
|
||||
}
|
||||
|
||||
def test_flags(self) -> None:
|
||||
flags = self.world.options.flags
|
||||
self.assertIn("S", flags)
|
||||
self.assertNotIn("v", flags)
|
||||
|
||||
|
||||
@skipUnless(hasattr(Sniffamizer, "option_everywhere"), "Feature disabled")
|
||||
class TestSniffamizerEverywhereRandom(Bases.TestSniffamizerPool):
|
||||
"""
|
||||
Test that everywhere + random ingredients also adds extra items and locations
|
||||
"""
|
||||
options: typing.Dict[str, typing.Any] = {
|
||||
"sniffamizer": "everywhere",
|
||||
"sniff_ingredients": "random_ingredients",
|
||||
}
|
||||
|
||||
def test_flags(self) -> None:
|
||||
flags = self.world.options.flags
|
||||
self.assertIn("S", flags)
|
||||
self.assertIn("v", flags)
|
||||
|
||||
|
||||
@skipUnless(hasattr(Sniffamizer, "option_everywhere"), "Feature disabled")
|
||||
class EverywhereAccessTest(SoETestBase):
|
||||
"""
|
||||
Test that everywhere has certain rules
|
||||
"""
|
||||
options: typing.Dict[str, typing.Any] = {
|
||||
"sniffamizer": "everywhere",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _resolve_numbers(spots: typing.Mapping[str, typing.Iterable[int]]) -> typing.List[str]:
|
||||
return [f"{name} #{number}" for name, numbers in spots.items() for number in numbers]
|
||||
|
||||
def test_knight_basher(self) -> None:
|
||||
locations = ["Mungola", "Lightning Storm"] + self._resolve_numbers({
|
||||
"Gomi's Tower Sniff": range(473, 491),
|
||||
"Gomi's Tower": range(195, 199),
|
||||
})
|
||||
items = [["Knight Basher"]]
|
||||
self.assertAccessDependency(locations, items)
|
Loading…
Reference in New Issue