Stardew Valley: Refactor skill progression to use new feature system (#3662)

* create a first draft of the feature

* use feature in items and locations

* add content to more places

* use feature in logic

* replace option check by feature

* remove unused code

* remove weird white space

* some import nitpicking

* flip negative if
This commit is contained in:
Jouramie 2024-11-30 21:52:07 -05:00 committed by GitHub
parent f735416bda
commit a67688749f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 244 additions and 181 deletions

View File

@ -148,7 +148,7 @@ class StardewValleyWorld(World):
region.exits = [Entrance(self.player, exit_name, region) for exit_name in exits]
return region
world_regions, world_entrances, self.randomized_entrances = create_regions(create_region, self.random, self.options)
world_regions, world_entrances, self.randomized_entrances = create_regions(create_region, self.random, self.options, self.content)
self.logic = StardewLogic(self.player, self.options, self.content, world_regions.keys())
self.modified_bundles = get_all_bundles(self.random,
@ -184,7 +184,7 @@ class StardewValleyWorld(World):
self.multiworld.itempool += created_items
setup_early_items(self.multiworld, self.options, self.player, self.random)
setup_early_items(self.multiworld, self.options, self.content, self.player, self.random)
self.setup_player_events()
self.setup_victory()

View File

@ -3,8 +3,8 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from ..content import StardewContent
from ..options import StardewValleyOptions, ExcludeGingerIsland, FestivalLocations, SkillProgression
from ..content import StardewContent, content_packs
from ..options import StardewValleyOptions, FestivalLocations
from ..strings.crop_names import Fruit
from ..strings.currency_names import Currency
from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
@ -12,34 +12,35 @@ from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
class BundleItemSource(ABC):
@abstractmethod
def can_appear(self, options: StardewValleyOptions) -> bool:
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
...
class VanillaItemSource(BundleItemSource):
def can_appear(self, options: StardewValleyOptions) -> bool:
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
return True
class IslandItemSource(BundleItemSource):
def can_appear(self, options: StardewValleyOptions) -> bool:
return options.exclude_ginger_island == ExcludeGingerIsland.option_false
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
return content_packs.ginger_island_content_pack.name in content.registered_packs
class FestivalItemSource(BundleItemSource):
def can_appear(self, options: StardewValleyOptions) -> bool:
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
return options.festival_locations != FestivalLocations.option_disabled
# FIXME remove this once recipes are in content packs
class MasteryItemSource(BundleItemSource):
def can_appear(self, options: StardewValleyOptions) -> bool:
return options.skill_progression == SkillProgression.option_progressive_with_masteries
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
return content.features.skill_progression.are_masteries_shuffled
class ContentItemSource(BundleItemSource):
"""This is meant to be used for items that are managed by the content packs."""
def can_appear(self, options: StardewValleyOptions) -> bool:
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
raise ValueError("This should not be called, check if the item is in the content instead.")
@ -97,5 +98,4 @@ class BundleItem:
if isinstance(self.source, ContentItemSource):
return self.get_item() in content.game_items
return self.source.can_appear(options)
return self.source.can_appear(content, options)

View File

@ -1,5 +1,5 @@
from . import content_packs
from .feature import cropsanity, friendsanity, fishsanity, booksanity
from .feature import cropsanity, friendsanity, fishsanity, booksanity, skill_progression
from .game_content import ContentPack, StardewContent, StardewFeatures
from .unpacking import unpack_content
from .. import options
@ -31,7 +31,8 @@ def choose_features(player_options: options.StardewValleyOptions) -> StardewFeat
choose_booksanity(player_options.booksanity),
choose_cropsanity(player_options.cropsanity),
choose_fishsanity(player_options.fishsanity),
choose_friendsanity(player_options.friendsanity, player_options.friendsanity_heart_size)
choose_friendsanity(player_options.friendsanity, player_options.friendsanity_heart_size),
choose_skill_progression(player_options.skill_progression),
)
@ -105,3 +106,19 @@ def choose_friendsanity(friendsanity_option: options.Friendsanity, heart_size: o
return friendsanity.FriendsanityAllWithMarriage(heart_size.value)
raise ValueError(f"No friendsanity feature mapped to {str(friendsanity_option.value)}")
skill_progression_by_option = {
options.SkillProgression.option_vanilla: skill_progression.SkillProgressionVanilla(),
options.SkillProgression.option_progressive: skill_progression.SkillProgressionProgressive(),
options.SkillProgression.option_progressive_with_masteries: skill_progression.SkillProgressionProgressiveWithMasteries(),
}
def choose_skill_progression(skill_progression_option: options.SkillProgression) -> skill_progression.SkillProgressionFeature:
skill_progression_feature = skill_progression_by_option.get(skill_progression_option)
if skill_progression_feature is None:
raise ValueError(f"No skill progression feature mapped to {str(skill_progression_option.value)}")
return skill_progression_feature

View File

@ -2,3 +2,4 @@ from . import booksanity
from . import cropsanity
from . import fishsanity
from . import friendsanity
from . import skill_progression

View File

@ -0,0 +1,46 @@
from abc import ABC, abstractmethod
from typing import ClassVar, Iterable, Tuple
from ...data.skill import Skill
class SkillProgressionFeature(ABC):
is_progressive: ClassVar[bool]
are_masteries_shuffled: ClassVar[bool]
@abstractmethod
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[Tuple[int, str]]:
...
@abstractmethod
def is_mastery_randomized(self, skill: Skill) -> bool:
...
class SkillProgressionVanilla(SkillProgressionFeature):
is_progressive = False
are_masteries_shuffled = False
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[Tuple[int, str]]:
return ()
def is_mastery_randomized(self, skill: Skill) -> bool:
return False
class SkillProgressionProgressive(SkillProgressionFeature):
is_progressive = True
are_masteries_shuffled = False
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[Tuple[int, str]]:
return skill.level_names_by_level
def is_mastery_randomized(self, skill: Skill) -> bool:
return False
class SkillProgressionProgressiveWithMasteries(SkillProgressionProgressive):
are_masteries_shuffled = True
def is_mastery_randomized(self, skill: Skill) -> bool:
return skill.has_mastery

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, Iterable, Set, Any, Mapping, Type, Tuple, Union
from .feature import booksanity, cropsanity, fishsanity, friendsanity
from .feature import booksanity, cropsanity, fishsanity, friendsanity, skill_progression
from ..data.fish_data import FishItem
from ..data.game_item import GameItem, ItemSource, ItemTag
from ..data.skill import Skill
@ -53,6 +53,7 @@ class StardewFeatures:
cropsanity: cropsanity.CropsanityFeature
fishsanity: fishsanity.FishsanityFeature
friendsanity: friendsanity.FriendsanityFeature
skill_progression: skill_progression.SkillProgressionFeature
@dataclass(frozen=True)

View File

@ -1,7 +1,21 @@
from dataclasses import dataclass, field
from functools import cached_property
from typing import Iterable, Tuple
@dataclass(frozen=True)
class Skill:
name: str
has_mastery: bool = field(kw_only=True)
@cached_property
def mastery_name(self) -> str:
return f"{self.name} Mastery"
@cached_property
def level_name(self) -> str:
return f"{self.name} Level"
@cached_property
def level_names_by_level(self) -> Iterable[Tuple[int, str]]:
return tuple((level, f"Level {level} {self.name}") for level in range(1, 11))

View File

@ -1,11 +1,13 @@
from random import Random
from . import options as stardew_options
from .content import StardewContent
from .strings.ap_names.ap_weapon_names import APWeapon
from .strings.ap_names.transport_names import Transportation
from .strings.building_names import Building
from .strings.region_names import Region
from .strings.season_names import Season
from .strings.skill_names import Skill
from .strings.tv_channel_names import Channel
from .strings.wallet_item_names import Wallet
@ -14,7 +16,7 @@ always_early_candidates = [Region.greenhouse, Transportation.desert_obelisk, Wal
seasons = [Season.spring, Season.summer, Season.fall, Season.winter]
def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions, player: int, random: Random):
def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions, content: StardewContent, player: int, random: Random):
early_forced = []
early_candidates = []
early_candidates.extend(always_early_candidates)
@ -31,12 +33,13 @@ def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions,
early_forced.append("Progressive Backpack")
if options.tool_progression & stardew_options.ToolProgression.option_progressive:
if options.fishsanity != stardew_options.Fishsanity.option_none:
if content.features.fishsanity.is_enabled:
early_candidates.append("Progressive Fishing Rod")
early_forced.append("Progressive Pickaxe")
if options.skill_progression == stardew_options.SkillProgression.option_progressive:
early_forced.append("Fishing Level")
fishing = content.skills.get(Skill.fishing)
if fishing is not None and content.features.skill_progression.is_progressive:
early_forced.append(fishing.level_name)
if options.quest_locations >= 0:
early_candidates.append(Wallet.magnifying_glass)

View File

@ -15,7 +15,7 @@ from .data.game_item import ItemTag
from .logic.logic_event import all_events
from .mods.mod_data import ModNames
from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Museumsanity, \
BuildingProgression, SkillProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \
BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \
Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity, Walnutsanity, EnabledFillerBuffs
from .strings.ap_names.ap_option_names import OptionName
from .strings.ap_names.ap_weapon_names import APWeapon
@ -226,8 +226,8 @@ def create_unique_items(item_factory: StardewItemFactory, options: StardewValley
create_weapons(item_factory, options, items)
items.append(item_factory("Skull Key"))
create_elevators(item_factory, options, items)
create_tools(item_factory, options, items)
create_skills(item_factory, options, items)
create_tools(item_factory, options, content, items)
create_skills(item_factory, content, items)
create_wizard_buildings(item_factory, options, items)
create_carpenter_buildings(item_factory, options, items)
items.append(item_factory("Railroad Boulder Removed"))
@ -316,7 +316,7 @@ def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOpt
items.extend([item_factory(item) for item in ["Progressive Skull Cavern Elevator"] * 8])
def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
if options.tool_progression & ToolProgression.option_progressive:
for item_data in items_by_group[Group.PROGRESSIVE_TOOLS]:
name = item_data.name
@ -325,28 +325,29 @@ def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions
items.append(item_factory(item_data, ItemClassification.useful))
else:
items.extend([item_factory(item) for item in [item_data] * 4])
if options.skill_progression == SkillProgression.option_progressive_with_masteries:
if content.features.skill_progression.are_masteries_shuffled:
# Masteries add another tier to the scythe and the fishing rod
items.append(item_factory("Progressive Scythe"))
items.append(item_factory("Progressive Fishing Rod"))
# The golden scythe is always randomized
items.append(item_factory("Progressive Scythe"))
def create_skills(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if options.skill_progression == SkillProgression.option_vanilla:
def create_skills(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
skill_progression = content.features.skill_progression
if not skill_progression.is_progressive:
return
for item in items_by_group[Group.SKILL_LEVEL_UP]:
if item.mod_name not in options.mods and item.mod_name is not None:
continue
items.extend(item_factory(item) for item in [item.name] * 10)
for skill in content.skills.values():
items.extend(item_factory(skill.level_name) for _ in skill_progression.get_randomized_level_names_by_level(skill))
if options.skill_progression != SkillProgression.option_progressive_with_masteries:
return
if skill_progression.is_mastery_randomized(skill):
items.append(item_factory(skill.mastery_name))
for item in items_by_group[Group.SKILL_MASTERY]:
if item.mod_name not in options.mods and item.mod_name is not None:
continue
items.append(item_factory(item))
if skill_progression.are_masteries_shuffled:
items.append(item_factory(Wallet.mastery_of_the_five_ways))
def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):

View File

@ -11,7 +11,7 @@ from .data.game_item import ItemTag
from .data.museum_data import all_museum_items
from .mods.mod_data import ModNames
from .options import ExcludeGingerIsland, ArcadeMachineLocations, SpecialOrderLocations, Museumsanity, \
FestivalLocations, SkillProgression, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression, FarmType
FestivalLocations, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression, FarmType
from .options import StardewValleyOptions, Craftsanity, Chefsanity, Cooksanity, Shipsanity, Monstersanity
from .strings.goal_names import Goal
from .strings.quest_names import ModQuest, Quest
@ -188,12 +188,12 @@ def extend_cropsanity_locations(randomized_locations: List[LocationData], conten
for item in content.find_tagged_items(ItemTag.CROPSANITY))
def extend_quests_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_quests_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.quest_locations < 0:
return
story_quest_locations = locations_by_tag[LocationTags.STORY_QUEST]
story_quest_locations = filter_disabled_locations(options, story_quest_locations)
story_quest_locations = filter_disabled_locations(options, content, story_quest_locations)
randomized_locations.extend(story_quest_locations)
for i in range(0, options.quest_locations.value):
@ -284,9 +284,9 @@ def extend_desert_festival_chef_locations(randomized_locations: List[LocationDat
randomized_locations.extend(locations_to_add)
def extend_special_order_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_special_order_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.special_order_locations & SpecialOrderLocations.option_board:
board_locations = filter_disabled_locations(options, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD])
board_locations = filter_disabled_locations(options, content, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD])
randomized_locations.extend(board_locations)
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
@ -308,9 +308,9 @@ def extend_walnut_purchase_locations(randomized_locations: List[LocationData], o
randomized_locations.extend(locations_by_tag[LocationTags.WALNUT_PURCHASE])
def extend_mandatory_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_mandatory_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
mandatory_locations = [location for location in locations_by_tag[LocationTags.MANDATORY]]
filtered_mandatory_locations = filter_disabled_locations(options, mandatory_locations)
filtered_mandatory_locations = filter_disabled_locations(options, content, mandatory_locations)
randomized_locations.extend(filtered_mandatory_locations)
@ -349,32 +349,32 @@ def extend_elevator_locations(randomized_locations: List[LocationData], options:
randomized_locations.extend(filtered_elevator_locations)
def extend_monstersanity_locations(randomized_locations: List[LocationData], options):
def extend_monstersanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
monstersanity = options.monstersanity
if monstersanity == Monstersanity.option_none:
return
if monstersanity == Monstersanity.option_one_per_monster or monstersanity == Monstersanity.option_split_goals:
monster_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_MONSTER]]
filtered_monster_locations = filter_disabled_locations(options, monster_locations)
filtered_monster_locations = filter_disabled_locations(options, content, monster_locations)
randomized_locations.extend(filtered_monster_locations)
return
goal_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_GOALS]]
filtered_goal_locations = filter_disabled_locations(options, goal_locations)
filtered_goal_locations = filter_disabled_locations(options, content, goal_locations)
randomized_locations.extend(filtered_goal_locations)
if monstersanity != Monstersanity.option_progressive_goals:
return
progressive_goal_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_PROGRESSIVE_GOALS]]
filtered_progressive_goal_locations = filter_disabled_locations(options, progressive_goal_locations)
filtered_progressive_goal_locations = filter_disabled_locations(options, content, progressive_goal_locations)
randomized_locations.extend(filtered_progressive_goal_locations)
def extend_shipsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_shipsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
shipsanity = options.shipsanity
if shipsanity == Shipsanity.option_none:
return
if shipsanity == Shipsanity.option_everything:
ship_locations = [location for location in locations_by_tag[LocationTags.SHIPSANITY]]
filtered_ship_locations = filter_disabled_locations(options, ship_locations)
filtered_ship_locations = filter_disabled_locations(options, content, ship_locations)
randomized_locations.extend(filtered_ship_locations)
return
shipsanity_locations = set()
@ -385,11 +385,11 @@ def extend_shipsanity_locations(randomized_locations: List[LocationData], option
if shipsanity == Shipsanity.option_full_shipment or shipsanity == Shipsanity.option_full_shipment_with_fish:
shipsanity_locations = shipsanity_locations.union({location for location in locations_by_tag[LocationTags.SHIPSANITY_FULL_SHIPMENT]})
filtered_shipsanity_locations = filter_disabled_locations(options, list(shipsanity_locations))
filtered_shipsanity_locations = filter_disabled_locations(options, content, list(shipsanity_locations))
randomized_locations.extend(filtered_shipsanity_locations)
def extend_cooksanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_cooksanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
cooksanity = options.cooksanity
if cooksanity == Cooksanity.option_none:
return
@ -398,11 +398,11 @@ def extend_cooksanity_locations(randomized_locations: List[LocationData], option
else:
cooksanity_locations = (location for location in locations_by_tag[LocationTags.COOKSANITY])
filtered_cooksanity_locations = filter_disabled_locations(options, cooksanity_locations)
filtered_cooksanity_locations = filter_disabled_locations(options, content, cooksanity_locations)
randomized_locations.extend(filtered_cooksanity_locations)
def extend_chefsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_chefsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
chefsanity = options.chefsanity
if chefsanity == Chefsanity.option_none:
return
@ -418,16 +418,16 @@ def extend_chefsanity_locations(randomized_locations: List[LocationData], option
if chefsanity & Chefsanity.option_skills:
chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_SKILL]})
filtered_chefsanity_locations = filter_disabled_locations(options, list(chefsanity_locations_by_name.values()))
filtered_chefsanity_locations = filter_disabled_locations(options, content, list(chefsanity_locations_by_name.values()))
randomized_locations.extend(filtered_chefsanity_locations)
def extend_craftsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_craftsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.craftsanity == Craftsanity.option_none:
return
craftsanity_locations = [craft for craft in locations_by_tag[LocationTags.CRAFTSANITY]]
filtered_craftsanity_locations = filter_disabled_locations(options, craftsanity_locations)
filtered_craftsanity_locations = filter_disabled_locations(options, content, craftsanity_locations)
randomized_locations.extend(filtered_craftsanity_locations)
@ -467,7 +467,7 @@ def create_locations(location_collector: StardewLocationCollector,
random: Random):
randomized_locations = []
extend_mandatory_locations(randomized_locations, options)
extend_mandatory_locations(randomized_locations, options, content)
extend_bundle_locations(randomized_locations, bundle_rooms)
extend_backpack_locations(randomized_locations, options)
@ -476,13 +476,12 @@ def create_locations(location_collector: StardewLocationCollector,
extend_elevator_locations(randomized_locations, options)
if not options.skill_progression == SkillProgression.option_vanilla:
for location in locations_by_tag[LocationTags.SKILL_LEVEL]:
if location.mod_name is not None and location.mod_name not in options.mods:
continue
if LocationTags.MASTERY_LEVEL in location.tags and options.skill_progression != SkillProgression.option_progressive_with_masteries:
continue
randomized_locations.append(location_table[location.name])
skill_progression = content.features.skill_progression
if skill_progression.is_progressive:
for skill in content.skills.values():
randomized_locations.extend([location_table[location_name] for _, location_name in skill_progression.get_randomized_level_names_by_level(skill)])
if skill_progression.is_mastery_randomized(skill):
randomized_locations.append(location_table[skill.mastery_name])
if options.building_progression & BuildingProgression.option_progressive:
for location in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
@ -501,15 +500,15 @@ def create_locations(location_collector: StardewLocationCollector,
extend_friendsanity_locations(randomized_locations, content)
extend_festival_locations(randomized_locations, options, random)
extend_special_order_locations(randomized_locations, options)
extend_special_order_locations(randomized_locations, options, content)
extend_walnut_purchase_locations(randomized_locations, options)
extend_monstersanity_locations(randomized_locations, options)
extend_shipsanity_locations(randomized_locations, options)
extend_cooksanity_locations(randomized_locations, options)
extend_chefsanity_locations(randomized_locations, options)
extend_craftsanity_locations(randomized_locations, options)
extend_quests_locations(randomized_locations, options)
extend_monstersanity_locations(randomized_locations, options, content)
extend_shipsanity_locations(randomized_locations, options, content)
extend_cooksanity_locations(randomized_locations, options, content)
extend_chefsanity_locations(randomized_locations, options, content)
extend_craftsanity_locations(randomized_locations, options, content)
extend_quests_locations(randomized_locations, options, content)
extend_book_locations(randomized_locations, content)
extend_walnutsanity_locations(randomized_locations, options)
@ -538,19 +537,21 @@ def filter_qi_order_locations(options: StardewValleyOptions, locations: Iterable
return (location for location in locations if include_qi_orders or LocationTags.REQUIRES_QI_ORDERS not in location.tags)
def filter_masteries_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
include_masteries = options.skill_progression == SkillProgression.option_progressive_with_masteries
return (location for location in locations if include_masteries or LocationTags.REQUIRES_MASTERIES not in location.tags)
def filter_masteries_locations(content: StardewContent, locations: Iterable[LocationData]) -> Iterable[LocationData]:
# FIXME Remove once recipes are handled by the content packs
if content.features.skill_progression.are_masteries_shuffled:
return locations
return (location for location in locations if LocationTags.REQUIRES_MASTERIES not in location.tags)
def filter_modded_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
return (location for location in locations if location.mod_name is None or location.mod_name in options.mods)
def filter_disabled_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
def filter_disabled_locations(options: StardewValleyOptions, content: StardewContent, locations: Iterable[LocationData]) -> Iterable[LocationData]:
locations_farm_filter = filter_farm_type(options, locations)
locations_island_filter = filter_ginger_island(options, locations_farm_filter)
locations_qi_filter = filter_qi_order_locations(options, locations_island_filter)
locations_masteries_filter = filter_masteries_locations(options, locations_qi_filter)
locations_masteries_filter = filter_masteries_locations(content, locations_qi_filter)
locations_mod_filter = filter_modded_locations(options, locations_masteries_filter)
return locations_mod_filter

View File

@ -16,7 +16,7 @@ from ..data.craftable_data import CraftingRecipe, all_crafting_recipes_by_name
from ..data.recipe_source import CutsceneSource, ShopTradeSource, ArchipelagoSource, LogicSource, SpecialOrderSource, \
FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource, SkillCraftsanitySource
from ..locations import locations_by_tag, LocationTags
from ..options import Craftsanity, SpecialOrderLocations, ExcludeGingerIsland, SkillProgression
from ..options import Craftsanity, SpecialOrderLocations, ExcludeGingerIsland
from ..stardew_rule import StardewRule, True_, False_
from ..strings.region_names import Region
@ -101,12 +101,13 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
craftsanity_prefix = "Craft "
all_recipes_names = []
exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
exclude_masteries = self.options.skill_progression != SkillProgression.option_progressive_with_masteries
exclude_masteries = not self.content.features.skill_progression.are_masteries_shuffled
for location in locations_by_tag[LocationTags.CRAFTSANITY]:
if not location.name.startswith(craftsanity_prefix):
continue
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
continue
# FIXME Remove when recipes are in content packs
if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags:
continue
if location.mod_name and location.mod_name not in self.options.mods:

View File

@ -7,7 +7,6 @@ from .has_logic import HasLogicMixin
from .received_logic import ReceivedLogicMixin
from .region_logic import RegionLogicMixin
from .time_logic import TimeLogicMixin
from ..options import Booksanity
from ..stardew_rule import StardewRule, HasProgressionPercent
from ..strings.book_names import Book
from ..strings.craftable_names import Consumable
@ -39,7 +38,7 @@ class GrindLogic(BaseLogic[Union[GrindLogicMixin, HasLogicMixin, ReceivedLogicMi
opening_rule = self.logic.region.can_reach(Region.blacksmith)
mystery_box_rule = self.logic.has(Consumable.mystery_box)
book_of_mysteries_rule = self.logic.true_ \
if self.options.booksanity == Booksanity.option_none \
if not self.content.features.booksanity.is_enabled \
else self.logic.book.has_book_power(Book.book_of_mysteries)
# Assuming one box per day, but halved because we don't know how many months have passed before Mr. Qi's Plane Ride.
time_rule = self.logic.time.has_lived_months(quantity // 14)

View File

@ -58,14 +58,19 @@ SkillLogicMixin, CookingLogicMixin]]):
rules = []
weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier)
rules.append(weapon_rule)
if self.options.tool_progression & ToolProgression.option_progressive:
rules.append(self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier]))
if self.options.skill_progression >= options.SkillProgression.option_progressive:
skill_tier = min(10, max(0, tier * 2))
rules.append(self.logic.skill.has_level(Skill.combat, skill_tier))
rules.append(self.logic.skill.has_level(Skill.mining, skill_tier))
# No alternative for vanilla because we assume that you will grind the levels in the mines.
if self.content.features.skill_progression.is_progressive:
skill_level = min(10, max(0, tier * 2))
rules.append(self.logic.skill.has_level(Skill.combat, skill_level))
rules.append(self.logic.skill.has_level(Skill.mining, skill_level))
if tier >= 4:
rules.append(self.logic.cooking.can_cook())
return self.logic.and_(*rules)
@cache_self1
@ -82,10 +87,14 @@ SkillLogicMixin, CookingLogicMixin]]):
rules = []
weapon_rule = self.logic.combat.has_great_weapon
rules.append(weapon_rule)
if self.options.tool_progression & ToolProgression.option_progressive:
rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2))))
if self.options.skill_progression >= options.SkillProgression.option_progressive:
skill_tier = min(10, max(0, tier * 2 + 6))
rules.extend({self.logic.skill.has_level(Skill.combat, skill_tier),
self.logic.skill.has_level(Skill.mining, skill_tier)})
# No alternative for vanilla because we assume that you will grind the levels in the mines.
if self.content.features.skill_progression.is_progressive:
skill_level = min(10, max(0, tier * 2 + 6))
rules.extend((self.logic.skill.has_level(Skill.combat, skill_level),
self.logic.skill.has_level(Skill.mining, skill_level)))
return self.logic.and_(*rules)

View File

@ -11,7 +11,6 @@ from .region_logic import RegionLogicMixin
from .season_logic import SeasonLogicMixin
from .time_logic import TimeLogicMixin
from .tool_logic import ToolLogicMixin
from .. import options
from ..data.harvest import HarvestCropSource
from ..mods.logic.magic_logic import MagicLogicMixin
from ..mods.logic.mod_skills_levels import get_mod_skill_levels
@ -77,21 +76,21 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
if level == 0:
return true_
if self.options.skill_progression == options.SkillProgression.option_vanilla:
return self.logic.skill.can_earn_level(skill, level)
if self.content.features.skill_progression.is_progressive:
return self.logic.received(f"{skill} Level", level)
return self.logic.received(f"{skill} Level", level)
return self.logic.skill.can_earn_level(skill, level)
def has_previous_level(self, skill: str, level: int) -> StardewRule:
assert level > 0, f"There is no level before level 0."
if level == 1:
return true_
if self.options.skill_progression == options.SkillProgression.option_vanilla:
months = max(1, level - 1)
return self.logic.time.has_lived_months(months)
if self.content.features.skill_progression.is_progressive:
return self.logic.received(f"{skill} Level", level - 1)
return self.logic.received(f"{skill} Level", level - 1)
months = max(1, level - 1)
return self.logic.time.has_lived_months(months)
@cache_self1
def has_farming_level(self, level: int) -> StardewRule:
@ -102,7 +101,7 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
if level <= 0:
return True_()
if self.options.skill_progression >= options.SkillProgression.option_progressive:
if self.content.features.skill_progression.is_progressive:
skills_items = vanilla_skill_items
if allow_modded_skills:
skills_items += get_mod_skill_levels(self.options.mods)
@ -148,7 +147,7 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
@cached_property
def can_get_fishing_xp(self) -> StardewRule:
if self.options.skill_progression >= options.SkillProgression.option_progressive:
if self.content.features.skill_progression.is_progressive:
return self.logic.skill.can_fish() | self.logic.skill.can_crab_pot
return self.logic.skill.can_fish()
@ -178,7 +177,9 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
@cached_property
def can_crab_pot(self) -> StardewRule:
crab_pot_rule = self.logic.has(Fishing.bait)
if self.options.skill_progression >= options.SkillProgression.option_progressive:
# We can't use the same rule if skills are vanilla, because fishing levels are required to crab pot, which is required to get fishing levels...
if self.content.features.skill_progression.is_progressive:
crab_pot_rule = crab_pot_rule & self.logic.has(Machine.crab_pot)
else:
crab_pot_rule = crab_pot_rule & self.logic.skill.can_get_fishing_xp
@ -200,14 +201,14 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
return self.logic.skill.can_earn_level(skill, 11) & self.logic.region.can_reach(Region.mastery_cave)
def has_mastery(self, skill: str) -> StardewRule:
if self.options.skill_progression == options.SkillProgression.option_progressive_with_masteries:
if self.content.features.skill_progression.are_masteries_shuffled:
return self.logic.received(f"{skill} Mastery")
return self.logic.skill.can_earn_mastery(skill)
@cached_property
def can_enter_mastery_cave(self) -> StardewRule:
if self.options.skill_progression == options.SkillProgression.option_progressive_with_masteries:
if self.content.features.skill_progression.are_masteries_shuffled:
return self.logic.received(Wallet.mastery_of_the_five_ways)
return self.has_any_skills_maxed(included_modded_skills=False)

View File

@ -1,6 +1,5 @@
from typing import Union
from ... import options
from ...logic.base_logic import BaseLogicMixin, BaseLogic
from ...logic.combat_logic import CombatLogicMixin
from ...logic.cooking_logic import CookingLogicMixin
@ -45,9 +44,9 @@ CookingLogicMixin]]):
self.logic.received(ModTransportation.woods_obelisk))
tier = int(depth / 25) + 1
if self.options.skill_progression >= options.SkillProgression.option_progressive:
combat_tier = min(10, max(0, tier + 5))
rules.append(self.logic.skill.has_level(Skill.combat, combat_tier))
if self.content.features.skill_progression.is_progressive:
combat_level = min(10, max(0, tier + 5))
rules.append(self.logic.skill.has_level(Skill.combat, combat_level))
return self.logic.and_(*rules)

View File

@ -13,7 +13,6 @@ from ...logic.region_logic import RegionLogicMixin
from ...logic.relationship_logic import RelationshipLogicMixin
from ...logic.tool_logic import ToolLogicMixin
from ...mods.mod_data import ModNames
from ...options import SkillProgression
from ...stardew_rule import StardewRule, False_, True_, And
from ...strings.building_names import Building
from ...strings.craftable_names import ModCraftable, ModMachine
@ -37,7 +36,7 @@ ToolLogicMixin, FishingLogicMixin, CookingLogicMixin, CraftingLogicMixin, MagicL
if level <= 0:
return True_()
if self.options.skill_progression == SkillProgression.option_progressive:
if self.content.features.skill_progression.is_progressive:
return self.logic.received(f"{skill} Level", level)
return self.can_earn_mod_skill_level(skill, level)
@ -85,13 +84,15 @@ ToolLogicMixin, FishingLogicMixin, CookingLogicMixin, CraftingLogicMixin, MagicL
def can_earn_archaeology_skill_level(self, level: int) -> StardewRule:
shifter_rule = True_()
preservation_rule = True_()
if self.options.skill_progression == self.options.skill_progression.option_progressive:
if self.content.features.skill_progression.is_progressive:
shifter_rule = self.logic.has(ModCraftable.water_shifter)
preservation_rule = self.logic.has(ModMachine.hardwood_preservation_chamber)
if level >= 8:
return (self.logic.tool.has_tool(Tool.pan, ToolMaterial.iridium) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.gold)) & shifter_rule & preservation_rule
tool_rule = self.logic.tool.has_tool(Tool.pan, ToolMaterial.iridium) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.gold)
return tool_rule & shifter_rule & preservation_rule
if level >= 5:
return (self.logic.tool.has_tool(Tool.pan, ToolMaterial.gold) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.iron)) & shifter_rule
tool_rule = self.logic.tool.has_tool(Tool.pan, ToolMaterial.gold) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.iron)
return tool_rule & shifter_rule
if level >= 3:
return self.logic.tool.has_tool(Tool.pan, ToolMaterial.iron) | self.logic.tool.has_tool(Tool.hoe, ToolMaterial.copper)
return self.logic.tool.has_tool(Tool.pan, ToolMaterial.copper) | self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic)

View File

@ -2,8 +2,9 @@ from random import Random
from typing import Iterable, Dict, Protocol, List, Tuple, Set
from BaseClasses import Region, Entrance
from .content import content_packs, StardewContent
from .mods.mod_regions import ModDataList, vanilla_connections_to_remove_by_mod
from .options import EntranceRandomization, ExcludeGingerIsland, StardewValleyOptions, SkillProgression
from .options import EntranceRandomization, ExcludeGingerIsland, StardewValleyOptions
from .region_classes import RegionData, ConnectionData, RandomizationFlag, ModificationFlag
from .strings.entrance_names import Entrance, LogicEntrance
from .strings.region_names import Region, LogicRegion
@ -587,7 +588,7 @@ def modify_vanilla_regions(existing_region: RegionData, modified_region: RegionD
return updated_region
def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewValleyOptions) \
def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewValleyOptions, content: StardewContent) \
-> Tuple[Dict[str, Region], Dict[str, Entrance], Dict[str, str]]:
entrances_data, regions_data = create_final_connections_and_regions(world_options)
regions_by_name: Dict[str: Region] = {region_name: region_factory(region_name, regions_data[region_name].exits) for region_name in regions_data}
@ -598,7 +599,7 @@ def create_regions(region_factory: RegionFactory, random: Random, world_options:
if entrance.name in entrances_data
}
connections, randomized_data = randomize_connections(random, world_options, regions_data, entrances_data)
connections, randomized_data = randomize_connections(random, world_options, content, regions_data, entrances_data)
for connection in connections:
if connection.name in entrances_by_name:
@ -606,7 +607,7 @@ def create_regions(region_factory: RegionFactory, random: Random, world_options:
return regions_by_name, entrances_by_name, randomized_data
def randomize_connections(random: Random, world_options: StardewValleyOptions, regions_by_name: Dict[str, RegionData],
def randomize_connections(random: Random, world_options: StardewValleyOptions, content: StardewContent, regions_by_name: Dict[str, RegionData],
connections_by_name: Dict[str, ConnectionData]) -> Tuple[List[ConnectionData], Dict[str, str]]:
connections_to_randomize: List[ConnectionData] = []
if world_options.entrance_randomization == EntranceRandomization.option_pelican_town:
@ -621,7 +622,7 @@ def randomize_connections(random: Random, world_options: StardewValleyOptions, r
elif world_options.entrance_randomization == EntranceRandomization.option_chaos:
connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
RandomizationFlag.BUILDINGS in connections_by_name[connection].flag]
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, world_options)
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, content)
# On Chaos, we just add the connections to randomize, unshuffled, and the client does it every day
randomized_data_for_mod = {}
@ -630,7 +631,7 @@ def randomize_connections(random: Random, world_options: StardewValleyOptions, r
randomized_data_for_mod[connection.reverse] = connection.reverse
return list(connections_by_name.values()), randomized_data_for_mod
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, world_options)
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, content)
random.shuffle(connections_to_randomize)
destination_pool = list(connections_to_randomize)
random.shuffle(destination_pool)
@ -645,12 +646,11 @@ def randomize_connections(random: Random, world_options: StardewValleyOptions, r
return randomized_connections_for_generation, randomized_data_for_mod
def remove_excluded_entrances(connections_to_randomize: List[ConnectionData], world_options: StardewValleyOptions) -> List[ConnectionData]:
exclude_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_true
if exclude_island:
def remove_excluded_entrances(connections_to_randomize: List[ConnectionData], content: StardewContent) -> List[ConnectionData]:
# FIXME remove when regions are handled in content packs
if content_packs.ginger_island_content_pack.name not in content.registered_packs:
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.GINGER_ISLAND not in connection.flag]
exclude_masteries = world_options.skill_progression != SkillProgression.option_progressive_with_masteries
if exclude_masteries:
if not content.features.skill_progression.are_masteries_shuffled:
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.MASTERIES not in connection.flag]
return connections_to_randomize

View File

@ -21,7 +21,7 @@ from .logic.tool_logic import tool_upgrade_prices
from .mods.mod_data import ModNames
from .options import StardewValleyOptions, Walnutsanity
from .options import ToolProgression, BuildingProgression, ExcludeGingerIsland, SpecialOrderLocations, Museumsanity, BackpackProgression, Shipsanity, \
Monstersanity, Chefsanity, Craftsanity, ArcadeMachineLocations, Cooksanity, SkillProgression
Monstersanity, Chefsanity, Craftsanity, ArcadeMachineLocations, Cooksanity
from .stardew_rule import And, StardewRule, true_
from .stardew_rule.indirect_connection import look_for_indirect_connection
from .stardew_rule.rule_explain import explain
@ -47,7 +47,7 @@ from .strings.performance_names import Performance
from .strings.quest_names import Quest
from .strings.region_names import Region
from .strings.season_names import Season
from .strings.skill_names import ModSkill, Skill
from .strings.skill_names import Skill
from .strings.tool_names import Tool, ToolMaterial
from .strings.tv_channel_names import Channel
from .strings.villager_names import NPC, ModNPC
@ -70,7 +70,7 @@ def set_rules(world):
set_ginger_island_rules(logic, multiworld, player, world_options)
set_tool_rules(logic, multiworld, player, world_options)
set_skills_rules(logic, multiworld, player, world_options)
set_skills_rules(logic, multiworld, player, world_content)
set_bundle_rules(bundle_rooms, logic, multiworld, player, world_options)
set_building_rules(logic, multiworld, player, world_options)
set_cropsanity_rules(logic, multiworld, player, world_content)
@ -164,58 +164,21 @@ def set_bundle_rules(bundle_rooms: List[BundleRoom], logic: StardewLogic, multiw
MultiWorldRules.add_rule(multiworld.get_location(room_location, player), And(*room_rules))
def set_skills_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
mods = world_options.mods
if world_options.skill_progression == SkillProgression.option_vanilla:
def set_skills_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, content: StardewContent):
skill_progression = content.features.skill_progression
if not skill_progression.is_progressive:
return
for i in range(1, 11):
set_vanilla_skill_rule_for_level(logic, multiworld, player, i)
set_modded_skill_rule_for_level(logic, multiworld, player, mods, i)
for skill in content.skills.values():
for level, level_name in skill_progression.get_randomized_level_names_by_level(skill):
rule = logic.skill.can_earn_level(skill.name, level)
location = multiworld.get_location(level_name, player)
MultiWorldRules.set_rule(location, rule)
if world_options.skill_progression == SkillProgression.option_progressive:
return
for skill in [Skill.farming, Skill.fishing, Skill.foraging, Skill.mining, Skill.combat]:
MultiWorldRules.set_rule(multiworld.get_location(f"{skill} Mastery", player), logic.skill.can_earn_mastery(skill))
def set_vanilla_skill_rule_for_level(logic: StardewLogic, multiworld, player, level: int):
set_vanilla_skill_rule(logic, multiworld, player, Skill.farming, level)
set_vanilla_skill_rule(logic, multiworld, player, Skill.fishing, level)
set_vanilla_skill_rule(logic, multiworld, player, Skill.foraging, level)
set_vanilla_skill_rule(logic, multiworld, player, Skill.mining, level)
set_vanilla_skill_rule(logic, multiworld, player, Skill.combat, level)
def set_modded_skill_rule_for_level(logic: StardewLogic, multiworld, player, mods, level: int):
if ModNames.luck_skill in mods:
set_modded_skill_rule(logic, multiworld, player, ModSkill.luck, level)
if ModNames.magic in mods:
set_modded_skill_rule(logic, multiworld, player, ModSkill.magic, level)
if ModNames.binning_skill in mods:
set_modded_skill_rule(logic, multiworld, player, ModSkill.binning, level)
if ModNames.cooking_skill in mods:
set_modded_skill_rule(logic, multiworld, player, ModSkill.cooking, level)
if ModNames.socializing_skill in mods:
set_modded_skill_rule(logic, multiworld, player, ModSkill.socializing, level)
if ModNames.archaeology in mods:
set_modded_skill_rule(logic, multiworld, player, ModSkill.archaeology, level)
def get_skill_level_location(multiworld, player, skill: str, level: int):
location_name = f"Level {level} {skill}"
return multiworld.get_location(location_name, player)
def set_vanilla_skill_rule(logic: StardewLogic, multiworld, player, skill: str, level: int):
rule = logic.skill.can_earn_level(skill, level)
MultiWorldRules.set_rule(get_skill_level_location(multiworld, player, skill, level), rule)
def set_modded_skill_rule(logic: StardewLogic, multiworld, player, skill: str, level: int):
rule = logic.skill.can_earn_level(skill, level)
MultiWorldRules.set_rule(get_skill_level_location(multiworld, player, skill, level), rule)
if skill_progression.is_mastery_randomized(skill):
rule = logic.skill.can_earn_mastery(skill.name)
location = multiworld.get_location(skill.mastery_name, player)
MultiWorldRules.set_rule(location, rule)
def set_entrance_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):

View File

@ -4,6 +4,7 @@ from typing import Set
from BaseClasses import get_seed
from . import SVTestCase, complete_options_with_default
from .. import create_content
from ..options import EntranceRandomization, ExcludeGingerIsland, SkillProgression
from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag, create_final_connections_and_regions
from ..strings.entrance_names import Entrance as EntranceName
@ -63,11 +64,12 @@ class TestEntranceRando(SVTestCase):
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
})
content = create_content(sv_options)
seed = get_seed()
rand = random.Random(seed)
with self.subTest(flag=flag, msg=f"Seed: {seed}"):
entrances, regions = create_final_connections_and_regions(sv_options)
_, randomized_connections = randomize_connections(rand, sv_options, regions, entrances)
_, randomized_connections = randomize_connections(rand, sv_options, content, regions, entrances)
for connection in vanilla_connections:
if flag in connection.flag:
@ -90,11 +92,12 @@ class TestEntranceRando(SVTestCase):
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
})
content = create_content(sv_options)
seed = get_seed()
rand = random.Random(seed)
with self.subTest(option=option, flag=flag, seed=seed):
entrances, regions = create_final_connections_and_regions(sv_options)
_, randomized_connections = randomize_connections(rand, sv_options, regions, entrances)
_, randomized_connections = randomize_connections(rand, sv_options, content, regions, entrances)
for connection in vanilla_connections:
if flag in connection.flag:
@ -118,13 +121,14 @@ class TestEntranceRando(SVTestCase):
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
})
content = create_content(sv_options)
for i in range(0, 100 if self.skip_long_tests else 10000):
seed = get_seed()
rand = random.Random(seed)
with self.subTest(msg=f"Seed: {seed}"):
entrances, regions = create_final_connections_and_regions(sv_options)
randomized_connections, randomized_data = randomize_connections(rand, sv_options, regions, entrances)
randomized_connections, randomized_data = randomize_connections(rand, sv_options, content, regions, entrances)
connections_by_name = {connection.name: connection for connection in randomized_connections}
blocked_entrances = {EntranceName.use_island_obelisk, EntranceName.boat_to_ginger_island}

View File

@ -7,7 +7,8 @@ default_features = StardewFeatures(
feature.booksanity.BooksanityDisabled(),
feature.cropsanity.CropsanityDisabled(),
feature.fishsanity.FishsanityNone(),
feature.friendsanity.FriendsanityNone()
feature.friendsanity.FriendsanityNone(),
feature.skill_progression.SkillProgressionVanilla(),
)

View File

@ -3,7 +3,7 @@ import random
from BaseClasses import get_seed
from .. import SVTestBase, SVTestCase, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, complete_options_with_default, solo_multiworld
from ..assertion import ModAssertMixin, WorldAssertMixin
from ... import items, Group, ItemClassification
from ... import items, Group, ItemClassification, create_content
from ... import options
from ...items import items_by_group
from ...options import SkillProgression, Walnutsanity
@ -128,12 +128,13 @@ class TestModEntranceRando(SVTestCase):
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
options.Mods.internal_name: frozenset(options.Mods.valid_keys)
})
content = create_content(sv_options)
seed = get_seed()
rand = random.Random(seed)
with self.subTest(option=option, flag=flag, seed=seed):
final_connections, final_regions = create_final_connections_and_regions(sv_options)
_, randomized_connections = randomize_connections(rand, sv_options, final_regions, final_connections)
_, randomized_connections = randomize_connections(rand, sv_options, content, final_regions, final_connections)
for connection_name in final_connections:
connection = final_connections[connection_name]