Skip to content

Commit ecd5113

Browse files
committed
Stardew Valley: Refactor buildings to use content packs (ArchipelagoMW#4239)
1 parent 1f557cb commit ecd5113

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+742
-456
lines changed

worlds/stardew_valley/__init__.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def add_location(name: str, code: Optional[int], region: str):
145145

146146
def create_items(self):
147147
self.precollect_starting_season()
148-
self.precollect_farm_type_items()
148+
self.precollect_building_items()
149149
items_to_exclude = [excluded_items
150150
for excluded_items in self.multiworld.precollected_items[self.player]
151151
if not item_table[excluded_items.name].has_any_group(Group.RESOURCE_PACK,
@@ -200,9 +200,16 @@ def precollect_starting_season(self):
200200
starting_season = self.create_item(self.random.choice(season_pool))
201201
self.multiworld.push_precollected(starting_season)
202202

203-
def precollect_farm_type_items(self):
204-
if self.options.farm_type == FarmType.option_meadowlands and self.options.building_progression & BuildingProgression.option_progressive:
205-
self.multiworld.push_precollected(self.create_item("Progressive Coop"))
203+
def precollect_building_items(self):
204+
building_progression = self.content.features.building_progression
205+
# Not adding items when building are vanilla because the buildings are already placed in the world.
206+
if not building_progression.is_progressive:
207+
return
208+
209+
for building in building_progression.starting_buildings:
210+
item, quantity = building_progression.to_progressive_item(building)
211+
for _ in range(quantity):
212+
self.multiworld.push_precollected(self.create_item(item))
206213

207214
def setup_logic_events(self):
208215
def register_event(name: str, region: str, rule: StardewRule):

worlds/stardew_valley/content/__init__.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from . import content_packs
2-
from .feature import cropsanity, friendsanity, fishsanity, booksanity, skill_progression, tool_progression
2+
from .feature import cropsanity, friendsanity, fishsanity, booksanity, building_progression, skill_progression, tool_progression
33
from .game_content import ContentPack, StardewContent, StardewFeatures
44
from .unpacking import unpack_content
55
from .. import options
6+
from ..strings.building_names import Building
67

78

89
def create_content(player_options: options.StardewValleyOptions) -> StardewContent:
@@ -29,6 +30,7 @@ def choose_content_packs(player_options: options.StardewValleyOptions):
2930
def choose_features(player_options: options.StardewValleyOptions) -> StardewFeatures:
3031
return StardewFeatures(
3132
choose_booksanity(player_options.booksanity),
33+
choose_building_progression(player_options.building_progression, player_options.farm_type),
3234
choose_cropsanity(player_options.cropsanity),
3335
choose_fishsanity(player_options.fishsanity),
3436
choose_friendsanity(player_options.friendsanity, player_options.friendsanity_heart_size),
@@ -109,6 +111,32 @@ def choose_friendsanity(friendsanity_option: options.Friendsanity, heart_size: o
109111
raise ValueError(f"No friendsanity feature mapped to {str(friendsanity_option.value)}")
110112

111113

114+
def choose_building_progression(building_option: options.BuildingProgression,
115+
farm_type_option: options.FarmType) -> building_progression.BuildingProgressionFeature:
116+
starting_buildings = {Building.farm_house, Building.pet_bowl, Building.shipping_bin}
117+
118+
if farm_type_option == options.FarmType.option_meadowlands:
119+
starting_buildings.add(Building.coop)
120+
121+
if (building_option == options.BuildingProgression.option_vanilla
122+
or building_option == options.BuildingProgression.option_vanilla_cheap
123+
or building_option == options.BuildingProgression.option_vanilla_very_cheap):
124+
return building_progression.BuildingProgressionVanilla(
125+
starting_buildings=starting_buildings,
126+
)
127+
128+
starting_buildings.remove(Building.shipping_bin)
129+
130+
if (building_option == options.BuildingProgression.option_progressive
131+
or building_option == options.BuildingProgression.option_progressive_cheap
132+
or building_option == options.BuildingProgression.option_progressive_very_cheap):
133+
return building_progression.BuildingProgressionProgressive(
134+
starting_buildings=starting_buildings,
135+
)
136+
137+
raise ValueError(f"No building progression feature mapped to {str(building_option.value)}")
138+
139+
112140
skill_progression_by_option = {
113141
options.SkillProgression.option_vanilla: skill_progression.SkillProgressionVanilla(),
114142
options.SkillProgression.option_progressive: skill_progression.SkillProgressionProgressive(),

worlds/stardew_valley/content/feature/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from . import booksanity
2+
from . import building_progression
23
from . import cropsanity
34
from . import fishsanity
45
from . import friendsanity
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from abc import ABC
2+
from dataclasses import dataclass
3+
from typing import ClassVar, Set, Tuple
4+
5+
from ...strings.building_names import Building
6+
7+
progressive_house = "Progressive House"
8+
9+
# This assumes that the farm house is always available, which might not be true forever...
10+
progressive_house_by_upgrade_name = {
11+
Building.farm_house: 0,
12+
Building.kitchen: 1,
13+
Building.kids_room: 2,
14+
Building.cellar: 3
15+
}
16+
17+
18+
def to_progressive_item(building: str) -> Tuple[str, int]:
19+
"""Return the name of the progressive item and its quantity required to unlock the building.
20+
"""
21+
if building in [Building.coop, Building.barn, Building.shed]:
22+
return f"Progressive {building}", 1
23+
elif building.startswith("Big"):
24+
return f"Progressive {building[building.index(' ') + 1:]}", 2
25+
elif building.startswith("Deluxe"):
26+
return f"Progressive {building[building.index(' ') + 1:]}", 3
27+
elif building in progressive_house_by_upgrade_name:
28+
return progressive_house, progressive_house_by_upgrade_name[building]
29+
30+
return building, 1
31+
32+
33+
def to_location_name(building: str) -> str:
34+
return f"{building} Blueprint"
35+
36+
37+
@dataclass(frozen=True)
38+
class BuildingProgressionFeature(ABC):
39+
is_progressive: ClassVar[bool]
40+
starting_buildings: Set[str]
41+
42+
to_progressive_item = staticmethod(to_progressive_item)
43+
progressive_house = progressive_house
44+
45+
to_location_name = staticmethod(to_location_name)
46+
47+
48+
class BuildingProgressionVanilla(BuildingProgressionFeature):
49+
is_progressive = False
50+
51+
52+
class BuildingProgressionProgressive(BuildingProgressionFeature):
53+
is_progressive = True

worlds/stardew_valley/content/game_content.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from dataclasses import dataclass, field
44
from typing import Dict, Iterable, Set, Any, Mapping, Type, Tuple, Union
55

6-
from .feature import booksanity, cropsanity, fishsanity, friendsanity, skill_progression, tool_progression
6+
from .feature import booksanity, cropsanity, fishsanity, friendsanity, skill_progression, building_progression, tool_progression
7+
from ..data.building import Building
78
from ..data.fish_data import FishItem
8-
from ..data.game_item import GameItem, ItemSource, ItemTag
9+
from ..data.game_item import GameItem, Source, ItemTag
910
from ..data.skill import Skill
1011
from ..data.villagers_data import Villager
1112

@@ -20,16 +21,17 @@ class StardewContent:
2021
game_items: Dict[str, GameItem] = field(default_factory=dict)
2122
fishes: Dict[str, FishItem] = field(default_factory=dict)
2223
villagers: Dict[str, Villager] = field(default_factory=dict)
24+
farm_buildings: Dict[str, Building] = field(default_factory=dict)
2325
skills: Dict[str, Skill] = field(default_factory=dict)
2426
quests: Dict[str, Any] = field(default_factory=dict)
2527

26-
def find_sources_of_type(self, types: Union[Type[ItemSource], Tuple[Type[ItemSource]]]) -> Iterable[ItemSource]:
28+
def find_sources_of_type(self, types: Union[Type[Source], Tuple[Type[Source]]]) -> Iterable[Source]:
2729
for item in self.game_items.values():
2830
for source in item.sources:
2931
if isinstance(source, types):
3032
yield source
3133

32-
def source_item(self, item_name: str, *sources: ItemSource):
34+
def source_item(self, item_name: str, *sources: Source):
3335
item = self.game_items.setdefault(item_name, GameItem(item_name))
3436
item.add_sources(sources)
3537

@@ -50,6 +52,7 @@ def find_tagged_items(self, tag: ItemTag) -> Iterable[GameItem]:
5052
@dataclass(frozen=True)
5153
class StardewFeatures:
5254
booksanity: booksanity.BooksanityFeature
55+
building_progression: building_progression.BuildingProgressionFeature
5356
cropsanity: cropsanity.CropsanityFeature
5457
fishsanity: fishsanity.FishsanityFeature
5558
friendsanity: friendsanity.FriendsanityFeature
@@ -70,13 +73,13 @@ class ContentPack:
7073
# def item_hook
7174
# ...
7275

73-
harvest_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
76+
harvest_sources: Mapping[str, Iterable[Source]] = field(default_factory=dict)
7477
"""Harvest sources contains both crops and forageables, but also fruits from trees, the cave farm and stuff harvested from tapping like maple syrup."""
7578

7679
def harvest_source_hook(self, content: StardewContent):
7780
...
7881

79-
shop_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
82+
shop_sources: Mapping[str, Iterable[Source]] = field(default_factory=dict)
8083

8184
def shop_source_hook(self, content: StardewContent):
8285
...
@@ -86,12 +89,12 @@ def shop_source_hook(self, content: StardewContent):
8689
def fish_hook(self, content: StardewContent):
8790
...
8891

89-
crafting_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
92+
crafting_sources: Mapping[str, Iterable[Source]] = field(default_factory=dict)
9093

9194
def crafting_hook(self, content: StardewContent):
9295
...
9396

94-
artisan_good_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
97+
artisan_good_sources: Mapping[str, Iterable[Source]] = field(default_factory=dict)
9598

9699
def artisan_good_hook(self, content: StardewContent):
97100
...
@@ -101,6 +104,11 @@ def artisan_good_hook(self, content: StardewContent):
101104
def villager_hook(self, content: StardewContent):
102105
...
103106

107+
farm_buildings: Iterable[Building] = ()
108+
109+
def farm_building_hook(self, content: StardewContent):
110+
...
111+
104112
skills: Iterable[Skill] = ()
105113

106114
def skill_hook(self, content: StardewContent):
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
11
from ..game_content import ContentPack
22
from ..mod_registry import register_mod_content_pack
3+
from ...data.building import Building
4+
from ...data.shop import ShopSource
35
from ...mods.mod_data import ModNames
6+
from ...strings.artisan_good_names import ArtisanGood
7+
from ...strings.building_names import ModBuilding
8+
from ...strings.metal_names import MetalBar
9+
from ...strings.region_names import Region
410

511
register_mod_content_pack(ContentPack(
612
ModNames.tractor,
13+
farm_buildings=(
14+
Building(
15+
ModBuilding.tractor_garage,
16+
sources=(
17+
ShopSource(
18+
shop_region=Region.carpenter,
19+
money_price=150_000,
20+
items_price=((20, MetalBar.iron), (5, MetalBar.iridium), (1, ArtisanGood.battery_pack)),
21+
),
22+
),
23+
),
24+
),
725
))

worlds/stardew_valley/content/unpacking.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from .game_content import StardewContent, ContentPack, StardewFeatures
77
from .vanilla.base import base_game as base_game_content_pack
8-
from ..data.game_item import GameItem, ItemSource
8+
from ..data.game_item import GameItem, Source
99

1010

1111
def unpack_content(features: StardewFeatures, packs: Iterable[ContentPack]) -> StardewContent:
@@ -61,6 +61,10 @@ def register_pack(content: StardewContent, pack: ContentPack):
6161
content.villagers[villager.name] = villager
6262
pack.villager_hook(content)
6363

64+
for building in pack.farm_buildings:
65+
content.farm_buildings[building.name] = building
66+
pack.farm_building_hook(content)
67+
6468
for skill in pack.skills:
6569
content.skills[skill.name] = skill
6670
pack.skill_hook(content)
@@ -73,7 +77,7 @@ def register_pack(content: StardewContent, pack: ContentPack):
7377

7478

7579
def register_sources_and_call_hook(content: StardewContent,
76-
sources_by_item_name: Mapping[str, Iterable[ItemSource]],
80+
sources_by_item_name: Mapping[str, Iterable[Source]],
7781
hook: Callable[[StardewContent], None]):
7882
for item_name, sources in sources_by_item_name.items():
7983
item = content.game_items.setdefault(item_name, GameItem(item_name))

0 commit comments

Comments
 (0)