Skip to content

Commit 1de91fa

Browse files
authored
Stardew Valley: 7.x.x - The Jojapocalypse Update (ArchipelagoMW#5432)
Major Content update for Stardew Valley ### Features - New BundleRandomization Value: Meme Bundles - Over 100 custom bundles, designed to be jokes, references, trolls, etc - New Setting: Bundles Per Room modifier - New Setting: Backpack Size - New Setting: Secretsanity - Checks for triggering easter eggs and secrets - New Setting: Moviesanity - Checks for watching movies and sharing snacks with Villagers - New Setting: Eatsanity - Checks for eating items - New Setting: Hatsanity - Checks for wearing Hats - New Setting: Start Without - Allows you to select any combination of various "starting" items, that you will actually not start with. Notably, tools, backpack slots, Day5 unlocks, etc. - New Setting: Allowed Filler Items - Allows you to customize the filler items you'll get - New Setting: Endgame Locations - Checks for various expensive endgame tasks and purchases - New Shipsanity value: Crops and Fish - New Settings: Jojapocalypse and settings to customize it - Bundle Plando: Replaced with BundleWhitelist and BundleBlacklist, for more customization freedom - Added a couple of Host.yaml settings to help hosts allow or ban specific difficult settings that could cause problems if the people don't know what they are signing up for. Plus a truckload of improvements on the mod side, not seen in this PR. ### Removed features - Integration for Stardew Valley Expanded. It is simply disabled, the code is all still there, but I'm extremely tired of providing tech support for it, plus Stardew Valley 1.7 was announced and that will break it again, so I'm done. When a maintainer steps up, it can be re-enabled.
1 parent 4ef5436 commit 1de91fa

File tree

255 files changed

+15225
-6092
lines changed

Some content is hidden

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

255 files changed

+15225
-6092
lines changed

worlds/stardew_valley/__init__.py

Lines changed: 181 additions & 53 deletions
Large diffs are not rendered by default.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"game": "Stardew Valley",
3-
"authors": ["KaitoKid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"],
3+
"authors": ["Kaito Kid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"],
44
"minimum_ap_version": "0.6.4",
5-
"world_version": "6.0.0"
5+
"world_version": "7.4.0"
66
}

worlds/stardew_valley/bundles/bundle.py

Lines changed: 202 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
from random import Random
44
from typing import List, Tuple
55

6+
from Options import DeathLink
67
from .bundle_item import BundleItem
78
from ..content import StardewContent
8-
from ..options import BundlePrice, StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
9-
from ..strings.currency_names import Currency
9+
from ..options import BundlePrice, StardewValleyOptions, ExcludeGingerIsland, FestivalLocations, TrapDifficulty, \
10+
MultipleDaySleepEnabled, Gifting, EntranceRandomization
11+
from ..strings.bundle_names import MemeBundleName
12+
from ..strings.currency_names import Currency, MemeCurrency
1013

1114

1215
@dataclass
@@ -19,6 +22,10 @@ class Bundle:
1922
def __repr__(self):
2023
return f"{self.name} -> {self.number_required} from {repr(self.items)}"
2124

25+
def special_behavior(self, world):
26+
if self.name == MemeBundleName.clickbait:
27+
world.options.exclude_locations.value.add(MemeBundleName.clickbait)
28+
2229

2330
@dataclass
2431
class BundleTemplate:
@@ -42,7 +49,42 @@ def extend_from(template, items: List[BundleItem]):
4249
template.number_required_items)
4350

4451
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
45-
number_required, price_multiplier = get_bundle_final_prices(options.bundle_price, self.number_required_items, False)
52+
try:
53+
number_required, price_multiplier = get_bundle_final_prices(options.bundle_price, self.number_required_items, False)
54+
filtered_items = [item for item in self.items if item.can_appear(content, options)]
55+
number_items = len(filtered_items)
56+
number_chosen_items = self.number_possible_items
57+
if number_chosen_items < number_required:
58+
number_chosen_items = number_required
59+
60+
if number_chosen_items > number_items:
61+
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
62+
else:
63+
chosen_items = random.sample(filtered_items, number_chosen_items)
64+
chosen_items = [item.as_amount(min(999, max(1, math.floor(item.amount * price_multiplier)))) for item in chosen_items]
65+
return Bundle(self.room, self.name, chosen_items, number_required)
66+
except Exception as e:
67+
raise Exception(f"Failed at creating bundle '{self.name}'. Error: {e}")
68+
69+
70+
def can_appear(self, options: StardewValleyOptions) -> bool:
71+
if self.name == MemeBundleName.trap and options.trap_items.value == TrapDifficulty.option_no_traps:
72+
return False
73+
if self.name == MemeBundleName.hibernation and options.multiple_day_sleep_enabled == MultipleDaySleepEnabled.option_false:
74+
return False
75+
if self.name == MemeBundleName.cooperation and options.gifting == Gifting.option_false:
76+
return False
77+
return True
78+
79+
80+
class FixedMultiplierBundleTemplate(BundleTemplate):
81+
82+
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
83+
number_required_items: int):
84+
super().__init__(room, name, items, number_possible_items, number_required_items)
85+
86+
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
87+
number_required = get_number_required_items(options.bundle_price, self.number_required_items)
4688
filtered_items = [item for item in self.items if item.can_appear(content, options)]
4789
number_items = len(filtered_items)
4890
number_chosen_items = self.number_possible_items
@@ -53,11 +95,53 @@ def create_bundle(self, random: Random, content: StardewContent, options: Starde
5395
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
5496
else:
5597
chosen_items = random.sample(filtered_items, number_chosen_items)
56-
chosen_items = [item.as_amount(max(1, math.floor(item.amount * price_multiplier))) for item in chosen_items]
98+
chosen_items = [item.as_amount(min(999, max(1, item.amount))) for item in chosen_items]
5799
return Bundle(self.room, self.name, chosen_items, number_required)
58100

59-
def can_appear(self, options: StardewValleyOptions) -> bool:
60-
return True
101+
102+
class FixedPriceBundleTemplate(BundleTemplate):
103+
104+
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
105+
number_required_items: int):
106+
super().__init__(room, name, items, number_possible_items, number_required_items)
107+
108+
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
109+
filtered_items = [item for item in self.items if item.can_appear(content, options)]
110+
number_items = len(filtered_items)
111+
number_chosen_items = self.number_possible_items
112+
if number_chosen_items < self.number_required_items:
113+
number_chosen_items = self.number_required_items
114+
115+
if number_chosen_items > number_items:
116+
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
117+
else:
118+
chosen_items = random.sample(filtered_items, number_chosen_items)
119+
chosen_items = [item.as_amount(max(1, math.floor(item.amount))) for item in chosen_items]
120+
return Bundle(self.room, self.name, chosen_items, self.number_required_items)
121+
122+
123+
# This type of bundle will always match the number of slots and the number of items
124+
class FixedSlotsBundleTemplate(BundleTemplate):
125+
126+
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
127+
number_required_items: int):
128+
super().__init__(room, name, items, number_possible_items, number_required_items)
129+
130+
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
131+
try:
132+
number_required, price_multiplier = get_bundle_final_prices(options.bundle_price, self.number_required_items, False)
133+
filtered_items = [item for item in self.items if item.can_appear(content, options)]
134+
number_items = len(filtered_items)
135+
number_chosen_items = number_required
136+
if number_chosen_items > number_items:
137+
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
138+
else:
139+
chosen_items = random.sample(filtered_items, number_chosen_items)
140+
chosen_items = [item.as_amount(min(999, max(1, math.floor(item.amount * price_multiplier)))) for item in chosen_items]
141+
return Bundle(self.room, self.name, chosen_items, number_required)
142+
except Exception as e:
143+
raise Exception(f"Failed at creating bundle '{self.name}'. Error: {e}")
144+
61145

62146

63147
class CurrencyBundleTemplate(BundleTemplate):
@@ -77,15 +161,33 @@ def get_currency_amount(self, bundle_price_option: BundlePrice):
77161
return currency_amount
78162

79163
def can_appear(self, options: StardewValleyOptions) -> bool:
164+
if not super().can_appear(options):
165+
return False
80166
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
81167
if self.item.item_name == Currency.qi_gem or self.item.item_name == Currency.golden_walnut or self.item.item_name == Currency.cinder_shard:
82168
return False
83169
if options.festival_locations == FestivalLocations.option_disabled:
84170
if self.item.item_name == Currency.star_token:
85171
return False
172+
if options.entrance_randomization != EntranceRandomization.option_disabled:
173+
if self.item.item_name == MemeCurrency.time_elapsed:
174+
return False
175+
if options.death_link != DeathLink.option_true:
176+
if self.item.item_name == MemeCurrency.deathlinks:
177+
return False
86178
return True
87179

88180

181+
class FixedPriceCurrencyBundleTemplate(CurrencyBundleTemplate):
182+
183+
def __init__(self, room: str, name: str, item: BundleItem):
184+
super().__init__(room, name, item)
185+
186+
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
187+
currency_amount = self.item.amount
188+
return Bundle(self.room, self.name, [BundleItem(self.item.item_name, currency_amount)], 1)
189+
190+
89191
class MoneyBundleTemplate(CurrencyBundleTemplate):
90192

91193
def __init__(self, room: str, default_name: str, item: BundleItem):
@@ -111,11 +213,15 @@ def get_currency_amount(self, bundle_price_option: BundlePrice):
111213

112214
class IslandBundleTemplate(BundleTemplate):
113215
def can_appear(self, options: StardewValleyOptions) -> bool:
216+
if not super().can_appear(options):
217+
return False
114218
return options.exclude_ginger_island == ExcludeGingerIsland.option_false
115219

116220

117221
class FestivalBundleTemplate(BundleTemplate):
118222
def can_appear(self, options: StardewValleyOptions) -> bool:
223+
if not super().can_appear(options):
224+
return False
119225
return options.festival_locations != FestivalLocations.option_disabled
120226

121227

@@ -149,6 +255,96 @@ def create_bundle(self, random: Random, content: StardewContent, options: Starde
149255
return Bundle(self.room, self.name, chosen_items, number_required)
150256

151257

258+
class FixedPriceDeepBundleTemplate(DeepBundleTemplate):
259+
260+
def __init__(self, room: str, name: str, categories: List[List[BundleItem]], number_possible_items: int,
261+
number_required_items: int):
262+
super().__init__(room, name, categories, number_possible_items, number_required_items)
263+
264+
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
265+
number_required = self.number_required_items
266+
number_categories = len(self.categories)
267+
number_chosen_categories = self.number_possible_items
268+
if number_chosen_categories < number_required:
269+
number_chosen_categories = number_required
270+
271+
if number_chosen_categories > number_categories:
272+
chosen_categories = self.categories + random.choices(self.categories,
273+
k=number_chosen_categories - number_categories)
274+
else:
275+
chosen_categories = random.sample(self.categories, number_chosen_categories)
276+
277+
chosen_items = []
278+
for category in chosen_categories:
279+
filtered_items = [item for item in category if item.can_appear(content, options)]
280+
chosen_items.append(random.choice(filtered_items))
281+
282+
chosen_items = [item.as_amount(max(1, math.floor(item.amount))) for item in chosen_items]
283+
return Bundle(self.room, self.name, chosen_items, number_required)
284+
285+
286+
@dataclass
287+
class BureaucracyBundleTemplate(BundleTemplate):
288+
289+
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
290+
number_required_items: int):
291+
super(BureaucracyBundleTemplate, self).__init__(room, name, items, number_possible_items, number_required_items)
292+
293+
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
294+
number_required = self.number_required_items + options.bundle_price.value
295+
number_required = min(12, max(4, number_required))
296+
if options.bundle_price == BundlePrice.option_minimum:
297+
number_required = 4
298+
if options.bundle_price == BundlePrice.option_maximum:
299+
number_required = 12
300+
price_multiplier = 1
301+
302+
filtered_items = [item for item in self.items if item.can_appear(content, options)]
303+
number_items = len(filtered_items)
304+
number_chosen_items = self.number_possible_items
305+
if number_chosen_items < number_required:
306+
number_chosen_items = number_required
307+
308+
if number_chosen_items > number_items:
309+
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
310+
else:
311+
chosen_items = random.sample(filtered_items, number_chosen_items)
312+
chosen_items = [item.as_amount(max(1, math.floor(item.amount * price_multiplier))) for item in chosen_items]
313+
return Bundle(self.room, self.name, chosen_items, number_required)
314+
315+
316+
@dataclass
317+
class RecursiveBundleTemplate(BundleTemplate):
318+
number_sub_bundles: int
319+
320+
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
321+
number_required_items: int, number_sub_bundles: int):
322+
super(RecursiveBundleTemplate, self).__init__(room, name, items, number_possible_items, number_required_items)
323+
self.number_sub_bundles = number_sub_bundles
324+
325+
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
326+
number_required = self.number_required_items + (options.bundle_price.value * self.number_sub_bundles)
327+
if options.bundle_price == BundlePrice.option_minimum:
328+
number_required = self.number_sub_bundles
329+
if options.bundle_price == BundlePrice.option_maximum:
330+
number_required = self.number_sub_bundles * 8
331+
number_required = min(self.number_sub_bundles * 8, max(self.number_sub_bundles, number_required))
332+
price_multiplier = get_price_multiplier(options.bundle_price, False)
333+
334+
filtered_items = [item for item in self.items if item.can_appear(content, options)]
335+
number_items = len(filtered_items)
336+
number_chosen_items = self.number_possible_items
337+
if number_chosen_items < number_required:
338+
number_chosen_items = number_required
339+
340+
if number_chosen_items > number_items:
341+
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
342+
else:
343+
chosen_items = random.sample(filtered_items, number_chosen_items)
344+
chosen_items = [item.as_amount(max(1, math.floor(item.amount * price_multiplier))) for item in chosen_items]
345+
return Bundle(self.room, self.name, chosen_items, number_required)
346+
347+
152348
def get_bundle_final_prices(bundle_price_option: BundlePrice, default_required_items: int, is_currency: bool) -> Tuple[int, float]:
153349
number_required_items = get_number_required_items(bundle_price_option, default_required_items)
154350
price_multiplier = get_price_multiplier(bundle_price_option, is_currency)

worlds/stardew_valley/bundles/bundle_item.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ def can_appear(self, content: StardewContent, options: StardewValleyOptions) ->
3737
return content.features.skill_progression.are_masteries_shuffled
3838

3939

40+
class QiBoardItemSource(BundleItemSource):
41+
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
42+
return content_packs.qi_board_content_pack.name in content.registered_packs
43+
44+
4045
class ContentItemSource(BundleItemSource):
4146
"""This is meant to be used for items that are managed by the content packs."""
4247

@@ -51,6 +56,7 @@ class Sources:
5156
island = IslandItemSource()
5257
festival = FestivalItemSource()
5358
masteries = MasteryItemSource()
59+
qi_board = QiBoardItemSource()
5460
content = ContentItemSource()
5561

5662
item_name: str

worlds/stardew_valley/bundles/bundle_room.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,73 @@
55
from .bundle import Bundle, BundleTemplate
66
from ..content import StardewContent
77
from ..options import StardewValleyOptions
8+
from ..strings.bundle_names import CCRoom
89

910

1011
@dataclass
1112
class BundleRoom:
1213
name: str
1314
bundles: List[Bundle]
1415

16+
def special_behavior(self, world):
17+
for bundle in self.bundles:
18+
bundle.special_behavior(world)
19+
20+
21+
def simplify_name(name: str) -> str:
22+
return name.lower().replace(" ", "").replace("-", "").replace("_", "").replace(".", "")
23+
24+
25+
# In the context of meme bundles, some of the bundles are directly references to specific people, mostly content creators.
26+
# This ensures that they roll their own bundle as part of their community center.
27+
def is_bundle_related_to_player(bundle: BundleTemplate, player_name: str) -> bool:
28+
if player_name == "":
29+
return False
30+
simple_bundle = simplify_name(bundle.name)
31+
simple_player = simplify_name(player_name)
32+
return simple_player in simple_bundle or simple_bundle in simple_player
33+
1534

1635
@dataclass
1736
class BundleRoomTemplate:
1837
name: str
1938
bundles: List[BundleTemplate]
2039
number_bundles: int
2140

22-
def create_bundle_room(self, random: Random, content: StardewContent, options: StardewValleyOptions):
41+
def create_bundle_room(self, random: Random, content: StardewContent, options: StardewValleyOptions, player_name: str = "", is_entire_cc: bool = False):
2342
filtered_bundles = [bundle for bundle in self.bundles if bundle.can_appear(options)]
2443

25-
priority_bundles = []
44+
whitelist_bundles = []
2645
unpriority_bundles = []
46+
blacklist_bundles = []
2747
for bundle in filtered_bundles:
28-
if bundle.name in options.bundle_plando:
29-
priority_bundles.append(bundle)
30-
else:
48+
if options.bundle_whitelist.prioritizes(bundle.name) or is_bundle_related_to_player(bundle, player_name):
49+
whitelist_bundles.append(bundle)
50+
elif options.bundle_blacklist.allows(bundle.name):
3151
unpriority_bundles.append(bundle)
52+
else:
53+
blacklist_bundles.append(bundle)
3254

33-
if self.number_bundles <= len(priority_bundles):
34-
chosen_bundles = random.sample(priority_bundles, self.number_bundles)
55+
modifier = options.bundle_per_room.value
56+
if is_entire_cc:
57+
modifier *= 6
58+
number_bundles = self.number_bundles + modifier
59+
bundles_cap = max(len(filtered_bundles), self.number_bundles)
60+
number_bundles = max(1, min(bundles_cap, number_bundles))
61+
if number_bundles < len(whitelist_bundles):
62+
chosen_bundles = random.sample(whitelist_bundles, number_bundles)
3563
else:
36-
chosen_bundles = priority_bundles
37-
num_remaining_bundles = self.number_bundles - len(priority_bundles)
38-
if num_remaining_bundles > len(unpriority_bundles):
39-
chosen_bundles.extend(random.choices(unpriority_bundles, k=num_remaining_bundles))
40-
else:
64+
chosen_bundles = whitelist_bundles
65+
num_remaining_bundles = number_bundles - len(whitelist_bundles)
66+
if num_remaining_bundles < len(unpriority_bundles):
4167
chosen_bundles.extend(random.sample(unpriority_bundles, num_remaining_bundles))
68+
else:
69+
chosen_bundles.extend(unpriority_bundles)
70+
num_remaining_bundles = num_remaining_bundles - len(unpriority_bundles)
71+
if num_remaining_bundles > 0:
72+
if self.name == CCRoom.raccoon_requests:
73+
chosen_bundles.extend(random.choices(unpriority_bundles, k=num_remaining_bundles))
74+
else:
75+
chosen_bundles.extend(random.sample(blacklist_bundles, num_remaining_bundles))
4276

4377
return BundleRoom(self.name, [bundle.create_bundle(random, content, options) for bundle in chosen_bundles])

0 commit comments

Comments
 (0)