Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SoE: update to pyevermizer v0.48.0 #3050

Merged
merged 1 commit into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions worlds/soe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
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:
Expand Down Expand Up @@ -64,20 +64,28 @@
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',
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -238,16 +246,26 @@ def get_sphere_index(evermizer_loc: pyevermizer.Location) -> int:
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"
Expand Down Expand Up @@ -299,6 +317,15 @@ def create_items(self) -> None:
# 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):
Expand Down Expand Up @@ -345,7 +372,12 @@ def set_rules(self) -> None:
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))

Expand Down
7 changes: 5 additions & 2 deletions worlds/soe/logic.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import typing
from itertools import chain
from typing import Callable, Set

from . import pyevermizer
Expand All @@ -11,10 +12,12 @@

# 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]


Expand Down
25 changes: 22 additions & 3 deletions worlds/soe/options.py
Original file line number Diff line number Diff line change
@@ -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, \
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -207,7 +225,7 @@ def __new__(mcs, name: str, bases: Tuple[type], attrs: Dict[Any, Any]) -> "ItemC
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)


Expand Down Expand Up @@ -268,6 +286,7 @@ class SoEOptions(PerGameCommonOptions):
short_boss_rush: ShortBossRush
ingredienizer: Ingredienizer
sniffamizer: Sniffamizer
sniff_ingredients: SniffIngredients
callbeadamizer: Callbeadamizer
musicmizer: Musicmizer
doggomizer: Doggomizer
Expand Down
72 changes: 36 additions & 36 deletions worlds/soe/requirements.txt
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions worlds/soe/test/__init__.py
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
Loading
Loading