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:
parent
f735416bda
commit
a67688749f
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2,3 +2,4 @@ from . import booksanity
|
|||
from . import cropsanity
|
||||
from . import fishsanity
|
||||
from . import friendsanity
|
||||
from . import skill_progression
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue