Stardew Valley: Fix a bug where locations in logic would disappear from universal tracker as items get sent (#4230)

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
Jouramie 2024-11-29 19:46:35 -05:00 committed by GitHub
parent 492e3a355e
commit e262c8be9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 243 additions and 203 deletions

View File

@ -3,7 +3,7 @@ from random import Random
from typing import Dict, Any, Iterable, Optional, Union, List, TextIO from typing import Dict, Any, Iterable, Optional, Union, List, TextIO
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState
from Options import PerGameCommonOptions from Options import PerGameCommonOptions, Accessibility
from worlds.AutoWorld import World, WebWorld from worlds.AutoWorld import World, WebWorld
from . import rules from . import rules
from .bundles.bundle_room import BundleRoom from .bundles.bundle_room import BundleRoom
@ -91,15 +91,14 @@ class StardewValleyWorld(World):
web = StardewWebWorld() web = StardewWebWorld()
modified_bundles: List[BundleRoom] modified_bundles: List[BundleRoom]
randomized_entrances: Dict[str, str] randomized_entrances: Dict[str, str]
total_progression_items: int
# all_progression_items: Dict[str, int] # If you need to debug total_progression_items, uncommenting this will help tremendously total_progression_items: int
excluded_from_total_progression_items: List[str] = [Event.received_walnuts]
def __init__(self, multiworld: MultiWorld, player: int): def __init__(self, multiworld: MultiWorld, player: int):
super().__init__(multiworld, player) super().__init__(multiworld, player)
self.filler_item_pool_names = [] self.filler_item_pool_names = []
self.total_progression_items = 0 self.total_progression_items = 0
# self.all_progression_items = dict()
# Taking the seed specified in slot data for UT, otherwise just generating the seed. # Taking the seed specified in slot data for UT, otherwise just generating the seed.
self.seed = getattr(multiworld, "re_gen_passthrough", {}).get(STARDEW_VALLEY, self.random.getrandbits(64)) self.seed = getattr(multiworld, "re_gen_passthrough", {}).get(STARDEW_VALLEY, self.random.getrandbits(64))
@ -121,17 +120,27 @@ class StardewValleyWorld(World):
goal_is_perfection = self.options.goal == Goal.option_perfection goal_is_perfection = self.options.goal == Goal.option_perfection
goal_is_island_related = goal_is_walnut_hunter or goal_is_perfection goal_is_island_related = goal_is_walnut_hunter or goal_is_perfection
exclude_ginger_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true exclude_ginger_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
if goal_is_island_related and exclude_ginger_island: if goal_is_island_related and exclude_ginger_island:
self.options.exclude_ginger_island.value = ExcludeGingerIsland.option_false self.options.exclude_ginger_island.value = ExcludeGingerIsland.option_false
goal_name = self.options.goal.current_key goal_name = self.options.goal.current_key
player_name = self.multiworld.player_name[self.player]
logger.warning( logger.warning(
f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})") f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({self.player_name})")
if exclude_ginger_island and self.options.walnutsanity != Walnutsanity.preset_none: if exclude_ginger_island and self.options.walnutsanity != Walnutsanity.preset_none:
self.options.walnutsanity.value = Walnutsanity.preset_none self.options.walnutsanity.value = Walnutsanity.preset_none
player_name = self.multiworld.player_name[self.player]
logger.warning( logger.warning(
f"Walnutsanity requires Ginger Island. Ginger Island was excluded from {self.player} ({player_name})'s world, so walnutsanity was force disabled") f"Walnutsanity requires Ginger Island. Ginger Island was excluded from {self.player} ({self.player_name})'s world, so walnutsanity was force disabled")
if goal_is_perfection and self.options.accessibility == Accessibility.option_minimal:
self.options.accessibility.value = Accessibility.option_full
logger.warning(
f"Goal 'Perfection' requires full accessibility. Accessibility setting forced to 'Full' for player {self.player} ({self.player_name})")
elif self.options.goal == Goal.option_allsanity and self.options.accessibility == Accessibility.option_minimal:
self.options.accessibility.value = Accessibility.option_full
logger.warning(
f"Goal 'Allsanity' requires full accessibility. Accessibility setting forced to 'Full' for player {self.player} ({self.player_name})")
def create_regions(self): def create_regions(self):
def create_region(name: str, exits: Iterable[str]) -> Region: def create_region(name: str, exits: Iterable[str]) -> Region:
@ -171,8 +180,7 @@ class StardewValleyWorld(World):
for location in self.multiworld.get_locations(self.player) for location in self.multiworld.get_locations(self.player)
if location.address is not None]) if location.address is not None])
created_items = create_items(self.create_item, self.delete_item, locations_count, items_to_exclude, self.options, self.content, created_items = create_items(self.create_item, locations_count, items_to_exclude, self.options, self.content, self.random)
self.random)
self.multiworld.itempool += created_items self.multiworld.itempool += created_items
@ -180,6 +188,18 @@ class StardewValleyWorld(World):
self.setup_player_events() self.setup_player_events()
self.setup_victory() self.setup_victory()
# This is really a best-effort to get the total progression items count. It is mostly used to spread grinds across spheres are push back locations that
# only become available after months or years in game. In most cases, not having the exact count will not impact the logic.
#
# The actual total can be impacted by the start_inventory_from_pool, when items are removed from the pool but not from the total. The is also a bug
# with plando where additional progression items can be created without being accounted for, which impact the real amount of progression items. This can
# ultimately create unwinnable seeds where some items (like Blueberry seeds) are locked in Shipsanity: Blueberry, but world is deemed winnable as the
# winning rule only check the count of collected progression items.
self.total_progression_items += sum(1 for i in self.multiworld.precollected_items[self.player] if i.advancement)
self.total_progression_items += sum(1 for i in self.multiworld.get_filled_locations(self.player) if i.advancement)
self.total_progression_items += sum(1 for i in created_items if i.advancement)
self.total_progression_items -= 1 # -1 for the victory event
def precollect_starting_season(self): def precollect_starting_season(self):
if self.options.season_randomization == SeasonRandomization.option_progressive: if self.options.season_randomization == SeasonRandomization.option_progressive:
return return
@ -304,14 +324,8 @@ class StardewValleyWorld(World):
if override_classification is None: if override_classification is None:
override_classification = item.classification override_classification = item.classification
if override_classification & ItemClassification.progression:
self.total_progression_items += 1
return StardewItem(item.name, override_classification, item.code, self.player) return StardewItem(item.name, override_classification, item.code, self.player)
def delete_item(self, item: Item):
if item.classification & ItemClassification.progression:
self.total_progression_items -= 1
def create_starting_item(self, item: Union[str, ItemData]) -> StardewItem: def create_starting_item(self, item: Union[str, ItemData]) -> StardewItem:
if isinstance(item, str): if isinstance(item, str):
item = item_table[item] item = item_table[item]
@ -330,10 +344,6 @@ class StardewValleyWorld(World):
region.locations.append(location) region.locations.append(location)
location.place_locked_item(StardewItem(item, ItemClassification.progression, None, self.player)) location.place_locked_item(StardewItem(item, ItemClassification.progression, None, self.player))
# This is not ideal, but the rule count them so...
if item != Event.victory:
self.total_progression_items += 1
def set_rules(self): def set_rules(self):
set_rules(self) set_rules(self)
@ -426,15 +436,25 @@ class StardewValleyWorld(World):
def collect(self, state: CollectionState, item: StardewItem) -> bool: def collect(self, state: CollectionState, item: StardewItem) -> bool:
change = super().collect(state, item) change = super().collect(state, item)
if change: if not change:
state.prog_items[self.player][Event.received_walnuts] += self.get_walnut_amount(item.name) return False
return change
walnut_amount = self.get_walnut_amount(item.name)
if walnut_amount:
state.prog_items[self.player][Event.received_walnuts] += walnut_amount
return True
def remove(self, state: CollectionState, item: StardewItem) -> bool: def remove(self, state: CollectionState, item: StardewItem) -> bool:
change = super().remove(state, item) change = super().remove(state, item)
if change: if not change:
state.prog_items[self.player][Event.received_walnuts] -= self.get_walnut_amount(item.name) return False
return change
walnut_amount = self.get_walnut_amount(item.name)
if walnut_amount:
state.prog_items[self.player][Event.received_walnuts] -= walnut_amount
return True
@staticmethod @staticmethod
def get_walnut_amount(item_name: str) -> int: def get_walnut_amount(item_name: str) -> int:

View File

@ -169,14 +169,14 @@ def get_too_many_items_error_message(locations_count: int, items_count: int) ->
return f"There should be at least as many locations [{locations_count}] as there are mandatory items [{items_count}]" return f"There should be at least as many locations [{locations_count}] as there are mandatory items [{items_count}]"
def create_items(item_factory: StardewItemFactory, item_deleter: StardewItemDeleter, locations_count: int, items_to_exclude: List[Item], def create_items(item_factory: StardewItemFactory, locations_count: int, items_to_exclude: List[Item],
options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]: options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]:
items = [] items = []
unique_items = create_unique_items(item_factory, options, content, random) unique_items = create_unique_items(item_factory, options, content, random)
remove_items(item_deleter, items_to_exclude, unique_items) remove_items(items_to_exclude, unique_items)
remove_items_if_no_room_for_them(item_deleter, unique_items, locations_count, random) remove_items_if_no_room_for_them(unique_items, locations_count, random)
items += unique_items items += unique_items
logger.debug(f"Created {len(unique_items)} unique items") logger.debug(f"Created {len(unique_items)} unique items")
@ -192,14 +192,13 @@ def create_items(item_factory: StardewItemFactory, item_deleter: StardewItemDele
return items return items
def remove_items(item_deleter: StardewItemDeleter, items_to_remove, items): def remove_items(items_to_remove, items):
for item in items_to_remove: for item in items_to_remove:
if item in items: if item in items:
items.remove(item) items.remove(item)
item_deleter(item)
def remove_items_if_no_room_for_them(item_deleter: StardewItemDeleter, unique_items: List[Item], locations_count: int, random: Random): def remove_items_if_no_room_for_them(unique_items: List[Item], locations_count: int, random: Random):
if len(unique_items) <= locations_count: if len(unique_items) <= locations_count:
return return
@ -212,7 +211,7 @@ def remove_items_if_no_room_for_them(item_deleter: StardewItemDeleter, unique_it
logger.debug(f"Player has more items than locations, trying to remove {number_of_items_to_remove} random filler items") logger.debug(f"Player has more items than locations, trying to remove {number_of_items_to_remove} random filler items")
assert len(removable_items) >= number_of_items_to_remove, get_too_many_items_error_message(locations_count, len(unique_items)) assert len(removable_items) >= number_of_items_to_remove, get_too_many_items_error_message(locations_count, len(unique_items))
items_to_remove = random.sample(removable_items, number_of_items_to_remove) items_to_remove = random.sample(removable_items, number_of_items_to_remove)
remove_items(item_deleter, items_to_remove, unique_items) remove_items(items_to_remove, unique_items)
def create_unique_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]: def create_unique_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]:

View File

@ -1,10 +1,13 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Iterable, Union, List, Tuple, Hashable from typing import Iterable, Union, List, Tuple, Hashable, TYPE_CHECKING
from BaseClasses import CollectionState from BaseClasses import CollectionState
from .base import BaseStardewRule, CombinableStardewRule from .base import BaseStardewRule, CombinableStardewRule
from .protocol import StardewRule from .protocol import StardewRule
if TYPE_CHECKING:
from .. import StardewValleyWorld
class TotalReceived(BaseStardewRule): class TotalReceived(BaseStardewRule):
count: int count: int
@ -102,16 +105,19 @@ class HasProgressionPercent(CombinableStardewRule):
return self.percent return self.percent
def __call__(self, state: CollectionState) -> bool: def __call__(self, state: CollectionState) -> bool:
stardew_world = state.multiworld.worlds[self.player] stardew_world: "StardewValleyWorld" = state.multiworld.worlds[self.player]
total_count = stardew_world.total_progression_items total_count = stardew_world.total_progression_items
needed_count = (total_count * self.percent) // 100 needed_count = (total_count * self.percent) // 100
player_state = state.prog_items[self.player] player_state = state.prog_items[self.player]
if needed_count <= len(player_state): if needed_count <= len(player_state) - len(stardew_world.excluded_from_total_progression_items):
return True return True
total_count = 0 total_count = 0
for item, item_count in player_state.items(): for item, item_count in player_state.items():
if item in stardew_world.excluded_from_total_progression_items:
continue
total_count += item_count total_count += item_count
if total_count >= needed_count: if total_count >= needed_count:
return True return True

View File

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

View File

@ -1,6 +1,6 @@
import itertools import itertools
from Options import NamedRange from Options import NamedRange, Accessibility
from . import SVTestCase, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, solo_multiworld from . import SVTestCase, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, solo_multiworld
from .assertion import WorldAssertMixin from .assertion import WorldAssertMixin
from .long.option_names import all_option_choices from .long.option_names import all_option_choices
@ -54,6 +54,23 @@ class TestGoal(SVTestCase):
victory = multi_world.find_item("Victory", 1) victory = multi_world.find_item("Victory", 1)
self.assertEqual(victory.name, location) self.assertEqual(victory.name, location)
def test_given_perfection_goal_when_generate_then_accessibility_is_forced_to_full(self):
"""There is a bug with the current victory condition of the perfection goal that can create unwinnable seeds if the accessibility is set to minimal and
the world gets flooded with progression items through plando. This will increase the amount of collected progression items pass the total amount
calculated for the world when creating the item pool. This will cause the victory condition to be met before all locations are collected, so some could
be left inaccessible, which in practice will make the seed unwinnable.
"""
for accessibility in Accessibility.options.keys():
world_options = {Goal.internal_name: Goal.option_perfection, "accessibility": accessibility}
with self.solo_world_sub_test(f"Accessibility: {accessibility}", world_options) as (_, world):
self.assertEqual(world.options.accessibility, Accessibility.option_full)
def test_given_allsanity_goal_when_generate_then_accessibility_is_forced_to_full(self):
for accessibility in Accessibility.options.keys():
world_options = {Goal.internal_name: Goal.option_allsanity, "accessibility": accessibility}
with self.solo_world_sub_test(f"Accessibility: {accessibility}", world_options) as (_, world):
self.assertEqual(world.options.accessibility, Accessibility.option_full)
class TestSeasonRandomization(SVTestCase): class TestSeasonRandomization(SVTestCase):
def test_given_disabled_when_generate_then_all_seasons_are_precollected(self): def test_given_disabled_when_generate_then_all_seasons_are_precollected(self):

View File

@ -6,7 +6,7 @@ from argparse import Namespace
from contextlib import contextmanager from contextlib import contextmanager
from typing import Dict, ClassVar, Iterable, Tuple, Optional, List, Union, Any from typing import Dict, ClassVar, Iterable, Tuple, Optional, List, Union, Any
from BaseClasses import MultiWorld, CollectionState, PlandoOptions, get_seed, Location, Item, ItemClassification from BaseClasses import MultiWorld, CollectionState, PlandoOptions, get_seed, Location, Item
from Options import VerifyKeys from Options import VerifyKeys
from test.bases import WorldTestBase from test.bases import WorldTestBase
from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld
@ -236,7 +236,6 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
self.original_state = self.multiworld.state.copy() self.original_state = self.multiworld.state.copy()
self.original_itempool = self.multiworld.itempool.copy() self.original_itempool = self.multiworld.itempool.copy()
self.original_prog_item_count = world.total_progression_items
self.unfilled_locations = self.multiworld.get_unfilled_locations(1) self.unfilled_locations = self.multiworld.get_unfilled_locations(1)
if self.constructed: if self.constructed:
self.world = world # noqa self.world = world # noqa
@ -246,7 +245,6 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
self.multiworld.itempool = self.original_itempool self.multiworld.itempool = self.original_itempool
for location in self.unfilled_locations: for location in self.unfilled_locations:
location.item = None location.item = None
self.world.total_progression_items = self.original_prog_item_count
self.multiworld.lock.release() self.multiworld.lock.release()
@ -257,20 +255,13 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
return super().run_default_tests return super().run_default_tests
def collect_lots_of_money(self, percent: float = 0.25): def collect_lots_of_money(self, percent: float = 0.25):
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), prevent_sweep=False) self.collect("Shipping Bin")
real_total_prog_items = self.multiworld.worlds[self.player].total_progression_items real_total_prog_items = self.world.total_progression_items
required_prog_items = int(round(real_total_prog_items * percent)) required_prog_items = int(round(real_total_prog_items * percent))
for i in range(required_prog_items): self.collect("Stardrop", required_prog_items)
self.multiworld.state.collect(self.world.create_item("Stardrop"), prevent_sweep=False)
self.multiworld.worlds[self.player].total_progression_items = real_total_prog_items
def collect_all_the_money(self): def collect_all_the_money(self):
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), prevent_sweep=False) self.collect_lots_of_money(0.95)
real_total_prog_items = self.multiworld.worlds[self.player].total_progression_items
required_prog_items = int(round(real_total_prog_items * 0.95))
for i in range(required_prog_items):
self.multiworld.state.collect(self.world.create_item("Stardrop"), prevent_sweep=False)
self.multiworld.worlds[self.player].total_progression_items = real_total_prog_items
def collect_everything(self): def collect_everything(self):
non_event_items = [item for item in self.multiworld.get_items() if item.code] non_event_items = [item for item in self.multiworld.get_items() if item.code]
@ -278,7 +269,8 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
self.multiworld.state.collect(item) self.multiworld.state.collect(item)
def collect_all_except(self, item_to_not_collect: str): def collect_all_except(self, item_to_not_collect: str):
for item in self.multiworld.get_items(): non_event_items = [item for item in self.multiworld.get_items() if item.code]
for item in non_event_items:
if item.name != item_to_not_collect: if item.name != item_to_not_collect:
self.multiworld.state.collect(item) self.multiworld.state.collect(item)
@ -290,25 +282,26 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
def collect(self, item: Union[str, Item, Iterable[Item]], count: int = 1) -> Union[None, Item, List[Item]]: def collect(self, item: Union[str, Item, Iterable[Item]], count: int = 1) -> Union[None, Item, List[Item]]:
assert count > 0 assert count > 0
if not isinstance(item, str): if not isinstance(item, str):
super().collect(item) super().collect(item)
return return
if count == 1: if count == 1:
item = self.create_item(item) item = self.create_item(item)
self.multiworld.state.collect(item) self.multiworld.state.collect(item)
return item return item
items = [] items = []
for i in range(count): for i in range(count):
item = self.create_item(item) item = self.create_item(item)
self.multiworld.state.collect(item) self.multiworld.state.collect(item)
items.append(item) items.append(item)
return items return items
def create_item(self, item: str) -> StardewItem: def create_item(self, item: str) -> StardewItem:
created_item = self.world.create_item(item) return self.world.create_item(item)
if created_item.classification & ItemClassification.progression:
self.multiworld.worlds[self.player].total_progression_items -= 1
return created_item
def remove_one_by_name(self, item: str) -> None: def remove_one_by_name(self, item: str) -> None:
self.remove(self.create_item(item)) self.remove(self.create_item(item))
@ -336,7 +329,6 @@ def solo_multiworld(world_options: Optional[Dict[Union[str, StardewValleyOption]
original_state = multiworld.state.copy() original_state = multiworld.state.copy()
original_itempool = multiworld.itempool.copy() original_itempool = multiworld.itempool.copy()
unfilled_locations = multiworld.get_unfilled_locations(1) unfilled_locations = multiworld.get_unfilled_locations(1)
original_prog_item_count = world.total_progression_items
yield multiworld, world yield multiworld, world
@ -344,7 +336,6 @@ def solo_multiworld(world_options: Optional[Dict[Union[str, StardewValleyOption]
multiworld.itempool = original_itempool multiworld.itempool = original_itempool
for location in unfilled_locations: for location in unfilled_locations:
location.item = None location.item = None
multiworld.total_progression_items = original_prog_item_count
multiworld.lock.release() multiworld.lock.release()

View File

@ -19,8 +19,8 @@ class TestArcadeMachinesLogic(SVTestBase):
life = self.create_item("JotPK: Extra Life") life = self.create_item("JotPK: Extra Life")
drop = self.create_item("JotPK: Increased Drop Rate") drop = self.create_item("JotPK: Increased Drop Rate")
self.multiworld.state.collect(boots, prevent_sweep=True) self.multiworld.state.collect(boots)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state)) 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 2")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(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(boots)
self.remove(gun) self.remove(gun)
self.multiworld.state.collect(boots, prevent_sweep=True) self.multiworld.state.collect(boots)
self.multiworld.state.collect(boots, prevent_sweep=True) self.multiworld.state.collect(boots)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state)) 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 2")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(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.remove(boots) self.remove(boots)
self.multiworld.state.collect(boots, prevent_sweep=True) self.multiworld.state.collect(boots)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.multiworld.state.collect(ammo, prevent_sweep=True) self.multiworld.state.collect(ammo)
self.multiworld.state.collect(life, prevent_sweep=True) self.multiworld.state.collect(life)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state)) 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 2")(self.multiworld.state))
self.assertFalse(self.world.logic.region.can_reach("JotPK World 3")(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(ammo)
self.remove(life) self.remove(life)
self.multiworld.state.collect(boots, prevent_sweep=True) self.multiworld.state.collect(boots)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.multiworld.state.collect(ammo, prevent_sweep=True) self.multiworld.state.collect(ammo)
self.multiworld.state.collect(ammo, prevent_sweep=True) self.multiworld.state.collect(ammo)
self.multiworld.state.collect(life, prevent_sweep=True) self.multiworld.state.collect(life)
self.multiworld.state.collect(drop, prevent_sweep=True) self.multiworld.state.collect(drop)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state)) 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 2")(self.multiworld.state))
self.assertTrue(self.world.logic.region.can_reach("JotPK World 3")(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(life)
self.remove(drop) self.remove(drop)
self.multiworld.state.collect(boots, prevent_sweep=True) self.multiworld.state.collect(boots)
self.multiworld.state.collect(boots, prevent_sweep=True) self.multiworld.state.collect(boots)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.multiworld.state.collect(gun, prevent_sweep=True) self.multiworld.state.collect(gun)
self.multiworld.state.collect(ammo, prevent_sweep=True) self.multiworld.state.collect(ammo)
self.multiworld.state.collect(ammo, prevent_sweep=True) self.multiworld.state.collect(ammo)
self.multiworld.state.collect(ammo, prevent_sweep=True) self.multiworld.state.collect(ammo)
self.multiworld.state.collect(life, prevent_sweep=True) self.multiworld.state.collect(life)
self.multiworld.state.collect(drop, prevent_sweep=True) self.multiworld.state.collect(drop)
self.assertTrue(self.world.logic.region.can_reach("JotPK World 1")(self.multiworld.state)) 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 2")(self.multiworld.state))
self.assertTrue(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state)) self.assertTrue(self.world.logic.region.can_reach("JotPK World 3")(self.multiworld.state))

View File

@ -23,7 +23,7 @@ class TestBuildingLogic(SVTestBase):
self.assertFalse(big_coop_blueprint_rule(self.multiworld.state), self.assertFalse(big_coop_blueprint_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}") f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
self.multiworld.state.collect(self.create_item("Progressive Coop"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive Coop"))
self.assertTrue(big_coop_blueprint_rule(self.multiworld.state), self.assertTrue(big_coop_blueprint_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}") f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}")
@ -33,10 +33,10 @@ class TestBuildingLogic(SVTestBase):
self.collect_lots_of_money() self.collect_lots_of_money()
self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)) self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
self.multiworld.state.collect(self.create_item("Progressive Coop"), prevent_sweep=True) self.multiworld.state.collect(self.create_item("Progressive Coop"))
self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)) self.assertFalse(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
self.multiworld.state.collect(self.create_item("Progressive Coop"), prevent_sweep=True) self.multiworld.state.collect(self.create_item("Progressive Coop"))
self.assertTrue(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)) self.assertTrue(self.world.logic.region.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state))
def test_big_shed_blueprint(self): def test_big_shed_blueprint(self):
@ -48,6 +48,6 @@ class TestBuildingLogic(SVTestBase):
self.assertFalse(big_shed_rule(self.multiworld.state), self.assertFalse(big_shed_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}") f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}")
self.multiworld.state.collect(self.create_item("Progressive Shed"), prevent_sweep=True) self.multiworld.state.collect(self.create_item("Progressive Shed"))
self.assertTrue(big_shed_rule(self.multiworld.state), self.assertTrue(big_shed_rule(self.multiworld.state),
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}") 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) rule = self.world.logic.region.can_reach_location(location)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Progressive House"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive House"))
self.multiworld.state.collect(self.create_item("Radish Seeds"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Radish Seeds"))
self.multiworld.state.collect(self.create_item("Spring"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Spring"))
self.multiworld.state.collect(self.create_item("Summer"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Summer"))
self.collect_lots_of_money() self.collect_lots_of_money()
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("The Queen of Sauce"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("The Queen of Sauce"))
self.assert_rule_true(rule, self.multiworld.state) self.assert_rule_true(rule, self.multiworld.state)
@ -42,21 +42,21 @@ class TestRecipeReceiveLogic(SVTestBase):
rule = self.world.logic.region.can_reach_location(location) rule = self.world.logic.region.can_reach_location(location)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Progressive House"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive House"))
self.multiworld.state.collect(self.create_item("Radish Seeds"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Radish Seeds"))
self.multiworld.state.collect(self.create_item("Summer"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Summer"))
self.collect_lots_of_money() self.collect_lots_of_money()
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
spring = self.create_item("Spring") spring = self.create_item("Spring")
qos = self.create_item("The Queen of Sauce") qos = self.create_item("The Queen of Sauce")
self.multiworld.state.collect(spring, prevent_sweep=False) self.multiworld.state.collect(spring)
self.multiworld.state.collect(qos, prevent_sweep=False) self.multiworld.state.collect(qos)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.remove(spring) self.multiworld.state.remove(spring)
self.multiworld.state.remove(qos) self.multiworld.state.remove(qos)
self.multiworld.state.collect(self.create_item("Radish Salad Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Radish Salad Recipe"))
self.assert_rule_true(rule, self.multiworld.state) self.assert_rule_true(rule, self.multiworld.state)
def test_get_chefsanity_check_recipe(self): def test_get_chefsanity_check_recipe(self):
@ -64,20 +64,20 @@ class TestRecipeReceiveLogic(SVTestBase):
rule = self.world.logic.region.can_reach_location(location) rule = self.world.logic.region.can_reach_location(location)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Spring"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Spring"))
self.collect_lots_of_money() self.collect_lots_of_money()
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
seeds = self.create_item("Radish Seeds") seeds = self.create_item("Radish Seeds")
summer = self.create_item("Summer") summer = self.create_item("Summer")
house = self.create_item("Progressive House") house = self.create_item("Progressive House")
self.multiworld.state.collect(seeds, prevent_sweep=False) self.multiworld.state.collect(seeds)
self.multiworld.state.collect(summer, prevent_sweep=False) self.multiworld.state.collect(summer)
self.multiworld.state.collect(house, prevent_sweep=False) self.multiworld.state.collect(house)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.remove(seeds) self.multiworld.state.remove(seeds)
self.multiworld.state.remove(summer) self.multiworld.state.remove(summer)
self.multiworld.state.remove(house) self.multiworld.state.remove(house)
self.multiworld.state.collect(self.create_item("The Queen of Sauce"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("The Queen of Sauce"))
self.assert_rule_true(rule, self.multiworld.state) self.assert_rule_true(rule, self.multiworld.state)

View File

@ -25,7 +25,7 @@ class TestCraftsanityLogic(SVTestBase):
self.collect_all_the_money() self.collect_all_the_money()
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Marble Brazier Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Marble Brazier Recipe"))
self.assert_rule_true(rule, self.multiworld.state) self.assert_rule_true(rule, self.multiworld.state)
def test_can_learn_crafting_recipe(self): def test_can_learn_crafting_recipe(self):
@ -38,16 +38,16 @@ class TestCraftsanityLogic(SVTestBase):
def test_can_craft_festival_recipe(self): def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"] recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Pumpkin Seeds"))
self.multiworld.state.collect(self.create_item("Torch Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Torch Recipe"))
self.collect_lots_of_money() self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe) rule = self.world.logic.crafting.can_craft(recipe)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Fall"))
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"))
self.assert_rule_true(rule, self.multiworld.state) self.assert_rule_true(rule, self.multiworld.state)
def test_require_furnace_recipe_for_smelting_checks(self): def test_require_furnace_recipe_for_smelting_checks(self):
@ -64,7 +64,7 @@ class TestCraftsanityLogic(SVTestBase):
self.collect_all_the_money() self.collect_all_the_money()
self.assert_rules_false(rules, self.multiworld.state) self.assert_rules_false(rules, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Furnace Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Furnace Recipe"))
self.assert_rules_true(rules, self.multiworld.state) self.assert_rules_true(rules, self.multiworld.state)
@ -79,16 +79,16 @@ class TestCraftsanityWithFestivalsLogic(SVTestBase):
def test_can_craft_festival_recipe(self): def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"] recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Pumpkin Seeds"))
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Fall"))
self.collect_lots_of_money() self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe) rule = self.world.logic.crafting.can_craft(recipe)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"))
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Torch Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Torch Recipe"))
self.assert_rule_true(rule, self.multiworld.state) self.assert_rule_true(rule, self.multiworld.state)
@ -109,7 +109,7 @@ class TestNoCraftsanityLogic(SVTestBase):
def test_can_craft_festival_recipe(self): def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"] recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Pumpkin Seeds"))
self.collect_lots_of_money() self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe) rule = self.world.logic.crafting.can_craft(recipe)
result = rule(self.multiworld.state) result = rule(self.multiworld.state)
@ -126,7 +126,7 @@ class TestNoCraftsanityLogic(SVTestBase):
self.collect([self.create_item("Progressive Sword")] * 4) self.collect([self.create_item("Progressive Sword")] * 4)
self.collect([self.create_item("Progressive Mine Elevator")] * 24) self.collect([self.create_item("Progressive Mine Elevator")] * 24)
self.collect([self.create_item("Progressive Trash Can")] * 2) self.collect([self.create_item("Progressive Trash Can")] * 2)
self.multiworld.state.collect(self.create_item("Furnace Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Furnace Recipe"))
self.collect([self.create_item("Combat Level")] * 10) self.collect([self.create_item("Combat Level")] * 10)
self.collect([self.create_item("Fishing Level")] * 10) self.collect([self.create_item("Fishing Level")] * 10)
self.collect_all_the_money() self.collect_all_the_money()
@ -147,11 +147,11 @@ class TestNoCraftsanityWithFestivalsLogic(SVTestBase):
def test_can_craft_festival_recipe(self): def test_can_craft_festival_recipe(self):
recipe = all_crafting_recipes_by_name["Jack-O-Lantern"] recipe = all_crafting_recipes_by_name["Jack-O-Lantern"]
self.multiworld.state.collect(self.create_item("Pumpkin Seeds"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Pumpkin Seeds"))
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Fall"))
self.collect_lots_of_money() self.collect_lots_of_money()
rule = self.world.logic.crafting.can_craft(recipe) rule = self.world.logic.crafting.can_craft(recipe)
self.assert_rule_false(rule, self.multiworld.state) self.assert_rule_false(rule, self.multiworld.state)
self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Jack-O-Lantern Recipe"))
self.assert_rule_true(rule, self.multiworld.state) 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]: for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state)) self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False) self.multiworld.state.collect(self.create_item(railroad_item))
for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]: for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state)) 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: for donation in donation_locations:
self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state)) self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False) self.multiworld.state.collect(self.create_item(railroad_item))
for donation in donation_locations: for donation in donation_locations:
self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state)) 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]: for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state)) self.assertFalse(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state))
self.multiworld.state.collect(self.create_item(railroad_item), prevent_sweep=False) self.multiworld.state.collect(self.create_item(railroad_item))
for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]: for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
self.assertTrue(self.world.logic.region.can_reach_location(donation.name)(self.multiworld.state)) 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): def test_earning_dating_heart_requires_dating(self):
self.collect_all_the_money() self.collect_all_the_money()
self.multiworld.state.collect(self.create_item("Fall"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Fall"))
self.multiworld.state.collect(self.create_item("Beach Bridge"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Beach Bridge"))
self.multiworld.state.collect(self.create_item("Progressive House"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive House"))
for i in range(3): for i in range(3):
self.multiworld.state.collect(self.create_item("Progressive Pickaxe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive Pickaxe"))
self.multiworld.state.collect(self.create_item("Progressive Weapon"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive Weapon"))
self.multiworld.state.collect(self.create_item("Progressive Axe"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive Axe"))
self.multiworld.state.collect(self.create_item("Progressive Barn"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive Barn"))
for i in range(10): for i in range(10):
self.multiworld.state.collect(self.create_item("Foraging Level"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Foraging Level"))
self.multiworld.state.collect(self.create_item("Farming Level"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Farming Level"))
self.multiworld.state.collect(self.create_item("Mining Level"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Mining Level"))
self.multiworld.state.collect(self.create_item("Combat Level"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Combat Level"))
self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"))
self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"), prevent_sweep=False) self.multiworld.state.collect(self.create_item("Progressive Mine Elevator"))
npc = "Abigail" npc = "Abigail"
heart_name = f"{npc} <3" heart_name = f"{npc} <3"
step = 3 step = 3
self.assert_can_reach_heart_up_to(npc, 3, step) self.assert_can_reach_heart_up_to(npc, 3, step)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False) self.multiworld.state.collect(self.create_item(heart_name))
self.assert_can_reach_heart_up_to(npc, 6, step) self.assert_can_reach_heart_up_to(npc, 6, step)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False) self.multiworld.state.collect(self.create_item(heart_name))
self.assert_can_reach_heart_up_to(npc, 8, step) self.assert_can_reach_heart_up_to(npc, 8, step)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False) self.multiworld.state.collect(self.create_item(heart_name))
self.assert_can_reach_heart_up_to(npc, 10, step) self.assert_can_reach_heart_up_to(npc, 10, step)
self.multiworld.state.collect(self.create_item(heart_name), prevent_sweep=False) self.multiworld.state.collect(self.create_item(heart_name))
self.assert_can_reach_heart_up_to(npc, 14, step) 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): 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): with self.subTest(location.name):
self.remove(bin_item) self.remove(bin_item)
self.assertFalse(self.world.logic.region.can_reach_location(location.name)(self.multiworld.state)) self.assertFalse(self.world.logic.region.can_reach_location(location.name)(self.multiworld.state))
self.multiworld.state.collect(bin_item, prevent_sweep=False) self.multiworld.state.collect(bin_item)
shipsanity_rule = self.world.logic.region.can_reach_location(location.name) shipsanity_rule = self.world.logic.region.can_reach_location(location.name)
self.assert_rule_true(shipsanity_rule, self.multiworld.state) self.assert_rule_true(shipsanity_rule, self.multiworld.state)
self.remove(bin_item) self.remove(bin_item)

View File

@ -1,12 +1,22 @@
import unittest from .. import SVTestBase, allsanity_mods_6_x_x
from ...stardew_rule import HasProgressionPercent
from BaseClasses import ItemClassification
from ...test import solo_multiworld
class TestHasProgressionPercent(unittest.TestCase): class TestHasProgressionPercentWithVictory(SVTestBase):
def test_max_item_amount_is_full_collection(self): options = allsanity_mods_6_x_x()
# Not caching because it fails too often for some reason
with solo_multiworld(world_caching=False) as (multiworld, world): def test_has_100_progression_percent_is_false_while_items_are_missing(self):
progression_item_count = sum(1 for i in multiworld.get_items() if i.classification & ItemClassification.progression) has_100_progression_percent = HasProgressionPercent(1, 100)
self.assertEqual(world.total_progression_items, progression_item_count - 1) # -1 to skip Victory
for i, item in enumerate([i for i in self.multiworld.get_items() if i.advancement and i.code][1:]):
if item.name != "Victory":
self.collect(item)
self.assertFalse(has_100_progression_percent(self.multiworld.state),
f"Rule became true after {i} items, total_progression_items is {self.world.total_progression_items}")
def test_has_100_progression_percent_account_for_victory_not_being_collected(self):
has_100_progression_percent = HasProgressionPercent(1, 100)
self.collect_all_except("Victory")
self.assert_rule_true(has_100_progression_percent, self.multiworld.state)

View File

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

View File

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

View File

@ -7,9 +7,6 @@ import unittest
from BaseClasses import get_seed from BaseClasses import get_seed
from .. import SVTestCase from .. import SVTestCase
# There seems to be 4 bytes that appear at random at the end of the output, breaking the json... I don't know where they came from.
BYTES_TO_REMOVE = 4
# <function Location.<lambda> at 0x102ca98a0> # <function Location.<lambda> at 0x102ca98a0>
lambda_regex = re.compile(r"^<function Location\.<lambda> at (.*)>$") lambda_regex = re.compile(r"^<function Location\.<lambda> at (.*)>$")
@ -27,8 +24,8 @@ class TestGenerationIsStable(SVTestCase):
output_a = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)]) output_a = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)])
output_b = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)]) output_b = subprocess.check_output([sys.executable, '-m', 'worlds.stardew_valley.test.stability.StabilityOutputScript', '--seed', str(seed)])
result_a = json.loads(output_a[:-BYTES_TO_REMOVE]) result_a = json.loads(output_a)
result_b = json.loads(output_b[:-BYTES_TO_REMOVE]) result_b = json.loads(output_b)
for i, ((room_a, bundles_a), (room_b, bundles_b)) in enumerate(zip(result_a["bundles"].items(), result_b["bundles"].items())): for i, ((room_a, bundles_a), (room_b, bundles_b)) in enumerate(zip(result_a["bundles"].items(), result_b["bundles"].items())):
self.assertEqual(room_a, room_b, f"Bundle rooms at index {i} is different between both executions. Seed={seed}") self.assertEqual(room_a, room_b, f"Bundle rooms at index {i} is different between both executions. Seed={seed}")