Stardew Valley: 5.x.x - The Allsanity Update (#2764)

Major Content update for Stardew Valley, including the following features

- Major performance improvements all across the Stardew Valley apworld, including a significant reduction in the test time
- Randomized Farm Type
- Bundles rework (Remixed Bundles and Missing Bundle!)
- New Settings:
  * Shipsanity - Shipping individual items
  * Monstersanity - Slaying monsters
  * Cooksanity - Cooking individual recipes
  * Chefsanity - Learning individual recipes
  * Craftsanity - Crafting individual items
- New Goals:
  * Protector of the Valley - Complete every monster slayer goal
  * Full Shipment - Ship every item
  * Craftmaster - Craft every item
  * Gourmet Chef - Cook every recipe
  * Legend - Earn 10 000 000g
  * Mystery of the Stardrops - Find every stardrop (Maguffin Hunt)
  * Allsanity - Complete every check in your slot
- Building Shuffle: Cheaper options
- Tool Shuffle: Cheaper options
- Money rework
- New traps
- New isolated checks and items, including the farm cave, the movie theater, etc
- Mod Support: SVE [Albrekka]
- Mod Support: Distant Lands [Albrekka]
- Mod Support: Hat Mouse Lacey [Albrekka]
- Mod Support: Boarding House [Albrekka]

Co-authored-by: Witchybun <elnendil@gmail.com>
Co-authored-by: Witchybun <96719127+Witchybun@users.noreply.github.com>
Co-authored-by: Jouramie <jouramie@hotmail.com>
Co-authored-by: Alchav <59858495+Alchav@users.noreply.github.com>
This commit is contained in:
agilbert1412
2024-03-15 15:05:14 +03:00
committed by GitHub
parent f7da833572
commit 52e65e208e
177 changed files with 17815 additions and 6863 deletions

View File

@@ -0,0 +1,163 @@
from dataclasses import dataclass
from random import Random
from typing import List
from .bundle_item import BundleItem
from ..options import BundlePrice, StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
from ..strings.currency_names import Currency
@dataclass
class Bundle:
room: str
name: str
items: List[BundleItem]
number_required: int
def __repr__(self):
return f"{self.name} -> {self.number_required} from {repr(self.items)}"
@dataclass
class BundleTemplate:
room: str
name: str
items: List[BundleItem]
number_possible_items: int
number_required_items: int
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int, number_required_items: int):
self.room = room
self.name = name
self.items = items
self.number_possible_items = number_possible_items
self.number_required_items = number_required_items
@staticmethod
def extend_from(template, items: List[BundleItem]):
return BundleTemplate(template.room, template.name, items, template.number_possible_items, template.number_required_items)
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
if bundle_price_option == BundlePrice.option_minimum:
number_required = 1
elif bundle_price_option == BundlePrice.option_maximum:
number_required = 8
else:
number_required = self.number_required_items + bundle_price_option.value
number_required = max(1, number_required)
filtered_items = [item for item in self.items if item.can_appear(options)]
number_items = len(filtered_items)
number_chosen_items = self.number_possible_items
if number_chosen_items < number_required:
number_chosen_items = number_required
if number_chosen_items > number_items:
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
else:
chosen_items = random.sample(filtered_items, number_chosen_items)
return Bundle(self.room, self.name, chosen_items, number_required)
def can_appear(self, options: StardewValleyOptions) -> bool:
return True
class CurrencyBundleTemplate(BundleTemplate):
item: BundleItem
def __init__(self, room: str, name: str, item: BundleItem):
super().__init__(room, name, [item], 1, 1)
self.item = item
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
currency_amount = self.get_currency_amount(bundle_price_option)
return Bundle(self.room, self.name, [BundleItem(self.item.item_name, currency_amount)], 1)
def get_currency_amount(self, bundle_price_option: BundlePrice):
if bundle_price_option == BundlePrice.option_minimum:
price_multiplier = 0.1
elif bundle_price_option == BundlePrice.option_maximum:
price_multiplier = 4
else:
price_multiplier = round(1 + (bundle_price_option.value * 0.4), 2)
currency_amount = int(self.item.amount * price_multiplier)
return currency_amount
def can_appear(self, options: StardewValleyOptions) -> bool:
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
if self.item.item_name == Currency.qi_gem or self.item.item_name == Currency.golden_walnut or self.item.item_name == Currency.cinder_shard:
return False
if options.festival_locations == FestivalLocations.option_disabled:
if self.item.item_name == Currency.star_token:
return False
return True
class MoneyBundleTemplate(CurrencyBundleTemplate):
def __init__(self, room: str, item: BundleItem):
super().__init__(room, "", item)
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
currency_amount = self.get_currency_amount(bundle_price_option)
currency_name = "g"
if currency_amount >= 1000:
unit_amount = currency_amount % 1000
unit_amount = "000" if unit_amount == 0 else unit_amount
currency_display = f"{currency_amount // 1000},{unit_amount}"
else:
currency_display = f"{currency_amount}"
name = f"{currency_display}{currency_name} Bundle"
return Bundle(self.room, name, [BundleItem(self.item.item_name, currency_amount)], 1)
def get_currency_amount(self, bundle_price_option: BundlePrice):
if bundle_price_option == BundlePrice.option_minimum:
price_multiplier = 0.1
elif bundle_price_option == BundlePrice.option_maximum:
price_multiplier = 4
else:
price_multiplier = round(1 + (bundle_price_option.value * 0.4), 2)
currency_amount = int(self.item.amount * price_multiplier)
return currency_amount
class IslandBundleTemplate(BundleTemplate):
def can_appear(self, options: StardewValleyOptions) -> bool:
return options.exclude_ginger_island == ExcludeGingerIsland.option_false
class FestivalBundleTemplate(BundleTemplate):
def can_appear(self, options: StardewValleyOptions) -> bool:
return options.festival_locations != FestivalLocations.option_disabled
class DeepBundleTemplate(BundleTemplate):
categories: List[List[BundleItem]]
def __init__(self, room: str, name: str, categories: List[List[BundleItem]], number_possible_items: int, number_required_items: int):
super().__init__(room, name, [], number_possible_items, number_required_items)
self.categories = categories
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
if bundle_price_option == BundlePrice.option_minimum:
number_required = 1
elif bundle_price_option == BundlePrice.option_maximum:
number_required = 8
else:
number_required = self.number_required_items + bundle_price_option.value
number_categories = len(self.categories)
number_chosen_categories = self.number_possible_items
if number_chosen_categories < number_required:
number_chosen_categories = number_required
if number_chosen_categories > number_categories:
chosen_categories = self.categories + random.choices(self.categories, k=number_chosen_categories - number_categories)
else:
chosen_categories = random.sample(self.categories, number_chosen_categories)
chosen_items = []
for category in chosen_categories:
filtered_items = [item for item in category if item.can_appear(options)]
chosen_items.append(random.choice(filtered_items))
return Bundle(self.room, self.name, chosen_items, number_required)

View File

@@ -0,0 +1,73 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from ..options import StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
from ..strings.crop_names import Fruit
from ..strings.currency_names import Currency
from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
class BundleItemSource(ABC):
@abstractmethod
def can_appear(self, options: StardewValleyOptions) -> bool:
...
class VanillaItemSource(BundleItemSource):
def can_appear(self, options: StardewValleyOptions) -> bool:
return True
class IslandItemSource(BundleItemSource):
def can_appear(self, options: StardewValleyOptions) -> bool:
return options.exclude_ginger_island == ExcludeGingerIsland.option_false
class FestivalItemSource(BundleItemSource):
def can_appear(self, options: StardewValleyOptions) -> bool:
return options.festival_locations != FestivalLocations.option_disabled
@dataclass(frozen=True, order=True)
class BundleItem:
class Sources:
vanilla = VanillaItemSource()
island = IslandItemSource()
festival = FestivalItemSource()
item_name: str
amount: int = 1
quality: str = CropQuality.basic
source: BundleItemSource = Sources.vanilla
@staticmethod
def money_bundle(amount: int) -> BundleItem:
return BundleItem(Currency.money, amount)
def as_amount(self, amount: int) -> BundleItem:
return BundleItem(self.item_name, amount, self.quality, self.source)
def as_quality(self, quality: str) -> BundleItem:
return BundleItem(self.item_name, self.amount, quality, self.source)
def as_quality_crop(self) -> BundleItem:
amount = 5
difficult_crops = [Fruit.sweet_gem_berry, Fruit.ancient_fruit]
if self.item_name in difficult_crops:
amount = 1
return self.as_quality(CropQuality.gold).as_amount(amount)
def as_quality_fish(self) -> BundleItem:
return self.as_quality(FishQuality.gold)
def as_quality_forage(self) -> BundleItem:
return self.as_quality(ForageQuality.gold)
def __repr__(self):
quality = "" if self.quality == CropQuality.basic else self.quality
return f"{self.amount} {quality} {self.item_name}"
def can_appear(self, options: StardewValleyOptions) -> bool:
return self.source.can_appear(options)

View File

@@ -0,0 +1,24 @@
from dataclasses import dataclass
from random import Random
from typing import List
from .bundle import Bundle, BundleTemplate
from ..options import BundlePrice, StardewValleyOptions
@dataclass
class BundleRoom:
name: str
bundles: List[Bundle]
@dataclass
class BundleRoomTemplate:
name: str
bundles: List[BundleTemplate]
number_bundles: int
def create_bundle_room(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions):
filtered_bundles = [bundle for bundle in self.bundles if bundle.can_appear(options)]
chosen_bundles = random.sample(filtered_bundles, self.number_bundles)
return BundleRoom(self.name, [bundle.create_bundle(bundle_price_option, random, options) for bundle in chosen_bundles])

View File

@@ -0,0 +1,80 @@
from random import Random
from typing import List
from .bundle_room import BundleRoom
from ..data.bundle_data import pantry_vanilla, crafts_room_vanilla, fish_tank_vanilla, boiler_room_vanilla, bulletin_board_vanilla, vault_vanilla, \
pantry_thematic, crafts_room_thematic, fish_tank_thematic, boiler_room_thematic, bulletin_board_thematic, vault_thematic, pantry_remixed, \
crafts_room_remixed, fish_tank_remixed, boiler_room_remixed, bulletin_board_remixed, vault_remixed, all_bundle_items_except_money, \
abandoned_joja_mart_thematic, abandoned_joja_mart_vanilla, abandoned_joja_mart_remixed
from ..logic.logic import StardewLogic
from ..options import BundleRandomization, StardewValleyOptions, ExcludeGingerIsland
def get_all_bundles(random: Random, logic: StardewLogic, options: StardewValleyOptions) -> List[BundleRoom]:
if options.bundle_randomization == BundleRandomization.option_vanilla:
return get_vanilla_bundles(random, options)
elif options.bundle_randomization == BundleRandomization.option_thematic:
return get_thematic_bundles(random, options)
elif options.bundle_randomization == BundleRandomization.option_remixed:
return get_remixed_bundles(random, options)
elif options.bundle_randomization == BundleRandomization.option_shuffled:
return get_shuffled_bundles(random, logic, options)
raise NotImplementedError
def get_vanilla_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
pantry = pantry_vanilla.create_bundle_room(options.bundle_price, random, options)
crafts_room = crafts_room_vanilla.create_bundle_room(options.bundle_price, random, options)
fish_tank = fish_tank_vanilla.create_bundle_room(options.bundle_price, random, options)
boiler_room = boiler_room_vanilla.create_bundle_room(options.bundle_price, random, options)
bulletin_board = bulletin_board_vanilla.create_bundle_room(options.bundle_price, random, options)
vault = vault_vanilla.create_bundle_room(options.bundle_price, random, options)
abandoned_joja_mart = abandoned_joja_mart_vanilla.create_bundle_room(options.bundle_price, random, options)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
def get_thematic_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
pantry = pantry_thematic.create_bundle_room(options.bundle_price, random, options)
crafts_room = crafts_room_thematic.create_bundle_room(options.bundle_price, random, options)
fish_tank = fish_tank_thematic.create_bundle_room(options.bundle_price, random, options)
boiler_room = boiler_room_thematic.create_bundle_room(options.bundle_price, random, options)
bulletin_board = bulletin_board_thematic.create_bundle_room(options.bundle_price, random, options)
vault = vault_thematic.create_bundle_room(options.bundle_price, random, options)
abandoned_joja_mart = abandoned_joja_mart_thematic.create_bundle_room(options.bundle_price, random, options)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
def get_remixed_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
pantry = pantry_remixed.create_bundle_room(options.bundle_price, random, options)
crafts_room = crafts_room_remixed.create_bundle_room(options.bundle_price, random, options)
fish_tank = fish_tank_remixed.create_bundle_room(options.bundle_price, random, options)
boiler_room = boiler_room_remixed.create_bundle_room(options.bundle_price, random, options)
bulletin_board = bulletin_board_remixed.create_bundle_room(options.bundle_price, random, options)
vault = vault_remixed.create_bundle_room(options.bundle_price, random, options)
abandoned_joja_mart = abandoned_joja_mart_remixed.create_bundle_room(options.bundle_price, random, options)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
def get_shuffled_bundles(random: Random, logic: StardewLogic, options: StardewValleyOptions) -> List[BundleRoom]:
valid_bundle_items = [bundle_item for bundle_item in all_bundle_items_except_money if bundle_item.can_appear(options)]
rooms = [room for room in get_remixed_bundles(random, options) if room.name != "Vault"]
required_items = 0
for room in rooms:
for bundle in room.bundles:
required_items += len(bundle.items)
random.shuffle(room.bundles)
random.shuffle(rooms)
chosen_bundle_items = random.sample(valid_bundle_items, required_items)
sorted_bundle_items = sorted(chosen_bundle_items, key=lambda x: logic.has(x.item_name).get_difficulty())
for room in rooms:
for bundle in room.bundles:
num_items = len(bundle.items)
bundle.items = sorted_bundle_items[:num_items]
sorted_bundle_items = sorted_bundle_items[num_items:]
vault = vault_remixed.create_bundle_room(options.bundle_price, random, options)
return [*rooms, vault]