Stardew Valley 6.x.x: The Content Update (#3478)

Focus of the Update: Compatibility with Stardew Valley 1.6 Released on March 19th 2024
This includes randomization for pretty much all of the new content, including but not limited to
- Raccoon Bundles
- Booksanity
- Skill Masteries
- New Recipes, Craftables, Fish, Maps, Farm Type, Festivals and Quests

This also includes a significant reorganisation of the code into "Content Packs", to allow for easier modularity of various game mechanics between the settings and the supported mods. This improves maintainability quite a bit.

In addition to that, a few **very** requested new features have been introduced, although they weren't the focus of this update
- Walnutsanity
- Player Buffs
- More customizability in settings, such as shorter special orders, ER without farmhouse
- New Remixed Bundles
This commit is contained in:
agilbert1412
2024-07-07 16:04:25 +03:00
committed by GitHub
parent f99ee77325
commit 9b22458f44
210 changed files with 10298 additions and 4540 deletions

View File

@@ -0,0 +1,107 @@
from . import content_packs
from .feature import cropsanity, friendsanity, fishsanity, booksanity
from .game_content import ContentPack, StardewContent, StardewFeatures
from .unpacking import unpack_content
from .. import options
def create_content(player_options: options.StardewValleyOptions) -> StardewContent:
active_packs = choose_content_packs(player_options)
features = choose_features(player_options)
return unpack_content(features, active_packs)
def choose_content_packs(player_options: options.StardewValleyOptions):
active_packs = [content_packs.pelican_town, content_packs.the_desert, content_packs.the_farm, content_packs.the_mines]
if player_options.exclude_ginger_island == options.ExcludeGingerIsland.option_false:
active_packs.append(content_packs.ginger_island_content_pack)
if player_options.special_order_locations & options.SpecialOrderLocations.value_qi:
active_packs.append(content_packs.qi_board_content_pack)
for mod in player_options.mods.value:
active_packs.append(content_packs.by_mod[mod])
return active_packs
def choose_features(player_options: options.StardewValleyOptions) -> StardewFeatures:
return StardewFeatures(
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)
)
booksanity_by_option = {
options.Booksanity.option_none: booksanity.BooksanityDisabled(),
options.Booksanity.option_power: booksanity.BooksanityPower(),
options.Booksanity.option_power_skill: booksanity.BooksanityPowerSkill(),
options.Booksanity.option_all: booksanity.BooksanityAll(),
}
def choose_booksanity(booksanity_option: options.Booksanity) -> booksanity.BooksanityFeature:
booksanity_feature = booksanity_by_option.get(booksanity_option)
if booksanity_feature is None:
raise ValueError(f"No booksanity feature mapped to {str(booksanity_option.value)}")
return booksanity_feature
cropsanity_by_option = {
options.Cropsanity.option_disabled: cropsanity.CropsanityDisabled(),
options.Cropsanity.option_enabled: cropsanity.CropsanityEnabled(),
}
def choose_cropsanity(cropsanity_option: options.Cropsanity) -> cropsanity.CropsanityFeature:
cropsanity_feature = cropsanity_by_option.get(cropsanity_option)
if cropsanity_feature is None:
raise ValueError(f"No cropsanity feature mapped to {str(cropsanity_option.value)}")
return cropsanity_feature
fishsanity_by_option = {
options.Fishsanity.option_none: fishsanity.FishsanityNone(),
options.Fishsanity.option_legendaries: fishsanity.FishsanityLegendaries(),
options.Fishsanity.option_special: fishsanity.FishsanitySpecial(),
options.Fishsanity.option_randomized: fishsanity.FishsanityAll(randomization_ratio=0.4),
options.Fishsanity.option_all: fishsanity.FishsanityAll(),
options.Fishsanity.option_exclude_legendaries: fishsanity.FishsanityExcludeLegendaries(),
options.Fishsanity.option_exclude_hard_fish: fishsanity.FishsanityExcludeHardFish(),
options.Fishsanity.option_only_easy_fish: fishsanity.FishsanityOnlyEasyFish(),
}
def choose_fishsanity(fishsanity_option: options.Fishsanity) -> fishsanity.FishsanityFeature:
fishsanity_feature = fishsanity_by_option.get(fishsanity_option)
if fishsanity_feature is None:
raise ValueError(f"No fishsanity feature mapped to {str(fishsanity_option.value)}")
return fishsanity_feature
def choose_friendsanity(friendsanity_option: options.Friendsanity, heart_size: options.FriendsanityHeartSize) -> friendsanity.FriendsanityFeature:
if friendsanity_option == options.Friendsanity.option_none:
return friendsanity.FriendsanityNone()
if friendsanity_option == options.Friendsanity.option_bachelors:
return friendsanity.FriendsanityBachelors(heart_size.value)
if friendsanity_option == options.Friendsanity.option_starting_npcs:
return friendsanity.FriendsanityStartingNpc(heart_size.value)
if friendsanity_option == options.Friendsanity.option_all:
return friendsanity.FriendsanityAll(heart_size.value)
if friendsanity_option == options.Friendsanity.option_all_with_marriage:
return friendsanity.FriendsanityAllWithMarriage(heart_size.value)
raise ValueError(f"No friendsanity feature mapped to {str(friendsanity_option.value)}")

View File

@@ -0,0 +1,31 @@
import importlib
import pkgutil
from . import mods
from .mod_registry import by_mod
from .vanilla.base import base_game
from .vanilla.ginger_island import ginger_island_content_pack
from .vanilla.pelican_town import pelican_town
from .vanilla.qi_board import qi_board_content_pack
from .vanilla.the_desert import the_desert
from .vanilla.the_farm import the_farm
from .vanilla.the_mines import the_mines
assert base_game
assert ginger_island_content_pack
assert pelican_town
assert qi_board_content_pack
assert the_desert
assert the_farm
assert the_mines
# Dynamically register everything currently in the mods folder. This would ideally be done through a metaclass, but I have not looked into that yet.
mod_modules = pkgutil.iter_modules(mods.__path__)
loaded_modules = {}
for mod_module in mod_modules:
module_name = mod_module.name
module = importlib.import_module("." + module_name, mods.__name__)
loaded_modules[module_name] = module
assert by_mod

View File

@@ -0,0 +1,4 @@
from . import booksanity
from . import cropsanity
from . import fishsanity
from . import friendsanity

View File

@@ -0,0 +1,72 @@
from abc import ABC, abstractmethod
from typing import ClassVar, Optional, Iterable
from ...data.game_item import GameItem, ItemTag
from ...strings.book_names import ordered_lost_books
item_prefix = "Power: "
location_prefix = "Read "
def to_item_name(book: str) -> str:
return item_prefix + book
def to_location_name(book: str) -> str:
return location_prefix + book
def extract_book_from_location_name(location_name: str) -> Optional[str]:
if not location_name.startswith(location_prefix):
return None
return location_name[len(location_prefix):]
class BooksanityFeature(ABC):
is_enabled: ClassVar[bool]
to_item_name = staticmethod(to_item_name)
progressive_lost_book = "Progressive Lost Book"
to_location_name = staticmethod(to_location_name)
extract_book_from_location_name = staticmethod(extract_book_from_location_name)
@abstractmethod
def is_included(self, book: GameItem) -> bool:
...
@staticmethod
def get_randomized_lost_books() -> Iterable[str]:
return []
class BooksanityDisabled(BooksanityFeature):
is_enabled = False
def is_included(self, book: GameItem) -> bool:
return False
class BooksanityPower(BooksanityFeature):
is_enabled = True
def is_included(self, book: GameItem) -> bool:
return ItemTag.BOOK_POWER in book.tags
class BooksanityPowerSkill(BooksanityFeature):
is_enabled = True
def is_included(self, book: GameItem) -> bool:
return ItemTag.BOOK_POWER in book.tags or ItemTag.BOOK_SKILL in book.tags
class BooksanityAll(BooksanityFeature):
is_enabled = True
def is_included(self, book: GameItem) -> bool:
return ItemTag.BOOK_POWER in book.tags or ItemTag.BOOK_SKILL in book.tags
@staticmethod
def get_randomized_lost_books() -> Iterable[str]:
return ordered_lost_books

View File

@@ -0,0 +1,42 @@
from abc import ABC, abstractmethod
from typing import ClassVar, Optional
from ...data.game_item import GameItem, ItemTag
location_prefix = "Harvest "
def to_location_name(crop: str) -> str:
return location_prefix + crop
def extract_crop_from_location_name(location_name: str) -> Optional[str]:
if not location_name.startswith(location_prefix):
return None
return location_name[len(location_prefix):]
class CropsanityFeature(ABC):
is_enabled: ClassVar[bool]
to_location_name = staticmethod(to_location_name)
extract_crop_from_location_name = staticmethod(extract_crop_from_location_name)
@abstractmethod
def is_included(self, crop: GameItem) -> bool:
...
class CropsanityDisabled(CropsanityFeature):
is_enabled = False
def is_included(self, crop: GameItem) -> bool:
return False
class CropsanityEnabled(CropsanityFeature):
is_enabled = True
def is_included(self, crop: GameItem) -> bool:
return ItemTag.CROPSANITY_SEED in crop.tags

View File

@@ -0,0 +1,101 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import ClassVar, Optional
from ...data.fish_data import FishItem
from ...strings.fish_names import Fish
location_prefix = "Fishsanity: "
def to_location_name(fish: str) -> str:
return location_prefix + fish
def extract_fish_from_location_name(location_name: str) -> Optional[str]:
if not location_name.startswith(location_prefix):
return None
return location_name[len(location_prefix):]
@dataclass(frozen=True)
class FishsanityFeature(ABC):
is_enabled: ClassVar[bool]
randomization_ratio: float = 1
to_location_name = staticmethod(to_location_name)
extract_fish_from_location_name = staticmethod(extract_fish_from_location_name)
@property
def is_randomized(self) -> bool:
return self.randomization_ratio != 1
@abstractmethod
def is_included(self, fish: FishItem) -> bool:
...
class FishsanityNone(FishsanityFeature):
is_enabled = False
def is_included(self, fish: FishItem) -> bool:
return False
class FishsanityLegendaries(FishsanityFeature):
is_enabled = True
def is_included(self, fish: FishItem) -> bool:
return fish.legendary
class FishsanitySpecial(FishsanityFeature):
is_enabled = True
included_fishes = {
Fish.angler,
Fish.crimsonfish,
Fish.glacierfish,
Fish.legend,
Fish.mutant_carp,
Fish.blobfish,
Fish.lava_eel,
Fish.octopus,
Fish.scorpion_carp,
Fish.ice_pip,
Fish.super_cucumber,
Fish.dorado
}
def is_included(self, fish: FishItem) -> bool:
return fish.name in self.included_fishes
class FishsanityAll(FishsanityFeature):
is_enabled = True
def is_included(self, fish: FishItem) -> bool:
return True
class FishsanityExcludeLegendaries(FishsanityFeature):
is_enabled = True
def is_included(self, fish: FishItem) -> bool:
return not fish.legendary
class FishsanityExcludeHardFish(FishsanityFeature):
is_enabled = True
def is_included(self, fish: FishItem) -> bool:
return fish.difficulty < 80
class FishsanityOnlyEasyFish(FishsanityFeature):
is_enabled = True
def is_included(self, fish: FishItem) -> bool:
return fish.difficulty < 50

View File

@@ -0,0 +1,139 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from functools import lru_cache
from typing import Optional, Tuple, ClassVar
from ...data.villagers_data import Villager
from ...strings.villager_names import NPC
suffix = " <3"
location_prefix = "Friendsanity: "
def to_item_name(npc_name: str) -> str:
return npc_name + suffix
def to_location_name(npc_name: str, heart: int) -> str:
return location_prefix + npc_name + " " + str(heart) + suffix
pet_heart_item_name = to_item_name(NPC.pet)
def extract_npc_from_item_name(item_name: str) -> Optional[str]:
if not item_name.endswith(suffix):
return None
return item_name[:-len(suffix)]
def extract_npc_from_location_name(location_name: str) -> Tuple[Optional[str], int]:
if not location_name.endswith(suffix):
return None, 0
trimmed = location_name[len(location_prefix):-len(suffix)]
last_space = trimmed.rindex(" ")
return trimmed[:last_space], int(trimmed[last_space + 1:])
@lru_cache(maxsize=32) # Should not go pass 32 values if every friendsanity options are in the multi world
def get_heart_steps(max_heart: int, heart_size: int) -> Tuple[int, ...]:
return tuple(range(heart_size, max_heart + 1, heart_size)) + ((max_heart,) if max_heart % heart_size else ())
@dataclass(frozen=True)
class FriendsanityFeature(ABC):
is_enabled: ClassVar[bool]
heart_size: int
to_item_name = staticmethod(to_item_name)
to_location_name = staticmethod(to_location_name)
pet_heart_item_name = pet_heart_item_name
extract_npc_from_item_name = staticmethod(extract_npc_from_item_name)
extract_npc_from_location_name = staticmethod(extract_npc_from_location_name)
@abstractmethod
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
...
@property
def is_pet_randomized(self):
return bool(self.get_pet_randomized_hearts())
@abstractmethod
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
...
class FriendsanityNone(FriendsanityFeature):
is_enabled = False
def __init__(self):
super().__init__(1)
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
return ()
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
return ()
@dataclass(frozen=True)
class FriendsanityBachelors(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
if not villager.bachelor:
return ()
return get_heart_steps(8, self.heart_size)
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
return ()
@dataclass(frozen=True)
class FriendsanityStartingNpc(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
if not villager.available:
return ()
if villager.bachelor:
return get_heart_steps(8, self.heart_size)
return get_heart_steps(10, self.heart_size)
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
return get_heart_steps(5, self.heart_size)
@dataclass(frozen=True)
class FriendsanityAll(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
if villager.bachelor:
return get_heart_steps(8, self.heart_size)
return get_heart_steps(10, self.heart_size)
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
return get_heart_steps(5, self.heart_size)
@dataclass(frozen=True)
class FriendsanityAllWithMarriage(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
if villager.bachelor:
return get_heart_steps(14, self.heart_size)
return get_heart_steps(10, self.heart_size)
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
return get_heart_steps(5, self.heart_size)

View File

@@ -0,0 +1,117 @@
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 ..data.fish_data import FishItem
from ..data.game_item import GameItem, ItemSource, ItemTag
from ..data.skill import Skill
from ..data.villagers_data import Villager
@dataclass(frozen=True)
class StardewContent:
features: StardewFeatures
registered_packs: Set[str] = field(default_factory=set)
# regions -> To be used with can reach rule
game_items: Dict[str, GameItem] = field(default_factory=dict)
fishes: Dict[str, FishItem] = field(default_factory=dict)
villagers: Dict[str, Villager] = field(default_factory=dict)
skills: Dict[str, Skill] = field(default_factory=dict)
quests: Dict[str, Any] = field(default_factory=dict)
def find_sources_of_type(self, types: Union[Type[ItemSource], Tuple[Type[ItemSource]]]) -> Iterable[ItemSource]:
for item in self.game_items.values():
for source in item.sources:
if isinstance(source, types):
yield source
def source_item(self, item_name: str, *sources: ItemSource):
item = self.game_items.setdefault(item_name, GameItem(item_name))
item.add_sources(sources)
def tag_item(self, item_name: str, *tags: ItemTag):
item = self.game_items.setdefault(item_name, GameItem(item_name))
item.add_tags(tags)
def untag_item(self, item_name: str, tag: ItemTag):
self.game_items[item_name].tags.remove(tag)
def find_tagged_items(self, tag: ItemTag) -> Iterable[GameItem]:
# TODO might be worth caching this, but it need to only be cached once the content is finalized...
for item in self.game_items.values():
if tag in item.tags:
yield item
@dataclass(frozen=True)
class StardewFeatures:
booksanity: booksanity.BooksanityFeature
cropsanity: cropsanity.CropsanityFeature
fishsanity: fishsanity.FishsanityFeature
friendsanity: friendsanity.FriendsanityFeature
@dataclass(frozen=True)
class ContentPack:
name: str
dependencies: Iterable[str] = ()
""" Hard requirement, generation will fail if it's missing. """
weak_dependencies: Iterable[str] = ()
""" Not a strict dependency, only used only for ordering the packs to make sure hooks are applied correctly. """
# items
# def item_hook
# ...
harvest_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
"""Harvest sources contains both crops and forageables, but also fruits from trees, the cave farm and stuff harvested from tapping like maple syrup."""
def harvest_source_hook(self, content: StardewContent):
...
shop_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
def shop_source_hook(self, content: StardewContent):
...
fishes: Iterable[FishItem] = ()
def fish_hook(self, content: StardewContent):
...
crafting_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
def crafting_hook(self, content: StardewContent):
...
artisan_good_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
def artisan_good_hook(self, content: StardewContent):
...
villagers: Iterable[Villager] = ()
def villager_hook(self, content: StardewContent):
...
skills: Iterable[Skill] = ()
def skill_hook(self, content: StardewContent):
...
quests: Iterable[Any] = ()
def quest_hook(self, content: StardewContent):
...
def finalize_hook(self, content: StardewContent):
"""Last hook called on the pack, once all other content packs have been registered.
This is the place to do any final adjustments to the content, like adding rules based on tags applied by other packs.
"""
...

View File

@@ -0,0 +1,7 @@
from .game_content import ContentPack
by_mod = {}
def register_mod_content_pack(content_pack: ContentPack):
by_mod[content_pack.name] = content_pack

View File

@@ -0,0 +1,20 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...data.game_item import ItemTag, Tag
from ...data.shop import ShopSource
from ...data.skill import Skill
from ...mods.mod_data import ModNames
from ...strings.book_names import ModBook
from ...strings.region_names import LogicRegion
from ...strings.skill_names import ModSkill
register_mod_content_pack(ContentPack(
ModNames.archaeology,
shop_sources={
ModBook.digging_like_worms: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=500, shop_region=LogicRegion.bookseller_1),),
},
skills=(Skill(name=ModSkill.archaeology, has_mastery=False),),
))

View File

@@ -0,0 +1,7 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...mods.mod_data import ModNames
register_mod_content_pack(ContentPack(
ModNames.big_backpack,
))

View File

@@ -0,0 +1,13 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...data import villagers_data
from ...mods.mod_data import ModNames
register_mod_content_pack(ContentPack(
ModNames.boarding_house,
villagers=(
villagers_data.gregory,
villagers_data.sheila,
villagers_data.joel,
)
))

View File

@@ -0,0 +1,28 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...data.harvest import ForagingSource
from ...mods.mod_data import ModNames
from ...strings.crop_names import Fruit
from ...strings.flower_names import Flower
from ...strings.region_names import DeepWoodsRegion
from ...strings.season_names import Season
register_mod_content_pack(ContentPack(
ModNames.deepwoods,
harvest_sources={
# Deep enough to have seen such a tree at least once
Fruit.apple: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Fruit.apricot: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Fruit.cherry: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Fruit.orange: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Fruit.peach: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Fruit.pomegranate: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Fruit.mango: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Flower.tulip: (ForagingSource(seasons=Season.not_winter, regions=(DeepWoodsRegion.floor_10,)),),
Flower.blue_jazz: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
Flower.summer_spangle: (ForagingSource(seasons=Season.not_winter, regions=(DeepWoodsRegion.floor_10,)),),
Flower.poppy: (ForagingSource(seasons=Season.not_winter, regions=(DeepWoodsRegion.floor_10,)),),
Flower.fairy_rose: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
}
))

View File

@@ -0,0 +1,17 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...data import villagers_data, fish_data
from ...mods.mod_data import ModNames
register_mod_content_pack(ContentPack(
ModNames.distant_lands,
fishes=(
fish_data.void_minnow,
fish_data.purple_algae,
fish_data.swamp_leech,
fish_data.giant_horsehoe_crab,
),
villagers=(
villagers_data.zic,
)
))

View File

@@ -0,0 +1,14 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ..override import override
from ...data import villagers_data
from ...mods.mod_data import ModNames
register_mod_content_pack(ContentPack(
ModNames.jasper,
villagers=(
villagers_data.jasper,
override(villagers_data.gunther, mod_name=ModNames.jasper),
override(villagers_data.marlon, mod_name=ModNames.jasper),
)
))

View File

@@ -0,0 +1,10 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...data.skill import Skill
from ...mods.mod_data import ModNames
from ...strings.skill_names import ModSkill
register_mod_content_pack(ContentPack(
ModNames.magic,
skills=(Skill(name=ModSkill.magic, has_mastery=False),)
))

View File

@@ -0,0 +1,88 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...data import villagers_data
from ...mods.mod_data import ModNames
register_mod_content_pack(ContentPack(
ModNames.alec,
villagers=(
villagers_data.alec,
)
))
register_mod_content_pack(ContentPack(
ModNames.ayeisha,
villagers=(
villagers_data.ayeisha,
)
))
register_mod_content_pack(ContentPack(
ModNames.delores,
villagers=(
villagers_data.delores,
)
))
register_mod_content_pack(ContentPack(
ModNames.eugene,
villagers=(
villagers_data.eugene,
)
))
register_mod_content_pack(ContentPack(
ModNames.juna,
villagers=(
villagers_data.juna,
)
))
register_mod_content_pack(ContentPack(
ModNames.ginger,
villagers=(
villagers_data.kitty,
)
))
register_mod_content_pack(ContentPack(
ModNames.shiko,
villagers=(
villagers_data.shiko,
)
))
register_mod_content_pack(ContentPack(
ModNames.wellwick,
villagers=(
villagers_data.wellwick,
)
))
register_mod_content_pack(ContentPack(
ModNames.yoba,
villagers=(
villagers_data.yoba,
)
))
register_mod_content_pack(ContentPack(
ModNames.riley,
villagers=(
villagers_data.riley,
)
))
register_mod_content_pack(ContentPack(
ModNames.alecto,
villagers=(
villagers_data.alecto,
)
))
register_mod_content_pack(ContentPack(
ModNames.lacey,
villagers=(
villagers_data.lacey,
)
))

View File

@@ -0,0 +1,25 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...data.skill import Skill
from ...mods.mod_data import ModNames
from ...strings.skill_names import ModSkill
register_mod_content_pack(ContentPack(
ModNames.luck_skill,
skills=(Skill(name=ModSkill.luck, has_mastery=False),)
))
register_mod_content_pack(ContentPack(
ModNames.socializing_skill,
skills=(Skill(name=ModSkill.socializing, has_mastery=False),)
))
register_mod_content_pack(ContentPack(
ModNames.cooking_skill,
skills=(Skill(name=ModSkill.cooking, has_mastery=False),)
))
register_mod_content_pack(ContentPack(
ModNames.binning_skill,
skills=(Skill(name=ModSkill.binning, has_mastery=False),)
))

View File

@@ -0,0 +1,7 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...mods.mod_data import ModNames
register_mod_content_pack(ContentPack(
ModNames.skull_cavern_elevator,
))

View File

@@ -0,0 +1,126 @@
from ..game_content import ContentPack, StardewContent
from ..mod_registry import register_mod_content_pack
from ..override import override
from ..vanilla.ginger_island import ginger_island_content_pack as ginger_island_content_pack
from ...data import villagers_data, fish_data
from ...data.harvest import ForagingSource
from ...data.requirement import YearRequirement
from ...mods.mod_data import ModNames
from ...strings.crop_names import Fruit
from ...strings.fish_names import WaterItem
from ...strings.flower_names import Flower
from ...strings.forageable_names import Mushroom, Forageable
from ...strings.region_names import Region, SVERegion
from ...strings.season_names import Season
class SVEContentPack(ContentPack):
def fish_hook(self, content: StardewContent):
if ginger_island_content_pack.name not in content.registered_packs:
content.fishes.pop(fish_data.baby_lunaloo.name)
content.fishes.pop(fish_data.clownfish.name)
content.fishes.pop(fish_data.lunaloo.name)
content.fishes.pop(fish_data.seahorse.name)
content.fishes.pop(fish_data.shiny_lunaloo.name)
content.fishes.pop(fish_data.starfish.name)
content.fishes.pop(fish_data.sea_sponge.name)
# Remove Highlands fishes at it requires 2 Lance hearts for the quest to access it
content.fishes.pop(fish_data.daggerfish.name)
content.fishes.pop(fish_data.gemfish.name)
# Remove Fable Reef fishes at it requires 8 Lance hearts for the event to access it
content.fishes.pop(fish_data.torpedo_trout.name)
def villager_hook(self, content: StardewContent):
if ginger_island_content_pack.name not in content.registered_packs:
# Remove Lance if Ginger Island is not in content since he is first encountered in Volcano Forge
content.villagers.pop(villagers_data.lance.name)
register_mod_content_pack(SVEContentPack(
ModNames.sve,
weak_dependencies=(
ginger_island_content_pack.name,
ModNames.jasper, # To override Marlon and Gunther
),
harvest_sources={
Mushroom.red: (
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.summer, Season.fall)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
),
Mushroom.purple: (
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
),
Mushroom.morel: (
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
),
Mushroom.chanterelle: (
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
),
Flower.tulip: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring,)),),
Flower.blue_jazz: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring,)),),
Flower.summer_spangle: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.summer,)),),
Flower.sunflower: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.summer,)),),
Flower.fairy_rose: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.fall,)),),
Fruit.ancient_fruit: (
ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring, Season.summer, Season.fall), other_requirements=(YearRequirement(3),)),
ForagingSource(regions=(SVERegion.sprite_spring_cave,)),
),
Fruit.sweet_gem_berry: (
ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring, Season.summer, Season.fall), other_requirements=(YearRequirement(3),)),
),
# Fable Reef
WaterItem.coral: (ForagingSource(regions=(SVERegion.fable_reef,)),),
Forageable.rainbow_shell: (ForagingSource(regions=(SVERegion.fable_reef,)),),
WaterItem.sea_urchin: (ForagingSource(regions=(SVERegion.fable_reef,)),),
},
fishes=(
fish_data.baby_lunaloo, # Removed when no ginger island
fish_data.bonefish,
fish_data.bull_trout,
fish_data.butterfish,
fish_data.clownfish, # Removed when no ginger island
fish_data.daggerfish,
fish_data.frog,
fish_data.gemfish,
fish_data.goldenfish,
fish_data.grass_carp,
fish_data.king_salmon,
fish_data.kittyfish,
fish_data.lunaloo, # Removed when no ginger island
fish_data.meteor_carp,
fish_data.minnow,
fish_data.puppyfish,
fish_data.radioactive_bass,
fish_data.seahorse, # Removed when no ginger island
fish_data.shiny_lunaloo, # Removed when no ginger island
fish_data.snatcher_worm,
fish_data.starfish, # Removed when no ginger island
fish_data.torpedo_trout,
fish_data.undeadfish,
fish_data.void_eel,
fish_data.water_grub,
fish_data.sea_sponge, # Removed when no ginger island
),
villagers=(
villagers_data.claire,
villagers_data.lance, # Removed when no ginger island
villagers_data.mommy,
villagers_data.sophia,
villagers_data.victor,
villagers_data.andy,
villagers_data.apples,
villagers_data.gunther,
villagers_data.martin,
villagers_data.marlon,
villagers_data.morgan,
villagers_data.scarlett,
villagers_data.susan,
villagers_data.morris,
# The wizard leaves his tower on sunday, for like 1 hour... Good enough for entrance rando!
override(villagers_data.wizard, locations=(Region.wizard_tower, Region.forest), bachelor=True, mod_name=ModNames.sve),
)
))

View File

@@ -0,0 +1,7 @@
from ..game_content import ContentPack
from ..mod_registry import register_mod_content_pack
from ...mods.mod_data import ModNames
register_mod_content_pack(ContentPack(
ModNames.tractor,
))

View File

@@ -0,0 +1,7 @@
from typing import Any
def override(content: Any, **kwargs) -> Any:
attributes = dict(content.__dict__)
attributes.update(kwargs)
return type(content)(**attributes)

View File

@@ -0,0 +1,97 @@
from __future__ import annotations
from typing import Iterable, Mapping, Callable
from .game_content import StardewContent, ContentPack, StardewFeatures
from .vanilla.base import base_game as base_game_content_pack
from ..data.game_item import GameItem, ItemSource
try:
from graphlib import TopologicalSorter
except ImportError:
from graphlib_backport import TopologicalSorter # noqa
def unpack_content(features: StardewFeatures, packs: Iterable[ContentPack]) -> StardewContent:
# Base game is always registered first.
content = StardewContent(features)
packs_to_finalize = [base_game_content_pack]
register_pack(content, base_game_content_pack)
# Content packs are added in order based on their dependencies
sorter = TopologicalSorter()
packs_by_name = {p.name: p for p in packs}
# Build the dependency graph
for name, pack in packs_by_name.items():
sorter.add(name,
*pack.dependencies,
*(wd for wd in pack.weak_dependencies if wd in packs_by_name))
# Graph is traversed in BFS
sorter.prepare()
while sorter.is_active():
# Packs get shuffled in TopologicalSorter, most likely due to hash seeding.
for pack_name in sorted(sorter.get_ready()):
pack = packs_by_name[pack_name]
register_pack(content, pack)
sorter.done(pack_name)
packs_to_finalize.append(pack)
prune_inaccessible_items(content)
for pack in packs_to_finalize:
pack.finalize_hook(content)
# Maybe items without source should be removed at some point
return content
def register_pack(content: StardewContent, pack: ContentPack):
# register regions
# register entrances
register_sources_and_call_hook(content, pack.harvest_sources, pack.harvest_source_hook)
register_sources_and_call_hook(content, pack.shop_sources, pack.shop_source_hook)
register_sources_and_call_hook(content, pack.crafting_sources, pack.crafting_hook)
register_sources_and_call_hook(content, pack.artisan_good_sources, pack.artisan_good_hook)
for fish in pack.fishes:
content.fishes[fish.name] = fish
pack.fish_hook(content)
for villager in pack.villagers:
content.villagers[villager.name] = villager
pack.villager_hook(content)
for skill in pack.skills:
content.skills[skill.name] = skill
pack.skill_hook(content)
# register_quests
# ...
content.registered_packs.add(pack.name)
def register_sources_and_call_hook(content: StardewContent,
sources_by_item_name: Mapping[str, Iterable[ItemSource]],
hook: Callable[[StardewContent], None]):
for item_name, sources in sources_by_item_name.items():
item = content.game_items.setdefault(item_name, GameItem(item_name))
item.add_sources(sources)
for source in sources:
for requirement_name, tags in source.requirement_tags.items():
requirement_item = content.game_items.setdefault(requirement_name, GameItem(requirement_name))
requirement_item.add_tags(tags)
hook(content)
def prune_inaccessible_items(content: StardewContent):
for item in list(content.game_items.values()):
if not item.sources:
content.game_items.pop(item.name)

View File

@@ -0,0 +1,172 @@
from ..game_content import ContentPack, StardewContent
from ...data.artisan import MachineSource
from ...data.game_item import ItemTag, CustomRuleSource, GameItem
from ...data.harvest import HarvestFruitTreeSource, HarvestCropSource
from ...data.skill import Skill
from ...strings.artisan_good_names import ArtisanGood
from ...strings.craftable_names import WildSeeds
from ...strings.crop_names import Fruit, Vegetable
from ...strings.flower_names import Flower
from ...strings.food_names import Beverage
from ...strings.forageable_names import all_edible_mushrooms, Mushroom, Forageable
from ...strings.fruit_tree_names import Sapling
from ...strings.machine_names import Machine
from ...strings.monster_names import Monster
from ...strings.season_names import Season
from ...strings.seed_names import Seed
from ...strings.skill_names import Skill as SkillName
all_fruits = (
Fruit.ancient_fruit, Fruit.apple, Fruit.apricot, Fruit.banana, Forageable.blackberry, Fruit.blueberry, Forageable.cactus_fruit, Fruit.cherry,
Forageable.coconut, Fruit.cranberries, Forageable.crystal_fruit, Fruit.grape, Fruit.hot_pepper, Fruit.mango, Fruit.melon, Fruit.orange, Fruit.peach,
Fruit.pineapple, Fruit.pomegranate, Fruit.powdermelon, Fruit.qi_fruit, Fruit.rhubarb, Forageable.salmonberry, Forageable.spice_berry, Fruit.starfruit,
Fruit.strawberry
)
all_vegetables = (
Vegetable.amaranth, Vegetable.artichoke, Vegetable.beet, Vegetable.bok_choy, Vegetable.broccoli, Vegetable.carrot, Vegetable.cauliflower,
Vegetable.corn, Vegetable.eggplant, Forageable.fiddlehead_fern, Vegetable.garlic, Vegetable.green_bean, Vegetable.hops, Vegetable.kale,
Vegetable.parsnip, Vegetable.potato, Vegetable.pumpkin, Vegetable.radish, Vegetable.red_cabbage, Vegetable.summer_squash, Vegetable.taro_root,
Vegetable.tea_leaves, Vegetable.tomato, Vegetable.unmilled_rice, Vegetable.wheat, Vegetable.yam
)
non_juiceable_vegetables = (Vegetable.hops, Vegetable.tea_leaves, Vegetable.wheat, Vegetable.tea_leaves)
# This will hold items, skills and stuff that is available everywhere across the game, but not directly needing pelican town (crops, ore, foraging, etc.)
class BaseGameContentPack(ContentPack):
def harvest_source_hook(self, content: StardewContent):
coffee_starter = content.game_items[Seed.coffee_starter]
content.game_items[Seed.coffee_starter] = GameItem(Seed.coffee, sources=coffee_starter.sources, tags=coffee_starter.tags)
content.untag_item(WildSeeds.ancient, ItemTag.CROPSANITY_SEED)
for fruit in all_fruits:
content.tag_item(fruit, ItemTag.FRUIT)
for vegetable in all_vegetables:
content.tag_item(vegetable, ItemTag.VEGETABLE)
for edible_mushroom in all_edible_mushrooms:
if edible_mushroom == Mushroom.magma_cap:
continue
content.tag_item(edible_mushroom, ItemTag.EDIBLE_MUSHROOM)
def finalize_hook(self, content: StardewContent):
# FIXME I hate this design. A listener design pattern would be more appropriate so artisan good are register at the exact moment a FRUIT tag is added.
for fruit in tuple(content.find_tagged_items(ItemTag.FRUIT)):
wine = ArtisanGood.specific_wine(fruit.name)
content.source_item(wine, MachineSource(item=fruit.name, machine=Machine.keg))
content.source_item(ArtisanGood.wine, MachineSource(item=fruit.name, machine=Machine.keg))
if fruit.name == Fruit.grape:
content.source_item(ArtisanGood.raisins, MachineSource(item=fruit.name, machine=Machine.dehydrator))
else:
dried_fruit = ArtisanGood.specific_dried_fruit(fruit.name)
content.source_item(dried_fruit, MachineSource(item=fruit.name, machine=Machine.dehydrator))
content.source_item(ArtisanGood.dried_fruit, MachineSource(item=fruit.name, machine=Machine.dehydrator))
jelly = ArtisanGood.specific_jelly(fruit.name)
content.source_item(jelly, MachineSource(item=fruit.name, machine=Machine.preserves_jar))
content.source_item(ArtisanGood.jelly, MachineSource(item=fruit.name, machine=Machine.preserves_jar))
for vegetable in tuple(content.find_tagged_items(ItemTag.VEGETABLE)):
if vegetable.name not in non_juiceable_vegetables:
juice = ArtisanGood.specific_juice(vegetable.name)
content.source_item(juice, MachineSource(item=vegetable.name, machine=Machine.keg))
content.source_item(ArtisanGood.juice, MachineSource(item=vegetable.name, machine=Machine.keg))
pickles = ArtisanGood.specific_pickles(vegetable.name)
content.source_item(pickles, MachineSource(item=vegetable.name, machine=Machine.preserves_jar))
content.source_item(ArtisanGood.pickles, MachineSource(item=vegetable.name, machine=Machine.preserves_jar))
for mushroom in tuple(content.find_tagged_items(ItemTag.EDIBLE_MUSHROOM)):
dried_mushroom = ArtisanGood.specific_dried_mushroom(mushroom.name)
content.source_item(dried_mushroom, MachineSource(item=mushroom.name, machine=Machine.dehydrator))
content.source_item(ArtisanGood.dried_mushroom, MachineSource(item=mushroom.name, machine=Machine.dehydrator))
# for fish in tuple(content.find_tagged_items(ItemTag.FISH)):
# smoked_fish = ArtisanGood.specific_smoked_fish(fish.name)
# content.source_item(smoked_fish, MachineSource(item=fish.name, machine=Machine.fish_smoker))
# content.source_item(ArtisanGood.smoked_fish, MachineSource(item=fish.name, machine=Machine.fish_smoker))
base_game = BaseGameContentPack(
"Base game (Vanilla)",
harvest_sources={
# Fruit tree
Fruit.apple: (HarvestFruitTreeSource(sapling=Sapling.apple, seasons=(Season.fall,)),),
Fruit.apricot: (HarvestFruitTreeSource(sapling=Sapling.apricot, seasons=(Season.spring,)),),
Fruit.cherry: (HarvestFruitTreeSource(sapling=Sapling.cherry, seasons=(Season.spring,)),),
Fruit.orange: (HarvestFruitTreeSource(sapling=Sapling.orange, seasons=(Season.summer,)),),
Fruit.peach: (HarvestFruitTreeSource(sapling=Sapling.peach, seasons=(Season.summer,)),),
Fruit.pomegranate: (HarvestFruitTreeSource(sapling=Sapling.pomegranate, seasons=(Season.fall,)),),
# Crops
Vegetable.parsnip: (HarvestCropSource(seed=Seed.parsnip, seasons=(Season.spring,)),),
Vegetable.green_bean: (HarvestCropSource(seed=Seed.bean, seasons=(Season.spring,)),),
Vegetable.cauliflower: (HarvestCropSource(seed=Seed.cauliflower, seasons=(Season.spring,)),),
Vegetable.potato: (HarvestCropSource(seed=Seed.potato, seasons=(Season.spring,)),),
Flower.tulip: (HarvestCropSource(seed=Seed.tulip, seasons=(Season.spring,)),),
Vegetable.kale: (HarvestCropSource(seed=Seed.kale, seasons=(Season.spring,)),),
Flower.blue_jazz: (HarvestCropSource(seed=Seed.jazz, seasons=(Season.spring,)),),
Vegetable.garlic: (HarvestCropSource(seed=Seed.garlic, seasons=(Season.spring,)),),
Vegetable.unmilled_rice: (HarvestCropSource(seed=Seed.rice, seasons=(Season.spring,)),),
Fruit.melon: (HarvestCropSource(seed=Seed.melon, seasons=(Season.summer,)),),
Vegetable.tomato: (HarvestCropSource(seed=Seed.tomato, seasons=(Season.summer,)),),
Fruit.blueberry: (HarvestCropSource(seed=Seed.blueberry, seasons=(Season.summer,)),),
Fruit.hot_pepper: (HarvestCropSource(seed=Seed.pepper, seasons=(Season.summer,)),),
Vegetable.wheat: (HarvestCropSource(seed=Seed.wheat, seasons=(Season.summer, Season.fall)),),
Vegetable.radish: (HarvestCropSource(seed=Seed.radish, seasons=(Season.summer,)),),
Flower.poppy: (HarvestCropSource(seed=Seed.poppy, seasons=(Season.summer,)),),
Flower.summer_spangle: (HarvestCropSource(seed=Seed.spangle, seasons=(Season.summer,)),),
Vegetable.hops: (HarvestCropSource(seed=Seed.hops, seasons=(Season.summer,)),),
Vegetable.corn: (HarvestCropSource(seed=Seed.corn, seasons=(Season.summer, Season.fall)),),
Flower.sunflower: (HarvestCropSource(seed=Seed.sunflower, seasons=(Season.summer, Season.fall)),),
Vegetable.red_cabbage: (HarvestCropSource(seed=Seed.red_cabbage, seasons=(Season.summer,)),),
Vegetable.eggplant: (HarvestCropSource(seed=Seed.eggplant, seasons=(Season.fall,)),),
Vegetable.pumpkin: (HarvestCropSource(seed=Seed.pumpkin, seasons=(Season.fall,)),),
Vegetable.bok_choy: (HarvestCropSource(seed=Seed.bok_choy, seasons=(Season.fall,)),),
Vegetable.yam: (HarvestCropSource(seed=Seed.yam, seasons=(Season.fall,)),),
Fruit.cranberries: (HarvestCropSource(seed=Seed.cranberry, seasons=(Season.fall,)),),
Flower.fairy_rose: (HarvestCropSource(seed=Seed.fairy, seasons=(Season.fall,)),),
Vegetable.amaranth: (HarvestCropSource(seed=Seed.amaranth, seasons=(Season.fall,)),),
Fruit.grape: (HarvestCropSource(seed=Seed.grape, seasons=(Season.fall,)),),
Vegetable.artichoke: (HarvestCropSource(seed=Seed.artichoke, seasons=(Season.fall,)),),
Vegetable.broccoli: (HarvestCropSource(seed=Seed.broccoli, seasons=(Season.fall,)),),
Vegetable.carrot: (HarvestCropSource(seed=Seed.carrot, seasons=(Season.spring,)),),
Fruit.powdermelon: (HarvestCropSource(seed=Seed.powdermelon, seasons=(Season.summer,)),),
Vegetable.summer_squash: (HarvestCropSource(seed=Seed.summer_squash, seasons=(Season.summer,)),),
Fruit.strawberry: (HarvestCropSource(seed=Seed.strawberry, seasons=(Season.spring,)),),
Fruit.sweet_gem_berry: (HarvestCropSource(seed=Seed.rare_seed, seasons=(Season.fall,)),),
Fruit.ancient_fruit: (HarvestCropSource(seed=WildSeeds.ancient, seasons=(Season.spring, Season.summer, Season.fall,)),),
Seed.coffee_starter: (CustomRuleSource(lambda logic: logic.traveling_merchant.has_days(3) & logic.monster.can_kill_many(Monster.dust_sprite)),),
Seed.coffee: (HarvestCropSource(seed=Seed.coffee_starter, seasons=(Season.spring, Season.summer,)),),
Vegetable.tea_leaves: (CustomRuleSource(lambda logic: logic.has(Sapling.tea) & logic.time.has_lived_months(2) & logic.season.has_any_not_winter()),),
},
artisan_good_sources={
Beverage.beer: (MachineSource(item=Vegetable.wheat, machine=Machine.keg),),
# Ingredient.vinegar: (MachineSource(item=Ingredient.rice, machine=Machine.keg),),
Beverage.coffee: (MachineSource(item=Seed.coffee, machine=Machine.keg),
CustomRuleSource(lambda logic: logic.has(Machine.coffee_maker)),
CustomRuleSource(lambda logic: logic.has("Hot Java Ring"))),
ArtisanGood.green_tea: (MachineSource(item=Vegetable.tea_leaves, machine=Machine.keg),),
ArtisanGood.mead: (MachineSource(item=ArtisanGood.honey, machine=Machine.keg),),
ArtisanGood.pale_ale: (MachineSource(item=Vegetable.hops, machine=Machine.keg),),
},
skills=(
Skill(SkillName.farming, has_mastery=True),
Skill(SkillName.foraging, has_mastery=True),
Skill(SkillName.fishing, has_mastery=True),
Skill(SkillName.mining, has_mastery=True),
Skill(SkillName.combat, has_mastery=True),
)
)

View File

@@ -0,0 +1,81 @@
from .pelican_town import pelican_town as pelican_town_content_pack
from ..game_content import ContentPack, StardewContent
from ...data import villagers_data, fish_data
from ...data.game_item import ItemTag, Tag
from ...data.harvest import ForagingSource, HarvestFruitTreeSource, HarvestCropSource
from ...data.shop import ShopSource
from ...strings.book_names import Book
from ...strings.crop_names import Fruit, Vegetable
from ...strings.fish_names import Fish
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.fruit_tree_names import Sapling
from ...strings.metal_names import Fossil, Mineral
from ...strings.region_names import Region
from ...strings.season_names import Season
from ...strings.seed_names import Seed
class GingerIslandContentPack(ContentPack):
def harvest_source_hook(self, content: StardewContent):
content.tag_item(Fruit.banana, ItemTag.FRUIT)
content.tag_item(Fruit.pineapple, ItemTag.FRUIT)
content.tag_item(Fruit.mango, ItemTag.FRUIT)
content.tag_item(Vegetable.taro_root, ItemTag.VEGETABLE)
content.tag_item(Mushroom.magma_cap, ItemTag.EDIBLE_MUSHROOM)
ginger_island_content_pack = GingerIslandContentPack(
"Ginger Island (Vanilla)",
weak_dependencies=(
pelican_town_content_pack.name,
),
harvest_sources={
# Foraging
Forageable.dragon_tooth: (
ForagingSource(regions=(Region.volcano_floor_10,)),
),
Forageable.ginger: (
ForagingSource(regions=(Region.island_west,)),
),
Mushroom.magma_cap: (
ForagingSource(regions=(Region.volcano_floor_5,)),
),
# Fruit tree
Fruit.banana: (HarvestFruitTreeSource(sapling=Sapling.banana, seasons=(Season.summer,)),),
Fruit.mango: (HarvestFruitTreeSource(sapling=Sapling.mango, seasons=(Season.summer,)),),
# Crop
Vegetable.taro_root: (HarvestCropSource(seed=Seed.taro, seasons=(Season.summer,)),),
Fruit.pineapple: (HarvestCropSource(seed=Seed.pineapple, seasons=(Season.summer,)),),
},
shop_sources={
Seed.taro: (ShopSource(items_price=((2, Fossil.bone_fragment),), shop_region=Region.island_trader),),
Seed.pineapple: (ShopSource(items_price=((1, Mushroom.magma_cap),), shop_region=Region.island_trader),),
Sapling.banana: (ShopSource(items_price=((5, Forageable.dragon_tooth),), shop_region=Region.island_trader),),
Sapling.mango: (ShopSource(items_price=((75, Fish.mussel_node),), shop_region=Region.island_trader),),
# This one is 10 diamonds, should maybe add time?
Book.the_diamond_hunter: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(items_price=((10, Mineral.diamond),), shop_region=Region.volcano_dwarf_shop),
),
},
fishes=(
# TODO override region so no need to add inaccessible regions in logic
fish_data.blue_discus,
fish_data.lionfish,
fish_data.midnight_carp,
fish_data.pufferfish,
fish_data.stingray,
fish_data.super_cucumber,
fish_data.tilapia,
fish_data.tuna
),
villagers=(
villagers_data.leo,
)
)

View File

@@ -0,0 +1,393 @@
from ..game_content import ContentPack
from ...data import villagers_data, fish_data
from ...data.game_item import GenericSource, ItemTag, Tag, CustomRuleSource
from ...data.harvest import ForagingSource, SeasonalForagingSource, ArtifactSpotSource
from ...data.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement
from ...data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource
from ...strings.book_names import Book
from ...strings.crop_names import Fruit
from ...strings.fish_names import WaterItem
from ...strings.food_names import Beverage, Meal
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.fruit_tree_names import Sapling
from ...strings.generic_names import Generic
from ...strings.material_names import Material
from ...strings.region_names import Region, LogicRegion
from ...strings.season_names import Season
from ...strings.seed_names import Seed, TreeSeed
from ...strings.skill_names import Skill
from ...strings.tool_names import Tool, ToolMaterial
pelican_town = ContentPack(
"Pelican Town (Vanilla)",
harvest_sources={
# Spring
Forageable.daffodil: (
ForagingSource(seasons=(Season.spring,), regions=(Region.bus_stop, Region.town, Region.railroad)),
),
Forageable.dandelion: (
ForagingSource(seasons=(Season.spring,), regions=(Region.bus_stop, Region.forest, Region.railroad)),
),
Forageable.leek: (
ForagingSource(seasons=(Season.spring,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
),
Forageable.wild_horseradish: (
ForagingSource(seasons=(Season.spring,), regions=(Region.backwoods, Region.mountain, Region.forest, Region.secret_woods)),
),
Forageable.salmonberry: (
SeasonalForagingSource(season=Season.spring, days=(15, 16, 17, 18),
regions=(Region.backwoods, Region.mountain, Region.town, Region.forest, Region.tunnel_entrance, Region.railroad)),
),
Forageable.spring_onion: (
ForagingSource(seasons=(Season.spring,), regions=(Region.forest,)),
),
# Summer
Fruit.grape: (
ForagingSource(seasons=(Season.summer,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
),
Forageable.spice_berry: (
ForagingSource(seasons=(Season.summer,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.forest, Region.railroad)),
),
Forageable.sweet_pea: (
ForagingSource(seasons=(Season.summer,), regions=(Region.bus_stop, Region.town, Region.forest, Region.railroad)),
),
Forageable.fiddlehead_fern: (
ForagingSource(seasons=(Season.summer,), regions=(Region.secret_woods,)),
),
# Fall
Forageable.blackberry: (
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.town, Region.forest, Region.railroad)),
SeasonalForagingSource(season=Season.fall, days=(8, 9, 10, 11),
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.tunnel_entrance,
Region.railroad)),
),
Forageable.hazelnut: (
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
),
Forageable.wild_plum: (
ForagingSource(seasons=(Season.fall,), regions=(Region.mountain, Region.bus_stop, Region.railroad)),
),
# Winter
Forageable.crocus: (
ForagingSource(seasons=(Season.winter,),
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.secret_woods)),
),
Forageable.crystal_fruit: (
ForagingSource(seasons=(Season.winter,),
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad)),
),
Forageable.holly: (
ForagingSource(seasons=(Season.winter,),
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad)),
),
Forageable.snow_yam: (
ForagingSource(seasons=(Season.winter,),
regions=(Region.farm, Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad,
Region.secret_woods, Region.beach),
other_requirements=(ToolRequirement(Tool.hoe),)),
),
Forageable.winter_root: (
ForagingSource(seasons=(Season.winter,),
regions=(Region.farm, Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad,
Region.secret_woods, Region.beach),
other_requirements=(ToolRequirement(Tool.hoe),)),
),
# Mushrooms
Mushroom.common: (
ForagingSource(seasons=(Season.spring,), regions=(Region.secret_woods,)),
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.mountain, Region.forest)),
),
Mushroom.chanterelle: (
ForagingSource(seasons=(Season.fall,), regions=(Region.secret_woods,)),
),
Mushroom.morel: (
ForagingSource(seasons=(Season.spring, Season.fall), regions=(Region.secret_woods,)),
),
Mushroom.red: (
ForagingSource(seasons=(Season.summer, Season.fall), regions=(Region.secret_woods,)),
),
# Beach
WaterItem.coral: (
ForagingSource(regions=(Region.tide_pools,)),
SeasonalForagingSource(season=Season.summer, days=(12, 13, 14), regions=(Region.beach,)),
),
WaterItem.nautilus_shell: (
ForagingSource(seasons=(Season.winter,), regions=(Region.beach,)),
),
Forageable.rainbow_shell: (
ForagingSource(seasons=(Season.summer,), regions=(Region.beach,)),
),
WaterItem.sea_urchin: (
ForagingSource(regions=(Region.tide_pools,)),
),
Seed.mixed: (
ForagingSource(seasons=(Season.spring, Season.summer, Season.fall,), regions=(Region.town, Region.farm, Region.forest)),
),
Seed.mixed_flower: (
ForagingSource(seasons=(Season.summer,), regions=(Region.town, Region.farm, Region.forest)),
),
# Books
Book.jack_be_nimble_jack_be_thick: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ArtifactSpotSource(amount=22),), # After 22 spots, there are 50.48% chances player received the book.
Book.woodys_secret: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
GenericSource(regions=(Region.forest, Region.mountain),
other_requirements=(ToolRequirement(Tool.axe, ToolMaterial.iron), SkillRequirement(Skill.foraging, 5))),),
},
shop_sources={
# Saplings
Sapling.apple: (ShopSource(money_price=4000, shop_region=Region.pierre_store),),
Sapling.apricot: (ShopSource(money_price=2000, shop_region=Region.pierre_store),),
Sapling.cherry: (ShopSource(money_price=3400, shop_region=Region.pierre_store),),
Sapling.orange: (ShopSource(money_price=4000, shop_region=Region.pierre_store),),
Sapling.peach: (ShopSource(money_price=6000, shop_region=Region.pierre_store),),
Sapling.pomegranate: (ShopSource(money_price=6000, shop_region=Region.pierre_store),),
# Crop seeds, assuming they are bought in season, otherwise price is different with missing stock list.
Seed.parsnip: (ShopSource(money_price=20, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.bean: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.cauliflower: (ShopSource(money_price=80, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.potato: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.tulip: (ShopSource(money_price=20, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.kale: (ShopSource(money_price=70, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.jazz: (ShopSource(money_price=30, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.garlic: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.rice: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.melon: (ShopSource(money_price=80, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.tomato: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.blueberry: (ShopSource(money_price=80, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.pepper: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.wheat: (ShopSource(money_price=10, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
Seed.radish: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.poppy: (ShopSource(money_price=100, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.spangle: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.hops: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.corn: (ShopSource(money_price=150, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
Seed.sunflower: (ShopSource(money_price=200, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
Seed.red_cabbage: (ShopSource(money_price=100, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.eggplant: (ShopSource(money_price=20, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.pumpkin: (ShopSource(money_price=100, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.bok_choy: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.yam: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.cranberry: (ShopSource(money_price=240, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.fairy: (ShopSource(money_price=200, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.amaranth: (ShopSource(money_price=70, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.grape: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.artichoke: (ShopSource(money_price=30, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.broccoli: (ShopSource(items_price=((5, Material.moss),), shop_region=LogicRegion.raccoon_shop),),
Seed.carrot: (ShopSource(items_price=((1, TreeSeed.maple),), shop_region=LogicRegion.raccoon_shop),),
Seed.powdermelon: (ShopSource(items_price=((2, TreeSeed.acorn),), shop_region=LogicRegion.raccoon_shop),),
Seed.summer_squash: (ShopSource(items_price=((15, Material.sap),), shop_region=LogicRegion.raccoon_shop),),
Seed.strawberry: (ShopSource(money_price=100, shop_region=LogicRegion.egg_festival, seasons=(Season.spring,)),),
Seed.rare_seed: (ShopSource(money_price=1000, shop_region=LogicRegion.traveling_cart, seasons=(Season.spring, Season.summer)),),
# Saloon
Beverage.beer: (ShopSource(money_price=400, shop_region=Region.saloon),),
Meal.salad: (ShopSource(money_price=220, shop_region=Region.saloon),),
Meal.bread: (ShopSource(money_price=100, shop_region=Region.saloon),),
Meal.spaghetti: (ShopSource(money_price=240, shop_region=Region.saloon),),
Meal.pizza: (ShopSource(money_price=600, shop_region=Region.saloon),),
Beverage.coffee: (ShopSource(money_price=300, shop_region=Region.saloon),),
# Books
Book.animal_catalogue: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=5000, shop_region=Region.ranch),),
Book.book_of_mysteries: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
MysteryBoxSource(amount=38),), # After 38 boxes, there are 49.99% chances player received the book.
Book.dwarvish_safety_manual: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=4000, shop_region=LogicRegion.mines_dwarf_shop),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.friendship_101: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
PrizeMachineSource(amount=9),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.horse_the_book: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=25000, shop_region=LogicRegion.bookseller_2),),
Book.jack_be_nimble_jack_be_thick: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.jewels_of_the_sea: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
FishingTreasureChestSource(amount=21), # After 21 chests, there are 49.44% chances player received the book.
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.mapping_cave_systems: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
GenericSource(regions=Region.adventurer_guild_bedroom),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.monster_compendium: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
CustomRuleSource(create_rule=lambda logic: logic.monster.can_kill_many(Generic.any)),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.ol_slitherlegs: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=25000, shop_region=LogicRegion.bookseller_2),),
Book.price_catalogue: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=3000, shop_region=LogicRegion.bookseller_2),),
Book.the_alleyway_buffet: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
GenericSource(regions=Region.town,
other_requirements=(ToolRequirement(Tool.axe, ToolMaterial.iron), ToolRequirement(Tool.pickaxe, ToolMaterial.iron))),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.the_art_o_crabbing: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
GenericSource(regions=Region.beach,
other_requirements=(ToolRequirement(Tool.fishing_rod, ToolMaterial.iridium),
SkillRequirement(Skill.fishing, 6),
SeasonRequirement(Season.winter))),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.treasure_appraisal_guide: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ArtifactTroveSource(amount=18), # After 18 troves, there is 49,88% chances player received the book.
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.raccoon_journal: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),
ShopSource(items_price=((999, Material.fiber),), shop_region=LogicRegion.raccoon_shop),),
Book.way_of_the_wind_pt_1: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=15000, shop_region=LogicRegion.bookseller_2),),
Book.way_of_the_wind_pt_2: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=35000, shop_region=LogicRegion.bookseller_2, other_requirements=(BookRequirement(Book.way_of_the_wind_pt_1),)),),
Book.woodys_secret: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
# Experience Books
Book.book_of_stars: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
Book.bait_and_bobber: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
Book.combat_quarterly: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
Book.mining_monthly: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
Book.stardew_valley_almanac: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
Book.woodcutters_weekly: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
Book.queen_of_sauce_cookbook: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=50000, shop_region=LogicRegion.bookseller_2),), # Worst book ever
},
fishes=(
fish_data.albacore,
fish_data.anchovy,
fish_data.bream,
fish_data.bullhead,
fish_data.carp,
fish_data.catfish,
fish_data.chub,
fish_data.dorado,
fish_data.eel,
fish_data.flounder,
fish_data.goby,
fish_data.halibut,
fish_data.herring,
fish_data.largemouth_bass,
fish_data.lingcod,
fish_data.midnight_carp, # Ginger island override
fish_data.octopus,
fish_data.perch,
fish_data.pike,
fish_data.pufferfish, # Ginger island override
fish_data.rainbow_trout,
fish_data.red_mullet,
fish_data.red_snapper,
fish_data.salmon,
fish_data.sardine,
fish_data.sea_cucumber,
fish_data.shad,
fish_data.slimejack,
fish_data.smallmouth_bass,
fish_data.squid,
fish_data.sturgeon,
fish_data.sunfish,
fish_data.super_cucumber, # Ginger island override
fish_data.tiger_trout,
fish_data.tilapia, # Ginger island override
fish_data.tuna, # Ginger island override
fish_data.void_salmon,
fish_data.walleye,
fish_data.woodskip,
fish_data.blobfish,
fish_data.midnight_squid,
fish_data.spook_fish,
# Legendaries
fish_data.angler,
fish_data.crimsonfish,
fish_data.glacierfish,
fish_data.legend,
fish_data.mutant_carp,
# Crab pot
fish_data.clam,
fish_data.cockle,
fish_data.crab,
fish_data.crayfish,
fish_data.lobster,
fish_data.mussel,
fish_data.oyster,
fish_data.periwinkle,
fish_data.shrimp,
fish_data.snail,
),
villagers=(
villagers_data.josh,
villagers_data.elliott,
villagers_data.harvey,
villagers_data.sam,
villagers_data.sebastian,
villagers_data.shane,
villagers_data.abigail,
villagers_data.emily,
villagers_data.haley,
villagers_data.leah,
villagers_data.maru,
villagers_data.penny,
villagers_data.caroline,
villagers_data.clint,
villagers_data.demetrius,
villagers_data.evelyn,
villagers_data.george,
villagers_data.gus,
villagers_data.jas,
villagers_data.jodi,
villagers_data.kent,
villagers_data.krobus,
villagers_data.lewis,
villagers_data.linus,
villagers_data.marnie,
villagers_data.pam,
villagers_data.pierre,
villagers_data.robin,
villagers_data.vincent,
villagers_data.willy,
villagers_data.wizard,
)
)

View File

@@ -0,0 +1,36 @@
from .ginger_island import ginger_island_content_pack as ginger_island_content_pack
from .pelican_town import pelican_town as pelican_town_content_pack
from ..game_content import ContentPack, StardewContent
from ...data import fish_data
from ...data.game_item import GenericSource, ItemTag
from ...data.harvest import HarvestCropSource
from ...strings.crop_names import Fruit
from ...strings.region_names import Region
from ...strings.season_names import Season
from ...strings.seed_names import Seed
class QiBoardContentPack(ContentPack):
def harvest_source_hook(self, content: StardewContent):
content.untag_item(Seed.qi_bean, ItemTag.CROPSANITY_SEED)
qi_board_content_pack = QiBoardContentPack(
"Qi Board (Vanilla)",
dependencies=(
pelican_town_content_pack.name,
ginger_island_content_pack.name,
),
harvest_sources={
# This one is a bit special, because it's only available during the special order, but it can be found from like, everywhere.
Seed.qi_bean: (GenericSource(regions=(Region.qi_walnut_room,)),),
Fruit.qi_fruit: (HarvestCropSource(seed=Seed.qi_bean),),
},
fishes=(
fish_data.ms_angler,
fish_data.son_of_crimsonfish,
fish_data.glacierfish_jr,
fish_data.legend_ii,
fish_data.radioactive_carp,
)
)

View File

@@ -0,0 +1,46 @@
from .pelican_town import pelican_town as pelican_town_content_pack
from ..game_content import ContentPack
from ...data import fish_data, villagers_data
from ...data.harvest import ForagingSource, HarvestCropSource
from ...data.shop import ShopSource
from ...strings.crop_names import Fruit, Vegetable
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.region_names import Region
from ...strings.season_names import Season
from ...strings.seed_names import Seed
the_desert = ContentPack(
"The Desert (Vanilla)",
dependencies=(
pelican_town_content_pack.name,
),
harvest_sources={
Forageable.cactus_fruit: (
ForagingSource(regions=(Region.desert,)),
HarvestCropSource(seed=Seed.cactus, seasons=())
),
Forageable.coconut: (
ForagingSource(regions=(Region.desert,)),
),
Mushroom.purple: (
ForagingSource(regions=(Region.skull_cavern_25,)),
),
Fruit.rhubarb: (HarvestCropSource(seed=Seed.rhubarb, seasons=(Season.spring,)),),
Fruit.starfruit: (HarvestCropSource(seed=Seed.starfruit, seasons=(Season.summer,)),),
Vegetable.beet: (HarvestCropSource(seed=Seed.beet, seasons=(Season.fall,)),),
},
shop_sources={
Seed.cactus: (ShopSource(money_price=150, shop_region=Region.oasis),),
Seed.rhubarb: (ShopSource(money_price=100, shop_region=Region.oasis, seasons=(Season.spring,)),),
Seed.starfruit: (ShopSource(money_price=400, shop_region=Region.oasis, seasons=(Season.summer,)),),
Seed.beet: (ShopSource(money_price=20, shop_region=Region.oasis, seasons=(Season.fall,)),),
},
fishes=(
fish_data.sandfish,
fish_data.scorpion_carp,
),
villagers=(
villagers_data.sandy,
),
)

View File

@@ -0,0 +1,43 @@
from .pelican_town import pelican_town as pelican_town_content_pack
from ..game_content import ContentPack
from ...data.harvest import FruitBatsSource, MushroomCaveSource
from ...strings.forageable_names import Forageable, Mushroom
the_farm = ContentPack(
"The Farm (Vanilla)",
dependencies=(
pelican_town_content_pack.name,
),
harvest_sources={
# Fruit cave
Forageable.blackberry: (
FruitBatsSource(),
),
Forageable.salmonberry: (
FruitBatsSource(),
),
Forageable.spice_berry: (
FruitBatsSource(),
),
Forageable.wild_plum: (
FruitBatsSource(),
),
# Mushrooms
Mushroom.common: (
MushroomCaveSource(),
),
Mushroom.chanterelle: (
MushroomCaveSource(),
),
Mushroom.morel: (
MushroomCaveSource(),
),
Mushroom.purple: (
MushroomCaveSource(),
),
Mushroom.red: (
MushroomCaveSource(),
),
}
)

View File

@@ -0,0 +1,35 @@
from .pelican_town import pelican_town as pelican_town_content_pack
from ..game_content import ContentPack
from ...data import fish_data, villagers_data
from ...data.harvest import ForagingSource
from ...data.requirement import ToolRequirement
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.region_names import Region
from ...strings.tool_names import Tool
the_mines = ContentPack(
"The Mines (Vanilla)",
dependencies=(
pelican_town_content_pack.name,
),
harvest_sources={
Forageable.cave_carrot: (
ForagingSource(regions=(Region.mines_floor_10,), other_requirements=(ToolRequirement(Tool.hoe),)),
),
Mushroom.red: (
ForagingSource(regions=(Region.mines_floor_95,)),
),
Mushroom.purple: (
ForagingSource(regions=(Region.mines_floor_95,)),
)
},
fishes=(
fish_data.ghostfish,
fish_data.ice_pip,
fish_data.lava_eel,
fish_data.stonefish,
),
villagers=(
villagers_data.dwarf,
),
)