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]
|
region.exits = [Entrance(self.player, exit_name, region) for exit_name in exits]
|
||||||
return region
|
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.logic = StardewLogic(self.player, self.options, self.content, world_regions.keys())
|
||||||
self.modified_bundles = get_all_bundles(self.random,
|
self.modified_bundles = get_all_bundles(self.random,
|
||||||
|
@ -184,7 +184,7 @@ class StardewValleyWorld(World):
|
||||||
|
|
||||||
self.multiworld.itempool += created_items
|
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_player_events()
|
||||||
self.setup_victory()
|
self.setup_victory()
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ from __future__ import annotations
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from ..content import StardewContent
|
from ..content import StardewContent, content_packs
|
||||||
from ..options import StardewValleyOptions, ExcludeGingerIsland, FestivalLocations, SkillProgression
|
from ..options import StardewValleyOptions, FestivalLocations
|
||||||
from ..strings.crop_names import Fruit
|
from ..strings.crop_names import Fruit
|
||||||
from ..strings.currency_names import Currency
|
from ..strings.currency_names import Currency
|
||||||
from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
|
from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
|
||||||
|
@ -12,34 +12,35 @@ from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
|
||||||
|
|
||||||
class BundleItemSource(ABC):
|
class BundleItemSource(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class VanillaItemSource(BundleItemSource):
|
class VanillaItemSource(BundleItemSource):
|
||||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class IslandItemSource(BundleItemSource):
|
class IslandItemSource(BundleItemSource):
|
||||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
|
||||||
return options.exclude_ginger_island == ExcludeGingerIsland.option_false
|
return content_packs.ginger_island_content_pack.name in content.registered_packs
|
||||||
|
|
||||||
|
|
||||||
class FestivalItemSource(BundleItemSource):
|
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
|
return options.festival_locations != FestivalLocations.option_disabled
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME remove this once recipes are in content packs
|
||||||
class MasteryItemSource(BundleItemSource):
|
class MasteryItemSource(BundleItemSource):
|
||||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
|
||||||
return options.skill_progression == SkillProgression.option_progressive_with_masteries
|
return content.features.skill_progression.are_masteries_shuffled
|
||||||
|
|
||||||
|
|
||||||
class ContentItemSource(BundleItemSource):
|
class ContentItemSource(BundleItemSource):
|
||||||
"""This is meant to be used for items that are managed by the content packs."""
|
"""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.")
|
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):
|
if isinstance(self.source, ContentItemSource):
|
||||||
return self.get_item() in content.game_items
|
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 . 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 .game_content import ContentPack, StardewContent, StardewFeatures
|
||||||
from .unpacking import unpack_content
|
from .unpacking import unpack_content
|
||||||
from .. import options
|
from .. import options
|
||||||
|
@ -31,7 +31,8 @@ def choose_features(player_options: options.StardewValleyOptions) -> StardewFeat
|
||||||
choose_booksanity(player_options.booksanity),
|
choose_booksanity(player_options.booksanity),
|
||||||
choose_cropsanity(player_options.cropsanity),
|
choose_cropsanity(player_options.cropsanity),
|
||||||
choose_fishsanity(player_options.fishsanity),
|
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)
|
return friendsanity.FriendsanityAllWithMarriage(heart_size.value)
|
||||||
|
|
||||||
raise ValueError(f"No friendsanity feature mapped to {str(friendsanity_option.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 cropsanity
|
||||||
from . import fishsanity
|
from . import fishsanity
|
||||||
from . import friendsanity
|
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 dataclasses import dataclass, field
|
||||||
from typing import Dict, Iterable, Set, Any, Mapping, Type, Tuple, Union
|
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.fish_data import FishItem
|
||||||
from ..data.game_item import GameItem, ItemSource, ItemTag
|
from ..data.game_item import GameItem, ItemSource, ItemTag
|
||||||
from ..data.skill import Skill
|
from ..data.skill import Skill
|
||||||
|
@ -53,6 +53,7 @@ class StardewFeatures:
|
||||||
cropsanity: cropsanity.CropsanityFeature
|
cropsanity: cropsanity.CropsanityFeature
|
||||||
fishsanity: fishsanity.FishsanityFeature
|
fishsanity: fishsanity.FishsanityFeature
|
||||||
friendsanity: friendsanity.FriendsanityFeature
|
friendsanity: friendsanity.FriendsanityFeature
|
||||||
|
skill_progression: skill_progression.SkillProgressionFeature
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
|
@ -1,7 +1,21 @@
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from functools import cached_property
|
||||||
|
from typing import Iterable, Tuple
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Skill:
|
class Skill:
|
||||||
name: str
|
name: str
|
||||||
has_mastery: bool = field(kw_only=True)
|
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 random import Random
|
||||||
|
|
||||||
from . import options as stardew_options
|
from . import options as stardew_options
|
||||||
|
from .content import StardewContent
|
||||||
from .strings.ap_names.ap_weapon_names import APWeapon
|
from .strings.ap_names.ap_weapon_names import APWeapon
|
||||||
from .strings.ap_names.transport_names import Transportation
|
from .strings.ap_names.transport_names import Transportation
|
||||||
from .strings.building_names import Building
|
from .strings.building_names import Building
|
||||||
from .strings.region_names import Region
|
from .strings.region_names import Region
|
||||||
from .strings.season_names import Season
|
from .strings.season_names import Season
|
||||||
|
from .strings.skill_names import Skill
|
||||||
from .strings.tv_channel_names import Channel
|
from .strings.tv_channel_names import Channel
|
||||||
from .strings.wallet_item_names import Wallet
|
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]
|
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_forced = []
|
||||||
early_candidates = []
|
early_candidates = []
|
||||||
early_candidates.extend(always_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")
|
early_forced.append("Progressive Backpack")
|
||||||
|
|
||||||
if options.tool_progression & stardew_options.ToolProgression.option_progressive:
|
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_candidates.append("Progressive Fishing Rod")
|
||||||
early_forced.append("Progressive Pickaxe")
|
early_forced.append("Progressive Pickaxe")
|
||||||
|
|
||||||
if options.skill_progression == stardew_options.SkillProgression.option_progressive:
|
fishing = content.skills.get(Skill.fishing)
|
||||||
early_forced.append("Fishing Level")
|
if fishing is not None and content.features.skill_progression.is_progressive:
|
||||||
|
early_forced.append(fishing.level_name)
|
||||||
|
|
||||||
if options.quest_locations >= 0:
|
if options.quest_locations >= 0:
|
||||||
early_candidates.append(Wallet.magnifying_glass)
|
early_candidates.append(Wallet.magnifying_glass)
|
||||||
|
|
|
@ -15,7 +15,7 @@ from .data.game_item import ItemTag
|
||||||
from .logic.logic_event import all_events
|
from .logic.logic_event import all_events
|
||||||
from .mods.mod_data import ModNames
|
from .mods.mod_data import ModNames
|
||||||
from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Museumsanity, \
|
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
|
Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity, Walnutsanity, EnabledFillerBuffs
|
||||||
from .strings.ap_names.ap_option_names import OptionName
|
from .strings.ap_names.ap_option_names import OptionName
|
||||||
from .strings.ap_names.ap_weapon_names import APWeapon
|
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)
|
create_weapons(item_factory, options, items)
|
||||||
items.append(item_factory("Skull Key"))
|
items.append(item_factory("Skull Key"))
|
||||||
create_elevators(item_factory, options, items)
|
create_elevators(item_factory, options, items)
|
||||||
create_tools(item_factory, options, items)
|
create_tools(item_factory, options, content, items)
|
||||||
create_skills(item_factory, options, items)
|
create_skills(item_factory, content, items)
|
||||||
create_wizard_buildings(item_factory, options, items)
|
create_wizard_buildings(item_factory, options, items)
|
||||||
create_carpenter_buildings(item_factory, options, items)
|
create_carpenter_buildings(item_factory, options, items)
|
||||||
items.append(item_factory("Railroad Boulder Removed"))
|
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])
|
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:
|
if options.tool_progression & ToolProgression.option_progressive:
|
||||||
for item_data in items_by_group[Group.PROGRESSIVE_TOOLS]:
|
for item_data in items_by_group[Group.PROGRESSIVE_TOOLS]:
|
||||||
name = item_data.name
|
name = item_data.name
|
||||||
|
@ -325,28 +325,29 @@ def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions
|
||||||
items.append(item_factory(item_data, ItemClassification.useful))
|
items.append(item_factory(item_data, ItemClassification.useful))
|
||||||
else:
|
else:
|
||||||
items.extend([item_factory(item) for item in [item_data] * 4])
|
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 Scythe"))
|
||||||
items.append(item_factory("Progressive Fishing Rod"))
|
items.append(item_factory("Progressive Fishing Rod"))
|
||||||
|
|
||||||
|
# The golden scythe is always randomized
|
||||||
items.append(item_factory("Progressive Scythe"))
|
items.append(item_factory("Progressive Scythe"))
|
||||||
|
|
||||||
|
|
||||||
def create_skills(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
def create_skills(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
|
||||||
if options.skill_progression == SkillProgression.option_vanilla:
|
skill_progression = content.features.skill_progression
|
||||||
|
if not skill_progression.is_progressive:
|
||||||
return
|
return
|
||||||
|
|
||||||
for item in items_by_group[Group.SKILL_LEVEL_UP]:
|
for skill in content.skills.values():
|
||||||
if item.mod_name not in options.mods and item.mod_name is not None:
|
items.extend(item_factory(skill.level_name) for _ in skill_progression.get_randomized_level_names_by_level(skill))
|
||||||
continue
|
|
||||||
items.extend(item_factory(item) for item in [item.name] * 10)
|
|
||||||
|
|
||||||
if options.skill_progression != SkillProgression.option_progressive_with_masteries:
|
if skill_progression.is_mastery_randomized(skill):
|
||||||
return
|
items.append(item_factory(skill.mastery_name))
|
||||||
|
|
||||||
for item in items_by_group[Group.SKILL_MASTERY]:
|
if skill_progression.are_masteries_shuffled:
|
||||||
if item.mod_name not in options.mods and item.mod_name is not None:
|
items.append(item_factory(Wallet.mastery_of_the_five_ways))
|
||||||
continue
|
|
||||||
items.append(item_factory(item))
|
|
||||||
|
|
||||||
|
|
||||||
def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
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 .data.museum_data import all_museum_items
|
||||||
from .mods.mod_data import ModNames
|
from .mods.mod_data import ModNames
|
||||||
from .options import ExcludeGingerIsland, ArcadeMachineLocations, SpecialOrderLocations, Museumsanity, \
|
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 .options import StardewValleyOptions, Craftsanity, Chefsanity, Cooksanity, Shipsanity, Monstersanity
|
||||||
from .strings.goal_names import Goal
|
from .strings.goal_names import Goal
|
||||||
from .strings.quest_names import ModQuest, Quest
|
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))
|
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:
|
if options.quest_locations < 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
story_quest_locations = locations_by_tag[LocationTags.STORY_QUEST]
|
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)
|
randomized_locations.extend(story_quest_locations)
|
||||||
|
|
||||||
for i in range(0, options.quest_locations.value):
|
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)
|
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:
|
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)
|
randomized_locations.extend(board_locations)
|
||||||
|
|
||||||
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
|
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])
|
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]]
|
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)
|
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)
|
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
|
monstersanity = options.monstersanity
|
||||||
if monstersanity == Monstersanity.option_none:
|
if monstersanity == Monstersanity.option_none:
|
||||||
return
|
return
|
||||||
if monstersanity == Monstersanity.option_one_per_monster or monstersanity == Monstersanity.option_split_goals:
|
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]]
|
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)
|
randomized_locations.extend(filtered_monster_locations)
|
||||||
return
|
return
|
||||||
goal_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_GOALS]]
|
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)
|
randomized_locations.extend(filtered_goal_locations)
|
||||||
if monstersanity != Monstersanity.option_progressive_goals:
|
if monstersanity != Monstersanity.option_progressive_goals:
|
||||||
return
|
return
|
||||||
progressive_goal_locations = [location for location in locations_by_tag[LocationTags.MONSTERSANITY_PROGRESSIVE_GOALS]]
|
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)
|
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
|
shipsanity = options.shipsanity
|
||||||
if shipsanity == Shipsanity.option_none:
|
if shipsanity == Shipsanity.option_none:
|
||||||
return
|
return
|
||||||
if shipsanity == Shipsanity.option_everything:
|
if shipsanity == Shipsanity.option_everything:
|
||||||
ship_locations = [location for location in locations_by_tag[LocationTags.SHIPSANITY]]
|
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)
|
randomized_locations.extend(filtered_ship_locations)
|
||||||
return
|
return
|
||||||
shipsanity_locations = set()
|
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:
|
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]})
|
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)
|
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
|
cooksanity = options.cooksanity
|
||||||
if cooksanity == Cooksanity.option_none:
|
if cooksanity == Cooksanity.option_none:
|
||||||
return
|
return
|
||||||
|
@ -398,11 +398,11 @@ def extend_cooksanity_locations(randomized_locations: List[LocationData], option
|
||||||
else:
|
else:
|
||||||
cooksanity_locations = (location for location in locations_by_tag[LocationTags.COOKSANITY])
|
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)
|
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
|
chefsanity = options.chefsanity
|
||||||
if chefsanity == Chefsanity.option_none:
|
if chefsanity == Chefsanity.option_none:
|
||||||
return
|
return
|
||||||
|
@ -418,16 +418,16 @@ def extend_chefsanity_locations(randomized_locations: List[LocationData], option
|
||||||
if chefsanity & Chefsanity.option_skills:
|
if chefsanity & Chefsanity.option_skills:
|
||||||
chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_SKILL]})
|
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)
|
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:
|
if options.craftsanity == Craftsanity.option_none:
|
||||||
return
|
return
|
||||||
|
|
||||||
craftsanity_locations = [craft for craft in locations_by_tag[LocationTags.CRAFTSANITY]]
|
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)
|
randomized_locations.extend(filtered_craftsanity_locations)
|
||||||
|
|
||||||
|
|
||||||
|
@ -467,7 +467,7 @@ def create_locations(location_collector: StardewLocationCollector,
|
||||||
random: Random):
|
random: Random):
|
||||||
randomized_locations = []
|
randomized_locations = []
|
||||||
|
|
||||||
extend_mandatory_locations(randomized_locations, options)
|
extend_mandatory_locations(randomized_locations, options, content)
|
||||||
extend_bundle_locations(randomized_locations, bundle_rooms)
|
extend_bundle_locations(randomized_locations, bundle_rooms)
|
||||||
extend_backpack_locations(randomized_locations, options)
|
extend_backpack_locations(randomized_locations, options)
|
||||||
|
|
||||||
|
@ -476,13 +476,12 @@ def create_locations(location_collector: StardewLocationCollector,
|
||||||
|
|
||||||
extend_elevator_locations(randomized_locations, options)
|
extend_elevator_locations(randomized_locations, options)
|
||||||
|
|
||||||
if not options.skill_progression == SkillProgression.option_vanilla:
|
skill_progression = content.features.skill_progression
|
||||||
for location in locations_by_tag[LocationTags.SKILL_LEVEL]:
|
if skill_progression.is_progressive:
|
||||||
if location.mod_name is not None and location.mod_name not in options.mods:
|
for skill in content.skills.values():
|
||||||
continue
|
randomized_locations.extend([location_table[location_name] for _, location_name in skill_progression.get_randomized_level_names_by_level(skill)])
|
||||||
if LocationTags.MASTERY_LEVEL in location.tags and options.skill_progression != SkillProgression.option_progressive_with_masteries:
|
if skill_progression.is_mastery_randomized(skill):
|
||||||
continue
|
randomized_locations.append(location_table[skill.mastery_name])
|
||||||
randomized_locations.append(location_table[location.name])
|
|
||||||
|
|
||||||
if options.building_progression & BuildingProgression.option_progressive:
|
if options.building_progression & BuildingProgression.option_progressive:
|
||||||
for location in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
|
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_friendsanity_locations(randomized_locations, content)
|
||||||
|
|
||||||
extend_festival_locations(randomized_locations, options, random)
|
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_walnut_purchase_locations(randomized_locations, options)
|
||||||
|
|
||||||
extend_monstersanity_locations(randomized_locations, options)
|
extend_monstersanity_locations(randomized_locations, options, content)
|
||||||
extend_shipsanity_locations(randomized_locations, options)
|
extend_shipsanity_locations(randomized_locations, options, content)
|
||||||
extend_cooksanity_locations(randomized_locations, options)
|
extend_cooksanity_locations(randomized_locations, options, content)
|
||||||
extend_chefsanity_locations(randomized_locations, options)
|
extend_chefsanity_locations(randomized_locations, options, content)
|
||||||
extend_craftsanity_locations(randomized_locations, options)
|
extend_craftsanity_locations(randomized_locations, options, content)
|
||||||
extend_quests_locations(randomized_locations, options)
|
extend_quests_locations(randomized_locations, options, content)
|
||||||
extend_book_locations(randomized_locations, content)
|
extend_book_locations(randomized_locations, content)
|
||||||
extend_walnutsanity_locations(randomized_locations, options)
|
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)
|
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]:
|
def filter_masteries_locations(content: StardewContent, locations: Iterable[LocationData]) -> Iterable[LocationData]:
|
||||||
include_masteries = options.skill_progression == SkillProgression.option_progressive_with_masteries
|
# FIXME Remove once recipes are handled by the content packs
|
||||||
return (location for location in locations if include_masteries or LocationTags.REQUIRES_MASTERIES not in location.tags)
|
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]:
|
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)
|
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_farm_filter = filter_farm_type(options, locations)
|
||||||
locations_island_filter = filter_ginger_island(options, locations_farm_filter)
|
locations_island_filter = filter_ginger_island(options, locations_farm_filter)
|
||||||
locations_qi_filter = filter_qi_order_locations(options, locations_island_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)
|
locations_mod_filter = filter_modded_locations(options, locations_masteries_filter)
|
||||||
return locations_mod_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, \
|
from ..data.recipe_source import CutsceneSource, ShopTradeSource, ArchipelagoSource, LogicSource, SpecialOrderSource, \
|
||||||
FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource, SkillCraftsanitySource
|
FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource, SkillCraftsanitySource
|
||||||
from ..locations import locations_by_tag, LocationTags
|
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 ..stardew_rule import StardewRule, True_, False_
|
||||||
from ..strings.region_names import Region
|
from ..strings.region_names import Region
|
||||||
|
|
||||||
|
@ -101,12 +101,13 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
|
||||||
craftsanity_prefix = "Craft "
|
craftsanity_prefix = "Craft "
|
||||||
all_recipes_names = []
|
all_recipes_names = []
|
||||||
exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
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]:
|
for location in locations_by_tag[LocationTags.CRAFTSANITY]:
|
||||||
if not location.name.startswith(craftsanity_prefix):
|
if not location.name.startswith(craftsanity_prefix):
|
||||||
continue
|
continue
|
||||||
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
|
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
|
||||||
continue
|
continue
|
||||||
|
# FIXME Remove when recipes are in content packs
|
||||||
if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags:
|
if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags:
|
||||||
continue
|
continue
|
||||||
if location.mod_name and location.mod_name not in self.options.mods:
|
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 .received_logic import ReceivedLogicMixin
|
||||||
from .region_logic import RegionLogicMixin
|
from .region_logic import RegionLogicMixin
|
||||||
from .time_logic import TimeLogicMixin
|
from .time_logic import TimeLogicMixin
|
||||||
from ..options import Booksanity
|
|
||||||
from ..stardew_rule import StardewRule, HasProgressionPercent
|
from ..stardew_rule import StardewRule, HasProgressionPercent
|
||||||
from ..strings.book_names import Book
|
from ..strings.book_names import Book
|
||||||
from ..strings.craftable_names import Consumable
|
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)
|
opening_rule = self.logic.region.can_reach(Region.blacksmith)
|
||||||
mystery_box_rule = self.logic.has(Consumable.mystery_box)
|
mystery_box_rule = self.logic.has(Consumable.mystery_box)
|
||||||
book_of_mysteries_rule = self.logic.true_ \
|
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)
|
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.
|
# 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)
|
time_rule = self.logic.time.has_lived_months(quantity // 14)
|
||||||
|
|
|
@ -58,14 +58,19 @@ SkillLogicMixin, CookingLogicMixin]]):
|
||||||
rules = []
|
rules = []
|
||||||
weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier)
|
weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier)
|
||||||
rules.append(weapon_rule)
|
rules.append(weapon_rule)
|
||||||
|
|
||||||
if self.options.tool_progression & ToolProgression.option_progressive:
|
if self.options.tool_progression & ToolProgression.option_progressive:
|
||||||
rules.append(self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier]))
|
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))
|
# No alternative for vanilla because we assume that you will grind the levels in the mines.
|
||||||
rules.append(self.logic.skill.has_level(Skill.combat, skill_tier))
|
if self.content.features.skill_progression.is_progressive:
|
||||||
rules.append(self.logic.skill.has_level(Skill.mining, skill_tier))
|
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:
|
if tier >= 4:
|
||||||
rules.append(self.logic.cooking.can_cook())
|
rules.append(self.logic.cooking.can_cook())
|
||||||
|
|
||||||
return self.logic.and_(*rules)
|
return self.logic.and_(*rules)
|
||||||
|
|
||||||
@cache_self1
|
@cache_self1
|
||||||
|
@ -82,10 +87,14 @@ SkillLogicMixin, CookingLogicMixin]]):
|
||||||
rules = []
|
rules = []
|
||||||
weapon_rule = self.logic.combat.has_great_weapon
|
weapon_rule = self.logic.combat.has_great_weapon
|
||||||
rules.append(weapon_rule)
|
rules.append(weapon_rule)
|
||||||
|
|
||||||
if self.options.tool_progression & ToolProgression.option_progressive:
|
if self.options.tool_progression & ToolProgression.option_progressive:
|
||||||
rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2))))
|
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))
|
# No alternative for vanilla because we assume that you will grind the levels in the mines.
|
||||||
rules.extend({self.logic.skill.has_level(Skill.combat, skill_tier),
|
if self.content.features.skill_progression.is_progressive:
|
||||||
self.logic.skill.has_level(Skill.mining, skill_tier)})
|
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)
|
return self.logic.and_(*rules)
|
||||||
|
|
|
@ -11,7 +11,6 @@ from .region_logic import RegionLogicMixin
|
||||||
from .season_logic import SeasonLogicMixin
|
from .season_logic import SeasonLogicMixin
|
||||||
from .time_logic import TimeLogicMixin
|
from .time_logic import TimeLogicMixin
|
||||||
from .tool_logic import ToolLogicMixin
|
from .tool_logic import ToolLogicMixin
|
||||||
from .. import options
|
|
||||||
from ..data.harvest import HarvestCropSource
|
from ..data.harvest import HarvestCropSource
|
||||||
from ..mods.logic.magic_logic import MagicLogicMixin
|
from ..mods.logic.magic_logic import MagicLogicMixin
|
||||||
from ..mods.logic.mod_skills_levels import get_mod_skill_levels
|
from ..mods.logic.mod_skills_levels import get_mod_skill_levels
|
||||||
|
@ -77,21 +76,21 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
|
||||||
if level == 0:
|
if level == 0:
|
||||||
return true_
|
return true_
|
||||||
|
|
||||||
if self.options.skill_progression == options.SkillProgression.option_vanilla:
|
if self.content.features.skill_progression.is_progressive:
|
||||||
return self.logic.skill.can_earn_level(skill, level)
|
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:
|
def has_previous_level(self, skill: str, level: int) -> StardewRule:
|
||||||
assert level > 0, f"There is no level before level 0."
|
assert level > 0, f"There is no level before level 0."
|
||||||
if level == 1:
|
if level == 1:
|
||||||
return true_
|
return true_
|
||||||
|
|
||||||
if self.options.skill_progression == options.SkillProgression.option_vanilla:
|
if self.content.features.skill_progression.is_progressive:
|
||||||
months = max(1, level - 1)
|
return self.logic.received(f"{skill} Level", level - 1)
|
||||||
return self.logic.time.has_lived_months(months)
|
|
||||||
|
|
||||||
return self.logic.received(f"{skill} Level", level - 1)
|
months = max(1, level - 1)
|
||||||
|
return self.logic.time.has_lived_months(months)
|
||||||
|
|
||||||
@cache_self1
|
@cache_self1
|
||||||
def has_farming_level(self, level: int) -> StardewRule:
|
def has_farming_level(self, level: int) -> StardewRule:
|
||||||
|
@ -102,7 +101,7 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
|
||||||
if level <= 0:
|
if level <= 0:
|
||||||
return True_()
|
return True_()
|
||||||
|
|
||||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
if self.content.features.skill_progression.is_progressive:
|
||||||
skills_items = vanilla_skill_items
|
skills_items = vanilla_skill_items
|
||||||
if allow_modded_skills:
|
if allow_modded_skills:
|
||||||
skills_items += get_mod_skill_levels(self.options.mods)
|
skills_items += get_mod_skill_levels(self.options.mods)
|
||||||
|
@ -148,7 +147,7 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def can_get_fishing_xp(self) -> StardewRule:
|
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() | self.logic.skill.can_crab_pot
|
||||||
|
|
||||||
return self.logic.skill.can_fish()
|
return self.logic.skill.can_fish()
|
||||||
|
@ -178,7 +177,9 @@ CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
|
||||||
@cached_property
|
@cached_property
|
||||||
def can_crab_pot(self) -> StardewRule:
|
def can_crab_pot(self) -> StardewRule:
|
||||||
crab_pot_rule = self.logic.has(Fishing.bait)
|
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)
|
crab_pot_rule = crab_pot_rule & self.logic.has(Machine.crab_pot)
|
||||||
else:
|
else:
|
||||||
crab_pot_rule = crab_pot_rule & self.logic.skill.can_get_fishing_xp
|
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)
|
return self.logic.skill.can_earn_level(skill, 11) & self.logic.region.can_reach(Region.mastery_cave)
|
||||||
|
|
||||||
def has_mastery(self, skill: str) -> StardewRule:
|
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.received(f"{skill} Mastery")
|
||||||
|
|
||||||
return self.logic.skill.can_earn_mastery(skill)
|
return self.logic.skill.can_earn_mastery(skill)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def can_enter_mastery_cave(self) -> StardewRule:
|
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.logic.received(Wallet.mastery_of_the_five_ways)
|
||||||
|
|
||||||
return self.has_any_skills_maxed(included_modded_skills=False)
|
return self.has_any_skills_maxed(included_modded_skills=False)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from ... import options
|
|
||||||
from ...logic.base_logic import BaseLogicMixin, BaseLogic
|
from ...logic.base_logic import BaseLogicMixin, BaseLogic
|
||||||
from ...logic.combat_logic import CombatLogicMixin
|
from ...logic.combat_logic import CombatLogicMixin
|
||||||
from ...logic.cooking_logic import CookingLogicMixin
|
from ...logic.cooking_logic import CookingLogicMixin
|
||||||
|
@ -45,9 +44,9 @@ CookingLogicMixin]]):
|
||||||
self.logic.received(ModTransportation.woods_obelisk))
|
self.logic.received(ModTransportation.woods_obelisk))
|
||||||
|
|
||||||
tier = int(depth / 25) + 1
|
tier = int(depth / 25) + 1
|
||||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
if self.content.features.skill_progression.is_progressive:
|
||||||
combat_tier = min(10, max(0, tier + 5))
|
combat_level = min(10, max(0, tier + 5))
|
||||||
rules.append(self.logic.skill.has_level(Skill.combat, combat_tier))
|
rules.append(self.logic.skill.has_level(Skill.combat, combat_level))
|
||||||
|
|
||||||
return self.logic.and_(*rules)
|
return self.logic.and_(*rules)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ from ...logic.region_logic import RegionLogicMixin
|
||||||
from ...logic.relationship_logic import RelationshipLogicMixin
|
from ...logic.relationship_logic import RelationshipLogicMixin
|
||||||
from ...logic.tool_logic import ToolLogicMixin
|
from ...logic.tool_logic import ToolLogicMixin
|
||||||
from ...mods.mod_data import ModNames
|
from ...mods.mod_data import ModNames
|
||||||
from ...options import SkillProgression
|
|
||||||
from ...stardew_rule import StardewRule, False_, True_, And
|
from ...stardew_rule import StardewRule, False_, True_, And
|
||||||
from ...strings.building_names import Building
|
from ...strings.building_names import Building
|
||||||
from ...strings.craftable_names import ModCraftable, ModMachine
|
from ...strings.craftable_names import ModCraftable, ModMachine
|
||||||
|
@ -37,7 +36,7 @@ ToolLogicMixin, FishingLogicMixin, CookingLogicMixin, CraftingLogicMixin, MagicL
|
||||||
if level <= 0:
|
if level <= 0:
|
||||||
return True_()
|
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.logic.received(f"{skill} Level", level)
|
||||||
|
|
||||||
return self.can_earn_mod_skill_level(skill, 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:
|
def can_earn_archaeology_skill_level(self, level: int) -> StardewRule:
|
||||||
shifter_rule = True_()
|
shifter_rule = True_()
|
||||||
preservation_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)
|
shifter_rule = self.logic.has(ModCraftable.water_shifter)
|
||||||
preservation_rule = self.logic.has(ModMachine.hardwood_preservation_chamber)
|
preservation_rule = self.logic.has(ModMachine.hardwood_preservation_chamber)
|
||||||
if level >= 8:
|
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:
|
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:
|
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.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)
|
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 typing import Iterable, Dict, Protocol, List, Tuple, Set
|
||||||
|
|
||||||
from BaseClasses import Region, Entrance
|
from BaseClasses import Region, Entrance
|
||||||
|
from .content import content_packs, StardewContent
|
||||||
from .mods.mod_regions import ModDataList, vanilla_connections_to_remove_by_mod
|
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 .region_classes import RegionData, ConnectionData, RandomizationFlag, ModificationFlag
|
||||||
from .strings.entrance_names import Entrance, LogicEntrance
|
from .strings.entrance_names import Entrance, LogicEntrance
|
||||||
from .strings.region_names import Region, LogicRegion
|
from .strings.region_names import Region, LogicRegion
|
||||||
|
@ -587,7 +588,7 @@ def modify_vanilla_regions(existing_region: RegionData, modified_region: RegionD
|
||||||
return updated_region
|
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]]:
|
-> Tuple[Dict[str, Region], Dict[str, Entrance], Dict[str, str]]:
|
||||||
entrances_data, regions_data = create_final_connections_and_regions(world_options)
|
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}
|
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
|
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:
|
for connection in connections:
|
||||||
if connection.name in entrances_by_name:
|
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
|
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_by_name: Dict[str, ConnectionData]) -> Tuple[List[ConnectionData], Dict[str, str]]:
|
||||||
connections_to_randomize: List[ConnectionData] = []
|
connections_to_randomize: List[ConnectionData] = []
|
||||||
if world_options.entrance_randomization == EntranceRandomization.option_pelican_town:
|
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:
|
elif world_options.entrance_randomization == EntranceRandomization.option_chaos:
|
||||||
connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
|
connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
|
||||||
RandomizationFlag.BUILDINGS in connections_by_name[connection].flag]
|
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
|
# On Chaos, we just add the connections to randomize, unshuffled, and the client does it every day
|
||||||
randomized_data_for_mod = {}
|
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
|
randomized_data_for_mod[connection.reverse] = connection.reverse
|
||||||
return list(connections_by_name.values()), randomized_data_for_mod
|
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)
|
random.shuffle(connections_to_randomize)
|
||||||
destination_pool = list(connections_to_randomize)
|
destination_pool = list(connections_to_randomize)
|
||||||
random.shuffle(destination_pool)
|
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
|
return randomized_connections_for_generation, randomized_data_for_mod
|
||||||
|
|
||||||
|
|
||||||
def remove_excluded_entrances(connections_to_randomize: List[ConnectionData], world_options: StardewValleyOptions) -> List[ConnectionData]:
|
def remove_excluded_entrances(connections_to_randomize: List[ConnectionData], content: StardewContent) -> List[ConnectionData]:
|
||||||
exclude_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
# FIXME remove when regions are handled in content packs
|
||||||
if exclude_island:
|
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]
|
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 not content.features.skill_progression.are_masteries_shuffled:
|
||||||
if exclude_masteries:
|
|
||||||
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.MASTERIES not in connection.flag]
|
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.MASTERIES not in connection.flag]
|
||||||
|
|
||||||
return connections_to_randomize
|
return connections_to_randomize
|
||||||
|
|
|
@ -21,7 +21,7 @@ from .logic.tool_logic import tool_upgrade_prices
|
||||||
from .mods.mod_data import ModNames
|
from .mods.mod_data import ModNames
|
||||||
from .options import StardewValleyOptions, Walnutsanity
|
from .options import StardewValleyOptions, Walnutsanity
|
||||||
from .options import ToolProgression, BuildingProgression, ExcludeGingerIsland, SpecialOrderLocations, Museumsanity, BackpackProgression, Shipsanity, \
|
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 import And, StardewRule, true_
|
||||||
from .stardew_rule.indirect_connection import look_for_indirect_connection
|
from .stardew_rule.indirect_connection import look_for_indirect_connection
|
||||||
from .stardew_rule.rule_explain import explain
|
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.quest_names import Quest
|
||||||
from .strings.region_names import Region
|
from .strings.region_names import Region
|
||||||
from .strings.season_names import Season
|
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.tool_names import Tool, ToolMaterial
|
||||||
from .strings.tv_channel_names import Channel
|
from .strings.tv_channel_names import Channel
|
||||||
from .strings.villager_names import NPC, ModNPC
|
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_ginger_island_rules(logic, multiworld, player, world_options)
|
||||||
|
|
||||||
set_tool_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_bundle_rules(bundle_rooms, logic, multiworld, player, world_options)
|
||||||
set_building_rules(logic, multiworld, player, world_options)
|
set_building_rules(logic, multiworld, player, world_options)
|
||||||
set_cropsanity_rules(logic, multiworld, player, world_content)
|
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))
|
MultiWorldRules.add_rule(multiworld.get_location(room_location, player), And(*room_rules))
|
||||||
|
|
||||||
|
|
||||||
def set_skills_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
|
def set_skills_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, content: StardewContent):
|
||||||
mods = world_options.mods
|
skill_progression = content.features.skill_progression
|
||||||
if world_options.skill_progression == SkillProgression.option_vanilla:
|
if not skill_progression.is_progressive:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(1, 11):
|
for skill in content.skills.values():
|
||||||
set_vanilla_skill_rule_for_level(logic, multiworld, player, i)
|
for level, level_name in skill_progression.get_randomized_level_names_by_level(skill):
|
||||||
set_modded_skill_rule_for_level(logic, multiworld, player, mods, i)
|
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:
|
if skill_progression.is_mastery_randomized(skill):
|
||||||
return
|
rule = logic.skill.can_earn_mastery(skill.name)
|
||||||
|
location = multiworld.get_location(skill.mastery_name, player)
|
||||||
for skill in [Skill.farming, Skill.fishing, Skill.foraging, Skill.mining, Skill.combat]:
|
MultiWorldRules.set_rule(location, rule)
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def set_entrance_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
|
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 BaseClasses import get_seed
|
||||||
from . import SVTestCase, complete_options_with_default
|
from . import SVTestCase, complete_options_with_default
|
||||||
|
from .. import create_content
|
||||||
from ..options import EntranceRandomization, ExcludeGingerIsland, SkillProgression
|
from ..options import EntranceRandomization, ExcludeGingerIsland, SkillProgression
|
||||||
from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag, create_final_connections_and_regions
|
from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag, create_final_connections_and_regions
|
||||||
from ..strings.entrance_names import Entrance as EntranceName
|
from ..strings.entrance_names import Entrance as EntranceName
|
||||||
|
@ -63,11 +64,12 @@ class TestEntranceRando(SVTestCase):
|
||||||
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
|
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
|
||||||
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
|
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
|
||||||
})
|
})
|
||||||
|
content = create_content(sv_options)
|
||||||
seed = get_seed()
|
seed = get_seed()
|
||||||
rand = random.Random(seed)
|
rand = random.Random(seed)
|
||||||
with self.subTest(flag=flag, msg=f"Seed: {seed}"):
|
with self.subTest(flag=flag, msg=f"Seed: {seed}"):
|
||||||
entrances, regions = create_final_connections_and_regions(sv_options)
|
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:
|
for connection in vanilla_connections:
|
||||||
if flag in connection.flag:
|
if flag in connection.flag:
|
||||||
|
@ -90,11 +92,12 @@ class TestEntranceRando(SVTestCase):
|
||||||
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
|
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
|
||||||
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
|
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
|
||||||
})
|
})
|
||||||
|
content = create_content(sv_options)
|
||||||
seed = get_seed()
|
seed = get_seed()
|
||||||
rand = random.Random(seed)
|
rand = random.Random(seed)
|
||||||
with self.subTest(option=option, flag=flag, seed=seed):
|
with self.subTest(option=option, flag=flag, seed=seed):
|
||||||
entrances, regions = create_final_connections_and_regions(sv_options)
|
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:
|
for connection in vanilla_connections:
|
||||||
if flag in connection.flag:
|
if flag in connection.flag:
|
||||||
|
@ -118,13 +121,14 @@ class TestEntranceRando(SVTestCase):
|
||||||
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
|
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
|
||||||
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
|
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):
|
for i in range(0, 100 if self.skip_long_tests else 10000):
|
||||||
seed = get_seed()
|
seed = get_seed()
|
||||||
rand = random.Random(seed)
|
rand = random.Random(seed)
|
||||||
with self.subTest(msg=f"Seed: {seed}"):
|
with self.subTest(msg=f"Seed: {seed}"):
|
||||||
entrances, regions = create_final_connections_and_regions(sv_options)
|
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}
|
connections_by_name = {connection.name: connection for connection in randomized_connections}
|
||||||
|
|
||||||
blocked_entrances = {EntranceName.use_island_obelisk, EntranceName.boat_to_ginger_island}
|
blocked_entrances = {EntranceName.use_island_obelisk, EntranceName.boat_to_ginger_island}
|
||||||
|
|
|
@ -7,7 +7,8 @@ default_features = StardewFeatures(
|
||||||
feature.booksanity.BooksanityDisabled(),
|
feature.booksanity.BooksanityDisabled(),
|
||||||
feature.cropsanity.CropsanityDisabled(),
|
feature.cropsanity.CropsanityDisabled(),
|
||||||
feature.fishsanity.FishsanityNone(),
|
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 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 .. 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 ..assertion import ModAssertMixin, WorldAssertMixin
|
||||||
from ... import items, Group, ItemClassification
|
from ... import items, Group, ItemClassification, create_content
|
||||||
from ... import options
|
from ... import options
|
||||||
from ...items import items_by_group
|
from ...items import items_by_group
|
||||||
from ...options import SkillProgression, Walnutsanity
|
from ...options import SkillProgression, Walnutsanity
|
||||||
|
@ -128,12 +128,13 @@ class TestModEntranceRando(SVTestCase):
|
||||||
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
|
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
|
||||||
options.Mods.internal_name: frozenset(options.Mods.valid_keys)
|
options.Mods.internal_name: frozenset(options.Mods.valid_keys)
|
||||||
})
|
})
|
||||||
|
content = create_content(sv_options)
|
||||||
seed = get_seed()
|
seed = get_seed()
|
||||||
rand = random.Random(seed)
|
rand = random.Random(seed)
|
||||||
with self.subTest(option=option, flag=flag, seed=seed):
|
with self.subTest(option=option, flag=flag, seed=seed):
|
||||||
final_connections, final_regions = create_final_connections_and_regions(sv_options)
|
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:
|
for connection_name in final_connections:
|
||||||
connection = final_connections[connection_name]
|
connection = final_connections[connection_name]
|
||||||
|
|
Loading…
Reference in New Issue