Merge remote-tracking branch 'refs/remotes/origin/main' into tunc-portal-direction-pairing

# Conflicts:
#	worlds/tunic/__init__.py
#	worlds/tunic/er_rules.py
#	worlds/tunic/rules.py
This commit is contained in:
Scipio Wright
2024-08-13 21:02:16 -04:00
33 changed files with 249 additions and 264 deletions

View File

@@ -863,19 +863,15 @@ class CollectionState():
)
# Item related
def collect(self, item: Item, event: bool = False, location: Optional[Location] = None) -> bool:
def collect(self, item: Item, prevent_sweep: bool = False, location: Optional[Location] = None) -> bool:
if location:
self.locations_checked.add(location)
changed = self.multiworld.worlds[item.player].collect(self, item)
if not changed and event:
self.prog_items[item.player][item.name] += 1
changed = True
self.stale[item.player] = True
if changed and not event:
if changed and not prevent_sweep:
self.sweep_for_events()
return changed
@@ -1427,7 +1423,7 @@ class Spoiler:
# Maybe move the big bomb over to the Event system instead?
if any(exit_path == 'Pyramid Fairy' for path in self.paths.values()
for (_, exit_path) in path):
if multiworld.mode[player] != 'inverted':
if multiworld.worlds[player].options.mode != 'inverted':
self.paths[str(multiworld.get_region('Big Bomb Shop', player))] = \
get_path(state, multiworld.get_region('Big Bomb Shop', player))
else:

19
Fill.py
View File

@@ -12,7 +12,12 @@ from worlds.generic.Rules import add_item_rule
class FillError(RuntimeError):
pass
def __init__(self, *args: typing.Union[str, typing.Any], **kwargs) -> None:
if "multiworld" in kwargs and isinstance(args[0], str):
placements = (args[0] + f"\nAll Placements:\n" +
f"{[(loc, loc.item) for loc in kwargs['multiworld'].get_filled_locations()]}")
args = (placements, *args[1:])
super().__init__(*args)
def _log_fill_progress(name: str, placed: int, total_items: int) -> None:
@@ -212,7 +217,7 @@ def fill_restrictive(multiworld: MultiWorld, base_state: CollectionState, locati
f"Unfilled locations:\n"
f"{', '.join(str(location) for location in locations)}\n"
f"Already placed {len(placements)}:\n"
f"{', '.join(str(place) for place in placements)}")
f"{', '.join(str(place) for place in placements)}", multiworld=multiworld)
item_pool.extend(unplaced_items)
@@ -299,7 +304,7 @@ def remaining_fill(multiworld: MultiWorld,
f"Unfilled locations:\n"
f"{', '.join(str(location) for location in locations)}\n"
f"Already placed {len(placements)}:\n"
f"{', '.join(str(place) for place in placements)}")
f"{', '.join(str(place) for place in placements)}", multiworld=multiworld)
itempool.extend(unplaced_items)
@@ -506,7 +511,8 @@ def distribute_items_restrictive(multiworld: MultiWorld,
if progitempool:
raise FillError(
f"Not enough locations for progression items. "
f"There are {len(progitempool)} more progression items than there are available locations."
f"There are {len(progitempool)} more progression items than there are available locations.",
multiworld=multiworld,
)
accessibility_corrections(multiworld, multiworld.state, defaultlocations)
@@ -523,7 +529,8 @@ def distribute_items_restrictive(multiworld: MultiWorld,
if excludedlocations:
raise FillError(
f"Not enough filler items for excluded locations. "
f"There are {len(excludedlocations)} more excluded locations than filler or trap items."
f"There are {len(excludedlocations)} more excluded locations than filler or trap items.",
multiworld=multiworld,
)
restitempool = filleritempool + usefulitempool
@@ -589,7 +596,7 @@ def flood_items(multiworld: MultiWorld) -> None:
if candidate_item_to_place is not None:
item_to_place = candidate_item_to_place
else:
raise FillError('No more progress items left to place.')
raise FillError('No more progress items left to place.', multiworld=multiworld)
# find item to replace with progress item
location_list = multiworld.get_reachable_locations()

View File

@@ -266,7 +266,7 @@ def run_gui():
if file and component:
run_component(component, file)
else:
logging.warning(f"unable to identify component for {filename}")
logging.warning(f"unable to identify component for {file}")
def _stop(self, *largs):
# ran into what appears to be https://groups.google.com/g/kivy-users/c/saWDLoYCSZ4 with PyCharm.

20
Main.py
View File

@@ -11,7 +11,8 @@ from typing import Dict, List, Optional, Set, Tuple, Union
import worlds
from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld, Region
from Fill import balance_multiworld_progression, distribute_items_restrictive, distribute_planned, flood_items
from Fill import FillError, balance_multiworld_progression, distribute_items_restrictive, distribute_planned, \
flood_items
from Options import StartInventoryPool
from Utils import __version__, output_path, version_tuple, get_settings
from settings import get_settings
@@ -151,6 +152,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
# Because some worlds don't actually create items during create_items this has to be as late as possible.
if any(getattr(multiworld.worlds[player].options, "start_inventory_from_pool", None) for player in multiworld.player_ids):
new_items: List[Item] = []
old_items: List[Item] = []
depletion_pool: Dict[int, Dict[str, int]] = {
player: getattr(multiworld.worlds[player].options,
"start_inventory_from_pool",
@@ -169,20 +171,24 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
depletion_pool[item.player][item.name] -= 1
# quick abort if we have found all items
if not target:
new_items.extend(multiworld.itempool[i+1:])
old_items.extend(multiworld.itempool[i+1:])
break
else:
new_items.append(item)
old_items.append(item)
# leftovers?
if target:
for player, remaining_items in depletion_pool.items():
remaining_items = {name: count for name, count in remaining_items.items() if count}
if remaining_items:
raise Exception(f"{multiworld.get_player_name(player)}"
logger.warning(f"{multiworld.get_player_name(player)}"
f" is trying to remove items from their pool that don't exist: {remaining_items}")
assert len(multiworld.itempool) == len(new_items), "Item Pool amounts should not change."
multiworld.itempool[:] = new_items
# find all filler we generated for the current player and remove until it matches
removables = [item for item in new_items if item.player == player]
for _ in range(sum(remaining_items.values())):
new_items.remove(removables.pop())
assert len(multiworld.itempool) == len(new_items + old_items), "Item Pool amounts should not change."
multiworld.itempool[:] = new_items + old_items
multiworld.link_items()
@@ -341,7 +347,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
output_file_futures.append(pool.submit(write_multidata))
if not check_accessibility_task.result():
if not multiworld.can_beat_game():
raise Exception("Game appears as unbeatable. Aborting.")
raise FillError("Game appears as unbeatable. Aborting.", multiworld=multiworld)
else:
logger.warning("Location Accessibility requirements not fulfilled.")

View File

@@ -1236,6 +1236,7 @@ class CommonOptions(metaclass=OptionsMetaProperty):
:param option_names: names of the options to return
:param casing: case of the keys to return. Supports `snake`, `camel`, `pascal`, `kebab`
"""
assert option_names, "options.as_dict() was used without any option names."
option_results = {}
for option_name in option_names:
if option_name in type(self).type_hints:
@@ -1517,31 +1518,3 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge
with open(os.path.join(target_folder, game_name + ".yaml"), "w", encoding="utf-8-sig") as f:
f.write(res)
if __name__ == "__main__":
from worlds.alttp.Options import Logic
import argparse
map_shuffle = Toggle
compass_shuffle = Toggle
key_shuffle = Toggle
big_key_shuffle = Toggle
hints = Toggle
test = argparse.Namespace()
test.logic = Logic.from_text("no_logic")
test.map_shuffle = map_shuffle.from_text("ON")
test.hints = hints.from_text('OFF')
try:
test.logic = Logic.from_text("overworld_glitches_typo")
except KeyError as e:
print(e)
try:
test.logic_owg = Logic.from_text("owg")
except KeyError as e:
print(e)
if test.map_shuffle:
print("map_shuffle is on")
print(f"Hints are {bool(test.hints)}")
print(test)

View File

@@ -23,7 +23,7 @@ class TestBase(unittest.TestCase):
state = CollectionState(self.multiworld)
for item in items:
item.classification = ItemClassification.progression
state.collect(item, event=True)
state.collect(item, prevent_sweep=True)
state.sweep_for_events()
state.update_reachable_regions(1)
self._state_cache[self.multiworld, tuple(items)] = state

View File

@@ -55,7 +55,7 @@ class TestAllGamesMultiworld(MultiworldTestBase):
all_worlds = list(AutoWorldRegister.world_types.values())
self.multiworld = setup_multiworld(all_worlds, ())
for world in self.multiworld.worlds.values():
world.options.accessibility.value = Accessibility.option_locations
world.options.accessibility.value = Accessibility.option_full
self.assertSteps(gen_steps)
with self.subTest("filling multiworld", seed=self.multiworld.seed):
distribute_items_restrictive(self.multiworld)
@@ -66,8 +66,8 @@ class TestAllGamesMultiworld(MultiworldTestBase):
class TestTwoPlayerMulti(MultiworldTestBase):
def test_two_player_single_game_fills(self) -> None:
"""Tests that a multiworld of two players for each registered game world can generate."""
for world in AutoWorldRegister.world_types.values():
self.multiworld = setup_multiworld([world, world], ())
for world_type in AutoWorldRegister.world_types.values():
self.multiworld = setup_multiworld([world_type, world_type], ())
for world in self.multiworld.worlds.values():
world.options.accessibility.value = Accessibility.option_full
self.assertSteps(gen_steps)

View File

@@ -54,7 +54,7 @@ class TestDungeon(LTTPTestBase):
for item in items:
item.classification = ItemClassification.progression
state.collect(item, event=True) # event=True prevents running sweep_for_events() and picking up
state.collect(item, prevent_sweep=True) # prevent_sweep=True prevents running sweep_for_events() and picking up
state.sweep_for_events() # key drop keys repeatedly
self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), access, f"failed {self.multiworld.get_location(location, 1)} with: {item_pool}")
self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), access, f"failed {self.multiworld.get_location(location, 1)} with: {item_pool}")

View File

@@ -1,6 +1,9 @@
from typing import Callable, Dict, NamedTuple, Optional
from typing import Callable, Dict, NamedTuple, Optional, TYPE_CHECKING
from BaseClasses import Item, ItemClassification, MultiWorld
from BaseClasses import Item, ItemClassification
if TYPE_CHECKING:
from . import CliqueWorld
class CliqueItem(Item):
@@ -10,7 +13,7 @@ class CliqueItem(Item):
class CliqueItemData(NamedTuple):
code: Optional[int] = None
type: ItemClassification = ItemClassification.filler
can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True
can_create: Callable[["CliqueWorld"], bool] = lambda world: True
item_data_table: Dict[str, CliqueItemData] = {
@@ -21,11 +24,11 @@ item_data_table: Dict[str, CliqueItemData] = {
"Button Activation": CliqueItemData(
code=69696968,
type=ItemClassification.progression,
can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]),
can_create=lambda world: world.options.hard_mode,
),
"A Cool Filler Item (No Satisfaction Guaranteed)": CliqueItemData(
code=69696967,
can_create=lambda multiworld, player: False # Only created from `get_filler_item_name`.
can_create=lambda world: False # Only created from `get_filler_item_name`.
),
"The Urge to Push": CliqueItemData(
type=ItemClassification.progression,

View File

@@ -1,6 +1,9 @@
from typing import Callable, Dict, NamedTuple, Optional
from typing import Callable, Dict, NamedTuple, Optional, TYPE_CHECKING
from BaseClasses import Location, MultiWorld
from BaseClasses import Location
if TYPE_CHECKING:
from . import CliqueWorld
class CliqueLocation(Location):
@@ -10,7 +13,7 @@ class CliqueLocation(Location):
class CliqueLocationData(NamedTuple):
region: str
address: Optional[int] = None
can_create: Callable[[MultiWorld, int], bool] = lambda multiworld, player: True
can_create: Callable[["CliqueWorld"], bool] = lambda world: True
locked_item: Optional[str] = None
@@ -22,7 +25,7 @@ location_data_table: Dict[str, CliqueLocationData] = {
"The Item on the Desk": CliqueLocationData(
region="The Button Realm",
address=69696968,
can_create=lambda multiworld, player: bool(getattr(multiworld, "hard_mode")[player]),
can_create=lambda world: world.options.hard_mode,
),
"In the Player's Mind": CliqueLocationData(
region="The Button Realm",

View File

@@ -1,6 +1,5 @@
from typing import Dict
from Options import Choice, Option, Toggle
from dataclasses import dataclass
from Options import Choice, Toggle, PerGameCommonOptions, StartInventoryPool
class HardMode(Toggle):
@@ -25,10 +24,11 @@ class ButtonColor(Choice):
option_black = 11
clique_options: Dict[str, type(Option)] = {
"color": ButtonColor,
"hard_mode": HardMode,
@dataclass
class CliqueOptions(PerGameCommonOptions):
color: ButtonColor
hard_mode: HardMode
start_inventory_from_pool: StartInventoryPool
# DeathLink is always on. Always.
# "death_link": DeathLink,
}
# death_link: DeathLink

View File

@@ -1,10 +1,13 @@
from typing import Callable
from typing import Callable, TYPE_CHECKING
from BaseClasses import CollectionState, MultiWorld
from BaseClasses import CollectionState
if TYPE_CHECKING:
from . import CliqueWorld
def get_button_rule(multiworld: MultiWorld, player: int) -> Callable[[CollectionState], bool]:
if getattr(multiworld, "hard_mode")[player]:
return lambda state: state.has("Button Activation", player)
def get_button_rule(world: "CliqueWorld") -> Callable[[CollectionState], bool]:
if world.options.hard_mode:
return lambda state: state.has("Button Activation", world.player)
return lambda state: True

View File

@@ -1,10 +1,10 @@
from typing import List
from typing import List, Dict, Any
from BaseClasses import Region, Tutorial
from worlds.AutoWorld import WebWorld, World
from .Items import CliqueItem, item_data_table, item_table
from .Locations import CliqueLocation, location_data_table, location_table, locked_locations
from .Options import clique_options
from .Options import CliqueOptions
from .Regions import region_data_table
from .Rules import get_button_rule
@@ -38,7 +38,8 @@ class CliqueWorld(World):
game = "Clique"
web = CliqueWebWorld()
option_definitions = clique_options
options: CliqueOptions
options_dataclass = CliqueOptions
location_name_to_id = location_table
item_name_to_id = item_table
@@ -48,7 +49,7 @@ class CliqueWorld(World):
def create_items(self) -> None:
item_pool: List[CliqueItem] = []
for name, item in item_data_table.items():
if item.code and item.can_create(self.multiworld, self.player):
if item.code and item.can_create(self):
item_pool.append(self.create_item(name))
self.multiworld.itempool += item_pool
@@ -61,41 +62,40 @@ class CliqueWorld(World):
# Create locations.
for region_name, region_data in region_data_table.items():
region = self.multiworld.get_region(region_name, self.player)
region = self.get_region(region_name)
region.add_locations({
location_name: location_data.address for location_name, location_data in location_data_table.items()
if location_data.region == region_name and location_data.can_create(self.multiworld, self.player)
if location_data.region == region_name and location_data.can_create(self)
}, CliqueLocation)
region.add_exits(region_data_table[region_name].connecting_regions)
# Place locked locations.
for location_name, location_data in locked_locations.items():
# Ignore locations we never created.
if not location_data.can_create(self.multiworld, self.player):
if not location_data.can_create(self):
continue
locked_item = self.create_item(location_data_table[location_name].locked_item)
self.multiworld.get_location(location_name, self.player).place_locked_item(locked_item)
self.get_location(location_name).place_locked_item(locked_item)
# Set priority location for the Big Red Button!
self.multiworld.priority_locations[self.player].value.add("The Big Red Button")
self.options.priority_locations.value.add("The Big Red Button")
def get_filler_item_name(self) -> str:
return "A Cool Filler Item (No Satisfaction Guaranteed)"
def set_rules(self) -> None:
button_rule = get_button_rule(self.multiworld, self.player)
self.multiworld.get_location("The Big Red Button", self.player).access_rule = button_rule
self.multiworld.get_location("In the Player's Mind", self.player).access_rule = button_rule
button_rule = get_button_rule(self)
self.get_location("The Big Red Button").access_rule = button_rule
self.get_location("In the Player's Mind").access_rule = button_rule
# Do not allow button activations on buttons.
self.multiworld.get_location("The Big Red Button", self.player).item_rule =\
lambda item: item.name != "Button Activation"
self.get_location("The Big Red Button").item_rule = lambda item: item.name != "Button Activation"
# Completion condition.
self.multiworld.completion_condition[self.player] = lambda state: state.has("The Urge to Push", self.player)
def fill_slot_data(self):
def fill_slot_data(self) -> Dict[str, Any]:
return {
"color": getattr(self.multiworld, "color")[self.player].current_key
"color": self.options.color.current_key
}

View File

@@ -796,7 +796,7 @@ def validate_world(ootworld, entrance_placed, locations_to_ensure_reachable, all
if ootworld.shuffle_interior_entrances or ootworld.shuffle_overworld_entrances or ootworld.spawn_positions:
time_travel_state = none_state.copy()
time_travel_state.collect(ootworld.create_item('Time Travel'), event=True)
time_travel_state.collect(ootworld.create_item('Time Travel'), prevent_sweep=True)
time_travel_state._oot_update_age_reachable_regions(player)
# Unless entrances are decoupled, we don't want the player to end up through certain entrances as the wrong age

View File

@@ -1388,7 +1388,7 @@ class OOTWorld(World):
self.multiworld.worlds[item.player].collect(all_state, item)
# If free_scarecrow give Scarecrow Song
if self.free_scarecrow:
all_state.collect(self.create_item("Scarecrow Song"), event=True)
all_state.collect(self.create_item("Scarecrow Song"), prevent_sweep=True)
all_state.stale[self.player] = True
return all_state

View File

@@ -11,10 +11,10 @@ class TestCropsanityRules(SVTestBase):
harvest_cactus = self.world.logic.region.can_reach_location("Harvest Cactus Fruit")
self.assert_rule_false(harvest_cactus, self.multiworld.state)
self.multiworld.state.collect(self.world.create_item("Cactus Seeds"), event=False)
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), event=False)
self.multiworld.state.collect(self.world.create_item("Desert Obelisk"), event=False)
self.multiworld.state.collect(self.world.create_item("Cactus Seeds"), prevent_sweep=False)
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), prevent_sweep=False)
self.multiworld.state.collect(self.world.create_item("Desert Obelisk"), prevent_sweep=False)
self.assert_rule_false(harvest_cactus, self.multiworld.state)
self.multiworld.state.collect(self.world.create_item("Greenhouse"), event=False)
self.multiworld.state.collect(self.world.create_item("Greenhouse"), prevent_sweep=False)
self.assert_rule_true(harvest_cactus, self.multiworld.state)

View File

@@ -12,29 +12,29 @@ from ..strings.wallet_item_names import Wallet
def collect_fishing_abilities(tester: SVTestBase):
for i in range(4):
tester.multiworld.state.collect(tester.world.create_item(APTool.fishing_rod), event=False)
tester.multiworld.state.collect(tester.world.create_item(APTool.pickaxe), event=False)
tester.multiworld.state.collect(tester.world.create_item(APTool.axe), event=False)
tester.multiworld.state.collect(tester.world.create_item(APWeapon.weapon), event=False)
tester.multiworld.state.collect(tester.world.create_item(APTool.fishing_rod), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item(APTool.pickaxe), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item(APTool.axe), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item(APWeapon.weapon), prevent_sweep=False)
for i in range(10):
tester.multiworld.state.collect(tester.world.create_item("Fishing Level"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Combat Level"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Mining Level"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Fishing Level"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Combat Level"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Mining Level"), prevent_sweep=False)
for i in range(17):
tester.multiworld.state.collect(tester.world.create_item("Progressive Mine Elevator"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Spring"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Summer"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Fall"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Winter"), event=False)
tester.multiworld.state.collect(tester.world.create_item(Transportation.desert_obelisk), event=False)
tester.multiworld.state.collect(tester.world.create_item("Railroad Boulder Removed"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Island North Turtle"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Island West Turtle"), event=False)
tester.multiworld.state.collect(tester.world.create_item("Progressive Mine Elevator"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Spring"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Summer"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Fall"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Winter"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item(Transportation.desert_obelisk), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Railroad Boulder Removed"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Island North Turtle"), prevent_sweep=False)
tester.multiworld.state.collect(tester.world.create_item("Island West Turtle"), prevent_sweep=False)
def create_and_collect(tester: SVTestBase, item_name: str) -> StardewItem:
item = tester.world.create_item(item_name)
tester.multiworld.state.collect(item, event=False)
tester.multiworld.state.collect(item, prevent_sweep=False)
return item

View File

@@ -12,7 +12,7 @@ from ..options import BundleRandomization
def collect_all(mw):
for item in mw.get_items():
mw.state.collect(item, event=True)
mw.state.collect(item, prevent_sweep=True)
class LogicTestBase(RuleAssertMixin, TestCase):

View File

@@ -257,16 +257,16 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
return super().run_default_tests
def collect_lots_of_money(self):
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), event=False)
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), prevent_sweep=False)
required_prog_items = int(round(self.multiworld.worlds[self.player].total_progression_items * 0.25))
for i in range(required_prog_items):
self.multiworld.state.collect(self.world.create_item("Stardrop"), event=False)
self.multiworld.state.collect(self.world.create_item("Stardrop"), prevent_sweep=False)
def collect_all_the_money(self):
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), event=False)
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), prevent_sweep=False)
required_prog_items = int(round(self.multiworld.worlds[self.player].total_progression_items * 0.95))
for i in range(required_prog_items):
self.multiworld.state.collect(self.world.create_item("Stardrop"), event=False)
self.multiworld.state.collect(self.world.create_item("Stardrop"), prevent_sweep=False)
def collect_everything(self):
non_event_items = [item for item in self.multiworld.get_items() if item.code]

View File

@@ -33,14 +33,14 @@ class WorldAssertMixin(RuleAssertMixin, TestCase):
self.assert_can_reach_victory(multiworld)
multiworld.state.remove(item)
self.assert_cannot_reach_victory(multiworld)
multiworld.state.collect(item, event=False)
multiworld.state.collect(item, prevent_sweep=False)
self.assert_can_reach_victory(multiworld)
def assert_item_was_not_necessary_for_victory(self, item: StardewItem, multiworld: MultiWorld):
self.assert_can_reach_victory(multiworld)
multiworld.state.remove(item)
self.assert_can_reach_victory(multiworld)
multiworld.state.collect(item, event=False)
multiworld.state.collect(item, prevent_sweep=False)
self.assert_can_reach_victory(multiworld)
def assert_can_win(self, multiworld: MultiWorld):

View File

@@ -19,8 +19,8 @@ class TestArcadeMachinesLogic(SVTestBase):
life = self.create_item("JotPK: Extra Life")
drop = self.create_item("JotPK: Increased Drop Rate")
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(boots, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
@@ -28,8 +28,8 @@ class TestArcadeMachinesLogic(SVTestBase):
self.remove(boots)
self.remove(gun)
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(boots, prevent_sweep=True)
self.multiworld.state.collect(boots, prevent_sweep=True)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
@@ -37,10 +37,10 @@ class TestArcadeMachinesLogic(SVTestBase):
self.remove(boots)
self.remove(boots)
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(life, event=True)
self.multiworld.state.collect(boots, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.multiworld.state.collect(ammo, prevent_sweep=True)
self.multiworld.state.collect(life, prevent_sweep=True)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
self.assertTrue(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
@@ -50,13 +50,13 @@ class TestArcadeMachinesLogic(SVTestBase):
self.remove(ammo)
self.remove(life)
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(life, event=True)
self.multiworld.state.collect(drop, event=True)
self.multiworld.state.collect(boots, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.multiworld.state.collect(ammo, prevent_sweep=True)
self.multiworld.state.collect(ammo, prevent_sweep=True)
self.multiworld.state.collect(life, prevent_sweep=True)
self.multiworld.state.collect(drop, prevent_sweep=True)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
self.assertTrue(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
self.assertTrue(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))
@@ -69,17 +69,17 @@ class TestArcadeMachinesLogic(SVTestBase):
self.remove(life)
self.remove(drop)
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(boots, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(gun, event=True)
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(ammo, event=True)
self.multiworld.state.collect(life, event=True)
self.multiworld.state.collect(drop, event=True)
self.multiworld.state.collect(boots, prevent_sweep=True)
self.multiworld.state.collect(boots, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.multiworld.state.collect(gun, prevent_sweep=True)
self.multiworld.state.collect(ammo, prevent_sweep=True)
self.multiworld.state.collect(ammo, prevent_sweep=True)
self.multiworld.state.collect(ammo, prevent_sweep=True)
self.multiworld.state.collect(life, prevent_sweep=True)
self.multiworld.state.collect(drop, prevent_sweep=True)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state))
self.assertTrue(self.world.logic.region.can_reach("JotPK World 2")(self.multiworld.state))
self.assertTrue(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))

View File

@@ -23,11 +23,11 @@ class TestBuildingLogic(SVTestBase):
self.assertFalse(big_coop_blueprint_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
self.multiworld.state.collect(self.create_item("Can Construct Buildings"), event=True)
self.multiworld.state.collect(self.create_item("Can Construct Buildings"), prevent_sweep=True)
self.assertFalse(big_coop_blueprint_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
self.multiworld.state.collect(self.create_item("Progressive Coop"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Coop"), prevent_sweep=False)
self.assertTrue(big_coop_blueprint_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
@@ -35,13 +35,13 @@ class TestBuildingLogic(SVTestBase):
self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
self.collect_lots_of_money()
self.multiworld.state.collect(self.create_item("Can Construct Buildings"), event=True)
self.multiworld.state.collect(self.create_item("Can Construct Buildings"), prevent_sweep=True)
self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
self.multiworld.state.collect(self.create_item("Progressive Coop"), event=True)
self.multiworld.state.collect(self.create_item("Progressive Coop"), prevent_sweep=True)
self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
self.multiworld.state.collect(self.create_item("Progressive Coop"), event=True)
self.multiworld.state.collect(self.create_item("Progressive Coop"), prevent_sweep=True)
self.assertTrue(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
def test_big_shed_blueprint(self):
@@ -53,10 +53,10 @@ class TestBuildingLogic(SVTestBase):
self.assertFalse(big_shed_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
self.multiworld.state.collect(self.create_item("Can Construct Buildings"), event=True)
self.multiworld.state.collect(self.create_item("Can Construct Buildings"), prevent_sweep=True)
self.assertFalse(big_shed_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
self.multiworld.state.collect(self.create_item("Progressive Shed"), event=True)
self.multiworld.state.collect(self.create_item("Progressive Shed"), prevent_sweep=True)
self.assertTrue(big_shed_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")

View File

@@ -17,14 +17,14 @@ class TestRecipeLearnLogic(SVTestBase):
rule = self.world.logic.region.can_reach_location(location)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Progressive House"), event=False)
self.multiworld.state.collect(self.create_item("Radish Seeds"), event=False)
self.multiworld.state.collect(self.create_item("Spring"), event=False)
self.multiworld.state.collect(self.create_item("Summer"), event=False)
self.multiworld.state.collect(self.create_item("Progressive House"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Radish Seeds"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Spring"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Summer"), prevent_sweep=False)
self.collect_lots_of_money()
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("The Queen of Sauce"), event=False)
self.multiworld.state.collect(self.create_item("The Queen of Sauce"), prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)
@@ -42,21 +42,21 @@ class TestRecipeReceiveLogic(SVTestBase):
rule = self.world.logic.region.can_reach_location(location)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Progressive House"), event=False)
self.multiworld.state.collect(self.create_item("Radish Seeds"), event=False)
self.multiworld.state.collect(self.create_item("Summer"), event=False)
self.multiworld.state.collect(self.create_item("Progressive House"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Radish Seeds"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Summer"), prevent_sweep=False)
self.collect_lots_of_money()
self.assert_rule_false(rule, self.multiworld.state)
spring = self.create_item("Spring")
qos = self.create_item("The Queen of Sauce")
self.multiworld.state.collect(spring, event=False)
self.multiworld.state.collect(qos, event=False)
self.multiworld.state.collect(spring, prevent_sweep=False)
self.multiworld.state.collect(qos, prevent_sweep=False)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.remove(spring)
self.multiworld.state.remove(qos)
self.multiworld.state.collect(self.create_item("Radish Salad Recipe"), event=False)
self.multiworld.state.collect(self.create_item("Radish Salad Recipe"), prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)
def test_get_chefsanity_check_recipe(self):
@@ -64,20 +64,20 @@ class TestRecipeReceiveLogic(SVTestBase):
rule = self.world.logic.region.can_reach_location(location)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Spring"), event=False)
self.multiworld.state.collect(self.create_item("Spring"), prevent_sweep=False)
self.collect_lots_of_money()
self.assert_rule_false(rule, self.multiworld.state)
seeds = self.create_item("Radish Seeds")
summer = self.create_item("Summer")
house = self.create_item("Progressive House")
self.multiworld.state.collect(seeds, event=False)
self.multiworld.state.collect(summer, event=False)
self.multiworld.state.collect(house, event=False)
self.multiworld.state.collect(seeds, prevent_sweep=False)
self.multiworld.state.collect(summer, prevent_sweep=False)
self.multiworld.state.collect(house, prevent_sweep=False)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.remove(seeds)
self.multiworld.state.remove(summer)
self.multiworld.state.remove(house)
self.multiworld.state.collect(self.create_item("The Queen of Sauce"), event=False)
self.multiworld.state.collect(self.create_item("The Queen of Sauce"), prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)

View File

@@ -25,7 +25,7 @@ class TestCraftsanityLogic(SVTestBase):
self.collect_all_the_money()
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Marble Brazier Recipe"), event=False)
self.multiworld.state.collect(self.create_item("Marble Brazier Recipe"), prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)
def test_can_learn_crafting_recipe(self):
@@ -38,16 +38,16 @@ class TestCraftsanityLogic(SVTestBase):
def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), event=False)
self.multiworld.state.collect(self.create_item("Torch Recipe"), event=False)
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Torch Recipe"), prevent_sweep=False)
self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Fall"), event=False)
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), event=False)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)
@@ -62,16 +62,16 @@ class TestCraftsanityWithFestivalsLogic(SVTestBase):
def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), event=False)
self.multiworld.state.collect(self.create_item("Fall"), event=False)
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False)
self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), event=False)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), prevent_sweep=False)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Torch Recipe"), event=False)
self.multiworld.state.collect(self.create_item("Torch Recipe"), prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)
@@ -92,7 +92,7 @@ class TestNoCraftsanityLogic(SVTestBase):
def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), event=False)
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False)
self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe)
result = rule(self.multiworld.state)
@@ -113,11 +113,11 @@ class TestNoCraftsanityWithFestivalsLogic(SVTestBase):
def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), event=False)
self.multiworld.state.collect(self.create_item("Fall"), event=False)
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False)
self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe)
self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), event=False)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)

View File

@@ -18,7 +18,7 @@ class TestDonationLogicAll(SVTestBase):
for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
self.multiworld.state.collect(self.create_item(railroad_item), event=False)
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False)
for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
@@ -39,7 +39,7 @@ class TestDonationLogicRandomized(SVTestBase):
for donation in donation_locations:
self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
self.multiworld.state.collect(self.create_item(railroad_item), event=False)
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False)
for donation in donation_locations:
self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
@@ -58,7 +58,7 @@ class TestDonationLogicMilestones(SVTestBase):
for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
self.multiworld.state.collect(self.create_item(railroad_item), event=False)
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False)
for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))

View File

@@ -11,34 +11,34 @@ class TestFriendsanityDatingRules(SVTestBase):
def test_earning_dating_heart_requires_dating(self):
self.collect_all_the_money()
self.multiworld.state.collect(self.create_item("Fall"), event=False)
self.multiworld.state.collect(self.create_item("Beach Bridge"), event=False)
self.multiworld.state.collect(self.create_item("Progressive House"), event=False)
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Beach Bridge"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Progressive House"), prevent_sweep=False)
for i in range(3):
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Weapon"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Axe"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Barn"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Progressive Weapon"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Progressive Axe"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Progressive Barn"), prevent_sweep=False)
for i in range(10):
self.multiworld.state.collect(self.create_item("Foraging Level"), event=False)
self.multiworld.state.collect(self.create_item("Farming Level"), event=False)
self.multiworld.state.collect(self.create_item("Mining Level"), event=False)
self.multiworld.state.collect(self.create_item("Combat Level"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"), event=False)
self.multiworld.state.collect(self.create_item("Foraging Level"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Farming Level"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Mining Level"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Combat Level"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"), prevent_sweep=False)
npc = "Abigail"
heart_name = f"{npc} <3"
step = 3
self.assert_can_reach_heart_up_to(npc, 3, step)
self.multiworld.state.collect(self.create_item(heart_name), event=False)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False)
self.assert_can_reach_heart_up_to(npc, 6, step)
self.multiworld.state.collect(self.create_item(heart_name), event=False)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False)
self.assert_can_reach_heart_up_to(npc, 8, step)
self.multiworld.state.collect(self.create_item(heart_name), event=False)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False)
self.assert_can_reach_heart_up_to(npc, 10, step)
self.multiworld.state.collect(self.create_item(heart_name), event=False)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False)
self.assert_can_reach_heart_up_to(npc, 14, step)
def assert_can_reach_heart_up_to(self, npc: str, max_reachable: int, step: int):

View File

@@ -76,7 +76,7 @@ class TestShipsanityEverything(SVTestBase):
with self.subTest(location.name):
self.remove(bin_item)
self.assertFalse(self.world.logic.region.can_reach_location(location.name)(self.multiworld.state))
self.multiworld.state.collect(bin_item, event=False)
self.multiworld.state.collect(bin_item, prevent_sweep=False)
shipsanity_rule = self.world.logic.region.can_reach_location(location.name)
self.assert_rule_true(shipsanity_rule, self.multiworld.state)
self.remove(bin_item)

View File

@@ -21,30 +21,30 @@ class TestProgressiveToolsLogic(SVTestBase):
self.assert_rule_false(sturgeon_rule, self.multiworld.state)
summer = self.create_item("Summer")
self.multiworld.state.collect(summer, event=False)
self.multiworld.state.collect(summer, prevent_sweep=False)
self.assert_rule_false(sturgeon_rule, self.multiworld.state)
fishing_rod = self.create_item("Progressive Fishing Rod")
self.multiworld.state.collect(fishing_rod, event=False)
self.multiworld.state.collect(fishing_rod, event=False)
self.multiworld.state.collect(fishing_rod, prevent_sweep=False)
self.multiworld.state.collect(fishing_rod, prevent_sweep=False)
self.assert_rule_false(sturgeon_rule, self.multiworld.state)
fishing_level = self.create_item("Fishing Level")
self.multiworld.state.collect(fishing_level, event=False)
self.multiworld.state.collect(fishing_level, prevent_sweep=False)
self.assert_rule_false(sturgeon_rule, self.multiworld.state)
self.multiworld.state.collect(fishing_level, event=False)
self.multiworld.state.collect(fishing_level, event=False)
self.multiworld.state.collect(fishing_level, event=False)
self.multiworld.state.collect(fishing_level, event=False)
self.multiworld.state.collect(fishing_level, event=False)
self.multiworld.state.collect(fishing_level, prevent_sweep=False)
self.multiworld.state.collect(fishing_level, prevent_sweep=False)
self.multiworld.state.collect(fishing_level, prevent_sweep=False)
self.multiworld.state.collect(fishing_level, prevent_sweep=False)
self.multiworld.state.collect(fishing_level, prevent_sweep=False)
self.assert_rule_true(sturgeon_rule, self.multiworld.state)
self.remove(summer)
self.assert_rule_false(sturgeon_rule, self.multiworld.state)
winter = self.create_item("Winter")
self.multiworld.state.collect(winter, event=False)
self.multiworld.state.collect(winter, prevent_sweep=False)
self.assert_rule_true(sturgeon_rule, self.multiworld.state)
self.remove(fishing_rod)
@@ -53,24 +53,24 @@ class TestProgressiveToolsLogic(SVTestBase):
def test_old_master_cannoli(self):
self.multiworld.state.prog_items = {1: Counter()}
self.multiworld.state.collect(self.create_item("Progressive Axe"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Axe"), event=False)
self.multiworld.state.collect(self.create_item("Summer"), event=False)
self.multiworld.state.collect(self.create_item("Progressive Axe"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Progressive Axe"), prevent_sweep=False)
self.multiworld.state.collect(self.create_item("Summer"), prevent_sweep=False)
self.collect_lots_of_money()
rule = self.world.logic.region.can_reach_location("Old Master Cannoli")
self.assert_rule_false(rule, self.multiworld.state)
fall = self.create_item("Fall")
self.multiworld.state.collect(fall, event=False)
self.multiworld.state.collect(fall, prevent_sweep=False)
self.assert_rule_false(rule, self.multiworld.state)
tuesday = self.create_item("Traveling Merchant: Tuesday")
self.multiworld.state.collect(tuesday, event=False)
self.multiworld.state.collect(tuesday, prevent_sweep=False)
self.assert_rule_false(rule, self.multiworld.state)
rare_seed = self.create_item("Rare Seed")
self.multiworld.state.collect(rare_seed, event=False)
self.multiworld.state.collect(rare_seed, prevent_sweep=False)
self.assert_rule_true(rule, self.multiworld.state)
self.remove(fall)
@@ -80,11 +80,11 @@ class TestProgressiveToolsLogic(SVTestBase):
green_house = self.create_item("Greenhouse")
self.collect(self.create_item(Event.fall_farming))
self.multiworld.state.collect(green_house, event=False)
self.multiworld.state.collect(green_house, prevent_sweep=False)
self.assert_rule_false(rule, self.multiworld.state)
friday = self.create_item("Traveling Merchant: Friday")
self.multiworld.state.collect(friday, event=False)
self.multiworld.state.collect(friday, prevent_sweep=False)
self.assertTrue(self.multiworld.get_location("Old Master Cannoli", 1).access_rule(self.multiworld.state))
self.remove(green_house)
@@ -111,7 +111,7 @@ class TestToolVanillaRequiresBlacksmith(SVTestBase):
for material in [ToolMaterial.copper, ToolMaterial.iron, ToolMaterial.gold, ToolMaterial.iridium]:
self.assert_rule_false(self.world.logic.tool.has_tool(tool, material), self.multiworld.state)
self.multiworld.state.collect(self.create_item(railroad_item), event=False)
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False)
for tool in [Tool.pickaxe, Tool.axe, Tool.hoe, Tool.trash_can, Tool.watering_can]:
for material in [ToolMaterial.copper, ToolMaterial.iron, ToolMaterial.gold, ToolMaterial.iridium]:
@@ -125,7 +125,7 @@ class TestToolVanillaRequiresBlacksmith(SVTestBase):
for fishing_rod_level in [3, 4]:
self.assert_rule_false(self.world.logic.tool.has_fishing_rod(fishing_rod_level), self.multiworld.state)
self.multiworld.state.collect(self.create_item(railroad_item), event=False)
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False)
for fishing_rod_level in [3, 4]:
self.assert_rule_true(self.world.logic.tool.has_fishing_rod(fishing_rod_level), self.multiworld.state)

View File

@@ -10,16 +10,16 @@ class TestWeaponsLogic(SVTestBase):
}
def test_mine(self):
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), event=True)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), event=True)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), event=True)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), event=True)
self.multiworld.state.collect(self.create_item("Progressive House"), event=True)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), prevent_sweep=True)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), prevent_sweep=True)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), prevent_sweep=True)
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), prevent_sweep=True)
self.multiworld.state.collect(self.create_item("Progressive House"), prevent_sweep=True)
self.collect([self.create_item("Combat Level")] * 10)
self.collect([self.create_item("Mining Level")] * 10)
self.collect([self.create_item("Progressive Mine Elevator")] * 24)
self.multiworld.state.collect(self.create_item("Bus Repair"), event=True)
self.multiworld.state.collect(self.create_item("Skull Key"), event=True)
self.multiworld.state.collect(self.create_item("Bus Repair"), prevent_sweep=True)
self.multiworld.state.collect(self.create_item("Skull Key"), prevent_sweep=True)
self.GiveItemAndCheckReachableMine("Progressive Sword", 1)
self.GiveItemAndCheckReachableMine("Progressive Dagger", 1)
@@ -43,7 +43,7 @@ class TestWeaponsLogic(SVTestBase):
def GiveItemAndCheckReachableMine(self, item_name: str, reachable_level: int):
item = self.multiworld.create_item(item_name, self.player)
self.multiworld.state.collect(item, event=True)
self.multiworld.state.collect(item, prevent_sweep=True)
rule = self.world.logic.mine.can_mine_in_the_mines_floor_1_40()
if reachable_level > 0:
self.assert_rule_true(rule, self.multiworld.state)

View File

@@ -135,11 +135,11 @@ def get_location_datas(player: Optional[int], options: Optional[TimespinnerOptio
LocationData('Upper Lake Serene', 'Lake Serene: Pyramid keys room', 1337104),
LocationData('Upper Lake Serene', 'Lake Serene (Upper): Chicken ledge', 1337174),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Deep dive', 1337105),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Under the eels', 1337106),
LocationData('Left Side forest Caves', 'Lake Serene (Lower): Under the eels', 1337106, lambda state: state.has('Water Mask', player)),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Water spikes room', 1337107),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater secret', 1337108, logic.can_break_walls),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): T chest', 1337109, lambda state: flooded.flood_lake_serene or logic.has_doublejump_of_npc(state)),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Past the eels', 1337110),
LocationData('Left Side forest Caves', 'Lake Serene (Lower): Past the eels', 1337110, lambda state: state.has('Water Mask', player)),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater pedestal', 1337111, lambda state: flooded.flood_lake_serene or logic.has_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Shroom jump room', 1337112, lambda state: flooded.flood_maw or logic.has_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Secret room', 1337113, lambda state: logic.can_break_walls(state) and (not flooded.flood_maw or state.has('Water Mask', player))),

View File

@@ -220,8 +220,8 @@ class TunicWorld(World):
player_dir = "<->" if player_cxn.direction == "both" else "-->"
raise OptionError(f"TUNIC: Conflict between seed group {group}'s plando "
f"connection {group_cxn.entrance} {group_dir} {group_cxn.exit} and "
f"{tunic.multiworld.get_player_name(tunic.player)}'s plando "
f"connection {player_cxn.entrance} {player_dir} {player_cxn.exit}")
f"{tunic.player_name}'s plando connection "
f"{player_cxn.entrance} {player_dir} {player_cxn.exit}")
if new_cxn:
cls.seed_groups[group]["plando"].append(player_cxn)
@@ -254,17 +254,17 @@ class TunicWorld(World):
if self.options.laurels_location:
laurels = self.create_item("Hero's Laurels")
if self.options.laurels_location == "6_coins":
self.multiworld.get_location("Coins in the Well - 6 Coins", self.player).place_locked_item(laurels)
self.get_location("Coins in the Well - 6 Coins").place_locked_item(laurels)
elif self.options.laurels_location == "10_coins":
self.multiworld.get_location("Coins in the Well - 10 Coins", self.player).place_locked_item(laurels)
self.get_location("Coins in the Well - 10 Coins").place_locked_item(laurels)
elif self.options.laurels_location == "10_fairies":
self.multiworld.get_location("Secret Gathering Place - 10 Fairy Reward", self.player).place_locked_item(laurels)
self.get_location("Secret Gathering Place - 10 Fairy Reward").place_locked_item(laurels)
items_to_create["Hero's Laurels"] = 0
if self.options.keys_behind_bosses:
for rgb_hexagon, location in hexagon_locations.items():
hex_item = self.create_item(gold_hexagon if self.options.hexagon_quest else rgb_hexagon)
self.multiworld.get_location(location, self.player).place_locked_item(hex_item)
self.get_location(location).place_locked_item(hex_item)
items_to_create[rgb_hexagon] = 0
items_to_create[gold_hexagon] -= 3
@@ -364,15 +364,15 @@ class TunicWorld(World):
self.multiworld.regions.append(region)
for region_name, exits in tunic_regions.items():
region = self.multiworld.get_region(region_name, self.player)
region = self.get_region(region_name)
region.add_exits(exits)
for location_name, location_id in self.location_name_to_id.items():
region = self.multiworld.get_region(location_table[location_name].region, self.player)
region = self.get_region(location_table[location_name].region)
location = TunicLocation(self.player, location_name, location_id, region)
region.locations.append(location)
victory_region = self.multiworld.get_region("Spirit Arena", self.player)
victory_region = self.get_region("Spirit Arena")
victory_location = TunicLocation(self.player, "The Heir", None, victory_region)
victory_location.place_locked_item(TunicItem("Victory", ItemClassification.progression, None, self.player))
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
@@ -420,10 +420,8 @@ class TunicWorld(World):
name, connection = paths[location.parent_region]
except KeyError:
# logic bug, proceed with warning since it takes a long time to update AP
warning(f"{location.name} is not logically accessible for "
f"{self.multiworld.get_file_safe_player_name(self.player)}. "
"Creating entrance hint Inaccessible. "
"Please report this to the TUNIC rando devs.")
warning(f"{location.name} is not logically accessible for {self.player_name}. "
"Creating entrance hint Inaccessible. Please report this to the TUNIC rando devs.")
hint_text = "Inaccessible"
else:
while connection != ("Menu", None):
@@ -496,7 +494,9 @@ class TunicWorld(World):
return slot_data
# for the universal tracker, doesn't get called in standard gen
# docs: https://github.com/FarisTheAncient/Archipelago/blob/tracker/worlds/tracker/docs/re-gen-passthrough.md
@staticmethod
def interpret_slot_data(slot_data: Dict[str, Any]) -> Dict[str, Any]:
# returning slot_data so it regens, giving it back in multiworld.re_gen_passthrough
# we are using re_gen_passthrough over modifying the world here due to complexities with ER
return slot_data

View File

@@ -183,7 +183,7 @@ def pair_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal
portal_pairs: Dict[Portal, Portal] = {}
dead_ends: List[Portal] = []
two_plus: List[Portal] = []
player_name = world.multiworld.get_player_name(world.player)
player_name = world.player_name
portal_map = portal_mapping.copy()
laurels_zips = world.options.laurels_zips.value
ice_grappling = world.options.ice_grappling.value

View File

@@ -67,12 +67,15 @@ class UndertaleWorld(World):
"only_flakes": bool(self.options.only_flakes.value),
"no_equips": bool(self.options.no_equips.value),
"key_hunt": bool(self.options.key_hunt.value),
"key_pieces": self.options.key_pieces.value,
"rando_love": bool(self.options.rando_love.value),
"rando_stats": bool(self.options.rando_stats.value),
"key_pieces": int(self.options.key_pieces.value),
"rando_love": bool(self.options.rando_love and (self.options.route_required == "genocide" or self.options.route_required == "all_routes")),
"rando_stats": bool(self.options.rando_stats and (self.options.route_required == "genocide" or self.options.route_required == "all_routes")),
"prog_armor": bool(self.options.prog_armor.value),
"prog_weapons": bool(self.options.prog_weapons.value),
"rando_item_button": bool(self.options.rando_item_button.value)
"rando_item_button": bool(self.options.rando_item_button.value),
"route_required": int(self.options.route_required.value),
"temy_include": int(self.options.temy_include.value)
}
def get_filler_item_name(self):
@@ -220,16 +223,7 @@ class UndertaleWorld(World):
link_undertale_areas(self.multiworld, self.player)
def fill_slot_data(self):
slot_data = self._get_undertale_data()
for option_name in self.options.as_dict():
option = getattr(self.multiworld, option_name)[self.player]
if (option_name == "rando_love" or option_name == "rando_stats") and \
self.options.route_required != "genocide" and \
self.options.route_required != "all_routes":
option.value = False
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
slot_data[option_name] = int(option.value)
return slot_data
return self._get_undertale_data()
def create_item(self, name: str) -> Item:
item_data = item_table[name]