Stardew Valley: 7.x.x - The Jojapocalypse Update (#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.
This commit is contained in:
agilbert1412
2026-02-15 12:02:21 -05:00
committed by GitHub
parent 4ef5436559
commit 1de91fab67
255 changed files with 15198 additions and 6065 deletions

View File

@@ -1,30 +1,41 @@
import logging
import math
import typing
from collections import Counter
from functools import wraps
from random import Random
from typing import Dict, Any, Optional, List, TextIO
from typing import Dict, List, Any, ClassVar, TextIO, Optional
import entrance_rando
from BaseClasses import Region, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState
from Options import PerGameCommonOptions
from worlds.AutoWorld import World, WebWorld
from worlds.LauncherComponents import components, Component, icon_paths, Type
from .bundles.bundle_room import BundleRoom
from .bundles.bundles import get_all_bundles
from .bundles.bundles import get_all_bundles, get_trash_bear_requests
from .content import StardewContent, create_content
from .early_items import setup_early_items
from .items import item_table, ItemData, Group, items_by_group
from .items.item_creation import create_items, get_all_filler_items, remove_limited_amount_packs, \
generate_filler_choice_pool
from .content.feature.special_order_locations import get_qi_gem_amount
from .content.feature.walnutsanity import get_walnut_amount
from .items import item_table, ItemData, Group, items_by_group, create_items, generate_filler_choice_pool, \
setup_early_items
from .items.item_data import FILLER_GROUPS
from .locations import location_table, create_locations, LocationData, locations_by_tag
from .logic.combat_logic import valid_weapons
from .logic.logic import StardewLogic
from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, EnabledFillerBuffs, \
NumberOfMovementBuffs, BuildingProgression, EntranceRandomization, FarmType
from .options.forced_options import force_change_options_if_incompatible
NumberOfMovementBuffs, BuildingProgression, EntranceRandomization, ToolProgression, BackpackProgression, TrapDistribution, BundlePrice, \
BundleWhitelist, BundleBlacklist, BundlePerRoom, FarmType
from .options.forced_options import force_change_options_if_incompatible, force_change_options_if_banned
from .options.jojapocalypse_options import JojaAreYouSure
from .options.option_groups import sv_option_groups
from .options.presets import sv_options_presets
from .options.settings import StardewSettings
from .options.worlds_group import apply_most_restrictive_options
from .regions import create_regions, prepare_mod_data
from .rules import set_rules
from .stardew_rule import True_, StardewRule, HasProgressionPercent
from .strings.ap_names.ap_option_names import StartWithoutOptionName
from .strings.ap_names.ap_weapon_names import APWeapon
from .strings.ap_names.event_names import Event
from .strings.goal_names import Goal as GoalName
@@ -34,6 +45,7 @@ STARDEW_VALLEY = "Stardew Valley"
UNIVERSAL_TRACKER_SEED_PROPERTY = "ut_seed"
client_version = 0
TRACKER_ENABLED = True
class StardewLocation(Location):
@@ -42,6 +54,12 @@ class StardewLocation(Location):
class StardewItem(Item):
game: str = STARDEW_VALLEY
events_to_collect: Counter[str]
@wraps(Item.__init__)
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.events_to_collect = Counter()
class StardewWebWorld(WebWorld):
@@ -56,7 +74,7 @@ class StardewWebWorld(WebWorld):
"English",
"setup_en.md",
"setup/en",
["KaitoKid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"]
["Kaito Kid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"]
)
setup_fr = Tutorial(
@@ -71,13 +89,35 @@ class StardewWebWorld(WebWorld):
tutorials = [setup_en, setup_fr]
if TRACKER_ENABLED:
from .. import user_folder
import os
# Best effort to detect if universal tracker is installed
if any("tracker.apworld" in f.name for f in os.scandir(user_folder)):
def launch_client(*args):
from worlds.LauncherComponents import launch
from .client import launch as client_main
launch(client_main, name="Stardew Valley Tracker", args=args)
components.append(Component(
"Stardew Valley Tracker",
func=launch_client,
component_type=Type.CLIENT,
icon='stardew'
))
icon_paths['stardew'] = f"ap:{__name__}/stardew.png"
class StardewValleyWorld(World):
"""
Stardew Valley is an open-ended country-life RPG. You can farm, fish, mine, fight, complete quests,
befriend villagers, and uncover dark secrets.
"""
game = STARDEW_VALLEY
topology_present = False
topology_present = True
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = {name: data.code for name, data in location_table.items()}
@@ -95,14 +135,17 @@ class StardewValleyWorld(World):
options_dataclass = StardewValleyOptions
options: StardewValleyOptions
settings: ClassVar[StardewSettings]
content: StardewContent
logic: StardewLogic
web = StardewWebWorld()
modified_bundles: List[BundleRoom]
randomized_entrances: Dict[str, str]
trash_bear_requests: Dict[str, List[str]]
total_progression_items: int
classifications_to_override_post_fill: list[tuple[StardewItem, ItemClassification]]
@classmethod
def create_group(cls, multiworld: MultiWorld, new_player_id: int, players: set[int]) -> World:
@@ -111,13 +154,15 @@ class StardewValleyWorld(World):
group_options = typing.cast(StardewValleyOptions, world_group.options)
worlds_options = [typing.cast(StardewValleyOptions, multiworld.worlds[player].options) for player in players]
apply_most_restrictive_options(group_options, worlds_options)
world_group.content = create_content(group_options)
return world_group
def __init__(self, multiworld: MultiWorld, player: int):
super().__init__(multiworld, player)
self.filler_item_pool_names = []
self.filler_item_pool_names = None
self.total_progression_items = 0
self.classifications_to_override_post_fill = []
# Taking the seed specified in slot data for UT, otherwise just generating the seed.
self.seed = getattr(multiworld, "re_gen_passthrough", {}).get(STARDEW_VALLEY, self.random.getrandbits(64))
@@ -128,9 +173,25 @@ class StardewValleyWorld(World):
seed = slot_data.get(UNIVERSAL_TRACKER_SEED_PROPERTY)
if seed is None:
logger.warning(f"World was generated before Universal Tracker support. Tracker might not be accurate.")
for option_name in slot_data:
if option_name in self.options_dataclass.type_hints:
option_value = slot_data[option_name]
option_type = self.options_dataclass.type_hints[option_name]
if isinstance(option_value, option_type):
self.options.__setattr__(option_name, option_value)
continue
parsed_option_value = option_type.from_any(option_value)
if isinstance(parsed_option_value, option_type):
self.options.__setattr__(option_name, parsed_option_value)
continue
logger.warning(f"Option {option_name} was found in slot data, but could not be automatically parsed to be used in generation.\n"
f"Slot Data Value: {option_value}"
f"Parsed Value: {parsed_option_value}"
f"Yaml Value: {self.options.__getattribute__(option_name)}")
return seed
def generate_early(self):
force_change_options_if_banned(self.options, self.settings, self.player, self.player_name)
force_change_options_if_incompatible(self.options, self.player, self.player_name)
self.content = create_content(self.options)
@@ -141,23 +202,31 @@ class StardewValleyWorld(World):
world_regions = create_regions(create_region, self.options, self.content)
self.logic = StardewLogic(self.player, self.options, self.content, world_regions.keys())
self.modified_bundles = get_all_bundles(self.random, self.logic, self.content, self.options)
self.modified_bundles = get_all_bundles(self.random, self.logic, self.content, self.options, self.player_name)
self.trash_bear_requests = get_trash_bear_requests(self.random, self.content, self.options)
for bundle_room in self.modified_bundles:
bundle_room.special_behavior(self)
def add_location(name: str, code: Optional[int], region: str):
assert region in world_regions, f"Location {name} cannot be created in region {region}, because the region does not exist in this slot"
region: Region = world_regions[region]
location = StardewLocation(self.player, name, code, region)
region.locations.append(location)
create_locations(add_location, self.modified_bundles, self.options, self.content, self.random)
create_locations(add_location, self.modified_bundles, self.trash_bear_requests, self.options, self.content, self.random)
self.multiworld.regions.extend(world_regions.values())
def create_items(self):
self.precollect_start_inventory_items_if_needed()
self.precollect_start_without_items()
self.precollect_starting_season()
self.precollect_building_items()
self.precollect_starting_backpacks()
items_to_exclude = [excluded_items
for excluded_items in self.multiworld.precollected_items[self.player]
if item_table[excluded_items.name].has_any_group(Group.MAXIMUM_ONE)
or not item_table[excluded_items.name].has_any_group(Group.RESOURCE_PACK, Group.FRIENDSHIP_PACK)]
or not item_table[excluded_items.name].has_any_group(*FILLER_GROUPS, Group.FRIENDSHIP_PACK)]
if self.options.season_randomization == SeasonRandomization.option_disabled:
items_to_exclude = [item for item in items_to_exclude
@@ -172,6 +241,7 @@ class StardewValleyWorld(World):
self.multiworld.itempool += created_items
setup_early_items(self.multiworld, self.options, self.content, self.player, self.random)
self.setup_logic_events()
self.setup_victory()
@@ -187,6 +257,31 @@ class StardewValleyWorld(World):
self.total_progression_items += sum(1 for i in created_items if i.advancement)
self.total_progression_items -= 1 # -1 for the victory event
player_state = self.multiworld.state.prog_items[self.player]
self.update_received_progression_percent(player_state)
def precollect_start_inventory_items_if_needed(self):
# The only reason this is necessary, is because in an UT context, precollected items was not filled up, and this messes with the seeded random later
for item_name in self.options.start_inventory:
item_count = self.options.start_inventory[item_name]
precollected_count = len([precollected_item for precollected_item in self.multiworld.precollected_items[self.player]
if precollected_item.name == item_name])
while precollected_count < item_count:
self.multiworld.push_precollected(self.create_item(item_name))
precollected_count += 1
def precollect_start_without_items(self):
if StartWithoutOptionName.landslide not in self.options.start_without:
self.multiworld.push_precollected(self.create_item("Landslide Removed"))
if StartWithoutOptionName.community_center not in self.options.start_without:
self.multiworld.push_precollected(self.create_item("Community Center Key"))
self.multiworld.push_precollected(self.create_item("Forest Magic"))
self.multiworld.push_precollected(self.create_item("Wizard Invitation"))
if StartWithoutOptionName.buildings not in self.options.start_without:
self.multiworld.push_precollected(self.create_item("Shipping Bin"))
self.multiworld.push_precollected(self.create_item("Pet Bowl"))
def precollect_starting_season(self):
if self.options.season_randomization == SeasonRandomization.option_progressive:
return
@@ -220,9 +315,23 @@ class StardewValleyWorld(World):
for _ in range(quantity):
self.multiworld.push_precollected(self.create_item(item))
def precollect_starting_backpacks(self):
if self.options.backpack_progression != BackpackProgression.option_vanilla and StartWithoutOptionName.backpack in self.options.start_without:
minimum_start_slots = 4 if StartWithoutOptionName.tools in self.options.start_without else 6
num_starting_slots = max(minimum_start_slots, self.options.backpack_size.value)
num_starting_backpacks = math.ceil(num_starting_slots / self.options.backpack_size.value)
num_already_starting_backpacks = 0
for precollected_item in self.multiworld.precollected_items[self.player]:
if precollected_item.name == "Progressive Backpack":
num_already_starting_backpacks += 1
for i in range(num_starting_backpacks - num_already_starting_backpacks):
self.multiworld.push_precollected(self.create_item("Progressive Backpack"))
def setup_logic_events(self):
def register_event(name: str, region: str, rule: StardewRule):
event_location = LocationData(None, region, name)
def register_event(name: str, region: str, rule: StardewRule, location_name: str | None = None) -> None:
if location_name is None:
location_name = name
event_location = LocationData(None, region, location_name)
self.create_event_location(event_location, rule, name)
self.logic.setup_events(register_event)
@@ -284,6 +393,14 @@ class StardewValleyWorld(World):
self.create_event_location(location_table[GoalName.mystery_of_the_stardrops],
self.logic.goal.can_complete_mystery_of_the_stardrop(),
Event.victory)
elif self.options.goal == Goal.option_mad_hatter:
self.create_event_location(location_table[GoalName.mad_hatter],
self.logic.goal.can_complete_mad_hatter(self.get_all_location_names()),
Event.victory)
elif self.options.goal == Goal.option_ultimate_foodie:
self.create_event_location(location_table[GoalName.ultimate_foodie],
self.logic.goal.can_complete_ultimate_foodie(self.get_all_location_names()),
Event.victory)
elif self.options.goal == Goal.option_allsanity:
self.create_event_location(location_table[GoalName.allsanity],
self.logic.goal.can_complete_allsanity(),
@@ -298,18 +415,36 @@ class StardewValleyWorld(World):
def get_all_location_names(self) -> List[str]:
return list(location.name for location in self.multiworld.get_locations(self.player))
def create_item(self, item: str | ItemData, override_classification: ItemClassification = None) -> StardewItem:
def create_item(self, item: str | ItemData,
classification_pre_fill: ItemClassification = None,
classification_post_fill: ItemClassification = None) -> StardewItem:
if isinstance(item, str):
item = item_table[item]
if override_classification is None:
override_classification = item.classification
if classification_pre_fill is None:
classification_pre_fill = item.classification
return StardewItem(item.name, override_classification, item.code, self.player)
stardew_item = StardewItem(item.name, classification_pre_fill, item.code, self.player)
if stardew_item.advancement:
# Progress is only counted for pre-fill progression items, so we don't count filler items later converted to progression post-fill.
stardew_item.events_to_collect[Event.received_progression_item] = 1
if (walnut_amount := get_walnut_amount(stardew_item.name)) > 0:
stardew_item.events_to_collect[Event.received_walnuts] = walnut_amount
if (qi_gem_amount := get_qi_gem_amount(stardew_item.name)) > 0:
stardew_item.events_to_collect[Event.received_qi_gems] = qi_gem_amount
if classification_post_fill is not None:
self.classifications_to_override_post_fill.append((stardew_item, classification_post_fill))
return stardew_item
def create_event_location(self, location_data: LocationData, rule: StardewRule, item: str):
region = self.multiworld.get_region(location_data.region, self.player)
region.add_event(location_data.name, item, rule, StardewLocation, StardewItem)
item = typing.cast(StardewItem, region.add_event(location_data.name, item, rule, StardewLocation, StardewItem))
item.events_to_collect[Event.received_progression_item] = 1
def set_rules(self):
set_rules(self)
@@ -322,9 +457,14 @@ class StardewValleyWorld(World):
def generate_basic(self):
pass
def post_fill(self) -> None:
# Not updating the prog item count, as any change could make some locations inaccessible, which is pretty much illegal in post fill.
for item, classification in self.classifications_to_override_post_fill:
item.classification = classification
def get_filler_item_name(self) -> str:
if not self.filler_item_pool_names:
self.filler_item_pool_names = generate_filler_choice_pool(self.options)
self.filler_item_pool_names = generate_filler_choice_pool(self.options, self.content)
return self.random.choice(self.filler_item_pool_names)
def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
@@ -365,9 +505,10 @@ class StardewValleyWorld(World):
for bundle in room.bundles:
bundles[room.name][bundle.name] = {"number_required": bundle.number_required}
for i, item in enumerate(bundle.items):
bundles[room.name][bundle.name][i] = f"{item.get_item()}|{item.amount}|{item.quality}"
bundles[room.name][bundle.name][str(i)] = f"{item.get_item()}|{item.amount}|{item.quality}"
excluded_options = [BundleRandomization, NumberOfMovementBuffs, EnabledFillerBuffs]
excluded_options = [BundleRandomization, BundlePerRoom, NumberOfMovementBuffs,
EnabledFillerBuffs, TrapDistribution, BundleWhitelist, BundleBlacklist, JojaAreYouSure]
excluded_option_names = [option.internal_name for option in excluded_options]
generic_option_names = [option_name for option_name in PerGameCommonOptions.type_hints]
excluded_option_names.extend(generic_option_names)
@@ -377,8 +518,9 @@ class StardewValleyWorld(World):
UNIVERSAL_TRACKER_SEED_PROPERTY: self.seed,
"seed": self.random.randrange(1000000000), # Seed should be max 9 digits
"randomized_entrances": self.randomized_entrances,
"trash_bear_requests": self.trash_bear_requests,
"modified_bundles": bundles,
"client_version": "6.0.0",
"client_version": self.world_version.as_simple_string(),
})
return slot_data
@@ -389,18 +531,12 @@ class StardewValleyWorld(World):
return False
player_state = state.prog_items[self.player]
player_state.update(item.events_to_collect)
received_progression_count = player_state[Event.received_progression_item]
received_progression_count += 1
if self.total_progression_items:
# Total progression items is not set until all items are created, but collect will be called during the item creation when an item is precollected.
# We can't update the percentage if we don't know the total progression items, can't divide by 0.
player_state[Event.received_progression_percent] = received_progression_count * 100 // self.total_progression_items
player_state[Event.received_progression_item] = received_progression_count
self.update_received_progression_percent(player_state)
walnut_amount = self.get_walnut_amount(item.name)
if walnut_amount:
player_state[Event.received_walnuts] += walnut_amount
if item.name in APWeapon.all_weapons:
player_state[Event.received_progressive_weapon] = max(player_state[Event.received_progressive_weapon], player_state[item.name])
return True
@@ -410,26 +546,18 @@ class StardewValleyWorld(World):
return False
player_state = state.prog_items[self.player]
player_state.subtract(item.events_to_collect)
received_progression_count = player_state[Event.received_progression_item]
received_progression_count -= 1
if self.total_progression_items:
# We can't update the percentage if we don't know the total progression items, can't divide by 0.
player_state[Event.received_progression_percent] = received_progression_count * 100 // self.total_progression_items
player_state[Event.received_progression_item] = received_progression_count
self.update_received_progression_percent(player_state)
walnut_amount = self.get_walnut_amount(item.name)
if walnut_amount:
player_state[Event.received_walnuts] -= walnut_amount
if item.name in APWeapon.all_weapons:
player_state[Event.received_progressive_weapon] = max(player_state[weapon] for weapon in APWeapon.all_weapons)
return True
@staticmethod
def get_walnut_amount(item_name: str) -> int:
if item_name == "Golden Walnut":
return 1
if item_name == "3 Golden Walnuts":
return 3
if item_name == "5 Golden Walnuts":
return 5
return 0
def update_received_progression_percent(self, player_state: Counter[str]) -> None:
if self.total_progression_items:
received_progression_count = player_state[Event.received_progression_item]
# Total progression items is not set until all items are created, but collect will be called during the item creation when an item is precollected.
# We can't update the percentage if we don't know the total progression items, can't divide by 0.
player_state[Event.received_progression_percent] = received_progression_count * 100 // self.total_progression_items

View File

@@ -1,6 +1,6 @@
{
"game": "Stardew Valley",
"authors": ["KaitoKid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"],
"authors": ["Kaito Kid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"],
"minimum_ap_version": "0.6.4",
"world_version": "6.0.0"
"world_version": "7.4.0"
}

View File

@@ -3,10 +3,13 @@ from dataclasses import dataclass
from random import Random
from typing import List, Tuple
from Options import DeathLink
from .bundle_item import BundleItem
from ..content import StardewContent
from ..options import BundlePrice, StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
from ..strings.currency_names import Currency
from ..options import BundlePrice, StardewValleyOptions, ExcludeGingerIsland, FestivalLocations, TrapDifficulty, \
MultipleDaySleepEnabled, Gifting, EntranceRandomization
from ..strings.bundle_names import MemeBundleName
from ..strings.currency_names import Currency, MemeCurrency
@dataclass
@@ -19,6 +22,10 @@ class Bundle:
def __repr__(self):
return f"{self.name} -> {self.number_required} from {repr(self.items)}"
def special_behavior(self, world):
if self.name == MemeBundleName.clickbait:
world.options.exclude_locations.value.add(MemeBundleName.clickbait)
@dataclass
class BundleTemplate:
@@ -42,7 +49,42 @@ class BundleTemplate:
template.number_required_items)
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
number_required, price_multiplier = get_bundle_final_prices(options.bundle_price, self.number_required_items, False)
try:
number_required, price_multiplier = get_bundle_final_prices(options.bundle_price, self.number_required_items, False)
filtered_items = [item for item in self.items if item.can_appear(content, 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)
chosen_items = [item.as_amount(min(999, max(1, math.floor(item.amount * price_multiplier)))) for item in chosen_items]
return Bundle(self.room, self.name, chosen_items, number_required)
except Exception as e:
raise Exception(f"Failed at creating bundle '{self.name}'. Error: {e}")
def can_appear(self, options: StardewValleyOptions) -> bool:
if self.name == MemeBundleName.trap and options.trap_items.value == TrapDifficulty.option_no_traps:
return False
if self.name == MemeBundleName.hibernation and options.multiple_day_sleep_enabled == MultipleDaySleepEnabled.option_false:
return False
if self.name == MemeBundleName.cooperation and options.gifting == Gifting.option_false:
return False
return True
class FixedMultiplierBundleTemplate(BundleTemplate):
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
number_required_items: int):
super().__init__(room, name, items, number_possible_items, number_required_items)
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
number_required = get_number_required_items(options.bundle_price, self.number_required_items)
filtered_items = [item for item in self.items if item.can_appear(content, options)]
number_items = len(filtered_items)
number_chosen_items = self.number_possible_items
@@ -53,11 +95,53 @@ class BundleTemplate:
chosen_items = filtered_items + random.choices(filtered_items, k=number_chosen_items - number_items)
else:
chosen_items = random.sample(filtered_items, number_chosen_items)
chosen_items = [item.as_amount(max(1, math.floor(item.amount * price_multiplier))) for item in chosen_items]
chosen_items = [item.as_amount(min(999, max(1, item.amount))) for item in chosen_items]
return Bundle(self.room, self.name, chosen_items, number_required)
def can_appear(self, options: StardewValleyOptions) -> bool:
return True
class FixedPriceBundleTemplate(BundleTemplate):
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
number_required_items: int):
super().__init__(room, name, items, number_possible_items, number_required_items)
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
filtered_items = [item for item in self.items if item.can_appear(content, options)]
number_items = len(filtered_items)
number_chosen_items = self.number_possible_items
if number_chosen_items < self.number_required_items:
number_chosen_items = self.number_required_items
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)
chosen_items = [item.as_amount(max(1, math.floor(item.amount))) for item in chosen_items]
return Bundle(self.room, self.name, chosen_items, self.number_required_items)
# This type of bundle will always match the number of slots and the number of items
class FixedSlotsBundleTemplate(BundleTemplate):
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
number_required_items: int):
super().__init__(room, name, items, number_possible_items, number_required_items)
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
try:
number_required, price_multiplier = get_bundle_final_prices(options.bundle_price, self.number_required_items, False)
filtered_items = [item for item in self.items if item.can_appear(content, options)]
number_items = len(filtered_items)
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)
chosen_items = [item.as_amount(min(999, max(1, math.floor(item.amount * price_multiplier)))) for item in chosen_items]
return Bundle(self.room, self.name, chosen_items, number_required)
except Exception as e:
raise Exception(f"Failed at creating bundle '{self.name}'. Error: {e}")
class CurrencyBundleTemplate(BundleTemplate):
@@ -77,15 +161,33 @@ class CurrencyBundleTemplate(BundleTemplate):
return currency_amount
def can_appear(self, options: StardewValleyOptions) -> bool:
if not super().can_appear(options):
return False
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
if options.entrance_randomization != EntranceRandomization.option_disabled:
if self.item.item_name == MemeCurrency.time_elapsed:
return False
if options.death_link != DeathLink.option_true:
if self.item.item_name == MemeCurrency.deathlinks:
return False
return True
class FixedPriceCurrencyBundleTemplate(CurrencyBundleTemplate):
def __init__(self, room: str, name: str, item: BundleItem):
super().__init__(room, name, item)
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
currency_amount = self.item.amount
return Bundle(self.room, self.name, [BundleItem(self.item.item_name, currency_amount)], 1)
class MoneyBundleTemplate(CurrencyBundleTemplate):
def __init__(self, room: str, default_name: str, item: BundleItem):
@@ -111,11 +213,15 @@ class MoneyBundleTemplate(CurrencyBundleTemplate):
class IslandBundleTemplate(BundleTemplate):
def can_appear(self, options: StardewValleyOptions) -> bool:
if not super().can_appear(options):
return False
return options.exclude_ginger_island == ExcludeGingerIsland.option_false
class FestivalBundleTemplate(BundleTemplate):
def can_appear(self, options: StardewValleyOptions) -> bool:
if not super().can_appear(options):
return False
return options.festival_locations != FestivalLocations.option_disabled
@@ -149,6 +255,96 @@ class DeepBundleTemplate(BundleTemplate):
return Bundle(self.room, self.name, chosen_items, number_required)
class FixedPriceDeepBundleTemplate(DeepBundleTemplate):
def __init__(self, room: str, name: str, categories: List[List[BundleItem]], number_possible_items: int,
number_required_items: int):
super().__init__(room, name, categories, number_possible_items, number_required_items)
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
number_required = self.number_required_items
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(content, options)]
chosen_items.append(random.choice(filtered_items))
chosen_items = [item.as_amount(max(1, math.floor(item.amount))) for item in chosen_items]
return Bundle(self.room, self.name, chosen_items, number_required)
@dataclass
class BureaucracyBundleTemplate(BundleTemplate):
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
number_required_items: int):
super(BureaucracyBundleTemplate, self).__init__(room, name, items, number_possible_items, number_required_items)
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
number_required = self.number_required_items + options.bundle_price.value
number_required = min(12, max(4, number_required))
if options.bundle_price == BundlePrice.option_minimum:
number_required = 4
if options.bundle_price == BundlePrice.option_maximum:
number_required = 12
price_multiplier = 1
filtered_items = [item for item in self.items if item.can_appear(content, 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)
chosen_items = [item.as_amount(max(1, math.floor(item.amount * price_multiplier))) for item in chosen_items]
return Bundle(self.room, self.name, chosen_items, number_required)
@dataclass
class RecursiveBundleTemplate(BundleTemplate):
number_sub_bundles: int
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
number_required_items: int, number_sub_bundles: int):
super(RecursiveBundleTemplate, self).__init__(room, name, items, number_possible_items, number_required_items)
self.number_sub_bundles = number_sub_bundles
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
number_required = self.number_required_items + (options.bundle_price.value * self.number_sub_bundles)
if options.bundle_price == BundlePrice.option_minimum:
number_required = self.number_sub_bundles
if options.bundle_price == BundlePrice.option_maximum:
number_required = self.number_sub_bundles * 8
number_required = min(self.number_sub_bundles * 8, max(self.number_sub_bundles, number_required))
price_multiplier = get_price_multiplier(options.bundle_price, False)
filtered_items = [item for item in self.items if item.can_appear(content, 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)
chosen_items = [item.as_amount(max(1, math.floor(item.amount * price_multiplier))) for item in chosen_items]
return Bundle(self.room, self.name, chosen_items, number_required)
def get_bundle_final_prices(bundle_price_option: BundlePrice, default_required_items: int, is_currency: bool) -> Tuple[int, float]:
number_required_items = get_number_required_items(bundle_price_option, default_required_items)
price_multiplier = get_price_multiplier(bundle_price_option, is_currency)

View File

@@ -37,6 +37,11 @@ class MasteryItemSource(BundleItemSource):
return content.features.skill_progression.are_masteries_shuffled
class QiBoardItemSource(BundleItemSource):
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
return content_packs.qi_board_content_pack.name in content.registered_packs
class ContentItemSource(BundleItemSource):
"""This is meant to be used for items that are managed by the content packs."""
@@ -51,6 +56,7 @@ class BundleItem:
island = IslandItemSource()
festival = FestivalItemSource()
masteries = MasteryItemSource()
qi_board = QiBoardItemSource()
content = ContentItemSource()
item_name: str

View File

@@ -5,6 +5,7 @@ from typing import List
from .bundle import Bundle, BundleTemplate
from ..content import StardewContent
from ..options import StardewValleyOptions
from ..strings.bundle_names import CCRoom
@dataclass
@@ -12,6 +13,24 @@ class BundleRoom:
name: str
bundles: List[Bundle]
def special_behavior(self, world):
for bundle in self.bundles:
bundle.special_behavior(world)
def simplify_name(name: str) -> str:
return name.lower().replace(" ", "").replace("-", "").replace("_", "").replace(".", "")
# In the context of meme bundles, some of the bundles are directly references to specific people, mostly content creators.
# This ensures that they roll their own bundle as part of their community center.
def is_bundle_related_to_player(bundle: BundleTemplate, player_name: str) -> bool:
if player_name == "":
return False
simple_bundle = simplify_name(bundle.name)
simple_player = simplify_name(player_name)
return simple_player in simple_bundle or simple_bundle in simple_player
@dataclass
class BundleRoomTemplate:
@@ -19,25 +38,40 @@ class BundleRoomTemplate:
bundles: List[BundleTemplate]
number_bundles: int
def create_bundle_room(self, random: Random, content: StardewContent, options: StardewValleyOptions):
def create_bundle_room(self, random: Random, content: StardewContent, options: StardewValleyOptions, player_name: str = "", is_entire_cc: bool = False):
filtered_bundles = [bundle for bundle in self.bundles if bundle.can_appear(options)]
priority_bundles = []
whitelist_bundles = []
unpriority_bundles = []
blacklist_bundles = []
for bundle in filtered_bundles:
if bundle.name in options.bundle_plando:
priority_bundles.append(bundle)
else:
if options.bundle_whitelist.prioritizes(bundle.name) or is_bundle_related_to_player(bundle, player_name):
whitelist_bundles.append(bundle)
elif options.bundle_blacklist.allows(bundle.name):
unpriority_bundles.append(bundle)
if self.number_bundles <= len(priority_bundles):
chosen_bundles = random.sample(priority_bundles, self.number_bundles)
else:
chosen_bundles = priority_bundles
num_remaining_bundles = self.number_bundles - len(priority_bundles)
if num_remaining_bundles > len(unpriority_bundles):
chosen_bundles.extend(random.choices(unpriority_bundles, k=num_remaining_bundles))
else:
blacklist_bundles.append(bundle)
modifier = options.bundle_per_room.value
if is_entire_cc:
modifier *= 6
number_bundles = self.number_bundles + modifier
bundles_cap = max(len(filtered_bundles), self.number_bundles)
number_bundles = max(1, min(bundles_cap, number_bundles))
if number_bundles < len(whitelist_bundles):
chosen_bundles = random.sample(whitelist_bundles, number_bundles)
else:
chosen_bundles = whitelist_bundles
num_remaining_bundles = number_bundles - len(whitelist_bundles)
if num_remaining_bundles < len(unpriority_bundles):
chosen_bundles.extend(random.sample(unpriority_bundles, num_remaining_bundles))
else:
chosen_bundles.extend(unpriority_bundles)
num_remaining_bundles = num_remaining_bundles - len(unpriority_bundles)
if num_remaining_bundles > 0:
if self.name == CCRoom.raccoon_requests:
chosen_bundles.extend(random.choices(unpriority_bundles, k=num_remaining_bundles))
else:
chosen_bundles.extend(random.sample(blacklist_bundles, num_remaining_bundles))
return BundleRoom(self.name, [bundle.create_bundle(random, content, options) for bundle in chosen_bundles])

View File

@@ -1,19 +1,26 @@
from random import Random
from typing import List, Tuple
from typing import List, Tuple, Dict
from .bundle import Bundle
from .bundle_room import BundleRoom, BundleRoomTemplate
from ..content import StardewContent
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, raccoon_vanilla, raccoon_thematic, raccoon_remixed, \
community_center_remixed_anywhere
from ..data.bundles_data.bundle_data import pantry_remixed, \
crafts_room_remixed, fish_tank_remixed, boiler_room_remixed, bulletin_board_remixed, vault_remixed, \
all_bundle_items_except_money, \
abandoned_joja_mart_remixed, giant_stump_remixed
from ..data.bundles_data.bundle_set import vanilla_bundles, remixed_bundles, thematic_bundles
from ..data.bundles_data.meme_bundles import community_center_meme_bundles, pantry_meme, crafts_room_meme, \
fish_tank_meme, bulletin_board_meme, \
boiler_room_meme, vault_meme
from ..data.bundles_data.remixed_anywhere_bundles import community_center_remixed_anywhere
from ..data.game_item import ItemTag
from ..data.recipe_data import all_cooking_recipes
from ..logic.logic import StardewLogic
from ..options import BundleRandomization, StardewValleyOptions
from ..strings.bundle_names import CCRoom
def get_all_bundles(random: Random, logic: StardewLogic, content: StardewContent, options: StardewValleyOptions) -> List[BundleRoom]:
def get_all_bundles(random: Random, logic: StardewLogic, content: StardewContent, options: StardewValleyOptions, player_name: str) -> List[BundleRoom]:
if options.bundle_randomization == BundleRandomization.option_vanilla:
return get_vanilla_bundles(random, content, options)
elif options.bundle_randomization == BundleRandomization.option_thematic:
@@ -24,72 +31,76 @@ def get_all_bundles(random: Random, logic: StardewLogic, content: StardewContent
return get_remixed_bundles_anywhere(random, content, options)
elif options.bundle_randomization == BundleRandomization.option_shuffled:
return get_shuffled_bundles(random, logic, content, options)
elif options.bundle_randomization == BundleRandomization.option_meme:
return get_meme_bundles(random, content, options, player_name)
raise NotImplementedError
def get_vanilla_bundles(random: Random, content: StardewContent, options: StardewValleyOptions) -> List[BundleRoom]:
pantry = pantry_vanilla.create_bundle_room(random, content, options)
crafts_room = crafts_room_vanilla.create_bundle_room(random, content, options)
fish_tank = fish_tank_vanilla.create_bundle_room(random, content, options)
boiler_room = boiler_room_vanilla.create_bundle_room(random, content, options)
bulletin_board = bulletin_board_vanilla.create_bundle_room(random, content, options)
vault = vault_vanilla.create_bundle_room(random, content, options)
abandoned_joja_mart = abandoned_joja_mart_vanilla.create_bundle_room(random, content, options)
raccoon = raccoon_vanilla.create_bundle_room(random, content, options)
fix_raccoon_bundle_names(raccoon)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart, raccoon]
generated_bundle_rooms = {room_name: vanilla_bundles.bundles_by_room[room_name].create_bundle_room(random, content, options) for room_name in
vanilla_bundles.bundles_by_room}
fix_raccoon_bundle_names(generated_bundle_rooms[CCRoom.raccoon_requests])
return list(generated_bundle_rooms.values())
def get_thematic_bundles(random: Random, content: StardewContent, options: StardewValleyOptions) -> List[BundleRoom]:
pantry = pantry_thematic.create_bundle_room(random, content, options)
crafts_room = crafts_room_thematic.create_bundle_room(random, content, options)
fish_tank = fish_tank_thematic.create_bundle_room(random, content, options)
boiler_room = boiler_room_thematic.create_bundle_room(random, content, options)
bulletin_board = bulletin_board_thematic.create_bundle_room(random, content, options)
vault = vault_thematic.create_bundle_room(random, content, options)
abandoned_joja_mart = abandoned_joja_mart_thematic.create_bundle_room(random, content, options)
raccoon = raccoon_thematic.create_bundle_room(random, content, options)
fix_raccoon_bundle_names(raccoon)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart, raccoon]
generated_bundle_rooms = {room_name: thematic_bundles.bundles_by_room[room_name].create_bundle_room(random, content, options) for room_name in
thematic_bundles.bundles_by_room}
fix_raccoon_bundle_names(generated_bundle_rooms[CCRoom.raccoon_requests])
return list(generated_bundle_rooms.values())
def get_remixed_bundles(random: Random, content: StardewContent, options: StardewValleyOptions) -> List[BundleRoom]:
pantry = pantry_remixed.create_bundle_room(random, content, options)
crafts_room = crafts_room_remixed.create_bundle_room(random, content, options)
fish_tank = fish_tank_remixed.create_bundle_room(random, content, options)
boiler_room = boiler_room_remixed.create_bundle_room(random, content, options)
bulletin_board = bulletin_board_remixed.create_bundle_room(random, content, options)
vault = vault_remixed.create_bundle_room(random, content, options)
abandoned_joja_mart = abandoned_joja_mart_remixed.create_bundle_room(random, content, options)
raccoon = raccoon_remixed.create_bundle_room(random, content, options)
fix_raccoon_bundle_names(raccoon)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart, raccoon]
generated_bundle_rooms = {room_name: remixed_bundles.bundles_by_room[room_name].create_bundle_room(random, content, options) for room_name in
remixed_bundles.bundles_by_room}
fix_raccoon_bundle_names(generated_bundle_rooms[CCRoom.raccoon_requests])
return list(generated_bundle_rooms.values())
def get_remixed_bundles_anywhere(random: Random, content: StardewContent, options: StardewValleyOptions) -> List[BundleRoom]:
big_room = community_center_remixed_anywhere.create_bundle_room(random, content, options)
big_room = community_center_remixed_anywhere.create_bundle_room(random, content, options, is_entire_cc=True)
all_chosen_bundles = big_room.bundles
random.shuffle(all_chosen_bundles)
end_index = 0
pantry, end_index = create_room_from_bundles(pantry_remixed, all_chosen_bundles, end_index)
crafts_room, end_index = create_room_from_bundles(crafts_room_remixed, all_chosen_bundles, end_index)
fish_tank, end_index = create_room_from_bundles(fish_tank_remixed, all_chosen_bundles, end_index)
boiler_room, end_index = create_room_from_bundles(boiler_room_remixed, all_chosen_bundles, end_index)
bulletin_board, end_index = create_room_from_bundles(bulletin_board_remixed, all_chosen_bundles, end_index)
pantry, end_index = create_room_from_bundles(pantry_remixed, all_chosen_bundles, options, end_index)
crafts_room, end_index = create_room_from_bundles(crafts_room_remixed, all_chosen_bundles, options, end_index)
fish_tank, end_index = create_room_from_bundles(fish_tank_remixed, all_chosen_bundles, options, end_index)
boiler_room, end_index = create_room_from_bundles(boiler_room_remixed, all_chosen_bundles, options, end_index)
bulletin_board, end_index = create_room_from_bundles(bulletin_board_remixed, all_chosen_bundles, options, end_index)
vault, end_index = create_room_from_bundles(vault_remixed, all_chosen_bundles, options, end_index)
vault = vault_remixed.create_bundle_room(random, content, options)
abandoned_joja_mart = abandoned_joja_mart_remixed.create_bundle_room(random, content, options)
raccoon = raccoon_remixed.create_bundle_room(random, content, options)
raccoon = giant_stump_remixed.create_bundle_room(random, content, options)
fix_raccoon_bundle_names(raccoon)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart, raccoon]
def create_room_from_bundles(template: BundleRoomTemplate, all_bundles: List[Bundle], end_index: int) -> Tuple[BundleRoom, int]:
def get_meme_bundles(random: Random, content: StardewContent, options: StardewValleyOptions, player_name: str) -> List[BundleRoom]:
big_room = community_center_meme_bundles.create_bundle_room(random, content, options, player_name, is_entire_cc=True)
all_chosen_bundles = big_room.bundles
random.shuffle(all_chosen_bundles)
end_index = 0
pantry, end_index = create_room_from_bundles(pantry_meme, all_chosen_bundles, options, end_index)
crafts_room, end_index = create_room_from_bundles(crafts_room_meme, all_chosen_bundles, options, end_index)
fish_tank, end_index = create_room_from_bundles(fish_tank_meme, all_chosen_bundles, options, end_index)
boiler_room, end_index = create_room_from_bundles(boiler_room_meme, all_chosen_bundles, options, end_index)
bulletin_board, end_index = create_room_from_bundles(bulletin_board_meme, all_chosen_bundles, options, end_index)
vault, end_index = create_room_from_bundles(vault_meme, all_chosen_bundles, options, end_index)
abandoned_joja_mart = abandoned_joja_mart_remixed.create_bundle_room(random, content, options)
raccoon = giant_stump_remixed.create_bundle_room(random, content, options)
fix_raccoon_bundle_names(raccoon)
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart, raccoon]
def create_room_from_bundles(template: BundleRoomTemplate, all_bundles: List[Bundle], options: StardewValleyOptions, end_index: int) -> Tuple[BundleRoom, int]:
start_index = end_index
end_index += template.number_bundles
end_index += template.number_bundles + options.bundle_per_room.value
return BundleRoom(template.name, all_bundles[start_index:end_index]), end_index
@@ -122,3 +133,31 @@ def fix_raccoon_bundle_names(raccoon):
for i in range(len(raccoon.bundles)):
raccoon_bundle = raccoon.bundles[i]
raccoon_bundle.name = f"Raccoon Request {i + 1}"
def get_trash_bear_requests(random: Random, content: StardewContent, options: StardewValleyOptions) -> Dict[str, List[str]]:
trash_bear_requests = dict()
num_per_type = 2
if options.bundle_price < 0:
num_per_type = 1
elif options.bundle_price > 0:
num_per_type = min(4, num_per_type + options.bundle_price)
trash_bear_requests["Foraging"] = pick_trash_bear_items(ItemTag.FORAGE, content, num_per_type, random)
if options.bundle_per_room >= 0:
# Cooking items are not in content packs yet. This can be simplified once they are
# trash_bear_requests["Cooking"] = pick_trash_bear_items(ItemTag.COOKING, content, num_per_type, random)
trash_bear_requests["Cooking"] = random.sample(
[recipe.meal for recipe in all_cooking_recipes if not recipe.content_pack or content.is_enabled(recipe.content_pack)], num_per_type)
if options.bundle_per_room >= 1:
trash_bear_requests["Farming"] = pick_trash_bear_items(ItemTag.CROPSANITY, content, num_per_type, random)
if options.bundle_per_room >= 2:
# Fish items are not tagged properly in content packs yet. This can be simplified once they are
# trash_bear_requests["Fishing"] = pick_trash_bear_items(ItemTag.FISH, content, num_per_type, random)
trash_bear_requests["Fishing"] = random.sample([fish for fish in content.fishes], num_per_type)
return trash_bear_requests
def pick_trash_bear_items(item_tag: ItemTag, content: StardewContent, number_items: int, random: Random):
forage_items = [item.name for item in content.find_tagged_items(item_tag)]
return random.sample(forage_items, number_items)

View File

@@ -0,0 +1,284 @@
from __future__ import annotations
import asyncio
import re
# webserver imports
import urllib.parse
from collections.abc import Iterable
import Utils
from BaseClasses import CollectionState, Location
from CommonClient import logger, get_base_parser, gui_enabled, server_loop
from MultiServer import mark_raw
from NetUtils import JSONMessagePart
from kvui import CommandPromptTextInput
from . import StardewValleyWorld
from .logic.logic import StardewLogic
from .stardew_rule.rule_explain import explain, ExplainMode, RuleExplanation
try:
from worlds.tracker.TrackerClient import TrackerGameContext, TrackerCommandProcessor as ClientCommandProcessor, UT_VERSION # noqa
from worlds.tracker.TrackerCore import TrackerCore
tracker_loaded = True
except ImportError as e:
logger.error(e)
from CommonClient import CommonContext, ClientCommandProcessor
TrackerCore = object
class TrackerGameContextMixin:
"""Expecting the TrackerGameContext to have these methods."""
tracker_core: TrackerCore
def make_gui(self, manager):
...
def run_generator(self):
...
class TrackerGameContext(CommonContext, TrackerGameContextMixin):
pass
tracker_loaded = False
UT_VERSION = "Not found"
class StardewCommandProcessor(ClientCommandProcessor):
ctx: StardewClientContext
@mark_raw
def _cmd_explain(self, location: str = ""):
"""Explain the logic behind a location."""
logic = self.ctx.logic
if logic is None:
return
try:
rule = logic.region.can_reach_location(location)
expl = explain(rule, self.ctx.current_state, expected=None, mode=ExplainMode.CLIENT)
except KeyError:
result, usable, response = Utils.get_intended_text(location, [loc.name for loc in self.ctx.all_locations])
if usable:
rule = logic.region.can_reach_location(result)
expl = explain(rule, self.ctx.current_state, expected=None, mode=ExplainMode.CLIENT)
else:
self.ctx.ui.last_autofillable_command = "/explain"
self.output(response)
return
self.ctx.previous_explanation = expl
self.ctx.ui.print_json(parse_explanation(expl))
@mark_raw
def _cmd_explain_item(self, item: str = ""):
"""Explain the logic behind a game item."""
logic = self.ctx.logic
if logic is None:
return
result, usable, response = Utils.get_intended_text(item, logic.registry.item_rules.keys())
if usable:
rule = logic.has(result)
expl = explain(rule, self.ctx.current_state, expected=None, mode=ExplainMode.CLIENT)
else:
self.ctx.ui.last_autofillable_command = "/explain_item"
self.output(response)
return
self.ctx.previous_explanation = expl
self.ctx.ui.print_json(parse_explanation(expl))
@mark_raw
def _cmd_explain_missing(self, location: str = ""):
"""Explain what is missing for a location to be in logic. It explains the logic behind a location, while skipping the rules that are already satisfied."""
self.__explain("/explain_missing", location, expected=True)
@mark_raw
def _cmd_explain_how(self, location: str = ""):
"""Explain how a location is in logic. It explains the logic behind the location, while skipping the rules that are not satisfied."""
self.__explain("/explain_how", location, expected=False)
def __explain(self, command: str, location: str, expected: bool | None = None):
logic = self.ctx.logic
if logic is None:
return
try:
rule = logic.region.can_reach_location(location)
expl = explain(rule, self.ctx.current_state, expected=expected, mode=ExplainMode.CLIENT)
except KeyError:
result, usable, response = Utils.get_intended_text(location, [loc.name for loc in self.ctx.all_locations])
if usable:
rule = logic.region.can_reach_location(result)
expl = explain(rule, self.ctx.current_state, expected=expected, mode=ExplainMode.CLIENT)
else:
self.ctx.ui.last_autofillable_command = command
self.output(response)
return
self.ctx.previous_explanation = expl
self.ctx.ui.print_json(parse_explanation(expl))
@mark_raw
def _cmd_more(self, index: str = ""):
"""Will tell you what's missing to consider a location in logic."""
if self.ctx.previous_explanation is None:
self.output("No previous explanation found.")
return
try:
expl = self.ctx.previous_explanation.more(int(index))
except (ValueError, IndexError):
self.output("Which previous rule do you want to explained?")
self.ctx.ui.last_autofillable_command = "/more"
for i, rule in enumerate(self.ctx.previous_explanation.more_explanations):
# TODO handle autofillable commands
self.output(f"/more {i} -> {str(rule)})")
return
self.ctx.previous_explanation = expl
self.ctx.ui.print_json(parse_explanation(expl))
if not tracker_loaded:
del _cmd_explain
del _cmd_explain_missing
class StardewClientContext(TrackerGameContext):
game = "Stardew Valley"
command_processor = StardewCommandProcessor
previous_explanation: RuleExplanation | None = None
def make_gui(self):
ui = super().make_gui() # before the kivy imports so kvui gets loaded first
class StardewManager(ui):
base_title = f"Stardew Valley Tracker with UT {UT_VERSION} for AP version" # core appends ap version so this works
ctx: StardewClientContext
def build(self):
container = super().build()
if not tracker_loaded:
logger.info("To enable the tracker page, install Universal Tracker.")
# Until self.ctx.ui.last_autofillable_command allows for / commands, this is needed to remove the "!" before the /commands when using intended text autofill.
def on_text_remove_hardcoded_exclamation_mark_garbage(textinput: CommandPromptTextInput, text: str) -> None:
if text.startswith("!/"):
textinput.text = text[1:]
self.textinput.bind(text=on_text_remove_hardcoded_exclamation_mark_garbage)
return container
return StardewManager
@property
def logic(self) -> StardewLogic | None:
if self.tracker_core.get_current_world() is None:
logger.warning("Internal logic was not able to load, check your yamls and relaunch.")
return None
if self.game != "Stardew Valley":
logger.warning(f"Please connect to a slot with explainable logic (not {self.game}).")
return None
return self.tracker_core.get_current_world().logic
@property
def current_state(self) -> CollectionState:
return self.tracker_core.updateTracker().state
@property
def world(self) -> StardewValleyWorld:
return self.tracker_core.get_current_world()
@property
def all_locations(self) -> Iterable[Location]:
return self.tracker_core.multiworld.get_locations(self.tracker_core.player_id)
def parse_explanation(explanation: RuleExplanation) -> list[JSONMessagePart]:
# Split the explanation in parts, by isolating all the delimiters, being \(, \), & , -> , | , \d+x , \[ , \] , \(\w+\), \n\s*
result_regex = r"(\(|\)| & | -> | \| |\d+x | \[|\](?: ->)?\s*| \(\w+\)|\n\s*)"
splits = re.split(result_regex, str(explanation).strip())
messages = []
for s in splits:
if len(s) == 0:
continue
if s == "True":
messages.append({"type": "color", "color": "green", "text": s})
elif s == "False":
messages.append({"type": "color", "color": "salmon", "text": s})
elif s.startswith("Reach Location "):
messages.append({"type": "text", "text": "Reach Location "})
messages.append({"type": "location_name", "text": s[15:]})
elif s.startswith("Reach Entrance "):
messages.append({"type": "text", "text": "Reach Entrance "})
messages.append({"type": "entrance_name", "text": s[15:]})
elif s.startswith("Reach Region "):
messages.append({"type": "text", "text": "Reach Region "})
messages.append({"type": "color", "color": "yellow", "text": s[13:]})
elif s.startswith("Received event "):
messages.append({"type": "text", "text": "Received event "})
messages.append({"type": "item_name", "text": s[15:]})
elif s.startswith("Received "):
messages.append({"type": "text", "text": "Received "})
messages.append({"type": "item_name", "flags": 0b001, "text": s[9:]})
elif s.startswith("Has "):
if s[4].isdigit():
messages.append({"type": "text", "text": "Has "})
digit_end = re.search(r"\D", s[4:])
digit = s[4:4 + digit_end.start()]
messages.append({"type": "color", "color": "cyan", "text": digit})
messages.append({"type": "text", "text": s[4 + digit_end.start():]})
else:
messages.append({"text": s, "type": "text"})
else:
messages.append({"text": s, "type": "text"})
return messages
async def main(args):
ctx = StardewClientContext(args.connect, args.password)
ctx.auth = args.name
ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop")
if tracker_loaded:
ctx.run_generator()
else:
logger.warning("Could not find Universal Tracker.")
if gui_enabled:
ctx.run_gui()
ctx.run_cli()
await ctx.exit_event.wait()
await ctx.shutdown()
def launch(*args):
parser = get_base_parser(description="Gameless Archipelago Client, for text interfacing.")
parser.add_argument('--name', default=None, help="Slot Name to connect as.")
parser.add_argument("url", nargs="?", help="Archipelago connection url")
args = parser.parse_args(args)
if args.url:
url = urllib.parse.urlparse(args.url)
args.connect = url.netloc
if url.username:
args.name = urllib.parse.unquote(url.username)
if url.password:
args.password = urllib.parse.unquote(url.password)
asyncio.run(main(args))

View File

@@ -1,8 +1,9 @@
from . import content_packs
from .feature import cropsanity, friendsanity, fishsanity, booksanity, building_progression, skill_progression, tool_progression
from .feature import cropsanity, friendsanity, fishsanity, booksanity, building_progression, skill_progression, tool_progression, hatsanity, museumsanity
from .game_content import ContentPack, StardewContent, StardewFeatures
from .unpacking import unpack_content
from .. import options
from ..strings.ap_names.ap_option_names import StartWithoutOptionName
from ..strings.building_names import Building
@@ -34,8 +35,10 @@ def choose_features(player_options: options.StardewValleyOptions) -> StardewFeat
choose_cropsanity(player_options.cropsanity),
choose_fishsanity(player_options.fishsanity),
choose_friendsanity(player_options.friendsanity, player_options.friendsanity_heart_size),
choose_hatsanity(player_options.hatsanity),
choose_museumsanity(player_options.museumsanity),
choose_skill_progression(player_options.skill_progression),
choose_tool_progression(player_options.tool_progression, player_options.skill_progression),
choose_tool_progression(player_options.tool_progression, player_options.skill_progression, player_options.start_without),
)
@@ -56,6 +59,33 @@ def choose_booksanity(booksanity_option: options.Booksanity) -> booksanity.Books
return booksanity_feature
def choose_building_progression(building_option: options.BuildingProgression,
farm_type_option: options.FarmType) -> building_progression.BuildingProgressionFeature:
starting_buildings = {Building.farm_house, Building.pet_bowl, Building.shipping_bin}
if farm_type_option == options.FarmType.option_meadowlands:
starting_buildings.add(Building.coop)
if (building_option == options.BuildingProgression.option_vanilla
or building_option == options.BuildingProgression.option_vanilla_cheap
or building_option == options.BuildingProgression.option_vanilla_very_cheap):
return building_progression.BuildingProgressionVanilla(
starting_buildings=starting_buildings,
)
starting_buildings.remove(Building.shipping_bin)
starting_buildings.remove(Building.pet_bowl)
if (building_option == options.BuildingProgression.option_progressive
or building_option == options.BuildingProgression.option_progressive_cheap
or building_option == options.BuildingProgression.option_progressive_very_cheap):
return building_progression.BuildingProgressionProgressive(
starting_buildings=starting_buildings,
)
raise ValueError(f"No building progression feature mapped to {str(building_option.value)}")
cropsanity_by_option = {
options.Cropsanity.option_disabled: cropsanity.CropsanityDisabled(),
options.Cropsanity.option_enabled: cropsanity.CropsanityEnabled(),
@@ -111,30 +141,27 @@ def choose_friendsanity(friendsanity_option: options.Friendsanity, heart_size: o
raise ValueError(f"No friendsanity feature mapped to {str(friendsanity_option.value)}")
def choose_building_progression(building_option: options.BuildingProgression,
farm_type_option: options.FarmType) -> building_progression.BuildingProgressionFeature:
starting_buildings = {Building.farm_house, Building.pet_bowl, Building.shipping_bin}
def choose_hatsanity(hat_option: options.Hatsanity) -> hatsanity.HatsanityFeature:
if hat_option == options.Hatsanity.preset_none:
return hatsanity.HatsanityNone()
if farm_type_option == options.FarmType.option_meadowlands:
starting_buildings.add(Building.coop)
return hatsanity.HatsanityHats(enabled_hats=frozenset(hat_option.value))
if (building_option == options.BuildingProgression.option_vanilla
or building_option == options.BuildingProgression.option_vanilla_cheap
or building_option == options.BuildingProgression.option_vanilla_very_cheap):
return building_progression.BuildingProgressionVanilla(
starting_buildings=starting_buildings,
)
starting_buildings.remove(Building.shipping_bin)
def choose_museumsanity(museumsanity_option: options.Museumsanity) -> museumsanity.MuseumsanityFeature:
if museumsanity_option == options.Museumsanity.option_none:
return museumsanity.MuseumsanityNone()
if (building_option == options.BuildingProgression.option_progressive
or building_option == options.BuildingProgression.option_progressive_cheap
or building_option == options.BuildingProgression.option_progressive_very_cheap):
return building_progression.BuildingProgressionProgressive(
starting_buildings=starting_buildings,
)
if museumsanity_option == options.Museumsanity.option_milestones:
return museumsanity.MuseumsanityMilestones()
raise ValueError(f"No building progression feature mapped to {str(building_option.value)}")
if museumsanity_option == options.Museumsanity.option_randomized:
return museumsanity.MuseumsanityRandomized()
if museumsanity_option == options.Museumsanity.option_all:
return museumsanity.MuseumsanityAll()
raise ValueError(f"No museumsanity feature mapped to {str(museumsanity_option.value)}")
skill_progression_by_option = {
@@ -153,16 +180,17 @@ def choose_skill_progression(skill_progression_option: options.SkillProgression)
return skill_progression_feature
def choose_tool_progression(tool_option: options.ToolProgression, skill_option: options.SkillProgression) -> tool_progression.ToolProgressionFeature:
def choose_tool_progression(tool_option: options.ToolProgression, skill_option: options.SkillProgression, start_without_option: options.StartWithout) -> tool_progression.ToolProgressionFeature:
if tool_option.is_vanilla:
return tool_progression.ToolProgressionVanilla()
tools_distribution = tool_progression.get_tools_distribution(
progressive_tools_enabled=True,
skill_masteries_enabled=skill_option == options.SkillProgression.option_progressive_with_masteries,
)
if tool_option.is_progressive:
return tool_progression.ToolProgressionProgressive(tools_distribution)
starting_tools, tools_distribution = tool_progression.get_tools_distribution(
progressive_tools_enabled=True,
skill_masteries_enabled=skill_option == options.SkillProgression.option_progressive_with_masteries,
no_starting_tools_enabled=bool(StartWithoutOptionName.tools in start_without_option),
)
return tool_progression.ToolProgressionProgressive(starting_tools, tools_distribution)
raise ValueError(f"No tool progression feature mapped to {str(tool_option.value)}")

View File

@@ -11,14 +11,6 @@ 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__)
@@ -28,4 +20,13 @@ for mod_module in mod_modules:
module = importlib.import_module("." + module_name, mods.__name__)
loaded_modules[module_name] = module
assert by_mod
vanilla_content_pack_names = frozenset({
base_game.name,
ginger_island_content_pack.name,
pelican_town.name,
qi_board_content_pack.name,
the_desert.name,
the_farm.name,
the_mines.name
})
all_content_pack_names = vanilla_content_pack_names | frozenset(by_mod.keys())

View File

@@ -1,7 +1,31 @@
from . import booksanity
from . import building_progression
from . import cropsanity
from . import fishsanity
from . import friendsanity
from . import skill_progression
from . import tool_progression
from . import booksanity, building_progression, cropsanity, fishsanity, friendsanity, hatsanity, museumsanity, skill_progression, tool_progression
from .booksanity import BooksanityFeature
from .building_progression import BuildingProgressionFeature
from .cropsanity import CropsanityFeature
from .fishsanity import FishsanityFeature
from .friendsanity import FriendsanityFeature
from .hatsanity import HatsanityFeature
from .museumsanity import MuseumsanityFeature
from .skill_progression import SkillProgressionFeature
from .tool_progression import ToolProgressionFeature
__exports__ = [
"booksanity",
"BooksanityFeature",
"building_progression",
"BuildingProgressionFeature",
"cropsanity",
"CropsanityFeature",
"fishsanity",
"FishsanityFeature",
"friendsanity",
"FriendsanityFeature",
"hatsanity",
"HatsanityFeature",
"museumsanity",
"MuseumsanityFeature",
"skill_progression",
"SkillProgressionFeature",
"tool_progression"
"ToolProgressionFeature",
]

View File

@@ -0,0 +1,71 @@
import inspect
from abc import ABC
from collections.abc import Mapping
from typing import TYPE_CHECKING, Protocol
from ...data.game_item import Source, Requirement
if TYPE_CHECKING:
from ... import StardewContent
class DisableSourceHook(Protocol):
def __call__(self, source: Source, /, *, content: "StardewContent") -> bool:
"""Return True if the source should be disabled by this feature."""
...
class DisableRequirementHook(Protocol):
def __call__(self, requirement: Requirement, /, *, content: "StardewContent") -> bool:
"""Return True if the requirement should be disabled by this feature."""
...
def wrap_optional_content_arg(hook):
"""Wraps a hook to ensure it has the correct signature."""
if "content" in hook.__annotations__:
return hook
def wrapper(*args, content: "StardewContent", **kwargs):
return hook(*args, **kwargs)
return wrapper
class FeatureBase(ABC):
@property
def disable_source_hooks(self) -> Mapping[type[Source], DisableSourceHook]:
"""All hooks to call when a source is created to check if it has to be disabled by this feature."""
disable_source_hooks = {}
for attribute_name in dir(self):
if not attribute_name.startswith("_disable_") or not callable((attribute := getattr(self, attribute_name))):
continue
sig = inspect.signature(attribute)
source_param = sig.parameters.get("source")
if source_param is not None:
source_type = source_param.annotation
disable_source_hooks[source_type] = wrap_optional_content_arg(attribute)
continue
return disable_source_hooks
@property
def disable_requirement_hooks(self) -> Mapping[type[Requirement], DisableRequirementHook]:
"""All hooks to call when a requirement is created to check if it has to be disabled by this feature."""
disable_requirement_hooks = {}
for attribute_name in dir(self):
if not attribute_name.startswith("_disable_") or not callable((attribute := getattr(self, attribute_name))):
continue
sig = inspect.signature(attribute)
requirement_param = sig.parameters.get("requirement")
if requirement_param is not None:
requirement_type = requirement_param.annotation
disable_requirement_hooks[requirement_type] = wrap_optional_content_arg(attribute)
continue
return disable_requirement_hooks

View File

@@ -1,6 +1,8 @@
from abc import ABC, abstractmethod
from typing import ClassVar, Optional, Iterable
from collections.abc import Iterable
from typing import ClassVar
from .base import FeatureBase
from ...data.game_item import GameItem, ItemTag
from ...strings.book_names import ordered_lost_books
@@ -16,14 +18,14 @@ def to_location_name(book: str) -> str:
return location_prefix + book
def extract_book_from_location_name(location_name: str) -> Optional[str]:
def extract_book_from_location_name(location_name: str) -> str | None:
if not location_name.startswith(location_prefix):
return None
return location_name[len(location_prefix):]
class BooksanityFeature(ABC):
class BooksanityFeature(FeatureBase, ABC):
is_enabled: ClassVar[bool]
to_item_name = staticmethod(to_item_name)

View File

@@ -1,7 +1,8 @@
from abc import ABC
from dataclasses import dataclass
from typing import ClassVar, Set, Tuple
from typing import ClassVar
from .base import FeatureBase
from ...strings.building_names import Building
progressive_house = "Progressive House"
@@ -15,7 +16,7 @@ progressive_house_by_upgrade_name = {
}
def to_progressive_item(building: str) -> Tuple[str, int]:
def to_progressive_item(building: str) -> tuple[str, int]:
"""Return the name of the progressive item and its quantity required to unlock the building.
"""
if building in [Building.coop, Building.barn, Building.shed]:
@@ -35,9 +36,9 @@ def to_location_name(building: str) -> str:
@dataclass(frozen=True)
class BuildingProgressionFeature(ABC):
class BuildingProgressionFeature(FeatureBase, ABC):
is_progressive: ClassVar[bool]
starting_buildings: Set[str]
starting_buildings: set[str]
to_progressive_item = staticmethod(to_progressive_item)
progressive_house = progressive_house

View File

@@ -1,6 +1,7 @@
from abc import ABC, abstractmethod
from typing import ClassVar, Optional
from typing import ClassVar
from .base import FeatureBase
from ...data.game_item import GameItem, ItemTag
location_prefix = "Harvest "
@@ -10,14 +11,14 @@ def to_location_name(crop: str) -> str:
return location_prefix + crop
def extract_crop_from_location_name(location_name: str) -> Optional[str]:
def extract_crop_from_location_name(location_name: str) -> str | None:
if not location_name.startswith(location_prefix):
return None
return location_name[len(location_prefix):]
class CropsanityFeature(ABC):
class CropsanityFeature(FeatureBase, ABC):
is_enabled: ClassVar[bool]
to_location_name = staticmethod(to_location_name)

View File

@@ -1,7 +1,8 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import ClassVar, Optional
from typing import ClassVar
from .base import FeatureBase
from ...data.fish_data import FishItem
from ...strings.fish_names import Fish
@@ -12,7 +13,7 @@ def to_location_name(fish: str) -> str:
return location_prefix + fish
def extract_fish_from_location_name(location_name: str) -> Optional[str]:
def extract_fish_from_location_name(location_name: str) -> str | None:
if not location_name.startswith(location_prefix):
return None
@@ -20,7 +21,7 @@ def extract_fish_from_location_name(location_name: str) -> Optional[str]:
@dataclass(frozen=True)
class FishsanityFeature(ABC):
class FishsanityFeature(FeatureBase, ABC):
is_enabled: ClassVar[bool]
randomization_ratio: float = 1

View File

@@ -1,8 +1,9 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from functools import lru_cache
from typing import Optional, Tuple, ClassVar
from typing import ClassVar
from .base import FeatureBase
from ...data.villagers_data import Villager
from ...strings.villager_names import NPC
@@ -21,14 +22,14 @@ def to_location_name(npc_name: str, heart: int) -> str:
pet_heart_item_name = to_item_name(NPC.pet)
def extract_npc_from_item_name(item_name: str) -> Optional[str]:
def extract_npc_from_item_name(item_name: str) -> str | None:
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]:
def extract_npc_from_location_name(location_name: str) -> tuple[str | None, int]:
if not location_name.endswith(suffix):
return None, 0
@@ -38,12 +39,12 @@ def extract_npc_from_location_name(location_name: str) -> Tuple[Optional[str], i
@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, ...]:
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):
class FriendsanityFeature(FeatureBase, ABC):
is_enabled: ClassVar[bool]
heart_size: int
@@ -55,7 +56,7 @@ class FriendsanityFeature(ABC):
extract_npc_from_location_name = staticmethod(extract_npc_from_location_name)
@abstractmethod
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
def get_randomized_hearts(self, villager: Villager) -> tuple[int, ...]:
...
@property
@@ -63,7 +64,7 @@ class FriendsanityFeature(ABC):
return bool(self.get_pet_randomized_hearts())
@abstractmethod
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
def get_pet_randomized_hearts(self) -> tuple[int, ...]:
...
@@ -73,10 +74,10 @@ class FriendsanityNone(FriendsanityFeature):
def __init__(self):
super().__init__(1)
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
def get_randomized_hearts(self, villager: Villager) -> tuple[int, ...]:
return ()
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
def get_pet_randomized_hearts(self) -> tuple[int, ...]:
return ()
@@ -84,13 +85,13 @@ class FriendsanityNone(FriendsanityFeature):
class FriendsanityBachelors(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
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, ...]:
def get_pet_randomized_hearts(self) -> tuple[int, ...]:
return ()
@@ -98,7 +99,7 @@ class FriendsanityBachelors(FriendsanityFeature):
class FriendsanityStartingNpc(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
def get_randomized_hearts(self, villager: Villager) -> tuple[int, ...]:
if not villager.available:
return ()
@@ -107,7 +108,7 @@ class FriendsanityStartingNpc(FriendsanityFeature):
return get_heart_steps(10, self.heart_size)
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
def get_pet_randomized_hearts(self) -> tuple[int, ...]:
return get_heart_steps(5, self.heart_size)
@@ -115,13 +116,13 @@ class FriendsanityStartingNpc(FriendsanityFeature):
class FriendsanityAll(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
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, ...]:
def get_pet_randomized_hearts(self) -> tuple[int, ...]:
return get_heart_steps(5, self.heart_size)
@@ -129,11 +130,11 @@ class FriendsanityAll(FriendsanityFeature):
class FriendsanityAllWithMarriage(FriendsanityFeature):
is_enabled = True
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
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, ...]:
def get_pet_randomized_hearts(self) -> tuple[int, ...]:
return get_heart_steps(5, self.heart_size)

View File

@@ -0,0 +1,52 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import ClassVar
from .base import FeatureBase
from ...data.hats_data import HatItem
wear_prefix = "Wear "
def to_location_name(hat: str | HatItem) -> str:
if isinstance(hat, HatItem):
hat = hat.name
return f"{wear_prefix}{hat}"
def extract_hat_from_location_name(location_name: str) -> str | None:
if not location_name.startswith(wear_prefix):
return None
return location_name[len(wear_prefix):]
@dataclass(frozen=True)
class HatsanityFeature(FeatureBase, ABC):
is_enabled: ClassVar[bool]
to_location_name = staticmethod(to_location_name)
extract_hat_from_location_name = staticmethod(extract_hat_from_location_name)
@abstractmethod
def is_included(self, hat: HatItem) -> bool:
...
class HatsanityNone(HatsanityFeature):
is_enabled = False
def is_included(self, hat: HatItem) -> bool:
return False
@dataclass(frozen=True)
class HatsanityHats(HatsanityFeature):
is_enabled = True
enabled_hats: frozenset[str]
def is_included(self, hat: HatItem) -> bool:
for difficulty in hat.difficulty:
if difficulty not in self.enabled_hats:
return False
return True

View File

@@ -0,0 +1,38 @@
from abc import ABC
from dataclasses import dataclass
from typing import ClassVar
from .base import FeatureBase
from ...data.requirement import MuseumCompletionRequirement
@dataclass(frozen=True)
class MuseumsanityFeature(FeatureBase, ABC):
is_enabled: ClassVar[bool]
class MuseumsanityNone(MuseumsanityFeature):
is_enabled = False
@staticmethod
def _disable_museum_completion_requirement(requirement: MuseumCompletionRequirement) -> bool:
return True
@dataclass(frozen=True)
class MuseumsanityMilestones(MuseumsanityFeature):
is_enabled = True
@dataclass(frozen=True)
class MuseumsanityRandomized(MuseumsanityFeature):
is_enabled = True
amount_of_randomized_donations: int = 40
def _disable_museum_completion_requirement(self, requirement: MuseumCompletionRequirement) -> bool:
return requirement.number_donated > self.amount_of_randomized_donations
@dataclass(frozen=True)
class MuseumsanityAll(MuseumsanityFeature):
is_enabled = True

View File

@@ -1,15 +1,17 @@
from abc import ABC, abstractmethod
from typing import ClassVar, Iterable, Tuple
from collections.abc import Iterable
from typing import ClassVar
from .base import FeatureBase
from ...data.skill import Skill
class SkillProgressionFeature(ABC):
class SkillProgressionFeature(FeatureBase, ABC):
is_progressive: ClassVar[bool]
are_masteries_shuffled: ClassVar[bool]
@abstractmethod
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[Tuple[int, str]]:
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[tuple[int, str]]:
...
@abstractmethod
@@ -21,7 +23,7 @@ class SkillProgressionVanilla(SkillProgressionFeature):
is_progressive = False
are_masteries_shuffled = False
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[Tuple[int, str]]:
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[tuple[int, str]]:
return ()
def is_mastery_randomized(self, skill: Skill) -> bool:
@@ -32,7 +34,7 @@ class SkillProgressionProgressive(SkillProgressionFeature):
is_progressive = True
are_masteries_shuffled = False
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[Tuple[int, str]]:
def get_randomized_level_names_by_level(self, skill: Skill) -> Iterable[tuple[int, str]]:
return skill.level_names_by_level
def is_mastery_randomized(self, skill: Skill) -> bool:

View File

@@ -0,0 +1,5 @@
def get_qi_gem_amount(item_name: str) -> int:
amount = item_name.replace(" Qi Gems", "")
if amount.isdigit():
return int(amount)
return 0

View File

@@ -6,14 +6,19 @@ from functools import cache
from types import MappingProxyType
from typing import ClassVar
from .base import FeatureBase
from ...strings.tool_names import Tool
def to_progressive_item(tool: str) -> str:
def to_progressive_item_name(tool: str) -> str:
"""Return the name of the progressive item."""
return f"Progressive {tool}"
def to_upgrade_location_name(tool: str, material: str) -> str:
return f"{material} {tool} Upgrade"
# The golden scythe is always randomized
VANILLA_TOOL_DISTRIBUTION = MappingProxyType({
Tool.scythe: 1,
@@ -29,6 +34,17 @@ PROGRESSIVE_TOOL_DISTRIBUTION = MappingProxyType({
Tool.fishing_rod: 4,
})
VANILLA_STARTING_TOOLS_DISTRIBUTION = MappingProxyType({
Tool.scythe: 1,
Tool.axe: 1,
Tool.hoe: 1,
Tool.pickaxe: 1,
Tool.pan: 0,
Tool.trash_can: 1,
Tool.watering_can: 1,
Tool.fishing_rod: 0,
})
# Masteries add another tier to the scythe and the fishing rod
SKILL_MASTERIES_TOOL_DISTRIBUTION = MappingProxyType({
Tool.scythe: 1,
@@ -37,8 +53,10 @@ SKILL_MASTERIES_TOOL_DISTRIBUTION = MappingProxyType({
@cache
def get_tools_distribution(progressive_tools_enabled: bool, skill_masteries_enabled: bool) -> Mapping[str, int]:
def get_tools_distribution(progressive_tools_enabled: bool, skill_masteries_enabled: bool, no_starting_tools_enabled: bool) \
-> tuple[Mapping[str, int], Mapping[str, int]]:
distribution = Counter(VANILLA_TOOL_DISTRIBUTION)
starting = Counter(VANILLA_STARTING_TOOLS_DISTRIBUTION)
if progressive_tools_enabled:
distribution += PROGRESSIVE_TOOL_DISTRIBUTION
@@ -46,21 +64,31 @@ def get_tools_distribution(progressive_tools_enabled: bool, skill_masteries_enab
if skill_masteries_enabled:
distribution += SKILL_MASTERIES_TOOL_DISTRIBUTION
return MappingProxyType(distribution)
if no_starting_tools_enabled:
distribution += VANILLA_STARTING_TOOLS_DISTRIBUTION
starting.clear()
return MappingProxyType(starting), MappingProxyType(distribution)
@dataclass(frozen=True)
class ToolProgressionFeature(ABC):
class ToolProgressionFeature(FeatureBase, ABC):
is_progressive: ClassVar[bool]
starting_tools: Mapping[str, int]
tool_distribution: Mapping[str, int]
to_progressive_item = staticmethod(to_progressive_item)
to_progressive_item_name = staticmethod(to_progressive_item_name)
to_upgrade_location_name = staticmethod(to_upgrade_location_name)
def get_tools_distribution(self) -> tuple[Mapping[str, int], Mapping[str, int]]:
return self.starting_tools, self.tool_distribution
@dataclass(frozen=True)
class ToolProgressionVanilla(ToolProgressionFeature):
is_progressive = False
# FIXME change the default_factory to a simple default when python 3.11 is no longer supported
starting_tools: Mapping[str, int] = field(default_factory=lambda: VANILLA_STARTING_TOOLS_DISTRIBUTION)
tool_distribution: Mapping[str, int] = field(default_factory=lambda: VANILLA_TOOL_DISTRIBUTION)

View File

@@ -0,0 +1,8 @@
def get_walnut_amount(item_name: str) -> int:
if item_name == "Golden Walnut":
return 1
if item_name == "3 Golden Walnuts":
return 3
if item_name == "5 Golden Walnuts":
return 5
return 0

View File

@@ -1,13 +1,18 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Iterable, Set, Any, Mapping, Type, Tuple, Union
from functools import cached_property
from types import MappingProxyType
from typing import Iterable, Set, Any, Mapping, Generator
from .feature import booksanity, cropsanity, fishsanity, friendsanity, skill_progression, building_progression, tool_progression
from .feature import BooksanityFeature, BuildingProgressionFeature, CropsanityFeature, FishsanityFeature, FriendsanityFeature, HatsanityFeature, \
MuseumsanityFeature, SkillProgressionFeature, ToolProgressionFeature
from .feature.base import DisableSourceHook, DisableRequirementHook, FeatureBase
from ..data.animal import Animal
from ..data.building import Building
from ..data.fish_data import FishItem
from ..data.game_item import GameItem, Source, ItemTag
from ..data.game_item import GameItem, Source, ItemTag, Requirement
from ..data.hats_data import HatItem
from ..data.skill import Skill
from ..data.villagers_data import Villager
@@ -26,16 +31,25 @@ class StardewContent:
animals: dict[str, Animal] = field(default_factory=dict)
skills: dict[str, Skill] = field(default_factory=dict)
quests: dict[str, Any] = field(default_factory=dict)
hats: dict[str, HatItem] = field(default_factory=dict)
def find_sources_of_type(self, types: Union[Type[Source], Tuple[Type[Source]]]) -> Iterable[Source]:
def find_sources_of_type(self, *types: type[Source]) -> Iterable[Source]:
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: Source):
def source_item(self, item_name: str, *sources: Source) -> GameItem:
filtered_sources = list(self._filter_sources(sources))
item = self.game_items.setdefault(item_name, GameItem(item_name))
item.add_sources(sources)
item.add_sources(filtered_sources)
for source in filtered_sources:
for requirement_name, tags in source.requirement_tags.items():
self.tag_item(requirement_name, *tags)
return item
def tag_item(self, item_name: str, *tags: ItemTag):
item = self.game_items.setdefault(item_name, GameItem(item_name))
@@ -44,22 +58,79 @@ class StardewContent:
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]:
def find_tagged_items(self, tag: ItemTag) -> Generator[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
def are_all_enabled(self, content_packs: frozenset[str]) -> bool:
return content_packs.issubset(self.registered_packs)
def is_enabled(self, content_pack: str | ContentPack) -> bool:
if isinstance(content_pack, ContentPack):
content_pack = content_pack.name
return content_pack in self.registered_packs
def _filter_sources(self, sources: Iterable[Source]) -> Generator[Source]:
"""Filters out sources that are disabled by features."""
for source in sources:
if not self._is_disabled(source):
yield source
def _is_disabled(self, source: Source) -> bool:
"""Checks if a source is disabled by any feature."""
try:
hook = self.features.disable_source_hooks[type(source)]
if hook(source, content=self):
return True
except KeyError:
pass
for requirement in source.all_requirements:
try:
hook = self.features.disable_requirement_hooks[type(requirement)]
if hook(requirement, content=self):
return True
except KeyError:
pass
return False
@dataclass(frozen=True)
class StardewFeatures:
booksanity: booksanity.BooksanityFeature
building_progression: building_progression.BuildingProgressionFeature
cropsanity: cropsanity.CropsanityFeature
fishsanity: fishsanity.FishsanityFeature
friendsanity: friendsanity.FriendsanityFeature
skill_progression: skill_progression.SkillProgressionFeature
tool_progression: tool_progression.ToolProgressionFeature
booksanity: BooksanityFeature
building_progression: BuildingProgressionFeature
cropsanity: CropsanityFeature
fishsanity: FishsanityFeature
friendsanity: FriendsanityFeature
hatsanity: HatsanityFeature
museumsanity: MuseumsanityFeature
skill_progression: SkillProgressionFeature
tool_progression: ToolProgressionFeature
@cached_property
def all_features(self) -> Iterable[FeatureBase]:
return (self.booksanity, self.building_progression, self.cropsanity, self.fishsanity, self.friendsanity, self.hatsanity, self.museumsanity,
self.skill_progression, self.tool_progression)
@cached_property
def disable_source_hooks(self) -> Mapping[type[Source], DisableSourceHook]:
"""Returns a set of source types that are disabled by features. Need to be exact types, subclasses are not considered."""
hooks = {}
for feature in self.all_features:
hooks.update(feature.disable_source_hooks)
return MappingProxyType(hooks)
@cached_property
def disable_requirement_hooks(self) -> Mapping[type[Requirement], DisableRequirementHook]:
"""Returns a set of requirement types that are disabled by features. Need to be exact types, subclasses are not considered."""
hooks = {}
for feature in self.all_features:
hooks.update(feature.disable_requirement_hooks)
return MappingProxyType(hooks)
@dataclass(frozen=True)
@@ -125,6 +196,12 @@ class ContentPack:
def quest_hook(self, content: StardewContent):
...
...
hat_sources: Mapping[HatItem, Iterable[Source]] = field(default_factory=dict)
def hat_source_hook(self, content: StardewContent):
...
def finalize_hook(self, content: StardewContent):
"""Last hook called on the pack, once all other content packs have been registered.

View File

@@ -1,34 +1,46 @@
from ..game_content import ContentPack, StardewContent
from ..mod_registry import register_mod_content_pack
from ...data.artisan import MachineSource
from ...data.game_item import ItemTag, Tag
from ...data.harvest import ArtifactSpotSource
from ...data.requirement import SkillRequirement
from ...data.skill import Skill
from ...mods.mod_data import ModNames
from ...strings.ap_names.mods.mod_items import ModBooks
from ...strings.craftable_names import ModMachine
from ...strings.fish_names import ModTrash
from ...strings.metal_names import all_artifacts, all_fossils
from ...strings.metal_names import all_artifacts, all_fossils, Fossil
from ...strings.skill_names import ModSkill
def source_display_items(item: str, content: StardewContent):
wood_display = f"Wooden Display: {item}"
hardwood_display = f"Hardwood Display: {item}"
if item == Fossil.trilobite:
wood_display = f"Wooden Display: Trilobite Fossil"
hardwood_display = f"Hardwood Display: Trilobite Fossil"
content.source_item(wood_display, MachineSource(item=str(item), machine=ModMachine.preservation_chamber))
content.source_item(hardwood_display, MachineSource(item=str(item), machine=ModMachine.hardwood_preservation_chamber))
class ArchaeologyContentPack(ContentPack):
def artisan_good_hook(self, content: StardewContent):
# Done as honestly there are too many display items to put into the initial registration traditionally.
display_items = all_artifacts + all_fossils
for item in display_items:
self.source_display_items(item, content)
source_display_items(item, content)
content.source_item(ModTrash.rusty_scrap, *(MachineSource(item=artifact, machine=ModMachine.grinder) for artifact in all_artifacts))
def source_display_items(self, item: str, content: StardewContent):
wood_display = f"Wooden Display: {item}"
hardwood_display = f"Hardwood Display: {item}"
if item == "Trilobite":
wood_display = f"Wooden Display: Trilobite Fossil"
hardwood_display = f"Hardwood Display: Trilobite Fossil"
content.source_item(wood_display, MachineSource(item=str(item), machine=ModMachine.preservation_chamber))
content.source_item(hardwood_display, MachineSource(item=str(item), machine=ModMachine.hardwood_preservation_chamber))
register_mod_content_pack(ArchaeologyContentPack(
ModNames.archaeology,
skills=(Skill(name=ModSkill.archaeology, has_mastery=False),),
harvest_sources={
ModBooks.digging_like_worms: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ArtifactSpotSource(amount=22, # I'm just copying Jack Be Nimble's chances for now -reptar
other_requirements=(SkillRequirement(ModSkill.archaeology, 2),)),
)
}
))

View File

@@ -39,4 +39,4 @@ register_mod_content_pack(DistantLandsContentPack(
DistantLandsSeed.vile_ancient_fruit: (ForagingSource(regions=(Region.witch_swamp,), other_requirements=(QuestRequirement(ModQuest.CorruptedCropsTask),)),),
DistantLandsCrop.vile_ancient_fruit: (Tag(ItemTag.FRUIT), HarvestCropSource(seed=DistantLandsSeed.vile_ancient_fruit, seasons=(Season.spring, Season.summer, Season.fall)),)
}
))
))

View File

@@ -5,7 +5,7 @@ from ..vanilla.ginger_island import ginger_island_content_pack as ginger_island_
from ...data import villagers_data, fish_data
from ...data.game_item import ItemTag, Tag
from ...data.harvest import ForagingSource, HarvestCropSource
from ...data.requirement import YearRequirement, CombatRequirement, RelationshipRequirement, ToolRequirement, SkillRequirement, FishingRequirement
from ...data.requirement import YearRequirement, CombatRequirement, SpecificFriendRequirement, ToolRequirement, SkillRequirement, FishingRequirement
from ...data.shop import ShopSource
from ...mods.mod_data import ModNames
from ...strings.craftable_names import ModEdible
@@ -82,18 +82,18 @@ register_mod_content_pack(SVEContentPack(
ModNames.jasper, # To override Marlon and Gunther
),
shop_sources={
SVEGift.aged_blue_moon_wine: (ShopSource(money_price=28000, shop_region=SVERegion.blue_moon_vineyard),),
SVEGift.blue_moon_wine: (ShopSource(money_price=3000, shop_region=SVERegion.blue_moon_vineyard),),
ModEdible.lightning_elixir: (ShopSource(money_price=12000, shop_region=SVERegion.galmoran_outpost),),
ModEdible.barbarian_elixir: (ShopSource(money_price=22000, shop_region=SVERegion.galmoran_outpost),),
ModEdible.gravity_elixir: (ShopSource(money_price=4000, shop_region=SVERegion.galmoran_outpost),),
SVEMeal.grampleton_orange_chicken: (ShopSource(money_price=650,
SVEGift.aged_blue_moon_wine: (ShopSource(price=28000, shop_region=SVERegion.blue_moon_vineyard),),
SVEGift.blue_moon_wine: (ShopSource(price=3000, shop_region=SVERegion.blue_moon_vineyard),),
ModEdible.lightning_elixir: (ShopSource(price=12000, shop_region=SVERegion.galmoran_outpost),),
ModEdible.barbarian_elixir: (ShopSource(price=22000, shop_region=SVERegion.galmoran_outpost),),
ModEdible.gravity_elixir: (ShopSource(price=4000, shop_region=SVERegion.galmoran_outpost),),
SVEMeal.grampleton_orange_chicken: (ShopSource(price=650,
shop_region=Region.saloon,
other_requirements=(RelationshipRequirement(ModNPC.sophia, 6),)),),
ModEdible.hero_elixir: (ShopSource(money_price=8000, shop_region=SVERegion.isaac_shop),),
ModEdible.aegis_elixir: (ShopSource(money_price=28000, shop_region=SVERegion.galmoran_outpost),),
SVEBeverage.sports_drink: (ShopSource(money_price=750, shop_region=Region.hospital),),
SVEMeal.stamina_capsule: (ShopSource(money_price=4000, shop_region=Region.hospital),),
other_requirements=(SpecificFriendRequirement(ModNPC.sophia, 6),)),),
ModEdible.hero_elixir: (ShopSource(price=8000, shop_region=SVERegion.isaac_shop),),
ModEdible.aegis_elixir: (ShopSource(price=28000, shop_region=SVERegion.galmoran_outpost),),
SVEBeverage.sports_drink: (ShopSource(price=750, shop_region=Region.hospital),),
SVEMeal.stamina_capsule: (ShopSource(price=4000, shop_region=Region.hospital),),
},
harvest_sources={
Mushroom.red: (

View File

@@ -16,7 +16,7 @@ register_mod_content_pack(ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=150_000,
price=150_000,
items_price=((20, MetalBar.iron), (5, MetalBar.iridium), (1, ArtisanGood.battery_pack)),
),
),

View File

@@ -5,7 +5,7 @@ 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, Source
from ..data.game_item import Source
def unpack_content(features: StardewFeatures, packs: Iterable[ContentPack]) -> StardewContent:
@@ -73,6 +73,13 @@ def register_pack(content: StardewContent, pack: ContentPack):
content.skills[skill.name] = skill
pack.skill_hook(content)
for hat, sources in pack.hat_sources.items():
item = content.source_item(hat.clarified_name, *sources)
# Some sources may be filtered out. We don't want to register a hat without source.
if item.sources:
content.hats[hat.name] = hat
pack.hat_source_hook(content)
# register_quests
# ...
@@ -84,14 +91,7 @@ def register_sources_and_call_hook(content: StardewContent,
sources_by_item_name: Mapping[str, Iterable[Source]],
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)
content.source_item(item_name, *sources)
hook(content)

View File

@@ -1,20 +1,30 @@
from ..game_content import ContentPack, StardewContent
from ...data.artisan import MachineSource
from ...data.game_item import ItemTag, CustomRuleSource, GameItem
from ...data.game_item import ItemTag, CustomRuleSource, GameItem, Tag
from ...data.harvest import HarvestFruitTreeSource, HarvestCropSource
from ...data.hats_data import Hats
from ...data.requirement import ToolRequirement, TotalEarningsRequirement, ShipOneCropRequirement, CraftedItemsRequirement, CookedRecipesRequirement, \
CaughtFishRequirement
from ...data.shop import HatMouseSource
from ...data.skill import Skill
from ...logic.tailoring_logic import TailoringSource
from ...strings.animal_product_names import AnimalProduct
from ...strings.artisan_good_names import ArtisanGood
from ...strings.craftable_names import WildSeeds
from ...strings.craftable_names import WildSeeds, Edible, Consumable, Lighting
from ...strings.crop_names import Fruit, Vegetable
from ...strings.fish_names import Fish, WaterChest
from ...strings.flower_names import Flower
from ...strings.food_names import Beverage
from ...strings.food_names import Beverage, Meal
from ...strings.forageable_names import all_edible_mushrooms, Mushroom, Forageable
from ...strings.fruit_tree_names import Sapling
from ...strings.gift_names import Gift
from ...strings.machine_names import Machine
from ...strings.metal_names import Fossil, Artifact
from ...strings.monster_names import Monster
from ...strings.season_names import Season
from ...strings.seed_names import Seed
from ...strings.seed_names import Seed, TreeSeed
from ...strings.skill_names import Skill as SkillName
from ...strings.tool_names import Tool, ToolMaterial
all_fruits = (
Fruit.ancient_fruit, Fruit.apple, Fruit.apricot, Fruit.banana, Forageable.blackberry, Fruit.blueberry, Forageable.cactus_fruit, Fruit.cherry,
@@ -110,16 +120,19 @@ base_game = BaseGameContentPack(
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,)),),
Vegetable.kale: (HarvestCropSource(seed=Seed.kale, seasons=(Season.spring,),
other_requirements=(ToolRequirement(Tool.scythe),)),),
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,)),),
Vegetable.unmilled_rice: (HarvestCropSource(seed=Seed.rice, seasons=(Season.spring,),
other_requirements=(ToolRequirement(Tool.scythe),)),),
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.wheat: (HarvestCropSource(seed=Seed.wheat, seasons=(Season.summer, Season.fall),
other_requirements=(ToolRequirement(Tool.scythe),)),),
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,)),),
@@ -134,7 +147,8 @@ base_game = BaseGameContentPack(
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,)),),
Vegetable.amaranth: (HarvestCropSource(seed=Seed.amaranth, seasons=(Season.fall,),
other_requirements=(ToolRequirement(Tool.scythe),)),),
Fruit.grape: (HarvestCropSource(seed=Seed.grape, seasons=(Season.fall,)),),
Vegetable.artichoke: (HarvestCropSource(seed=Seed.artichoke, seasons=(Season.fall,)),),
@@ -147,18 +161,18 @@ base_game = BaseGameContentPack(
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_starter: (CustomRuleSource(create_rule=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(WildSeeds.tea_sapling) & logic.time.has_lived_months(2) & logic.season.has_any_not_winter()),),
CustomRuleSource(create_rule=lambda logic: logic.has(WildSeeds.tea_sapling) & 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"))),
CustomRuleSource(create_rule=lambda logic: logic.has(Machine.coffee_maker)),
CustomRuleSource(create_rule=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),),
@@ -169,5 +183,48 @@ base_game = BaseGameContentPack(
Skill(SkillName.fishing, has_mastery=True),
Skill(SkillName.mining, has_mastery=True),
Skill(SkillName.combat, has_mastery=True),
)
),
hat_sources={
Hats.good_ol_cap: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(TotalEarningsRequirement(15000),)),),
Hats.lucky_bow: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(TotalEarningsRequirement(50000),)),),
Hats.cool_cap: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(TotalEarningsRequirement(250000),)),),
Hats.bowler: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(TotalEarningsRequirement(1000000),)),),
Hats.sombrero: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(TotalEarningsRequirement(10000000),)),),
Hats.delicate_bow: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CookedRecipesRequirement(10),)),),
Hats.plum_chapeau: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CookedRecipesRequirement(25),)),),
Hats.daisy: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CraftedItemsRequirement(15),)),),
Hats.trucker_hat: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CraftedItemsRequirement(30),)),),
Hats.souwester: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CaughtFishRequirement(10, unique=True),)),),
Hats.official_cap: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CaughtFishRequirement(24, unique=True),)),),
Hats.watermelon_band: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CaughtFishRequirement(100, unique=False),)),),
Hats.cowgal_hat: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(ShipOneCropRequirement(300),)),),
Hats.living_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.grind.can_grind_weeds(100000)),),
Hats.spotted_headscarf: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Mushroom.red,)),),
Hats.beanie: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(TreeSeed.acorn, TreeSeed.mahogany, TreeSeed.maple, TreeSeed.pine,)),),
Hats.blobfish_mask: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Fish.blobfish,)),),
Hats.bridal_veil: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Gift.pearl,)),),
Hats.dinosaur_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(AnimalProduct.dinosaur_egg,)),),
Hats.fashion_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(ArtisanGood.caviar,)),),
Hats.flat_topped_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Meal.cranberry_sauce, Meal.stuffing,)),),
Hats.floppy_beanie: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(ArtisanGood.maple_syrup, ArtisanGood.oak_resin, ArtisanGood.pine_tar,)),),
Hats.goggles: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Edible.bug_steak,)),),
Hats.hair_bone: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Fossil.prehistoric_tibia,)),),
Hats.party_hat_blue: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Meal.chocolate_cake, Consumable.fireworks_purple,)),),
Hats.party_hat_green: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Consumable.fireworks_green, Meal.fish_taco,)),),
Hats.party_hat_red: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Consumable.fireworks_red, Meal.pizza,)),),
Hats.pirate_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(WaterChest.treasure,)),),
Hats.propeller_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Meal.miners_treat,)),),
Hats.pumpkin_mask: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Lighting.jack_o_lantern,)),),
Hats.wearable_dwarf_helm: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Artifact.dwarf_gadget, Artifact.dwarvish_helm,)),),
Hats.white_turban: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Fruit.sweet_gem_berry,)),),
Hats.witch_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Gift.golden_pumpkin,)),),
Hats.totem_mask: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Consumable.rain_totem, Consumable.warp_totem_farm,
Consumable.warp_totem_mountains, Consumable.warp_totem_beach,
Consumable.warp_totem_desert, Consumable.treasure_totem)),),
Hats.copper_pan_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.tool.has_pan(ToolMaterial.copper)),),
Hats.steel_pan_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.tool.has_pan(ToolMaterial.iron)),),
Hats.gold_pan_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.tool.has_pan(ToolMaterial.gold)),),
Hats.iridium_pan_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.tool.has_pan(ToolMaterial.iridium)),),
},
)

View File

@@ -2,22 +2,34 @@ 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.animal import Animal, AnimalName, OstrichIncubatorSource
from ...data.fish_data import FishingSource
from ...data.game_item import ItemTag, Tag, CustomRuleSource
from ...data.harvest import ForagingSource, HarvestFruitTreeSource, HarvestCropSource
from ...data.requirement import WalnutRequirement
from ...data.shop import ShopSource
from ...data.hats_data import Hats
from ...data.monster_data import MonsterSource
from ...data.requirement import WalnutRequirement, ForgeInfinityWeaponRequirement, CookedRecipesRequirement, \
CaughtFishRequirement, FullShipmentRequirement, RegionRequirement, \
AllAchievementsRequirement, PerfectionPercentRequirement, ReadAllBooksRequirement, HasItemRequirement, ToolRequirement
from ...data.shop import ShopSource, HatMouseSource
from ...logic.tailoring_logic import TailoringSource
from ...logic.time_logic import MAX_MONTHS
from ...strings.animal_product_names import AnimalProduct
from ...strings.book_names import Book
from ...strings.building_names import Building
from ...strings.crop_names import Fruit, Vegetable
from ...strings.currency_names import Currency
from ...strings.fish_names import Fish
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.fruit_tree_names import Sapling
from ...strings.generic_names import Generic
from ...strings.geode_names import Geode
from ...strings.material_names import Material
from ...strings.metal_names import Fossil, Mineral
from ...strings.monster_names import Monster
from ...strings.region_names import Region, LogicRegion
from ...strings.season_names import Season
from ...strings.seed_names import Seed
from ...strings.seed_names import Seed, TreeSeed
from ...strings.tool_names import Tool
class GingerIslandContentPack(ContentPack):
@@ -38,12 +50,16 @@ ginger_island_content_pack = GingerIslandContentPack(
harvest_sources={
# Foraging
Forageable.dragon_tooth: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.volcano_floor_10,)),
),
Forageable.ginger: (
ForagingSource(regions=(Region.island_west,)),
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.island_west,),
other_requirements=(ToolRequirement(Tool.hoe),)),
),
Mushroom.magma_cap: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.volcano_floor_5,)),
),
@@ -76,8 +92,7 @@ ginger_island_content_pack = GingerIslandContentPack(
),
Book.queen_of_sauce_cookbook: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=50000, shop_region=LogicRegion.bookseller_2, other_requirements=(WalnutRequirement(100),)),), # Worst book ever
ShopSource(price=50000, shop_region=LogicRegion.bookseller_permanent, other_requirements=(WalnutRequirement(100),)),), # Worst book ever
},
fishes=(
# TODO override region so no need to add inaccessible regions in logic
@@ -99,5 +114,40 @@ ginger_island_content_pack = GingerIslandContentPack(
sources=(
OstrichIncubatorSource(AnimalProduct.ostrich_egg_starter),
)),
)
),
hat_sources={
Hats.infinity_crown: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(ForgeInfinityWeaponRequirement(),)),),
Hats.archers_cap: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CookedRecipesRequirement(9999),)),),
Hats.chef_hat: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CookedRecipesRequirement(9999),)),),
Hats.eye_patch: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CaughtFishRequirement(9999, unique=True),)),),
Hats.cowpoke_hat: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(FullShipmentRequirement(),)),),
Hats.goblin_mask: (Tag(ItemTag.HAT), HatMouseSource(price=10000, unlock_requirements=(FullShipmentRequirement(),)),),
Hats.elegant_turban: (Tag(ItemTag.HAT), HatMouseSource(price=50000, unlock_requirements=(AllAchievementsRequirement(),)),),
Hats.junimo_hat: (Tag(ItemTag.HAT), HatMouseSource(price=25000, unlock_requirements=(PerfectionPercentRequirement(100),)),),
Hats.paper_hat: (Tag(ItemTag.HAT), HatMouseSource(price=10000, unlock_requirements=(RegionRequirement(Region.island_south),)),),
Hats.pageboy_cap: (Tag(ItemTag.HAT), HatMouseSource(price=5000, unlock_requirements=(ReadAllBooksRequirement(),)),),
Hats.concerned_ape_mask: (Tag(ItemTag.HAT), ShopSource(price=10000, shop_region=LogicRegion.lost_items_shop,
other_requirements=(PerfectionPercentRequirement(100), RegionRequirement(Region.volcano_floor_10))),),
Hats.golden_helmet: (Tag(ItemTag.HAT), ShopSource(price=10000, shop_region=LogicRegion.lost_items_shop,
other_requirements=(RegionRequirement(Region.blacksmith), HasItemRequirement(Geode.golden_coconut),)),),
Hats.bluebird_mask: (Tag(ItemTag.HAT), ShopSource(price=30, currency=Vegetable.taro_root, shop_region=Region.island_trader),),
Hats.deluxe_cowboy_hat: (Tag(ItemTag.HAT), ShopSource(price=30, currency=Vegetable.taro_root, shop_region=Region.island_trader),),
Hats.small_cap: (Tag(ItemTag.HAT), ShopSource(price=30, currency=Vegetable.taro_root, shop_region=Region.island_trader),),
Hats.mr_qis_hat: (Tag(ItemTag.HAT), ShopSource(price=5, currency=Currency.qi_gem, shop_region=Region.qi_walnut_room),),
Hats.pink_bow: (Tag(ItemTag.HAT), ShopSource(price=10000, shop_region=Region.volcano_dwarf_shop),),
Hats.tiger_hat: (Tag(ItemTag.HAT), MonsterSource(monsters=(Monster.tiger_slime,), amount_tier=MAX_MONTHS,
other_requirements=(RegionRequirement(region=Region.adventurer_guild),)),),
Hats.deluxe_pirate_hat: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.volcano, Region.volcano_floor_5, Region.volcano_floor_10,),
require_all_regions=True),),
Hats.foragers_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Forageable.ginger,)),),
Hats.sunglasses: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Material.cinder_shard,)),),
Hats.swashbuckler_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Forageable.dragon_tooth,)),),
Hats.warrior_helmet: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(AnimalProduct.ostrich_egg,)),),
Hats.star_helmet: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(TreeSeed.mushroom,)),),
Hats.frog_hat: (Tag(ItemTag.HAT), FishingSource(region=Region.gourmand_frog_cave,),),
},
)

View File

@@ -1,99 +1,131 @@
from ..game_content import ContentPack
from ...data import villagers_data, fish_data
from ...data.building import Building
from ...data.game_item import GenericSource, ItemTag, Tag, CustomRuleSource
from ...data.game_item import GenericSource, ItemTag, Tag, CustomRuleSource, AllRegionsSource
from ...data.harvest import ForagingSource, SeasonalForagingSource, ArtifactSpotSource
from ...data.requirement import ToolRequirement, BookRequirement, SkillRequirement, YearRequirement
from ...data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource
from ...data.hats_data import Hats
from ...data.monster_data import MonsterSource
from ...data.requirement import ToolRequirement, BookRequirement, SkillRequirement, YearRequirement, \
GrangeDisplayRequirement, EggHuntRequirement, MuseumCompletionRequirement, BuildingRequirement, \
NumberOfFriendsRequirement, HelpWantedRequirement, FishingCompetitionRequirement, MovieRequirement, LuauDelightRequirementRequirement, \
ReceivedRaccoonsRequirement, \
PrizeMachineRequirement, SpecificFriendRequirement, RegionRequirement, CatalogueRequirement
from ...data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, \
FishingTreasureChestSource, HatMouseSource
from ...logic.tailoring_logic import TailoringSource
from ...logic.time_logic import MAX_MONTHS
from ...strings.artisan_good_names import ArtisanGood
from ...strings.book_names import Book
from ...strings.building_names import Building as BuildingNames
from ...strings.catalogue_names import Catalogue
from ...strings.craftable_names import Furniture
from ...strings.crop_names import Fruit
from ...strings.fish_names import WaterItem
from ...strings.currency_names import Currency
from ...strings.fish_names import WaterItem, Fish
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.metal_names import MetalBar
from ...strings.monster_names import Monster
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
from ...strings.villager_names import NPC
pelican_town = ContentPack(
"Pelican Town (Vanilla)",
harvest_sources={
# Spring
Forageable.daffodil: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.spring,), regions=(Region.bus_stop, Region.town, Region.railroad)),
),
Forageable.dandelion: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.spring,), regions=(Region.bus_stop, Region.forest, Region.railroad)),
),
Forageable.leek: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.spring,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
),
Forageable.wild_horseradish: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.spring,), regions=(Region.backwoods, Region.mountain, Region.forest, Region.secret_woods)),
),
Forageable.salmonberry: (
Tag(ItemTag.FORAGE),
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: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.spring,), regions=(Region.forest,)),
),
# Summer
Fruit.grape: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.summer,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
),
Forageable.spice_berry: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.summer,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.forest, Region.railroad)),
),
Forageable.sweet_pea: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.summer,), regions=(Region.bus_stop, Region.town, Region.forest, Region.railroad)),
),
Forageable.fiddlehead_fern: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.summer,), regions=(Region.secret_woods,)),
),
# Fall
Forageable.blackberry: (
Tag(ItemTag.FORAGE),
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: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
),
Forageable.wild_plum: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.fall,), regions=(Region.mountain, Region.bus_stop, Region.railroad)),
),
# Winter
Forageable.crocus: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.winter,),
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.secret_woods)),
),
Forageable.crystal_fruit: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.winter,),
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad)),
),
Forageable.holly: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.winter,),
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad)),
),
Forageable.snow_yam: (
Tag(ItemTag.FORAGE),
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: (
Tag(ItemTag.FORAGE),
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),
@@ -102,31 +134,39 @@ pelican_town = ContentPack(
# Mushrooms
Mushroom.common: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.spring,), regions=(Region.secret_woods,)),
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.mountain, Region.forest)),
),
Mushroom.chanterelle: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.fall,), regions=(Region.secret_woods,)),
),
Mushroom.morel: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.spring, Season.fall), regions=(Region.secret_woods,)),
),
Mushroom.red: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.summer, Season.fall), regions=(Region.secret_woods,)),
),
# Beach
WaterItem.coral: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.tide_pools,)),
SeasonalForagingSource(season=Season.summer, days=(12, 13, 14), regions=(Region.beach,)),
),
WaterItem.nautilus_shell: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.winter,), regions=(Region.beach,)),
),
Forageable.rainbow_shell: (
Tag(ItemTag.FORAGE),
ForagingSource(seasons=(Season.summer,), regions=(Region.beach,)),
),
WaterItem.sea_urchin: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.tide_pools,)),
),
@@ -149,150 +189,159 @@ pelican_town = ContentPack(
},
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),),
Sapling.apple: (ShopSource(price=4000, shop_region=Region.pierre_store),),
Sapling.apricot: (ShopSource(price=2000, shop_region=Region.pierre_store),),
Sapling.cherry: (ShopSource(price=3400, shop_region=Region.pierre_store),),
Sapling.orange: (ShopSource(price=4000, shop_region=Region.pierre_store),),
Sapling.peach: (ShopSource(price=6000, shop_region=Region.pierre_store),),
Sapling.pomegranate: (ShopSource(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.parsnip: (ShopSource(price=20, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.bean: (ShopSource(price=60, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.cauliflower: (ShopSource(price=80, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.potato: (ShopSource(price=50, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.tulip: (ShopSource(price=20, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.kale: (ShopSource(price=70, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.jazz: (ShopSource(price=30, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.garlic: (ShopSource(price=40, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
Seed.rice: (ShopSource(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.melon: (ShopSource(price=80, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.tomato: (ShopSource(price=50, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.blueberry: (ShopSource(price=80, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.pepper: (ShopSource(price=40, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.wheat: (ShopSource(price=10, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
Seed.radish: (ShopSource(price=40, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.poppy: (ShopSource(price=100, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.spangle: (ShopSource(price=50, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.hops: (ShopSource(price=60, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
Seed.corn: (ShopSource(price=150, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
Seed.sunflower: (ShopSource(price=200, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
Seed.red_cabbage: (ShopSource(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.eggplant: (ShopSource(price=20, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.pumpkin: (ShopSource(price=100, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.bok_choy: (ShopSource(price=50, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.yam: (ShopSource(price=60, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.cranberry: (ShopSource(price=240, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.fairy: (ShopSource(price=200, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.amaranth: (ShopSource(price=70, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.grape: (ShopSource(price=60, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
Seed.artichoke: (ShopSource(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.broccoli: (ShopSource(items_price=((5, Material.moss),), shop_region=LogicRegion.raccoon_shop_1),),
Seed.carrot: (ShopSource(items_price=((1, TreeSeed.maple),), shop_region=LogicRegion.raccoon_shop_1),),
Seed.powdermelon: (ShopSource(items_price=((2, TreeSeed.acorn),), shop_region=LogicRegion.raccoon_shop_1),),
Seed.summer_squash: (ShopSource(items_price=((15, Material.sap),), shop_region=LogicRegion.raccoon_shop_1),),
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)),),
Seed.strawberry: (ShopSource(price=100, shop_region=LogicRegion.egg_festival, seasons=(Season.spring,)),),
Seed.rare_seed: (ShopSource(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),),
Beverage.beer: (ShopSource(price=400, shop_region=Region.saloon),),
Meal.salad: (ShopSource(price=220, shop_region=Region.saloon),),
Meal.bread: (ShopSource(price=100, shop_region=Region.saloon),),
Meal.spaghetti: (ShopSource(price=240, shop_region=Region.saloon),),
Meal.pizza: (ShopSource(price=600, shop_region=Region.saloon),),
Beverage.coffee: (ShopSource(price=300, shop_region=Region.saloon),),
# Books
Book.animal_catalogue: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=5000, shop_region=Region.ranch, other_requirements=(YearRequirement(2),)),),
ShopSource(price=5000, shop_region=Region.ranch, other_requirements=(YearRequirement(2),)),),
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.
MysteryBoxSource(amount=50),), # 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),),
ShopSource(price=4000, shop_region=LogicRegion.mines_dwarf_shop),),
# ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),), # Repeatable, so no need for bookseller
Book.friendship_101: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
PrizeMachineSource(amount=9),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
Book.horse_the_book: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=25000, shop_region=LogicRegion.bookseller_2),),
ShopSource(price=25000, shop_region=LogicRegion.bookseller_permanent),),
Book.jack_be_nimble_jack_be_thick: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
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),),
FishingTreasureChestSource(amount=25), # After 21 chests, there are 49.44% chances player received the book.
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
Book.mapping_cave_systems: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
GenericSource(regions=(Region.adventurer_guild_bedroom,)),
AllRegionsSource(regions=(Region.adventurer_guild_bedroom, LogicRegion.bookseller_rare,)),
# Disabling the shop source for better game design.
# ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),
# ShopSource(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),),
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
Book.ol_slitherlegs: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=25000, shop_region=LogicRegion.bookseller_2),),
ShopSource(price=25000, shop_region=LogicRegion.bookseller_permanent),),
Book.price_catalogue: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=3000, shop_region=LogicRegion.bookseller_2),),
ShopSource(price=3000, shop_region=LogicRegion.bookseller_permanent),),
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),),
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
Book.the_art_o_crabbing: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
CustomRuleSource(create_rule=lambda logic: logic.festival.has_squidfest_day_1_iridium_reward()),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
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),),
ArtifactTroveSource(amount=20), # After 18 troves, there is 49,88% chances player received the book.
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
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),),
# ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare), # Repeatable, so no need for bookseller
ShopSource(items_price=((999, Material.fiber),), shop_region=LogicRegion.raccoon_shop_2),),
Book.way_of_the_wind_pt_1: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
ShopSource(money_price=15000, shop_region=LogicRegion.bookseller_2),),
ShopSource(price=15000, shop_region=LogicRegion.bookseller_permanent),),
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),)),),
ShopSource(price=35000, shop_region=LogicRegion.bookseller_permanent, 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),),
ShopSource(price=20000, shop_region=LogicRegion.bookseller_rare),),
# Experience Books
Book.book_of_stars: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
ShopSource(price=5000, shop_region=LogicRegion.bookseller_permanent),),
Book.bait_and_bobber: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
ShopSource(price=5000, shop_region=LogicRegion.bookseller_experience),),
Book.combat_quarterly: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
ShopSource(price=5000, shop_region=LogicRegion.bookseller_experience),),
Book.mining_monthly: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
ShopSource(price=5000, shop_region=LogicRegion.bookseller_experience),),
Book.stardew_valley_almanac: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
ShopSource(price=5000, shop_region=LogicRegion.bookseller_experience),),
Book.woodcutters_weekly: (
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
ShopSource(price=5000, shop_region=LogicRegion.bookseller_experience),),
# Catalogues
Catalogue.wizard: (ShopSource(price=150000, shop_region=Region.sewer, other_requirements=(CatalogueRequirement(Catalogue.wizard),)),),
Catalogue.furniture: (ShopSource(price=200000, shop_region=Region.carpenter, other_requirements=(CatalogueRequirement(Catalogue.furniture),BuildingRequirement(BuildingNames.kitchen),)),),
# Furniture
Furniture.single_bed: (ShopSource(price=500, shop_region=Region.carpenter),),
Furniture.crane_game_house_plant: (ShopSource(price=500, shop_region=Region.movie_theater),),
Furniture.cursed_mannequin: (MonsterSource(monsters=(Monster.haunted_skull,), amount_tier=MAX_MONTHS),),
},
fishes=(
fish_data.albacore,
@@ -396,7 +445,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=6000,
price=6000,
items_price=((350, Material.wood), (150, Material.stone))
),
),
@@ -406,7 +455,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=12_000,
price=12_000,
items_price=((450, Material.wood), (200, Material.stone))
),
),
@@ -417,7 +466,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=25_000,
price=25_000,
items_price=((550, Material.wood), (300, Material.stone))
),
),
@@ -428,7 +477,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=4000,
price=4000,
items_price=((300, Material.wood), (100, Material.stone))
),
),
@@ -438,7 +487,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=10_000,
price=10_000,
items_price=((400, Material.wood), (150, Material.stone))
),
),
@@ -449,7 +498,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=20_000,
price=20_000,
items_price=((500, Material.wood), (200, Material.stone))
),
),
@@ -460,7 +509,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=5000,
price=5000,
items_price=((200, Material.stone), (5, WaterItem.seaweed), (5, WaterItem.green_algae))
),
),
@@ -470,7 +519,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=2500,
price=2500,
items_price=((50, Material.stone), (150, Material.wood), (4, ArtisanGood.cloth))
),
),
@@ -480,7 +529,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=15_000,
price=15_000,
items_price=((300, Material.wood),)
),
),
@@ -490,7 +539,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=20_000,
price=20_000,
items_price=((550, Material.wood), (300, Material.stone))
),
),
@@ -501,7 +550,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=100,
price=100,
items_price=((100, Material.stone), (10, Material.clay), (5, MetalBar.copper))
),
),
@@ -511,7 +560,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=10_000,
price=10_000,
items_price=((500, Material.stone), (10, MetalBar.quartz), (1, MetalBar.iridium))
),
),
@@ -521,7 +570,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=10_000,
price=10_000,
items_price=((100, Material.hardwood), (5, MetalBar.iron))
),
),
@@ -531,7 +580,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=1000,
price=1000,
items_price=((75, Material.stone),)
),
),
@@ -541,7 +590,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=250,
price=250,
items_price=((150, Material.wood),)
),
),
@@ -551,7 +600,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=5000,
price=5000,
items_price=((25, Material.hardwood),)
),
),
@@ -561,7 +610,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=10_000,
price=10_000,
items_price=((450, Material.wood),)
),
),
@@ -572,7 +621,7 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=65_000,
price=65_000,
items_price=((100, Material.hardwood),)
),
),
@@ -583,10 +632,115 @@ pelican_town = ContentPack(
sources=(
ShopSource(
shop_region=Region.carpenter,
money_price=100_000,
price=100_000,
),
),
upgrade_from=BuildingNames.kids_room,
),
)
# Building(
# WizardBuilding.earth_obelisk,
# sources=(
# ShopSource(
# shop_region=Region.wizard_tower,
# price=500_000,
# items_price=((10, MetalBar.iridium), (10, Mineral.earth_crystal),)
# ),
# ),
# ),
# Building(
# WizardBuilding.water_obelisk,
# sources=(
# ShopSource(
# shop_region=Region.wizard_tower,
# price=500_000,
# items_price=((5, MetalBar.iridium), (10, Fish.clam), (10, WaterItem.coral),)
# ),
# ),
# ),
# Building(
# WizardBuilding.desert_obelisk,
# sources=(
# ShopSource(
# shop_region=Region.wizard_tower,
# price=1_000_000,
# items_price=((20, MetalBar.iridium), (10, Forageable.coconut), (10, Forageable.cactus_fruit),)
# ),
# ),
# ),
# Building(
# WizardBuilding.island_obelisk,
# sources=(
# ShopSource(
# shop_region=Region.wizard_tower,
# price=1_000_000,
# items_price=((10, MetalBar.iridium), (10, Forageable.dragon_tooth), (10, Fruit.banana),)
# ),
# ),
# ),
# Building(
# WizardBuilding.junimo_hut,
# sources=(
# ShopSource(
# shop_region=Region.wizard_tower,
# price=20_000,
# items_price=((200, Material.stone), (9, Fruit.starfruit), (100, Material.fiber),)
# ),
# ),
# ),
# Building(
# WizardBuilding.gold_clock,
# sources=(
# ShopSource(
# shop_region=Region.wizard_tower,
# price=10_000_000,
# ),
# ),
# ),
),
hat_sources={
# Hats from the Hat Mouse
Hats.blue_ribbon: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(GrangeDisplayRequirement(),)),),
Hats.blue_bonnet: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(MuseumCompletionRequirement(40),)),),
Hats.cowboy: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(MuseumCompletionRequirement(),)),),
Hats.butterfly_bow: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(NumberOfFriendsRequirement(1, 5),)),),
Hats.mouse_ears: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(NumberOfFriendsRequirement(1, 10),)),),
Hats.cat_ears: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(NumberOfFriendsRequirement(8, 10),)),),
Hats.tiara: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(NumberOfFriendsRequirement(4, 5),)),),
Hats.santa_hat: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(NumberOfFriendsRequirement(10, 5),)),),
Hats.earmuffs: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(NumberOfFriendsRequirement(20, 5),)),),
Hats.tropiclip: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(BuildingRequirement(BuildingNames.kitchen),)),),
Hats.hunters_cap: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(BuildingRequirement(BuildingNames.cellar),)),),
Hats.polka_bow: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(HelpWantedRequirement(10),)),),
Hats.chicken_mask: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(HelpWantedRequirement(40),)),),
Hats.straw: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(EggHuntRequirement(),)),),
Hats.sailors_cap: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(FishingCompetitionRequirement(),)),),
Hats.jester_hat: (Tag(ItemTag.HAT), HatMouseSource(price=25000, unlock_requirements=(MovieRequirement(),)),),
Hats.governors_hat: (Tag(ItemTag.HAT), HatMouseSource(price=5000, unlock_requirements=(LuauDelightRequirementRequirement(),)),),
Hats.white_bow: (Tag(ItemTag.HAT), HatMouseSource(price=5000, unlock_requirements=(ReceivedRaccoonsRequirement(8),)),),
Hats.sports_cap: (Tag(ItemTag.HAT), HatMouseSource(price=5000, unlock_requirements=(PrizeMachineRequirement(11),)),),
Hats.emilys_magic_hat: (Tag(ItemTag.HAT), ShopSource(price=10000, shop_region=LogicRegion.lost_items_shop,
other_requirements=(
SpecificFriendRequirement(NPC.emily, 14), RegionRequirement(Region.farm))),),
Hats.fedora: (Tag(ItemTag.HAT), ShopSource(price=500, currency=Currency.star_token, shop_region=LogicRegion.fair),),
Hats.cone_hat: (Tag(ItemTag.HAT), ShopSource(price=5000, shop_region=LogicRegion.night_market),),
Hats.red_fez: (Tag(ItemTag.HAT), ShopSource(price=8000, shop_region=LogicRegion.traveling_cart),),
Hats.garbage_hat: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.town,), grind_months=12),),
Hats.mystery_hat: (Tag(ItemTag.HAT), MysteryBoxSource(amount=100),),
Hats.fishing_hat: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Fish.stonefish, Fish.ice_pip, Fish.scorpion_carp, Fish.spook_fish,
Fish.midnight_squid, Fish.void_salmon, Fish.slimejack,)),),
Hats.bucket_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.hat.has_bucket_hat),),
Hats.leprechaun_hat: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.forest,), seasons=(Season.spring,), ),),
Hats.mushroom_cap: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.farm,), seasons=(Season.fall,),
other_requirements=(ToolRequirement(Tool.axe),),),),
Hats.raccoon_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.quest.has_raccoon_shop(3) &
logic.region.can_reach(LogicRegion.raccoon_shop_3)),),
Hats.squid_hat: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.festival.can_squidfest_iridium_reward()),),
}
)

View File

@@ -2,9 +2,14 @@ from .ginger_island import ginger_island_content_pack as ginger_island_content_p
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.game_item import GenericSource, ItemTag, Tag
from ...data.harvest import HarvestCropSource
from ...data.hats_data import Hats
from ...data.requirement import DangerousMinesRequirement, CraftedItemsRequirement
from ...data.shop import HatMouseSource
from ...logic.tailoring_logic import TailoringSource
from ...strings.crop_names import Fruit
from ...strings.metal_names import MetalBar
from ...strings.region_names import Region
from ...strings.seed_names import Seed
@@ -31,5 +36,11 @@ qi_board_content_pack = QiBoardContentPack(
fish_data.glacierfish_jr,
fish_data.legend_ii,
fish_data.radioactive_carp,
)
),
hat_sources={
Hats.space_helmet: (HatMouseSource(price=20000, unlock_requirements=(DangerousMinesRequirement(120),)),),
Hats.qi_mask: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Fruit.qi_fruit,)),),
Hats.radioactive_goggles: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(MetalBar.radioactive,)),),
Hats.gnomes_cap: (Tag(ItemTag.HAT), HatMouseSource(price=1000, unlock_requirements=(CraftedItemsRequirement(9999),)),),
},
)

View File

@@ -1,13 +1,24 @@
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.game_item import CustomRuleSource, ItemTag, Tag
from ...data.harvest import ForagingSource, HarvestCropSource
from ...data.hats_data import Hats
from ...data.monster_data import MonsterSource
from ...data.requirement import RegionRequirement, MeetRequirement, MonsterKillRequirement
from ...data.shop import ShopSource
from ...logic.tailoring_logic import TailoringSource
from ...logic.time_logic import MAX_MONTHS
from ...strings.crop_names import Fruit, Vegetable
from ...strings.currency_names import Currency
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.region_names import Region
from ...strings.geode_names import Geode
from ...strings.metal_names import Artifact
from ...strings.monster_names import Monster
from ...strings.region_names import Region, LogicRegion
from ...strings.season_names import Season
from ...strings.seed_names import Seed
from ...strings.villager_names import NPC
the_desert = ContentPack(
"The Desert (Vanilla)",
@@ -16,13 +27,16 @@ the_desert = ContentPack(
),
harvest_sources={
Forageable.cactus_fruit: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.desert,)),
HarvestCropSource(seed=Seed.cactus, seasons=())
),
Forageable.coconut: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.desert,)),
),
Mushroom.purple: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.skull_cavern_25,)),
),
@@ -31,10 +45,10 @@ the_desert = ContentPack(
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,)),),
Seed.cactus: (ShopSource(price=150, shop_region=Region.oasis),),
Seed.rhubarb: (ShopSource(price=100, shop_region=Region.oasis, seasons=(Season.spring,)),),
Seed.starfruit: (ShopSource(price=400, shop_region=Region.oasis, seasons=(Season.summer,)),),
Seed.beet: (ShopSource(price=20, shop_region=Region.oasis, seasons=(Season.fall,)),),
},
fishes=(
fish_data.sandfish,
@@ -43,4 +57,33 @@ the_desert = ContentPack(
villagers=(
villagers_data.sandy,
),
hat_sources={
Hats.top_hat: (Tag(ItemTag.HAT), ShopSource(price=8000, shop_region=Region.casino, currency=Currency.qi_coin),),
Hats.gils_hat: (Tag(ItemTag.HAT), ShopSource(price=10000, shop_region=LogicRegion.lost_items_shop,
other_requirements=(
RegionRequirement(Region.skull_cavern_100), RegionRequirement(LogicRegion.desert_festival),)),),
Hats.abigails_bow: (Tag(ItemTag.HAT), ShopSource(price=60, currency=Currency.calico_egg, shop_region=LogicRegion.desert_festival,
other_requirements=(MeetRequirement(NPC.abigail),)),),
Hats.tricorn: (Tag(ItemTag.HAT), ShopSource(price=100, currency=Currency.calico_egg, shop_region=LogicRegion.desert_festival,
other_requirements=(MeetRequirement(NPC.elliott),)),),
Hats.blue_bow: (Tag(ItemTag.HAT), ShopSource(price=60, currency=Currency.calico_egg, shop_region=LogicRegion.desert_festival),),
Hats.dark_velvet_bow: (Tag(ItemTag.HAT), ShopSource(price=75, currency=Currency.calico_egg, shop_region=LogicRegion.desert_festival),),
Hats.mummy_mask: (Tag(ItemTag.HAT), ShopSource(price=120, currency=Currency.calico_egg, shop_region=LogicRegion.desert_festival),),
Hats.arcane_hat: (Tag(ItemTag.HAT), ShopSource(price=20000, shop_region=Region.adventurer_guild,
other_requirements=(MonsterKillRequirement((Monster.mummy,), 100),)),),
Hats.green_turban: (Tag(ItemTag.HAT), ShopSource(price=50, currency=Geode.omni, shop_region=Region.desert,),),
Hats.magic_cowboy_hat: (Tag(ItemTag.HAT), ShopSource(price=333, currency=Geode.omni, shop_region=Region.desert,),),
Hats.magic_turban: (Tag(ItemTag.HAT), ShopSource(price=333, currency=Geode.omni, shop_region=Region.desert,),),
Hats.laurel_wreath_crown: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.hat.can_get_unlikely_hat_at_outfit_services),),
Hats.joja_cap: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.hat.can_get_unlikely_hat_at_outfit_services),),
Hats.dark_ballcap: (Tag(ItemTag.HAT), CustomRuleSource(create_rule=lambda logic: logic.hat.can_get_unlikely_hat_at_outfit_services),),
Hats.dark_cowboy_hat: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.skull_cavern_100,)),),
Hats.blue_cowboy_hat: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.skull_cavern_100,))),
Hats.red_cowboy_hat: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.skull_cavern_100,))),
Hats.golden_mask: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Artifact.golden_mask,)),),
Hats.white_turban: (Tag(ItemTag.HAT), ForagingSource(regions=(Region.skull_cavern_100,))),
Hats.knights_helmet: (Tag(ItemTag.HAT), MonsterSource(monsters=(Monster.pepper_rex,), amount_tier=MAX_MONTHS,
other_requirements=(RegionRequirement(region=Region.adventurer_guild),)),),
}
)

View File

@@ -1,6 +1,7 @@
from .pelican_town import pelican_town as pelican_town_content_pack
from ..game_content import ContentPack
from ...data.animal import IncubatorSource, Animal, AnimalName
from ...data.game_item import Tag, ItemTag
from ...data.harvest import FruitBatsSource, MushroomCaveSource
from ...data.shop import ShopSource
from ...strings.animal_product_names import AnimalProduct
@@ -16,32 +17,41 @@ the_farm = ContentPack(
harvest_sources={
# Fruit cave
Forageable.blackberry: (
Tag(ItemTag.FORAGE),
FruitBatsSource(),
),
Forageable.salmonberry: (
Tag(ItemTag.FORAGE),
FruitBatsSource(),
),
Forageable.spice_berry: (
Tag(ItemTag.FORAGE),
FruitBatsSource(),
),
Forageable.wild_plum: (
Tag(ItemTag.FORAGE),
FruitBatsSource(),
),
# Mushrooms
Mushroom.common: (
Tag(ItemTag.FORAGE),
MushroomCaveSource(),
),
Mushroom.chanterelle: (
Tag(ItemTag.FORAGE),
MushroomCaveSource(),
),
Mushroom.morel: (
Tag(ItemTag.FORAGE),
MushroomCaveSource(),
),
Mushroom.purple: (
Tag(ItemTag.FORAGE),
MushroomCaveSource(),
),
Mushroom.red: (
Tag(ItemTag.FORAGE),
MushroomCaveSource(),
),
},
@@ -49,41 +59,41 @@ the_farm = ContentPack(
Animal(AnimalName.chicken,
required_building=Building.coop,
sources=(
ShopSource(shop_region=Region.ranch, money_price=800),
ShopSource(shop_region=Region.ranch, price=800),
# For now there is no way to obtain the starter item, so this adds additional rules in the system for nothing.
# IncubatorSource(AnimalProduct.egg_starter)
)),
Animal(AnimalName.cow,
required_building=Building.barn,
sources=(
ShopSource(shop_region=Region.ranch, money_price=1500),
ShopSource(shop_region=Region.ranch, price=1500),
)),
Animal(AnimalName.goat,
required_building=Building.big_barn,
sources=(
ShopSource(shop_region=Region.ranch, money_price=4000),
ShopSource(shop_region=Region.ranch, price=4000),
)),
Animal(AnimalName.duck,
required_building=Building.big_coop,
sources=(
ShopSource(shop_region=Region.ranch, money_price=1200),
ShopSource(shop_region=Region.ranch, price=1200),
# For now there is no way to obtain the starter item, so this adds additional rules in the system for nothing.
# IncubatorSource(AnimalProduct.duck_egg_starter)
)),
Animal(AnimalName.sheep,
required_building=Building.deluxe_barn,
sources=(
ShopSource(shop_region=Region.ranch, money_price=8000),
ShopSource(shop_region=Region.ranch, price=8000),
)),
Animal(AnimalName.rabbit,
required_building=Building.deluxe_coop,
sources=(
ShopSource(shop_region=Region.ranch, money_price=8000),
ShopSource(shop_region=Region.ranch, price=8000),
)),
Animal(AnimalName.pig,
required_building=Building.deluxe_barn,
sources=(
ShopSource(shop_region=Region.ranch, money_price=16000),
ShopSource(shop_region=Region.ranch, price=16000),
)),
Animal(AnimalName.void_chicken,
required_building=Building.big_coop,

View File

@@ -1,9 +1,16 @@
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.game_item import Tag, ItemTag
from ...data.harvest import ForagingSource
from ...data.requirement import ToolRequirement
from ...data.hats_data import Hats
from ...data.monster_data import MonsterSource
from ...data.requirement import ToolRequirement, RegionRequirement
from ...logic.tailoring_logic import TailoringSource
from ...logic.time_logic import MAX_MONTHS
from ...strings.fish_names import Fish
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.monster_names import Monster
from ...strings.region_names import Region
from ...strings.tool_names import Tool
@@ -14,12 +21,15 @@ the_mines = ContentPack(
),
harvest_sources={
Forageable.cave_carrot: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.mines_floor_10,), other_requirements=(ToolRequirement(Tool.hoe),)),
),
Mushroom.red: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.mines_floor_95,)),
),
Mushroom.purple: (
Tag(ItemTag.FORAGE),
ForagingSource(regions=(Region.mines_floor_95,)),
)
},
@@ -32,4 +42,15 @@ the_mines = ContentPack(
villagers=(
villagers_data.dwarf,
),
hat_sources={
Hats.logo_cap: (Tag(ItemTag.HAT), TailoringSource(tailoring_items=(Fish.lava_eel,)),),
Hats.hard_hat: (Tag(ItemTag.HAT), MonsterSource(monsters=(Monster.duggy, Monster.duggy_dangerous, Monster.magma_duggy,),
amount_tier=3,
other_requirements=(RegionRequirement(region=Region.adventurer_guild),)),),
Hats.skeleton_mask: (Tag(ItemTag.HAT), MonsterSource(monsters=(Monster.skeleton, Monster.skeleton_mage, Monster.skeleton_dangerous,),
amount_tier=MAX_MONTHS,
other_requirements=(RegionRequirement(region=Region.adventurer_guild),)),),
Hats.squires_helmet: (Tag(ItemTag.HAT), MonsterSource(monsters=(Monster.metal_head,),
amount_tier=MAX_MONTHS),),
},
)

View File

@@ -1,906 +0,0 @@
from ..bundles.bundle import BundleTemplate, IslandBundleTemplate, DeepBundleTemplate, CurrencyBundleTemplate, MoneyBundleTemplate, FestivalBundleTemplate
from ..bundles.bundle_item import BundleItem
from ..bundles.bundle_room import BundleRoomTemplate
from ..content import content_packs
from ..content.vanilla.base import all_fruits, all_vegetables, all_edible_mushrooms
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
from ..strings.bundle_names import CCRoom, BundleName
from ..strings.craftable_names import Fishing, Craftable, Bomb, Consumable, Lighting
from ..strings.crop_names import Fruit, Vegetable
from ..strings.currency_names import Currency
from ..strings.fertilizer_names import Fertilizer, RetainingSoil, SpeedGro
from ..strings.fish_names import Fish, WaterItem, Trash
from ..strings.flower_names import Flower
from ..strings.food_names import Beverage, Meal
from ..strings.forageable_names import Forageable, Mushroom
from ..strings.geode_names import Geode
from ..strings.gift_names import Gift
from ..strings.ingredient_names import Ingredient
from ..strings.material_names import Material
from ..strings.metal_names import MetalBar, Artifact, Fossil, Ore, Mineral
from ..strings.monster_drop_names import Loot
from ..strings.quality_names import ForageQuality, ArtisanQuality, FishQuality
from ..strings.seed_names import Seed, TreeSeed
wild_horseradish = BundleItem(Forageable.wild_horseradish)
daffodil = BundleItem(Forageable.daffodil)
leek = BundleItem(Forageable.leek)
dandelion = BundleItem(Forageable.dandelion)
morel = BundleItem(Mushroom.morel)
common_mushroom = BundleItem(Mushroom.common)
salmonberry = BundleItem(Forageable.salmonberry)
spring_onion = BundleItem(Forageable.spring_onion)
grape = BundleItem(Fruit.grape)
spice_berry = BundleItem(Forageable.spice_berry)
sweet_pea = BundleItem(Forageable.sweet_pea)
red_mushroom = BundleItem(Mushroom.red)
fiddlehead_fern = BundleItem(Forageable.fiddlehead_fern)
wild_plum = BundleItem(Forageable.wild_plum)
hazelnut = BundleItem(Forageable.hazelnut)
blackberry = BundleItem(Forageable.blackberry)
chanterelle = BundleItem(Mushroom.chanterelle)
winter_root = BundleItem(Forageable.winter_root)
crystal_fruit = BundleItem(Forageable.crystal_fruit)
snow_yam = BundleItem(Forageable.snow_yam)
crocus = BundleItem(Forageable.crocus)
holly = BundleItem(Forageable.holly)
coconut = BundleItem(Forageable.coconut)
cactus_fruit = BundleItem(Forageable.cactus_fruit)
cave_carrot = BundleItem(Forageable.cave_carrot)
purple_mushroom = BundleItem(Mushroom.purple)
maple_syrup = BundleItem(ArtisanGood.maple_syrup)
oak_resin = BundleItem(ArtisanGood.oak_resin)
pine_tar = BundleItem(ArtisanGood.pine_tar)
nautilus_shell = BundleItem(WaterItem.nautilus_shell)
coral = BundleItem(WaterItem.coral)
sea_urchin = BundleItem(WaterItem.sea_urchin)
rainbow_shell = BundleItem(Forageable.rainbow_shell)
clam = BundleItem(Fish.clam)
cockle = BundleItem(Fish.cockle)
mussel = BundleItem(Fish.mussel)
oyster = BundleItem(Fish.oyster)
seaweed = BundleItem(WaterItem.seaweed, can_have_quality=False)
wood = BundleItem(Material.wood, 99)
stone = BundleItem(Material.stone, 99)
hardwood = BundleItem(Material.hardwood, 10)
clay = BundleItem(Material.clay, 10)
fiber = BundleItem(Material.fiber, 99)
moss = BundleItem(Material.moss, 10)
mixed_seeds = BundleItem(Seed.mixed)
acorn = BundleItem(TreeSeed.acorn)
maple_seed = BundleItem(TreeSeed.maple)
pine_cone = BundleItem(TreeSeed.pine)
mahogany_seed = BundleItem(TreeSeed.mahogany)
mushroom_tree_seed = BundleItem(TreeSeed.mushroom, source=BundleItem.Sources.island)
mystic_tree_seed = BundleItem(TreeSeed.mystic, source=BundleItem.Sources.masteries)
mossy_seed = BundleItem(TreeSeed.mossy)
strawberry_seeds = BundleItem(Seed.strawberry)
blue_jazz = BundleItem(Flower.blue_jazz)
cauliflower = BundleItem(Vegetable.cauliflower)
green_bean = BundleItem(Vegetable.green_bean)
kale = BundleItem(Vegetable.kale)
parsnip = BundleItem(Vegetable.parsnip)
potato = BundleItem(Vegetable.potato)
strawberry = BundleItem(Fruit.strawberry, source=BundleItem.Sources.festival)
tulip = BundleItem(Flower.tulip)
unmilled_rice = BundleItem(Vegetable.unmilled_rice)
coffee_bean = BundleItem(Seed.coffee)
garlic = BundleItem(Vegetable.garlic)
blueberry = BundleItem(Fruit.blueberry)
corn = BundleItem(Vegetable.corn)
hops = BundleItem(Vegetable.hops)
hot_pepper = BundleItem(Fruit.hot_pepper)
melon = BundleItem(Fruit.melon)
poppy = BundleItem(Flower.poppy)
radish = BundleItem(Vegetable.radish)
summer_spangle = BundleItem(Flower.summer_spangle)
sunflower = BundleItem(Flower.sunflower)
tomato = BundleItem(Vegetable.tomato)
wheat = BundleItem(Vegetable.wheat)
hay = BundleItem(Forageable.hay)
amaranth = BundleItem(Vegetable.amaranth)
bok_choy = BundleItem(Vegetable.bok_choy)
cranberries = BundleItem(Fruit.cranberries)
eggplant = BundleItem(Vegetable.eggplant)
fairy_rose = BundleItem(Flower.fairy_rose)
pumpkin = BundleItem(Vegetable.pumpkin)
yam = BundleItem(Vegetable.yam)
sweet_gem_berry = BundleItem(Fruit.sweet_gem_berry)
rhubarb = BundleItem(Fruit.rhubarb)
beet = BundleItem(Vegetable.beet)
red_cabbage = BundleItem(Vegetable.red_cabbage)
starfruit = BundleItem(Fruit.starfruit)
artichoke = BundleItem(Vegetable.artichoke)
pineapple = BundleItem(Fruit.pineapple, source=BundleItem.Sources.content)
taro_root = BundleItem(Vegetable.taro_root, source=BundleItem.Sources.content)
carrot = BundleItem(Vegetable.carrot)
summer_squash = BundleItem(Vegetable.summer_squash)
broccoli = BundleItem(Vegetable.broccoli)
powdermelon = BundleItem(Fruit.powdermelon)
egg = BundleItem(AnimalProduct.egg)
large_egg = BundleItem(AnimalProduct.large_egg)
brown_egg = BundleItem(AnimalProduct.brown_egg)
large_brown_egg = BundleItem(AnimalProduct.large_brown_egg)
wool = BundleItem(AnimalProduct.wool)
milk = BundleItem(AnimalProduct.milk)
large_milk = BundleItem(AnimalProduct.large_milk)
goat_milk = BundleItem(AnimalProduct.goat_milk)
large_goat_milk = BundleItem(AnimalProduct.large_goat_milk)
truffle = BundleItem(AnimalProduct.truffle)
duck_feather = BundleItem(AnimalProduct.duck_feather)
duck_egg = BundleItem(AnimalProduct.duck_egg)
rabbit_foot = BundleItem(AnimalProduct.rabbit_foot)
dinosaur_egg = BundleItem(AnimalProduct.dinosaur_egg)
void_egg = BundleItem(AnimalProduct.void_egg)
ostrich_egg = BundleItem(AnimalProduct.ostrich_egg, source=BundleItem.Sources.content)
golden_egg = BundleItem(AnimalProduct.golden_egg)
truffle_oil = BundleItem(ArtisanGood.truffle_oil)
cloth = BundleItem(ArtisanGood.cloth)
goat_cheese = BundleItem(ArtisanGood.goat_cheese)
cheese = BundleItem(ArtisanGood.cheese)
honey = BundleItem(ArtisanGood.honey)
beer = BundleItem(Beverage.beer)
juice = BundleItem(ArtisanGood.juice)
mead = BundleItem(ArtisanGood.mead)
pale_ale = BundleItem(ArtisanGood.pale_ale)
wine = BundleItem(ArtisanGood.wine)
jelly = BundleItem(ArtisanGood.jelly)
pickles = BundleItem(ArtisanGood.pickles)
caviar = BundleItem(ArtisanGood.caviar)
aged_roe = BundleItem(ArtisanGood.aged_roe)
roe = BundleItem(AnimalProduct.roe)
squid_ink = BundleItem(AnimalProduct.squid_ink)
coffee = BundleItem(Beverage.coffee)
green_tea = BundleItem(ArtisanGood.green_tea)
apple = BundleItem(Fruit.apple)
apricot = BundleItem(Fruit.apricot)
orange = BundleItem(Fruit.orange)
peach = BundleItem(Fruit.peach)
pomegranate = BundleItem(Fruit.pomegranate)
cherry = BundleItem(Fruit.cherry)
banana = BundleItem(Fruit.banana, source=BundleItem.Sources.content)
mango = BundleItem(Fruit.mango, source=BundleItem.Sources.content)
basic_fertilizer = BundleItem(Fertilizer.basic, 100)
quality_fertilizer = BundleItem(Fertilizer.quality, 20)
deluxe_fertilizer = BundleItem(Fertilizer.deluxe, 5, source=BundleItem.Sources.island)
basic_retaining_soil = BundleItem(RetainingSoil.basic, 80)
quality_retaining_soil = BundleItem(RetainingSoil.quality, 50)
deluxe_retaining_soil = BundleItem(RetainingSoil.deluxe, 20, source=BundleItem.Sources.island)
speed_gro = BundleItem(SpeedGro.basic, 40)
deluxe_speed_gro = BundleItem(SpeedGro.deluxe, 20)
hyper_speed_gro = BundleItem(SpeedGro.hyper, 5, source=BundleItem.Sources.island)
tree_fertilizer = BundleItem(Fertilizer.tree, 20)
lobster = BundleItem(Fish.lobster)
crab = BundleItem(Fish.crab)
shrimp = BundleItem(Fish.shrimp)
crayfish = BundleItem(Fish.crayfish)
snail = BundleItem(Fish.snail)
periwinkle = BundleItem(Fish.periwinkle)
trash = BundleItem(Trash.trash)
driftwood = BundleItem(Trash.driftwood)
soggy_newspaper = BundleItem(Trash.soggy_newspaper)
broken_cd = BundleItem(Trash.broken_cd)
broken_glasses = BundleItem(Trash.broken_glasses)
chub = BundleItem(Fish.chub)
catfish = BundleItem(Fish.catfish)
rainbow_trout = BundleItem(Fish.rainbow_trout)
lingcod = BundleItem(Fish.lingcod)
walleye = BundleItem(Fish.walleye)
perch = BundleItem(Fish.perch)
pike = BundleItem(Fish.pike)
bream = BundleItem(Fish.bream)
salmon = BundleItem(Fish.salmon)
sunfish = BundleItem(Fish.sunfish)
tiger_trout = BundleItem(Fish.tiger_trout)
shad = BundleItem(Fish.shad)
smallmouth_bass = BundleItem(Fish.smallmouth_bass)
dorado = BundleItem(Fish.dorado)
carp = BundleItem(Fish.carp)
midnight_carp = BundleItem(Fish.midnight_carp)
largemouth_bass = BundleItem(Fish.largemouth_bass)
sturgeon = BundleItem(Fish.sturgeon)
bullhead = BundleItem(Fish.bullhead)
tilapia = BundleItem(Fish.tilapia)
pufferfish = BundleItem(Fish.pufferfish)
tuna = BundleItem(Fish.tuna)
super_cucumber = BundleItem(Fish.super_cucumber)
flounder = BundleItem(Fish.flounder)
anchovy = BundleItem(Fish.anchovy)
sardine = BundleItem(Fish.sardine)
red_mullet = BundleItem(Fish.red_mullet)
herring = BundleItem(Fish.herring)
eel = BundleItem(Fish.eel)
octopus = BundleItem(Fish.octopus)
red_snapper = BundleItem(Fish.red_snapper)
squid = BundleItem(Fish.squid)
sea_cucumber = BundleItem(Fish.sea_cucumber)
albacore = BundleItem(Fish.albacore)
halibut = BundleItem(Fish.halibut)
scorpion_carp = BundleItem(Fish.scorpion_carp)
sandfish = BundleItem(Fish.sandfish)
woodskip = BundleItem(Fish.woodskip)
lava_eel = BundleItem(Fish.lava_eel)
ice_pip = BundleItem(Fish.ice_pip)
stonefish = BundleItem(Fish.stonefish)
ghostfish = BundleItem(Fish.ghostfish)
bouquet = BundleItem(Gift.bouquet)
wilted_bouquet = BundleItem(Gift.wilted_bouquet)
copper_bar = BundleItem(MetalBar.copper)
iron_Bar = BundleItem(MetalBar.iron)
gold_bar = BundleItem(MetalBar.gold)
iridium_bar = BundleItem(MetalBar.iridium)
refined_quartz = BundleItem(MetalBar.quartz)
coal = BundleItem(Material.coal, 5)
iridium_ore = BundleItem(Ore.iridium)
gold_ore = BundleItem(Ore.gold)
iron_ore = BundleItem(Ore.iron)
copper_ore = BundleItem(Ore.copper)
battery_pack = BundleItem(ArtisanGood.battery_pack)
quartz = BundleItem(Mineral.quartz)
fire_quartz = BundleItem(Mineral.fire_quartz)
frozen_tear = BundleItem(Mineral.frozen_tear)
earth_crystal = BundleItem(Mineral.earth_crystal)
emerald = BundleItem(Mineral.emerald)
aquamarine = BundleItem(Mineral.aquamarine)
ruby = BundleItem(Mineral.ruby)
amethyst = BundleItem(Mineral.amethyst)
topaz = BundleItem(Mineral.topaz)
jade = BundleItem(Mineral.jade)
slime = BundleItem(Loot.slime, 99)
bug_meat = BundleItem(Loot.bug_meat, 10)
bat_wing = BundleItem(Loot.bat_wing, 10)
solar_essence = BundleItem(Loot.solar_essence)
void_essence = BundleItem(Loot.void_essence)
petrified_slime = BundleItem(Mineral.petrified_slime)
blue_slime_egg = BundleItem(AnimalProduct.slime_egg_blue)
red_slime_egg = BundleItem(AnimalProduct.slime_egg_red)
purple_slime_egg = BundleItem(AnimalProduct.slime_egg_purple)
green_slime_egg = BundleItem(AnimalProduct.slime_egg_green)
tiger_slime_egg = BundleItem(AnimalProduct.slime_egg_tiger, source=BundleItem.Sources.island)
cherry_bomb = BundleItem(Bomb.cherry_bomb, 5)
bomb = BundleItem(Bomb.bomb, 2)
mega_bomb = BundleItem(Bomb.mega_bomb)
explosive_ammo = BundleItem(Craftable.explosive_ammo, 5)
maki_roll = BundleItem(Meal.maki_roll)
fried_egg = BundleItem(Meal.fried_egg)
omelet = BundleItem(Meal.omelet)
pizza = BundleItem(Meal.pizza)
hashbrowns = BundleItem(Meal.hashbrowns)
pancakes = BundleItem(Meal.pancakes)
bread = BundleItem(Meal.bread)
tortilla = BundleItem(Meal.tortilla)
triple_shot_espresso = BundleItem(Beverage.triple_shot_espresso)
farmer_s_lunch = BundleItem(Meal.farmer_lunch)
survival_burger = BundleItem(Meal.survival_burger)
dish_o_the_sea = BundleItem(Meal.dish_o_the_sea)
miner_s_treat = BundleItem(Meal.miners_treat)
roots_platter = BundleItem(Meal.roots_platter)
salad = BundleItem(Meal.salad)
cheese_cauliflower = BundleItem(Meal.cheese_cauliflower)
parsnip_soup = BundleItem(Meal.parsnip_soup)
fried_mushroom = BundleItem(Meal.fried_mushroom)
salmon_dinner = BundleItem(Meal.salmon_dinner)
pepper_poppers = BundleItem(Meal.pepper_poppers)
spaghetti = BundleItem(Meal.spaghetti)
sashimi = BundleItem(Meal.sashimi)
blueberry_tart = BundleItem(Meal.blueberry_tart)
algae_soup = BundleItem(Meal.algae_soup)
pale_broth = BundleItem(Meal.pale_broth)
chowder = BundleItem(Meal.chowder)
cookie = BundleItem(Meal.cookie)
ancient_doll = BundleItem(Artifact.ancient_doll)
ice_cream = BundleItem(Meal.ice_cream)
cranberry_candy = BundleItem(Meal.cranberry_candy)
ginger_ale = BundleItem(Beverage.ginger_ale, source=BundleItem.Sources.island)
pink_cake = BundleItem(Meal.pink_cake)
plum_pudding = BundleItem(Meal.plum_pudding)
chocolate_cake = BundleItem(Meal.chocolate_cake)
rhubarb_pie = BundleItem(Meal.rhubarb_pie)
shrimp_cocktail = BundleItem(Meal.shrimp_cocktail)
pina_colada = BundleItem(Beverage.pina_colada, source=BundleItem.Sources.island)
stuffing = BundleItem(Meal.stuffing)
magic_rock_candy = BundleItem(Meal.magic_rock_candy)
spicy_eel = BundleItem(Meal.spicy_eel)
crab_cakes = BundleItem(Meal.crab_cakes)
eggplant_parmesan = BundleItem(Meal.eggplant_parmesan)
pumpkin_soup = BundleItem(Meal.pumpkin_soup)
lucky_lunch = BundleItem(Meal.lucky_lunch)
green_algae = BundleItem(WaterItem.green_algae)
white_algae = BundleItem(WaterItem.white_algae)
geode = BundleItem(Geode.geode)
frozen_geode = BundleItem(Geode.frozen)
magma_geode = BundleItem(Geode.magma)
omni_geode = BundleItem(Geode.omni)
sap = BundleItem(Material.sap)
dwarf_scroll_1 = BundleItem(Artifact.dwarf_scroll_i)
dwarf_scroll_2 = BundleItem(Artifact.dwarf_scroll_ii)
dwarf_scroll_3 = BundleItem(Artifact.dwarf_scroll_iii)
dwarf_scroll_4 = BundleItem(Artifact.dwarf_scroll_iv)
elvish_jewelry = BundleItem(Artifact.elvish_jewelry)
ancient_drum = BundleItem(Artifact.ancient_drum)
dried_starfish = BundleItem(Fossil.dried_starfish)
bone_fragment = BundleItem(Fossil.bone_fragment)
golden_mask = BundleItem(Artifact.golden_mask)
golden_relic = BundleItem(Artifact.golden_relic)
dwarf_gadget = BundleItem(Artifact.dwarf_gadget)
dwarvish_helm = BundleItem(Artifact.dwarvish_helm)
prehistoric_handaxe = BundleItem(Artifact.prehistoric_handaxe)
bone_flute = BundleItem(Artifact.bone_flute)
anchor = BundleItem(Artifact.anchor)
prehistoric_tool = BundleItem(Artifact.prehistoric_tool)
chicken_statue = BundleItem(Artifact.chicken_statue)
rusty_cog = BundleItem(Artifact.rusty_cog)
rusty_spur = BundleItem(Artifact.rusty_spur)
rusty_spoon = BundleItem(Artifact.rusty_spoon)
ancient_sword = BundleItem(Artifact.ancient_sword)
ornamental_fan = BundleItem(Artifact.ornamental_fan)
chipped_amphora = BundleItem(Artifact.chipped_amphora)
prehistoric_scapula = BundleItem(Fossil.prehistoric_scapula)
prehistoric_tibia = BundleItem(Fossil.prehistoric_tibia)
prehistoric_skull = BundleItem(Fossil.prehistoric_skull)
skeletal_hand = BundleItem(Fossil.skeletal_hand)
prehistoric_rib = BundleItem(Fossil.prehistoric_rib)
prehistoric_vertebra = BundleItem(Fossil.prehistoric_vertebra)
skeletal_tail = BundleItem(Fossil.skeletal_tail)
nautilus_fossil = BundleItem(Fossil.nautilus_fossil)
amphibian_fossil = BundleItem(Fossil.amphibian_fossil)
palm_fossil = BundleItem(Fossil.palm_fossil)
trilobite = BundleItem(Fossil.trilobite)
dinosaur_mayo = BundleItem(ArtisanGood.dinosaur_mayonnaise)
void_mayo = BundleItem(ArtisanGood.void_mayonnaise)
prismatic_shard = BundleItem(Mineral.prismatic_shard)
diamond = BundleItem(Mineral.diamond)
ancient_fruit = BundleItem(Fruit.ancient_fruit)
void_salmon = BundleItem(Fish.void_salmon)
tea_leaves = BundleItem(Vegetable.tea_leaves)
blobfish = BundleItem(Fish.blobfish)
spook_fish = BundleItem(Fish.spook_fish)
lionfish = BundleItem(Fish.lionfish, source=BundleItem.Sources.island)
blue_discus = BundleItem(Fish.blue_discus, source=BundleItem.Sources.island)
stingray = BundleItem(Fish.stingray, source=BundleItem.Sources.island)
spookfish = BundleItem(Fish.spookfish)
midnight_squid = BundleItem(Fish.midnight_squid)
angler = BundleItem(Fish.angler)
crimsonfish = BundleItem(Fish.crimsonfish)
mutant_carp = BundleItem(Fish.mutant_carp)
glacierfish = BundleItem(Fish.glacierfish)
legend = BundleItem(Fish.legend)
spinner = BundleItem(Fishing.spinner)
dressed_spinner = BundleItem(Fishing.dressed_spinner)
trap_bobber = BundleItem(Fishing.trap_bobber)
sonar_bobber = BundleItem(Fishing.sonar_bobber)
cork_bobber = BundleItem(Fishing.cork_bobber)
lead_bobber = BundleItem(Fishing.lead_bobber)
treasure_hunter = BundleItem(Fishing.treasure_hunter)
barbed_hook = BundleItem(Fishing.barbed_hook)
curiosity_lure = BundleItem(Fishing.curiosity_lure)
quality_bobber = BundleItem(Fishing.quality_bobber)
bait = BundleItem(Fishing.bait, 100)
deluxe_bait = BundleItem(Fishing.deluxe_bait, 50)
magnet = BundleItem(Fishing.magnet)
wild_bait = BundleItem(Fishing.wild_bait, 20)
magic_bait = BundleItem(Fishing.magic_bait, 10, source=BundleItem.Sources.island)
pearl = BundleItem(Gift.pearl)
challenge_bait = BundleItem(Fishing.challenge_bait, 25, source=BundleItem.Sources.masteries)
targeted_bait = BundleItem(ArtisanGood.targeted_bait, 25, source=BundleItem.Sources.content)
ginger = BundleItem(Forageable.ginger, source=BundleItem.Sources.content)
magma_cap = BundleItem(Mushroom.magma_cap, source=BundleItem.Sources.content)
wheat_flour = BundleItem(Ingredient.wheat_flour)
sugar = BundleItem(Ingredient.sugar)
vinegar = BundleItem(Ingredient.vinegar)
jack_o_lantern = BundleItem(Lighting.jack_o_lantern)
prize_ticket = BundleItem(Currency.prize_ticket)
mystery_box = BundleItem(Consumable.mystery_box)
gold_mystery_box = BundleItem(Consumable.gold_mystery_box, source=BundleItem.Sources.masteries)
calico_egg = BundleItem(Currency.calico_egg)
raccoon_crab_pot_fish_items = [periwinkle.as_amount(5), snail.as_amount(5), crayfish.as_amount(5), mussel.as_amount(5),
oyster.as_amount(5), cockle.as_amount(5), clam.as_amount(5)]
raccoon_smoked_fish_items = [BundleItem(ArtisanGood.smoked_fish, flavor=fish) for fish in
[Fish.largemouth_bass, Fish.bream, Fish.bullhead, Fish.chub, Fish.ghostfish, Fish.flounder, Fish.shad,
Fish.rainbow_trout, Fish.tilapia, Fish.red_mullet, Fish.tuna, Fish.midnight_carp, Fish.salmon, Fish.perch]]
raccoon_fish_items_flat = [*raccoon_crab_pot_fish_items, *raccoon_smoked_fish_items]
raccoon_fish_items_deep = [raccoon_crab_pot_fish_items, raccoon_smoked_fish_items]
raccoon_fish_bundle_vanilla = DeepBundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_fish, raccoon_fish_items_deep, 2, 2)
raccoon_fish_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_fish, raccoon_fish_items_flat, 3, 2)
all_specific_jellies = [BundleItem(ArtisanGood.jelly, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits]
all_specific_pickles = [BundleItem(ArtisanGood.pickles, flavor=vegetable, source=BundleItem.Sources.content) for vegetable in all_vegetables]
all_specific_dried_fruits = [*[BundleItem(ArtisanGood.dried_fruit, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits],
BundleItem(ArtisanGood.raisins, source=BundleItem.Sources.content)]
all_specific_juices = [BundleItem(ArtisanGood.juice, flavor=vegetable, source=BundleItem.Sources.content) for vegetable in all_vegetables]
raccoon_artisan_items = [*all_specific_jellies, *all_specific_pickles, *all_specific_dried_fruits, *all_specific_juices]
raccoon_artisan_bundle_vanilla = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_artisan, raccoon_artisan_items, 2, 2)
raccoon_artisan_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_artisan, raccoon_artisan_items, 3, 2)
all_specific_dried_mushrooms = [BundleItem(ArtisanGood.dried_mushroom, flavor=mushroom, source=BundleItem.Sources.content) for mushroom in all_edible_mushrooms]
raccoon_food_items = [egg.as_amount(5), cave_carrot.as_amount(5), white_algae.as_amount(5)]
raccoon_food_items_vanilla = [all_specific_dried_mushrooms, raccoon_food_items]
raccoon_food_items_thematic = [*all_specific_dried_mushrooms, *raccoon_food_items, brown_egg.as_amount(5), large_egg.as_amount(2), large_brown_egg.as_amount(2),
green_algae.as_amount(10)]
raccoon_food_bundle_vanilla = DeepBundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_food, raccoon_food_items_vanilla, 2, 2)
raccoon_food_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_food, raccoon_food_items_thematic, 3, 2)
raccoon_foraging_items = [moss, rusty_spoon, trash.as_amount(5), slime.as_amount(99), bat_wing.as_amount(10), geode.as_amount(8),
frozen_geode.as_amount(5), magma_geode.as_amount(3), coral.as_amount(4), sea_urchin.as_amount(2), bug_meat.as_amount(10),
diamond, topaz.as_amount(3), ghostfish.as_amount(3)]
raccoon_foraging_bundle_vanilla = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_foraging, raccoon_foraging_items, 2, 2)
raccoon_foraging_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_foraging, raccoon_foraging_items, 3, 2)
raccoon_bundles_vanilla = [raccoon_fish_bundle_vanilla, raccoon_artisan_bundle_vanilla, raccoon_food_bundle_vanilla, raccoon_foraging_bundle_vanilla]
raccoon_bundles_thematic = [raccoon_fish_bundle_thematic, raccoon_artisan_bundle_thematic, raccoon_food_bundle_thematic, raccoon_foraging_bundle_thematic]
raccoon_bundles_remixed = raccoon_bundles_thematic
raccoon_vanilla = BundleRoomTemplate(CCRoom.raccoon_requests, raccoon_bundles_vanilla, 8)
raccoon_thematic = BundleRoomTemplate(CCRoom.raccoon_requests, raccoon_bundles_thematic, 8)
raccoon_remixed = BundleRoomTemplate(CCRoom.raccoon_requests, raccoon_bundles_remixed, 8)
# Crafts Room
spring_foraging_items_vanilla = [wild_horseradish, daffodil, leek, dandelion]
spring_foraging_items_thematic = [*spring_foraging_items_vanilla, spring_onion, salmonberry, morel]
spring_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.spring_foraging, spring_foraging_items_vanilla, 4, 4)
spring_foraging_bundle_thematic = BundleTemplate.extend_from(spring_foraging_bundle_vanilla, spring_foraging_items_thematic)
summer_foraging_items_vanilla = [grape, spice_berry, sweet_pea]
summer_foraging_items_thematic = [*summer_foraging_items_vanilla, fiddlehead_fern, red_mushroom, rainbow_shell]
summer_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.summer_foraging, summer_foraging_items_vanilla, 3, 3)
summer_foraging_bundle_thematic = BundleTemplate.extend_from(summer_foraging_bundle_vanilla, summer_foraging_items_thematic)
fall_foraging_items_vanilla = [common_mushroom, wild_plum, hazelnut, blackberry]
fall_foraging_items_thematic = [*fall_foraging_items_vanilla, chanterelle]
fall_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.fall_foraging, fall_foraging_items_vanilla, 4, 4)
fall_foraging_bundle_thematic = BundleTemplate.extend_from(fall_foraging_bundle_vanilla, fall_foraging_items_thematic)
winter_foraging_items_vanilla = [winter_root, crystal_fruit, snow_yam, crocus]
winter_foraging_items_thematic = [*winter_foraging_items_vanilla, holly, nautilus_shell]
winter_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.winter_foraging, winter_foraging_items_vanilla, 4, 4)
winter_foraging_bundle_thematic = BundleTemplate.extend_from(winter_foraging_bundle_vanilla, winter_foraging_items_thematic)
construction_items_vanilla = [wood, stone, hardwood]
construction_items_thematic = [*construction_items_vanilla, clay, fiber, sap.as_amount(50)]
construction_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.construction, construction_items_vanilla, 4, 4)
construction_bundle_thematic = BundleTemplate.extend_from(construction_bundle_vanilla, construction_items_thematic)
exotic_foraging_items_vanilla = [coconut, cactus_fruit, cave_carrot, red_mushroom, purple_mushroom, maple_syrup, oak_resin, pine_tar, morel]
exotic_foraging_items_thematic = [*exotic_foraging_items_vanilla, coral, sea_urchin, clam, cockle, mussel, oyster, seaweed]
exotic_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.exotic_foraging, exotic_foraging_items_vanilla, 9, 5)
exotic_foraging_bundle_thematic = BundleTemplate.extend_from(exotic_foraging_bundle_vanilla, exotic_foraging_items_thematic)
beach_foraging_items = [nautilus_shell, coral, sea_urchin, rainbow_shell, clam, cockle, mussel, oyster, seaweed]
beach_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.beach_foraging, beach_foraging_items, 4, 4)
mines_foraging_items = [quartz, earth_crystal, frozen_tear, fire_quartz, red_mushroom, purple_mushroom, cave_carrot]
mines_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.mines_foraging, mines_foraging_items, 4, 4)
desert_foraging_items = [cactus_fruit.as_quality(ForageQuality.gold), cactus_fruit.as_amount(5), coconut.as_quality(ForageQuality.gold), coconut.as_amount(5)]
desert_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.desert_foraging, desert_foraging_items, 2, 2)
island_foraging_items = [ginger.as_amount(5), magma_cap.as_quality(ForageQuality.gold), magma_cap.as_amount(5),
fiddlehead_fern.as_quality(ForageQuality.gold), fiddlehead_fern.as_amount(5)]
island_foraging_bundle = IslandBundleTemplate(CCRoom.crafts_room, BundleName.island_foraging, island_foraging_items, 2, 2)
sticky_items = [sap.as_amount(500), sap.as_amount(500)]
sticky_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.sticky, sticky_items, 1, 1)
forest_items = [moss, fiber.as_amount(200), acorn.as_amount(10), maple_seed.as_amount(10), pine_cone.as_amount(10), mahogany_seed,
mushroom_tree_seed, mossy_seed.as_amount(5), mystic_tree_seed]
forest_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.forest, forest_items, 4, 2)
wild_medicine_items = [item.as_amount(5) for item in [purple_mushroom, fiddlehead_fern, white_algae, hops, blackberry, dandelion]]
wild_medicine_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.wild_medicine, wild_medicine_items, 4, 3)
quality_foraging_items = sorted({item.as_quality(ForageQuality.gold).as_amount(3)
for item in
[*spring_foraging_items_thematic, *summer_foraging_items_thematic, *fall_foraging_items_thematic,
*winter_foraging_items_thematic, *beach_foraging_items, *desert_foraging_items, magma_cap] if item.can_have_quality})
quality_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.quality_foraging, quality_foraging_items, 4, 3)
green_rain_items = [moss.as_amount(200), fiber.as_amount(200), mossy_seed.as_amount(20), fiddlehead_fern.as_amount(10)]
green_rain_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.green_rain, green_rain_items, 4, 3)
crafts_room_bundles_vanilla = [spring_foraging_bundle_vanilla, summer_foraging_bundle_vanilla, fall_foraging_bundle_vanilla,
winter_foraging_bundle_vanilla, construction_bundle_vanilla, exotic_foraging_bundle_vanilla]
crafts_room_bundles_thematic = [spring_foraging_bundle_thematic, summer_foraging_bundle_thematic, fall_foraging_bundle_thematic,
winter_foraging_bundle_thematic, construction_bundle_thematic, exotic_foraging_bundle_thematic]
crafts_room_bundles_remixed = [*crafts_room_bundles_thematic, beach_foraging_bundle, mines_foraging_bundle, desert_foraging_bundle,
island_foraging_bundle, sticky_bundle, forest_bundle, wild_medicine_bundle, quality_foraging_bundle, green_rain_bundle]
crafts_room_vanilla = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_vanilla, 6)
crafts_room_thematic = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_thematic, 6)
crafts_room_remixed = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_remixed, 6)
# Pantry
spring_crops_items_vanilla = [parsnip, green_bean, cauliflower, potato]
spring_crops_items_thematic = [*spring_crops_items_vanilla, blue_jazz, coffee_bean, garlic, kale, rhubarb, strawberry, tulip, unmilled_rice, carrot]
spring_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.spring_crops, spring_crops_items_vanilla, 4, 4)
spring_crops_bundle_thematic = BundleTemplate.extend_from(spring_crops_bundle_vanilla, spring_crops_items_thematic)
summer_crops_items_vanilla = [tomato, hot_pepper, blueberry, melon]
summer_crops_items_thematic = [*summer_crops_items_vanilla, corn, hops, poppy, radish, red_cabbage, starfruit, summer_spangle, sunflower, wheat, summer_squash]
summer_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.summer_crops, summer_crops_items_vanilla, 4, 4)
summer_crops_bundle_thematic = BundleTemplate.extend_from(summer_crops_bundle_vanilla, summer_crops_items_thematic)
fall_crops_items_vanilla = [corn, eggplant, pumpkin, yam]
fall_crops_items_thematic = [*fall_crops_items_vanilla, amaranth, artichoke, beet, bok_choy, cranberries, fairy_rose, grape,
sunflower, wheat, sweet_gem_berry, broccoli]
fall_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.fall_crops, fall_crops_items_vanilla, 4, 4)
fall_crops_bundle_thematic = BundleTemplate.extend_from(fall_crops_bundle_vanilla, fall_crops_items_thematic)
all_crops_items = sorted({*spring_crops_items_thematic, *summer_crops_items_thematic, *fall_crops_items_thematic, powdermelon})
quality_crops_items_vanilla = [item.as_quality_crop() for item in [parsnip, melon, pumpkin, corn]]
quality_crops_items_thematic = [item.as_quality_crop() for item in all_crops_items]
quality_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.quality_crops, quality_crops_items_vanilla, 4, 3)
quality_crops_bundle_thematic = BundleTemplate.extend_from(quality_crops_bundle_vanilla, quality_crops_items_thematic)
animal_items_vanilla = [large_milk, large_brown_egg, large_egg, large_goat_milk, wool, duck_egg]
animal_items_thematic = [*animal_items_vanilla, egg, brown_egg, milk, goat_milk, truffle,
duck_feather, rabbit_foot, dinosaur_egg, void_egg, golden_egg, ostrich_egg]
animal_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.animal, animal_items_vanilla, 6, 5)
animal_bundle_thematic = BundleTemplate.extend_from(animal_bundle_vanilla, animal_items_thematic)
artisan_items_vanilla = [truffle_oil, cloth, goat_cheese, cheese, honey, jelly, apple, apricot, orange, peach, pomegranate, cherry]
artisan_items_thematic = [*artisan_items_vanilla, beer, juice, mead, pale_ale, wine, pickles, caviar, aged_roe, coffee, green_tea, banana, mango]
artisan_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.artisan, artisan_items_vanilla, 12, 6)
artisan_bundle_thematic = BundleTemplate.extend_from(artisan_bundle_vanilla, artisan_items_thematic)
rare_crops_items = [ancient_fruit, sweet_gem_berry]
rare_crops_bundle = BundleTemplate(CCRoom.pantry, BundleName.rare_crops, rare_crops_items, 2, 2)
# all_specific_roes = [BundleItem(AnimalProduct.roe, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fish]
fish_farmer_items = [roe.as_amount(15), aged_roe.as_amount(5), squid_ink, caviar.as_amount(5)]
fish_farmer_bundle = BundleTemplate(CCRoom.pantry, BundleName.fish_farmer, fish_farmer_items, 3, 2)
garden_items = [tulip, blue_jazz, summer_spangle, sunflower, fairy_rose, poppy, bouquet]
garden_bundle = BundleTemplate(CCRoom.pantry, BundleName.garden, garden_items, 5, 4)
brewer_items = [mead, pale_ale, wine, juice, green_tea, beer]
brewer_bundle = BundleTemplate(CCRoom.pantry, BundleName.brewer, brewer_items, 5, 4)
orchard_items = [apple, apricot, orange, peach, pomegranate, cherry, banana, mango]
orchard_bundle = BundleTemplate(CCRoom.pantry, BundleName.orchard, orchard_items, 6, 4)
island_crops_items = [pineapple, taro_root, banana, mango]
island_crops_bundle = IslandBundleTemplate(CCRoom.pantry, BundleName.island_crops, island_crops_items, 3, 3)
agronomist_items = [basic_fertilizer, quality_fertilizer, deluxe_fertilizer,
basic_retaining_soil, quality_retaining_soil, deluxe_retaining_soil,
speed_gro, deluxe_speed_gro, hyper_speed_gro, tree_fertilizer]
agronomist_bundle = BundleTemplate(CCRoom.pantry, BundleName.agronomist, agronomist_items, 4, 3)
slime_farmer_items = [slime.as_amount(99), petrified_slime.as_amount(10), blue_slime_egg, red_slime_egg,
purple_slime_egg, green_slime_egg, tiger_slime_egg]
slime_farmer_bundle = BundleTemplate(CCRoom.pantry, BundleName.slime_farmer, slime_farmer_items, 4, 3)
sommelier_items = [BundleItem(ArtisanGood.wine, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits]
sommelier_bundle = BundleTemplate(CCRoom.pantry, BundleName.sommelier, sommelier_items, 6, 3)
dry_items = [*[BundleItem(ArtisanGood.dried_fruit, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits],
*[BundleItem(ArtisanGood.dried_mushroom, flavor=mushroom, source=BundleItem.Sources.content) for mushroom in all_edible_mushrooms],
BundleItem(ArtisanGood.raisins, source=BundleItem.Sources.content)]
dry_bundle = BundleTemplate(CCRoom.pantry, BundleName.dry, dry_items, 6, 3)
pantry_bundles_vanilla = [spring_crops_bundle_vanilla, summer_crops_bundle_vanilla, fall_crops_bundle_vanilla,
quality_crops_bundle_vanilla, animal_bundle_vanilla, artisan_bundle_vanilla]
pantry_bundles_thematic = [spring_crops_bundle_thematic, summer_crops_bundle_thematic, fall_crops_bundle_thematic,
quality_crops_bundle_thematic, animal_bundle_thematic, artisan_bundle_thematic]
pantry_bundles_remixed = [*pantry_bundles_thematic, rare_crops_bundle, fish_farmer_bundle, garden_bundle,
brewer_bundle, orchard_bundle, island_crops_bundle, agronomist_bundle, slime_farmer_bundle, sommelier_bundle, dry_bundle]
pantry_vanilla = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_vanilla, 6)
pantry_thematic = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_thematic, 6)
pantry_remixed = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_remixed, 6)
# Fish Tank
river_fish_items_vanilla = [sunfish, catfish, shad, tiger_trout]
river_fish_items_thematic = [*river_fish_items_vanilla, chub, rainbow_trout, lingcod, walleye, perch, pike, bream, salmon, smallmouth_bass, dorado]
river_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.river_fish, river_fish_items_vanilla, 4, 4)
river_fish_bundle_thematic = BundleTemplate.extend_from(river_fish_bundle_vanilla, river_fish_items_thematic)
lake_fish_items_vanilla = [largemouth_bass, carp, bullhead, sturgeon]
lake_fish_items_thematic = [*lake_fish_items_vanilla, chub, rainbow_trout, lingcod, walleye, perch, midnight_carp]
lake_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.lake_fish, lake_fish_items_vanilla, 4, 4)
lake_fish_bundle_thematic = BundleTemplate.extend_from(lake_fish_bundle_vanilla, lake_fish_items_thematic)
ocean_fish_items_vanilla = [sardine, tuna, red_snapper, tilapia]
ocean_fish_items_thematic = [*ocean_fish_items_vanilla, pufferfish, super_cucumber, flounder, anchovy, red_mullet,
herring, eel, octopus, squid, sea_cucumber, albacore, halibut]
ocean_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.ocean_fish, ocean_fish_items_vanilla, 4, 4)
ocean_fish_bundle_thematic = BundleTemplate.extend_from(ocean_fish_bundle_vanilla, ocean_fish_items_thematic)
night_fish_items_vanilla = [walleye, bream, eel]
night_fish_items_thematic = [*night_fish_items_vanilla, super_cucumber, squid, midnight_carp, midnight_squid]
night_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.night_fish, night_fish_items_vanilla, 3, 3)
night_fish_bundle_thematic = BundleTemplate.extend_from(night_fish_bundle_vanilla, night_fish_items_thematic)
crab_pot_items_vanilla = [lobster, crayfish, crab, cockle, mussel, shrimp, snail, periwinkle, oyster, clam]
crab_pot_trash_items = [trash, driftwood, soggy_newspaper, broken_cd, broken_glasses]
crab_pot_items_thematic = [*crab_pot_items_vanilla, *crab_pot_trash_items]
crab_pot_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.crab_pot, crab_pot_items_vanilla, 10, 5)
crab_pot_bundle_thematic = BundleTemplate.extend_from(crab_pot_bundle_vanilla, crab_pot_items_thematic)
trash_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.trash, crab_pot_trash_items, 4, 4)
specialty_fish_items_vanilla = [pufferfish, ghostfish, sandfish, woodskip]
specialty_fish_items_thematic = [*specialty_fish_items_vanilla, scorpion_carp, eel, octopus, lava_eel, ice_pip,
stonefish, void_salmon, stingray, spookfish, midnight_squid]
specialty_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.specialty_fish, specialty_fish_items_vanilla, 4, 4)
specialty_fish_bundle_thematic = BundleTemplate.extend_from(specialty_fish_bundle_vanilla, specialty_fish_items_thematic)
spring_fish_items = [herring, halibut, shad, flounder, sunfish, sardine, catfish, anchovy, smallmouth_bass, eel, legend]
spring_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.spring_fish, spring_fish_items, 4, 4)
summer_fish_items = [tuna, pike, red_mullet, sturgeon, red_snapper, super_cucumber, tilapia, pufferfish, rainbow_trout,
octopus, dorado, halibut, shad, flounder, sunfish, crimsonfish]
summer_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.summer_fish, summer_fish_items, 4, 4)
fall_fish_items = [red_snapper, super_cucumber, tilapia, shad, sardine, catfish, anchovy, smallmouth_bass, eel, midnight_carp,
walleye, sea_cucumber, tiger_trout, albacore, salmon, angler]
fall_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.fall_fish, fall_fish_items, 4, 4)
winter_fish_items = [perch, squid, lingcod, tuna, pike, red_mullet, sturgeon, red_snapper, herring, halibut, sardine,
midnight_carp, sea_cucumber, tiger_trout, albacore, glacierfish]
winter_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.winter_fish, winter_fish_items, 4, 4)
rain_fish_items = [red_snapper, shad, catfish, eel, walleye]
rain_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.rain_fish, rain_fish_items, 3, 3)
quality_fish_items = sorted({
item.as_quality(FishQuality.gold).as_amount(2)
for item in [*river_fish_items_thematic, *lake_fish_items_thematic, *ocean_fish_items_thematic]
})
quality_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.quality_fish, quality_fish_items, 4, 3)
master_fisher_items = [lava_eel, scorpion_carp, octopus, blobfish, lingcod, ice_pip, super_cucumber, stingray, void_salmon, pufferfish]
master_fisher_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.master_fisher, master_fisher_items, 4, 2)
legendary_fish_items = [angler, legend, mutant_carp, crimsonfish, glacierfish]
legendary_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.legendary_fish, legendary_fish_items, 4, 2)
island_fish_items = [lionfish, blue_discus, stingray]
island_fish_bundle = IslandBundleTemplate(CCRoom.fish_tank, BundleName.island_fish, island_fish_items, 3, 3)
tackle_items = [spinner, dressed_spinner, trap_bobber, sonar_bobber, cork_bobber, lead_bobber, treasure_hunter, barbed_hook, curiosity_lure, quality_bobber]
tackle_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.tackle, tackle_items, 3, 2)
bait_items = [bait, magnet, wild_bait, magic_bait, challenge_bait, deluxe_bait, targeted_bait]
bait_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.bait, bait_items, 3, 2)
# This bundle could change based on content packs, once the fish are properly in it. Until then, I'm not sure how, so pelican town only
specific_bait_items = [BundleItem(ArtisanGood.targeted_bait, flavor=fish.name).as_amount(10) for fish in content_packs.pelican_town.fishes]
specific_bait_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.specific_bait, specific_bait_items, 6, 3)
deep_fishing_items = [blobfish, spook_fish, midnight_squid, sea_cucumber, super_cucumber, octopus, pearl, seaweed]
deep_fishing_bundle = FestivalBundleTemplate(CCRoom.fish_tank, BundleName.deep_fishing, deep_fishing_items, 4, 3)
smokeable_fish = [Fish.largemouth_bass, Fish.bream, Fish.bullhead, Fish.chub, Fish.ghostfish, Fish.flounder, Fish.shad, Fish.rainbow_trout, Fish.tilapia,
Fish.red_mullet, Fish.tuna, Fish.midnight_carp, Fish.salmon, Fish.perch]
fish_smoker_items = [BundleItem(ArtisanGood.smoked_fish, flavor=fish) for fish in smokeable_fish]
fish_smoker_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.fish_smoker, fish_smoker_items, 6, 3)
fish_tank_bundles_vanilla = [river_fish_bundle_vanilla, lake_fish_bundle_vanilla, ocean_fish_bundle_vanilla,
night_fish_bundle_vanilla, crab_pot_bundle_vanilla, specialty_fish_bundle_vanilla]
fish_tank_bundles_thematic = [river_fish_bundle_thematic, lake_fish_bundle_thematic, ocean_fish_bundle_thematic,
night_fish_bundle_thematic, crab_pot_bundle_thematic, specialty_fish_bundle_thematic]
fish_tank_bundles_remixed = [*fish_tank_bundles_thematic, spring_fish_bundle, summer_fish_bundle, fall_fish_bundle, winter_fish_bundle, trash_bundle,
rain_fish_bundle, quality_fish_bundle, master_fisher_bundle, legendary_fish_bundle, tackle_bundle, bait_bundle,
specific_bait_bundle, deep_fishing_bundle, fish_smoker_bundle]
# In Remixed, the trash items are in the recycling bundle, so we don't use the thematic version of the crab pot bundle that added trash items to it
fish_tank_bundles_remixed.remove(crab_pot_bundle_thematic)
fish_tank_bundles_remixed.append(crab_pot_bundle_vanilla)
fish_tank_vanilla = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_vanilla, 6)
fish_tank_thematic = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_thematic, 6)
fish_tank_remixed = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_remixed, 6)
# Boiler Room
blacksmith_items_vanilla = [copper_bar, iron_Bar, gold_bar]
blacksmith_items_thematic = [*blacksmith_items_vanilla, iridium_bar, refined_quartz.as_amount(3), wilted_bouquet]
blacksmith_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.blacksmith, blacksmith_items_vanilla, 3, 3)
blacksmith_bundle_thematic = BundleTemplate.extend_from(blacksmith_bundle_vanilla, blacksmith_items_thematic)
geologist_items_vanilla = [quartz, earth_crystal, frozen_tear, fire_quartz]
geologist_items_thematic = [*geologist_items_vanilla, emerald, aquamarine, ruby, amethyst, topaz, jade, diamond]
geologist_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.geologist, geologist_items_vanilla, 4, 4)
geologist_bundle_thematic = BundleTemplate.extend_from(geologist_bundle_vanilla, geologist_items_thematic)
adventurer_items_vanilla = [slime, bat_wing, solar_essence, void_essence]
adventurer_items_thematic = [*adventurer_items_vanilla, bug_meat, coal, bone_fragment.as_amount(10)]
adventurer_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.adventurer, adventurer_items_vanilla, 4, 2)
adventurer_bundle_thematic = BundleTemplate.extend_from(adventurer_bundle_vanilla, adventurer_items_thematic)
# Where to put radioactive bar?
treasure_hunter_items = [emerald, aquamarine, ruby, amethyst, topaz, jade, diamond]
treasure_hunter_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.treasure_hunter, treasure_hunter_items, 6, 5)
engineer_items = [iridium_ore.as_amount(5), battery_pack, refined_quartz.as_amount(5), diamond]
engineer_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.engineer, engineer_items, 3, 3)
demolition_items = [cherry_bomb, bomb, mega_bomb, explosive_ammo]
demolition_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.demolition, demolition_items, 3, 3)
recycling_items = [stone, coal, iron_ore, wood, cloth, refined_quartz]
recycling_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.recycling, recycling_items, 4, 4)
archaeologist_items = [golden_mask, golden_relic, ancient_drum, dwarf_gadget, dwarvish_helm, prehistoric_handaxe, bone_flute, anchor, prehistoric_tool,
chicken_statue, rusty_cog, rusty_spur, rusty_spoon, ancient_sword, ornamental_fan, elvish_jewelry, ancient_doll, chipped_amphora]
archaeologist_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.archaeologist, archaeologist_items, 6, 3)
paleontologist_items = [prehistoric_scapula, prehistoric_tibia, prehistoric_skull, skeletal_hand, prehistoric_rib, prehistoric_vertebra, skeletal_tail,
nautilus_fossil, amphibian_fossil, palm_fossil, trilobite]
paleontologist_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.paleontologist, paleontologist_items, 6, 3)
boiler_room_bundles_vanilla = [blacksmith_bundle_vanilla, geologist_bundle_vanilla, adventurer_bundle_vanilla]
boiler_room_bundles_thematic = [blacksmith_bundle_thematic, geologist_bundle_thematic, adventurer_bundle_thematic]
boiler_room_bundles_remixed = [*boiler_room_bundles_thematic, treasure_hunter_bundle, engineer_bundle,
demolition_bundle, recycling_bundle, archaeologist_bundle, paleontologist_bundle]
boiler_room_vanilla = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_vanilla, 3)
boiler_room_thematic = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_thematic, 3)
boiler_room_remixed = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_remixed, 3)
# Bulletin Board
chef_items_vanilla = [maple_syrup, fiddlehead_fern, truffle, poppy, maki_roll, fried_egg]
# More recipes?
chef_items_thematic = [maki_roll, fried_egg, omelet, pizza, hashbrowns, pancakes, bread, tortilla,
farmer_s_lunch, survival_burger, dish_o_the_sea, miner_s_treat, roots_platter, salad,
cheese_cauliflower, parsnip_soup, fried_mushroom, salmon_dinner, pepper_poppers, spaghetti,
sashimi, blueberry_tart, algae_soup, pale_broth, chowder]
chef_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.chef, chef_items_vanilla, 6, 6)
chef_bundle_thematic = BundleTemplate.extend_from(chef_bundle_vanilla, chef_items_thematic)
dye_items_vanilla = [red_mushroom, sea_urchin, sunflower, duck_feather, aquamarine, red_cabbage]
dye_red_items = [cranberries, hot_pepper, radish, rhubarb, spaghetti, strawberry, tomato, tulip, red_mushroom]
dye_orange_items = [poppy, pumpkin, apricot, orange, spice_berry, winter_root]
dye_yellow_items = [corn, parsnip, summer_spangle, sunflower, starfruit]
dye_green_items = [fiddlehead_fern, kale, artichoke, bok_choy, green_bean, cactus_fruit, duck_feather, dinosaur_egg]
dye_blue_items = [blueberry, blue_jazz, blackberry, crystal_fruit, aquamarine]
dye_purple_items = [beet, crocus, eggplant, red_cabbage, sweet_pea, iridium_bar, sea_urchin, amaranth]
dye_items_thematic = [dye_red_items, dye_orange_items, dye_yellow_items, dye_green_items, dye_blue_items, dye_purple_items]
dye_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.dye, dye_items_vanilla, 6, 6)
dye_bundle_thematic = DeepBundleTemplate(CCRoom.bulletin_board, BundleName.dye, dye_items_thematic, 6, 6)
field_research_items_vanilla = [purple_mushroom, nautilus_shell, chub, frozen_geode]
field_research_items_thematic = [*field_research_items_vanilla, geode, magma_geode, omni_geode,
rainbow_shell, amethyst, bream, carp]
field_research_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.field_research, field_research_items_vanilla, 4, 4)
field_research_bundle_thematic = BundleTemplate.extend_from(field_research_bundle_vanilla, field_research_items_thematic)
fodder_items_vanilla = [wheat.as_amount(10), hay.as_amount(10), apple.as_amount(3)]
fodder_items_thematic = [*fodder_items_vanilla, kale.as_amount(3), corn.as_amount(3), green_bean.as_amount(3),
potato.as_amount(3), green_algae.as_amount(5), white_algae.as_amount(3)]
fodder_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.fodder, fodder_items_vanilla, 3, 3)
fodder_bundle_thematic = BundleTemplate.extend_from(fodder_bundle_vanilla, fodder_items_thematic)
enchanter_items_vanilla = [oak_resin, wine, rabbit_foot, pomegranate]
enchanter_items_thematic = [*enchanter_items_vanilla, purple_mushroom, solar_essence,
super_cucumber, void_essence, fire_quartz, frozen_tear, jade]
enchanter_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.enchanter, enchanter_items_vanilla, 4, 4)
enchanter_bundle_thematic = BundleTemplate.extend_from(enchanter_bundle_vanilla, enchanter_items_thematic)
children_items = [salmonberry.as_amount(10), cookie, ancient_doll, ice_cream, cranberry_candy, ginger_ale,
grape.as_amount(10), pink_cake, snail, fairy_rose, plum_pudding]
children_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.children, children_items, 4, 3)
forager_items = [salmonberry.as_amount(50), blackberry.as_amount(50), wild_plum.as_amount(20), snow_yam.as_amount(20),
common_mushroom.as_amount(20), grape.as_amount(20), spring_onion.as_amount(20)]
forager_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.forager, forager_items, 3, 2)
home_cook_items = [egg.as_amount(10), milk.as_amount(10), wheat_flour.as_amount(100), sugar.as_amount(100), vinegar.as_amount(100),
chocolate_cake, pancakes, rhubarb_pie]
home_cook_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.home_cook, home_cook_items, 3, 3)
helper_items = [prize_ticket, mystery_box.as_amount(5), gold_mystery_box]
helper_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.helper, helper_items, 2, 2)
spirit_eve_items = [jack_o_lantern, corn.as_amount(10), bat_wing.as_amount(10)]
spirit_eve_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.spirit_eve, spirit_eve_items, 3, 3)
winter_star_items = [holly.as_amount(5), plum_pudding, stuffing, powdermelon.as_amount(5)]
winter_star_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.winter_star, winter_star_items, 2, 2)
bartender_items = [shrimp_cocktail, triple_shot_espresso, ginger_ale, cranberry_candy, beer, pale_ale, pina_colada]
bartender_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.bartender, bartender_items, 3, 3)
calico_items = [calico_egg.as_amount(200), calico_egg.as_amount(200), calico_egg.as_amount(200), calico_egg.as_amount(200),
magic_rock_candy, mega_bomb.as_amount(10), mystery_box.as_amount(10), mixed_seeds.as_amount(50),
strawberry_seeds.as_amount(20),
spicy_eel.as_amount(5), crab_cakes.as_amount(5), eggplant_parmesan.as_amount(5),
pumpkin_soup.as_amount(5), lucky_lunch.as_amount(5) ]
calico_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.calico, calico_items, 2, 2)
raccoon_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.raccoon, raccoon_foraging_items, 4, 4)
bulletin_board_bundles_vanilla = [chef_bundle_vanilla, dye_bundle_vanilla, field_research_bundle_vanilla, fodder_bundle_vanilla, enchanter_bundle_vanilla]
bulletin_board_bundles_thematic = [chef_bundle_thematic, dye_bundle_thematic, field_research_bundle_thematic, fodder_bundle_thematic, enchanter_bundle_thematic]
bulletin_board_bundles_remixed = [*bulletin_board_bundles_thematic, children_bundle, forager_bundle, home_cook_bundle,
helper_bundle, spirit_eve_bundle, winter_star_bundle, bartender_bundle, calico_bundle, raccoon_bundle]
bulletin_board_vanilla = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_vanilla, 5)
bulletin_board_thematic = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_thematic, 5)
bulletin_board_remixed = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_remixed, 5)
missing_bundle_items_vanilla = [wine.as_quality(ArtisanQuality.silver), dinosaur_mayo, prismatic_shard, caviar,
ancient_fruit.as_quality_crop(), void_salmon.as_quality(FishQuality.gold)]
missing_bundle_items_thematic = [*missing_bundle_items_vanilla, pale_ale.as_quality(ArtisanQuality.silver), beer.as_quality(ArtisanQuality.silver),
mead.as_quality(ArtisanQuality.silver),
cheese.as_quality(ArtisanQuality.silver), goat_cheese.as_quality(ArtisanQuality.silver), void_mayo, cloth, green_tea,
truffle_oil, diamond,
sweet_gem_berry.as_quality_crop(), starfruit.as_quality_crop(),
tea_leaves.as_amount(5), lava_eel.as_quality(FishQuality.gold), scorpion_carp.as_quality(FishQuality.gold),
blobfish.as_quality(FishQuality.gold)]
missing_bundle_vanilla = BundleTemplate(CCRoom.abandoned_joja_mart, BundleName.missing_bundle, missing_bundle_items_vanilla, 6, 5)
missing_bundle_thematic = BundleTemplate.extend_from(missing_bundle_vanilla, missing_bundle_items_thematic)
abandoned_joja_mart_bundles_vanilla = [missing_bundle_vanilla]
abandoned_joja_mart_bundles_thematic = [missing_bundle_thematic]
abandoned_joja_mart_vanilla = BundleRoomTemplate(CCRoom.abandoned_joja_mart, abandoned_joja_mart_bundles_vanilla, 1)
abandoned_joja_mart_thematic = BundleRoomTemplate(CCRoom.abandoned_joja_mart, abandoned_joja_mart_bundles_thematic, 1)
abandoned_joja_mart_remixed = abandoned_joja_mart_thematic
vault_2500_gold = BundleItem.money_bundle(2500)
vault_5000_gold = BundleItem.money_bundle(5000)
vault_10000_gold = BundleItem.money_bundle(10000)
vault_25000_gold = BundleItem.money_bundle(25000)
vault_2500_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_2500, vault_2500_gold)
vault_5000_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_5000, vault_5000_gold)
vault_10000_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_10000, vault_10000_gold)
vault_25000_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_25000, vault_25000_gold)
vault_gambler_items = BundleItem(Currency.qi_coin, 10000)
vault_gambler_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.gambler, vault_gambler_items)
vault_carnival_items = BundleItem(Currency.star_token, 2500, source=BundleItem.Sources.festival)
vault_carnival_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.carnival, vault_carnival_items)
vault_walnut_hunter_items = BundleItem(Currency.golden_walnut, 25)
vault_walnut_hunter_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.walnut_hunter, vault_walnut_hunter_items)
vault_qi_helper_items = BundleItem(Currency.qi_gem, 25, source=BundleItem.Sources.island)
vault_qi_helper_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.qi_helper, vault_qi_helper_items)
vault_bundles_vanilla = [vault_2500_bundle, vault_5000_bundle, vault_10000_bundle, vault_25000_bundle]
vault_bundles_thematic = vault_bundles_vanilla
vault_bundles_remixed = [*vault_bundles_vanilla, vault_gambler_bundle, vault_qi_helper_bundle, vault_carnival_bundle] # , vault_walnut_hunter_bundle
vault_vanilla = BundleRoomTemplate(CCRoom.vault, vault_bundles_vanilla, 4)
vault_thematic = BundleRoomTemplate(CCRoom.vault, vault_bundles_thematic, 4)
vault_remixed = BundleRoomTemplate(CCRoom.vault, vault_bundles_remixed, 4)
all_cc_remixed_bundles = [*crafts_room_bundles_remixed, *pantry_bundles_remixed, *fish_tank_bundles_remixed,
*boiler_room_bundles_remixed, *bulletin_board_bundles_remixed]
community_center_remixed_anywhere = BundleRoomTemplate("Community Center", all_cc_remixed_bundles, 26)
all_bundle_items_except_money = []
all_remixed_bundles = [*crafts_room_bundles_remixed, *pantry_bundles_remixed, *fish_tank_bundles_remixed,
*boiler_room_bundles_remixed, *bulletin_board_bundles_remixed, missing_bundle_thematic,
*raccoon_bundles_remixed]
for bundle in all_remixed_bundles:
all_bundle_items_except_money.extend(bundle.items)
all_bundle_items_by_name = {item.item_name: item for item in all_bundle_items_except_money}

View File

@@ -0,0 +1,10 @@
from .remixed_bundles import *
all_bundle_items_except_money = []
all_remixed_bundles = [*crafts_room_bundles_remixed, *pantry_bundles_remixed, *fish_tank_bundles_remixed,
*boiler_room_bundles_remixed, *bulletin_board_bundles_remixed, missing_bundle_thematic,
*giant_stump_bundles_remixed]
for bundle in all_remixed_bundles:
all_bundle_items_except_money.extend(bundle.items)
all_bundle_items_by_name = {item.item_name: item for item in all_bundle_items_except_money}

View File

@@ -0,0 +1,599 @@
from ..hats_data import Hats
from ..shirt_data import Shirts
from ...bundles.bundle_item import BundleItem
from ...strings.animal_product_names import AnimalProduct
from ...strings.artisan_good_names import ArtisanGood
from ...strings.book_names import Book
from ...strings.boot_names import Boots
from ...strings.catalogue_names import CatalogueItem
from ...strings.craftable_names import Consumable, Lighting, Fishing, Craftable, Bomb, Furniture, Floor, Edible, Statue
from ...strings.crop_names import Vegetable, Fruit
from ...strings.currency_names import Currency
from ...strings.decoration_names import Decoration
from ...strings.fertilizer_names import Fertilizer, SpeedGro, RetainingSoil
from ...strings.fish_names import Fish, WaterItem, Trash
from ...strings.flower_names import Flower
from ...strings.food_names import Meal, Beverage
from ...strings.forageable_names import Forageable, Mushroom
from ...strings.fruit_tree_names import Sapling
from ...strings.geode_names import Geode
from ...strings.gift_names import Gift
from ...strings.ingredient_names import Ingredient
from ...strings.machine_names import Machine
from ...strings.material_names import Material
from ...strings.meme_item_names import MemeItem
from ...strings.metal_names import Fossil, Ore, MetalBar, Mineral, Artifact
from ...strings.monster_drop_names import Loot
from ...strings.seed_names import TreeSeed, Seed
from ...strings.special_item_names import SpecialItem, NotReallyAnItem
wild_horseradish = BundleItem(Forageable.wild_horseradish)
daffodil = BundleItem(Forageable.daffodil)
leek = BundleItem(Forageable.leek)
dandelion = BundleItem(Forageable.dandelion)
morel = BundleItem(Mushroom.morel)
common_mushroom = BundleItem(Mushroom.common)
salmonberry = BundleItem(Forageable.salmonberry, can_have_quality=False)
spring_onion = BundleItem(Forageable.spring_onion)
grape = BundleItem(Fruit.grape)
spice_berry = BundleItem(Forageable.spice_berry)
sweet_pea = BundleItem(Forageable.sweet_pea)
red_mushroom = BundleItem(Mushroom.red)
fiddlehead_fern = BundleItem(Forageable.fiddlehead_fern)
wild_plum = BundleItem(Forageable.wild_plum)
hazelnut = BundleItem(Forageable.hazelnut)
blackberry = BundleItem(Forageable.blackberry)
chanterelle = BundleItem(Mushroom.chanterelle)
winter_root = BundleItem(Forageable.winter_root)
crystal_fruit = BundleItem(Forageable.crystal_fruit)
snow_yam = BundleItem(Forageable.snow_yam)
crocus = BundleItem(Forageable.crocus)
holly = BundleItem(Forageable.holly)
coconut = BundleItem(Forageable.coconut)
golden_coconut = BundleItem(Geode.golden_coconut, source=BundleItem.Sources.island)
cactus_fruit = BundleItem(Forageable.cactus_fruit)
cave_carrot = BundleItem(Forageable.cave_carrot)
purple_mushroom = BundleItem(Mushroom.purple)
maple_syrup = BundleItem(ArtisanGood.maple_syrup)
oak_resin = BundleItem(ArtisanGood.oak_resin)
pine_tar = BundleItem(ArtisanGood.pine_tar)
nautilus_shell = BundleItem(WaterItem.nautilus_shell)
coral = BundleItem(WaterItem.coral)
sea_urchin = BundleItem(WaterItem.sea_urchin)
rainbow_shell = BundleItem(Forageable.rainbow_shell)
clam = BundleItem(Fish.clam)
cockle = BundleItem(Fish.cockle)
mussel = BundleItem(Fish.mussel)
oyster = BundleItem(Fish.oyster)
seaweed = BundleItem(WaterItem.seaweed, can_have_quality=False)
wood = BundleItem(Material.wood, 99)
stone = BundleItem(Material.stone, 99)
hardwood = BundleItem(Material.hardwood, 10)
clay = BundleItem(Material.clay)
fiber = BundleItem(Material.fiber)
moss = BundleItem(Material.moss)
mixed_seeds = BundleItem(Seed.mixed)
acorn = BundleItem(TreeSeed.acorn)
maple_seed = BundleItem(TreeSeed.maple)
pine_cone = BundleItem(TreeSeed.pine)
mahogany_seed = BundleItem(TreeSeed.mahogany)
mushroom_tree_seed = BundleItem(TreeSeed.mushroom, source=BundleItem.Sources.island)
mystic_tree_seed = BundleItem(TreeSeed.mystic, source=BundleItem.Sources.masteries)
mossy_seed = BundleItem(TreeSeed.mossy)
strawberry_seeds = BundleItem(Seed.strawberry)
sunflower_seeds = BundleItem(Seed.sunflower)
blue_jazz = BundleItem(Flower.blue_jazz)
cauliflower = BundleItem(Vegetable.cauliflower)
green_bean = BundleItem(Vegetable.green_bean)
kale = BundleItem(Vegetable.kale)
parsnip = BundleItem(Vegetable.parsnip)
potato = BundleItem(Vegetable.potato)
strawberry = BundleItem(Fruit.strawberry, source=BundleItem.Sources.festival)
tulip = BundleItem(Flower.tulip)
unmilled_rice = BundleItem(Vegetable.unmilled_rice)
coffee_bean = BundleItem(Seed.coffee)
garlic = BundleItem(Vegetable.garlic)
blueberry = BundleItem(Fruit.blueberry)
corn = BundleItem(Vegetable.corn)
hops = BundleItem(Vegetable.hops)
hot_pepper = BundleItem(Fruit.hot_pepper)
melon = BundleItem(Fruit.melon)
poppy = BundleItem(Flower.poppy)
radish = BundleItem(Vegetable.radish)
summer_spangle = BundleItem(Flower.summer_spangle)
sunflower = BundleItem(Flower.sunflower)
tomato = BundleItem(Vegetable.tomato)
wheat = BundleItem(Vegetable.wheat)
hay = BundleItem(Forageable.hay)
amaranth = BundleItem(Vegetable.amaranth)
bok_choy = BundleItem(Vegetable.bok_choy)
cranberries = BundleItem(Fruit.cranberries)
eggplant = BundleItem(Vegetable.eggplant)
fairy_rose = BundleItem(Flower.fairy_rose)
pumpkin = BundleItem(Vegetable.pumpkin)
yam = BundleItem(Vegetable.yam)
sweet_gem_berry = BundleItem(Fruit.sweet_gem_berry)
rhubarb = BundleItem(Fruit.rhubarb)
beet = BundleItem(Vegetable.beet)
red_cabbage = BundleItem(Vegetable.red_cabbage)
starfruit = BundleItem(Fruit.starfruit)
artichoke = BundleItem(Vegetable.artichoke)
pineapple = BundleItem(Fruit.pineapple, source=BundleItem.Sources.content)
taro_root = BundleItem(Vegetable.taro_root, source=BundleItem.Sources.content)
dragon_tooth = BundleItem(Forageable.dragon_tooth, source=BundleItem.Sources.content)
carrot = BundleItem(Vegetable.carrot)
summer_squash = BundleItem(Vegetable.summer_squash)
broccoli = BundleItem(Vegetable.broccoli)
powdermelon = BundleItem(Fruit.powdermelon)
egg = BundleItem(AnimalProduct.egg)
large_egg = BundleItem(AnimalProduct.large_egg)
brown_egg = BundleItem(AnimalProduct.brown_egg)
large_brown_egg = BundleItem(AnimalProduct.large_brown_egg)
wool = BundleItem(AnimalProduct.wool)
milk = BundleItem(AnimalProduct.milk)
large_milk = BundleItem(AnimalProduct.large_milk)
goat_milk = BundleItem(AnimalProduct.goat_milk)
large_goat_milk = BundleItem(AnimalProduct.large_goat_milk)
truffle = BundleItem(AnimalProduct.truffle)
duck_feather = BundleItem(AnimalProduct.duck_feather)
duck_egg = BundleItem(AnimalProduct.duck_egg)
rabbit_foot = BundleItem(AnimalProduct.rabbit_foot)
dinosaur_egg = BundleItem(AnimalProduct.dinosaur_egg)
void_egg = BundleItem(AnimalProduct.void_egg)
ostrich_egg = BundleItem(AnimalProduct.ostrich_egg, source=BundleItem.Sources.content)
golden_egg = BundleItem(AnimalProduct.golden_egg)
truffle_oil = BundleItem(ArtisanGood.truffle_oil)
cloth = BundleItem(ArtisanGood.cloth)
goat_cheese = BundleItem(ArtisanGood.goat_cheese)
cheese = BundleItem(ArtisanGood.cheese)
honey = BundleItem(ArtisanGood.honey)
beer = BundleItem(Beverage.beer)
mayonnaise = BundleItem(ArtisanGood.mayonnaise)
juice = BundleItem(ArtisanGood.juice)
mead = BundleItem(ArtisanGood.mead)
pale_ale = BundleItem(ArtisanGood.pale_ale)
wine = BundleItem(ArtisanGood.wine)
jelly = BundleItem(ArtisanGood.jelly)
pickles = BundleItem(ArtisanGood.pickles)
caviar = BundleItem(ArtisanGood.caviar)
aged_roe = BundleItem(ArtisanGood.aged_roe)
roe = BundleItem(AnimalProduct.roe)
squid_ink = BundleItem(AnimalProduct.squid_ink)
coffee = BundleItem(Beverage.coffee)
green_tea = BundleItem(ArtisanGood.green_tea)
apple = BundleItem(Fruit.apple)
apricot = BundleItem(Fruit.apricot)
orange = BundleItem(Fruit.orange)
peach = BundleItem(Fruit.peach)
pomegranate = BundleItem(Fruit.pomegranate)
cherry = BundleItem(Fruit.cherry)
banana = BundleItem(Fruit.banana, source=BundleItem.Sources.content)
mango = BundleItem(Fruit.mango, source=BundleItem.Sources.content)
basic_fertilizer = BundleItem(Fertilizer.basic, 100)
quality_fertilizer = BundleItem(Fertilizer.quality, 20)
deluxe_fertilizer = BundleItem(Fertilizer.deluxe, 5, source=BundleItem.Sources.island)
basic_retaining_soil = BundleItem(RetainingSoil.basic, 80)
quality_retaining_soil = BundleItem(RetainingSoil.quality, 50)
deluxe_retaining_soil = BundleItem(RetainingSoil.deluxe, 20, source=BundleItem.Sources.island)
speed_gro = BundleItem(SpeedGro.basic, 40)
deluxe_speed_gro = BundleItem(SpeedGro.deluxe, 20)
hyper_speed_gro = BundleItem(SpeedGro.hyper, 5, source=BundleItem.Sources.qi_board)
tree_fertilizer = BundleItem(Fertilizer.tree, 20)
lobster = BundleItem(Fish.lobster)
crab = BundleItem(Fish.crab)
shrimp = BundleItem(Fish.shrimp)
crayfish = BundleItem(Fish.crayfish)
snail = BundleItem(Fish.snail)
periwinkle = BundleItem(Fish.periwinkle)
trash = BundleItem(Trash.trash)
driftwood = BundleItem(Trash.driftwood)
soggy_newspaper = BundleItem(Trash.soggy_newspaper)
broken_cd = BundleItem(Trash.broken_cd)
broken_glasses = BundleItem(Trash.broken_glasses)
chub = BundleItem(Fish.chub)
catfish = BundleItem(Fish.catfish)
rainbow_trout = BundleItem(Fish.rainbow_trout)
lingcod = BundleItem(Fish.lingcod)
walleye = BundleItem(Fish.walleye)
perch = BundleItem(Fish.perch)
pike = BundleItem(Fish.pike)
bream = BundleItem(Fish.bream)
salmon = BundleItem(Fish.salmon)
sunfish = BundleItem(Fish.sunfish)
tiger_trout = BundleItem(Fish.tiger_trout)
shad = BundleItem(Fish.shad)
smallmouth_bass = BundleItem(Fish.smallmouth_bass)
dorado = BundleItem(Fish.dorado)
carp = BundleItem(Fish.carp)
midnight_carp = BundleItem(Fish.midnight_carp)
largemouth_bass = BundleItem(Fish.largemouth_bass)
sturgeon = BundleItem(Fish.sturgeon)
bullhead = BundleItem(Fish.bullhead)
tilapia = BundleItem(Fish.tilapia)
pufferfish = BundleItem(Fish.pufferfish)
tuna = BundleItem(Fish.tuna)
super_cucumber = BundleItem(Fish.super_cucumber)
flounder = BundleItem(Fish.flounder)
anchovy = BundleItem(Fish.anchovy)
sardine = BundleItem(Fish.sardine)
red_mullet = BundleItem(Fish.red_mullet)
herring = BundleItem(Fish.herring)
eel = BundleItem(Fish.eel)
octopus = BundleItem(Fish.octopus)
red_snapper = BundleItem(Fish.red_snapper)
squid = BundleItem(Fish.squid)
sea_cucumber = BundleItem(Fish.sea_cucumber)
albacore = BundleItem(Fish.albacore)
halibut = BundleItem(Fish.halibut)
scorpion_carp = BundleItem(Fish.scorpion_carp)
sandfish = BundleItem(Fish.sandfish)
woodskip = BundleItem(Fish.woodskip)
lava_eel = BundleItem(Fish.lava_eel)
ice_pip = BundleItem(Fish.ice_pip)
stonefish = BundleItem(Fish.stonefish)
ghostfish = BundleItem(Fish.ghostfish)
bouquet = BundleItem(Gift.bouquet)
wilted_bouquet = BundleItem(Gift.wilted_bouquet)
copper_bar = BundleItem(MetalBar.copper)
iron_bar = BundleItem(MetalBar.iron)
gold_bar = BundleItem(MetalBar.gold)
iridium_bar = BundleItem(MetalBar.iridium)
radioactive_bar = BundleItem(MetalBar.radioactive, source=BundleItem.Sources.island)
refined_quartz = BundleItem(MetalBar.quartz)
coal = BundleItem(Material.coal)
iridium_ore = BundleItem(Ore.iridium)
gold_ore = BundleItem(Ore.gold)
iron_ore = BundleItem(Ore.iron)
copper_ore = BundleItem(Ore.copper)
radioactive_ore = BundleItem(Ore.radioactive, source=BundleItem.Sources.qi_board)
battery_pack = BundleItem(ArtisanGood.battery_pack)
quartz = BundleItem(Mineral.quartz)
fire_quartz = BundleItem(Mineral.fire_quartz)
frozen_tear = BundleItem(Mineral.frozen_tear)
earth_crystal = BundleItem(Mineral.earth_crystal)
emerald = BundleItem(Mineral.emerald)
aquamarine = BundleItem(Mineral.aquamarine)
ruby = BundleItem(Mineral.ruby)
amethyst = BundleItem(Mineral.amethyst)
topaz = BundleItem(Mineral.topaz)
jade = BundleItem(Mineral.jade)
obsidian = BundleItem(Mineral.obsidian)
jamborite = BundleItem(Mineral.jamborite)
tigerseye = BundleItem(Mineral.tigerseye)
opal = BundleItem(Mineral.opal)
thunder_egg = BundleItem(Mineral.thunder_egg)
ghost_crystal = BundleItem(Mineral.ghost_crystal)
kyanite = BundleItem(Mineral.kyanite)
lemon_stone = BundleItem(Mineral.lemon_stone)
mudstone = BundleItem(Mineral.mudstone)
limestone = BundleItem(Mineral.limestone)
slime = BundleItem(Loot.slime, 99)
bug_meat = BundleItem(Loot.bug_meat, 10)
bat_wing = BundleItem(Loot.bat_wing, 10)
solar_essence = BundleItem(Loot.solar_essence)
void_essence = BundleItem(Loot.void_essence)
petrified_slime = BundleItem(Mineral.petrified_slime)
blue_slime_egg = BundleItem(AnimalProduct.slime_egg_blue)
red_slime_egg = BundleItem(AnimalProduct.slime_egg_red)
purple_slime_egg = BundleItem(AnimalProduct.slime_egg_purple)
green_slime_egg = BundleItem(AnimalProduct.slime_egg_green)
tiger_slime_egg = BundleItem(AnimalProduct.slime_egg_tiger, source=BundleItem.Sources.island)
cherry_bomb = BundleItem(Bomb.cherry_bomb, 5)
bomb = BundleItem(Bomb.bomb, 2)
mega_bomb = BundleItem(Bomb.mega_bomb)
explosive_ammo = BundleItem(Craftable.explosive_ammo, 5)
maki_roll = BundleItem(Meal.maki_roll)
fried_egg = BundleItem(Meal.fried_egg)
omelet = BundleItem(Meal.omelet)
pizza = BundleItem(Meal.pizza)
hashbrowns = BundleItem(Meal.hashbrowns)
pancakes = BundleItem(Meal.pancakes)
bread = BundleItem(Meal.bread)
tortilla = BundleItem(Meal.tortilla)
triple_shot_espresso = BundleItem(Beverage.triple_shot_espresso)
farmer_s_lunch = BundleItem(Meal.farmer_lunch)
survival_burger = BundleItem(Meal.survival_burger)
dish_o_the_sea = BundleItem(Meal.dish_o_the_sea)
miner_s_treat = BundleItem(Meal.miners_treat)
roots_platter = BundleItem(Meal.roots_platter)
salad = BundleItem(Meal.salad)
cheese_cauliflower = BundleItem(Meal.cheese_cauliflower)
parsnip_soup = BundleItem(Meal.parsnip_soup)
fried_mushroom = BundleItem(Meal.fried_mushroom)
salmon_dinner = BundleItem(Meal.salmon_dinner)
pepper_poppers = BundleItem(Meal.pepper_poppers)
spaghetti = BundleItem(Meal.spaghetti)
sashimi = BundleItem(Meal.sashimi)
blueberry_tart = BundleItem(Meal.blueberry_tart)
algae_soup = BundleItem(Meal.algae_soup)
pale_broth = BundleItem(Meal.pale_broth)
chowder = BundleItem(Meal.chowder)
cookie = BundleItem(Meal.cookie)
ancient_doll = BundleItem(Artifact.ancient_doll)
ice_cream = BundleItem(Meal.ice_cream)
cranberry_candy = BundleItem(Meal.cranberry_candy)
ginger_ale = BundleItem(Beverage.ginger_ale, source=BundleItem.Sources.island)
pink_cake = BundleItem(Meal.pink_cake)
plum_pudding = BundleItem(Meal.plum_pudding)
chocolate_cake = BundleItem(Meal.chocolate_cake)
rhubarb_pie = BundleItem(Meal.rhubarb_pie)
shrimp_cocktail = BundleItem(Meal.shrimp_cocktail)
pina_colada = BundleItem(Beverage.pina_colada, source=BundleItem.Sources.island)
stuffing = BundleItem(Meal.stuffing)
magic_rock_candy = BundleItem(Meal.magic_rock_candy)
spicy_eel = BundleItem(Meal.spicy_eel)
crab_cakes = BundleItem(Meal.crab_cakes)
eggplant_parmesan = BundleItem(Meal.eggplant_parmesan)
pumpkin_soup = BundleItem(Meal.pumpkin_soup)
lucky_lunch = BundleItem(Meal.lucky_lunch)
joja_cola = BundleItem(Trash.joja_cola)
strange_bun = BundleItem(Meal.strange_bun)
moss_soup = BundleItem(Meal.moss_soup)
roasted_hazelnuts = BundleItem(Meal.roasted_hazelnuts)
maple_bar = BundleItem(Meal.maple_bar)
green_algae = BundleItem(WaterItem.green_algae)
white_algae = BundleItem(WaterItem.white_algae)
geode = BundleItem(Geode.geode)
frozen_geode = BundleItem(Geode.frozen)
magma_geode = BundleItem(Geode.magma)
omni_geode = BundleItem(Geode.omni)
sap = BundleItem(Material.sap)
dwarf_scroll_1 = BundleItem(Artifact.dwarf_scroll_i)
dwarf_scroll_2 = BundleItem(Artifact.dwarf_scroll_ii)
dwarf_scroll_3 = BundleItem(Artifact.dwarf_scroll_iii)
dwarf_scroll_4 = BundleItem(Artifact.dwarf_scroll_iv)
elvish_jewelry = BundleItem(Artifact.elvish_jewelry)
ancient_drum = BundleItem(Artifact.ancient_drum)
dried_starfish = BundleItem(Fossil.dried_starfish)
bone_fragment = BundleItem(Fossil.bone_fragment)
golden_mask = BundleItem(Artifact.golden_mask)
golden_relic = BundleItem(Artifact.golden_relic)
dwarf_gadget = BundleItem(Artifact.dwarf_gadget)
dwarvish_helm = BundleItem(Artifact.dwarvish_helm)
prehistoric_handaxe = BundleItem(Artifact.prehistoric_handaxe)
bone_flute = BundleItem(Artifact.bone_flute)
anchor = BundleItem(Artifact.anchor)
prehistoric_tool = BundleItem(Artifact.prehistoric_tool)
chicken_statue = BundleItem(Artifact.chicken_statue)
rusty_cog = BundleItem(Artifact.rusty_cog)
rusty_spur = BundleItem(Artifact.rusty_spur)
rusty_spoon = BundleItem(Artifact.rusty_spoon)
ancient_sword = BundleItem(Artifact.ancient_sword)
ornamental_fan = BundleItem(Artifact.ornamental_fan)
chipped_amphora = BundleItem(Artifact.chipped_amphora)
strange_doll = BundleItem(Artifact.strange_doll)
strange_doll_green = BundleItem(Artifact.strange_doll_green)
ancient_seed = BundleItem(Artifact.ancient_seed)
rare_disc = BundleItem(Artifact.rare_disc)
prehistoric_scapula = BundleItem(Fossil.prehistoric_scapula)
prehistoric_tibia = BundleItem(Fossil.prehistoric_tibia)
prehistoric_skull = BundleItem(Fossil.prehistoric_skull)
skeletal_hand = BundleItem(Fossil.skeletal_hand)
prehistoric_rib = BundleItem(Fossil.prehistoric_rib)
prehistoric_vertebra = BundleItem(Fossil.prehistoric_vertebra)
skeletal_tail = BundleItem(Fossil.skeletal_tail)
nautilus_fossil = BundleItem(Fossil.nautilus_fossil)
amphibian_fossil = BundleItem(Fossil.amphibian_fossil)
palm_fossil = BundleItem(Fossil.palm_fossil)
trilobite = BundleItem(Fossil.trilobite)
snake_vertebrae = BundleItem(Fossil.snake_vertebrae, source=BundleItem.Sources.island)
mummified_bat = BundleItem(Fossil.mummified_bat, source=BundleItem.Sources.island)
fossilized_tail = BundleItem(Fossil.fossilized_tail, source=BundleItem.Sources.island)
dinosaur_mayo = BundleItem(ArtisanGood.dinosaur_mayonnaise)
void_mayo = BundleItem(ArtisanGood.void_mayonnaise)
prismatic_shard = BundleItem(Mineral.prismatic_shard)
diamond = BundleItem(Mineral.diamond)
ancient_fruit = BundleItem(Fruit.ancient_fruit)
void_salmon = BundleItem(Fish.void_salmon)
tea_leaves = BundleItem(Vegetable.tea_leaves)
blobfish = BundleItem(Fish.blobfish)
spook_fish = BundleItem(Fish.spook_fish)
lionfish = BundleItem(Fish.lionfish, source=BundleItem.Sources.island)
blue_discus = BundleItem(Fish.blue_discus, source=BundleItem.Sources.island)
stingray = BundleItem(Fish.stingray, source=BundleItem.Sources.island)
spookfish = BundleItem(Fish.spookfish)
midnight_squid = BundleItem(Fish.midnight_squid)
slimejack = BundleItem(Fish.slimejack)
goby = BundleItem(Fish.goby)
angler = BundleItem(Fish.angler)
crimsonfish = BundleItem(Fish.crimsonfish)
mutant_carp = BundleItem(Fish.mutant_carp)
glacierfish = BundleItem(Fish.glacierfish)
legend = BundleItem(Fish.legend)
spinner = BundleItem(Fishing.spinner)
dressed_spinner = BundleItem(Fishing.dressed_spinner)
trap_bobber = BundleItem(Fishing.trap_bobber)
sonar_bobber = BundleItem(Fishing.sonar_bobber)
cork_bobber = BundleItem(Fishing.cork_bobber)
lead_bobber = BundleItem(Fishing.lead_bobber)
treasure_hunter = BundleItem(Fishing.treasure_hunter)
barbed_hook = BundleItem(Fishing.barbed_hook)
curiosity_lure = BundleItem(Fishing.curiosity_lure)
quality_bobber = BundleItem(Fishing.quality_bobber)
bait = BundleItem(Fishing.bait, 100)
deluxe_bait = BundleItem(Fishing.deluxe_bait, 50)
magnet = BundleItem(Fishing.magnet)
wild_bait = BundleItem(Fishing.wild_bait, 20)
magic_bait = BundleItem(Fishing.magic_bait, 10, source=BundleItem.Sources.qi_board)
pearl = BundleItem(Gift.pearl)
challenge_bait = BundleItem(Fishing.challenge_bait, 25, source=BundleItem.Sources.masteries)
targeted_bait = BundleItem(ArtisanGood.targeted_bait, 25, source=BundleItem.Sources.content)
golden_bobber = BundleItem(Fishing.golden_bobber)
ginger = BundleItem(Forageable.ginger, source=BundleItem.Sources.content)
magma_cap = BundleItem(Mushroom.magma_cap, source=BundleItem.Sources.content)
wheat_flour = BundleItem(Ingredient.wheat_flour)
sugar = BundleItem(Ingredient.sugar)
vinegar = BundleItem(Ingredient.vinegar)
jack_o_lantern = BundleItem(Lighting.jack_o_lantern)
prize_ticket = BundleItem(Currency.prize_ticket)
mystery_box = BundleItem(Consumable.mystery_box)
gold_mystery_box = BundleItem(Consumable.gold_mystery_box, source=BundleItem.Sources.masteries)
calico_egg = BundleItem(Currency.calico_egg)
golden_tag = BundleItem(Currency.golden_tag)
stardrop_tea = BundleItem(ArtisanGood.stardrop_tea)
rotten_plant = BundleItem(Decoration.rotten_plant)
apple_slices = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.apple))
infinity_crown = BundleItem(Hats.infinity_crown.name, source=BundleItem.Sources.content)
bowler_hat = BundleItem(Hats.bowler.name, source=BundleItem.Sources.content)
sombrero = BundleItem(Hats.sombrero.name, source=BundleItem.Sources.content)
good_ol_cap = BundleItem(Hats.good_ol_cap.name, source=BundleItem.Sources.content)
living_hat = BundleItem(Hats.living_hat.name, source=BundleItem.Sources.content)
garbage_hat = BundleItem(Hats.garbage_hat.name, source=BundleItem.Sources.content)
golden_helmet = BundleItem(Hats.golden_helmet.name, source=BundleItem.Sources.content)
laurel_wreath_crown = BundleItem(Hats.laurel_wreath_crown.name, source=BundleItem.Sources.content)
joja_cap = BundleItem(Hats.joja_cap.name, source=BundleItem.Sources.content)
deluxe_pirate_hat = BundleItem(Hats.deluxe_pirate_hat.name, source=BundleItem.Sources.content)
dark_cowboy_hat = BundleItem(Hats.dark_cowboy_hat.name, source=BundleItem.Sources.content)
tiger_hat = BundleItem(Hats.tiger_hat.name, source=BundleItem.Sources.content)
mystery_hat = BundleItem(Hats.mystery_hat.name, source=BundleItem.Sources.content)
dark_ballcap = BundleItem(Hats.dark_ballcap.name, source=BundleItem.Sources.content)
goblin_mask = BundleItem(Hats.goblin_mask.name, source=BundleItem.Sources.island)
vacation_shirt = BundleItem(Shirts.vacation.name)
green_jacket_shirt = BundleItem(Shirts.green_jacket.name)
mermaid_boots = BundleItem(Boots.mermaid_boots)
lucky_purple_shorts = BundleItem(SpecialItem.lucky_purple_shorts)
trimmed_purple_shorts = BundleItem(SpecialItem.trimmed_purple_shorts)
ancient_fruit_wine = BundleItem(ArtisanGood.specific_wine(Fruit.ancient_fruit))
dried_ancient_fruit = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.ancient_fruit))
ancient_fruit_jelly = BundleItem(ArtisanGood.specific_jelly(Fruit.ancient_fruit))
starfruit_wine = BundleItem(ArtisanGood.specific_wine(Fruit.starfruit))
dried_starfruit = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.starfruit))
starfruit_jelly = BundleItem(ArtisanGood.specific_jelly(Fruit.starfruit))
rhubarb_wine = BundleItem(ArtisanGood.specific_wine(Fruit.rhubarb))
dried_rhubarb = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.rhubarb))
melon_wine = BundleItem(ArtisanGood.specific_wine(Fruit.melon))
dried_melon = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.melon))
pineapple_wine = BundleItem(ArtisanGood.specific_wine(Fruit.pineapple), source=BundleItem.Sources.content)
dried_pineapple = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.pineapple), source=BundleItem.Sources.content)
dried_banana = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.banana), source=BundleItem.Sources.content)
strawberry_wine = BundleItem(ArtisanGood.specific_wine(Fruit.strawberry))
dried_strawberry = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.strawberry))
pumpkin_juice = BundleItem(ArtisanGood.specific_juice(Vegetable.pumpkin))
raisins = BundleItem(ArtisanGood.raisins)
dried_qi_fruit = BundleItem(ArtisanGood.specific_dried_fruit(Fruit.qi_fruit), source=BundleItem.Sources.content)
aged_lava_eel_roe = BundleItem(ArtisanGood.specific_aged_roe(Fish.lava_eel))
aged_crimsonfish_roe = BundleItem(ArtisanGood.specific_aged_roe(Fish.crimsonfish))
aged_angler_roe = BundleItem(ArtisanGood.specific_aged_roe(Fish.angler))
legend_roe = BundleItem(AnimalProduct.specific_roe(Fish.legend))
aged_legend_roe = BundleItem(ArtisanGood.specific_aged_roe(Fish.legend))
aged_glacierfish_roe = BundleItem(ArtisanGood.specific_aged_roe(Fish.glacierfish))
aged_mutant_carp_roe = BundleItem(ArtisanGood.specific_aged_roe(Fish.mutant_carp))
midnight_squid_roe = BundleItem(AnimalProduct.specific_roe(Fish.midnight_squid))
legend_bait = BundleItem(ArtisanGood.specific_bait(Fish.legend))
smoked_legend = BundleItem(ArtisanGood.specific_smoked_fish(Fish.legend))
mystic_syrup = BundleItem(ArtisanGood.mystic_syrup)
apple_sapling = BundleItem(Sapling.apple)
apricot_sapling = BundleItem(Sapling.apricot)
banana_sapling = BundleItem(Sapling.banana, source=BundleItem.Sources.content)
cherry_sapling = BundleItem(Sapling.cherry)
mango_sapling = BundleItem(Sapling.mango, source=BundleItem.Sources.content)
orange_sapling = BundleItem(Sapling.orange)
peach_sapling = BundleItem(Sapling.peach)
pomegranate_sapling = BundleItem(Sapling.pomegranate)
cookout_kit = BundleItem(Craftable.cookout_kit)
tent_kit = BundleItem(Craftable.tent_kit)
bug_steak = BundleItem(Edible.bug_steak)
tea_set = BundleItem(Gift.tea_set)
golden_pumpkin = BundleItem(Gift.golden_pumpkin)
mermaid_pendant = BundleItem(Gift.mermaid_pendant)
void_ghost_pendant = BundleItem(Gift.void_ghost_pendant)
advanced_tv_remote = BundleItem(SpecialItem.advanced_tv_remote)
crystal_ball = BundleItem(CatalogueItem.crystal_ball)
amethyst_crystal_ball = BundleItem(CatalogueItem.amethyst_crystal_ball)
aquamarine_crystal_ball = BundleItem(CatalogueItem.aquamarine_crystal_ball)
emerald_crystal_ball = BundleItem(CatalogueItem.emerald_crystal_ball)
ruby_crystal_ball = BundleItem(CatalogueItem.ruby_crystal_ball)
topaz_crystal_ball = BundleItem(CatalogueItem.topaz_crystal_ball)
flute_block = BundleItem(Furniture.flute_block)
candle_lamp = BundleItem(Furniture.candle_lamp)
modern_lamp = BundleItem(Furniture.modern_lamp)
single_bed = BundleItem(Furniture.single_bed)
exotic_double_bed = BundleItem(Furniture.exotic_double_bed, source=BundleItem.Sources.qi_board)
statue_of_endless_fortune = BundleItem(Machine.statue_endless_fortune)
cursed_mannequin = BundleItem(Furniture.cursed_mannequin)
statue_of_blessings = BundleItem(Statue.blessings)
crane_house_plant = BundleItem(Furniture.crane_game_house_plant)
wood_floor = BundleItem(Floor.wood)
rustic_plank_floor = BundleItem(Floor.rustic)
straw_floor = BundleItem(Floor.straw)
weathered_floor = BundleItem(Floor.weathered)
crystal_floor = BundleItem(Floor.crystal)
stone_floor = BundleItem(Floor.stone)
stone_walkway_floor = BundleItem(Floor.stone_walkway)
brick_floor = BundleItem(Floor.brick)
wood_path = BundleItem(Floor.wood_path)
gravel_path = BundleItem(Floor.gravel_path)
cobblestone_path = BundleItem(Floor.cobblestone_path)
stepping_stone_path = BundleItem(Floor.stepping_stone_path)
crystal_path = BundleItem(Floor.crystal_path)
warp_totem_beach = BundleItem(Consumable.warp_totem_beach)
warp_totem_mountains = BundleItem(Consumable.warp_totem_mountains)
warp_totem_farm = BundleItem(Consumable.warp_totem_farm)
warp_totem_desert = BundleItem(Consumable.warp_totem_desert, source=BundleItem.Sources.content)
warp_totem_island = BundleItem(Consumable.warp_totem_island, source=BundleItem.Sources.island)
rain_totem = BundleItem(Consumable.rain_totem)
treasure_totem = BundleItem(Consumable.treasure_totem, source=BundleItem.Sources.masteries)
book_of_mysteries = BundleItem(Book.book_of_mysteries)
far_away_stone = BundleItem(SpecialItem.far_away_stone)
death = BundleItem(NotReallyAnItem.death)
camping_stove = BundleItem(MemeItem.camping_stove)
decorative_pot = BundleItem(MemeItem.decorative_pot)
slime_crate = BundleItem(MemeItem.slime_crate)
supply_crate = BundleItem(MemeItem.supply_crate)
warp_totem_qis_arena = BundleItem(MemeItem.warp_totem_qis_arena)
artifact_spot = BundleItem(MemeItem.artifact_spot)
twig = BundleItem(MemeItem.twig)
weeds = BundleItem(MemeItem.weeds)
lumber = BundleItem(MemeItem.lumber)
green_rain_weeds_0 = BundleItem(MemeItem.green_rain_weeds_0)
seed_spot = BundleItem(MemeItem.seed_spot)
pot_of_gold = BundleItem(MemeItem.pot_of_gold)

View File

@@ -0,0 +1,28 @@
from typing import Dict, List
from .remixed_anywhere_bundles import community_center_remixed_anywhere
from .remixed_bundles import pantry_remixed, crafts_room_remixed, fish_tank_remixed, boiler_room_remixed, bulletin_board_remixed, vault_remixed, \
abandoned_joja_mart_remixed, giant_stump_remixed
from .thematic_bundles import pantry_thematic, crafts_room_thematic, fish_tank_thematic, boiler_room_thematic, bulletin_board_thematic, vault_thematic, \
abandoned_joja_mart_thematic, giant_stump_thematic
from .vanilla_bundles import crafts_room_vanilla, pantry_vanilla, fish_tank_vanilla, boiler_room_vanilla, \
bulletin_board_vanilla, vault_vanilla, abandoned_joja_mart_vanilla, giant_stump_vanilla
from ...bundles.bundle_room import BundleRoomTemplate
class BundleSet:
bundles_by_room: Dict[str, BundleRoomTemplate]
def __init__(self, bundle_rooms: List[BundleRoomTemplate]):
self.bundles_by_room = {bundle_room.name: bundle_room for bundle_room in bundle_rooms}
vanilla_bundles = BundleSet([pantry_vanilla, crafts_room_vanilla, fish_tank_vanilla, boiler_room_vanilla, bulletin_board_vanilla,
vault_vanilla, abandoned_joja_mart_vanilla, giant_stump_vanilla])
thematic_bundles = BundleSet([pantry_thematic, crafts_room_thematic, fish_tank_thematic, boiler_room_thematic, bulletin_board_thematic,
vault_thematic, abandoned_joja_mart_thematic, giant_stump_thematic])
remixed_bundles = BundleSet([pantry_remixed, crafts_room_remixed, fish_tank_remixed, boiler_room_remixed, bulletin_board_remixed,
vault_remixed, abandoned_joja_mart_remixed, giant_stump_remixed])
remixed_anywhere_bundles = BundleSet([community_center_remixed_anywhere, abandoned_joja_mart_remixed, giant_stump_remixed])
# shuffled_bundles = BundleSet()

View File

@@ -0,0 +1,378 @@
from .bundle_data import all_bundle_items_by_name
from .meme_bundles_data.capitalist_bundle import capitalist_items
from .remixed_bundles import *
from ...bundles.bundle import BureaucracyBundleTemplate, RecursiveBundleTemplate, FixedPriceCurrencyBundleTemplate, \
FixedPriceBundleTemplate, FixedPriceDeepBundleTemplate, FixedMultiplierBundleTemplate, FixedSlotsBundleTemplate
from ...strings.bundle_names import MemeBundleName
from ...strings.currency_names import MemeCurrency
from ...strings.flower_names import all_flowers
from ...strings.machine_names import Machine
from ...strings.meme_item_names import MemeItem
from ...strings.quality_names import AnimalProductQuality, CropQuality
burger_king_items = [survival_burger, joja_cola, apple_slices, ice_cream, strange_doll, strange_doll_green, hashbrowns, infinity_crown]
burger_king_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.burger_king, burger_king_items, 6, 3)
capitalist_bundle = FixedMultiplierBundleTemplate(CCRoom.vault, MemeBundleName.capitalist, capitalist_items, 12, 2)
romance_items = [lucky_purple_shorts, truffle_oil, super_cucumber, good_ol_cap]
romance_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.romance, romance_items, 4, 4)
hurricane_tortilla_items = [tortilla.as_amount(4)]
hurricane_tortilla_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.hurricane_tortilla, hurricane_tortilla_items, 6, 6)
AAAA_items = [battery_pack.as_amount(12), battery_pack.as_amount(8), battery_pack.as_amount(6)]
AAAA_bundle = BundleTemplate(CCRoom.crafts_room, MemeBundleName.AAAA, AAAA_items, 3, 3)
anything_for_beyonce_items = [beet]
anything_for_beyonce_bundle = BundleTemplate(CCRoom.crafts_room, MemeBundleName.anything_for_beyonce, anything_for_beyonce_items, 1, 1)
crab_rave_items = [crab.as_amount(8)]
crab_rave_bundle = BundleTemplate(CCRoom.fish_tank, MemeBundleName.crab_rave, crab_rave_items, 12, 8)
potato_items = [potato.as_amount(8)]
potato_bundle = BundleTemplate(CCRoom.crafts_room, MemeBundleName.potato, potato_items, 12, 8)
look_at_chickens_items = [duck_egg.as_amount(2)]
look_at_chickens_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.look_at_chickens, look_at_chickens_items, 10, 4)
lemonade_stand_items = [grape]
lemonade_stand_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.lemonade_stand, lemonade_stand_items, 3, 1)
what_the_rock_is_cooking_items = [stone.as_amount(1), cookout_kit, strange_bun]
what_the_rock_is_cooking_bundle = FixedPriceBundleTemplate(CCRoom.pantry, MemeBundleName.what_the_rock_is_cooking, what_the_rock_is_cooking_items, 3, 3)
amons_fall_items = [stone.as_amount(1)]
amons_fall_bundle = FixedPriceBundleTemplate(CCRoom.boiler_room, MemeBundleName.amons_fall, amons_fall_items, 7, 7)
screw_you_items = [tea_set, ostrich_egg.as_quality(AnimalProductQuality.iridium), snake_vertebrae.as_amount(5), mummified_bat.as_amount(5)]
screw_you_bundle = BundleTemplate(CCRoom.boiler_room, MemeBundleName.screw_you, screw_you_items, 4, 4)
sunmaid_items = [raisins.as_amount(28)]
sunmaid_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.sunmaid, sunmaid_items, 1, 1)
rick_items = [pickles]
rick_bundle = FixedPriceBundleTemplate(CCRoom.boiler_room, MemeBundleName.rick, rick_items, 1, 1)
minecraft_items = [coal, copper_ore, iron_ore, quartz, amethyst, emerald, gold_ore, diamond, obsidian]
minecraft_bundle = BundleTemplate(CCRoom.boiler_room, MemeBundleName.minecraft, minecraft_items, 9, 8)
balls_items = [blue_jazz, cauliflower, blueberry, melon, red_cabbage, tomato, powdermelon, cranberries, fairy_rose, grape, pumpkin, ancient_fruit,
solar_essence, cherry_bomb, bomb, mega_bomb, coal, iridium_ore, aquamarine, jamborite, geode, magma_geode, ancient_seed, crystal_ball,
amethyst_crystal_ball, aquamarine_crystal_ball, emerald_crystal_ball, ruby_crystal_ball, topaz_crystal_ball, apple, pizza, explosive_ammo, peach,
orange, apricot, tigerseye, coconut, gold_ore, golden_coconut, pufferfish, lucky_lunch, salad, cactus_fruit, radioactive_ore, opal, broken_cd,
void_essence, wild_plum, pomegranate]
balls_items = [item.as_amount(1) for item in balls_items]
balls_bundle = BundleTemplate(CCRoom.boiler_room, MemeBundleName.balls, balls_items, 12, 6)
tilesanity_items = [wood_floor.as_amount(100), rustic_plank_floor.as_amount(100), straw_floor.as_amount(100), weathered_floor.as_amount(100),
crystal_floor.as_amount(100), stone_floor.as_amount(100), stone_walkway_floor.as_amount(100), brick_floor.as_amount(100),
wood_path.as_amount(100), gravel_path.as_amount(100), cobblestone_path.as_amount(100), stepping_stone_path.as_amount(100),
crystal_path.as_amount(100)]
tilesanity_bundle = BundleTemplate(CCRoom.boiler_room, MemeBundleName.tilesanity, tilesanity_items, 4, 4)
cap_items = [vacation_shirt, wood.as_amount(999), sap.as_amount(999), pine_cone.as_amount(100), acorn.as_amount(100),
maple_seed.as_amount(100), moss.as_amount(500), exotic_double_bed.as_amount(1)]
cap_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.cap, cap_items, 8, 4)
big_grapes_items = [coconut]
big_grapes_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.big_grapes, big_grapes_items, 4, 4)
obelisks_items = [earth_crystal.as_amount(10), clam.as_amount(10), coral.as_amount(10), coconut.as_amount(10), cactus_fruit.as_amount(10),
banana.as_amount(10), dragon_tooth.as_amount(10), iridium_bar.as_amount(45)]
obelisks_bundle = FixedPriceBundleTemplate(CCRoom.boiler_room, MemeBundleName.obelisks, obelisks_items, 8, 8)
burger_king_revenge_items = [fossilized_tail, void_salmon, ostrich_egg.as_amount(3), tea_leaves.as_amount(10), purple_slime_egg,
moss_soup.as_amount(3), radioactive_ore.as_amount(5), mystic_syrup.as_amount(10), truffle, aged_crimsonfish_roe]
burger_king_revenge_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.burger_king_revenge, burger_king_revenge_items, 8, 8)
trout_items = [golden_tag.as_amount(10), golden_tag.as_amount(20), golden_tag.as_amount(30)]
trout_bundle = BundleTemplate(CCRoom.fish_tank, MemeBundleName.trout, trout_items, 1, 1)
eg_items = [egg, brown_egg, large_egg, large_brown_egg, duck_egg, void_egg, golden_egg, dinosaur_egg, fried_egg, ostrich_egg,
thunder_egg, calico_egg, green_slime_egg, blue_slime_egg, purple_slime_egg, tiger_slime_egg, roe, aged_roe]
eg_items = [item.as_amount(57) for item in eg_items]
eg_bundle = FixedPriceBundleTemplate(CCRoom.pantry, MemeBundleName.eg, eg_items, 8, 2)
doctor_angler_items = [fish.as_quality(FishQuality.iridium) for fish in legendary_fish_items]
doctor_angler_bundle = BundleTemplate(CCRoom.fish_tank, MemeBundleName.doctor_angler, doctor_angler_items, 5, 5)
smapi_items = [camping_stove, decorative_pot, slime_crate, supply_crate, warp_totem_qis_arena,
artifact_spot, twig, weeds, lumber, green_rain_weeds_0, seed_spot, pot_of_gold]
smapi_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.smapi, smapi_items, 4, 4)
chaos_emerald_items = [diamond, emerald, ruby, limestone, obsidian, kyanite, lemon_stone]
chaos_emerald_bundle = FixedPriceBundleTemplate(CCRoom.crafts_room, MemeBundleName.chaos_emerald, chaos_emerald_items, 7, 7)
not_the_bees_items = [BundleItem(ArtisanGood.specific_honey(flower)) for flower in all_flowers]
not_the_bees_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.not_the_bees, not_the_bees_items, 4, 4)
sappy_items = [golden_pumpkin, magic_rock_candy, pearl, prismatic_shard, rabbit_foot, stardrop_tea]
sappy_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.sappy, sappy_items, 4, 4)
honorable_items = [stone.as_amount(1), prismatic_shard.as_amount(1)]
honorable_bundle = FixedPriceBundleTemplate(CCRoom.boiler_room, MemeBundleName.honorable, honorable_items, 2, 1)
caffeinated_items = [coffee_bean.as_amount(500)]
caffeinated_bundle = BundleTemplate(CCRoom.crafts_room, MemeBundleName.caffeinated, caffeinated_items, 1, 1)
hats_off_to_you_items = [living_hat, garbage_hat, golden_helmet, laurel_wreath_crown, joja_cap,
deluxe_pirate_hat, dark_cowboy_hat, tiger_hat, mystery_hat, dark_ballcap]
hats_off_to_you_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.hats_off_to_you, hats_off_to_you_items, 8, 2)
speedrunners_items = [parsnip, wine, cheese, sea_urchin, lucky_purple_shorts, mayonnaise]
speedrunners_bundle = FixedPriceBundleTemplate(CCRoom.pantry, MemeBundleName.speedrunners, speedrunners_items, 6, 6)
snitch_items = [lucky_purple_shorts]
snitch_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.snitch, snitch_items, 1, 1)
mermaid_items = [pearl, clam.as_amount(2), mermaid_pendant, mermaid_boots, flute_block.as_amount(5)]
mermaid_bundle = FixedPriceBundleTemplate(CCRoom.fish_tank, MemeBundleName.mermaid, mermaid_items, 5, 5)
commitment_items = [bouquet, mermaid_pendant, wilted_bouquet, ancient_doll.as_amount(2)]
commitment_bundle_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.commitment, commitment_items, 4, 4)
all_simple_items = [all_bundle_items_by_name[bundle_item_name] for bundle_item_name in all_bundle_items_by_name if
all_bundle_items_by_name[bundle_item_name].amount == 1 and
all_bundle_items_by_name[bundle_item_name].quality.startswith("Basic") and
all_bundle_items_by_name[bundle_item_name].flavor is None and
bundle_item_name != "Honey"]
permit_a38_items = [*all_simple_items]
permit_a38_bundle = BureaucracyBundleTemplate(CCRoom.vault, MemeBundleName.permit_a38, permit_a38_items, 1, 8)
journalist_items = [*all_simple_items]
journalist_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.journalist, journalist_items, 1, 1)
trap_items = [BundleItem(MemeItem.trap)]
trap_bundle = FixedSlotsBundleTemplate(CCRoom.bulletin_board, MemeBundleName.trap, trap_items, 4, 4)
off_your_back_items = [BundleItem(MemeItem.worn_hat), BundleItem(MemeItem.worn_shirt), BundleItem(MemeItem.worn_pants),
BundleItem(MemeItem.worn_boots), BundleItem(MemeItem.worn_left_ring), BundleItem(MemeItem.worn_right_ring)]
off_your_back_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.off_your_back, off_your_back_items, 6, 6)
sisyphus_items = [stone.as_amount(1)]
sisyphus_bundle = FixedPriceBundleTemplate(CCRoom.boiler_room, MemeBundleName.sisyphus, sisyphus_items, 12, 12)
vocaloid_items = [spring_onion, orange, banana, tuna, wine, ice_cream, carrot, bread, eggplant]
vocaloid_items = [item.as_amount(10) for item in vocaloid_items]
vocaloid_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.vocaloid, vocaloid_items, 6, 6)
legendairy_items = [legend, legend_roe, legend_bait, smoked_legend, aged_legend_roe,
milk.as_amount(10), cheese.as_amount(10), goat_milk.as_amount(10), goat_cheese.as_amount(10)]
legendairy_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.legendairy, legendairy_items, 6, 4)
kent_c_items = [broken_glasses.as_amount(5), refined_quartz.as_amount(10)]
kent_c_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.kent_c, kent_c_items, 2, 2)
fruit_items = [tomato, pumpkin, summer_squash, eggplant, hot_pepper]
fruit_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.fruit, fruit_items, 5, 5)
reverse_items = [*all_simple_items]
reverse_bundle = FixedSlotsBundleTemplate(CCRoom.crafts_room, MemeBundleName.reverse, reverse_items, 4, 4)
bundle_items = [*all_simple_items]
bundle_bundle = RecursiveBundleTemplate(CCRoom.fish_tank, MemeBundleName.bundle, bundle_items, 16, 16, 4)
bun_dle_items = [strange_bun, bread, tortilla, rabbit_foot]
bun_dle_bundle = BundleTemplate(CCRoom.pantry, MemeBundleName.bun_dle, bun_dle_items, 4, 4)
celeste_items = [strawberry.as_amount(175), strawberry_seeds.as_amount(4), strawberry.as_quality(CropQuality.gold).as_amount(26)]
celeste_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.celeste, celeste_items, 3, 3)
automation_items = [copper_bar.as_amount(15), iron_bar.as_amount(36), iron_bar.as_amount(20), copper_bar.as_amount(10)]
automation_bundle = FixedPriceBundleTemplate(CCRoom.boiler_room, MemeBundleName.automation, automation_items, 4, 4)
animal_well_items = [rare_disc, bone_flute, ruby_crystal_ball, cherry_bomb.as_amount(1), candle_lamp, modern_lamp, advanced_tv_remote]
animal_well_bundle = FixedPriceBundleTemplate(CCRoom.pantry, MemeBundleName.animal_well, animal_well_items, 7, 7)
schrodinger_items = [*all_simple_items]
schrodinger_bundle = FixedPriceBundleTemplate(CCRoom.fish_tank, MemeBundleName.schrodinger, schrodinger_items, 2, 1)
ikea_craftables = [Machine.mayonnaise_machine, Machine.bee_house, Machine.preserves_jar, Machine.cheese_press, Machine.keg, Machine.fish_smoker,
Machine.crystalarium, Machine.worm_bin, Furniture.tub_o_flowers]
ikea_items = [BundleItem(craftable) for craftable in ikea_craftables]
ikea_bundle = FixedPriceBundleTemplate(CCRoom.crafts_room, MemeBundleName.ikea, ikea_items, 1, 1)
this_is_fine_items = [coffee, fire_quartz, fire_quartz, fire_quartz, fire_quartz, fire_quartz, fire_quartz, fire_quartz]
this_is_fine_bundle = FixedPriceBundleTemplate(CCRoom.crafts_room, MemeBundleName.this_is_fine, this_is_fine_items, 8, 8)
crap_pot_items = [clay, mudstone, truffle, sunflower_seeds, roasted_hazelnuts, plum_pudding, rotten_plant, taro_root]
crap_pot_bundle = BundleTemplate(CCRoom.boiler_room, MemeBundleName.crap_pot, crap_pot_items, 4, 4)
emmalution_items = [garlic, bread, trash, goblin_mask, rain_totem]
emmalution_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.emmalution, emmalution_items, 5, 5)
unused_balls = [fairy_rose, melon, grape, geode, ancient_seed, crystal_ball, peach]
yellow_pool_balls = [item.as_amount(1) for item in [solar_essence, topaz_crystal_ball, pizza, apricot, gold_ore, golden_coconut, pufferfish, lucky_lunch]]
blue_pool_balls = [item.as_amount(2) for item in [blue_jazz, blueberry, powdermelon, ancient_fruit, iridium_ore, aquamarine, opal, broken_cd]]
red_pool_balls = [item.as_amount(3) for item in [tomato, mega_bomb, magma_geode, apple, explosive_ammo, cranberries, cherry_bomb]]
purple_pool_balls = [item.as_amount(4) for item in [red_cabbage, pomegranate, void_essence, wild_plum]]
orange_pool_balls = [item.as_amount(5) for item in [pumpkin, orange, tigerseye]]
green_pool_balls = [item.as_amount(6) for item in [cauliflower, jamborite, salad, cactus_fruit, radioactive_ore]]
brown_pool_balls = [item.as_amount(7) for item in [acorn, coconut, hazelnut, maple_bar, maple_syrup, potato, truffle, yam]]
black_pool_balls = [item.as_amount(8) for item in [bomb, coal, void_egg]]
pool_items = [yellow_pool_balls, blue_pool_balls, red_pool_balls, purple_pool_balls, orange_pool_balls, green_pool_balls, brown_pool_balls, black_pool_balls]
pool_bundle = FixedPriceDeepBundleTemplate(CCRoom.boiler_room, MemeBundleName.pool, pool_items, 8, 8)
argonmatrix_items = [radish.as_amount(32), radish.as_amount(87), melon.as_amount(127), chocolate_cake.as_amount(3), cactus_fruit.as_amount(1)]
argonmatrix_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.argonmatrix, argonmatrix_items, 5, 5)
frazzleduck_items = [duck_egg, duck_feather, eggplant, green_jacket_shirt]
frazzleduck_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.frazzleduck, frazzleduck_items, 4, 4)
loser_club_items = [tuna]
loser_club_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.loser_club, loser_club_items, 1, 1)
ministry_items = [item.as_amount(999) for item in [trash, joja_cola, broken_glasses, broken_cd, soggy_newspaper]]
ministry_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.ministry_of_madness, ministry_items, 4, 1)
pomnut_items = [pomegranate, hazelnut, carrot]
pomnut_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.pomnut, pomnut_items, 3, 3)
blossom_garden_items = [banana.as_amount(18), pizza.as_amount(32), spaghetti, single_bed, pink_cake, wood_floor, triple_shot_espresso, maple_bar, bug_steak, void_essence.as_amount(10), crystal_ball, solar_essence.as_amount(10)]
blossom_garden_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.blossom_garden, blossom_garden_items, 12, 6)
cooperation_items = [*all_simple_items]
cooperation_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.cooperation, cooperation_items, 4, 4)
doctor_items = [apple.as_amount(365)]
doctor_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.doctor, doctor_items, 1, 1)
very_sticky_items = [sap.as_amount(125), sap.as_amount(125), sap.as_amount(125), sap.as_amount(125)]
very_sticky_bundle = FixedPriceBundleTemplate(CCRoom.crafts_room, MemeBundleName.very_sticky, very_sticky_items, 4, 4)
square_hole_items = [*all_simple_items]
square_hole_bundle = FixedPriceBundleTemplate(CCRoom.bulletin_board, MemeBundleName.square_hole, square_hole_items, 6, 6)
distracted_items = [*all_simple_items] # (If you bring more than one item for it, the rest get sent home)
distracted_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.distracted, distracted_items, 4, 4)
algorerhythm_items =[item.as_amount(2) for item in
[midnight_squid_roe, tea_set, statue_of_endless_fortune, golden_bobber, dried_qi_fruit, cursed_mannequin,
statue_of_blessings, crane_house_plant, book_of_mysteries, far_away_stone, void_ghost_pendant, trimmed_purple_shorts]]
algorerhythm_bundle = BundleTemplate(CCRoom.bulletin_board, MemeBundleName.algorerhythm, algorerhythm_items, 12, 4)
red_fish_items = [red_mullet, red_snapper, lava_eel, crimsonfish]
blue_fish_items = [anchovy, tuna, sardine, bream, squid, ice_pip, albacore, blue_discus, midnight_squid, spook_fish, glacierfish]
other_fish = [pufferfish, largemouth_bass, smallmouth_bass, rainbow_trout, walleye, perch, carp, catfish, pike, sunfish, herring, eel, octopus, sea_cucumber,
super_cucumber, ghostfish, stonefish, sandfish, scorpion_carp, flounder, midnight_carp, tigerseye, bullhead, tilapia, chub, dorado, shad,
lingcod, halibut, slimejack, stingray, goby, blobfish, angler, legend, mutant_carp]
dr_seuss_items = [other_fish, [fish.as_amount(2) for fish in other_fish], red_fish_items, blue_fish_items]
dr_seuss_bundle = FixedPriceDeepBundleTemplate(CCRoom.crafts_room, MemeBundleName.dr_seuss, dr_seuss_items, 4, 4)
pollution_items = [trash, broken_cd, broken_glasses, joja_cola, soggy_newspaper, battery_pack]
pollution_bundle = BundleTemplate(CCRoom.fish_tank, MemeBundleName.pollution, pollution_items, 4, 4)
all_fish_item_names = sorted(list(set([item.item_name for item in [*spring_fish_items, *summer_fish_items, *fall_fish_items, *winter_fish_items]])))
all_fish_items = [BundleItem(item).as_amount(1).as_quality(FishQuality.basic) for item in all_fish_item_names]
catch_and_release_items = [*all_fish_items]
catch_and_release_bundle = BundleTemplate(CCRoom.fish_tank, MemeBundleName.catch_and_release, catch_and_release_items, 4, 4)
vampire_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.vampire, BundleItem(MemeCurrency.blood, 200))
exhaustion_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.exhaustion, BundleItem(MemeCurrency.energy, 400))
tick_tock_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.tick_tock, BundleItem(MemeCurrency.time, 1440))
archipela_go_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.archipela_go, BundleItem(MemeCurrency.steps, 20000))
clique_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.clique, BundleItem(MemeCurrency.clic, 1))
cookie_clicker_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.cookie_clicker, BundleItem(MemeCurrency.cookies, 200000))
communism_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.communism, BundleItem.money_bundle(1))
death_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.death, death)
flashbang_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.flashbang, BundleItem.money_bundle(0))
connection_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.connection, BundleItem.money_bundle(0))
reconnection_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.reconnection, BundleItem.money_bundle(0))
nft_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.nft, BundleItem.money_bundle(0))
firstborn_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.firstborn, BundleItem(MemeCurrency.child, 1))
restraint_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.restraint, BundleItem.money_bundle(0))
fast_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.fast, BundleItem(MemeCurrency.time_elapsed, 1000))
floor_is_lava_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.floor_is_lava, BundleItem.money_bundle(0))
joetg_bundle = CurrencyBundleTemplate(CCRoom.bulletin_board, MemeBundleName.joetg, BundleItem(MemeCurrency.dead_pumpkins, 750))
bad_farmer_bundle = CurrencyBundleTemplate(CCRoom.pantry, MemeBundleName.bad_farmer, BundleItem(MemeCurrency.dead_crops, 400))
bad_fisherman_bundle = CurrencyBundleTemplate(CCRoom.fish_tank, MemeBundleName.bad_fisherman, BundleItem(MemeCurrency.missed_fish, 20))
honeywell_bundle = CurrencyBundleTemplate(CCRoom.bulletin_board, MemeBundleName.honeywell, BundleItem(MemeCurrency.honeywell, 1))
gacha_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.gacha, BundleItem.money_bundle(10000))
hibernation_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.hibernation, BundleItem(MemeCurrency.sleep_days, 60))
crowdfunding_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.crowdfunding, BundleItem(MemeCurrency.bank_money, 10000))
clickbait_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.clickbait, BundleItem.money_bundle(100))
puzzle_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.puzzle, BundleItem.money_bundle(10))
asmr_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.asmr, BundleItem.money_bundle(0))
humble_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.humble, BundleItem.money_bundle(5000))
deathlink_bundle = CurrencyBundleTemplate(CCRoom.boiler_room, MemeBundleName.deathlink, BundleItem(MemeCurrency.deathlinks, 10))
investment_bundle = CurrencyBundleTemplate(CCRoom.vault, MemeBundleName.scam, BundleItem.money_bundle(10000))
stanley_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.stanley, BundleItem.money_bundle(9999999))
hairy_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.vault, MemeBundleName.hairy, BundleItem.money_bundle(0))
# colored_crystals_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.boiler_room, MemeBundleName.colored_crystals, BundleItem.money_bundle(10))
hint_bundle = FixedPriceCurrencyBundleTemplate(CCRoom.bulletin_board, MemeBundleName.hint, BundleItem.money_bundle(10))
sacrifice_bundle = CurrencyBundleTemplate(CCRoom.boiler_room, MemeBundleName.sacrifice, BundleItem(MemeCurrency.goat, 1))
# Stopped at 49 responses on the form
# Todo Bundles
# Acrostic Bundle (Asks for a specific word, you need to donate an item for each letter)
# Bubbles Bundle
# Cipher Bundle (Some sort of code?)
# DLC Bundle
# Doom Bundle
# Dragonball Bundle
# Empty Bundle (donate empty inventory spot)
# Friendship Bundle (Show some NPCs, gotta donate a loved gift for each of them)
# GeoGessr Bundle
# Ghost Bundle (it ghosts you)
# Joja/Morris Bundle
# Leaf Blower Bundle (Leaf Blower Minigame, similar to the cookie clicker one)
# Lingo Bundle
# Lost Axe Bundle (Donate your axe then talk to Robin)
# Maguffin Bundle (Ap items)
# Millibelle Bundle (money, run away, find at spa)
# Minesweeper bundle (donate bombs on correct spots)
# Pico-8 Bundle
# Pollution Bundle
# QA Bundle (Some sort of bug, not sure yet)
# Relay Bundle (Relay Stick passed around the multiworld)
# Robin's Lost Axe Bundle (Give your axe, then Robin brings it back to you)
# Scavenger Bundle (The bundle moves around the map and you need to keep finding it)
# Side Quest Bundle (Sends you on side quests to talk to random NPCs several times)
# Therapy Bundle
# Torrent Bundle (someone must seed it for you)
# Witness Bundle
# Change Cap Bundle to forgetting something at home
# Bundles that need special Mod Handling:
# None
pantry_bundles_meme = [hurricane_tortilla_bundle, look_at_chickens_bundle, lemonade_stand_bundle, what_the_rock_is_cooking_bundle, sunmaid_bundle,
big_grapes_bundle, eg_bundle, not_the_bees_bundle, speedrunners_bundle, bun_dle_bundle, animal_well_bundle, bad_farmer_bundle]
pantry_meme = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_meme, 6)
crafts_room_bundles_meme = [AAAA_bundle, anything_for_beyonce_bundle, potato_bundle, chaos_emerald_bundle, caffeinated_bundle, reverse_bundle,
ikea_bundle, this_is_fine_bundle, very_sticky_bundle, dr_seuss_bundle]
crafts_room_meme = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_meme, 6)
fish_tank_bundles_meme = [crab_rave_bundle, trout_bundle, doctor_angler_bundle, mermaid_bundle, legendairy_bundle, kent_c_bundle, bundle_bundle,
schrodinger_bundle, bad_fisherman_bundle, pollution_bundle, catch_and_release_bundle]
fish_tank_meme = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_meme, 6)
boiler_room_bundles_meme = [amons_fall_bundle, screw_you_bundle, rick_bundle, minecraft_bundle, balls_bundle, tilesanity_bundle, obelisks_bundle,
honorable_bundle, sisyphus_bundle, automation_bundle, crap_pot_bundle, deathlink_bundle, pool_bundle, # colored_crystals_bundle,
sacrifice_bundle]
boiler_room_meme = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_meme, 3)
bulletin_board_bundles_meme = [burger_king_bundle, romance_bundle, burger_king_revenge_bundle, smapi_bundle, sappy_bundle, hats_off_to_you_bundle,
snitch_bundle, commitment_bundle_bundle, journalist_bundle, trap_bundle, off_your_back_bundle, vocaloid_bundle, fruit_bundle,
celeste_bundle, cap_bundle, emmalution_bundle, joetg_bundle, honeywell_bundle, cooperation_bundle, square_hole_bundle,
ministry_bundle, loser_club_bundle, frazzleduck_bundle, argonmatrix_bundle, pomnut_bundle, blossom_garden_bundle, doctor_bundle,
hint_bundle, algorerhythm_bundle, distracted_bundle]
bulletin_board_meme = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_meme, 5)
vault_bundles_meme = [capitalist_bundle, death_bundle, permit_a38_bundle, vampire_bundle, exhaustion_bundle,
tick_tock_bundle, archipela_go_bundle, clique_bundle, cookie_clicker_bundle, communism_bundle,
flashbang_bundle, connection_bundle, reconnection_bundle, nft_bundle, firstborn_bundle, restraint_bundle, fast_bundle,
floor_is_lava_bundle, gacha_bundle, hibernation_bundle, crowdfunding_bundle, clickbait_bundle,
humble_bundle, puzzle_bundle, asmr_bundle, investment_bundle, stanley_bundle, hairy_bundle]
vault_meme = BundleRoomTemplate(CCRoom.vault, vault_bundles_meme, 4)
all_cc_meme_bundles = [*pantry_bundles_meme, *crafts_room_bundles_meme, *fish_tank_bundles_meme,
*boiler_room_bundles_meme, *bulletin_board_bundles_meme, *vault_bundles_meme]
community_center_meme_bundles = BundleRoomTemplate("Community Center", all_cc_meme_bundles, 30)

View File

@@ -0,0 +1,92 @@
import math
from typing import Dict, List
from ..bundle_items_data import *
from ....bundles.bundle_item import BundleItem
from ....strings.quality_names import ArtisanQuality
capitalist_value = 1000000
ancient_fruit_wines = {ArtisanQuality.basic: 2310, ArtisanQuality.silver: 2886, ArtisanQuality.gold: 3465, ArtisanQuality.iridium: 4620}
starfruit_wines = {ArtisanQuality.basic: 3150, ArtisanQuality.silver: 3936, ArtisanQuality.gold: 4725, ArtisanQuality.iridium: 6300}
rhubarb_wines = {ArtisanQuality.silver: 1155, ArtisanQuality.gold: 1386, ArtisanQuality.iridium: 1848}
melon_wines = {ArtisanQuality.basic: 1050, ArtisanQuality.silver: 1311, ArtisanQuality.gold: 1575, ArtisanQuality.iridium: 2100}
pineapple_wines = {ArtisanQuality.basic: 1260, ArtisanQuality.silver: 1575, ArtisanQuality.gold: 1890, ArtisanQuality.iridium: 2520}
starfruits = {ArtisanQuality.silver: 1030, ArtisanQuality.gold: 1237, ArtisanQuality.iridium: 1650}
sweet_gem_berries = {ArtisanQuality.basic: 3000, ArtisanQuality.silver: 3750, ArtisanQuality.gold: 4500, ArtisanQuality.iridium: 6000}
# These are just too rude I think
# cherry_saplings = {ArtisanQuality.silver: 1062, ArtisanQuality.gold: 1275, ArtisanQuality.iridium: 1700}
# banana_saplings = {ArtisanQuality.silver: 1062, ArtisanQuality.gold: 1275, ArtisanQuality.iridium: 1700}
# mango_saplings = {ArtisanQuality.silver: 1062, ArtisanQuality.gold: 1275, ArtisanQuality.iridium: 1700}
# orange_saplings = {ArtisanQuality.silver: 1250, ArtisanQuality.gold: 1500, ArtisanQuality.iridium: 2000}
# peach_saplings = {ArtisanQuality.basic: 1500, ArtisanQuality.silver: 1875, ArtisanQuality.gold: 2250, ArtisanQuality.iridium: 3000}
# apple_saplings = {ArtisanQuality.silver: 1250, ArtisanQuality.gold: 1500, ArtisanQuality.iridium: 2000}
# pomegranate_saplings = {ArtisanQuality.basic: 1500, ArtisanQuality.silver: 1875, ArtisanQuality.gold: 2250, ArtisanQuality.iridium: 3000}
def get_capitalist_item(item: BundleItem, quality: str, value: int) -> BundleItem:
amount = math.ceil(capitalist_value / value)
assert amount < 1000
return item.as_quality(quality).as_amount(amount)
def get_capitalist_items(item: BundleItem, values_by_quality: Dict[str, int]) -> List[BundleItem]:
return [get_capitalist_item(item, quality, values_by_quality[quality]) for quality in values_by_quality]
capitalist_items = [
*get_capitalist_items(ancient_fruit_wine, ancient_fruit_wines),
get_capitalist_item(dried_ancient_fruit, ArtisanQuality.basic, 5810),
get_capitalist_item(ancient_fruit_jelly, ArtisanQuality.basic, 1610),
get_capitalist_item(ancient_fruit, ArtisanQuality.iridium, 1210),
*get_capitalist_items(starfruit_wine, starfruit_wines),
get_capitalist_item(dried_starfruit, ArtisanQuality.basic, 7910),
get_capitalist_item(starfruit_jelly, ArtisanQuality.basic, 2170),
*get_capitalist_items(starfruit, starfruits),
*get_capitalist_items(rhubarb_wine, rhubarb_wines),
get_capitalist_item(dried_rhubarb, ArtisanQuality.basic, 2345),
*get_capitalist_items(melon_wine, melon_wines),
get_capitalist_item(dried_melon, ArtisanQuality.basic, 2660),
*get_capitalist_items(pineapple_wine, pineapple_wines),
get_capitalist_item(dried_pineapple, ArtisanQuality.basic, 3185),
get_capitalist_item(dried_banana, ArtisanQuality.basic, 1610),
get_capitalist_item(strawberry_wine, ArtisanQuality.iridium, 1008),
get_capitalist_item(dried_strawberry, ArtisanQuality.basic, 1295),
*get_capitalist_items(sweet_gem_berry, sweet_gem_berries),
get_capitalist_item(pumpkin_juice, ArtisanQuality.basic, 1008),
get_capitalist_item(goat_cheese, ArtisanQuality.iridium, 1120),
get_capitalist_item(golden_egg, ArtisanQuality.iridium, 1200),
get_capitalist_item(dinosaur_mayo, ArtisanQuality.basic, 1120),
get_capitalist_item(truffle_oil, ArtisanQuality.basic, 1491),
get_capitalist_item(truffle, ArtisanQuality.iridium, 1250),
get_capitalist_item(aged_lava_eel_roe, ArtisanQuality.basic, 1064),
get_capitalist_item(aged_crimsonfish_roe, ArtisanQuality.basic, 2184),
get_capitalist_item(aged_angler_roe, ArtisanQuality.basic, 1344),
get_capitalist_item(legend_roe, ArtisanQuality.basic, 2530),
get_capitalist_item(aged_legend_roe, ArtisanQuality.basic, 7084),
get_capitalist_item(aged_glacierfish_roe, ArtisanQuality.basic, 1484),
get_capitalist_item(aged_mutant_carp_roe, ArtisanQuality.basic, 1484),
get_capitalist_item(iridium_bar, ArtisanQuality.basic, 1500),
get_capitalist_item(radioactive_bar, ArtisanQuality.basic, 4500),
get_capitalist_item(prismatic_shard, ArtisanQuality.basic, 2600),
get_capitalist_item(mystic_syrup, ArtisanQuality.basic, 1250),
# *get_capitalist_items(cherry_sapling, cherry_saplings),
# *get_capitalist_items(banana_sapling, banana_saplings),
# *get_capitalist_items(mango_sapling, mango_saplings),
# *get_capitalist_items(orange_sapling, orange_saplings),
# *get_capitalist_items(peach_sapling, peach_saplings),
# *get_capitalist_items(apple_sapling, apple_saplings),
# *get_capitalist_items(pomegranate_sapling, pomegranate_saplings),
bowler_hat,
sombrero,
]

View File

@@ -0,0 +1,5 @@
from .remixed_bundles import *
all_cc_remixed_bundles = [*crafts_room_bundles_remixed, *pantry_bundles_remixed, *fish_tank_bundles_remixed,
*boiler_room_bundles_remixed, *bulletin_board_bundles_remixed, *vault_bundles_remixed]
community_center_remixed_anywhere = BundleRoomTemplate("Community Center", all_cc_remixed_bundles, 30)

View File

@@ -0,0 +1,245 @@
from .thematic_bundles import *
from ...bundles.bundle import IslandBundleTemplate, FestivalBundleTemplate, CurrencyBundleTemplate
from ...bundles.bundle_room import BundleRoomTemplate
from ...content import content_packs
from ...strings.bundle_names import CCRoom
# Giant Stump
from ...strings.quality_names import ForageQuality, FishQuality
giant_stump_bundles_remixed = giant_stump_bundles_thematic
giant_stump_remixed = BundleRoomTemplate(CCRoom.raccoon_requests, giant_stump_bundles_remixed, 8)
# Crafts Room
beach_foraging_items = [nautilus_shell, coral, sea_urchin, rainbow_shell, clam, cockle, mussel, oyster, seaweed]
beach_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.beach_foraging, beach_foraging_items, 4, 4)
mines_foraging_items = [quartz, earth_crystal, frozen_tear, fire_quartz, red_mushroom, purple_mushroom, cave_carrot]
mines_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.mines_foraging, mines_foraging_items, 4, 4)
desert_foraging_items = [cactus_fruit.as_quality(ForageQuality.gold), cactus_fruit.as_amount(5), coconut.as_quality(ForageQuality.gold), coconut.as_amount(5)]
desert_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.desert_foraging, desert_foraging_items, 2, 2)
island_foraging_items = [ginger.as_amount(5), magma_cap.as_quality(ForageQuality.gold), magma_cap.as_amount(5),
fiddlehead_fern.as_quality(ForageQuality.gold), fiddlehead_fern.as_amount(5)]
island_foraging_bundle = IslandBundleTemplate(CCRoom.crafts_room, BundleName.island_foraging, island_foraging_items, 2, 2)
sticky_items = [sap.as_amount(500), sap.as_amount(500)]
sticky_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.sticky, sticky_items, 1, 1)
forest_items = [moss.as_amount(10), fiber.as_amount(200), acorn.as_amount(10), maple_seed.as_amount(10), pine_cone.as_amount(10), mahogany_seed,
mushroom_tree_seed, mossy_seed.as_amount(5), mystic_tree_seed]
forest_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.forest, forest_items, 4, 2)
wild_medicine_items = [item.as_amount(5) for item in [purple_mushroom, fiddlehead_fern, white_algae, hops, blackberry, dandelion]]
wild_medicine_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.wild_medicine, wild_medicine_items, 4, 3)
quality_foraging_items = sorted({item.as_quality(ForageQuality.gold).as_amount(3)
for item in
[*spring_foraging_items_thematic, *summer_foraging_items_thematic, *fall_foraging_items_thematic,
*winter_foraging_items_thematic, *beach_foraging_items, *desert_foraging_items, magma_cap] if item.can_have_quality})
quality_foraging_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.quality_foraging, quality_foraging_items, 4, 3)
green_rain_items = [moss.as_amount(200), fiber.as_amount(200), mossy_seed.as_amount(20), fiddlehead_fern.as_amount(10)]
green_rain_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.green_rain, green_rain_items, 4, 3)
totems_items = [warp_totem_beach.as_amount(5), warp_totem_mountains.as_amount(5), warp_totem_farm.as_amount(5), warp_totem_desert.as_amount(5),
warp_totem_island.as_amount(5), rain_totem.as_amount(5), treasure_totem.as_amount(5)]
totems_bundle = BundleTemplate(CCRoom.crafts_room, BundleName.totems, totems_items, 4, 3)
crafts_room_bundles_remixed = [*crafts_room_bundles_thematic, beach_foraging_bundle, mines_foraging_bundle, desert_foraging_bundle,
island_foraging_bundle, sticky_bundle, forest_bundle, wild_medicine_bundle, quality_foraging_bundle, green_rain_bundle]
crafts_room_remixed = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_remixed, 6)
# Pantry
rare_crops_items = [ancient_fruit, sweet_gem_berry]
rare_crops_bundle = BundleTemplate(CCRoom.pantry, BundleName.rare_crops, rare_crops_items, 2, 2)
# all_specific_roes = [BundleItem(AnimalProduct.roe, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fish]
fish_farmer_items = [roe.as_amount(15), aged_roe.as_amount(5), squid_ink, caviar.as_amount(5)]
fish_farmer_bundle = BundleTemplate(CCRoom.pantry, BundleName.fish_farmer, fish_farmer_items, 3, 2)
garden_items = [tulip, blue_jazz, summer_spangle, sunflower, fairy_rose, poppy, bouquet]
garden_bundle = BundleTemplate(CCRoom.pantry, BundleName.garden, garden_items, 5, 4)
brewer_items = [mead, pale_ale, wine, juice, green_tea, beer]
brewer_bundle = BundleTemplate(CCRoom.pantry, BundleName.brewer, brewer_items, 5, 4)
orchard_items = [apple, apricot, orange, peach, pomegranate, cherry, banana, mango]
orchard_bundle = BundleTemplate(CCRoom.pantry, BundleName.orchard, orchard_items, 6, 4)
island_crops_items = [pineapple, taro_root, banana, mango]
island_crops_bundle = IslandBundleTemplate(CCRoom.pantry, BundleName.island_crops, island_crops_items, 3, 3)
agronomist_items = [basic_fertilizer, quality_fertilizer, deluxe_fertilizer,
basic_retaining_soil, quality_retaining_soil, deluxe_retaining_soil,
speed_gro, deluxe_speed_gro, hyper_speed_gro, tree_fertilizer]
agronomist_bundle = BundleTemplate(CCRoom.pantry, BundleName.agronomist, agronomist_items, 4, 3)
slime_farmer_items = [slime.as_amount(99), petrified_slime.as_amount(10), blue_slime_egg, red_slime_egg,
purple_slime_egg, green_slime_egg, tiger_slime_egg]
slime_farmer_bundle = BundleTemplate(CCRoom.pantry, BundleName.slime_farmer, slime_farmer_items, 4, 3)
sommelier_items = [BundleItem(ArtisanGood.wine, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits]
sommelier_bundle = BundleTemplate(CCRoom.pantry, BundleName.sommelier, sommelier_items, 6, 3)
dry_items = [*[BundleItem(ArtisanGood.dried_fruit, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits],
*[BundleItem(ArtisanGood.dried_mushroom, flavor=mushroom, source=BundleItem.Sources.content) for mushroom in all_edible_mushrooms],
BundleItem(ArtisanGood.raisins, source=BundleItem.Sources.content)]
dry_bundle = BundleTemplate(CCRoom.pantry, BundleName.dry, dry_items, 6, 3)
pantry_bundles_remixed = [*pantry_bundles_thematic, rare_crops_bundle, fish_farmer_bundle, garden_bundle,
brewer_bundle, orchard_bundle, island_crops_bundle, agronomist_bundle, slime_farmer_bundle, sommelier_bundle, dry_bundle]
pantry_remixed = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_remixed, 6)
# Fish Tank
trash_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.trash, crab_pot_trash_items, 4, 4)
spring_fish_items = [herring, halibut, shad, flounder, sunfish, sardine, catfish, anchovy, smallmouth_bass, eel, legend]
spring_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.spring_fish, spring_fish_items, 4, 4)
summer_fish_items = [tuna, pike, red_mullet, sturgeon, red_snapper, super_cucumber, tilapia, pufferfish, rainbow_trout,
octopus, dorado, halibut, shad, flounder, sunfish, crimsonfish]
summer_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.summer_fish, summer_fish_items, 4, 4)
fall_fish_items = [red_snapper, super_cucumber, tilapia, shad, sardine, catfish, anchovy, smallmouth_bass, eel, midnight_carp,
walleye, sea_cucumber, tiger_trout, albacore, salmon, angler]
fall_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.fall_fish, fall_fish_items, 4, 4)
winter_fish_items = [perch, squid, lingcod, tuna, pike, red_mullet, sturgeon, red_snapper, herring, halibut, sardine,
midnight_carp, sea_cucumber, tiger_trout, albacore, glacierfish]
winter_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.winter_fish, winter_fish_items, 4, 4)
rain_fish_items = [red_snapper, shad, catfish, eel, walleye]
rain_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.rain_fish, rain_fish_items, 3, 3)
quality_fish_items = sorted({
item.as_quality(FishQuality.gold).as_amount(2)
for item in [*river_fish_items_thematic, *lake_fish_items_thematic, *ocean_fish_items_thematic]
})
quality_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.quality_fish, quality_fish_items, 4, 3)
master_fisher_items = [lava_eel, scorpion_carp, octopus, blobfish, lingcod, ice_pip, super_cucumber, stingray, void_salmon, pufferfish]
master_fisher_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.master_fisher, master_fisher_items, 4, 2)
legendary_fish_items = [angler, legend, mutant_carp, crimsonfish, glacierfish]
legendary_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.legendary_fish, legendary_fish_items, 4, 2)
island_fish_items = [lionfish, blue_discus, stingray]
island_fish_bundle = IslandBundleTemplate(CCRoom.fish_tank, BundleName.island_fish, island_fish_items, 3, 3)
tackle_items = [spinner, dressed_spinner, trap_bobber, sonar_bobber, cork_bobber, lead_bobber, treasure_hunter, barbed_hook, curiosity_lure, quality_bobber]
tackle_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.tackle, tackle_items, 3, 2)
bait_items = [bait, magnet, wild_bait, magic_bait, challenge_bait, deluxe_bait, targeted_bait]
bait_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.bait, bait_items, 3, 2)
# This bundle could change based on content packs, once the fish are properly in it. Until then, I'm not sure how, so pelican town only
specific_bait_items = [BundleItem(ArtisanGood.targeted_bait, flavor=fish.name).as_amount(10) for fish in content_packs.pelican_town.fishes]
specific_bait_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.specific_bait, specific_bait_items, 6, 3)
deep_fishing_items = [blobfish, spook_fish, midnight_squid, sea_cucumber, super_cucumber, octopus, pearl, seaweed]
deep_fishing_bundle = FestivalBundleTemplate(CCRoom.fish_tank, BundleName.deep_fishing, deep_fishing_items, 4, 3)
smokeable_fish = [Fish.largemouth_bass, Fish.bream, Fish.bullhead, Fish.chub, Fish.ghostfish, Fish.flounder, Fish.shad, Fish.rainbow_trout, Fish.tilapia,
Fish.red_mullet, Fish.tuna, Fish.midnight_carp, Fish.salmon, Fish.perch]
fish_smoker_items = [BundleItem(ArtisanGood.smoked_fish, flavor=fish) for fish in smokeable_fish]
fish_smoker_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.fish_smoker, fish_smoker_items, 6, 3)
fish_tank_bundles_remixed = [*fish_tank_bundles_thematic, spring_fish_bundle, summer_fish_bundle,
fall_fish_bundle, winter_fish_bundle, trash_bundle, rain_fish_bundle,
quality_fish_bundle, master_fisher_bundle, legendary_fish_bundle,
tackle_bundle, bait_bundle, specific_bait_bundle, deep_fishing_bundle,
fish_smoker_bundle]
# In Remixed, the trash items are in the recycling bundle, so we don't use the thematic version of the crab pot bundle that added trash items to it
fish_tank_bundles_remixed.remove(crab_pot_bundle_thematic)
fish_tank_bundles_remixed.append(crab_pot_bundle_vanilla)
fish_tank_remixed = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_remixed, 6)
# Boiler Room
# Where to put radioactive bar?
treasure_hunter_items = [emerald, aquamarine, ruby, amethyst, topaz, jade, diamond]
treasure_hunter_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.treasure_hunter, treasure_hunter_items, 6, 5)
engineer_items = [iridium_ore.as_amount(5), battery_pack, refined_quartz.as_amount(5), diamond]
engineer_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.engineer, engineer_items, 3, 3)
demolition_items = [cherry_bomb, bomb, mega_bomb, explosive_ammo]
demolition_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.demolition, demolition_items, 3, 3)
recycling_items = [stone, coal, iron_ore, wood, cloth, refined_quartz]
recycling_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.recycling, recycling_items, 4, 4)
archaeologist_items = [golden_mask, golden_relic, ancient_drum, dwarf_gadget, dwarvish_helm, prehistoric_handaxe, bone_flute, anchor, prehistoric_tool,
chicken_statue, rusty_cog, rusty_spur, rusty_spoon, ancient_sword, ornamental_fan, elvish_jewelry, ancient_doll, chipped_amphora]
archaeologist_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.archaeologist, archaeologist_items, 6, 3)
paleontologist_items = [prehistoric_scapula, prehistoric_tibia, prehistoric_skull, skeletal_hand, prehistoric_rib, prehistoric_vertebra, skeletal_tail,
nautilus_fossil, amphibian_fossil, palm_fossil, trilobite]
paleontologist_bundle = BundleTemplate(CCRoom.boiler_room, BundleName.paleontologist, paleontologist_items, 6, 3)
boiler_room_bundles_remixed = [*boiler_room_bundles_thematic, treasure_hunter_bundle, engineer_bundle,
demolition_bundle, recycling_bundle, archaeologist_bundle, paleontologist_bundle]
boiler_room_remixed = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_remixed, 3)
# Bulletin Board
children_items = [salmonberry.as_amount(10), cookie, ancient_doll, ice_cream, cranberry_candy, ginger_ale,
grape.as_amount(10), pink_cake, snail, fairy_rose, plum_pudding]
children_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.children, children_items, 4, 3)
forager_items = [salmonberry.as_amount(50), blackberry.as_amount(50), wild_plum.as_amount(20), snow_yam.as_amount(20),
common_mushroom.as_amount(20), grape.as_amount(20), spring_onion.as_amount(20)]
forager_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.forager, forager_items, 3, 2)
home_cook_items = [egg.as_amount(10), milk.as_amount(10), wheat_flour.as_amount(100), sugar.as_amount(100), vinegar.as_amount(100),
chocolate_cake, pancakes, rhubarb_pie]
home_cook_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.home_cook, home_cook_items, 3, 3)
helper_items = [prize_ticket, mystery_box.as_amount(5), gold_mystery_box]
helper_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.helper, helper_items, 2, 2)
spirit_eve_items = [jack_o_lantern, corn.as_amount(10), bat_wing.as_amount(10)]
spirit_eve_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.spirit_eve, spirit_eve_items, 3, 3)
winter_star_items = [holly.as_amount(5), plum_pudding, stuffing, powdermelon.as_amount(5)]
winter_star_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.winter_star, winter_star_items, 2, 2)
bartender_items = [shrimp_cocktail, triple_shot_espresso, ginger_ale, cranberry_candy, beer, pale_ale, pina_colada]
bartender_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.bartender, bartender_items, 3, 3)
calico_items = [calico_egg.as_amount(200), calico_egg.as_amount(200), calico_egg.as_amount(200), calico_egg.as_amount(200),
magic_rock_candy, mega_bomb.as_amount(10), mystery_box.as_amount(10), mixed_seeds.as_amount(50),
strawberry_seeds.as_amount(20),
spicy_eel.as_amount(5), crab_cakes.as_amount(5), eggplant_parmesan.as_amount(5),
pumpkin_soup.as_amount(5), lucky_lunch.as_amount(5) ]
calico_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.calico, calico_items, 2, 2)
raccoon_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.raccoon, raccoon_foraging_items, 4, 4)
bulletin_board_bundles_remixed = [*bulletin_board_bundles_thematic, children_bundle, forager_bundle, home_cook_bundle,
helper_bundle, spirit_eve_bundle, winter_star_bundle, bartender_bundle, calico_bundle, raccoon_bundle]
bulletin_board_remixed = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_remixed, 5)
# Abandoned Joja Mart
abandoned_joja_mart_remixed = abandoned_joja_mart_thematic
# Vault
vault_gambler_items = BundleItem(Currency.qi_coin, 10000)
vault_gambler_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.gambler, vault_gambler_items)
vault_carnival_items = BundleItem(Currency.star_token, 2500, source=BundleItem.Sources.festival)
vault_carnival_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.carnival, vault_carnival_items)
vault_walnut_hunter_items = BundleItem(Currency.golden_walnut, 25)
vault_walnut_hunter_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.walnut_hunter, vault_walnut_hunter_items)
vault_qi_helper_items = BundleItem(Currency.qi_gem, 25, source=BundleItem.Sources.island)
vault_qi_helper_bundle = CurrencyBundleTemplate(CCRoom.vault, BundleName.qi_helper, vault_qi_helper_items)
vault_bundles_remixed = [*vault_bundles_vanilla, vault_gambler_bundle, vault_qi_helper_bundle, vault_carnival_bundle] # , vault_walnut_hunter_bundle
vault_remixed = BundleRoomTemplate(CCRoom.vault, vault_bundles_remixed, 4)

View File

@@ -0,0 +1,158 @@
from .vanilla_bundles import *
from ...bundles.bundle import BundleTemplate
from ...bundles.bundle_room import BundleRoomTemplate
from ...strings.bundle_names import CCRoom, BundleName
# Giant Stump
from ...strings.quality_names import ArtisanQuality, FishQuality
raccoon_fish_items_flat = [*raccoon_crab_pot_fish_items, *raccoon_smoked_fish_items]
raccoon_fish_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_fish, raccoon_fish_items_flat, 3, 2)
raccoon_artisan_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_artisan, raccoon_artisan_items, 3, 2)
raccoon_food_items_thematic = [*all_specific_dried_mushrooms, *raccoon_food_items, brown_egg.as_amount(5), large_egg.as_amount(2), large_brown_egg.as_amount(2),
green_algae.as_amount(10)]
raccoon_food_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_food, raccoon_food_items_thematic, 3, 2)
raccoon_foraging_bundle_thematic = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_foraging, raccoon_foraging_items, 3, 2)
giant_stump_bundles_thematic = [raccoon_fish_bundle_thematic, raccoon_artisan_bundle_thematic, raccoon_food_bundle_thematic, raccoon_foraging_bundle_thematic]
giant_stump_thematic = BundleRoomTemplate(CCRoom.raccoon_requests, giant_stump_bundles_thematic, 8)
# Crafts Room
spring_foraging_items_thematic = [*spring_foraging_items_vanilla, spring_onion, salmonberry, morel]
spring_foraging_bundle_thematic = BundleTemplate.extend_from(spring_foraging_bundle_vanilla, spring_foraging_items_thematic)
summer_foraging_items_thematic = [*summer_foraging_items_vanilla, fiddlehead_fern, red_mushroom, rainbow_shell]
summer_foraging_bundle_thematic = BundleTemplate.extend_from(summer_foraging_bundle_vanilla, summer_foraging_items_thematic)
fall_foraging_items_thematic = [*fall_foraging_items_vanilla, chanterelle]
fall_foraging_bundle_thematic = BundleTemplate.extend_from(fall_foraging_bundle_vanilla, fall_foraging_items_thematic)
winter_foraging_items_thematic = [*winter_foraging_items_vanilla, holly, nautilus_shell]
winter_foraging_bundle_thematic = BundleTemplate.extend_from(winter_foraging_bundle_vanilla, winter_foraging_items_thematic)
construction_items_thematic = [*construction_items_vanilla, clay.as_amount(10), fiber.as_amount(99), sap.as_amount(50)]
construction_bundle_thematic = BundleTemplate.extend_from(construction_bundle_vanilla, construction_items_thematic)
exotic_foraging_items_thematic = [*exotic_foraging_items_vanilla, coral, sea_urchin, clam, cockle, mussel, oyster, seaweed]
exotic_foraging_bundle_thematic = BundleTemplate.extend_from(exotic_foraging_bundle_vanilla, exotic_foraging_items_thematic)
crafts_room_bundles_thematic = [spring_foraging_bundle_thematic, summer_foraging_bundle_thematic, fall_foraging_bundle_thematic,
winter_foraging_bundle_thematic, construction_bundle_thematic, exotic_foraging_bundle_thematic]
crafts_room_thematic = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_thematic, 6)
# Pantry
spring_crops_items_thematic = [*spring_crops_items_vanilla, blue_jazz, coffee_bean, garlic, kale, rhubarb, strawberry, tulip, unmilled_rice, carrot]
spring_crops_bundle_thematic = BundleTemplate.extend_from(spring_crops_bundle_vanilla, spring_crops_items_thematic)
summer_crops_items_thematic = [*summer_crops_items_vanilla, corn, hops, poppy, radish, red_cabbage, starfruit, summer_spangle, sunflower, wheat, summer_squash]
summer_crops_bundle_thematic = BundleTemplate.extend_from(summer_crops_bundle_vanilla, summer_crops_items_thematic)
fall_crops_items_thematic = [*fall_crops_items_vanilla, amaranth, artichoke, beet, bok_choy, cranberries, fairy_rose, grape,
sunflower, wheat, sweet_gem_berry, broccoli]
fall_crops_bundle_thematic = BundleTemplate.extend_from(fall_crops_bundle_vanilla, fall_crops_items_thematic)
all_crops_items = sorted({*spring_crops_items_thematic, *summer_crops_items_thematic, *fall_crops_items_thematic, powdermelon})
quality_crops_items_thematic = [item.as_quality_crop() for item in all_crops_items]
quality_crops_bundle_thematic = BundleTemplate.extend_from(quality_crops_bundle_vanilla, quality_crops_items_thematic)
animal_items_thematic = [*animal_items_vanilla, egg, brown_egg, milk, goat_milk, truffle,
duck_feather, rabbit_foot, dinosaur_egg, void_egg, golden_egg, ostrich_egg]
animal_bundle_thematic = BundleTemplate.extend_from(animal_bundle_vanilla, animal_items_thematic)
artisan_items_thematic = [*artisan_items_vanilla, beer, juice, mead, pale_ale, wine, pickles, caviar, aged_roe, coffee, green_tea, banana, mango]
artisan_bundle_thematic = BundleTemplate.extend_from(artisan_bundle_vanilla, artisan_items_thematic)
pantry_bundles_thematic = [spring_crops_bundle_thematic, summer_crops_bundle_thematic, fall_crops_bundle_thematic,
quality_crops_bundle_thematic, animal_bundle_thematic, artisan_bundle_thematic]
pantry_thematic = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_thematic, 6)
# Fish Tank
river_fish_items_thematic = [*river_fish_items_vanilla, chub, rainbow_trout, lingcod, walleye, perch, pike, bream, salmon, smallmouth_bass, dorado]
river_fish_bundle_thematic = BundleTemplate.extend_from(river_fish_bundle_vanilla, river_fish_items_thematic)
lake_fish_items_thematic = [*lake_fish_items_vanilla, chub, rainbow_trout, lingcod, walleye, perch, midnight_carp]
lake_fish_bundle_thematic = BundleTemplate.extend_from(lake_fish_bundle_vanilla, lake_fish_items_thematic)
ocean_fish_items_thematic = [*ocean_fish_items_vanilla, pufferfish, super_cucumber, flounder, anchovy, red_mullet,
herring, eel, octopus, squid, sea_cucumber, albacore, halibut]
ocean_fish_bundle_thematic = BundleTemplate.extend_from(ocean_fish_bundle_vanilla, ocean_fish_items_thematic)
night_fish_items_thematic = [*night_fish_items_vanilla, super_cucumber, squid, midnight_carp, midnight_squid]
night_fish_bundle_thematic = BundleTemplate.extend_from(night_fish_bundle_vanilla, night_fish_items_thematic)
crab_pot_items_thematic = [*crab_pot_items_vanilla, *crab_pot_trash_items]
crab_pot_bundle_thematic = BundleTemplate.extend_from(crab_pot_bundle_vanilla, crab_pot_items_thematic)
specialty_fish_items_thematic = [*specialty_fish_items_vanilla, scorpion_carp, eel, octopus, lava_eel, ice_pip,
stonefish, void_salmon, stingray, spookfish, midnight_squid, slimejack, goby]
specialty_fish_bundle_thematic = BundleTemplate.extend_from(specialty_fish_bundle_vanilla, specialty_fish_items_thematic)
fish_tank_bundles_thematic = [river_fish_bundle_thematic, lake_fish_bundle_thematic, ocean_fish_bundle_thematic,
night_fish_bundle_thematic, crab_pot_bundle_thematic, specialty_fish_bundle_thematic]
fish_tank_thematic = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_thematic, 6)
# Boiler Room
blacksmith_items_thematic = [*blacksmith_items_vanilla, iridium_bar, refined_quartz.as_amount(3), wilted_bouquet]
blacksmith_bundle_thematic = BundleTemplate.extend_from(blacksmith_bundle_vanilla, blacksmith_items_thematic)
geologist_items_thematic = [*geologist_items_vanilla, emerald, aquamarine, ruby, amethyst, topaz, jade, diamond]
geologist_bundle_thematic = BundleTemplate.extend_from(geologist_bundle_vanilla, geologist_items_thematic)
adventurer_items_thematic = [*adventurer_items_vanilla, bug_meat, coal.as_amount(5), bone_fragment.as_amount(10)]
adventurer_bundle_thematic = BundleTemplate.extend_from(adventurer_bundle_vanilla, adventurer_items_thematic)
boiler_room_bundles_thematic = [blacksmith_bundle_thematic, geologist_bundle_thematic, adventurer_bundle_thematic]
boiler_room_thematic = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_thematic, 3)
# Bulletin Board
# More recipes?
chef_items_thematic = [maki_roll, fried_egg, omelet, pizza, hashbrowns, pancakes, bread, tortilla,
farmer_s_lunch, survival_burger, dish_o_the_sea, miner_s_treat, roots_platter, salad,
cheese_cauliflower, parsnip_soup, fried_mushroom, salmon_dinner, pepper_poppers, spaghetti,
sashimi, blueberry_tart, algae_soup, pale_broth, chowder]
chef_bundle_thematic = BundleTemplate.extend_from(chef_bundle_vanilla, chef_items_thematic)
dye_red_items = [cranberries, hot_pepper, radish, rhubarb, spaghetti, strawberry, tomato, tulip, red_mushroom]
dye_orange_items = [poppy, pumpkin, apricot, orange, spice_berry, winter_root]
dye_yellow_items = [corn, parsnip, summer_spangle, sunflower, starfruit]
dye_green_items = [fiddlehead_fern, kale, artichoke, bok_choy, green_bean, cactus_fruit, duck_feather, dinosaur_egg]
dye_blue_items = [blueberry, blue_jazz, blackberry, crystal_fruit, aquamarine]
dye_purple_items = [beet, crocus, eggplant, red_cabbage, sweet_pea, iridium_bar, sea_urchin, amaranth]
dye_items_thematic = [dye_red_items, dye_orange_items, dye_yellow_items, dye_green_items, dye_blue_items, dye_purple_items]
dye_bundle_thematic = DeepBundleTemplate(CCRoom.bulletin_board, BundleName.dye, dye_items_thematic, 6, 6)
field_research_items_thematic = [*field_research_items_vanilla, geode, magma_geode, omni_geode,
rainbow_shell, amethyst, bream, carp]
field_research_bundle_thematic = BundleTemplate.extend_from(field_research_bundle_vanilla, field_research_items_thematic)
fodder_items_thematic = [*fodder_items_vanilla, kale.as_amount(3), corn.as_amount(3), green_bean.as_amount(3),
potato.as_amount(3), green_algae.as_amount(5), white_algae.as_amount(3)]
fodder_bundle_thematic = BundleTemplate.extend_from(fodder_bundle_vanilla, fodder_items_thematic)
enchanter_items_thematic = [*enchanter_items_vanilla, purple_mushroom, solar_essence,
super_cucumber, void_essence, fire_quartz, frozen_tear, jade]
enchanter_bundle_thematic = BundleTemplate.extend_from(enchanter_bundle_vanilla, enchanter_items_thematic)
bulletin_board_bundles_thematic = [chef_bundle_thematic, dye_bundle_thematic, field_research_bundle_thematic, fodder_bundle_thematic, enchanter_bundle_thematic]
bulletin_board_thematic = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_thematic, 5)
# Abandoned Joja Mart
missing_bundle_items_thematic = [*missing_bundle_items_vanilla, pale_ale.as_quality(ArtisanQuality.silver), beer.as_quality(ArtisanQuality.silver),
mead.as_quality(ArtisanQuality.silver),
cheese.as_quality(ArtisanQuality.silver), goat_cheese.as_quality(ArtisanQuality.silver), void_mayo, cloth, green_tea,
truffle_oil, diamond,
sweet_gem_berry.as_quality_crop(), starfruit.as_quality_crop(),
tea_leaves.as_amount(5), lava_eel.as_quality(FishQuality.gold), scorpion_carp.as_quality(FishQuality.gold),
blobfish.as_quality(FishQuality.gold)]
missing_bundle_thematic = BundleTemplate.extend_from(missing_bundle_vanilla, missing_bundle_items_thematic)
abandoned_joja_mart_bundles_thematic = [missing_bundle_thematic]
abandoned_joja_mart_thematic = BundleRoomTemplate(CCRoom.abandoned_joja_mart, abandoned_joja_mart_bundles_thematic, 1)
# Vault
vault_bundles_thematic = vault_bundles_vanilla
vault_thematic = BundleRoomTemplate(CCRoom.vault, vault_bundles_thematic, 4)

View File

@@ -0,0 +1,167 @@
from .bundle_items_data import *
from ...bundles.bundle import DeepBundleTemplate, BundleTemplate, MoneyBundleTemplate
from ...bundles.bundle_item import BundleItem
from ...bundles.bundle_room import BundleRoomTemplate
from ...content.vanilla.base import all_fruits, all_vegetables
from ...strings.artisan_good_names import ArtisanGood
from ...strings.bundle_names import CCRoom, BundleName
from ...strings.fish_names import Fish
from ...strings.forageable_names import all_edible_mushrooms
# Giant Stump
from ...strings.quality_names import ArtisanQuality, FishQuality
all_specific_jellies = [BundleItem(ArtisanGood.jelly, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits]
all_specific_pickles = [BundleItem(ArtisanGood.pickles, flavor=vegetable, source=BundleItem.Sources.content) for vegetable in all_vegetables]
all_specific_dried_fruits = [*[BundleItem(ArtisanGood.dried_fruit, flavor=fruit, source=BundleItem.Sources.content) for fruit in all_fruits],
BundleItem(ArtisanGood.raisins, source=BundleItem.Sources.content)]
all_specific_juices = [BundleItem(ArtisanGood.juice, flavor=vegetable, source=BundleItem.Sources.content) for vegetable in all_vegetables]
raccoon_crab_pot_fish_items = [periwinkle.as_amount(5), snail.as_amount(5), crayfish.as_amount(5), mussel.as_amount(5),
oyster.as_amount(5), cockle.as_amount(5), clam.as_amount(5)]
raccoon_smoked_fish_items = [BundleItem(ArtisanGood.smoked_fish, flavor=fish) for fish in
[Fish.largemouth_bass, Fish.bream, Fish.bullhead, Fish.chub, Fish.ghostfish, Fish.flounder, Fish.shad,
Fish.rainbow_trout, Fish.tilapia, Fish.red_mullet, Fish.tuna, Fish.midnight_carp, Fish.salmon, Fish.perch]]
raccoon_artisan_items = [*all_specific_jellies, *all_specific_pickles, *all_specific_dried_fruits, *all_specific_juices]
raccoon_fish_items_deep = [raccoon_crab_pot_fish_items, raccoon_smoked_fish_items]
all_specific_dried_mushrooms = [BundleItem(ArtisanGood.dried_mushroom, flavor=mushroom, source=BundleItem.Sources.content) for mushroom in all_edible_mushrooms]
raccoon_food_items = [egg.as_amount(5), cave_carrot.as_amount(5), white_algae.as_amount(5)]
raccoon_foraging_items = [moss.as_amount(10), rusty_spoon, trash.as_amount(5), slime.as_amount(99), bat_wing.as_amount(10), geode.as_amount(8),
frozen_geode.as_amount(5), magma_geode.as_amount(3), coral.as_amount(4), sea_urchin.as_amount(2), bug_meat.as_amount(10),
diamond, topaz.as_amount(3), ghostfish.as_amount(3)]
raccoon_fish_bundle_vanilla = DeepBundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_fish, raccoon_fish_items_deep, 2, 2)
raccoon_artisan_bundle_vanilla = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_artisan, raccoon_artisan_items, 2, 2)
raccoon_food_items_vanilla = [all_specific_dried_mushrooms, raccoon_food_items]
raccoon_food_bundle_vanilla = DeepBundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_food, raccoon_food_items_vanilla, 2, 2)
raccoon_foraging_bundle_vanilla = BundleTemplate(CCRoom.raccoon_requests, BundleName.raccoon_foraging, raccoon_foraging_items, 2, 2)
giant_stump_bundles_vanilla = [raccoon_fish_bundle_vanilla, raccoon_artisan_bundle_vanilla, raccoon_food_bundle_vanilla, raccoon_foraging_bundle_vanilla]
giant_stump_vanilla = BundleRoomTemplate(CCRoom.raccoon_requests, giant_stump_bundles_vanilla, 8)
# Crafts Room
spring_foraging_items_vanilla = [wild_horseradish, daffodil, leek, dandelion]
spring_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.spring_foraging, spring_foraging_items_vanilla, 4, 4)
summer_foraging_items_vanilla = [grape, spice_berry, sweet_pea]
summer_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.summer_foraging, summer_foraging_items_vanilla, 3, 3)
fall_foraging_items_vanilla = [common_mushroom, wild_plum, hazelnut, blackberry]
fall_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.fall_foraging, fall_foraging_items_vanilla, 4, 4)
winter_foraging_items_vanilla = [winter_root, crystal_fruit, snow_yam, crocus]
winter_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.winter_foraging, winter_foraging_items_vanilla, 4, 4)
construction_items_vanilla = [wood, stone, hardwood]
construction_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.construction, construction_items_vanilla, 4, 4)
exotic_foraging_items_vanilla = [coconut, cactus_fruit, cave_carrot, red_mushroom, purple_mushroom, maple_syrup, oak_resin, pine_tar, morel]
exotic_foraging_bundle_vanilla = BundleTemplate(CCRoom.crafts_room, BundleName.exotic_foraging, exotic_foraging_items_vanilla, 9, 5)
crafts_room_bundles_vanilla = [spring_foraging_bundle_vanilla, summer_foraging_bundle_vanilla, fall_foraging_bundle_vanilla,
winter_foraging_bundle_vanilla, construction_bundle_vanilla, exotic_foraging_bundle_vanilla]
crafts_room_vanilla = BundleRoomTemplate(CCRoom.crafts_room, crafts_room_bundles_vanilla, 6)
# Pantry
spring_crops_items_vanilla = [parsnip, green_bean, cauliflower, potato]
spring_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.spring_crops, spring_crops_items_vanilla, 4, 4)
summer_crops_items_vanilla = [tomato, hot_pepper, blueberry, melon]
summer_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.summer_crops, summer_crops_items_vanilla, 4, 4)
fall_crops_items_vanilla = [corn, eggplant, pumpkin, yam]
fall_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.fall_crops, fall_crops_items_vanilla, 4, 4)
quality_crops_items_vanilla = [item.as_quality_crop() for item in [parsnip, melon, pumpkin, corn]]
quality_crops_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.quality_crops, quality_crops_items_vanilla, 4, 3)
animal_items_vanilla = [large_milk, large_brown_egg, large_egg, large_goat_milk, wool, duck_egg]
animal_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.animal, animal_items_vanilla, 6, 5)
artisan_items_vanilla = [truffle_oil, cloth, goat_cheese, cheese, honey, jelly, apple, apricot, orange, peach, pomegranate, cherry]
artisan_bundle_vanilla = BundleTemplate(CCRoom.pantry, BundleName.artisan, artisan_items_vanilla, 12, 6)
pantry_bundles_vanilla = [spring_crops_bundle_vanilla, summer_crops_bundle_vanilla, fall_crops_bundle_vanilla,
quality_crops_bundle_vanilla, animal_bundle_vanilla, artisan_bundle_vanilla]
pantry_vanilla = BundleRoomTemplate(CCRoom.pantry, pantry_bundles_vanilla, 6)
# Fish Tank
river_fish_items_vanilla = [sunfish, catfish, shad, tiger_trout]
river_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.river_fish, river_fish_items_vanilla, 4, 4)
lake_fish_items_vanilla = [largemouth_bass, carp, bullhead, sturgeon]
lake_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.lake_fish, lake_fish_items_vanilla, 4, 4)
ocean_fish_items_vanilla = [sardine, tuna, red_snapper, tilapia]
ocean_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.ocean_fish, ocean_fish_items_vanilla, 4, 4)
night_fish_items_vanilla = [walleye, bream, eel]
night_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.night_fish, night_fish_items_vanilla, 3, 3)
crab_pot_items_vanilla = [lobster, crayfish, crab, cockle, mussel, shrimp, snail, periwinkle, oyster, clam]
crab_pot_trash_items = [trash, driftwood, soggy_newspaper, broken_cd, broken_glasses]
crab_pot_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.crab_pot, crab_pot_items_vanilla, 10, 5)
specialty_fish_items_vanilla = [pufferfish, ghostfish, sandfish, woodskip]
specialty_fish_bundle_vanilla = BundleTemplate(CCRoom.fish_tank, BundleName.specialty_fish, specialty_fish_items_vanilla, 4, 4)
fish_tank_bundles_vanilla = [river_fish_bundle_vanilla, lake_fish_bundle_vanilla, ocean_fish_bundle_vanilla,
night_fish_bundle_vanilla, crab_pot_bundle_vanilla, specialty_fish_bundle_vanilla]
fish_tank_vanilla = BundleRoomTemplate(CCRoom.fish_tank, fish_tank_bundles_vanilla, 6)
# Boiler Room
blacksmith_items_vanilla = [copper_bar, iron_bar, gold_bar]
blacksmith_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.blacksmith, blacksmith_items_vanilla, 3, 3)
geologist_items_vanilla = [quartz, earth_crystal, frozen_tear, fire_quartz]
geologist_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.geologist, geologist_items_vanilla, 4, 4)
adventurer_items_vanilla = [slime, bat_wing, solar_essence, void_essence]
adventurer_bundle_vanilla = BundleTemplate(CCRoom.boiler_room, BundleName.adventurer, adventurer_items_vanilla, 4, 2)
boiler_room_bundles_vanilla = [blacksmith_bundle_vanilla, geologist_bundle_vanilla, adventurer_bundle_vanilla]
boiler_room_vanilla = BundleRoomTemplate(CCRoom.boiler_room, boiler_room_bundles_vanilla, 3)
# Bulletin Board
chef_items_vanilla = [maple_syrup, fiddlehead_fern, truffle, poppy, maki_roll, fried_egg]
chef_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.chef, chef_items_vanilla, 6, 6)
dye_items_vanilla = [red_mushroom, sea_urchin, sunflower, duck_feather, aquamarine, red_cabbage]
dye_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.dye, dye_items_vanilla, 6, 6)
field_research_items_vanilla = [purple_mushroom, nautilus_shell, chub, frozen_geode]
field_research_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.field_research, field_research_items_vanilla, 4, 4)
fodder_items_vanilla = [wheat.as_amount(10), hay.as_amount(10), apple.as_amount(3)]
fodder_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.fodder, fodder_items_vanilla, 3, 3)
enchanter_items_vanilla = [oak_resin, wine, rabbit_foot, pomegranate]
enchanter_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.enchanter, enchanter_items_vanilla, 4, 4)
bulletin_board_bundles_vanilla = [chef_bundle_vanilla, dye_bundle_vanilla, field_research_bundle_vanilla, fodder_bundle_vanilla, enchanter_bundle_vanilla]
bulletin_board_vanilla = BundleRoomTemplate(CCRoom.bulletin_board, bulletin_board_bundles_vanilla, 5)
# Abandoned Joja Mart
missing_bundle_items_vanilla = [wine.as_quality(ArtisanQuality.silver), dinosaur_mayo, prismatic_shard, caviar,
ancient_fruit.as_quality_crop(), void_salmon.as_quality(FishQuality.gold)]
missing_bundle_vanilla = BundleTemplate(CCRoom.abandoned_joja_mart, BundleName.missing_bundle, missing_bundle_items_vanilla, 6, 5)
abandoned_joja_mart_bundles_vanilla = [missing_bundle_vanilla]
abandoned_joja_mart_vanilla = BundleRoomTemplate(CCRoom.abandoned_joja_mart, abandoned_joja_mart_bundles_vanilla, 1)
# Vault
vault_2500_gold = BundleItem.money_bundle(2500)
vault_5000_gold = BundleItem.money_bundle(5000)
vault_10000_gold = BundleItem.money_bundle(10000)
vault_25000_gold = BundleItem.money_bundle(25000)
vault_2500_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_2500, vault_2500_gold)
vault_5000_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_5000, vault_5000_gold)
vault_10000_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_10000, vault_10000_gold)
vault_25000_bundle = MoneyBundleTemplate(CCRoom.vault, BundleName.money_25000, vault_25000_gold)
vault_bundles_vanilla = [vault_2500_bundle, vault_5000_bundle, vault_10000_bundle, vault_25000_bundle]
vault_vanilla = BundleRoomTemplate(CCRoom.vault, vault_bundles_vanilla, 4)

View File

@@ -1,7 +1,8 @@
from typing import Dict, List, Optional
from typing import Dict, List
from .recipe_source import RecipeSource, StarterSource, QueenOfSauceSource, ShopSource, SkillSource, FriendshipSource, ShopTradeSource, CutsceneSource, \
ArchipelagoSource, LogicSource, SpecialOrderSource, FestivalShopSource, QuestSource, MasterySource, SkillCraftsanitySource
ArchipelagoSource, LogicSource, SpecialOrderSource, FestivalShopSource, QuestSource, MasterySource, SkillCraftsanitySource, ShopWithKnownRecipeSource
from ..content.content_packs import ginger_island_content_pack
from ..mods.mod_data import ModNames
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
@@ -33,13 +34,13 @@ class CraftingRecipe:
item: str
ingredients: Dict[str, int]
source: RecipeSource
mod_name: Optional[str]
content_pack: frozenset[str] | None
def __init__(self, item: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None):
def __init__(self, item: str, ingredients: Dict[str, int], source: RecipeSource, content_pack: frozenset[str] | None = None):
self.item = item
self.ingredients = ingredients
self.source = source
self.mod_name = mod_name
self.content_pack = content_pack
def __repr__(self):
return f"{self.item} (Source: {self.source} |" \
@@ -49,9 +50,9 @@ class CraftingRecipe:
all_crafting_recipes: List[CraftingRecipe] = []
def friendship_recipe(name: str, friend: str, hearts: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
def friendship_recipe(name: str, friend: str, hearts: int, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = FriendshipSource(friend, hearts)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def cutscene_recipe(name: str, region: str, friend: str, hearts: int, ingredients: Dict[str, int]) -> CraftingRecipe:
@@ -59,24 +60,29 @@ def cutscene_recipe(name: str, region: str, friend: str, hearts: int, ingredient
return create_recipe(name, ingredients, source)
def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = SkillSource(skill, level)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def skill_craftsanity_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
def skill_craftsanity_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = SkillCraftsanitySource(skill, level)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def mastery_recipe(name: str, skill: str, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
def mastery_recipe(name: str, skill: str, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = MasterySource(skill)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = ShopSource(region, price)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def shop_with_known_recipe_recipe(name: str, region: str, price: int, recipe_requirement: str, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = ShopWithKnownRecipeSource(region, price, recipe_requirement)
return create_recipe(name, ingredients, source, content_pack)
def festival_shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int]) -> CraftingRecipe:
@@ -84,9 +90,9 @@ def festival_shop_recipe(name: str, region: str, price: int, ingredients: Dict[s
return create_recipe(name, ingredients, source)
def shop_trade_recipe(name: str, region: str, currency: str, price: int, ingredients: Dict[str, int]) -> CraftingRecipe:
def shop_trade_recipe(name: str, region: str, currency: str, price: int, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = ShopTradeSource(region, currency, price)
return create_recipe(name, ingredients, source)
return create_recipe(name, ingredients, source, content_pack)
def queen_of_sauce_recipe(name: str, year: int, season: str, day: int, ingredients: Dict[str, int]) -> CraftingRecipe:
@@ -94,14 +100,14 @@ def queen_of_sauce_recipe(name: str, year: int, season: str, day: int, ingredien
return create_recipe(name, ingredients, source)
def quest_recipe(name: str, quest: str, ingredients: Dict[str, int]) -> CraftingRecipe:
def quest_recipe(name: str, quest: str, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = QuestSource(quest)
return create_recipe(name, ingredients, source)
return create_recipe(name, ingredients, source, content_pack)
def special_order_recipe(name: str, special_order: str, ingredients: Dict[str, int]) -> CraftingRecipe:
def special_order_recipe(name: str, special_order: str, ingredients: Dict[str, int], content_pack: List[str] | str | None = None) -> CraftingRecipe:
source = SpecialOrderSource(special_order)
return create_recipe(name, ingredients, source)
return create_recipe(name, ingredients, source, content_pack)
def starter_recipe(name: str, ingredients: Dict[str, int]) -> CraftingRecipe:
@@ -109,11 +115,11 @@ def starter_recipe(name: str, ingredients: Dict[str, int]) -> CraftingRecipe:
return create_recipe(name, ingredients, source)
def ap_recipe(name: str, ingredients: Dict[str, int], ap_item: str = None) -> CraftingRecipe:
def ap_recipe(name: str, ingredients: Dict[str, int], ap_item: str = None, content_pack: List[str] | str | None = None) -> CraftingRecipe:
if ap_item is None:
ap_item = f"{name} Recipe"
source = ArchipelagoSource(ap_item)
return create_recipe(name, ingredients, source)
return create_recipe(name, ingredients, source, content_pack)
def cellar_recipe(name: str, ingredients: Dict[str, int]) -> CraftingRecipe:
@@ -121,8 +127,12 @@ def cellar_recipe(name: str, ingredients: Dict[str, int]) -> CraftingRecipe:
return create_recipe(name, ingredients, source)
def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None) -> CraftingRecipe:
recipe = CraftingRecipe(name, ingredients, source, mod_name)
def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource, content_pack: List[str] | str | None = None) -> CraftingRecipe:
if content_pack is None:
content_pack = []
if isinstance(content_pack, str):
content_pack = [content_pack]
recipe = CraftingRecipe(name, ingredients, source, frozenset(content_pack))
all_crafting_recipes.append(recipe)
return recipe
@@ -161,11 +171,11 @@ deluxe_fertilizer = ap_recipe(Fertilizer.deluxe, {MetalBar.iridium: 1, Material.
basic_speed_gro = skill_recipe(SpeedGro.basic, Skill.farming, 3, {ArtisanGood.pine_tar: 1, Material.moss: 5})
deluxe_speed_gro = skill_recipe(SpeedGro.deluxe, Skill.farming, 8, {ArtisanGood.oak_resin: 1, Fossil.bone_fragment: 5})
hyper_speed_gro = ap_recipe(SpeedGro.hyper, {Ore.radioactive: 1, Fossil.bone_fragment: 3, Loot.solar_essence: 1})
hyper_speed_gro = ap_recipe(SpeedGro.hyper, {Ore.radioactive: 1, Fossil.bone_fragment: 3, Loot.solar_essence: 1}, content_pack=ginger_island_content_pack.name)
basic_retaining_soil = skill_recipe(RetainingSoil.basic, Skill.farming, 4, {Material.stone: 2})
quality_retaining_soil = skill_recipe(RetainingSoil.quality, Skill.farming, 7, {Material.stone: 3, Material.clay: 1})
deluxe_retaining_soil = shop_trade_recipe(RetainingSoil.deluxe, Region.island_trader, Currency.cinder_shard, 50,
{Material.stone: 5, Material.fiber: 3, Material.clay: 1})
{Material.stone: 5, Material.fiber: 3, Material.clay: 1}, content_pack=ginger_island_content_pack.name)
tree_fertilizer = skill_recipe(Fertilizer.tree, Skill.foraging, 7, {Material.fiber: 5, Material.stone: 5})
spring_seeds = skill_recipe(WildSeeds.spring, Skill.foraging, 1,
@@ -176,7 +186,7 @@ winter_seeds = skill_recipe(WildSeeds.winter, Skill.foraging, 7,
{Forageable.winter_root: 1, Forageable.crystal_fruit: 1, Forageable.snow_yam: 1, Forageable.crocus: 1})
ancient_seeds = ap_recipe(WildSeeds.ancient, {Artifact.ancient_seed: 1})
grass_starter = shop_recipe(WildSeeds.grass_starter, Region.pierre_store, 1000, {Material.fiber: 10})
blue_grass_starter = ap_recipe(WildSeeds.blue_grass_starter, {Material.fiber: 25, Material.moss: 10, ArtisanGood.mystic_syrup: 1})
blue_grass_starter = ap_recipe(WildSeeds.blue_grass_starter, {Material.fiber: 25, Material.moss: 10, ArtisanGood.mystic_syrup: 1}, content_pack=ginger_island_content_pack.name)
for wild_seeds in [WildSeeds.spring, WildSeeds.summer, WildSeeds.fall, WildSeeds.winter]:
tea_sapling = cutscene_recipe(WildSeeds.tea_sapling, Region.sunroom, NPC.caroline, 2, {wild_seeds: 2, Material.fiber: 5, Material.wood: 5})
fiber_seeds = special_order_recipe(WildSeeds.fiber, SpecialOrder.community_cleanup, {Seed.mixed: 1, Material.sap: 5, Material.clay: 1})
@@ -207,13 +217,13 @@ magnet = skill_recipe(Fishing.magnet, Skill.fishing, 9, {MetalBar.iron: 1})
bait = skill_recipe(Fishing.bait, Skill.fishing, 2, {Loot.bug_meat: 1})
deluxe_bait = skill_recipe(Fishing.deluxe_bait, Skill.fishing, 4, {Fishing.bait: 5, Material.moss: 2})
wild_bait = cutscene_recipe(Fishing.wild_bait, Region.tent, NPC.linus, 4, {Material.fiber: 10, Loot.bug_meat: 5, Loot.slime: 5})
magic_bait = ap_recipe(Fishing.magic_bait, {Ore.radioactive: 1, Loot.bug_meat: 3})
magic_bait = ap_recipe(Fishing.magic_bait, {Ore.radioactive: 1, Loot.bug_meat: 3}, content_pack=ginger_island_content_pack.name)
crab_pot = skill_recipe(Machine.crab_pot, Skill.fishing, 3, {Material.wood: 40, MetalBar.iron: 3})
sturdy_ring = skill_recipe(Ring.sturdy_ring, Skill.combat, 1, {MetalBar.copper: 2, Loot.bug_meat: 25, Loot.slime: 25})
warrior_ring = skill_recipe(Ring.warrior_ring, Skill.combat, 4, {MetalBar.iron: 10, Material.coal: 25, Mineral.frozen_tear: 10})
ring_of_yoba = skill_recipe(Ring.ring_of_yoba, Skill.combat, 7, {MetalBar.gold: 5, MetalBar.iron: 5, Mineral.diamond: 1})
thorns_ring = skill_recipe(Ring.thorns_ring, Skill.combat, 7, {Fossil.bone_fragment: 50, Material.stone: 50, MetalBar.gold: 1})
thorns_ring = skill_recipe(Ring.thorns_ring, Skill.combat, 7, {Fossil.bone_fragment: 50, Material.stone: 50, MetalBar.gold: 1}, content_pack=ginger_island_content_pack.name)
glowstone_ring = skill_recipe(Ring.glowstone_ring, Skill.mining, 4, {Loot.solar_essence: 5, MetalBar.iron: 5})
iridium_band = skill_recipe(Ring.iridium_band, Skill.combat, 9, {MetalBar.iridium: 5, Loot.solar_essence: 50, Loot.void_essence: 50})
wedding_ring = shop_recipe(Ring.wedding_ring, LogicRegion.traveling_cart, 500, {MetalBar.iridium: 5, Mineral.prismatic_shard: 1})
@@ -224,14 +234,14 @@ life_elixir = skill_recipe(Edible.life_elixir, Skill.combat, 2, {Mushroom.red: 1
oil_of_garlic = skill_recipe(Edible.oil_of_garlic, Skill.combat, 6, {Vegetable.garlic: 10, Ingredient.oil: 1})
monster_musk = special_order_recipe(Consumable.monster_musk, SpecialOrder.prismatic_jelly, {Loot.bat_wing: 30, Loot.slime: 30})
fairy_dust = quest_recipe(Consumable.fairy_dust, Quest.the_pirates_wife, {Mineral.diamond: 1, Flower.fairy_rose: 1})
fairy_dust = quest_recipe(Consumable.fairy_dust, Quest.the_pirates_wife, {Mineral.diamond: 1, Flower.fairy_rose: 1}, content_pack=ginger_island_content_pack.name)
warp_totem_beach = skill_recipe(Consumable.warp_totem_beach, Skill.foraging, 6, {Material.hardwood: 1, WaterItem.coral: 2, Material.fiber: 10})
warp_totem_mountains = skill_recipe(Consumable.warp_totem_mountains, Skill.foraging, 7, {Material.hardwood: 1, MetalBar.iron: 1, Material.stone: 25})
warp_totem_farm = skill_recipe(Consumable.warp_totem_farm, Skill.foraging, 8, {Material.hardwood: 1, ArtisanGood.honey: 1, Material.fiber: 20})
warp_totem_desert = shop_trade_recipe(Consumable.warp_totem_desert, Region.desert, MetalBar.iridium, 10,
{Material.hardwood: 2, Forageable.coconut: 1, Ore.iridium: 4})
warp_totem_island = shop_recipe(Consumable.warp_totem_island, Region.volcano_dwarf_shop, 10000,
{Material.hardwood: 5, Forageable.dragon_tooth: 1, Forageable.ginger: 1})
{Material.hardwood: 5, Forageable.dragon_tooth: 1, Forageable.ginger: 1}, content_pack=ginger_island_content_pack.name)
rain_totem = skill_recipe(Consumable.rain_totem, Skill.foraging, 9, {Material.hardwood: 1, ArtisanGood.truffle_oil: 1, ArtisanGood.pine_tar: 5})
torch = starter_recipe(Lighting.torch, {Material.wood: 1, Material.sap: 2})
@@ -259,14 +269,14 @@ crystalarium = skill_recipe(Machine.crystalarium, Skill.mining, 9, {Material.sto
furnace = skill_craftsanity_recipe(Machine.furnace, Skill.mining, 1, {Ore.copper: 20, Material.stone: 25})
geode_crusher = special_order_recipe(Machine.geode_crusher, SpecialOrder.cave_patrol, {MetalBar.gold: 2, Material.stone: 50, Mineral.diamond: 1})
mushroom_log = skill_recipe(Machine.mushroom_log, Skill.foraging, 4, {Material.hardwood: 10, Material.moss: 10})
heavy_tapper = ap_recipe(Machine.heavy_tapper, {Material.hardwood: 30, MetalBar.radioactive: 1})
heavy_tapper = ap_recipe(Machine.heavy_tapper, {Material.hardwood: 30, MetalBar.radioactive: 1}, content_pack=ginger_island_content_pack.name)
lightning_rod = skill_recipe(Machine.lightning_rod, Skill.foraging, 6, {MetalBar.iron: 1, MetalBar.quartz: 1, Loot.bat_wing: 5})
ostrich_incubator = ap_recipe(Machine.ostrich_incubator, {Fossil.bone_fragment: 50, Material.hardwood: 50, Currency.cinder_shard: 20})
ostrich_incubator = ap_recipe(Machine.ostrich_incubator, {Fossil.bone_fragment: 50, Material.hardwood: 50, Currency.cinder_shard: 20}, content_pack=ginger_island_content_pack.name)
recycling_machine = skill_recipe(Machine.recycling_machine, Skill.fishing, 4, {Material.wood: 25, Material.stone: 25, MetalBar.iron: 1})
seed_maker = skill_recipe(Machine.seed_maker, Skill.farming, 9, {Material.wood: 25, Material.coal: 10, MetalBar.gold: 1})
slime_egg_press = skill_recipe(Machine.slime_egg_press, Skill.combat, 6, {Material.coal: 25, Mineral.fire_quartz: 1, ArtisanGood.battery_pack: 1})
slime_incubator = skill_recipe(Machine.slime_incubator, Skill.combat, 8, {MetalBar.iridium: 2, Loot.slime: 100})
solar_panel = special_order_recipe(Machine.solar_panel, SpecialOrder.island_ingredients, {MetalBar.quartz: 10, MetalBar.iron: 5, MetalBar.gold: 5})
solar_panel = special_order_recipe(Machine.solar_panel, SpecialOrder.island_ingredients, {MetalBar.quartz: 10, MetalBar.iron: 5, MetalBar.gold: 5})#, content_pack=ginger_island_content_pack.name) # If set this as a ginger island only recipe, the rule for battery packs will fail. It does OR on lightning rod and solar panel, even when GI is off
tapper = skill_recipe(Machine.tapper, Skill.foraging, 4, {Material.wood: 40, MetalBar.copper: 2})
@@ -282,7 +292,7 @@ drum_block = cutscene_recipe(Furniture.drum_block, Region.carpenter, NPC.robin,
chest = starter_recipe(Storage.chest, {Material.wood: 50})
stone_chest = special_order_recipe(Storage.stone_chest, SpecialOrder.robins_resource_rush, {Material.stone: 50})
big_chest = shop_recipe(Storage.big_chest, Region.carpenter, 5000, {Material.wood: 120, MetalBar.copper: 2})
big_stone_chest = shop_recipe(Storage.big_stone_chest, LogicRegion.mines_dwarf_shop, 5000, {Material.stone: 250})
big_stone_chest = shop_with_known_recipe_recipe(Storage.big_stone_chest, LogicRegion.mines_dwarf_shop, 5000, Storage.stone_chest, {Material.stone: 250})
wood_sign = starter_recipe(Sign.wood, {Material.wood: 25})
stone_sign = starter_recipe(Sign.stone, {Material.stone: 25})
@@ -300,7 +310,7 @@ mini_jukebox = cutscene_recipe(Craftable.mini_jukebox, Region.saloon, NPC.gus, 5
mini_obelisk = special_order_recipe(Craftable.mini_obelisk, SpecialOrder.a_curious_substance, {Material.hardwood: 30, Loot.solar_essence: 20, MetalBar.gold: 3})
farm_computer = special_order_recipe(Craftable.farm_computer, SpecialOrder.aquatic_overpopulation,
{Artifact.dwarf_gadget: 1, ArtisanGood.battery_pack: 1, MetalBar.quartz: 10})
hopper = ap_recipe(Craftable.hopper, {Material.hardwood: 10, MetalBar.iridium: 1, MetalBar.radioactive: 1})
hopper = ap_recipe(Craftable.hopper, {Material.hardwood: 10, MetalBar.iridium: 1, MetalBar.radioactive: 1}, content_pack=ginger_island_content_pack.name)
cookout_kit = skill_recipe(Craftable.cookout_kit, Skill.foraging, 3, {Material.wood: 15, Material.fiber: 10, Material.coal: 3})
tent_kit = skill_recipe(Craftable.tent_kit, Skill.foraging, 8, {Material.hardwood: 10, Material.fiber: 25, ArtisanGood.cloth: 1})
@@ -312,81 +322,81 @@ mystic_tree_seed = mastery_recipe(TreeSeed.mystic, Skill.foraging, {TreeSeed.aco
treasure_totem = mastery_recipe(Consumable.treasure_totem, Skill.foraging, {Material.hardwood: 5, ArtisanGood.mystic_syrup: 1, Material.moss: 10})
challenge_bait = mastery_recipe(Fishing.challenge_bait, Skill.fishing, {Fossil.bone_fragment: 5, Material.moss: 2})
anvil = mastery_recipe(Machine.anvil, Skill.combat, {MetalBar.iron: 50})
mini_forge = mastery_recipe(Machine.mini_forge, Skill.combat, {Forageable.dragon_tooth: 5, MetalBar.iron: 10, MetalBar.gold: 10, MetalBar.iridium: 5})
mini_forge = mastery_recipe(Machine.mini_forge, Skill.combat, {Forageable.dragon_tooth: 5, MetalBar.iron: 10, MetalBar.gold: 10, MetalBar.iridium: 5}, content_pack=ginger_island_content_pack.name)
travel_charm = shop_recipe(ModCraftable.travel_core, Region.adventurer_guild, 250, {Loot.solar_essence: 1, Loot.void_essence: 1}, ModNames.magic)
travel_charm = shop_recipe(ModCraftable.travel_core, Region.adventurer_guild, 250, {Loot.solar_essence: 1, Loot.void_essence: 1}, content_pack=ModNames.magic)
preservation_chamber = skill_recipe(ModMachine.preservation_chamber, ModSkill.archaeology, 1,
{MetalBar.copper: 1, Material.wood: 15, ArtisanGood.oak_resin: 30},
{MetalBar.copper: 1, Material.wood: 15, ArtisanGood.oak_resin: 10},
ModNames.archaeology)
restoration_table = skill_recipe(ModMachine.restoration_table, ModSkill.archaeology, 1, {Material.wood: 15, MetalBar.copper: 1, MetalBar.iron: 1},
restoration_table = skill_recipe(ModMachine.restoration_table, ModSkill.archaeology, 1, {Material.wood: 25, MetalBar.quartz: 1, MetalBar.iron: 1},
ModNames.archaeology)
preservation_chamber_h = skill_recipe(ModMachine.hardwood_preservation_chamber, ModSkill.archaeology, 6, {MetalBar.copper: 1, Material.hardwood: 15,
ArtisanGood.oak_resin: 30}, ModNames.archaeology)
grinder = skill_recipe(ModMachine.grinder, ModSkill.archaeology, 2, {Artifact.rusty_cog: 10, MetalBar.iron: 5, ArtisanGood.battery_pack: 1},
ArtisanGood.oak_resin: 10}, content_pack=ModNames.archaeology)
grinder = skill_recipe(ModMachine.grinder, ModSkill.archaeology, 2, {Artifact.rusty_cog: 4, MetalBar.iron: 5, ArtisanGood.battery_pack: 1},
ModNames.archaeology)
ancient_battery = skill_recipe(ModMachine.ancient_battery, ModSkill.archaeology, 7, {Material.stone: 40, MetalBar.copper: 10, MetalBar.iron: 5},
ancient_battery = skill_recipe(ModMachine.ancient_battery, ModSkill.archaeology, 7, {Material.stone: 40, Material.clay: 10, MetalBar.iron: 5},
ModNames.archaeology)
glass_bazier = skill_recipe(ModCraftable.glass_brazier, ModSkill.archaeology, 4, {Artifact.glass_shards: 10}, ModNames.archaeology)
glass_path = skill_recipe(ModFloor.glass_path, ModSkill.archaeology, 3, {Artifact.glass_shards: 1}, ModNames.archaeology)
glass_fence = skill_recipe(ModCraftable.glass_fence, ModSkill.archaeology, 7, {Artifact.glass_shards: 5}, ModNames.archaeology)
bone_path = skill_recipe(ModFloor.bone_path, ModSkill.archaeology, 4, {Fossil.bone_fragment: 1}, ModNames.archaeology)
rust_path = skill_recipe(ModFloor.rusty_path, ModSkill.archaeology, 2, {ModTrash.rusty_scrap: 2}, ModNames.archaeology)
glass_brazier = skill_recipe(ModCraftable.glass_brazier, ModSkill.archaeology, 4, {Artifact.glass_shards: 10, Material.coal: 1, Material.fiber: 1}, content_pack=ModNames.archaeology)
glass_path = skill_recipe(ModFloor.glass_path, ModSkill.archaeology, 3, {Artifact.glass_shards: 2}, content_pack=ModNames.archaeology)
glass_fence = skill_recipe(ModCraftable.glass_fence, ModSkill.archaeology, 7, {Artifact.glass_shards: 2}, content_pack=ModNames.archaeology)
bone_path = skill_recipe(ModFloor.bone_path, ModSkill.archaeology, 4, {Fossil.bone_fragment: 2}, content_pack=ModNames.archaeology)
rust_path = skill_recipe(ModFloor.rusty_path, ModSkill.archaeology, 2, {ModTrash.rusty_scrap: 2}, content_pack=ModNames.archaeology)
rusty_brazier = skill_recipe(ModCraftable.rusty_brazier, ModSkill.archaeology, 3, {ModTrash.rusty_scrap: 10, Material.coal: 1, Material.fiber: 1},
ModNames.archaeology)
bone_fence = skill_recipe(ModCraftable.bone_fence, ModSkill.archaeology, 8, {Fossil.bone_fragment: 2}, ModNames.archaeology)
water_shifter = skill_recipe(ModCraftable.water_shifter, ModSkill.archaeology, 4, {Material.wood: 40, MetalBar.copper: 4}, ModNames.archaeology)
wooden_display = skill_recipe(ModCraftable.wooden_display, ModSkill.archaeology, 1, {Material.wood: 25}, ModNames.archaeology)
hardwood_display = skill_recipe(ModCraftable.hardwood_display, ModSkill.archaeology, 7, {Material.hardwood: 10}, ModNames.archaeology)
bone_fence = skill_recipe(ModCraftable.bone_fence, ModSkill.archaeology, 8, {Fossil.bone_fragment: 2}, content_pack=ModNames.archaeology)
water_sifter = skill_recipe(ModCraftable.water_sifter, ModSkill.archaeology, 8, {MetalBar.copper: 4, Material.fiber: 8}, content_pack=ModNames.archaeology)
wooden_display = skill_recipe(ModCraftable.wooden_display, ModSkill.archaeology, 1, {Material.wood: 25}, content_pack=ModNames.archaeology)
hardwood_display = skill_recipe(ModCraftable.hardwood_display, ModSkill.archaeology, 6, {Material.hardwood: 10}, content_pack=ModNames.archaeology)
lucky_ring = skill_recipe(Ring.lucky_ring, ModSkill.archaeology, 8, {Artifact.elvish_jewelry: 1, AnimalProduct.rabbit_foot: 5, Mineral.tigerseye: 1},
ModNames.archaeology)
volcano_totem = skill_recipe(ModConsumable.volcano_totem, ModSkill.archaeology, 9, {Material.cinder_shard: 5, Artifact.rare_disc: 1, Artifact.dwarf_gadget: 1},
ModNames.archaeology)
haste_elixir = shop_recipe(ModEdible.haste_elixir, SVERegion.alesia_shop, 35000, {Loot.void_essence: 35, ModLoot.void_soul: 5, Ingredient.sugar: 1,
Meal.spicy_eel: 1}, ModNames.sve)
Meal.spicy_eel: 1}, content_pack=ModNames.sve)
hero_elixir = shop_recipe(ModEdible.hero_elixir, SVERegion.isaac_shop, 65000, {ModLoot.void_pebble: 3, ModLoot.void_soul: 5, Ingredient.oil: 1,
Loot.slime: 10}, ModNames.sve)
Loot.slime: 10}, content_pack=ModNames.sve)
armor_elixir = shop_recipe(ModEdible.armor_elixir, SVERegion.alesia_shop, 50000, {Loot.solar_essence: 30, ModLoot.void_soul: 5, Ingredient.vinegar: 5,
Fossil.bone_fragment: 5}, ModNames.sve)
Fossil.bone_fragment: 5}, content_pack=ModNames.sve)
ginger_tincture = friendship_recipe(ModConsumable.ginger_tincture, ModNPC.goblin, 4, {DistantLandsForageable.brown_amanita: 1, Forageable.ginger: 5,
Material.cinder_shard: 1, DistantLandsForageable.swamp_herb: 1},
ModNames.distant_lands)
content_pack=[ModNames.distant_lands, ginger_island_content_pack.name])
neanderthal_skeleton = shop_recipe(ModCraftable.neanderthal_skeleton, LogicRegion.mines_dwarf_shop, 5000,
{ModFossil.neanderthal_skull: 1, ModFossil.neanderthal_ribs: 1, ModFossil.neanderthal_pelvis: 1,
ModFossil.neanderthal_limb_bones: 1,
MetalBar.iron: 5, Material.hardwood: 10}, ModNames.boarding_house)
MetalBar.iron: 5, Material.hardwood: 10}, content_pack=ModNames.boarding_house)
pterodactyl_skeleton_l = shop_recipe(ModCraftable.pterodactyl_skeleton_l, LogicRegion.mines_dwarf_shop, 5000,
{ModFossil.pterodactyl_phalange: 1, ModFossil.pterodactyl_skull: 1, ModFossil.pterodactyl_l_wing_bone: 1,
MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
MetalBar.iron: 10, Material.hardwood: 15}, content_pack=ModNames.boarding_house)
pterodactyl_skeleton_m = shop_recipe(ModCraftable.pterodactyl_skeleton_m, LogicRegion.mines_dwarf_shop, 5000,
{ModFossil.pterodactyl_phalange: 1, ModFossil.pterodactyl_vertebra: 1, ModFossil.pterodactyl_ribs: 1,
MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
MetalBar.iron: 10, Material.hardwood: 15}, content_pack=ModNames.boarding_house)
pterodactyl_skeleton_r = shop_recipe(ModCraftable.pterodactyl_skeleton_r, LogicRegion.mines_dwarf_shop, 5000,
{ModFossil.pterodactyl_phalange: 1, ModFossil.pterodactyl_claw: 1, ModFossil.pterodactyl_r_wing_bone: 1,
MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
MetalBar.iron: 10, Material.hardwood: 15}, content_pack=ModNames.boarding_house)
trex_skeleton_l = shop_recipe(ModCraftable.trex_skeleton_l, LogicRegion.mines_dwarf_shop, 5000,
{ModFossil.dinosaur_vertebra: 1, ModFossil.dinosaur_tooth: 1, ModFossil.dinosaur_skull: 1,
MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
MetalBar.iron: 10, Material.hardwood: 15}, content_pack=ModNames.boarding_house)
trex_skeleton_m = shop_recipe(ModCraftable.trex_skeleton_m, LogicRegion.mines_dwarf_shop, 5000,
{ModFossil.dinosaur_vertebra: 1, ModFossil.dinosaur_ribs: 1, ModFossil.dinosaur_claw: 1,
MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
MetalBar.iron: 10, Material.hardwood: 15}, content_pack=ModNames.boarding_house)
trex_skeleton_r = shop_recipe(ModCraftable.trex_skeleton_r, LogicRegion.mines_dwarf_shop, 5000,
{ModFossil.dinosaur_vertebra: 1, ModFossil.dinosaur_femur: 1, ModFossil.dinosaur_pelvis: 1,
MetalBar.iron: 10, Material.hardwood: 15}, ModNames.boarding_house)
MetalBar.iron: 10, Material.hardwood: 15}, content_pack=ModNames.boarding_house)
bouquet = skill_recipe(Gift.bouquet, ModSkill.socializing, 3, {Flower.tulip: 3}, ModNames.socializing_skill)
trash_bin = skill_recipe(ModMachine.trash_bin, ModSkill.binning, 2, {Material.stone: 30, MetalBar.iron: 2}, ModNames.binning_skill)
composter = skill_recipe(ModMachine.composter, ModSkill.binning, 4, {Material.wood: 70, Material.sap: 20, Material.fiber: 30}, ModNames.binning_skill)
recycling_bin = skill_recipe(ModMachine.recycling_bin, ModSkill.binning, 7, {MetalBar.iron: 3, Material.fiber: 10, MetalBar.gold: 2}, ModNames.binning_skill)
bouquet = skill_recipe(Gift.bouquet, ModSkill.socializing, 3, {Flower.tulip: 3}, content_pack=ModNames.socializing_skill)
trash_bin = skill_recipe(ModMachine.trash_bin, ModSkill.binning, 2, {Material.stone: 30, MetalBar.iron: 2}, content_pack=ModNames.binning_skill)
composter = skill_recipe(ModMachine.composter, ModSkill.binning, 4, {Material.wood: 70, Material.sap: 20, Material.fiber: 30}, content_pack=ModNames.binning_skill)
recycling_bin = skill_recipe(ModMachine.recycling_bin, ModSkill.binning, 7, {MetalBar.iron: 3, Material.fiber: 10, MetalBar.gold: 2}, content_pack=ModNames.binning_skill)
advanced_recycling_machine = skill_recipe(ModMachine.advanced_recycling_machine, ModSkill.binning, 9,
{MetalBar.iridium: 5, ArtisanGood.battery_pack: 2, MetalBar.quartz: 10}, ModNames.binning_skill)
{MetalBar.iridium: 5, ArtisanGood.battery_pack: 2, MetalBar.quartz: 10}, content_pack=ModNames.binning_skill)
coppper_slot_machine = skill_recipe(ModMachine.copper_slot_machine, ModSkill.luck, 2, {MetalBar.copper: 15, Material.stone: 1, Material.wood: 1,
Material.fiber: 1, Material.sap: 1, Loot.slime: 1,
Forageable.salmonberry: 1, Material.clay: 1, Trash.joja_cola: 1}, ModNames.luck_skill)
Forageable.salmonberry: 1, Material.clay: 1, Trash.joja_cola: 1}, content_pack=ModNames.luck_skill)
gold_slot_machine = skill_recipe(ModMachine.gold_slot_machine, ModSkill.luck, 4, {MetalBar.gold: 15, ModMachine.copper_slot_machine: 1}, ModNames.luck_skill)
iridium_slot_machine = skill_recipe(ModMachine.iridium_slot_machine, ModSkill.luck, 6, {MetalBar.iridium: 15, ModMachine.gold_slot_machine: 1}, ModNames.luck_skill)
radioactive_slot_machine = skill_recipe(ModMachine.radioactive_slot_machine, ModSkill.luck, 8, {MetalBar.radioactive: 15, ModMachine.iridium_slot_machine: 1}, ModNames.luck_skill)
gold_slot_machine = skill_recipe(ModMachine.gold_slot_machine, ModSkill.luck, 4, {MetalBar.gold: 15, ModMachine.copper_slot_machine: 1}, content_pack=ModNames.luck_skill)
iridium_slot_machine = skill_recipe(ModMachine.iridium_slot_machine, ModSkill.luck, 6, {MetalBar.iridium: 15, ModMachine.gold_slot_machine: 1}, content_pack=ModNames.luck_skill)
radioactive_slot_machine = skill_recipe(ModMachine.radioactive_slot_machine, ModSkill.luck, 8, {MetalBar.radioactive: 15, ModMachine.iridium_slot_machine: 1}, content_pack=ModNames.luck_skill)
all_crafting_recipes_by_name = {recipe.item: recipe for recipe in all_crafting_recipes}

View File

@@ -2,9 +2,18 @@ from dataclasses import dataclass
from typing import Tuple, Union, Optional
from . import season_data as season
from .game_item import Source
from ..mods.mod_data import ModNames
from ..strings.fish_names import Fish, SVEFish, DistantLandsFish
from ..strings.region_names import Region, SVERegion, LogicRegion
from ..strings.tool_names import FishingRod
@dataclass(frozen=True, kw_only=True)
class FishingSource(Source):
region: str
minimum_rod: str = FishingRod.training
fishing_level: int = 0
@dataclass(frozen=True)
@@ -15,6 +24,7 @@ class FishItem:
difficulty: int
legendary: bool
extended_family: bool
minimum_level: int
mod_name: Optional[str] = None
def __repr__(self):
@@ -55,11 +65,11 @@ vineyard = (SVERegion.blue_moon_vineyard,)
def create_fish(name: str, locations: Tuple[str, ...], seasons: Union[str, Tuple[str, ...]],
difficulty: int, legendary: bool = False, extended_family: bool = False, mod_name: Optional[str] = None) -> FishItem:
difficulty: int, legendary: bool = False, extended_family: bool = False, minimum_level: int = 0, mod_name: Optional[str] = None) -> FishItem:
if isinstance(seasons, str):
seasons = (seasons,)
fish_item = FishItem(name, locations, seasons, difficulty, legendary, extended_family, mod_name)
fish_item = FishItem(name, locations, seasons, difficulty, legendary, extended_family, minimum_level, mod_name)
return fish_item
@@ -118,16 +128,16 @@ blobfish = create_fish(Fish.blobfish, night_market, season.winter, 75)
midnight_squid = create_fish(Fish.midnight_squid, night_market, season.winter, 55)
spook_fish = create_fish(Fish.spook_fish, night_market, season.winter, 60)
angler = create_fish(Fish.angler, town_river, season.fall, 85, True, False)
crimsonfish = create_fish(Fish.crimsonfish, tide_pools, season.summer, 95, True, False)
glacierfish = create_fish(Fish.glacierfish, forest_river, season.winter, 100, True, False)
legend = create_fish(Fish.legend, mountain_lake, season.spring, 110, True, False)
angler = create_fish(Fish.angler, town_river, season.fall, 85, True, False, minimum_level=3)
crimsonfish = create_fish(Fish.crimsonfish, tide_pools, season.summer, 95, True, False, minimum_level=5)
glacierfish = create_fish(Fish.glacierfish, forest_river, season.winter, 100, True, False, minimum_level=6)
legend = create_fish(Fish.legend, mountain_lake, season.spring, 110, True, False, minimum_level=10)
mutant_carp = create_fish(Fish.mutant_carp, sewers, season.all_seasons, 80, True, False)
ms_angler = create_fish(Fish.ms_angler, town_river, season.fall, 85, True, True)
son_of_crimsonfish = create_fish(Fish.son_of_crimsonfish, tide_pools, season.summer, 95, True, True)
glacierfish_jr = create_fish(Fish.glacierfish_jr, forest_river, season.winter, 100, True, True)
legend_ii = create_fish(Fish.legend_ii, mountain_lake, season.spring, 110, True, True)
ms_angler = create_fish(Fish.ms_angler, town_river, season.fall, 85, True, True, minimum_level=3)
son_of_crimsonfish = create_fish(Fish.son_of_crimsonfish, tide_pools, season.summer, 95, True, True, minimum_level=5)
glacierfish_jr = create_fish(Fish.glacierfish_jr, forest_river, season.winter, 100, True, True, minimum_level=6)
legend_ii = create_fish(Fish.legend_ii, mountain_lake, season.spring, 110, True, True, minimum_level=10)
radioactive_carp = create_fish(Fish.radioactive_carp, sewers, season.all_seasons, 80, True, True)
baby_lunaloo = create_fish(SVEFish.baby_lunaloo, ginger_island_ocean, season.all_seasons, 15, mod_name=ModNames.sve)

View File

@@ -0,0 +1,50 @@
from typing import Dict
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
from ..strings.crop_names import Fruit
from ..strings.fish_names import Fish, WaterItem
from ..strings.food_names import Meal
from ..strings.forageable_names import Forageable
from ..strings.metal_names import Mineral, Ore
from ..strings.monster_drop_names import Loot
from ..strings.seed_names import Seed
# Some of these are commented out, because they shouldn't be used, because they cause a loop on themselves, even if the loop is one of many ways to complete the quest
# I don't know the correct architectural way to fix this. So in the meantime, obtaining these items from fish ponds is not in logic
# Dictionary of fish pond requests, in the format of Dict[fish_name, Dict[population, Dict[item_name, item_amount]]]
fish_pond_quests: Dict[str, Dict[int, Dict[str, int]]] = {
Fish.blobfish: {
1: {WaterItem.coral: 3, Mineral.frozen_tear: 2, WaterItem.sea_urchin: 2, },
3: {Seed.coffee: 5, ArtisanGood.mayonnaise: 1, Meal.pizza: 1, },
5: {Meal.cookie: 1, ArtisanGood.green_tea: 1, ArtisanGood.wine: 1, },
7: {Forageable.rainbow_shell: 1, Meal.rice_pudding: 1, },
},
# Fish.lava_eel: {
# 1: {Mineral.fire_quartz: 3, },
# 3: {"Basalt": 1, Mineral.diamond: 2, Artifact.dwarf_scroll_iii: 1, },
# 5: {Bomb.mega_bomb: 2, },
# 7: {MetalBar.iridium: 1, },
# },
Fish.lionfish: {
3: {Forageable.ginger: 3, Fruit.pineapple: 1, },
5: {Fruit.mango: 1, },
},
# Fish.octopus: {
# 3: {WaterItem.coral: 3, ArtisanGood.honey: 1, Fish.oyster: 1, MetalBar.quartz: 3, },
# 5: {Fossil.dried_starfish: 1, Mineral.emerald: 2, Geode.omni: 2, Mushroom.purple: 2, },
# 7: {ArtisanGood.green_tea: 1, },
# },
# Fish.super_cucumber: {
# 3: {WaterItem.coral: 3, ArtisanGood.honey: 1, Fish.oyster: 1, Trash.driftwood: 3, MetalBar.quartz: 3, },
# 5: {Fossil.dried_starfish: 1, Mineral.emerald: 2, Geode.omni: 2, Mushroom.purple: 2 },
# 7: {Mineral.diamond: 1, MetalBar.gold: 3, Ore.iridium: 1, ArtisanGood.jelly: 1, ArtisanGood.pickles: 1, WaterItem.sea_urchin: 2 },
# },
Fish.void_salmon: {
1: {Loot.void_essence: 5, },
3: {Loot.bat_wing: 10, },
5: {Mineral.diamond: 1, AnimalProduct.void_egg: 1, },
7: {Ore.iridium: 1, },
},
}

View File

@@ -2,10 +2,13 @@ import enum
from abc import ABC
from dataclasses import dataclass, field
from types import MappingProxyType
from typing import List, Iterable, Set, ClassVar, Tuple, Mapping, Callable, Any
from typing import Iterable, ClassVar, Mapping, Callable, TYPE_CHECKING
from ..stardew_rule.protocol import StardewRule
if TYPE_CHECKING:
from ..logic.logic import StardewLogic
DEFAULT_REQUIREMENT_TAGS = MappingProxyType({})
@@ -24,34 +27,47 @@ class ItemTag(enum.Enum):
BOOK = enum.auto()
BOOK_POWER = enum.auto()
BOOK_SKILL = enum.auto()
HAT = enum.auto()
FORAGE = enum.auto()
COOKING = enum.auto()
@dataclass(frozen=True)
class Source(ABC):
add_tags: ClassVar[Tuple[ItemTag]] = ()
add_tags: ClassVar[tuple[ItemTag]] = ()
other_requirements: Tuple[Requirement, ...] = field(kw_only=True, default_factory=tuple)
other_requirements: tuple[Requirement, ...] = field(kw_only=True, default=())
@property
def requirement_tags(self) -> Mapping[str, Tuple[ItemTag, ...]]:
def requirement_tags(self) -> Mapping[str, tuple[ItemTag, ...]]:
return DEFAULT_REQUIREMENT_TAGS
@property
def all_requirements(self) -> Iterable[Requirement]:
"""Returns all requirements that are not directly part of the source."""
return self.other_requirements
@dataclass(frozen=True, kw_only=True)
class GenericSource(Source):
regions: Tuple[str, ...] = ()
regions: tuple[str, ...] = ()
"""No region means it's available everywhere."""
@dataclass(frozen=True, kw_only=True)
class AllRegionsSource(Source):
regions: tuple[str, ...] = ()
@dataclass(frozen=True)
class CustomRuleSource(Source):
"""Hopefully once everything is migrated to sources, we won't need these custom logic anymore."""
create_rule: Callable[[Any], StardewRule]
create_rule: "Callable[[StardewLogic], StardewRule]"
class Tag(Source):
"""Not a real source, just a way to add tags to an item. Will be removed from the item sources during unpacking."""
tag: Tuple[ItemTag, ...]
tag: tuple[ItemTag, ...]
def __init__(self, *tag: ItemTag):
self.tag = tag # noqa
@@ -64,8 +80,8 @@ class Tag(Source):
@dataclass(frozen=True)
class GameItem:
name: str
sources: List[Source] = field(default_factory=list)
tags: Set[ItemTag] = field(default_factory=set)
sources: list[Source] = field(default_factory=list)
tags: set[ItemTag] = field(default_factory=set)
def add_sources(self, sources: Iterable[Source]):
self.sources.extend(source for source in sources if type(source) is not Tag)

View File

@@ -9,6 +9,8 @@ from ..strings.season_names import Season
class ForagingSource(Source):
regions: Tuple[str, ...]
seasons: Tuple[str, ...] = Season.all
require_all_regions: bool = False
grind_months: int = 0
@dataclass(frozen=True, kw_only=True)

View File

@@ -0,0 +1,160 @@
from dataclasses import dataclass
from functools import cached_property
from typing import List
from worlds.stardew_valley.strings.ap_names.ap_option_names import HatsanityOptionName
hat_clarifier = " (Hat)"
@dataclass(frozen=True)
class HatItem:
name: str
difficulty: frozenset[str]
need_clarifier: bool = False
def __post_init__(self):
all_hats.append(self)
@cached_property
def clarified_name(self) -> str:
if self.need_clarifier:
return self.name + hat_clarifier
return self.name
def __repr__(self) -> str:
return f"{self.name} (Difficulty: {self.difficulty})"
def create_hat(name: str, difficulty: List[str] | str, need_clarifier: bool = False) -> HatItem:
if isinstance(difficulty, str):
difficulty = [difficulty]
return HatItem(name, frozenset(difficulty), need_clarifier)
all_hats: list[HatItem] = []
class Hats:
abigails_bow = create_hat("Abigail's Bow", HatsanityOptionName.rng)
arcane_hat = create_hat("Arcane Hat", HatsanityOptionName.near_perfection)
archers_cap = create_hat("Archer's Cap", HatsanityOptionName.near_perfection)
beanie = create_hat("Beanie", HatsanityOptionName.tailoring)
blobfish_mask = create_hat("Blobfish Mask", HatsanityOptionName.tailoring)
blue_bonnet = create_hat("Blue Bonnet", HatsanityOptionName.medium)
blue_bow = create_hat("Blue Bow", HatsanityOptionName.easy)
blue_cowboy_hat = create_hat("Blue Cowboy Hat", HatsanityOptionName.rng)
blue_ribbon = create_hat("Blue Ribbon", HatsanityOptionName.medium)
bluebird_mask = create_hat("Bluebird Mask", HatsanityOptionName.medium)
bowler = create_hat("Bowler Hat", HatsanityOptionName.difficult)
bridal_veil = create_hat("Bridal Veil", HatsanityOptionName.tailoring)
bucket_hat = create_hat("Bucket Hat", HatsanityOptionName.medium)
butterfly_bow = create_hat("Butterfly Bow", HatsanityOptionName.easy)
cat_ears = create_hat("Cat Ears", HatsanityOptionName.medium)
chef_hat = create_hat("Chef Hat", HatsanityOptionName.near_perfection)
chicken_mask = create_hat("Chicken Mask", HatsanityOptionName.near_perfection)
concerned_ape_mask = create_hat("???", HatsanityOptionName.post_perfection)
cone_hat = create_hat("Cone Hat", HatsanityOptionName.easy)
cool_cap = create_hat("Cool Cap", HatsanityOptionName.medium)
copper_pan_hat = create_hat("Copper Pan", HatsanityOptionName.easy, need_clarifier=True)
cowboy = create_hat("Cowboy Hat", HatsanityOptionName.near_perfection)
cowgal_hat = create_hat("Cowgal Hat", HatsanityOptionName.difficult)
cowpoke_hat = create_hat("Cowpoke Hat", HatsanityOptionName.difficult)
daisy = create_hat("Daisy", HatsanityOptionName.easy)
dark_ballcap = create_hat("Dark Ballcap", HatsanityOptionName.rng)
dark_cowboy_hat = create_hat("Dark Cowboy Hat", HatsanityOptionName.rng)
dark_velvet_bow = create_hat("Dark Velvet Bow", HatsanityOptionName.easy)
delicate_bow = create_hat("Delicate Bow", HatsanityOptionName.easy)
deluxe_cowboy_hat = create_hat("Deluxe Cowboy Hat", HatsanityOptionName.medium)
deluxe_pirate_hat = create_hat("Deluxe Pirate Hat", HatsanityOptionName.rng)
dinosaur_hat = create_hat("Dinosaur Hat", HatsanityOptionName.tailoring)
earmuffs = create_hat("Earmuffs", HatsanityOptionName.medium)
elegant_turban = create_hat("Elegant Turban", HatsanityOptionName.post_perfection)
emilys_magic_hat = create_hat("Emily's Magic Hat", HatsanityOptionName.medium)
eye_patch = create_hat("Eye Patch", HatsanityOptionName.near_perfection)
fashion_hat = create_hat("Fashion Hat", HatsanityOptionName.tailoring)
fedora = create_hat("Fedora", HatsanityOptionName.easy)
fishing_hat = create_hat("Fishing Hat", HatsanityOptionName.tailoring)
flat_topped_hat = create_hat("Flat Topped Hat", HatsanityOptionName.tailoring)
floppy_beanie = create_hat("Floppy Beanie", HatsanityOptionName.tailoring)
foragers_hat = create_hat("Forager's Hat", HatsanityOptionName.tailoring)
frog_hat = create_hat("Frog Hat", HatsanityOptionName.medium)
garbage_hat = create_hat("Garbage Hat", HatsanityOptionName.rng)
gils_hat = create_hat("Gil's Hat", HatsanityOptionName.difficult)
gnomes_cap = create_hat("Gnome's Cap", HatsanityOptionName.near_perfection)
goblin_mask = create_hat("Goblin Mask", HatsanityOptionName.near_perfection)
goggles = create_hat("Goggles", HatsanityOptionName.tailoring)
gold_pan_hat = create_hat("Gold Pan", HatsanityOptionName.easy, need_clarifier=True)
golden_helmet = create_hat("Golden Helmet", HatsanityOptionName.rng)
golden_mask = create_hat("Golden Mask", HatsanityOptionName.tailoring, need_clarifier=True)
good_ol_cap = create_hat("Good Ol' Cap", HatsanityOptionName.easy)
governors_hat = create_hat("Governor's Hat", HatsanityOptionName.medium)
green_turban = create_hat("Green Turban", HatsanityOptionName.medium)
hair_bone = create_hat("Hair Bone", HatsanityOptionName.tailoring)
hard_hat = create_hat("Hard Hat", HatsanityOptionName.difficult)
hunters_cap = create_hat("Hunter's Cap", HatsanityOptionName.medium)
infinity_crown = create_hat("Infinity Crown", HatsanityOptionName.difficult)
iridium_pan_hat = create_hat("Iridium Pan", HatsanityOptionName.easy, need_clarifier=True)
jester_hat = create_hat("Jester Hat", HatsanityOptionName.easy)
joja_cap = create_hat("Joja Cap", HatsanityOptionName.rng)
junimo_hat = create_hat("Junimo Hat", HatsanityOptionName.post_perfection)
knights_helmet = create_hat("Knight's Helmet", HatsanityOptionName.near_perfection)
laurel_wreath_crown = create_hat("Laurel Wreath Crown", HatsanityOptionName.rng)
leprechaun_hat = create_hat("Leprechaun Hat", HatsanityOptionName.easy)
living_hat = create_hat("Living Hat", HatsanityOptionName.rng)
logo_cap = create_hat("Logo Cap", HatsanityOptionName.tailoring)
lucky_bow = create_hat("Lucky Bow", HatsanityOptionName.medium)
magic_cowboy_hat = create_hat("Magic Cowboy Hat", HatsanityOptionName.difficult)
magic_turban = create_hat("Magic Turban", HatsanityOptionName.difficult)
mouse_ears = create_hat("Mouse Ears", HatsanityOptionName.easy)
mr_qis_hat = create_hat("Mr. Qi's Hat", HatsanityOptionName.medium)
mummy_mask = create_hat("Mummy Mask", HatsanityOptionName.easy)
mushroom_cap = create_hat("Mushroom Cap", HatsanityOptionName.rng)
mystery_hat = create_hat("Mystery Hat", HatsanityOptionName.rng)
official_cap = create_hat("Official Cap", HatsanityOptionName.medium)
pageboy_cap = create_hat("Pageboy Cap", HatsanityOptionName.near_perfection)
panda_hat = create_hat("Panda Hat", "Impossible")
paper_hat = create_hat("Paper Hat", HatsanityOptionName.easy)
party_hat_blue = create_hat("Party Hat (Blue)", HatsanityOptionName.tailoring)
party_hat_green = create_hat("Party Hat (Green)", HatsanityOptionName.tailoring)
party_hat_red = create_hat("Party Hat (Red)", HatsanityOptionName.tailoring)
pink_bow = create_hat("Pink Bow", HatsanityOptionName.easy)
pirate_hat = create_hat("Pirate Hat", HatsanityOptionName.tailoring)
plum_chapeau = create_hat("Plum Chapeau", HatsanityOptionName.difficult)
polka_bow = create_hat("Polka Bow", HatsanityOptionName.medium)
propeller_hat = create_hat("Propeller Hat", HatsanityOptionName.tailoring)
pumpkin_mask = create_hat("Pumpkin Mask", HatsanityOptionName.tailoring)
qi_mask = create_hat("Qi Mask", HatsanityOptionName.tailoring)
raccoon_hat = create_hat("Raccoon Hat", HatsanityOptionName.medium)
radioactive_goggles = create_hat("Radioactive Goggles", HatsanityOptionName.tailoring)
red_cowboy_hat = create_hat("Red Cowboy Hat", HatsanityOptionName.rng)
red_fez = create_hat("Red Fez", HatsanityOptionName.medium)
sailors_cap = create_hat("Sailor's Cap", HatsanityOptionName.easy)
santa_hat = create_hat("Santa Hat", HatsanityOptionName.medium)
skeleton_mask = create_hat("Skeleton Mask", HatsanityOptionName.medium)
small_cap = create_hat("Small Cap", HatsanityOptionName.medium)
sombrero = create_hat("Sombrero", HatsanityOptionName.near_perfection)
souwester = create_hat("Sou'wester", HatsanityOptionName.easy)
space_helmet = create_hat("Space Helmet", HatsanityOptionName.difficult)
sports_cap = create_hat("Sports Cap", HatsanityOptionName.medium)
spotted_headscarf = create_hat("Spotted Headscarf", HatsanityOptionName.tailoring)
squid_hat = create_hat("Squid Hat", HatsanityOptionName.medium)
squires_helmet = create_hat("Squire's Helmet", HatsanityOptionName.rng)
star_helmet = create_hat("Star Helmet", HatsanityOptionName.tailoring)
steel_pan_hat = create_hat("Steel Pan", HatsanityOptionName.easy, need_clarifier=True)
straw = create_hat("Straw Hat", HatsanityOptionName.medium)
sunglasses = create_hat("Sunglasses", HatsanityOptionName.tailoring)
swashbuckler_hat = create_hat("Swashbuckler Hat", HatsanityOptionName.tailoring)
tiara = create_hat("Tiara", HatsanityOptionName.easy)
tiger_hat = create_hat("Tiger Hat", HatsanityOptionName.rng)
top_hat = create_hat("Top Hat", HatsanityOptionName.easy)
totem_mask = create_hat("Totem Mask", HatsanityOptionName.tailoring)
tricorn = create_hat("Tricorn Hat", HatsanityOptionName.rng)
tropiclip = create_hat("Tropiclip", HatsanityOptionName.easy)
trucker_hat = create_hat("Trucker Hat", HatsanityOptionName.medium)
warrior_helmet = create_hat("Warrior Helmet", HatsanityOptionName.tailoring)
watermelon_band = create_hat("Watermelon Band", HatsanityOptionName.difficult)
wearable_dwarf_helm = create_hat("Wearable Dwarf Helm", HatsanityOptionName.tailoring)
white_bow = create_hat("White Bow", HatsanityOptionName.difficult)
white_turban = create_hat("White Turban", HatsanityOptionName.tailoring)
witch_hat = create_hat("Witch Hat", HatsanityOptionName.tailoring)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
from dataclasses import dataclass
from typing import List, Tuple, Dict, Set, Callable
from typing import List, Tuple, Dict, Callable
from .game_item import Source
from ..mods.mod_data import ModNames
from ..mods.mod_monster_locations import modded_monsters_locations
from ..strings.monster_names import Monster, MonsterCategory
@@ -12,7 +13,7 @@ from ..strings.region_names import Region
class StardewMonster:
name: str
category: str
locations: Tuple[str]
locations: Tuple[str, ...]
difficulty: str
def __repr__(self):
@@ -20,6 +21,12 @@ class StardewMonster:
f" Difficulty: {self.difficulty}) |"
@dataclass(frozen=True, kw_only=True)
class MonsterSource(Source):
monsters: Tuple[StardewMonster, ...]
amount_tier: int = 0
slime_hutch = (Region.slime_hutch,)
mines_floor_20 = (Region.mines_floor_20,)
mines_floor_60 = (Region.mines_floor_60,)
@@ -97,6 +104,8 @@ armored_bug = create_monster(Monster.armored_bug, MonsterCategory.cave_insects,
armored_bug_dangerous = create_monster(Monster.armored_bug_dangerous, MonsterCategory.cave_insects, skull_cavern,
Performance.good) # Requires 'Bug Killer' enchantment
metal_head = create_monster(Monster.metal_head, MonsterCategory.metal_heads, mines_floor_100, Performance.good)
duggy = create_monster(Monster.duggy, MonsterCategory.duggies, mines_floor_20, Performance.basic)
duggy_dangerous = create_monster(Monster.duggy_dangerous, MonsterCategory.duggies, dangerous_mines_20, Performance.great)
magma_duggy = create_monster(Monster.magma_duggy, MonsterCategory.duggies, volcano, Performance.galaxy)
@@ -122,6 +131,8 @@ royal_serpent = create_monster(Monster.royal_serpent, MonsterCategory.serpents,
magma_sprite = create_monster(Monster.magma_sprite, MonsterCategory.magma_sprites, volcano, Performance.galaxy)
magma_sparker = create_monster(Monster.magma_sparker, MonsterCategory.magma_sprites, volcano_high, Performance.galaxy)
haunted_skull = create_monster(Monster.haunted_skull, MonsterCategory.none, quarry_mine, Performance.great)
register_monster_modification(ModNames.sve, shadow_brute_dangerous, update_monster_locations)
register_monster_modification(ModNames.sve, shadow_sniper, update_monster_locations)
register_monster_modification(ModNames.sve, shadow_shaman_dangerous, update_monster_locations)
@@ -145,7 +156,7 @@ register_monster_modification(ModNames.boarding_house, grub, update_monster_loca
register_monster_modification(ModNames.boarding_house, bug, update_monster_locations)
def all_monsters_by_name_given_mods(mods: Set[str]) -> Dict[str, StardewMonster]:
def all_monsters_by_name_given_content_packs(mods: set[str]) -> dict[str, StardewMonster]:
monsters_by_name = {}
for monster in all_monsters:
current_monster = monster
@@ -158,7 +169,7 @@ def all_monsters_by_name_given_mods(mods: Set[str]) -> Dict[str, StardewMonster]
return monsters_by_name
def all_monsters_by_category_given_mods(mods: Set[str]) -> Dict[str, Tuple[StardewMonster, ...]]:
def all_monsters_by_category_given_content_packs(mods: set[str]) -> dict[str, Tuple[StardewMonster, ...]]:
monsters_by_category = {}
for monster in all_monsters:
current_monster = monster

View File

@@ -0,0 +1,99 @@
from typing import List
from ..strings.season_names import Season
from ..strings.villager_names import NPC
movies_by_name = dict()
snacks_by_name = dict()
npc_snacks = dict()
def movie(movie_name: str, season: str, loving_npcs: List[str]):
movie = Movie(movie_name, season, loving_npcs)
movies_by_name[movie_name] = movie
return movie
def snack(snack_name: str, category: str, loving_npcs: List[str]):
snack = Snack(snack_name, category, loving_npcs)
snacks_by_name[snack_name] = snack
for npc in loving_npcs:
if npc not in npc_snacks:
npc_snacks[npc] = []
npc_snacks[npc].append(snack)
return snack
class Movie:
name: str
season: str
loving_npcs: List[str]
def __init__(self, name: str, season: str, loving_npcs: List[str]):
self.name = name
self.season = season
self.loving_npcs = loving_npcs
class Snack:
name: str
category: str
loving_npcs: List[str]
def __init__(self, name: str, category: str, loving_npcs: List[str]):
self.name = name
self.category = category
self.loving_npcs = loving_npcs
class MovieName:
brave_sapling = movie("The Brave Little Sapling", Season.spring, [NPC.caroline, NPC.dwarf, NPC.jas, NPC.penny, NPC.sandy, NPC.vincent])
prairie_king = movie("Journey Of The Prairie King: The Motion Picture", Season.summer, [NPC.caroline, NPC.dwarf, NPC.jas, NPC.robin, NPC.sandy, NPC.vincent])
mysterium = movie("Mysterium", Season.fall, [NPC.abigail, NPC.dwarf, NPC.elliott, NPC.leah, NPC.sandy, NPC.sebastian, NPC.wizard])
miracle_coldstar_ranch = movie("The Miracle At Coldstar Ranch", Season.winter, [NPC.dwarf, NPC.emily, NPC.evelyn, NPC.gus, NPC.harvey, NPC.marnie, NPC.sandy])
natural_wonders = movie("Natural Wonders: Exploring Our Vibrant World", Season.spring, [NPC.demetrius, NPC.dwarf, NPC.jas, NPC.leo, NPC.lewis, NPC.maru, NPC.sandy])
wumbus = movie("Wumbus", Season.summer, [NPC.alex, NPC.demetrius, NPC.dwarf, NPC.gus, NPC.jas, NPC.maru, NPC.pierre, NPC.sam, NPC.sandy, NPC.shane, NPC.vincent])
howls_in_rain = movie("It Howls In The Rain", Season.fall, [NPC.abigail, NPC.alex, NPC.dwarf, NPC.sandy, NPC.sebastian, NPC.shane])
zuzu_city_express = movie("The Zuzu City Express", Season.winter, [NPC.dwarf, NPC.evelyn, NPC.george, NPC.harvey, NPC.jodi, NPC.sandy])
class SnackCategory:
salty = "Movie Salty Snacks"
sweet = "Movie Sweet Snacks"
drinks = "Movie Drinks"
meals = "Movie Meals"
class SnackName:
apple_slices = snack("Apple Slices", SnackCategory.sweet, [NPC.harvey])
black_licorice = snack("Black Licorice", SnackCategory.sweet, [NPC.george, NPC.krobus, NPC.wizard])
cappuccino_mousse_cake = snack("Cappuccino Mousse Cake", SnackCategory.sweet, [NPC.elliott, NPC.evelyn, NPC.gus, NPC.haley])
chocolate_popcorn = snack("Chocolate Popcorn", SnackCategory.sweet, [NPC.jodi])
cotton_candy = snack("Cotton Candy", SnackCategory.sweet, [NPC.penny, NPC.sandy])
fries = snack("Fries", SnackCategory.salty, [NPC.clint])
hummus_snack_pack = snack("Hummus Snack Pack", SnackCategory.salty, [NPC.shane])
ice_cream_sandwich = snack("Ice Cream Sandwich", SnackCategory.sweet, [NPC.marnie])
jasmine_tea = snack("Jasmine Tea", SnackCategory.drinks, [NPC.caroline, NPC.harvey, NPC.lewis, NPC.sebastian])
jawbreaker = snack("Jawbreaker", SnackCategory.sweet, [NPC.vincent])
joja_cola = snack("Joja Cola", SnackCategory.drinks, [NPC.shane])
jojacorn = snack("JojaCorn", SnackCategory.salty, [NPC.shane])
kale_smoothie = snack("Kale Smoothie", SnackCategory.drinks, [NPC.emily])
nachos = snack("Nachos", SnackCategory.meals, [NPC.pam, NPC.shane])
panzanella_salad = snack("Panzanella Salad", SnackCategory.meals, [NPC.gus, NPC.leah])
personal_pizza = snack("Personal Pizza", SnackCategory.meals, [NPC.pierre, NPC.sam, NPC.shane])
popcorn = snack("Popcorn", SnackCategory.salty, [NPC.demetrius, NPC.kent])
rock_candy = snack("Rock Candy", SnackCategory.sweet, [NPC.abigail, NPC.dwarf])
salmon_burger = snack("Salmon Burger", SnackCategory.meals, [NPC.alex, NPC.linus, NPC.willy])
salted_peanuts = snack("Salted Peanuts", SnackCategory.salty, [NPC.robin])
sour_slimes = snack("Sour Slimes", SnackCategory.sweet, [NPC.jas])
star_cookie = snack("Star Cookie", SnackCategory.sweet, [NPC.evelyn, NPC.maru, NPC.wizard])
stardrop_sorbet = snack("Stardrop Sorbet", SnackCategory.sweet, [NPC.alex, NPC.harvey, NPC.sam, NPC.sebastian, NPC.shane, NPC.abigail, NPC.emily, NPC.haley,
NPC.leah, NPC.maru, NPC.penny, NPC.caroline, NPC.clint, NPC.demetrius, NPC.dwarf, NPC.evelyn,
NPC.george, NPC.gus, NPC.jas, NPC.jodi, NPC.kent, NPC.lewis, NPC.linus, NPC.marnie, NPC.pam,
NPC.pierre, NPC.robin, NPC.sandy, NPC.vincent, NPC.willy, NPC.wizard])
truffle_popcorn = snack("Truffle Popcorn", SnackCategory.salty, [NPC.caroline, NPC.elliott, NPC.gus])
# For some unknown reason, Leo doesn't love ANY snack
npc_snacks[NPC.leo] = []

View File

@@ -15,7 +15,7 @@ from ..strings.region_names import Region
@dataclass(frozen=True)
class MuseumItem:
item_name: str
locations: Tuple[str, ...]
artifact_spot_locations: Tuple[str, ...]
geodes: Tuple[str, ...]
monsters: Tuple[str, ...]
difficulty: float
@@ -23,11 +23,11 @@ class MuseumItem:
@staticmethod
def of(item_name: str,
difficulty: float,
locations: Union[str, Tuple[str, ...]],
artifact_spot_locations: Union[str, Tuple[str, ...]],
geodes: Union[str, Tuple[str, ...]],
monsters: Union[str, Tuple[str, ...]]) -> MuseumItem:
if isinstance(locations, str):
locations = (locations,)
if isinstance(artifact_spot_locations, str):
artifact_spot_locations = (artifact_spot_locations,)
if isinstance(geodes, str):
geodes = (geodes,)
@@ -35,10 +35,10 @@ class MuseumItem:
if isinstance(monsters, str):
monsters = (monsters,)
return MuseumItem(item_name, locations, geodes, monsters, difficulty)
return MuseumItem(item_name, artifact_spot_locations, geodes, monsters, difficulty)
def __repr__(self):
return f"{self.item_name} (Locations: {self.locations} |" \
return f"{self.item_name} (Artifact Spot Locations: {self.artifact_spot_locations} |" \
f" Geodes: {self.geodes} |" \
f" Monsters: {self.monsters}) "
@@ -53,17 +53,17 @@ all_museum_items: List[MuseumItem] = []
def create_artifact(name: str,
difficulty: float,
locations: Union[str, Tuple[str, ...]] = (),
artifact_spot_locations: Union[str, Tuple[str, ...]] = (),
geodes: Union[str, Tuple[str, ...]] = (),
monsters: Union[str, Tuple[str, ...]] = ()) -> MuseumItem:
artifact_item = MuseumItem.of(name, difficulty, locations, geodes, monsters)
artifact_item = MuseumItem.of(name, difficulty, artifact_spot_locations, geodes, monsters)
all_museum_artifacts.append(artifact_item)
all_museum_items.append(artifact_item)
return artifact_item
def create_mineral(name: str,
locations: Union[str, Tuple[str, ...]] = (),
artifact_spot_locations: Union[str, Tuple[str, ...]] = (),
geodes: Union[str, Tuple[str, ...]] = (),
monsters: Union[str, Tuple[str, ...]] = (),
difficulty: Optional[float] = None) -> MuseumItem:
@@ -80,7 +80,7 @@ def create_mineral(name: str,
if "Fishing Chest" in geodes:
difficulty += 4.3
mineral_item = MuseumItem.of(name, difficulty, locations, geodes, monsters)
mineral_item = MuseumItem.of(name, difficulty, artifact_spot_locations, geodes, monsters)
all_museum_minerals.append(mineral_item)
all_museum_items.append(mineral_item)
return mineral_item
@@ -168,7 +168,7 @@ class Artifact:
geodes=WaterChest.fishing_chest)
palm_fossil = create_artifact("Palm Fossil", 10.2,
(Region.dig_site, Region.desert, Region.forest, Region.beach))
trilobite = create_artifact("Trilobite", 7.4, (Region.dig_site, Region.desert, Region.forest, Region.beach))
trilobite = create_artifact(Fossil.trilobite, 7.4, (Region.dig_site, Region.desert, Region.forest, Region.beach))
class Mineral:
@@ -267,15 +267,15 @@ class Mineral:
geodes=(Geode.geode, Geode.omni))
basalt = create_mineral("Basalt",
geodes=(Geode.magma, Geode.omni))
limestone = create_mineral("Limestone",
limestone = create_mineral(Mineral.limestone,
geodes=(Geode.geode, Geode.omni))
soapstone = create_mineral("Soapstone",
geodes=(Geode.frozen, Geode.omni))
hematite = create_mineral("Hematite",
geodes=(Geode.frozen, Geode.omni))
mudstone = create_mineral("Mudstone",
mudstone = create_mineral(Mineral.mudstone,
geodes=(Geode.geode, Geode.omni))
obsidian = create_mineral("Obsidian",
obsidian = create_mineral(Mineral.obsidian,
geodes=(Geode.magma, Geode.omni))
slate = create_mineral("Slate", geodes=(Geode.geode, Geode.omni))
fairy_stone = create_mineral("Fairy Stone", geodes=(Geode.frozen, Geode.omni))

View File

@@ -1,6 +1,7 @@
from typing import Dict, List, Optional
from typing import List
from .recipe_source import RecipeSource, FriendshipSource, SkillSource, QueenOfSauceSource, ShopSource, StarterSource, ShopTradeSource, ShopFriendshipSource
from ..content.content_packs import ginger_island_content_pack
from ..mods.mod_data import ModNames
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
@@ -23,15 +24,15 @@ from ..strings.villager_names import NPC, ModNPC
class CookingRecipe:
meal: str
ingredients: Dict[str, int]
ingredients: dict[str, int]
source: RecipeSource
mod_name: Optional[str] = None
content_pack: str | None
def __init__(self, meal: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None):
def __init__(self, meal: str, ingredients: dict[str, int], source: RecipeSource, content_pack: str | None):
self.meal = meal
self.ingredients = ingredients
self.source = source
self.mod_name = mod_name
self.content_pack = content_pack
def __repr__(self):
return f"{self.meal} (Source: {self.source} |" \
@@ -41,44 +42,44 @@ class CookingRecipe:
all_cooking_recipes: List[CookingRecipe] = []
def friendship_recipe(name: str, friend: str, hearts: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
def friendship_recipe(name: str, friend: str, hearts: int, ingredients: dict[str, int], /, *, content_pack: str | None = None) -> CookingRecipe:
source = FriendshipSource(friend, hearts)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def friendship_and_shop_recipe(name: str, friend: str, hearts: int, region: str, price: int, ingredients: Dict[str, int],
mod_name: Optional[str] = None) -> CookingRecipe:
def friendship_and_shop_recipe(name: str, friend: str, hearts: int, region: str, price: int, ingredients: dict[str, int],
/, *, content_pack: str | None = None) -> CookingRecipe:
source = ShopFriendshipSource(friend, hearts, region, price)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
def skill_recipe(name: str, skill: str, level: int, ingredients: dict[str, int], /, *, content_pack: str | None = None) -> CookingRecipe:
source = SkillSource(skill, level)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
def shop_recipe(name: str, region: str, price: int, ingredients: dict[str, int], /, *, content_pack: str | None = None) -> CookingRecipe:
source = ShopSource(region, price)
return create_recipe(name, ingredients, source, mod_name)
return create_recipe(name, ingredients, source, content_pack)
def shop_trade_recipe(name: str, region: str, currency: str, price: int, ingredients: Dict[str, int]) -> CookingRecipe:
def shop_trade_recipe(name: str, region: str, currency: str, price: int, ingredients: dict[str, int], /, *, content_pack: str | None = None) -> CookingRecipe:
source = ShopTradeSource(region, currency, price)
return create_recipe(name, ingredients, source)
return create_recipe(name, ingredients, source, content_pack)
def queen_of_sauce_recipe(name: str, year: int, season: str, day: int, ingredients: Dict[str, int]) -> CookingRecipe:
def queen_of_sauce_recipe(name: str, year: int, season: str, day: int, ingredients: dict[str, int], /, *, content_pack: str | None = None) -> CookingRecipe:
source = QueenOfSauceSource(year, season, day)
return create_recipe(name, ingredients, source)
return create_recipe(name, ingredients, source, content_pack)
def starter_recipe(name: str, ingredients: Dict[str, int]) -> CookingRecipe:
def starter_recipe(name: str, ingredients: dict[str, int], /, *, content_pack: str | None = None) -> CookingRecipe:
source = StarterSource()
return create_recipe(name, ingredients, source)
return create_recipe(name, ingredients, source, content_pack)
def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource, mod_name: Optional[str] = None) -> CookingRecipe:
recipe = CookingRecipe(name, ingredients, source, mod_name)
def create_recipe(name: str, ingredients: dict[str, int], source: RecipeSource, content_pack: str | None = None) -> CookingRecipe:
recipe = CookingRecipe(name, ingredients, source, content_pack)
all_cooking_recipes.append(recipe)
return recipe
@@ -88,7 +89,8 @@ artichoke_dip = queen_of_sauce_recipe(Meal.artichoke_dip, 1, Season.fall, 28, {V
autumn_bounty = friendship_recipe(Meal.autumn_bounty, NPC.demetrius, 7, {Vegetable.yam: 1, Vegetable.pumpkin: 1})
baked_fish = queen_of_sauce_recipe(Meal.baked_fish, 1, Season.summer, 7, {Fish.sunfish: 1, Fish.bream: 1, Ingredient.wheat_flour: 1})
banana_pudding = shop_trade_recipe(Meal.banana_pudding, Region.island_trader, Fossil.bone_fragment, 30,
{Fruit.banana: 1, AnimalProduct.cow_milk: 1, Ingredient.sugar: 1})
{Fruit.banana: 1, AnimalProduct.cow_milk: 1, Ingredient.sugar: 1},
content_pack=ginger_island_content_pack.name)
bean_hotpot = friendship_recipe(Meal.bean_hotpot, NPC.clint, 7, {Vegetable.green_bean: 2})
blackberry_cobbler_ingredients = {Forageable.blackberry: 2, Ingredient.sugar: 1, Ingredient.wheat_flour: 1}
blackberry_cobbler_qos = queen_of_sauce_recipe(Meal.blackberry_cobbler, 2, Season.fall, 14, blackberry_cobbler_ingredients)
@@ -122,7 +124,8 @@ fried_eel = friendship_recipe(Meal.fried_eel, NPC.george, 3, {Fish.eel: 1, Ingre
fried_egg = starter_recipe(Meal.fried_egg, {AnimalProduct.chicken_egg: 1})
fried_mushroom = friendship_recipe(Meal.fried_mushroom, NPC.demetrius, 3, {Mushroom.common: 1, Mushroom.morel: 1, Ingredient.oil: 1})
fruit_salad = queen_of_sauce_recipe(Meal.fruit_salad, 2, Season.fall, 7, {Fruit.blueberry: 1, Fruit.melon: 1, Fruit.apricot: 1})
ginger_ale = shop_recipe(Beverage.ginger_ale, Region.volcano_dwarf_shop, 1000, {Forageable.ginger: 3, Ingredient.sugar: 1})
ginger_ale = shop_recipe(Beverage.ginger_ale, Region.volcano_dwarf_shop, 1000, {Forageable.ginger: 3, Ingredient.sugar: 1},
content_pack=ginger_island_content_pack.name)
glazed_yams = queen_of_sauce_recipe(Meal.glazed_yams, 1, Season.fall, 21, {Vegetable.yam: 1, Ingredient.sugar: 1})
hashbrowns = queen_of_sauce_recipe(Meal.hashbrowns, 2, Season.spring, 14, {Vegetable.potato: 1, Ingredient.oil: 1})
ice_cream = friendship_recipe(Meal.ice_cream, NPC.jodi, 7, {AnimalProduct.cow_milk: 1, Ingredient.sugar: 1})
@@ -131,7 +134,8 @@ lobster_bisque_friend = friendship_recipe(Meal.lobster_bisque, NPC.willy, 9, lob
lobster_bisque_qos = queen_of_sauce_recipe(Meal.lobster_bisque, 2, Season.winter, 14, lobster_bisque_ingredients)
lucky_lunch = queen_of_sauce_recipe(Meal.lucky_lunch, 2, Season.spring, 28, {Fish.sea_cucumber: 1, Meal.tortilla: 1, Flower.blue_jazz: 1})
maki_roll = queen_of_sauce_recipe(Meal.maki_roll, 1, Season.summer, 21, {Fish.any: 1, WaterItem.seaweed: 1, Ingredient.rice: 1})
mango_sticky_rice = friendship_recipe(Meal.mango_sticky_rice, NPC.leo, 7, {Fruit.mango: 1, Forageable.coconut: 1, Ingredient.rice: 1})
mango_sticky_rice = friendship_recipe(Meal.mango_sticky_rice, NPC.leo, 7, {Fruit.mango: 1, Forageable.coconut: 1, Ingredient.rice: 1},
content_pack=ginger_island_content_pack.name)
maple_bar = queen_of_sauce_recipe(Meal.maple_bar, 2, Season.summer, 14, {ArtisanGood.maple_syrup: 1, Ingredient.sugar: 1, Ingredient.wheat_flour: 1})
miners_treat = skill_recipe(Meal.miners_treat, Skill.mining, 3, {Forageable.cave_carrot: 2, Ingredient.sugar: 1, AnimalProduct.cow_milk: 1})
moss_soup = skill_recipe(Meal.moss_soup, Skill.foraging, 3, {Material.moss: 20})
@@ -146,7 +150,7 @@ pizza_ingredients = {Ingredient.wheat_flour: 1, Vegetable.tomato: 1, ArtisanGood
pizza_qos = queen_of_sauce_recipe(Meal.pizza, 2, Season.spring, 7, pizza_ingredients)
pizza_saloon = shop_recipe(Meal.pizza, Region.saloon, 150, pizza_ingredients)
plum_pudding = queen_of_sauce_recipe(Meal.plum_pudding, 1, Season.winter, 7, {Forageable.wild_plum: 2, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
poi = friendship_recipe(Meal.poi, NPC.leo, 3, {Vegetable.taro_root: 4})
poi = friendship_recipe(Meal.poi, NPC.leo, 3, {Vegetable.taro_root: 4}, content_pack=ginger_island_content_pack.name)
poppyseed_muffin = queen_of_sauce_recipe(Meal.poppyseed_muffin, 2, Season.winter, 7, {Flower.poppy: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
pumpkin_pie_ingredients = {Vegetable.pumpkin: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.cow_milk: 1}
pumpkin_pie_qos = queen_of_sauce_recipe(Meal.pumpkin_pie, 1, Season.winter, 21, pumpkin_pie_ingredients)
@@ -177,55 +181,59 @@ tortilla_ingredients = {Vegetable.corn: 1}
tortilla_qos = queen_of_sauce_recipe(Meal.tortilla, 1, Season.fall, 7, tortilla_ingredients)
tortilla_saloon = shop_recipe(Meal.tortilla, Region.saloon, 100, tortilla_ingredients)
triple_shot_espresso = shop_recipe(Beverage.triple_shot_espresso, Region.saloon, 5000, {Beverage.coffee: 3})
tropical_curry = shop_recipe(Meal.tropical_curry, Region.island_resort, 2000, {Forageable.coconut: 1, Fruit.pineapple: 1, Fruit.hot_pepper: 1})
tropical_curry = shop_recipe(Meal.tropical_curry, Region.island_resort, 2000, {Forageable.coconut: 1, Fruit.pineapple: 1, Fruit.hot_pepper: 1},
content_pack=ginger_island_content_pack.name)
trout_soup = queen_of_sauce_recipe(Meal.trout_soup, 1, Season.fall, 14, {Fish.rainbow_trout: 1, WaterItem.green_algae: 1})
vegetable_medley = friendship_recipe(Meal.vegetable_medley, NPC.caroline, 7, {Vegetable.tomato: 1, Vegetable.beet: 1})
magic_elixir = shop_recipe(ModEdible.magic_elixir, Region.adventurer_guild, 3000, {Edible.life_elixir: 1, Mushroom.purple: 1}, ModNames.magic)
magic_elixir = shop_recipe(ModEdible.magic_elixir, Region.adventurer_guild, 3000, {Edible.life_elixir: 1, Mushroom.purple: 1}, content_pack=ModNames.magic)
baked_berry_oatmeal = shop_recipe(SVEMeal.baked_berry_oatmeal, SVERegion.bear_shop, 0, {Forageable.salmonberry: 15, Forageable.blackberry: 15,
Ingredient.sugar: 1, Ingredient.wheat_flour: 2}, ModNames.sve)
Ingredient.sugar: 1, Ingredient.wheat_flour: 2},
content_pack=ModNames.sve)
big_bark_burger = friendship_and_shop_recipe(SVEMeal.big_bark_burger, NPC.gus, 5, Region.saloon, 5500,
{SVEFish.puppyfish: 1, Meal.bread: 1, Ingredient.oil: 1}, ModNames.sve)
{SVEFish.puppyfish: 1, Meal.bread: 1, Ingredient.oil: 1}, content_pack=ModNames.sve)
flower_cookie = shop_recipe(SVEMeal.flower_cookie, SVERegion.bear_shop, 0, {SVEForage.ferngill_primrose: 1, SVEForage.goldenrod: 1,
SVEForage.winter_star_rose: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1,
AnimalProduct.large_egg: 1}, ModNames.sve)
frog_legs = shop_recipe(SVEMeal.frog_legs, Region.adventurer_guild, 2000, {SVEFish.frog: 1, Ingredient.oil: 1, Ingredient.wheat_flour: 1}, ModNames.sve)
AnimalProduct.large_egg: 1}, content_pack=ModNames.sve)
frog_legs = shop_recipe(SVEMeal.frog_legs, Region.adventurer_guild, 2000, {SVEFish.frog: 1, Ingredient.oil: 1, Ingredient.wheat_flour: 1},
content_pack=ModNames.sve)
glazed_butterfish = friendship_and_shop_recipe(SVEMeal.glazed_butterfish, NPC.gus, 10, Region.saloon, 4000,
{SVEFish.butterfish: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1}, ModNames.sve)
{SVEFish.butterfish: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1}, content_pack=ModNames.sve)
mixed_berry_pie = shop_recipe(SVEMeal.mixed_berry_pie, Region.saloon, 3500, {Fruit.strawberry: 6, SVEFruit.salal_berry: 6, Forageable.blackberry: 6,
SVEForage.bearberry: 6, Ingredient.sugar: 1, Ingredient.wheat_flour: 1},
ModNames.sve)
content_pack=ModNames.sve)
mushroom_berry_rice = friendship_and_shop_recipe(SVEMeal.mushroom_berry_rice, ModNPC.marlon, 6, Region.adventurer_guild, 1500,
{SVEForage.poison_mushroom: 3, SVEForage.red_baneberry: 10, Ingredient.rice: 1, Ingredient.sugar: 2},
ModNames.sve)
{SVEForage.poison_mushroom: 3, SVEForage.red_baneberry: 10,
Ingredient.rice: 1, Ingredient.sugar: 2}, content_pack=ModNames.sve)
seaweed_salad = shop_recipe(SVEMeal.seaweed_salad, Region.fish_shop, 1250, {SVEWaterItem.dulse_seaweed: 2, WaterItem.seaweed: 2, Ingredient.oil: 1},
ModNames.sve)
content_pack=ModNames.sve)
void_delight = friendship_and_shop_recipe(SVEMeal.void_delight, NPC.krobus, 10, Region.sewer, 5000,
{SVEFish.void_eel: 1, Loot.void_essence: 50, Loot.solar_essence: 20}, ModNames.sve)
{SVEFish.void_eel: 1, Loot.void_essence: 50, Loot.solar_essence: 20}, content_pack=ModNames.sve)
void_salmon_sushi = friendship_and_shop_recipe(SVEMeal.void_salmon_sushi, NPC.krobus, 10, Region.sewer, 5000,
{Fish.void_salmon: 1, ArtisanGood.void_mayonnaise: 1, WaterItem.seaweed: 3}, ModNames.sve)
{Fish.void_salmon: 1, ArtisanGood.void_mayonnaise: 1, WaterItem.seaweed: 3}, content_pack=ModNames.sve)
mushroom_kebab = friendship_recipe(DistantLandsMeal.mushroom_kebab, ModNPC.goblin, 2, {Mushroom.chanterelle: 1, Mushroom.common: 1,
Mushroom.red: 1, Material.wood: 1}, ModNames.distant_lands)
void_mint_tea = friendship_recipe(DistantLandsMeal.void_mint_tea, ModNPC.goblin, 4, {DistantLandsCrop.void_mint: 1}, ModNames.distant_lands)
Mushroom.red: 1, Material.wood: 1}, content_pack=ModNames.distant_lands)
void_mint_tea = friendship_recipe(DistantLandsMeal.void_mint_tea, ModNPC.goblin, 4, {DistantLandsCrop.void_mint: 1}, content_pack=ModNames.distant_lands)
crayfish_soup = friendship_recipe(DistantLandsMeal.crayfish_soup, ModNPC.goblin, 6, {Forageable.cave_carrot: 1, Fish.crayfish: 1,
DistantLandsFish.purple_algae: 1, WaterItem.white_algae: 1},
ModNames.distant_lands)
content_pack=ModNames.distant_lands)
pemmican = friendship_recipe(DistantLandsMeal.pemmican, ModNPC.goblin, 8, {Loot.bug_meat: 1, Fish.any: 1, Forageable.salmonberry: 3,
Material.stone: 2}, ModNames.distant_lands)
Material.stone: 2}, content_pack=ModNames.distant_lands)
special_pumpkin_soup = friendship_recipe(BoardingHouseMeal.special_pumpkin_soup, ModNPC.joel, 6, {Vegetable.pumpkin: 2, AnimalProduct.large_goat_milk: 1,
Vegetable.garlic: 1}, ModNames.boarding_house)
Vegetable.garlic: 1}, content_pack=ModNames.boarding_house)
diggers_delight = skill_recipe(ArchaeologyMeal.diggers_delight, ModSkill.archaeology, 3,
{Forageable.cave_carrot: 2, Ingredient.sugar: 1, AnimalProduct.milk: 1}, ModNames.archaeology)
{Forageable.cave_carrot: 2, Ingredient.sugar: 1, AnimalProduct.milk: 1}, content_pack=ModNames.archaeology)
rocky_root = skill_recipe(ArchaeologyMeal.rocky_root, ModSkill.archaeology, 7, {Forageable.cave_carrot: 3, Seed.coffee: 1, Material.stone: 1},
ModNames.archaeology)
content_pack=ModNames.archaeology)
ancient_jello = skill_recipe(ArchaeologyMeal.ancient_jello, ModSkill.archaeology, 9,
{WaterItem.cave_jelly: 6, Ingredient.sugar: 5, AnimalProduct.egg: 1, AnimalProduct.milk: 1, Artifact.chipped_amphora: 1},
ModNames.archaeology)
content_pack=ModNames.archaeology)
grilled_cheese = skill_recipe(TrashyMeal.grilled_cheese, ModSkill.binning, 1, {Meal.bread: 1, ArtisanGood.cheese: 1}, ModNames.binning_skill)
fish_casserole = skill_recipe(TrashyMeal.fish_casserole, ModSkill.binning, 8, {Fish.any: 1, AnimalProduct.milk: 1, Vegetable.carrot: 1}, ModNames.binning_skill)
grilled_cheese = skill_recipe(TrashyMeal.grilled_cheese, ModSkill.binning, 1, {Meal.bread: 1, ArtisanGood.cheese: 1}, content_pack=ModNames.binning_skill)
fish_casserole = skill_recipe(TrashyMeal.fish_casserole, ModSkill.binning, 8, {Fish.any: 1, AnimalProduct.milk: 1, Vegetable.carrot: 1},
content_pack=ModNames.binning_skill)
all_cooking_recipes_by_name = {recipe.meal: recipe for recipe in all_cooking_recipes}

View File

@@ -121,6 +121,17 @@ class ShopSource(RecipeSource):
return f"ShopSource at {self.region} costing {self.price}g"
class ShopWithKnownRecipeSource(ShopSource):
recipe_required: str
def __init__(self, region: str, price: int, recipe_required: str):
super().__init__(region, price)
self.recipe_required = recipe_required
def __repr__(self):
return f"ShopSource at {self.region} costing {self.price}g"
class ShopFriendshipSource(RecipeSource):
friend: str
hearts: int

View File

@@ -1,9 +1,15 @@
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Tuple
from .game_item import Requirement
from ..strings.tool_names import ToolMaterial
@dataclass(frozen=True)
class HasItemRequirement(Requirement):
item: str
@dataclass(frozen=True)
class BookRequirement(Requirement):
book: str
@@ -47,8 +53,19 @@ class QuestRequirement(Requirement):
@dataclass(frozen=True)
class RelationshipRequirement(Requirement):
class MeetRequirement(Requirement):
npc: str
@dataclass(frozen=True)
class SpecificFriendRequirement(Requirement):
npc: str
hearts: int
@dataclass(frozen=True)
class NumberOfFriendsRequirement(Requirement):
friends: int
hearts: int
@@ -60,3 +77,125 @@ class FishingRequirement(Requirement):
@dataclass(frozen=True)
class WalnutRequirement(Requirement):
amount: int
@dataclass(frozen=True)
class TotalEarningsRequirement(Requirement):
amount: int
@dataclass(frozen=True)
class GrangeDisplayRequirement(Requirement):
pass
@dataclass(frozen=True)
class EggHuntRequirement(Requirement):
pass
@dataclass(frozen=True)
class FishingCompetitionRequirement(Requirement):
pass
@dataclass(frozen=True)
class LuauDelightRequirementRequirement(Requirement):
pass
@dataclass(frozen=True)
class MovieRequirement(Requirement):
pass
@dataclass(frozen=True)
class ForgeInfinityWeaponRequirement(Requirement):
pass
@dataclass(frozen=True)
class CaughtFishRequirement(Requirement):
number_fish: int
unique: bool = field(kw_only=True)
@dataclass(frozen=True)
class MuseumCompletionRequirement(Requirement):
number_donated: int = 95
@dataclass(frozen=True)
class FullShipmentRequirement(Requirement):
pass
@dataclass(frozen=True)
class BuildingRequirement(Requirement):
building: str
@dataclass(frozen=True)
class CookedRecipesRequirement(Requirement):
number_of_recipes: int
@dataclass(frozen=True)
class CraftedItemsRequirement(Requirement):
number_of_recipes: int
@dataclass(frozen=True)
class HelpWantedRequirement(Requirement):
number_of_quests: int
@dataclass(frozen=True)
class ShipOneCropRequirement(Requirement):
number: int
@dataclass(frozen=True)
class ReceivedRaccoonsRequirement(Requirement):
number_of_raccoons: int
@dataclass(frozen=True)
class PrizeMachineRequirement(Requirement):
number_of_tickets: int
@dataclass(frozen=True)
class AllAchievementsRequirement(Requirement):
pass
@dataclass(frozen=True)
class PerfectionPercentRequirement(Requirement):
percent: int
@dataclass(frozen=True)
class ReadAllBooksRequirement(Requirement):
pass
@dataclass(frozen=True)
class MinesRequirement(Requirement):
floor: int
@dataclass(frozen=True)
class DangerousMinesRequirement(Requirement):
floor: int
@dataclass(frozen=True)
class MonsterKillRequirement(Requirement):
monsters: Tuple[str, ...]
amount: int = 1
@dataclass(frozen=True)
class CatalogueRequirement(Requirement):
catalogue: str

View File

@@ -0,0 +1,76 @@
from dataclasses import dataclass
from typing import Tuple, Dict, List
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
from ..strings.crop_names import Vegetable, Fruit
from ..strings.flower_names import Flower
from ..strings.food_names import Meal, Beverage
from ..strings.forageable_names import Forageable
from ..strings.metal_names import Mineral, MetalBar
from ..strings.villager_names import NPC
class SecretNote:
note_1 = "Secret Note #1: A Page From Abigail's Diary"
note_2 = "Secret Note #2: Sam's Holiday Shopping List"
note_3 = "Secret Note #3: Leah's Perfect Dinner"
note_4 = "Secret Note #4: Maru's Greatest Invention Yet"
note_5 = "Secret Note #5: Penny gets everyone something they love"
note_6 = "Secret Note #6: Stardrop Saloon Special Orders"
note_7 = "Secret Note #7: Older Bachelors In Town"
note_8 = "Secret Note #8: To Haley And Emily"
note_9 = "Secret Note #9: Alex's Strength Training Diet"
note_10 = "Secret Note #10: Cryptic Note"
note_11 = "Secret Note #11: Marnie's Memory"
note_12 = "Secret Note #12: Good Things In Garbage Cans"
note_13 = "Secret Note #13: Junimo Plush"
note_14 = "Secret Note #14: Stone Junimo"
note_15 = "Secret Note #15: Mermaid Show"
note_16 = "Secret Note #16: Treasure Chest"
note_17 = "Secret Note #17: Green Strange Doll"
note_18 = "Secret Note #18: Yellow Strange Doll"
note_19_part_1 = "Secret Note #19: Solid Gold Lewis"
note_19_part_2 = "Secret Note #19: In Town For All To See"
note_20 = "Secret Note #20: Special Charm"
note_21 = "Secret Note #21: A Date In Nature"
note_22 = "Secret Note #22: The Mysterious Qi"
note_23 = "Secret Note #23: Strange Note"
note_24 = "Secret Note #24: M. Jasper's Book On Junimos"
note_25 = "Secret Note #25: Ornate Necklace"
note_26 = "Secret Note #26: Ancient Farming Secrets"
note_27 = "Secret Note #27: A Compendium Of My Greatest Discoveries"
@dataclass(frozen=True)
class RequiredGifts:
npc: str
gifts: Tuple[str, ...]
gift_requirements: Dict[str, List[RequiredGifts]] = {
SecretNote.note_1: [RequiredGifts(NPC.abigail, (Vegetable.pumpkin, Mineral.amethyst, Meal.chocolate_cake, Meal.spicy_eel, Meal.blackberry_cobbler,)), ],
SecretNote.note_2: [RequiredGifts(NPC.sebastian, (Mineral.frozen_tear, Meal.sashimi,)),
RequiredGifts(NPC.penny, (Mineral.emerald, Flower.poppy,)),
RequiredGifts(NPC.vincent, (Fruit.grape, Meal.cranberry_candy,)),
RequiredGifts(NPC.jodi, (Meal.crispy_bass, Meal.pancakes,)),
RequiredGifts(NPC.kent, (Meal.fiddlehead_risotto, Meal.roasted_hazelnuts,)),
RequiredGifts(NPC.sam, (Forageable.cactus_fruit, Meal.maple_bar, Meal.pizza,)), ],
SecretNote.note_3: [RequiredGifts(NPC.leah, (Meal.salad, ArtisanGood.goat_cheese, AnimalProduct.truffle, ArtisanGood.wine,)), ],
SecretNote.note_4: [RequiredGifts(NPC.maru, (MetalBar.gold, MetalBar.iridium, ArtisanGood.battery_pack, Mineral.diamond, Fruit.strawberry,)), ],
SecretNote.note_5: [RequiredGifts(NPC.pam, (Vegetable.parsnip, Meal.glazed_yams,)),
RequiredGifts(NPC.jas, (Flower.fairy_rose, Meal.plum_pudding,)),
RequiredGifts(NPC.vincent, (Meal.pink_cake, Fruit.grape,)),
RequiredGifts(NPC.george, (Forageable.leek, Meal.fried_mushroom,)),
RequiredGifts(NPC.evelyn, (Vegetable.beet, Flower.tulip,)), ],
SecretNote.note_6: [RequiredGifts(NPC.lewis, (Meal.autumn_bounty,)),
RequiredGifts(NPC.marnie, (Meal.pumpkin_pie,)),
RequiredGifts(NPC.demetrius, (Meal.bean_hotpot,)),
RequiredGifts(NPC.caroline, (Meal.fish_taco,)), ],
SecretNote.note_7: [RequiredGifts(NPC.harvey, (Beverage.coffee, ArtisanGood.pickles,)),
RequiredGifts(NPC.elliott, (Meal.crab_cakes, Fruit.pomegranate,)),
RequiredGifts(NPC.shane, (Beverage.beer, Meal.pizza, Meal.pepper_poppers,)), ],
SecretNote.note_8: [RequiredGifts(NPC.haley, (Meal.pink_cake, Flower.sunflower,)),
RequiredGifts(NPC.emily, (Mineral.amethyst, Mineral.aquamarine, Mineral.emerald, Mineral.jade, Mineral.ruby, Mineral.topaz, AnimalProduct.wool,)), ],
SecretNote.note_9: [RequiredGifts(NPC.alex, (Meal.complete_breakfast, Meal.salmon_dinner,)), ],
}

View File

@@ -0,0 +1,33 @@
from typing import List
from worlds.stardew_valley.strings.animal_product_names import AnimalProduct
from worlds.stardew_valley.strings.forageable_names import Forageable
all_shirts = []
all_considered_shirts = []
class Shirt:
name: str
required_items: List[str]
def __init__(self, name: str, items: List[str]):
self.name = name
self.required_items = items
# consider_in_logic exists as a temporary measure because I don't feel like writing out the logic for every single shirt at this stage,
# and I only need some of them for the meme bundle
def shirt(name: str, items: str | List[str], consider_in_logic: bool = True) -> Shirt:
if isinstance(items, str):
items = [items]
new_shirt = Shirt(name, items)
all_shirts.append(new_shirt)
if consider_in_logic:
all_considered_shirts.append(new_shirt)
return new_shirt
class Shirts:
vacation = shirt("Vacation Shirt", Forageable.coconut)
green_jacket = shirt("Green Jacket Shirt", AnimalProduct.duck_egg)

View File

@@ -1,21 +1,23 @@
from collections.abc import Iterable
from dataclasses import dataclass
from typing import Tuple, Optional
from .game_item import Source
from .game_item import Source, Requirement
from ..strings.currency_names import Currency
from ..strings.season_names import Season
ItemPrice = Tuple[int, str]
ItemPrice = tuple[int, str]
@dataclass(frozen=True, kw_only=True)
class ShopSource(Source):
shop_region: str
money_price: Optional[int] = None
items_price: Optional[Tuple[ItemPrice, ...]] = None
seasons: Tuple[str, ...] = Season.all
price: int | None = None
items_price: tuple[ItemPrice, ...] | None = None
seasons: tuple[str, ...] = Season.all
currency: str = Currency.money
def __post_init__(self):
assert self.money_price is not None or self.items_price is not None, "At least money price or items price need to be defined."
assert self.price is not None or self.items_price is not None, "At least money price or items price need to be defined."
assert self.items_price is None or all(isinstance(p, tuple) for p in self.items_price), "Items price should be a tuple."
@@ -37,3 +39,13 @@ class PrizeMachineSource(Source):
@dataclass(frozen=True, kw_only=True)
class FishingTreasureChestSource(Source):
amount: int
@dataclass(frozen=True, kw_only=True)
class HatMouseSource(Source):
price: int | None = None
unlock_requirements: tuple[Requirement, ...] | None = None
@property
def all_requirements(self) -> Iterable[Requirement]:
return self.other_requirements + (self.unlock_requirements or ())

View File

@@ -4,7 +4,8 @@ from typing import Tuple, Optional
from ..mods.mod_data import ModNames
from ..strings.food_names import Beverage
from ..strings.generic_names import Generic
from ..strings.region_names import Region, SVERegion, AlectoRegion, BoardingHouseRegion, LaceyRegion, LogicRegion
from ..strings.metal_names import Mineral, Fossil
from ..strings.region_names import Region, SVERegion, AlectoRegion, BoardingHouseRegion, LaceyRegion, LogicRegion, RileyRegion
from ..strings.season_names import Season
from ..strings.villager_names import NPC, ModNPC
@@ -43,6 +44,16 @@ sewers = (Region.sewer,)
island = (Region.island_east,)
secret_woods = (Region.secret_woods,)
wizard_tower = (Region.wizard_tower,)
sam_house = (Region.sam_house,)
pierre_shop = (Region.pierre_store,)
haley_house = (Region.haley_house,)
saloon = (Region.saloon,)
blacksmith = (Region.blacksmith,)
trailer = (Region.trailer,)
leah_house = (Region.leah_house,)
linus_tent = (Region.tent,)
lewis_house = (Region.mayor_house,)
fish_shop = (Region.fish_shop,)
# Stardew Valley Expanded Locations
adventurer = (Region.adventurer_guild,)
@@ -53,6 +64,8 @@ museum = (Region.museum,)
jojamart = (Region.jojamart,)
railroad = (Region.railroad,)
junimo = (SVERegion.junimo_woods,)
olivia_house = (SVERegion.jenkins_residence,)
andy_house = (SVERegion.fairhaven_farm,)
# Stray Locations
witch_swamp = (Region.witch_swamp,)
@@ -60,6 +73,7 @@ witch_attic = (AlectoRegion.witch_attic,)
hat_house = (LaceyRegion.hat_house,)
the_lost_valley = (BoardingHouseRegion.the_lost_valley,)
boarding_house = (BoardingHouseRegion.boarding_house_first,)
riley_house = (RileyRegion.riley_house,)
golden_pumpkin = ("Golden Pumpkin",)
# magic_rock_candy = ("Magic Rock Candy",)
@@ -90,7 +104,7 @@ pizza = ("Pizza",)
tigerseye = ("Tigerseye",)
sam_loves = cactus_fruit + maple_bar + pizza + tigerseye
frozen_tear = ("Frozen Tear",)
obsidian = ("Obsidian",)
obsidian = (Mineral.obsidian,)
# pumpkin_soup = ("Pumpkin Soup",)
# sashimi = ("Sashimi",)
void_egg = ("Void Egg",)
@@ -252,7 +266,7 @@ energy_tonic = ("Energy Tonic",)
kale = ("Kale",)
muscle_remedy = ("Muscle Remedy",)
vegetable_medley = ("Vegetable Medley",)
trilobite = ("Trilobite",)
trilobite = (Fossil.trilobite,)
golden_mask = ("Golden Mask",)
rainbow_shell = ("Rainbow Shell",)
blue_jazz = ("Blue Jazz",)
@@ -361,39 +375,39 @@ def villager(name: str, bachelor: bool, locations: Tuple[str, ...], birthday: st
return Villager(name, bachelor, locations, birthday, gifts, available, mod_name)
josh = villager(NPC.alex, True, town + alex_house, Season.summer, universal_loves + complete_breakfast + salmon_dinner, True)
elliott = villager(NPC.elliott, True, town + beach + elliott_house, Season.fall, universal_loves + elliott_loves, True)
harvey = villager(NPC.harvey, True, town + hospital, Season.winter, universal_loves + harvey_loves, True)
sam = villager(NPC.sam, True, town, Season.summer, universal_loves + sam_loves, True)
josh = villager(NPC.alex, True, alex_house, Season.summer, universal_loves + complete_breakfast + salmon_dinner, True)
elliott = villager(NPC.elliott, True, beach + elliott_house, Season.fall, universal_loves + elliott_loves, True)
harvey = villager(NPC.harvey, True, hospital, Season.winter, universal_loves + harvey_loves, True)
sam = villager(NPC.sam, True, sam_house, Season.summer, universal_loves + sam_loves, True)
sebastian = villager(NPC.sebastian, True, carpenter, Season.winter, universal_loves + sebastian_loves, True)
shane = villager(NPC.shane, True, ranch, Season.spring, universal_loves + shane_loves, True)
abigail = villager(NPC.abigail, True, town, Season.fall, universal_loves + abigail_loves, True)
emily = villager(NPC.emily, True, town, Season.spring, universal_loves + emily_loves, True)
haley = villager(NPC.haley, True, town, Season.spring, universal_loves_no_prismatic_shard + haley_loves, True)
leah = villager(NPC.leah, True, forest, Season.winter, universal_loves + leah_loves, True)
maru = villager(NPC.maru, True, carpenter + hospital + town, Season.summer, universal_loves + maru_loves, True)
penny = villager(NPC.penny, True, town, Season.fall, universal_loves_no_rabbit_foot + penny_loves, True)
caroline = villager(NPC.caroline, False, town, Season.winter, universal_loves + caroline_loves, True)
clint = villager(NPC.clint, False, town, Season.winter, universal_loves + clint_loves, True)
abigail = villager(NPC.abigail, True, pierre_shop, Season.fall, universal_loves + abigail_loves, True)
emily = villager(NPC.emily, True, haley_house, Season.spring, universal_loves + emily_loves, True)
haley = villager(NPC.haley, True, haley_house, Season.spring, universal_loves_no_prismatic_shard + haley_loves, True)
leah = villager(NPC.leah, True, forest + leah_house, Season.winter, universal_loves + leah_loves, True)
maru = villager(NPC.maru, True, carpenter + hospital, Season.summer, universal_loves + maru_loves, True)
penny = villager(NPC.penny, True, trailer, Season.fall, universal_loves_no_rabbit_foot + penny_loves, True)
caroline = villager(NPC.caroline, False, pierre_shop, Season.winter, universal_loves + caroline_loves, True)
clint = villager(NPC.clint, False, blacksmith, Season.winter, universal_loves + clint_loves, True)
demetrius = villager(NPC.demetrius, False, carpenter, Season.summer, universal_loves + demetrius_loves, True)
dwarf = villager(NPC.dwarf, False, mines_dwarf_shop, Season.summer, universal_loves + dwarf_loves, False)
evelyn = villager(NPC.evelyn, False, town, Season.winter, universal_loves + evelyn_loves, True)
george = villager(NPC.george, False, town, Season.fall, universal_loves + george_loves, True)
gus = villager(NPC.gus, False, town, Season.summer, universal_loves + gus_loves, True)
evelyn = villager(NPC.evelyn, False, alex_house, Season.winter, universal_loves + evelyn_loves, True)
george = villager(NPC.george, False, alex_house, Season.fall, universal_loves + george_loves, True)
gus = villager(NPC.gus, False, saloon, Season.summer, universal_loves + gus_loves, True)
jas = villager(NPC.jas, False, ranch, Season.summer, universal_loves + jas_loves, True)
jodi = villager(NPC.jodi, False, town, Season.fall, universal_loves + jodi_loves, True)
kent = villager(NPC.kent, False, town, Season.spring, universal_loves + kent_loves, False)
jodi = villager(NPC.jodi, False, sam_house, Season.fall, universal_loves + jodi_loves, True)
kent = villager(NPC.kent, False, sam_house, Season.spring, universal_loves + kent_loves, False)
krobus = villager(NPC.krobus, False, sewers, Season.winter, universal_loves + krobus_loves, False)
leo = villager(NPC.leo, False, island, Season.summer, universal_loves + leo_loves, False)
lewis = villager(NPC.lewis, False, town, Season.spring, universal_loves + lewis_loves, True)
linus = villager(NPC.linus, False, mountain, Season.winter, universal_loves + linus_loves, True)
lewis = villager(NPC.lewis, False, lewis_house, Season.spring, universal_loves + lewis_loves, True)
linus = villager(NPC.linus, False, mountain + linus_tent, Season.winter, universal_loves + linus_loves, True)
marnie = villager(NPC.marnie, False, ranch, Season.fall, universal_loves + marnie_loves, True)
pam = villager(NPC.pam, False, town, Season.spring, universal_loves + pam_loves, True)
pierre = villager(NPC.pierre, False, town, Season.spring, universal_loves + pierre_loves, True)
pam = villager(NPC.pam, False, trailer, Season.spring, universal_loves + pam_loves, True)
pierre = villager(NPC.pierre, False, pierre_shop, Season.spring, universal_loves + pierre_loves, True)
robin = villager(NPC.robin, False, carpenter, Season.fall, universal_loves + robin_loves, True)
sandy = villager(NPC.sandy, False, oasis, Season.fall, universal_loves + sandy_loves, False)
vincent = villager(NPC.vincent, False, town, Season.spring, universal_loves + vincent_loves, True)
willy = villager(NPC.willy, False, beach, Season.summer, universal_loves + willy_loves, True)
vincent = villager(NPC.vincent, False, sam_house, Season.spring, universal_loves + vincent_loves, True)
willy = villager(NPC.willy, False, beach + fish_shop, Season.summer, universal_loves + willy_loves, True)
wizard = villager(NPC.wizard, False, wizard_tower, Season.winter, universal_loves + wizard_loves, True)
# Custom NPCs
@@ -401,13 +415,13 @@ alec = villager(ModNPC.alec, True, forest, Season.winter, universal_loves + tril
ayeisha = villager(ModNPC.ayeisha, False, town, Season.summer, universal_loves + ayeisha_loves, True, ModNames.ayeisha)
delores = villager(ModNPC.delores, True, forest, Season.winter, universal_loves + delores_loves, True, ModNames.delores)
eugene = villager(ModNPC.eugene, True, forest, Season.spring, universal_loves + eugene_loves, True, ModNames.eugene)
jasper = villager(ModNPC.jasper, True, town, Season.fall, universal_loves + jasper_loves, True, ModNames.jasper)
jasper = villager(ModNPC.jasper, True, museum, Season.fall, universal_loves + jasper_loves, True, ModNames.jasper)
juna = villager(ModNPC.juna, False, forest, Season.summer, universal_loves + juna_loves, True, ModNames.juna)
kitty = villager(ModNPC.mr_ginger, False, forest, Season.summer, universal_loves + mister_ginger_loves, True, ModNames.ginger)
shiko = villager(ModNPC.shiko, True, town, Season.winter, universal_loves + shiko_loves, True, ModNames.shiko)
wellwick = villager(ModNPC.wellwick, True, forest, Season.winter, universal_loves + wellwick_loves, True, ModNames.wellwick)
shiko = villager(ModNPC.shiko, True, saloon, Season.winter, universal_loves + shiko_loves, True, ModNames.shiko)
wellwick = villager(ModNPC.wellwick, True, wizard_tower, Season.winter, universal_loves + wellwick_loves, True, ModNames.wellwick)
yoba = villager(ModNPC.yoba, False, secret_woods, Season.spring, universal_loves + yoba_loves, False, ModNames.yoba)
riley = villager(ModNPC.riley, True, town, Season.spring, universal_loves, True, ModNames.riley)
riley = villager(ModNPC.riley, True, riley_house, Season.spring, universal_loves, True, ModNames.riley)
zic = villager(ModNPC.goblin, False, witch_swamp, Season.fall, void_mayonnaise, False, ModNames.distant_lands)
alecto = villager(ModNPC.alecto, False, witch_attic, Generic.any, universal_loves, False, ModNames.alecto)
lacey = villager(ModNPC.lacey, True, forest, Season.spring, universal_loves, True, ModNames.lacey)
@@ -420,15 +434,15 @@ joel = villager(ModNPC.joel, False, boarding_house, Season.winter, universal_lov
# SVE Villagers
claire = villager(ModNPC.claire, True, town + jojamart, Season.fall, universal_loves + claire_loves, True, ModNames.sve)
lance = villager(ModNPC.lance, True, adventurer + highlands + island, Season.spring, lance_loves, False, ModNames.sve)
mommy = villager(ModNPC.olivia, True, town, Season.spring, universal_loves_no_rabbit_foot + olivia_loves, True, ModNames.sve)
mommy = villager(ModNPC.olivia, True, olivia_house, Season.spring, universal_loves_no_rabbit_foot + olivia_loves, True, ModNames.sve)
sophia = villager(ModNPC.sophia, True, bluemoon, Season.winter, universal_loves_no_rabbit_foot + sophia_loves, True, ModNames.sve)
victor = villager(ModNPC.victor, True, town, Season.summer, universal_loves + victor_loves, True, ModNames.sve)
andy = villager(ModNPC.andy, False, forest, Season.spring, universal_loves + andy_loves, True, ModNames.sve)
victor = villager(ModNPC.victor, True, olivia_house, Season.summer, universal_loves + victor_loves, True, ModNames.sve)
andy = villager(ModNPC.andy, False, andy_house, Season.spring, universal_loves + andy_loves, True, ModNames.sve)
apples = villager(ModNPC.apples, False, aurora + junimo, Generic.any, starfruit, False, ModNames.sve)
gunther = villager(ModNPC.gunther, False, museum, Season.winter, universal_loves + gunther_loves, True, ModNames.sve)
martin = villager(ModNPC.martin, False, town + jojamart, Season.summer, universal_loves + martin_loves, True, ModNames.sve)
marlon = villager(ModNPC.marlon, False, adventurer, Season.winter, universal_loves + marlon_loves, False, ModNames.sve)
morgan = villager(ModNPC.morgan, False, forest, Season.fall, universal_loves_no_rabbit_foot + morgan_loves, False, ModNames.sve)
morgan = villager(ModNPC.morgan, False, wizard_tower, Season.fall, universal_loves_no_rabbit_foot + morgan_loves, False, ModNames.sve)
scarlett = villager(ModNPC.scarlett, False, bluemoon, Season.summer, universal_loves + scarlett_loves, False, ModNames.sve)
susan = villager(ModNPC.susan, False, railroad, Season.fall, universal_loves + susan_loves, False, ModNames.sve)
morris = villager(ModNPC.morris, False, jojamart, Season.spring, universal_loves + morris_loves, True, ModNames.sve)

View File

@@ -36,10 +36,12 @@ The player can choose from a number of goals, using their YAML options.
- Become a [Craft Master](https://stardewvalleywiki.com/Crafting) by crafting every item
- Earn the title of [Legend](https://stardewvalleywiki.com/Gold) by earning 10 000 000g
- Solve the [Mystery of the Stardrops](https://stardewvalleywiki.com/Stardrop) by finding every stardrop
- Find and wear every [Hat](https://stardewvalleywiki.com/Hats) in the game.
- Find and eat every item in the game.
- Finish 100% of your randomizer slot with Allsanity: Complete every check in your slot
- Achieve [Perfection](https://stardewvalleywiki.com/Perfection) in your save file
The following goals [Community Center, Master Angler, Protector of the Valley, Full Shipment and Gourmet Chef] will adapt
The following goals [Community Center, Master Angler, Protector of the Valley, Full Shipment, Gourmet Chef and Mad Hatter] will adapt
to other options in your slots, and are therefore customizable in duration and difficulty. For example, if you set "Fishsanity"
to "Exclude Legendaries", and pick the Master Angler goal, you will not need to catch the legendaries to complete the goal.
@@ -78,6 +80,12 @@ There also are a number of location checks that are optional, and individual pla
- [Shipsanity](https://stardewvalleywiki.com/Shipping): Shipping individual items
- [Booksanity](https://stardewvalleywiki.com/Books): Reading individual books
- [Walnutsanity](https://stardewvalleywiki.com/Golden_Walnut): Collecting Walnuts on Ginger Island
- [Hatsanity](https://stardewvalleywiki.com/Hats): Wear individual Hats
- [Secretsanity](https://stardewvalleywiki.com/Secrets): Find secrets and easter eggs
- [Moviesanity](https://stardewvalleywiki.com/Movie_Theater): Watch movies with villagers and optionally share snacks with them
- Eatsanity: Eat every item in the game
And more!
## Which items can be in another player's world?
@@ -138,12 +146,11 @@ This means that, for these specific mods, if you decide to include them in your
with the assumption that you will install and play with these mods. The multiworld will contain related items and locations
for these mods, the specifics will vary from mod to mod
[Supported Mods Documentation](https://github.com/agilbert1412/StardewArchipelago/blob/6.x.x/Documentation/Supported%20Mods.md)
[Supported Mods Documentation](https://github.com/agilbert1412/StardewArchipelago/blob/7.x.x/Documentation/Supported%20Mods.md)
List of supported mods:
- General
- [Stardew Valley Expanded](https://www.nexusmods.com/stardewvalley/mods/3753)
- [Skull Cavern Elevator](https://www.nexusmods.com/stardewvalley/mods/963)
- [Bigger Backpack](https://www.nexusmods.com/stardewvalley/mods/1845)
- [Tractor Mod](https://www.nexusmods.com/stardewvalley/mods/1401)
@@ -169,5 +176,5 @@ Some of these mods might need a patch mod to tie the randomizer with the mod. Th
You cannot play an Archipelago Slot in multiplayer at the moment. There are no short-term plans to support that feature.
You can, however, send Stardew Valley objects as gifts from one Stardew Player to another Stardew , or a player in another game that supports gifting, using
You can, however, send Stardew Valley objects as gifts from one Stardew Player to another Stardew, or a player in another game that supports gifting, using
in-game Joja Prime delivery, for a fee. This exclusive feature can be turned off if you don't want to send and receive gifts.

View File

@@ -3,35 +3,49 @@
## Required Software
- Stardew Valley 1.6 on PC (Recommended: [Steam version](https://store.steampowered.com/app/413150/Stardew_Valley/))
- SMAPI ([Mod loader for Stardew Valley](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files))
- [StardewArchipelago Mod Release 6.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- It is important to use a mod release of version 6.x.x to play seeds that have been generated here. Later releases
- SMAPI ([Mod loader for Stardew Valley](https://smapi.io/) ([Nexus](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files) | [Github](https://github.com/Pathoschild/SMAPI/releases))
- [StardewArchipelago Mod Release 7.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- It is important to use a mod release of version 7.x.x to play seeds that have been generated here. Later releases
can only be used with later releases of the world generator, that are not hosted on archipelago.gg yet.
## Optional Software
- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases)
* (Only for the TextClient)
* This provides access to various built-in tools, text client, and local generation and hosting
- [Universal Tracker](https://github.com/FarisTheAncient/Archipelago/releases?q=Tracker)
* If you installed Archipelago, you can use a custom Stardew Tracker that depends on Universal Tracker.
- Other Stardew Valley Mods [Nexus Mods](https://www.nexusmods.com/stardewvalley)
* There are [supported mods](https://github.com/agilbert1412/StardewArchipelago/blob/6.x.x/Documentation/Supported%20Mods.md)
* There are [supported mods](https://github.com/agilbert1412/StardewArchipelago/blob/7.x.x/Documentation/Supported%20Mods.md)
that you can add to your yaml to include them with the Archipelago randomization
* It is **not** recommended to further mod Stardew Valley with unsupported mods, although it is possible to do so.
Mod interactions can be unpredictable, and no support will be offered for related bugs.
* The more unsupported mods you have, and the bigger they are, the more likely things are to break.
## Configuring your YAML file
## Configuring your Multiworld
### What is a YAML file and why do I need one?
### Customizing my options
You can customize your options by visiting the [Stardew Valley Player Options Page](/games/Stardew%20Valley/player-options)
From there, you can customize all your options, or use a preset. Then, you can either Generate a single player game, or export a yaml file.
### What is a YAML file and do I need one?
A yaml file serves to configure your game in the multiworld. If you play a solo game, you can skip it entirely.
If you intend to play in a multiworld, you will need to provide your yaml file to the person who is hosting the multiworld.
See the guide on setting up a basic YAML at the Archipelago setup
guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a YAML file?
### Creating a room
You can customize your options by visiting the [Stardew Valley Player Options Page](/games/Stardew%20Valley/player-options)
If you hosted your own game, you can now click "Create Room" and your multiworld will be ready for play!
## Joining a MultiWorld Game
Note that even if you play a solo game, you still connect to a server to play. Hosting on archipelago.gg is free and the easiest option, but you can also host it locally.
### Installing the mod
- Install [SMAPI](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files) by following the instructions on the mod page
@@ -41,7 +55,7 @@ your Stardew Valley "Mods" folder
- Otherwise just launch "StardewModdingAPI.exe" in your installation folder directly
- Stardew Valley should launch itself alongside a console which allows you to read mod information and interact with some of them.
### Connect to the MultiServer
### Connect to the Multiworld
Launch Stardew Valley with SMAPI. Once you have reached the Stardew Valley title screen, create a new farm.
@@ -51,7 +65,7 @@ On the new character creation page, you will see 3 new fields, used to link your
You can customize your farm and character as much as desired.
The Server text box needs to have both the address and the port, and your slotname is the name specified in your yaml
The Server text box needs to have both the address and the port, and your slotname is the name specified in your options. You can also see the name on the room page.
`archipelago.gg:38281`
@@ -62,13 +76,7 @@ The password is optional.
Your game will connect automatically to Archipelago, and reconnect automatically when loading the save, later.
You will never need to enter this information again for this character, unless your room changes its ip or port.
If the room's ip or port **does** change, you can follow these instructions to modify the connection information of your save file
- Launch modded Stardew Valley
- While **on the main menu** of the game, enter the follow command **in the SMAPI console**:
- `connect_override ip:port slot password`
- Example: `connect_override archipelago.gg:38281 StardewPlayer`
- Load your save game. The new connection information will be used, instead of the saved one
- Play a full day, sleep, and save the game. This connection information will overwrite the previous one and become permanent.
If the room's ip or port **does** change, you will be prompted, after the connection fails, to enter new values and try again.
### Interacting with the MultiWorld from in-game
@@ -82,14 +90,14 @@ Lastly, you can also run Archipelago commands `!help` from the in game chat box,
items, or check missing locations.
It is important to note that the Stardew Valley chat is fairly limited in its capabilities. For example, it doesn't allow
scrolling up to see history that has been pushed off screen. The SMAPI console running alonside your game will have the
scrolling up to see history that has been pushed off screen. The SMAPI console running alongside your game will have the
full history as well and may be better suited to read older messages.
For a better chat experience, you can also use the official Archipelago Text Client, altough it will not allow you to run
For a better chat experience, you can also use the official Archipelago Text Client, although it will not allow you to run
Stardew-exclusive commands.
### Playing with supported mods
See the [Supported mods documentation](https://github.com/agilbert1412/StardewArchipelago/blob/6.x.x/Documentation/Supported%20Mods.md)
See the [Supported mods documentation](https://github.com/agilbert1412/StardewArchipelago/blob/7.x.x/Documentation/Supported%20Mods.md)
### Multiplayer

View File

@@ -4,14 +4,14 @@
- Stardew Valley 1.6 sur PC (Recommandé: [Steam](https://store.steampowered.com/app/413150/Stardew_Valley/))
- SMAPI ([Mod loader pour Stardew Valley](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files))
- [StardewArchipelago Version 6.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- Il est important d'utiliser une release en 6.x.x pour jouer sur des seeds générées ici. Les versions ultérieures peuvent uniquement être utilisées pour des release ultérieures du générateur de mondes, qui ne sont pas encore hébergées sur archipelago.gg
- [StardewArchipelago Version 7.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- Il est important d'utiliser une release en 7.x.x pour jouer sur des seeds générées ici. Les versions ultérieures peuvent uniquement être utilisées pour des release ultérieures du générateur de mondes, qui ne sont pas encore hébergées sur archipelago.gg
## Logiciels optionnels
- Launcher Archipelago à partir de la [page des versions d'Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
- (Uniquement pour le client textuel)
- Autres [mods supportés](https://github.com/agilbert1412/StardewArchipelago/blob/6.x.x/Documentation/Supported%20Mods.md) que vous pouvez ajouter au yaml pour les inclure dans la randomization d'Archipelago
- Autres [mods supportés](https://github.com/agilbert1412/StardewArchipelago/blob/7.x.x/Documentation/Supported%20Mods.md) que vous pouvez ajouter au yaml pour les inclure dans la randomization d'Archipelago
- Il n'est **pas** recommandé de modder Stardew Valley avec des mods non supportés, même s'il est possible de le faire.
Les interactions entre mods peuvent être imprévisibles, et aucune aide ne sera fournie pour les bugs qui y sont liés.
@@ -80,7 +80,7 @@ Pour une meilleure expérience avec le chat, vous pouvez aussi utiliser le clien
### Jouer avec des mods supportés
Voir la [documentation des mods supportés](https://github.com/agilbert1412/StardewArchipelago/blob/6.x.x/Documentation/Supported%20Mods.md) (en Anglais).
Voir la [documentation des mods supportés](https://github.com/agilbert1412/StardewArchipelago/blob/7.x.x/Documentation/Supported%20Mods.md) (en Anglais).
### Multijoueur

View File

@@ -1 +1,4 @@
from .early_items import setup_early_items
from .fillers import generate_filler_choice_pool
from .item_creation import create_items
from .item_data import item_table, ItemData, Group, items_by_group, load_item_csv

View File

@@ -1,15 +1,17 @@
from random import Random
from . import options as stardew_options
from .content import StardewContent
from .strings.ap_names.ap_weapon_names import APWeapon
from .strings.ap_names.transport_names import Transportation
from .strings.building_names import Building
from .strings.region_names import Region
from .strings.season_names import Season
from .strings.skill_names import Skill
from .strings.tv_channel_names import Channel
from .strings.wallet_item_names import Wallet
from .. import options as stardew_options
from ..content import StardewContent
from ..content.vanilla.ginger_island import ginger_island_content_pack
from ..strings.ap_names.ap_option_names import ChefsanityOptionName, StartWithoutOptionName
from ..strings.ap_names.ap_weapon_names import APWeapon
from ..strings.ap_names.transport_names import Transportation
from ..strings.building_names import Building
from ..strings.region_names import Region
from ..strings.season_names import Season
from ..strings.skill_names import Skill
from ..strings.tv_channel_names import Channel
from ..strings.wallet_item_names import Wallet
early_candidate_rate = 4
always_early_candidates = [Region.greenhouse, Transportation.desert_obelisk, Wallet.rusty_key]
@@ -47,7 +49,7 @@ def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions,
if options.special_order_locations & stardew_options.SpecialOrderLocations.option_board:
early_candidates.append("Special Order Board")
if options.cooksanity != stardew_options.Cooksanity.option_none or options.chefsanity & stardew_options.Chefsanity.option_queen_of_sauce:
if options.cooksanity != stardew_options.Cooksanity.option_none or ChefsanityOptionName.queen_of_sauce in options.chefsanity:
early_candidates.append(Channel.queen_of_sauce)
if options.craftsanity != stardew_options.Craftsanity.option_none:
@@ -58,8 +60,9 @@ def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions,
else:
early_candidates.append(APWeapon.sword)
if options.exclude_ginger_island == stardew_options.ExcludeGingerIsland.option_false:
if content.is_enabled(ginger_island_content_pack):
early_candidates.append(Transportation.island_obelisk)
early_candidates.append(Transportation.boat_repair)
if options.walnutsanity.value:
early_candidates.append("Island North Turtle")
@@ -68,6 +71,14 @@ def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions,
if options.museumsanity != stardew_options.Museumsanity.option_none or options.shipsanity >= stardew_options.Shipsanity.option_full_shipment:
early_candidates.append(Wallet.metal_detector)
if StartWithoutOptionName.landslide in options.start_without:
early_candidates.append("Landslide Removed")
if StartWithoutOptionName.community_center in options.start_without:
early_candidates.append("Forest Magic")
early_candidates.append("Community Center Key")
early_candidates.append("Wizard Invitation")
early_forced.extend(random.sample(early_candidates, len(early_candidates) // early_candidate_rate))
for item_name in early_forced:

View File

@@ -0,0 +1,181 @@
from random import Random
from typing import List
from BaseClasses import Item, ItemClassification
from .filters import remove_excluded, remove_limited_amount_resource_packs, remove_already_included
from .item_data import items_by_group, Group, ItemData, StardewItemFactory, item_table
from ..content.game_content import StardewContent
from ..options import StardewValleyOptions, FestivalLocations
from ..strings.ap_names.ap_option_names import BuffOptionName, AllowedFillerOptionName
from ..strings.ap_names.buff_names import Buff
AllowedFillerTypesMap = {
AllowedFillerOptionName.farming: Group.FILLER_FARMING,
AllowedFillerOptionName.fishing: Group.FILLER_FISHING,
AllowedFillerOptionName.fruit_trees: Group.FILLER_FRUIT_TREES,
AllowedFillerOptionName.food: Group.FILLER_FOOD,
AllowedFillerOptionName.buff_food: Group.FILLER_BUFF_FOOD,
AllowedFillerOptionName.consumables: Group.FILLER_CONSUMABLE,
AllowedFillerOptionName.machines: Group.FILLER_MACHINE,
AllowedFillerOptionName.storage: Group.FILLER_STORAGE,
AllowedFillerOptionName.quality_of_life: Group.FILLER_QUALITY_OF_LIFE,
AllowedFillerOptionName.materials: Group.FILLER_MATERIALS,
AllowedFillerOptionName.currencies: Group.FILLER_CURRENCY,
AllowedFillerOptionName.money: Group.FILLER_MONEY,
AllowedFillerOptionName.hats: Group.FILLER_HAT,
AllowedFillerOptionName.decorations: Group.FILLER_DECORATION,
AllowedFillerOptionName.rings: Group.FILLER_RING,
}
def generate_filler_choice_pool(options: StardewValleyOptions, content: StardewContent) -> list[str]:
available_filler = get_all_filler_items(options)
available_filler = remove_excluded(available_filler, content, options)
available_filler = remove_limited_amount_resource_packs(available_filler)
return [item.name for item in available_filler]
def get_all_filler_items(options: StardewValleyOptions) -> list[ItemData]:
all_filler_items = []
allowed_filler_types = sorted(list(options.allowed_filler_items.value))
for allowed_filler_type in allowed_filler_types:
allowed_filler_group = AllowedFillerTypesMap[allowed_filler_type]
all_filler_items.extend([pack for pack in items_by_group[allowed_filler_group]])
all_filler_items.extend(items_by_group[Group.TRASH])
all_filler_items.extend(get_player_buffs(options))
all_filler_items.extend(get_traps(options))
return all_filler_items
def get_filler_weights(options: StardewValleyOptions, all_filler_packs: list[ItemData]) -> list[int]:
weights = []
for filler in all_filler_packs:
if filler.name in options.trap_distribution:
num = options.trap_distribution[filler.name]
else:
num = options.trap_distribution.default_weight
weights.append(num)
return weights
def generate_unique_filler_items(item_factory: StardewItemFactory, content: StardewContent, options: StardewValleyOptions, random: Random,
available_item_slots: int) -> list[Item]:
items = create_filler_festival_rewards(item_factory, content, options)
if len(items) > available_item_slots:
items = random.sample(items, available_item_slots)
return items
def create_filler_festival_rewards(item_factory: StardewItemFactory, content: StardewContent, options: StardewValleyOptions) -> list[Item]:
if options.festival_locations == FestivalLocations.option_disabled:
return []
filler_rewards = [item for item in items_by_group[Group.FESTIVAL] if item.classification == ItemClassification.filler]
filler_rewards = remove_excluded(filler_rewards, content, options)
return [item_factory(item) for item in filler_rewards]
def generate_resource_packs_and_traps(item_factory: StardewItemFactory,
options: StardewValleyOptions,
content: StardewContent,
random: Random,
already_added_items: list[Item],
available_item_slots: int) -> list[Item]:
def filler_factory(item: ItemData):
# Yes some fillers are progression. We add multiple fruit tree saplings for instance.
if ItemClassification.progression in item.classification:
return item_factory(item,
classification_pre_fill=ItemClassification.filler,
classification_post_fill=ItemClassification.progression_skip_balancing)
return item_factory(item)
already_added_items_names = {item.name for item in already_added_items}
priority_fillers = get_priority_fillers(options)
priority_fillers = remove_excluded(priority_fillers, content, options)
priority_fillers = remove_already_included(priority_fillers, already_added_items_names)
if available_item_slots < len(priority_fillers):
return [filler_factory(priority_filler)
for priority_filler in random.sample(priority_fillers, available_item_slots)]
chosen_fillers = []
chosen_fillers.extend([filler_factory(priority_filler) for priority_filler in priority_fillers])
available_item_slots -= len(priority_fillers)
already_added_items_names |= {priority_item.name for priority_item in priority_fillers}
all_fillers = get_all_filler_items(options)
all_fillers = remove_excluded(all_fillers, content, options)
all_fillers = remove_already_included(all_fillers, already_added_items_names)
filler_weights = get_filler_weights(options, all_fillers)
while available_item_slots > 0:
resource_pack = random.choices(all_fillers, weights=filler_weights, k=1)[0]
exactly_2 = Group.AT_LEAST_TWO in resource_pack.groups
while exactly_2 and available_item_slots == 1:
# We roll another filler since there is no place for the second one
resource_pack = random.choices(all_fillers, weights=filler_weights, k=1)[0]
exactly_2 = Group.AT_LEAST_TWO in resource_pack.groups
chosen_fillers.append(filler_factory(resource_pack))
available_item_slots -= 1
if exactly_2:
chosen_fillers.append(filler_factory(resource_pack))
available_item_slots -= 1
if resource_pack.has_limited_amount():
index = all_fillers.index(resource_pack)
all_fillers.pop(index)
filler_weights.pop(index)
return chosen_fillers
def get_priority_fillers(options: StardewValleyOptions) -> list[ItemData]:
buffs = get_player_buffs(options)
traps = get_traps(options)
return buffs + traps
def get_player_buffs(options: StardewValleyOptions) -> List[ItemData]:
buff_option = options.enabled_filler_buffs
allowed_buffs = []
if BuffOptionName.luck in buff_option:
allowed_buffs.append(item_table[Buff.luck])
if BuffOptionName.damage in buff_option:
allowed_buffs.append(item_table[Buff.damage])
if BuffOptionName.defense in buff_option:
allowed_buffs.append(item_table[Buff.defense])
if BuffOptionName.immunity in buff_option:
allowed_buffs.append(item_table[Buff.immunity])
if BuffOptionName.health in buff_option:
allowed_buffs.append(item_table[Buff.health])
if BuffOptionName.energy in buff_option:
allowed_buffs.append(item_table[Buff.energy])
if BuffOptionName.bite in buff_option:
allowed_buffs.append(item_table[Buff.bite_rate])
if BuffOptionName.fish_trap in buff_option:
allowed_buffs.append(item_table[Buff.fish_trap])
if BuffOptionName.fishing_bar in buff_option:
allowed_buffs.append(item_table[Buff.fishing_bar])
if BuffOptionName.quality in buff_option:
allowed_buffs.append(item_table[Buff.quality])
if BuffOptionName.glow in buff_option:
allowed_buffs.append(item_table[Buff.glow])
return allowed_buffs
def get_traps(options: StardewValleyOptions) -> list[ItemData]:
if not options.trap_difficulty.include_traps():
return []
return [
trap
for trap in items_by_group[Group.TRAP]
if trap.name not in options.trap_distribution or options.trap_distribution[trap.name] > 0
]

View File

@@ -0,0 +1,31 @@
from typing import Iterable
from .item_data import ItemData, Group, FILLER_GROUPS
from ..content import StardewContent
from ..options import StardewValleyOptions, Hatsanity
def remove_excluded(items: Iterable[ItemData], content: StardewContent, options: StardewValleyOptions) -> list[ItemData]:
filtered_items = [
item for item in items
if Group.DEPRECATED not in item.groups and content.are_all_enabled(item.content_packs)
]
if options.hatsanity == Hatsanity.preset_none:
return filtered_items
return [item for item in filtered_items if Group.FILLER_HAT not in item.groups]
def remove_limited_amount_resource_packs(packs: Iterable[ItemData]) -> list[ItemData]:
return [
resource_pack
for resource_pack in packs
if not resource_pack.has_limited_amount()
]
def remove_already_included(items: Iterable[ItemData], already_added_items: set[str]) -> list[ItemData]:
return [
item
for item in items
if item.name not in already_added_items or (item.has_any_group(*FILLER_GROUPS, Group.TRAP) and Group.MAXIMUM_ONE not in item.groups)
]

View File

@@ -1,30 +1,34 @@
import logging
from random import Random
from typing import List, Set
from typing import List
from BaseClasses import Item, ItemClassification
from .fillers import generate_resource_packs_and_traps, generate_unique_filler_items
from .filters import remove_excluded
from .item_data import StardewItemFactory, items_by_group, Group, item_table, ItemData
from ..content.feature import friendsanity
from ..content.game_content import StardewContent
from ..content.vanilla.ginger_island import ginger_island_content_pack
from ..content.vanilla.qi_board import qi_board_content_pack
from ..data.game_item import ItemTag
from ..mods.mod_data import ModNames
from ..options import StardewValleyOptions, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Museumsanity, \
from ..options import StardewValleyOptions, FestivalLocations, SpecialOrderLocations, SeasonRandomization, Museumsanity, \
ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \
Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity, Walnutsanity, EnabledFillerBuffs, TrapDifficulty
from ..strings.ap_names.ap_option_names import BuffOptionName, WalnutsanityOptionName
Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity, Walnutsanity, Moviesanity
from ..options.options import IncludeEndgameLocations, Friendsanity
from ..strings.ap_names.ap_option_names import WalnutsanityOptionName, SecretsanityOptionName, EatsanityOptionName, ChefsanityOptionName, StartWithoutOptionName
from ..strings.ap_names.ap_weapon_names import APWeapon
from ..strings.ap_names.buff_names import Buff
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade, Bookseller
from ..strings.ap_names.mods.mod_items import SVEQuestItem
from ..strings.backpack_tiers import Backpack
from ..strings.building_names import Building
from ..strings.currency_names import Currency
from ..strings.tool_names import Tool
from ..strings.wallet_item_names import Wallet
logger = logging.getLogger(__name__)
def get_too_many_items_error_message(locations_count: int, items_count: int) -> str:
return f"There should be at least as many locations [{locations_count}] as there are mandatory items [{items_count}]"
def create_items(item_factory: StardewItemFactory, locations_count: int, items_to_exclude: List[Item],
options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]:
@@ -38,21 +42,25 @@ def create_items(item_factory: StardewItemFactory, locations_count: int, items_t
items += unique_items
logger.debug(f"Created {len(unique_items)} unique items")
unique_filler_items = create_unique_filler_items(item_factory, options, random, locations_count - len(items))
unique_filler_items = generate_unique_filler_items(item_factory, content, options, random, locations_count - len(items))
items += unique_filler_items
logger.debug(f"Created {len(unique_filler_items)} unique filler items")
resource_pack_items = fill_with_resource_packs_and_traps(item_factory, options, random, items + items_to_exclude, locations_count - len(items))
resource_pack_items = generate_resource_packs_and_traps(item_factory, options, content, random, items + items_to_exclude, locations_count - len(items))
items += resource_pack_items
logger.debug(f"Created {len(resource_pack_items)} resource packs")
return items
def remove_items(items_to_remove, items):
for item in items_to_remove:
if item in items:
items.remove(item)
def remove_items(items_to_remove: List[Item], items: List[Item]):
for item_to_remove in items_to_remove:
for i, item_candidate in enumerate(items):
if item_to_remove != item_candidate:
continue
if item_to_remove.classification == item_candidate.classification and item_to_remove.advancement == item_candidate.advancement:
items.pop(i)
break
def remove_items_if_no_room_for_them(unique_items: List[Item], locations_count: int, random: Random):
@@ -66,7 +74,9 @@ def remove_items_if_no_room_for_them(unique_items: List[Item], locations_count:
removable_items = [item for item in unique_items if not item.classification & ItemClassification.progression]
else:
logger.debug(f"Player has more items than locations, trying to remove {number_of_items_to_remove} random filler items")
assert len(removable_items) >= number_of_items_to_remove, get_too_many_items_error_message(locations_count, len(unique_items))
count = len(unique_items)
assert len(removable_items) >= number_of_items_to_remove, \
f"There should be at least as many locations [{locations_count}] as there are mandatory items [{count - len(removable_items)}]"
items_to_remove = random.sample(removable_items, number_of_items_to_remove)
remove_items(items_to_remove, unique_items)
@@ -79,20 +89,20 @@ def create_unique_items(item_factory: StardewItemFactory, options: StardewValley
create_raccoons(item_factory, options, items)
items.append(item_factory(Wallet.metal_detector)) # Always offer at least one metal detector
create_backpack_items(item_factory, options, items)
create_weapons(item_factory, options, items)
create_backpack_items(item_factory, options, content, items)
create_weapons(item_factory, options, content, items)
items.append(item_factory("Skull Key"))
create_elevators(item_factory, options, items)
create_elevators(item_factory, options, content, items)
create_tools(item_factory, content, items)
create_skills(item_factory, content, items)
create_wizard_buildings(item_factory, options, items)
create_carpenter_buildings(item_factory, content, items)
create_wizard_buildings(item_factory, options, content, items)
create_carpenter_buildings(item_factory, options, content, items)
items.append(item_factory("Railroad Boulder Removed"))
items.append(item_factory(CommunityUpgrade.fruit_bats))
items.append(item_factory(CommunityUpgrade.mushroom_boxes))
items.append(item_factory("Beach Bridge"))
create_tv_channels(item_factory, options, items)
create_quest_rewards(item_factory, options, items)
create_quest_rewards(item_factory, options, content, items)
create_stardrops(item_factory, options, content, items)
create_museum_items(item_factory, options, items)
create_arcade_machine_items(item_factory, options, items)
@@ -104,84 +114,105 @@ def create_unique_items(item_factory: StardewItemFactory, options: StardewValley
create_friendsanity_items(item_factory, options, content, items, random)
create_festival_rewards(item_factory, options, items)
create_special_order_board_rewards(item_factory, options, items)
create_special_order_qi_rewards(item_factory, options, items)
create_walnuts(item_factory, options, items)
create_walnut_purchase_rewards(item_factory, options, items)
create_crafting_recipes(item_factory, options, items)
create_cooking_recipes(item_factory, options, items)
create_special_order_qi_rewards(item_factory, options, content, items)
create_walnuts(item_factory, options, content, items)
create_walnut_purchase_rewards(item_factory, content, items)
create_crafting_recipes(item_factory, options, content, items)
create_cooking_recipes(item_factory, options, content, items)
create_shipsanity_items(item_factory, options, items)
create_booksanity_items(item_factory, content, items)
create_booksanity_items(item_factory, options, content, items)
create_movie_items(item_factory, options, items)
create_secrets_items(item_factory, content, options, items)
create_eatsanity_enzyme_items(item_factory, options, items)
create_endgame_locations_items(item_factory, options, items)
create_goal_items(item_factory, options, items)
items.append(item_factory("Golden Egg"))
items.append(item_factory(CommunityUpgrade.mr_qi_plane_ride))
create_sve_special_items(item_factory, options, items)
create_magic_mod_spells(item_factory, options, items)
create_deepwoods_pendants(item_factory, options, items)
create_archaeology_items(item_factory, options, items)
items.append(item_factory(Wallet.mens_locker_key))
items.append(item_factory(Wallet.womens_locker_key))
create_sve_special_items(item_factory, content, items)
create_magic_mod_spells(item_factory, content, items)
create_deepwoods_pendants(item_factory, content, items)
create_archaeology_items(item_factory, content, items)
return items
def create_raccoons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
number_progressive_raccoons = 9
if options.bundle_per_room.value < 0:
number_progressive_raccoons -= options.bundle_per_room.value
if options.quest_locations.has_no_story_quests():
number_progressive_raccoons = number_progressive_raccoons - 1
items.extend(item_factory(item) for item in [CommunityUpgrade.raccoon] * number_progressive_raccoons)
def create_backpack_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if (options.backpack_progression == BackpackProgression.option_progressive or
options.backpack_progression == BackpackProgression.option_early_progressive):
items.extend(item_factory(item) for item in ["Progressive Backpack"] * 2)
if ModNames.big_backpack in options.mods:
items.append(item_factory("Progressive Backpack"))
def create_backpack_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
if options.backpack_progression == BackpackProgression.option_vanilla:
return
num_per_tier = options.backpack_size.count_per_tier()
backpack_tier_names = Backpack.get_purchasable_tiers(ModNames.big_backpack in content.registered_packs, StartWithoutOptionName.backpack in options.start_without)
num_backpacks = len(backpack_tier_names) * num_per_tier
items.extend(item_factory(item) for item in ["Progressive Backpack"] * num_backpacks)
def create_weapons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
weapons = weapons_count(options)
def create_footwear(item_factory: StardewItemFactory, number: int) -> List[Item]:
return [item_factory(APWeapon.footwear) for _ in range(number)]
def create_weapons(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
weapons = weapons_count(content)
items.extend(item_factory(item) for item in [APWeapon.slingshot] * 2)
monstersanity = options.monstersanity
ring_classification = ItemClassification.progression if options.bundle_randomization == BundleRandomization.option_meme else ItemClassification.useful
rings_items = [item for item in items_by_group[Group.FILLER_RING] if item.classification is not ItemClassification.filler]
if monstersanity == Monstersanity.option_none: # Without monstersanity, might not be enough checks to split the weapons
items.extend(item_factory(item) for item in [APWeapon.weapon] * weapons)
items.extend(item_factory(item) for item in [APWeapon.footwear] * 3) # 1-2 | 3-4 | 6-7-8
items.extend(create_footwear(item_factory, 3)) # 1-2 | 3-4 | 6-7-8
rings_items = [item for item in rings_items if item.classification is ItemClassification.progression]
items.extend(item_factory(item, classification_pre_fill=ring_classification) for item in rings_items)
return
items.extend(item_factory(item) for item in [APWeapon.sword] * weapons)
items.extend(item_factory(item) for item in [APWeapon.club] * weapons)
items.extend(item_factory(item) for item in [APWeapon.dagger] * weapons)
items.extend(item_factory(item) for item in [APWeapon.footwear] * 4) # 1-2 | 3-4 | 6-7-8 | 11-13
items.extend(create_footwear(item_factory, 4)) # 1-2 | 3-4 | 6-7-8 | 11-13
items.extend(item_factory(item, classification_pre_fill=ring_classification) for item in rings_items)
if monstersanity == Monstersanity.option_goals or monstersanity == Monstersanity.option_one_per_category or \
monstersanity == Monstersanity.option_short_goals or monstersanity == Monstersanity.option_very_short_goals:
return
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
rings_items = [item for item in items_by_group[Group.RING] if item.classification is not ItemClassification.filler]
else:
rings_items = [item for item in items_by_group[Group.RING]]
items.extend(item_factory(item) for item in rings_items)
def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
if options.elevator_progression == ElevatorProgression.option_vanilla:
return
items.extend([item_factory(item) for item in ["Progressive Mine Elevator"] * 24])
if ModNames.deepwoods in options.mods:
if ModNames.deepwoods in content.registered_packs:
items.extend([item_factory(item) for item in ["Progressive Woods Obelisk Sigils"] * 10])
if ModNames.skull_cavern_elevator in options.mods:
if ModNames.skull_cavern_elevator in content.registered_packs:
items.extend([item_factory(item) for item in ["Progressive Skull Cavern Elevator"] * 8])
def create_tools(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
tool_progression = content.features.tool_progression
for tool, count in tool_progression.tool_distribution.items():
item = item_table[tool_progression.to_progressive_item(tool)]
item = item_table[tool_progression.to_progressive_item_name(tool)]
# Trash can is only used in tool upgrade logic, so the last trash can is not progression because it basically does not unlock anything.
if tool == Tool.trash_can:
count -= 1
items.append(item_factory(item, ItemClassification.useful))
items.append(item_factory(item,
classification_pre_fill=ItemClassification.useful,
classification_post_fill=ItemClassification.progression_skip_balancing))
items.extend([item_factory(item) for _ in range(count)])
@@ -201,49 +232,53 @@ def create_skills(item_factory: StardewItemFactory, content: StardewContent, ite
items.append(item_factory(Wallet.mastery_of_the_five_ways))
def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
useless_buildings_classification = ItemClassification.progression_skip_balancing if world_is_perfection(options) else ItemClassification.useful
items.append(item_factory("Earth Obelisk", useless_buildings_classification))
items.append(item_factory("Water Obelisk", useless_buildings_classification))
def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
useful_buildings_classification = ItemClassification.progression_skip_balancing if goal_is_perfection(options) else ItemClassification.useful
items.append(item_factory("Earth Obelisk", classification_pre_fill=useful_buildings_classification))
items.append(item_factory("Water Obelisk", classification_pre_fill=useful_buildings_classification))
items.append(item_factory("Desert Obelisk"))
items.append(item_factory("Junimo Hut"))
items.append(item_factory("Gold Clock", useless_buildings_classification))
if options.exclude_ginger_island == ExcludeGingerIsland.option_false:
items.append(item_factory("Gold Clock", classification_pre_fill=useful_buildings_classification))
if content.is_enabled(ginger_island_content_pack):
items.append(item_factory("Island Obelisk"))
if ModNames.deepwoods in options.mods:
if content.is_enabled(ModNames.deepwoods):
items.append(item_factory("Woods Obelisk"))
def create_carpenter_buildings(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
def create_carpenter_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
building_progression = content.features.building_progression
if not building_progression.is_progressive:
return
for building in content.farm_buildings.values():
item_name, _ = building_progression.to_progressive_item(building.name)
items.append(item_factory(item_name))
if item_name in [Building.stable, Building.well] and options.bundle_randomization != BundleRandomization.option_meme:
items.append(item_factory(item_name, classification_pre_fill=ItemClassification.useful))
else:
items.append(item_factory(item_name))
def create_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
create_special_quest_rewards(item_factory, options, items)
def create_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
create_special_quest_rewards(item_factory, options, content, items)
create_help_wanted_quest_rewards(item_factory, options, items)
create_quest_rewards_sve(item_factory, options, items)
create_quest_rewards_sve(item_factory, options, content, items)
def create_special_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
def create_special_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
if options.quest_locations.has_no_story_quests():
return
# items.append(item_factory("Adventurer's Guild")) # Now unlocked always!
items.append(item_factory(Wallet.club_card))
items.append(item_factory(Wallet.magnifying_glass))
if ModNames.sve in options.mods:
items.append(item_factory(Wallet.magic_ink))
items.append(item_factory(Wallet.iridium_snake_milk))
if ModNames.sve in content.registered_packs:
items.append(item_factory(Wallet.bears_knowledge))
else:
items.append(item_factory(Wallet.bears_knowledge, ItemClassification.useful)) # Not necessary outside of SVE
items.append(item_factory(Wallet.iridium_snake_milk))
items.append(item_factory(Wallet.bears_knowledge, classification_pre_fill=ItemClassification.useful)) # Not necessary outside of SVE
items.append(item_factory("Dark Talisman"))
if options.exclude_ginger_island == ExcludeGingerIsland.option_false:
if content.is_enabled(ginger_island_content_pack):
items.append(item_factory("Fairy Dust Recipe"))
@@ -259,22 +294,24 @@ def create_help_wanted_quest_rewards(item_factory: StardewItemFactory, options:
def create_stardrops(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
stardrops_classification = get_stardrop_classification(options)
items.append(item_factory("Stardrop", stardrops_classification)) # The Mines level 100
items.append(item_factory("Stardrop", stardrops_classification)) # Old Master Cannoli
items.append(item_factory("Stardrop", stardrops_classification)) # Krobus Stardrop
items.append(item_factory("Stardrop", classification_pre_fill=stardrops_classification)) # The Mines level 100
items.append(item_factory("Stardrop", classification_pre_fill=stardrops_classification)) # Krobus Stardrop
if content.features.fishsanity.is_enabled:
items.append(item_factory("Stardrop", stardrops_classification)) # Master Angler Stardrop
if ModNames.deepwoods in options.mods:
items.append(item_factory("Stardrop", stardrops_classification)) # Petting the Unicorn
items.append(item_factory("Stardrop", classification_pre_fill=stardrops_classification)) # Master Angler Stardrop
if ModNames.deepwoods in content.registered_packs:
items.append(item_factory("Stardrop", classification_pre_fill=stardrops_classification)) # Petting the Unicorn
if content.features.friendsanity.is_enabled:
items.append(item_factory("Stardrop", stardrops_classification)) # Spouse Stardrop
items.append(item_factory("Stardrop", classification_pre_fill=stardrops_classification)) # Spouse Stardrop
if SecretsanityOptionName.easy in options.secretsanity:
# Always Progression as a different secret requires a stardrop
items.append(item_factory("Stardrop", classification_pre_fill=ItemClassification.progression)) # Old Master Cannoli.
def create_museum_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
items.append(item_factory(Wallet.rusty_key))
items.append(item_factory(Wallet.dwarvish_translation_guide))
items.append(item_factory("Ancient Seeds Recipe"))
items.append(item_factory("Stardrop", get_stardrop_classification(options)))
items.append(item_factory("Stardrop", classification_pre_fill=get_stardrop_classification(options)))
if options.museumsanity == Museumsanity.option_none:
return
items.extend(item_factory(item) for item in ["Magic Rock Candy"] * 10)
@@ -292,13 +329,10 @@ def create_friendsanity_items(item_factory: StardewItemFactory, options: Stardew
item_name = friendsanity.to_item_name(villager.name)
for _ in content.features.friendsanity.get_randomized_hearts(villager):
items.append(item_factory(item_name, ItemClassification.progression))
need_pet = options.goal == Goal.option_grandpa_evaluation
pet_item_classification = ItemClassification.progression_skip_balancing if need_pet else ItemClassification.useful
items.append(item_factory(item_name))
for _ in content.features.friendsanity.get_pet_randomized_hearts():
items.append(item_factory(friendsanity.pet_heart_item_name, pet_item_classification))
items.append(item_factory(friendsanity.pet_heart_item_name, classification_pre_fill=ItemClassification.progression_skip_balancing))
def create_babies(item_factory: StardewItemFactory, items: List[Item], random: Random):
@@ -359,12 +393,12 @@ def create_festival_rewards(item_factory: StardewItemFactory, options: StardewVa
return
festival_rewards = [item_factory(item) for item in items_by_group[Group.FESTIVAL] if item.classification != ItemClassification.filler]
items.extend([*festival_rewards, item_factory("Stardrop", get_stardrop_classification(options))])
items.extend([*festival_rewards, item_factory("Stardrop", classification_pre_fill=get_stardrop_classification(options))])
def create_walnuts(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
def create_walnuts(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
walnutsanity = options.walnutsanity
if options.exclude_ginger_island == ExcludeGingerIsland.option_true or walnutsanity == Walnutsanity.preset_none:
if not content.is_enabled(ginger_island_content_pack) or walnutsanity == Walnutsanity.preset_none:
return
# Give baseline walnuts just to be nice
@@ -391,8 +425,8 @@ def create_walnuts(item_factory: StardewItemFactory, options: StardewValleyOptio
items.extend([item_factory(item) for item in ["5 Golden Walnuts"] * num_penta_walnuts])
def create_walnut_purchase_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
def create_walnut_purchase_rewards(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
if not content.is_enabled(ginger_island_content_pack):
return
items.extend([item_factory("Boat Repair"),
@@ -420,15 +454,15 @@ def special_order_board_item_classification(item: ItemData, need_all_recipes: bo
return ItemClassification.useful
def create_special_order_qi_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
def create_special_order_qi_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
if not content.is_enabled(ginger_island_content_pack):
return
qi_gem_rewards = []
if options.bundle_randomization >= BundleRandomization.option_remixed:
qi_gem_rewards.append("15 Qi Gems")
qi_gem_rewards.append("15 Qi Gems")
if options.special_order_locations & SpecialOrderLocations.value_qi:
if content.is_enabled(qi_board_content_pack):
qi_gem_rewards.extend(["100 Qi Gems", "10 Qi Gems", "40 Qi Gems", "25 Qi Gems", "25 Qi Gems",
"40 Qi Gems", "20 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems"])
@@ -443,33 +477,33 @@ def create_tv_channels(item_factory: StardewItemFactory, options: StardewValleyO
items.extend([item_factory(item) for item in channels])
def create_crafting_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
def create_crafting_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
has_craftsanity = options.craftsanity == Craftsanity.option_all
crafting_recipes = []
crafting_recipes.extend([recipe for recipe in items_by_group[Group.QI_CRAFTING_RECIPE]])
if has_craftsanity:
crafting_recipes.extend([recipe for recipe in items_by_group[Group.CRAFTSANITY]])
crafting_recipes = remove_excluded_items(crafting_recipes, options)
crafting_recipes = remove_excluded(crafting_recipes, content, options)
items.extend([item_factory(item) for item in crafting_recipes])
def create_cooking_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
def create_cooking_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
chefsanity = options.chefsanity
if chefsanity == Chefsanity.option_none:
if chefsanity == Chefsanity.preset_none:
return
chefsanity_recipes_by_name = {recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_STARTER]} # Dictionary to not make duplicates
if chefsanity & Chefsanity.option_queen_of_sauce:
if ChefsanityOptionName.queen_of_sauce in chefsanity:
chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_QOS]})
if chefsanity & Chefsanity.option_purchases:
if ChefsanityOptionName.purchases in chefsanity:
chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_PURCHASE]})
if chefsanity & Chefsanity.option_friendship:
if ChefsanityOptionName.friendship in chefsanity:
chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_FRIENDSHIP]})
if chefsanity & Chefsanity.option_skills:
if ChefsanityOptionName.skills in chefsanity:
chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_SKILL]})
filtered_chefsanity_recipes = remove_excluded_items(list(chefsanity_recipes_by_name.values()), options)
filtered_chefsanity_recipes = remove_excluded(list(chefsanity_recipes_by_name.values()), content, options)
items.extend([item_factory(item) for item in filtered_chefsanity_recipes])
@@ -481,14 +515,95 @@ def create_shipsanity_items(item_factory: StardewItemFactory, options: StardewVa
items.append(item_factory(Wallet.metal_detector))
def create_booksanity_items(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
def create_booksanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
create_bookseller_items(item_factory, options, content, items)
booksanity = content.features.booksanity
if not booksanity.is_enabled:
return
items.extend(item_factory(item_table[booksanity.to_item_name(book.name)]) for book in content.find_tagged_items(ItemTag.BOOK_POWER))
progressive_lost_book = item_table[booksanity.progressive_lost_book]
items.extend(item_factory(progressive_lost_book) for _ in content.features.booksanity.get_randomized_lost_books())
# We do -1 here because the first lost book spawns freely in the museum
num_lost_books = len([book for book in content.features.booksanity.get_randomized_lost_books()]) - 1
items.extend(item_factory(progressive_lost_book) for _ in range(num_lost_books))
def create_bookseller_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
needs_books = options.shipsanity == Shipsanity.option_everything or content.features.booksanity.is_enabled or content.features.hatsanity.is_enabled
book_items = []
book_items.extend(item_factory(item_table[Bookseller.days]) for _ in range(4 if needs_books else 1))
if not needs_books:
book_items.extend(item_factory(item_table[Bookseller.days], classification_pre_fill=ItemClassification.filler) for _ in range(3))
book_items.extend(item_factory(item_table[Bookseller.stock_rare_books]) for _ in range(2 if needs_books else 1))
book_items.append(item_factory(item_table[Bookseller.stock_permanent_books]))
book_items.append(item_factory(item_table[Bookseller.stock_experience_books]))
if needs_books:
book_items.extend(item_factory(item_table[Bookseller.stock_experience_books], classification_pre_fill=ItemClassification.filler) for _ in range(2))
items.extend(book_items)
def create_movie_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if options.moviesanity.value < Moviesanity.option_all_movies:
return
items.extend(item_factory(item) for item in items_by_group[Group.MOVIESANITY])
def create_secrets_items(item_factory: StardewItemFactory, content: StardewContent, options: StardewValleyOptions, items: List[Item]):
if not options.secretsanity:
return
secret_items = []
if SecretsanityOptionName.easy in options.secretsanity:
secret_items.extend(items_by_group[Group.EASY_SECRET])
# if SecretsanityOptionName.fishing in options.secretsanity:
# secret_items.extend(items_by_group[Group.FISHING_SECRET]) # There are no longer any of these items, they are now part of FILLER_DECORATION
# if SecretsanityOptionName.difficult in options.secretsanity:
# items.extend(item_factory(item) for item in items_by_group[Group.DIFFICULT_SECRET])
if SecretsanityOptionName.secret_notes in options.secretsanity:
secret_items.extend(items_by_group[Group.SECRET_NOTES_SECRET])
if options.quest_locations.has_no_story_quests():
secret_items.append(item_table[Wallet.club_card])
secret_items.append(item_table[Wallet.iridium_snake_milk])
filtered_secret_items = remove_excluded(list(secret_items), content, options)
items.extend([item_factory(item) for item in filtered_secret_items])
def create_eatsanity_enzyme_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if EatsanityOptionName.lock_effects not in options.eatsanity:
return
# These items unlock progressively stronger ability to digest food items that give the associated buff
# Upon receiving the enzyme, you also get a temporary buff of whatever the effect is
# Stamina and Health items can go beyond their original max value, but the buffs cannot.
items.extend(item_factory(item) for item in ["Stamina Enzyme"]*10)
items.extend(item_factory(item) for item in ["Health Enzyme"]*10)
items.extend(item_factory(item) for item in ["Speed Enzyme"]*5)
items.extend(item_factory(item) for item in ["Luck Enzyme"]*5)
items.extend(item_factory(item) for item in ["Farming Enzyme"]*5)
items.extend(item_factory(item) for item in ["Foraging Enzyme"]*5)
items.extend(item_factory(item) for item in ["Fishing Enzyme"]*5)
items.extend(item_factory(item) for item in ["Mining Enzyme"]*5)
items.extend(item_factory(item) for item in ["Magnetism Enzyme"]*2)
items.extend(item_factory(item) for item in ["Defense Enzyme"]*5)
items.extend(item_factory(item) for item in ["Attack Enzyme"]*5)
items.extend(item_factory(item) for item in ["Max Stamina Enzyme"]*3)
items.extend(item_factory(item) for item in ["Squid Ink Enzyme"])
items.extend(item_factory(item) for item in ["Monster Musk Enzyme"])
items.extend(item_factory(item) for item in ["Oil Of Garlic Enzyme"])
items.extend(item_factory(item) for item in ["Tipsy Enzyme"])
def create_endgame_locations_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if options.include_endgame_locations == IncludeEndgameLocations.option_false:
return
items_to_add = []
items_to_add.extend(items_by_group[Group.ENDGAME_LOCATION_ITEMS])
if options.friendsanity != Friendsanity.option_all_with_marriage:
for portrait in items_by_group[Group.REQUIRES_FRIENDSANITY_MARRIAGE]:
items_to_add.remove(portrait)
items.extend(item_factory(item) for item in items_to_add)
def create_goal_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
@@ -499,233 +614,66 @@ def create_goal_items(item_factory: StardewItemFactory, options: StardewValleyOp
items.append(item_factory(Wallet.metal_detector))
def create_archaeology_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
mods = options.mods
if ModNames.archaeology not in mods:
def create_archaeology_items(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
if ModNames.archaeology not in content.registered_packs:
return
items.append(item_factory(Wallet.metal_detector))
def create_filler_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions) -> List[Item]:
if options.festival_locations == FestivalLocations.option_disabled:
return []
return [item_factory(item) for item in items_by_group[Group.FESTIVAL] if
item.classification == ItemClassification.filler]
def create_magic_mod_spells(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if ModNames.magic not in options.mods:
def create_magic_mod_spells(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
if ModNames.magic not in content.registered_packs:
return
items.extend([item_factory(item) for item in items_by_group[Group.MAGIC_SPELL]])
def create_deepwoods_pendants(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if ModNames.deepwoods not in options.mods:
def create_deepwoods_pendants(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
if ModNames.deepwoods not in content.registered_packs:
return
items.extend([item_factory(item) for item in ["Pendant of Elders", "Pendant of Community", "Pendant of Depths"]])
def create_sve_special_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if ModNames.sve not in options.mods:
def create_sve_special_items(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
if ModNames.sve not in content.registered_packs:
return
items.extend([item_factory(item) for item in items_by_group[Group.MOD_WARP] if item.mod_name == ModNames.sve])
items.extend([item_factory(item) for item in items_by_group[Group.MOD_WARP] if ModNames.sve in item.content_packs])
def create_quest_rewards_sve(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if ModNames.sve not in options.mods:
def create_quest_rewards_sve(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
if not content.is_enabled(ModNames.sve):
return
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
ginger_island_included = content.is_enabled(ginger_island_content_pack)
items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items])
if not exclude_ginger_island:
if ginger_island_included:
items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items_ginger_island])
if options.quest_locations.has_no_story_quests():
return
items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items])
if exclude_ginger_island:
if not ginger_island_included:
return
items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items_ginger_island])
def create_unique_filler_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random,
available_item_slots: int) -> List[Item]:
items = []
items.extend(create_filler_festival_rewards(item_factory, options))
if len(items) > available_item_slots:
items = random.sample(items, available_item_slots)
return items
def weapons_count(options: StardewValleyOptions):
def weapons_count(content: StardewContent):
weapon_count = 5
if ModNames.sve in options.mods:
if ModNames.sve in content.registered_packs:
weapon_count += 1
return weapon_count
def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random,
items_already_added: List[Item],
available_item_slots: int) -> List[Item]:
include_traps = options.trap_difficulty != TrapDifficulty.option_no_traps
items_already_added_names = [item.name for item in items_already_added]
useful_resource_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK_USEFUL]
if pack.name not in items_already_added_names]
trap_items = [trap for trap in items_by_group[Group.TRAP]
if trap.name not in items_already_added_names and
Group.DEPRECATED not in trap.groups and
(trap.mod_name is None or trap.mod_name in options.mods) and
options.trap_distribution[trap.name] > 0]
player_buffs = get_allowed_player_buffs(options.enabled_filler_buffs)
priority_filler_items = []
priority_filler_items.extend(useful_resource_packs)
priority_filler_items.extend(player_buffs)
if include_traps:
priority_filler_items.extend(trap_items)
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
all_filler_packs = remove_excluded_items(get_all_filler_items(include_traps, exclude_ginger_island), options)
all_filler_packs.extend(player_buffs)
priority_filler_items = remove_excluded_items(priority_filler_items, options)
number_priority_items = len(priority_filler_items)
if available_item_slots < number_priority_items:
chosen_priority_items = [item_factory(resource_pack) for resource_pack in
random.sample(priority_filler_items, available_item_slots)]
return chosen_priority_items
items = []
chosen_priority_items = [item_factory(resource_pack,
ItemClassification.trap if resource_pack.classification == ItemClassification.trap else ItemClassification.useful)
for resource_pack in priority_filler_items]
items.extend(chosen_priority_items)
available_item_slots -= number_priority_items
all_filler_packs = [filler_pack for filler_pack in all_filler_packs
if Group.MAXIMUM_ONE not in filler_pack.groups or
(filler_pack.name not in [priority_item.name for priority_item in
priority_filler_items] and filler_pack.name not in items_already_added_names)]
filler_weights = get_filler_weights(options, all_filler_packs)
while available_item_slots > 0:
resource_pack = random.choices(all_filler_packs, weights=filler_weights, k=1)[0]
exactly_2 = Group.AT_LEAST_TWO in resource_pack.groups
while exactly_2 and available_item_slots == 1:
resource_pack = random.choices(all_filler_packs, weights=filler_weights, k=1)[0]
exactly_2 = Group.AT_LEAST_TWO in resource_pack.groups
classification = ItemClassification.useful if resource_pack.classification == ItemClassification.progression else resource_pack.classification
items.append(item_factory(resource_pack, classification))
available_item_slots -= 1
if exactly_2:
items.append(item_factory(resource_pack, classification))
available_item_slots -= 1
if exactly_2 or Group.MAXIMUM_ONE in resource_pack.groups:
index = all_filler_packs.index(resource_pack)
all_filler_packs.pop(index)
filler_weights.pop(index)
return items
def get_filler_weights(options: StardewValleyOptions, all_filler_packs: List[ItemData]):
weights = []
for filler in all_filler_packs:
if filler.name in options.trap_distribution:
num = options.trap_distribution[filler.name]
else:
num = options.trap_distribution.default_weight
weights.append(num)
return weights
def filter_deprecated_items(items: List[ItemData]) -> List[ItemData]:
return [item for item in items if Group.DEPRECATED not in item.groups]
def filter_ginger_island_items(exclude_island: bool, items: List[ItemData]) -> List[ItemData]:
return [item for item in items if not exclude_island or Group.GINGER_ISLAND not in item.groups]
def filter_mod_items(mods: Set[str], items: List[ItemData]) -> List[ItemData]:
return [item for item in items if item.mod_name is None or item.mod_name in mods]
def remove_excluded_items(items, options: StardewValleyOptions):
return remove_excluded_items_island_mods(items, options.exclude_ginger_island == ExcludeGingerIsland.option_true, options.mods.value)
def remove_excluded_items_island_mods(items, exclude_ginger_island: bool, mods: Set[str]):
deprecated_filter = filter_deprecated_items(items)
ginger_island_filter = filter_ginger_island_items(exclude_ginger_island, deprecated_filter)
mod_filter = filter_mod_items(mods, ginger_island_filter)
return mod_filter
def generate_filler_choice_pool(options: StardewValleyOptions) -> list[str]:
include_traps = options.trap_difficulty != TrapDifficulty.option_no_traps
exclude_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
available_filler = get_all_filler_items(include_traps, exclude_island)
available_filler = remove_limited_amount_packs(available_filler)
return [item.name for item in available_filler]
def remove_limited_amount_packs(packs):
return [pack for pack in packs if Group.MAXIMUM_ONE not in pack.groups and Group.AT_LEAST_TWO not in pack.groups]
def get_all_filler_items(include_traps: bool, exclude_ginger_island: bool) -> List[ItemData]:
all_filler_items = [pack for pack in items_by_group[Group.RESOURCE_PACK]]
all_filler_items.extend(items_by_group[Group.TRASH])
if include_traps:
all_filler_items.extend(items_by_group[Group.TRAP])
all_filler_items = remove_excluded_items_island_mods(all_filler_items, exclude_ginger_island, set())
return all_filler_items
def get_allowed_player_buffs(buff_option: EnabledFillerBuffs) -> List[ItemData]:
allowed_buffs = []
if BuffOptionName.luck in buff_option:
allowed_buffs.append(item_table[Buff.luck])
if BuffOptionName.damage in buff_option:
allowed_buffs.append(item_table[Buff.damage])
if BuffOptionName.defense in buff_option:
allowed_buffs.append(item_table[Buff.defense])
if BuffOptionName.immunity in buff_option:
allowed_buffs.append(item_table[Buff.immunity])
if BuffOptionName.health in buff_option:
allowed_buffs.append(item_table[Buff.health])
if BuffOptionName.energy in buff_option:
allowed_buffs.append(item_table[Buff.energy])
if BuffOptionName.bite in buff_option:
allowed_buffs.append(item_table[Buff.bite_rate])
if BuffOptionName.fish_trap in buff_option:
allowed_buffs.append(item_table[Buff.fish_trap])
if BuffOptionName.fishing_bar in buff_option:
allowed_buffs.append(item_table[Buff.fishing_bar])
if BuffOptionName.quality in buff_option:
allowed_buffs.append(item_table[Buff.quality])
if BuffOptionName.glow in buff_option:
allowed_buffs.append(item_table[Buff.glow])
return allowed_buffs
def get_stardrop_classification(options) -> ItemClassification:
return ItemClassification.progression_skip_balancing if world_is_perfection(options) or world_is_stardrops(options) else ItemClassification.useful
return ItemClassification.progression_skip_balancing \
if goal_is_perfection(options) or goal_is_stardrops(options) or EatsanityOptionName.shop in options.eatsanity \
else ItemClassification.useful
def world_is_perfection(options) -> bool:
def goal_is_perfection(options) -> bool:
return options.goal == Goal.option_perfection
def world_is_stardrops(options) -> bool:
def goal_is_stardrops(options) -> bool:
return options.goal == Goal.option_mystery_of_the_stardrops

View File

@@ -1,145 +1,197 @@
import csv
import enum
from dataclasses import dataclass, field
from functools import reduce
from pathlib import Path
from typing import Dict, List, Protocol, Union, Set, Optional
from BaseClasses import Item, ItemClassification
from .. import data
from ..logic.logic_event import all_events
ITEM_CODE_OFFSET = 717000
world_folder = Path(__file__).parent
class Group(enum.Enum):
RESOURCE_PACK = enum.auto()
FRIENDSHIP_PACK = enum.auto()
COMMUNITY_REWARD = enum.auto()
TRASH = enum.auto()
FOOTWEAR = enum.auto()
HATS = enum.auto()
RING = enum.auto()
WEAPON = enum.auto()
WEAPON_GENERIC = enum.auto()
WEAPON_SWORD = enum.auto()
WEAPON_CLUB = enum.auto()
WEAPON_DAGGER = enum.auto()
WEAPON_SLINGSHOT = enum.auto()
PROGRESSIVE_TOOLS = enum.auto()
SKILL_LEVEL_UP = enum.auto()
SKILL_MASTERY = enum.auto()
BUILDING = enum.auto()
WIZARD_BUILDING = enum.auto()
DESERT_TRANSPORTATION = enum.auto()
ISLAND_TRANSPORTATION = enum.auto()
ARCADE_MACHINE_BUFFS = enum.auto()
BASE_RESOURCE = enum.auto()
WARP_TOTEM = enum.auto()
GEODE = enum.auto()
ORE = enum.auto()
FERTILIZER = enum.auto()
SEED = enum.auto()
CROPSANITY = enum.auto()
FISHING_RESOURCE = enum.auto()
SEASON = enum.auto()
TRAVELING_MERCHANT_DAY = enum.auto()
MUSEUM = enum.auto()
FRIENDSANITY = enum.auto()
FESTIVAL = enum.auto()
RARECROW = enum.auto()
TRAP = enum.auto()
BONUS = enum.auto()
MAXIMUM_ONE = enum.auto()
AT_LEAST_TWO = enum.auto()
DEPRECATED = enum.auto()
RESOURCE_PACK_USEFUL = enum.auto()
SPECIAL_ORDER_BOARD = enum.auto()
SPECIAL_ORDER_QI = enum.auto()
BABY = enum.auto()
GINGER_ISLAND = enum.auto()
WALNUT_PURCHASE = enum.auto()
TV_CHANNEL = enum.auto()
QI_CRAFTING_RECIPE = enum.auto()
CHEFSANITY = enum.auto()
CHEFSANITY_STARTER = enum.auto()
CHEFSANITY_QOS = enum.auto()
CHEFSANITY_PURCHASE = enum.auto()
CHEFSANITY_FRIENDSHIP = enum.auto()
CHEFSANITY_SKILL = enum.auto()
CRAFTSANITY = enum.auto()
BOOK_POWER = enum.auto()
LOST_BOOK = enum.auto()
PLAYER_BUFF = enum.auto()
# Mods
MAGIC_SPELL = enum.auto()
MOD_WARP = enum.auto()
@dataclass(frozen=True)
class ItemData:
code_without_offset: Optional[int]
name: str
classification: ItemClassification
mod_name: Optional[str] = None
groups: Set[Group] = field(default_factory=frozenset)
def __post_init__(self):
if not isinstance(self.groups, frozenset):
super().__setattr__("groups", frozenset(self.groups))
@property
def code(self):
return ITEM_CODE_OFFSET + self.code_without_offset if self.code_without_offset is not None else None
def has_any_group(self, *group: Group) -> bool:
groups = set(group)
return bool(groups.intersection(self.groups))
class StardewItemFactory(Protocol):
def __call__(self, name: Union[str, ItemData], override_classification: ItemClassification = None) -> Item:
raise NotImplementedError
def load_item_csv():
from importlib.resources import files
items = []
with files(data).joinpath("items.csv").open() as file:
item_reader = csv.DictReader(file)
for item in item_reader:
id = int(item["id"]) if item["id"] else None
classification = reduce((lambda a, b: a | b), {ItemClassification[str_classification] for str_classification in item["classification"].split(",")})
groups = {Group[group] for group in item["groups"].split(",") if group}
mod_name = str(item["mod_name"]) if item["mod_name"] else None
items.append(ItemData(id, item["name"], classification, mod_name, groups))
return items
events = [
ItemData(None, e, ItemClassification.progression)
for e in sorted(all_events)
]
all_items: List[ItemData] = load_item_csv() + events
item_table: Dict[str, ItemData] = {}
items_by_group: Dict[Group, List[ItemData]] = {}
def initialize_groups():
for item in all_items:
for group in item.groups:
item_group = items_by_group.get(group, list())
item_group.append(item)
items_by_group[group] = item_group
def initialize_item_table():
item_table.update({item.name: item for item in all_items})
initialize_item_table()
initialize_groups()
from __future__ import annotations
import csv
import enum
from dataclasses import dataclass, field
from functools import reduce
from typing import Protocol
from BaseClasses import ItemClassification, Item
from .. import data
from ..content.vanilla.ginger_island import ginger_island_content_pack
from ..logic.logic_event import all_events
ITEM_CODE_OFFSET = 717000
class StardewItemFactory(Protocol):
def __call__(self, item: str | ItemData, /, *, classification_pre_fill: ItemClassification = None,
classification_post_fill: ItemClassification = None) -> Item:
"""
:param item: The item to create. Can be the name of the item or the item data.
:param classification_pre_fill: The classification to use for the item before the fill. If None, the basic classification of the item is used.
:param classification_post_fill: The classification to use for the item after the fill. If None, the pre_fill classification will be used.
"""
raise NotImplementedError
class Group(enum.Enum):
FRIENDSHIP_PACK = enum.auto()
COMMUNITY_REWARD = enum.auto()
TRASH_BEAR = enum.auto()
TRASH = enum.auto()
FOOTWEAR = enum.auto()
WEAPON = enum.auto()
WEAPON_GENERIC = enum.auto()
WEAPON_SWORD = enum.auto()
WEAPON_CLUB = enum.auto()
WEAPON_DAGGER = enum.auto()
WEAPON_SLINGSHOT = enum.auto()
PROGRESSIVE_TOOLS = enum.auto()
SKILL_LEVEL_UP = enum.auto()
SKILL_MASTERY = enum.auto()
BUILDING = enum.auto()
WIZARD_BUILDING = enum.auto()
DESERT_TRANSPORTATION = enum.auto()
ISLAND_TRANSPORTATION = enum.auto()
ARCADE_MACHINE_BUFFS = enum.auto()
BASE_RESOURCE = enum.auto()
WARP_TOTEM = enum.auto()
GEODE = enum.auto()
ORE = enum.auto()
FERTILIZER = enum.auto()
CROPSANITY = enum.auto()
FISHING_RESOURCE = enum.auto()
SEASON = enum.auto()
TRAVELING_MERCHANT_DAY = enum.auto()
MUSEUM = enum.auto()
FRIENDSANITY = enum.auto()
FESTIVAL = enum.auto()
RARECROW = enum.auto()
TRAP = enum.auto()
BONUS = enum.auto()
MAXIMUM_ONE = enum.auto()
AT_LEAST_TWO = enum.auto()
DEPRECATED = enum.auto()
SPECIAL_ORDER_BOARD = enum.auto()
SPECIAL_ORDER_QI = enum.auto()
BABY = enum.auto()
GINGER_ISLAND = enum.auto()
WALNUT_PURCHASE = enum.auto()
TV_CHANNEL = enum.auto()
QI_CRAFTING_RECIPE = enum.auto()
CHEFSANITY = enum.auto()
CHEFSANITY_STARTER = enum.auto()
CHEFSANITY_QOS = enum.auto()
CHEFSANITY_PURCHASE = enum.auto()
CHEFSANITY_FRIENDSHIP = enum.auto()
CHEFSANITY_SKILL = enum.auto()
CRAFTSANITY = enum.auto()
BOOK_POWER = enum.auto()
LOST_BOOK = enum.auto()
PLAYER_BUFF = enum.auto()
EASY_SECRET = enum.auto()
FISHING_SECRET = enum.auto()
SECRET_NOTES_SECRET = enum.auto()
MOVIESANITY = enum.auto()
TRINKET = enum.auto()
EATSANITY_ENZYME = enum.auto()
ENDGAME_LOCATION_ITEMS = enum.auto()
REQUIRES_FRIENDSANITY_MARRIAGE = enum.auto()
BOOKSELLER = enum.auto()
# Types of filler
FILLER_FARMING = enum.auto()
FILLER_FISHING = enum.auto()
FILLER_FRUIT_TREES = enum.auto()
FILLER_FOOD = enum.auto()
FILLER_BUFF_FOOD = enum.auto()
FILLER_CONSUMABLE = enum.auto()
FILLER_MACHINE = enum.auto()
FILLER_STORAGE = enum.auto()
FILLER_QUALITY_OF_LIFE = enum.auto()
FILLER_MATERIALS = enum.auto()
FILLER_CURRENCY = enum.auto()
FILLER_MONEY = enum.auto()
FILLER_HAT = enum.auto()
FILLER_DECORATION = enum.auto()
FILLER_RING = enum.auto()
# Mods
MAGIC_SPELL = enum.auto()
MOD_WARP = enum.auto()
FILLER_GROUPS = [Group.FILLER_FARMING, Group.FILLER_FISHING, Group.FILLER_FRUIT_TREES, Group.FILLER_FOOD, Group.FILLER_BUFF_FOOD,
Group.FILLER_CONSUMABLE, Group.FILLER_MACHINE, Group.FILLER_STORAGE, Group.FILLER_QUALITY_OF_LIFE, Group.FILLER_MATERIALS,
Group.FILLER_CURRENCY, Group.FILLER_MONEY, Group.FILLER_HAT, Group.FILLER_DECORATION, Group.FILLER_RING, ]
@dataclass(frozen=True)
class ItemData:
code_without_offset: int | None
name: str
classification: ItemClassification
content_packs: frozenset[str] = frozenset()
"""All the content packs required for this item to be available."""
groups: set[Group] = field(default_factory=frozenset)
def __post_init__(self):
if not isinstance(self.groups, frozenset):
super().__setattr__("groups", frozenset(self.groups))
@property
def code(self) -> int | None:
return ITEM_CODE_OFFSET + self.code_without_offset if self.code_without_offset is not None else None
def has_any_group(self, *group: Group) -> bool:
groups = set(group)
return bool(groups.intersection(self.groups))
def has_all_groups(self, *group: Group) -> bool:
groups = set(group)
return bool(groups.issubset(self.groups))
def has_limited_amount(self) -> bool:
return self.has_any_group(Group.MAXIMUM_ONE, Group.AT_LEAST_TWO)
def load_item_csv():
from importlib.resources import files
items = []
with files(data).joinpath("items.csv").open() as file:
item_reader = csv.DictReader(file)
for item in item_reader:
item_id = int(item["id"]) if item["id"] else None
item_name = item["name"]
classification = reduce((lambda a, b: a | b), {ItemClassification[str_classification] for str_classification in item["classification"].split(",")})
csv_groups = [Group[group] for group in item["groups"].split(",") if group]
groups = set(csv_groups)
csv_content_packs = [cp for cp in item["content_packs"].split(",") if cp]
content_packs = frozenset(csv_content_packs)
assert len(csv_groups) == len(groups), f"Item '{item_name}' has duplicate groups: {csv_groups}"
assert len(csv_content_packs) == len(content_packs)
if Group.GINGER_ISLAND in groups:
content_packs |= {ginger_island_content_pack.name}
items.append(ItemData(item_id, item_name, classification, content_packs, groups))
return items
events = [
ItemData(None, e, ItemClassification.progression)
for e in sorted(all_events)
]
all_items: list[ItemData] = load_item_csv() + events
item_table: dict[str, ItemData] = {}
items_by_group: dict[Group, list[ItemData]] = {}
def initialize_groups():
for item in all_items:
for group in item.groups:
item_group = items_by_group.get(group, list())
item_group.append(item)
items_by_group[group] = item_group
def initialize_item_table():
item_table.update({item.name: item for item in all_items})
initialize_item_table()
initialize_groups()

View File

@@ -1,18 +1,24 @@
import csv
import enum
import logging
from dataclasses import dataclass
from random import Random
from typing import Optional, Dict, Protocol, List, FrozenSet, Iterable
from typing import Optional, Dict, Protocol, List, Iterable
from . import data
from .bundles.bundle_room import BundleRoom
from .content.game_content import StardewContent
from .content.vanilla.ginger_island import ginger_island_content_pack
from .content.vanilla.qi_board import qi_board_content_pack
from .data.game_item import ItemTag
from .data.museum_data import all_museum_items
from .mods.mod_data import ModNames
from .options import ExcludeGingerIsland, ArcadeMachineLocations, SpecialOrderLocations, Museumsanity, \
from .options import ArcadeMachineLocations, SpecialOrderLocations, Museumsanity, \
FestivalLocations, ElevatorProgression, BackpackProgression, FarmType
from .options import StardewValleyOptions, Craftsanity, Chefsanity, Cooksanity, Shipsanity, Monstersanity
from .options.options import BackpackSize, Moviesanity, Eatsanity, IncludeEndgameLocations, Friendsanity
from .strings.ap_names.ap_option_names import WalnutsanityOptionName, SecretsanityOptionName, EatsanityOptionName, ChefsanityOptionName, StartWithoutOptionName
from .strings.backpack_tiers import Backpack
from .strings.goal_names import Goal
from .strings.quest_names import ModQuest, Quest
from .strings.region_names import Region, LogicRegion
@@ -20,10 +26,13 @@ from .strings.villager_names import NPC
LOCATION_CODE_OFFSET = 717000
logger = logging.getLogger(__name__)
class LocationTags(enum.Enum):
MANDATORY = enum.auto()
BUNDLE = enum.auto()
TRASH_BEAR = enum.auto()
COMMUNITY_CENTER_BUNDLE = enum.auto()
CRAFTS_ROOM_BUNDLE = enum.auto()
PANTRY_BUNDLE = enum.auto()
@@ -33,7 +42,10 @@ class LocationTags(enum.Enum):
VAULT_BUNDLE = enum.auto()
COMMUNITY_CENTER_ROOM = enum.auto()
RACCOON_BUNDLES = enum.auto()
MEME_BUNDLE = enum.auto()
BACKPACK = enum.auto()
BACKPACK_TIER = enum.auto()
SPLIT_BACKPACK = enum.auto()
TOOL_UPGRADE = enum.auto()
HOE_UPGRADE = enum.auto()
PICKAXE_UPGRADE = enum.auto()
@@ -42,6 +54,7 @@ class LocationTags(enum.Enum):
TRASH_CAN_UPGRADE = enum.auto()
FISHING_ROD_UPGRADE = enum.auto()
PAN_UPGRADE = enum.auto()
STARTING_TOOLS = enum.auto()
THE_MINES_TREASURE = enum.auto()
CROPSANITY = enum.auto()
ELEVATOR = enum.auto()
@@ -67,6 +80,7 @@ class LocationTags(enum.Enum):
FESTIVAL = enum.auto()
FESTIVAL_HARD = enum.auto()
DESERT_FESTIVAL_CHEF = enum.auto()
DESERT_FESTIVAL_CHEF_MEAL = enum.auto()
SPECIAL_ORDER_BOARD = enum.auto()
SPECIAL_ORDER_QI = enum.auto()
REQUIRES_QI_ORDERS = enum.auto()
@@ -97,10 +111,42 @@ class LocationTags(enum.Enum):
CHEFSANITY_SKILL = enum.auto()
CHEFSANITY_STARTER = enum.auto()
CRAFTSANITY = enum.auto()
CRAFTSANITY_CRAFT = enum.auto()
CRAFTSANITY_RECIPE = enum.auto()
BOOKSANITY = enum.auto()
BOOKSANITY_POWER = enum.auto()
BOOKSANITY_SKILL = enum.auto()
BOOKSANITY_LOST = enum.auto()
SECRETSANITY = enum.auto()
EASY_SECRET = enum.auto()
FISHING_SECRET = enum.auto()
DIFFICULT_SECRET = enum.auto()
SECRET_NOTE = enum.auto()
REPLACES_PREVIOUS_LOCATION = enum.auto()
ANY_MOVIE = enum.auto()
MOVIE = enum.auto()
MOVIE_SNACK = enum.auto()
HATSANITY = enum.auto()
HAT_EASY = enum.auto()
HAT_TAILORING = enum.auto()
HAT_MEDIUM = enum.auto()
HAT_DIFFICULT = enum.auto()
HAT_RNG = enum.auto()
HAT_NEAR_PERFECTION = enum.auto()
HAT_POST_PERFECTION = enum.auto()
HAT_IMPOSSIBLE = enum.auto()
EATSANITY = enum.auto()
EATSANITY_CROP = enum.auto()
EATSANITY_COOKING = enum.auto()
EATSANITY_FISH = enum.auto()
EATSANITY_ARTISAN = enum.auto()
EATSANITY_SHOP = enum.auto()
EATSANITY_POISONOUS = enum.auto()
ENDGAME_LOCATIONS = enum.auto()
REQUIRES_FRIENDSANITY = enum.auto()
REQUIRES_FRIENDSANITY_MARRIAGE = enum.auto()
BEACH_FARM = enum.auto()
# Mods
# Skill Mods
LUCK_LEVEL = enum.auto()
@@ -115,14 +161,15 @@ class LocationTags(enum.Enum):
@dataclass(frozen=True)
class LocationData:
code_without_offset: Optional[int]
code_without_offset: int | None
region: str
name: str
mod_name: Optional[str] = None
tags: FrozenSet[LocationTags] = frozenset()
content_packs: frozenset[str] = frozenset()
"""All the content packs required for this location to be active."""
tags: frozenset[LocationTags] = frozenset()
@property
def code(self) -> Optional[int]:
def code(self) -> int | None:
return LOCATION_CODE_OFFSET + self.code_without_offset if self.code_without_offset is not None else None
@@ -134,16 +181,28 @@ class StardewLocationCollector(Protocol):
def load_location_csv() -> List[LocationData]:
from importlib.resources import files
locations = []
with files(data).joinpath("locations.csv").open() as file:
reader = csv.DictReader(file)
return [LocationData(int(location["id"]) if location["id"] else None,
location["region"],
location["name"],
str(location["mod_name"]) if location["mod_name"] else None,
frozenset(LocationTags[group]
for group in location["tags"].split(",")
if group))
for location in reader]
location_reader = csv.DictReader(file)
for location in location_reader:
location_id = int(location["id"]) if location["id"] else None
location_name = location["name"]
csv_tags = [LocationTags[tag] for tag in location["tags"].split(",") if tag]
tags = frozenset(csv_tags)
csv_content_packs = [cp for cp in location["content_packs"].split(",") if cp]
content_packs = frozenset(csv_content_packs)
assert len(csv_tags) == len(tags), f"Location '{location_name}' has duplicate tags: {csv_tags}"
assert len(csv_content_packs) == len(content_packs)
if LocationTags.GINGER_ISLAND in tags:
content_packs |= {ginger_island_content_pack.name}
if LocationTags.SPECIAL_ORDER_QI in tags or LocationTags.REQUIRES_QI_ORDERS in tags:
content_packs |= {qi_board_content_pack.name}
locations.append(LocationData(location_id, location["region"], location_name, content_packs, tags))
return locations
events_locations = [
@@ -161,6 +220,8 @@ events_locations = [
LocationData(None, Region.farm, Goal.craft_master),
LocationData(None, LogicRegion.shipping, Goal.legend),
LocationData(None, Region.farm, Goal.mystery_of_the_stardrops),
LocationData(None, Region.farm, Goal.mad_hatter),
LocationData(None, Region.farm, Goal.ultimate_foodie),
LocationData(None, Region.farm, Goal.allsanity),
LocationData(None, Region.qi_walnut_room, Goal.perfection),
]
@@ -296,10 +357,12 @@ def extend_hard_festival_locations(randomized_locations: List[LocationData], opt
def extend_desert_festival_chef_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
festival_chef_locations = locations_by_tag[LocationTags.DESERT_FESTIVAL_CHEF]
number_to_add = 5 if options.festival_locations == FestivalLocations.option_easy else 10
locations_to_add = random.sample(festival_chef_locations, number_to_add)
randomized_locations.extend(locations_to_add)
if options.festival_locations == FestivalLocations.option_easy:
randomized_locations.append(location_table["Desert Chef"])
elif options.festival_locations == FestivalLocations.option_hard:
festival_chef_locations = locations_by_tag[LocationTags.DESERT_FESTIVAL_CHEF_MEAL]
location_to_add = random.choice(festival_chef_locations)
randomized_locations.append(location_to_add)
def extend_special_order_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
@@ -307,16 +370,15 @@ def extend_special_order_locations(randomized_locations: List[LocationData], opt
board_locations = filter_disabled_locations(options, content, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD])
randomized_locations.extend(board_locations)
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
if options.special_order_locations & SpecialOrderLocations.value_qi and include_island:
if content.is_enabled(qi_board_content_pack):
include_arcade = options.arcade_machine_locations != ArcadeMachineLocations.option_disabled
qi_orders = [location for location in locations_by_tag[LocationTags.SPECIAL_ORDER_QI] if
include_arcade or LocationTags.JUNIMO_KART not in location.tags]
randomized_locations.extend(qi_orders)
def extend_walnut_purchase_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
def extend_walnut_purchase_locations(randomized_locations: List[LocationData], content: StardewContent):
if not content.is_enabled(ginger_island_content_pack):
return
randomized_locations.append(location_table["Repair Ticket Machine"])
randomized_locations.append(location_table["Repair Boat Hull"])
@@ -332,14 +394,14 @@ def extend_mandatory_locations(randomized_locations: List[LocationData], options
randomized_locations.extend(filtered_mandatory_locations)
def extend_situational_quest_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_situational_quest_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.quest_locations.has_no_story_quests():
return
if ModNames.distant_lands in options.mods:
if ModNames.alecto in options.mods:
randomized_locations.append(location_table[ModQuest.WitchOrder])
if ModNames.distant_lands in content.registered_packs:
if ModNames.alecto in content.registered_packs:
randomized_locations.append(location_table[f"Quest: {ModQuest.WitchOrder}"])
else:
randomized_locations.append(location_table[ModQuest.CorruptedCropsTask])
randomized_locations.append(location_table[f"Quest: {ModQuest.CorruptedCropsTask}"])
def extend_bundle_locations(randomized_locations: List[LocationData], bundle_rooms: List[BundleRoom]):
@@ -351,19 +413,35 @@ def extend_bundle_locations(randomized_locations: List[LocationData], bundle_roo
randomized_locations.append(location_table[bundle.name])
def extend_backpack_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_trash_bear_locations(randomized_locations: List[LocationData], trash_bear_requests: Dict[str, List[str]]):
for request_type in trash_bear_requests:
randomized_locations.append(location_table[f"Trash Bear {request_type}"])
def extend_backpack_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.backpack_progression == BackpackProgression.option_vanilla:
return
backpack_locations = [location for location in locations_by_tag[LocationTags.BACKPACK]]
filtered_backpack_locations = filter_modded_locations(options, backpack_locations)
no_start_backpack = StartWithoutOptionName.backpack in options.start_without
if options.backpack_size == BackpackSize.option_12:
backpack_locations = [location for location in locations_by_tag[LocationTags.BACKPACK_TIER] if no_start_backpack or LocationTags.STARTING_TOOLS not in location.tags]
else:
num_per_tier = options.backpack_size.count_per_tier()
backpack_tier_names = Backpack.get_purchasable_tiers(ModNames.big_backpack in content.registered_packs, no_start_backpack)
backpack_locations = []
for tier in backpack_tier_names:
for i in range(1, num_per_tier + 1):
backpack_locations.append(location_table[f"{tier} {i}"])
filtered_backpack_locations = filter_modded_locations(backpack_locations, content)
randomized_locations.extend(filtered_backpack_locations)
def extend_elevator_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
def extend_elevator_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.elevator_progression == ElevatorProgression.option_vanilla:
return
elevator_locations = [location for location in locations_by_tag[LocationTags.ELEVATOR]]
filtered_elevator_locations = filter_modded_locations(options, elevator_locations)
filtered_elevator_locations = filter_modded_locations(elevator_locations, content)
randomized_locations.extend(filtered_elevator_locations)
@@ -396,14 +474,14 @@ def extend_shipsanity_locations(randomized_locations: List[LocationData], option
randomized_locations.extend(filtered_ship_locations)
return
shipsanity_locations = set()
if shipsanity == Shipsanity.option_fish or shipsanity == Shipsanity.option_full_shipment_with_fish:
if shipsanity == Shipsanity.option_fish or shipsanity == Shipsanity.option_crops_and_fish or shipsanity == Shipsanity.option_full_shipment_with_fish:
shipsanity_locations = shipsanity_locations.union({location for location in locations_by_tag[LocationTags.SHIPSANITY_FISH]})
if shipsanity == Shipsanity.option_crops:
if shipsanity == Shipsanity.option_crops or shipsanity == Shipsanity.option_crops_and_fish:
shipsanity_locations = shipsanity_locations.union({location for location in locations_by_tag[LocationTags.SHIPSANITY_CROP]})
if shipsanity == Shipsanity.option_full_shipment or shipsanity == Shipsanity.option_full_shipment_with_fish:
shipsanity_locations = shipsanity_locations.union({location for location in locations_by_tag[LocationTags.SHIPSANITY_FULL_SHIPMENT]})
filtered_shipsanity_locations = filter_disabled_locations(options, content, list(shipsanity_locations))
filtered_shipsanity_locations = filter_disabled_locations(options, content, sorted(list(shipsanity_locations), key=lambda x: x.name))
randomized_locations.extend(filtered_shipsanity_locations)
@@ -422,18 +500,18 @@ def extend_cooksanity_locations(randomized_locations: List[LocationData], option
def extend_chefsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
chefsanity = options.chefsanity
if chefsanity == Chefsanity.option_none:
if chefsanity == Chefsanity.preset_none:
return
chefsanity_locations_by_name = {} # Dictionary to not make duplicates
if chefsanity & Chefsanity.option_queen_of_sauce:
if ChefsanityOptionName.queen_of_sauce in chefsanity:
chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_QOS]})
if chefsanity & Chefsanity.option_purchases:
if ChefsanityOptionName.purchases in chefsanity:
chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_PURCHASE]})
if chefsanity & Chefsanity.option_friendship:
if ChefsanityOptionName.friendship in chefsanity:
chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_FRIENDSHIP]})
if chefsanity & Chefsanity.option_skills:
if ChefsanityOptionName.skills in chefsanity:
chefsanity_locations_by_name.update({location.name: location for location in locations_by_tag[LocationTags.CHEFSANITY_SKILL]})
filtered_chefsanity_locations = filter_disabled_locations(options, content, list(chefsanity_locations_by_name.values()))
@@ -468,18 +546,141 @@ def extend_walnutsanity_locations(randomized_locations: List[LocationData], opti
if not options.walnutsanity:
return
if "Puzzles" in options.walnutsanity:
if WalnutsanityOptionName.puzzles in options.walnutsanity:
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_PUZZLE])
if "Bushes" in options.walnutsanity:
if WalnutsanityOptionName.bushes in options.walnutsanity:
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_BUSH])
if "Dig Spots" in options.walnutsanity:
if WalnutsanityOptionName.dig_spots in options.walnutsanity:
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_DIG])
if "Repeatables" in options.walnutsanity:
if WalnutsanityOptionName.repeatables in options.walnutsanity:
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_REPEATABLE])
def extend_movies_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.moviesanity == Moviesanity.option_none:
return
locations = []
if options.moviesanity == Moviesanity.option_one:
locations.extend(locations_by_tag[LocationTags.ANY_MOVIE])
if options.moviesanity >= Moviesanity.option_all_movies:
locations.extend(locations_by_tag[LocationTags.MOVIE])
if options.moviesanity >= Moviesanity.option_all_movies_and_all_snacks:
locations.extend(locations_by_tag[LocationTags.MOVIE_SNACK])
filtered_locations = filter_disabled_locations(options, content, locations)
randomized_locations.extend(filtered_locations)
def extend_secrets_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if not options.secretsanity:
return
locations = []
if SecretsanityOptionName.easy in options.secretsanity:
locations.extend(locations_by_tag[LocationTags.EASY_SECRET])
if SecretsanityOptionName.fishing in options.secretsanity:
locations.extend(locations_by_tag[LocationTags.FISHING_SECRET])
if SecretsanityOptionName.difficult in options.secretsanity:
locations.extend(locations_by_tag[LocationTags.DIFFICULT_SECRET])
if SecretsanityOptionName.secret_notes in options.secretsanity:
locations.extend(locations_by_tag[LocationTags.SECRET_NOTE])
for location_dupe in locations_by_tag[LocationTags.REPLACES_PREVIOUS_LOCATION]:
second_part_of_name = location_dupe.name.split(":")[-1]
for location in randomized_locations:
second_part_of_dupe_name = location.name.split(":")[-1]
if second_part_of_name == second_part_of_dupe_name:
randomized_locations.remove(location)
filtered_locations = filter_disabled_locations(options, content, locations)
randomized_locations.extend(filtered_locations)
def extend_hats_locations(randomized_locations: List[LocationData], content: StardewContent):
hatsanity = content.features.hatsanity
if not hatsanity.is_enabled:
return
for hat in content.hats.values():
if not hatsanity.is_included(hat):
continue
randomized_locations.append(location_table[hatsanity.to_location_name(hat)])
def eatsanity_item_is_included(location: LocationData, options: StardewValleyOptions, content: StardewContent) -> bool:
eat_prefix = "Eat "
drink_prefix = "Drink "
if location.name.startswith(eat_prefix):
item_name = location.name[len(eat_prefix):]
elif location.name.startswith(drink_prefix):
item_name = location.name[len(drink_prefix):]
else:
raise Exception(f"Eatsanity Location does not have a recognized prefix: '{location.name}'")
# if not item_name in content.game_items:
# return False
if EatsanityOptionName.poisonous in options.eatsanity.value:
return True
if location in locations_by_tag[LocationTags.EATSANITY_POISONOUS]:
return False
return True
def extend_eatsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.eatsanity.value == Eatsanity.preset_none:
return
eatsanity_locations = []
if EatsanityOptionName.crops in options.eatsanity:
eatsanity_locations.extend(locations_by_tag[LocationTags.EATSANITY_CROP])
if EatsanityOptionName.cooking in options.eatsanity:
eatsanity_locations.extend(locations_by_tag[LocationTags.EATSANITY_COOKING])
if EatsanityOptionName.fish in options.eatsanity:
eatsanity_locations.extend(locations_by_tag[LocationTags.EATSANITY_FISH])
if EatsanityOptionName.artisan in options.eatsanity:
eatsanity_locations.extend(locations_by_tag[LocationTags.EATSANITY_ARTISAN])
if EatsanityOptionName.shop in options.eatsanity:
eatsanity_locations.extend(locations_by_tag[LocationTags.EATSANITY_SHOP])
eatsanity_locations = [location for location in eatsanity_locations if eatsanity_item_is_included(location, options, content)]
eatsanity_locations = filter_disabled_locations(options, content, eatsanity_locations)
randomized_locations.extend(eatsanity_locations)
def extend_endgame_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.include_endgame_locations.value == IncludeEndgameLocations.option_false:
return
has_friendsanity_marriage = options.friendsanity == Friendsanity.option_all_with_marriage
has_friendsanity = (not has_friendsanity_marriage) and options.friendsanity != Friendsanity.option_none
endgame_locations = []
endgame_locations.extend(locations_by_tag[LocationTags.ENDGAME_LOCATIONS])
endgame_locations = [location for location in endgame_locations if
LocationTags.REQUIRES_FRIENDSANITY_MARRIAGE not in location.tags or has_friendsanity_marriage]
endgame_locations = [location for location in endgame_locations if LocationTags.REQUIRES_FRIENDSANITY not in location.tags or has_friendsanity]
endgame_locations = filter_disabled_locations(options, content, endgame_locations)
randomized_locations.extend(endgame_locations)
def extend_filler_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
i = 1
while len(randomized_locations) < 90:
location_name = f"Traveling Merchant Sunday Item {i}"
while any(location.name == location_name for location in randomized_locations):
i += 1
location_name = f"Traveling Merchant Sunday Item {i}"
logger.debug(f"Player too few locations, adding Traveling Merchant Items #{i}")
for day in days:
location_name = f"Traveling Merchant {day} Item {i}"
randomized_locations.append(location_table[location_name])
def create_locations(location_collector: StardewLocationCollector,
bundle_rooms: List[BundleRoom],
trash_bear_requests: Dict[str, List[str]],
options: StardewValleyOptions,
content: StardewContent,
random: Random):
@@ -487,12 +688,13 @@ def create_locations(location_collector: StardewLocationCollector,
extend_mandatory_locations(randomized_locations, options, content)
extend_bundle_locations(randomized_locations, bundle_rooms)
extend_backpack_locations(randomized_locations, options)
extend_trash_bear_locations(randomized_locations, trash_bear_requests)
extend_backpack_locations(randomized_locations, options, content)
if content.features.tool_progression.is_progressive:
randomized_locations.extend(locations_by_tag[LocationTags.TOOL_UPGRADE])
extend_elevator_locations(randomized_locations, options)
extend_elevator_locations(randomized_locations, options, content)
skill_progression = content.features.skill_progression
if skill_progression.is_progressive:
@@ -516,7 +718,7 @@ def create_locations(location_collector: StardewLocationCollector,
extend_festival_locations(randomized_locations, options, random)
extend_special_order_locations(randomized_locations, options, content)
extend_walnut_purchase_locations(randomized_locations, options)
extend_walnut_purchase_locations(randomized_locations, content)
extend_monstersanity_locations(randomized_locations, options, content)
extend_shipsanity_locations(randomized_locations, options, content)
@@ -526,9 +728,16 @@ def create_locations(location_collector: StardewLocationCollector,
extend_quests_locations(randomized_locations, options, content)
extend_book_locations(randomized_locations, content)
extend_walnutsanity_locations(randomized_locations, options)
extend_movies_locations(randomized_locations, options, content)
extend_secrets_locations(randomized_locations, options, content)
extend_hats_locations(randomized_locations, content)
extend_eatsanity_locations(randomized_locations, options, content)
extend_endgame_locations(randomized_locations, options, content)
# Mods
extend_situational_quest_locations(randomized_locations, options)
extend_situational_quest_locations(randomized_locations, options, content)
extend_filler_locations(randomized_locations, options, content)
for location_data in randomized_locations:
location_collector(location_data.name, location_data.code, location_data.region)
@@ -538,21 +747,34 @@ def filter_deprecated_locations(locations: Iterable[LocationData]) -> Iterable[L
return [location for location in locations if LocationTags.DEPRECATED not in location.tags]
def filter_farm_type(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
def filter_animals_quest(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
# On Meadowlands, "Feeding Animals" replaces "Raising Animals"
if options.farm_type == FarmType.option_meadowlands:
return (location for location in locations if location.name != Quest.raising_animals)
return (location for location in locations if location.name != f"Quest: {Quest.raising_animals}")
else:
return (location for location in locations if location.name != Quest.feeding_animals)
return (location for location in locations if location.name != f"Quest: {Quest.feeding_animals}")
def filter_ginger_island(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
def filter_farm_exclusives(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
# Some locations are only on specific farms
if options.farm_type != FarmType.option_beach:
return (location for location in locations if LocationTags.BEACH_FARM not in location.tags)
return locations
def filter_farm_type(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
animals_filter = filter_animals_quest(options, locations)
exclusives_filter = filter_farm_exclusives(options, animals_filter)
return exclusives_filter
def filter_ginger_island(content: StardewContent, locations: Iterable[LocationData]) -> Iterable[LocationData]:
include_island = content.is_enabled(ginger_island_content_pack)
return (location for location in locations if include_island or LocationTags.GINGER_ISLAND not in location.tags)
def filter_qi_order_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
include_qi_orders = options.special_order_locations & SpecialOrderLocations.value_qi
def filter_qi_order_locations(content: StardewContent, locations: Iterable[LocationData]) -> Iterable[LocationData]:
include_qi_orders = content.is_enabled(qi_board_content_pack)
return (location for location in locations if include_qi_orders or LocationTags.REQUIRES_QI_ORDERS not in location.tags)
@@ -563,15 +785,15 @@ def filter_masteries_locations(content: StardewContent, locations: Iterable[Loca
return (location for location in locations if LocationTags.REQUIRES_MASTERIES not in location.tags)
def filter_modded_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
return (location for location in locations if location.mod_name is None or location.mod_name in options.mods)
def filter_modded_locations(locations: Iterable[LocationData], content: StardewContent) -> Iterable[LocationData]:
return (location for location in locations if content.are_all_enabled(location.content_packs))
def filter_disabled_locations(options: StardewValleyOptions, content: StardewContent, locations: Iterable[LocationData]) -> Iterable[LocationData]:
locations_deprecated_filter = filter_deprecated_locations(locations)
locations_farm_filter = filter_farm_type(options, locations_deprecated_filter)
locations_island_filter = filter_ginger_island(options, locations_farm_filter)
locations_qi_filter = filter_qi_order_locations(options, locations_island_filter)
locations_island_filter = filter_ginger_island(content, locations_farm_filter)
locations_qi_filter = filter_qi_order_locations(content, locations_island_filter)
locations_masteries_filter = filter_masteries_locations(content, locations_qi_filter)
locations_mod_filter = filter_modded_locations(options, locations_masteries_filter)
locations_mod_filter = filter_modded_locations(locations_masteries_filter, content)
return locations_mod_filter

View File

@@ -1,8 +1,10 @@
from .base_logic import BaseLogicMixin, BaseLogic
from ..stardew_rule import StardewRule
from ..options import FarmType
from ..stardew_rule import StardewRule, False_, True_
from ..strings.ap_names.ap_option_names import CustomLogicOptionName
from ..strings.region_names import Region
from ..strings.skill_names import Skill, ModSkill
from ..strings.tool_names import ToolMaterial, Tool
from ..strings.tool_names import ToolMaterial, Tool, FishingRod
class AbilityLogicMixin(BaseLogicMixin):
@@ -12,6 +14,13 @@ class AbilityLogicMixin(BaseLogicMixin):
class AbilityLogic(BaseLogic):
def can_mine_stone(self) -> StardewRule:
can_reach_any_mining_region = self.logic.region.can_reach_any(Region.mines, Region.skull_cavern, Region.volcano, Region.quarry_mine)
if self.options.farm_type in [FarmType.option_hill_top, FarmType.option_four_corners]:
can_reach_any_mining_region = can_reach_any_mining_region | self.logic.region.can_reach(Region.farm)
return self.logic.tool.has_tool(Tool.pickaxe) & can_reach_any_mining_region
def can_mine_perfectly(self) -> StardewRule:
return self.logic.mine.can_progress_in_the_mines_from_floor(160)
@@ -20,15 +29,17 @@ class AbilityLogic(BaseLogic):
self.logic.region.can_reach(Region.skull_cavern))
def can_farm_perfectly(self) -> StardewRule:
tool_rule = self.logic.tool.has_tool(Tool.hoe, ToolMaterial.iridium) & self.logic.tool.can_water(4)
tool_rule = self.logic.tool.has_tool(Tool.hoe, ToolMaterial.iridium) & self.logic.tool.can_water(5)
return tool_rule & self.logic.skill.has_farming_level(10)
def can_fish_perfectly(self) -> StardewRule:
skill_rule = self.logic.skill.has_level(Skill.fishing, 10)
return skill_rule & self.logic.tool.has_fishing_rod(4)
return skill_rule & self.logic.tool.has_fishing_rod(FishingRod.iridium)
def can_chop_trees(self) -> StardewRule:
return self.logic.tool.has_tool(Tool.axe) & self.logic.region.can_reach(Region.forest)
can_reach_any_tree_region = self.logic.region.can_reach_any(Region.forest, Region.backwoods, Region.bus_stop, Region.mountain, Region.desert,
Region.island_west, Region.island_north)
return self.logic.tool.has_tool(Tool.axe) & can_reach_any_tree_region
def can_chop_perfectly(self) -> StardewRule:
magic_rule = (self.logic.magic.can_use_clear_debris_instead_of_tool_level(3)) & self.logic.mod.skill.has_mod_level(ModSkill.magic, 10)
@@ -36,3 +47,20 @@ class AbilityLogic(BaseLogic):
foraging_rule = self.logic.skill.has_level(Skill.foraging, 10)
region_rule = self.logic.region.can_reach(Region.forest)
return region_rule & ((tool_rule & foraging_rule) | magic_rule)
def can_scythe_vines(self) -> StardewRule:
can_reach_any_vine_region = self.logic.region.can_reach_any(Region.forest, Region.railroad)
return self.logic.tool.has_scythe() & can_reach_any_vine_region & self.logic.season.has_any_not_winter()
def can_chair_skip(self) -> StardewRule:
if CustomLogicOptionName.chair_skips not in self.options.custom_logic:
return False_()
if CustomLogicOptionName.critical_free_samples in self.options.custom_logic:
if self.options.farm_type == FarmType.option_standard or \
self.options.farm_type == FarmType.option_riverland or \
self.options.farm_type == FarmType.option_forest or \
self.options.farm_type == FarmType.option_beach:
return True_()
return self.logic.money.can_spend_at(Region.carpenter, 350)

View File

@@ -3,8 +3,10 @@ from .base_logic import BaseLogic, BaseLogicMixin
from ..stardew_rule import StardewRule, True_
from ..strings.generic_names import Generic
from ..strings.geode_names import Geode
from ..strings.metal_names import Mineral
from ..strings.region_names import Region
from ..strings.tool_names import Tool
from ..strings.season_names import Season
from ..strings.tv_channel_names import Channel
class ActionLogicMixin(BaseLogicMixin):
@@ -19,10 +21,13 @@ class ActionLogic(BaseLogic):
tv_rule = True_()
if channel is None:
return tv_rule
if channel == Channel.sinister_signal:
sacrifice_rule = self.logic.relationship.has_children(1) & self.logic.region.can_reach(Region.witch_hut) & self.logic.has(Mineral.prismatic_shard)
return self.logic.received(channel) & tv_rule & sacrifice_rule & self.logic.season.has(Season.fall)
return self.logic.received(channel) & tv_rule
def can_pan_at(self, region: str, material: str) -> StardewRule:
return self.logic.region.can_reach(region) & self.logic.tool.has_tool(Tool.pan, material)
return self.logic.region.can_reach(region) & self.logic.tool.has_pan(material)
@cache_self1
def can_open_geode(self, geode: str) -> StardewRule:
@@ -31,3 +36,6 @@ class ActionLogic(BaseLogic):
if geode == Generic.any:
return blacksmith_access & self.logic.or_(*(self.logic.has(geode_type) for geode_type in geodes))
return blacksmith_access & self.logic.has(geode)
def can_speak_junimo(self) -> StardewRule:
return self.logic.received("Forest Magic")

View File

@@ -1,5 +1,3 @@
import typing
from .base_logic import BaseLogicMixin, BaseLogic
from ..stardew_rule import StardewRule
from ..strings.building_names import Building

View File

@@ -2,9 +2,12 @@ from .base_logic import BaseLogic, BaseLogicMixin
from ..data.artisan import MachineSource
from ..data.game_item import ItemTag
from ..stardew_rule import StardewRule
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
from ..strings.building_names import Building
from ..strings.crop_names import Vegetable, Fruit
from ..strings.fish_names import Fish, all_fish
from ..strings.flower_names import all_flowers
from ..strings.forageable_names import Mushroom
from ..strings.generic_names import Generic
from ..strings.machine_names import Machine
@@ -21,6 +24,9 @@ class ArtisanLogic(BaseLogic):
# TODO remove this one too once fish are converted to sources
self.registry.artisan_good_rules.update({ArtisanGood.specific_smoked_fish(fish): self.can_smoke(fish) for fish in all_fish})
self.registry.artisan_good_rules.update({ArtisanGood.specific_bait(fish): self.can_bait(fish) for fish in all_fish})
self.registry.artisan_good_rules.update({AnimalProduct.specific_roe(fish): self.can_get_roe(fish) for fish in all_fish})
self.registry.artisan_good_rules.update({ArtisanGood.specific_aged_roe(fish): self.can_preserves_jar(AnimalProduct.specific_roe(fish)) for fish in all_fish})
self.registry.artisan_good_rules.update({ArtisanGood.specific_honey(flower): self.can_get_honey(flower) for flower in all_flowers})
def has_jelly(self) -> StardewRule:
return self.logic.artisan.can_preserves_jar(Fruit.any)
@@ -73,6 +79,10 @@ class ArtisanLogic(BaseLogic):
machine_rule = self.logic.has(Machine.fish_smoker)
return machine_rule & self.logic.has(item)
def can_get_roe(self, item: str) -> StardewRule:
machine_rule = self.logic.building.has_building(Building.fish_pond)
return machine_rule & self.logic.has(item)
def can_bait(self, item: str) -> StardewRule:
machine_rule = self.logic.has(Machine.bait_maker)
return machine_rule & self.logic.has(item)
@@ -87,3 +97,11 @@ class ArtisanLogic(BaseLogic):
if item == Mushroom.any_edible:
return machine_rule & self.logic.has_any(*(mushroom.name for mushroom in self.content.find_tagged_items(ItemTag.EDIBLE_MUSHROOM)))
return machine_rule & self.logic.has(item)
def can_get_honey(self, flower: str) -> StardewRule:
machine_rule = self.logic.has(Machine.bee_house)
flower_rule = self.logic.has(flower)
return machine_rule & flower_rule
def can_replicate_gem(self, gem: str) -> StardewRule:
return self.logic.has(Machine.crystalarium) & self.logic.has(gem)

View File

@@ -25,6 +25,8 @@ class LogicRegistry:
self.festival_rules: Dict[str, StardewRule] = {}
self.quest_rules: Dict[str, StardewRule] = {}
self.special_order_rules: Dict[str, StardewRule] = {}
self.meme_item_rules: Dict[str, StardewRule] = {}
self.shirt_rules: Dict[str, StardewRule] = {}
self.sve_location_rules: Dict[str, StardewRule] = {}

View File

@@ -2,8 +2,13 @@ from functools import cached_property
from Utils import cache_self1
from .base_logic import BaseLogic, BaseLogicMixin
from ..stardew_rule import StardewRule, true_
from ..strings.building_names import Building
from ..stardew_rule import StardewRule, true_, false_
from ..strings.building_names import Building, WizardBuilding
from ..strings.crop_names import Fruit
from ..strings.fish_names import Fish, WaterItem
from ..strings.forageable_names import Forageable
from ..strings.material_names import Material
from ..strings.metal_names import MetalBar, Mineral
from ..strings.region_names import Region
AUTO_BUILDING_BUILDINGS = {Building.shipping_bin, Building.pet_bowl, Building.farm_house}
@@ -41,12 +46,33 @@ class BuildingLogic(BaseLogic):
# Those buildings are special. The mod auto-builds them when received, no need to go to Robin.
if building_name in AUTO_BUILDING_BUILDINGS:
return self.logic.received(Building.shipping_bin)
return self.logic.received(building_name)
carpenter_rule = self.logic.building.can_construct_buildings
item, count = building_progression.to_progressive_item(building_name)
return self.logic.received(item, count) & carpenter_rule
@cache_self1
def has_wizard_building(self, building_name: str) -> StardewRule:
return self.logic.region.can_reach(Region.wizard_tower) & self.logic.received(building_name)
@cached_property
def can_construct_buildings(self) -> StardewRule:
return self.logic.region.can_reach(Region.carpenter)
def can_purchase_wizard_blueprint(self, building_name: str) -> StardewRule:
# This rule is part of the region, so not needed here
# rule = self.logic.region.can_reach(Region.wizard_tower) & self.logic.quest.has_magic_ink()
if building_name == WizardBuilding.earth_obelisk:
return self.logic.money.can_spend(500_000) & self.logic.has_all(MetalBar.iridium, Mineral.earth_crystal)
if building_name == WizardBuilding.water_obelisk:
return self.logic.money.can_spend(500_000) & self.logic.has_all(MetalBar.iridium, Fish.clam, WaterItem.coral)
if building_name == WizardBuilding.desert_obelisk:
return self.logic.money.can_spend(1_000_000) & self.logic.has_all(MetalBar.iridium, Forageable.coconut, Forageable.cactus_fruit)
if building_name == WizardBuilding.island_obelisk:
return self.logic.money.can_spend(1_000_000) & self.logic.has_all(MetalBar.iridium, Forageable.dragon_tooth, Fruit.banana)
if building_name == WizardBuilding.junimo_hut:
return self.logic.money.can_spend(20_000) & self.logic.has_all(MetalBar.iridium, Material.stone, Fruit.starfruit, Material.fiber)
if building_name == WizardBuilding.gold_clock:
return self.logic.money.can_spend(10_000_000)
return false_

View File

@@ -5,6 +5,8 @@ from .base_logic import BaseLogicMixin, BaseLogic
from ..bundles.bundle import Bundle
from ..stardew_rule import StardewRule, True_
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
from ..strings.building_names import Building
from ..strings.bundle_names import MemeBundleName
from ..strings.currency_names import Currency
from ..strings.machine_names import Machine
from ..strings.quality_names import CropQuality, ForageQuality, FishQuality, ArtisanQuality
@@ -24,19 +26,31 @@ class BundleLogic(BaseLogic):
item_rules = []
qualities = []
time_to_grind = 0
can_speak_junimo = self.logic.region.can_reach(Region.wizard_tower)
building_rule = self.logic.true_
can_speak_junimo = self.logic.action.can_speak_junimo()
number_items_required = bundle.number_required
for bundle_item in bundle.items:
if Currency.is_currency(bundle_item.get_item()):
return can_speak_junimo & self.logic.money.can_trade(bundle_item.get_item(), bundle_item.amount)
item_rules.append(bundle_item.get_item())
item = bundle_item.get_item()
if Currency.is_currency(item):
return can_speak_junimo & self.logic.money.can_trade(item, bundle_item.amount)
if item == Building.well:
building_rule = self.logic.building.has_building(item)
number_items_required -= 1
else:
item_rules.append(item)
if bundle_item.amount > 50:
time_to_grind = bundle_item.amount // 50
qualities.append(bundle_item.quality)
quality_rules = self.get_quality_rules(qualities)
item_rules = self.logic.has_n(*item_rules, count=bundle.number_required)
item_rules = self.logic.has_n(*item_rules, count=number_items_required)
time_rule = self.logic.time.has_lived_months(time_to_grind)
return can_speak_junimo & item_rules & quality_rules & time_rule
special_rule = self.get_special_bundle_requirement(bundle)
return can_speak_junimo & item_rules & quality_rules & time_rule & building_rule & special_rule
def get_special_bundle_requirement(self, bundle: Bundle) -> StardewRule:
if bundle.name == MemeBundleName.pomnut:
return self.logic.building.has_building(Building.stable)
return self.logic.true_
def get_quality_rules(self, qualities: List[str]) -> StardewRule:
crop_quality = CropQuality.get_highest(qualities)
@@ -72,3 +86,8 @@ class BundleLogic(BaseLogic):
# 1 - Break the tree
# 2 - Build the house, which summons the bundle racoon. This one is done manually if quests are turned off
return self.logic.received(CommunityUpgrade.raccoon, 2)
def can_feed_trash_bear(self, *items: str) -> StardewRule:
return (self.logic.received("Trash Bear Arrival") &
self.logic.region.can_reach(Region.forest) &
self.logic.has_all(*items))

View File

@@ -2,9 +2,13 @@ from functools import cached_property
from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from ..options import Monstersanity
from ..stardew_rule import StardewRule, False_
from ..strings.ap_names.ap_weapon_names import APWeapon
from ..strings.ap_names.event_names import Event
from ..strings.boot_names import tier_by_boots
from ..strings.performance_names import Performance
from ..strings.region_names import Region
valid_weapons = (APWeapon.weapon, APWeapon.sword, APWeapon.club, APWeapon.dagger)
@@ -34,24 +38,31 @@ class CombatLogic(BaseLogic):
@cached_property
def has_any_weapon(self) -> StardewRule:
return self.logic.received_any(*valid_weapons)
return self.logic.received(Event.received_progressive_weapon)
@cached_property
def has_decent_weapon(self) -> StardewRule:
return self.logic.or_(*(self.logic.received(weapon, 2) for weapon in valid_weapons))
return self.logic.received(Event.received_progressive_weapon, 2)
@cached_property
def has_good_weapon(self) -> StardewRule:
return self.logic.or_(*(self.logic.received(weapon, 3) for weapon in valid_weapons))
return self.logic.received(Event.received_progressive_weapon, 3)
@cached_property
def has_great_weapon(self) -> StardewRule:
return self.logic.or_(*(self.logic.received(weapon, 4) for weapon in valid_weapons))
return self.logic.received(Event.received_progressive_weapon, 4)
@cached_property
def has_galaxy_weapon(self) -> StardewRule:
return self.logic.or_(*(self.logic.received(weapon, 5) for weapon in valid_weapons))
return self.logic.received(Event.received_progressive_weapon, 5)
@cached_property
def has_slingshot(self) -> StardewRule:
return self.logic.received(APWeapon.slingshot)
@cache_self1
def has_specific_boots(self, boots: str) -> StardewRule:
tier = tier_by_boots[boots]
if tier >= 4 and self.options.monstersanity == Monstersanity.option_none:
tier = 3 # no tier 4 boots in the pool, instead tier 4 boots can be purchased after tier 3 is received
return self.logic.received(APWeapon.footwear, tier) & self.logic.region.can_reach(Region.adventurer_guild)

View File

@@ -3,13 +3,14 @@ from functools import cached_property
from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from ..data.recipe_data import RecipeSource, StarterSource, ShopSource, SkillSource, FriendshipSource, \
QueenOfSauceSource, CookingRecipe, ShopFriendshipSource
QueenOfSauceSource, CookingRecipe, ShopFriendshipSource, all_cooking_recipes
from ..data.recipe_source import CutsceneSource, ShopTradeSource
from ..options import Chefsanity
from ..stardew_rule import StardewRule, True_, False_
from ..strings.ap_names.ap_option_names import ChefsanityOptionName
from ..strings.building_names import Building
from ..strings.craftable_names import Craftable
from ..strings.region_names import LogicRegion
from ..strings.skill_names import Skill
from ..strings.tv_channel_names import Channel
@@ -22,13 +23,15 @@ class CookingLogicMixin(BaseLogicMixin):
class CookingLogic(BaseLogic):
@cached_property
def can_cook_in_kitchen(self) -> StardewRule:
return self.logic.building.has_building(Building.kitchen) | self.logic.skill.has_level(Skill.foraging, 9)
return self.logic.building.has_building(Building.kitchen) | self.logic.has(Craftable.cookout_kit)
# Should be cached
def can_cook(self, recipe: CookingRecipe = None) -> StardewRule:
def can_cook(self, recipe: CookingRecipe | str = None) -> StardewRule:
cook_rule = self.logic.region.can_reach(LogicRegion.kitchen)
if recipe is None:
return cook_rule
if isinstance(recipe, str):
recipe = next(filter(lambda x: x.meal == recipe, all_cooking_recipes))
recipe_rule = self.logic.cooking.knows_recipe(recipe.source, recipe.meal)
ingredients_rule = self.logic.has_all(*sorted(recipe.ingredients))
@@ -36,23 +39,23 @@ class CookingLogic(BaseLogic):
# Should be cached
def knows_recipe(self, source: RecipeSource, meal_name: str) -> StardewRule:
if self.options.chefsanity == Chefsanity.option_none:
if self.options.chefsanity == Chefsanity.preset_none:
return self.logic.cooking.can_learn_recipe(source)
if isinstance(source, StarterSource):
return self.logic.cooking.received_recipe(meal_name)
if isinstance(source, ShopTradeSource) and self.options.chefsanity & Chefsanity.option_purchases:
if isinstance(source, ShopTradeSource) and ChefsanityOptionName.purchases in self.options.chefsanity:
return self.logic.cooking.received_recipe(meal_name)
if isinstance(source, ShopSource) and self.options.chefsanity & Chefsanity.option_purchases:
if isinstance(source, ShopSource) and ChefsanityOptionName.purchases in self.options.chefsanity:
return self.logic.cooking.received_recipe(meal_name)
if isinstance(source, SkillSource) and self.options.chefsanity & Chefsanity.option_skills:
if isinstance(source, SkillSource) and ChefsanityOptionName.skills in self.options.chefsanity:
return self.logic.cooking.received_recipe(meal_name)
if isinstance(source, CutsceneSource) and self.options.chefsanity & Chefsanity.option_friendship:
if isinstance(source, CutsceneSource) and ChefsanityOptionName.friendship in self.options.chefsanity:
return self.logic.cooking.received_recipe(meal_name)
if isinstance(source, FriendshipSource) and self.options.chefsanity & Chefsanity.option_friendship:
if isinstance(source, FriendshipSource) and ChefsanityOptionName.friendship in self.options.chefsanity:
return self.logic.cooking.received_recipe(meal_name)
if isinstance(source, QueenOfSauceSource) and self.options.chefsanity & Chefsanity.option_queen_of_sauce:
if isinstance(source, QueenOfSauceSource) and ChefsanityOptionName.queen_of_sauce in self.options.chefsanity:
return self.logic.cooking.received_recipe(meal_name)
if isinstance(source, ShopFriendshipSource) and self.options.chefsanity & Chefsanity.option_purchases:
if isinstance(source, ShopFriendshipSource) and ChefsanityOptionName.purchases in self.options.chefsanity:
return self.logic.cooking.received_recipe(meal_name)
return self.logic.cooking.can_learn_recipe(source)
@@ -79,3 +82,14 @@ class CookingLogic(BaseLogic):
@cache_self1
def received_recipe(self, meal_name: str):
return self.logic.received(f"{meal_name} Recipe")
def can_have_cooked_recipes(self, number: int) -> StardewRule:
if number <= 0:
return self.logic.true_
recipe_rules = []
for recipe in all_cooking_recipes:
if recipe.content_pack and not self.content.is_enabled(recipe.content_pack):
continue
recipe_rules.append(self.can_cook(recipe))
number = min(len(recipe_rules), number)
return self.logic.count(number, *recipe_rules)

View File

@@ -1,9 +1,9 @@
from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from .. import options
from ..data.craftable_data import CraftingRecipe
from ..data.craftable_data import CraftingRecipe, all_crafting_recipes, all_crafting_recipes_by_name
from ..data.recipe_source import CutsceneSource, ShopTradeSource, ArchipelagoSource, LogicSource, SpecialOrderSource, \
FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource, SkillCraftsanitySource
FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource, SkillCraftsanitySource, ShopWithKnownRecipeSource
from ..options import Craftsanity, SpecialOrderLocations
from ..stardew_rule import StardewRule, True_, False_
from ..strings.region_names import Region
@@ -55,6 +55,8 @@ class CraftingLogic(BaseLogic):
return self.logic.received_all(*recipe.source.ap_item)
if isinstance(recipe.source, ShopTradeSource):
return self.logic.money.can_trade_at(recipe.source.region, recipe.source.currency, recipe.source.price)
if isinstance(recipe.source, ShopWithKnownRecipeSource):
return self.knows_recipe(all_crafting_recipes_by_name[recipe.source.recipe_required]) & self.logic.money.can_spend_at(recipe.source.region, recipe.source.price)
if isinstance(recipe.source, ShopSource):
return self.logic.money.can_spend_at(recipe.source.region, recipe.source.price)
if isinstance(recipe.source, SkillCraftsanitySource):
@@ -83,3 +85,14 @@ class CraftingLogic(BaseLogic):
@cache_self1
def received_recipe(self, item_name: str):
return self.logic.received(f"{item_name} Recipe")
def can_have_crafted_recipes(self, number: int) -> StardewRule:
if number <= 0:
return self.logic.true_
recipe_rules = []
for recipe in all_crafting_recipes:
if recipe.content_pack is not None and not self.content.are_all_enabled(recipe.content_pack):
continue
recipe_rules.append(self.can_craft(recipe))
number = min(len(recipe_rules), number)
return self.logic.count(number, *recipe_rules)

View File

@@ -3,8 +3,10 @@ from typing import Union, Tuple
from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from .. import options
from ..stardew_rule import StardewRule, True_, false_
from ..content.vanilla.ginger_island import ginger_island_content_pack
from ..stardew_rule import StardewRule
from ..strings.ap_names.ap_option_names import CustomLogicOptionName
from ..strings.craftable_names import Bomb
from ..strings.fertilizer_names import Fertilizer
from ..strings.region_names import Region, LogicRegion
from ..strings.season_names import Season
@@ -27,17 +29,30 @@ class FarmingLogicMixin(BaseLogicMixin):
class FarmingLogic(BaseLogic):
@cached_property
def has_farming_tools(self) -> StardewRule:
return self.logic.tool.has_tool(Tool.hoe) & self.logic.tool.can_water(0)
def has_farming_tools_and_water(self) -> StardewRule:
if CustomLogicOptionName.rain_watering in self.options.custom_logic:
return self.has_hoeing_tool
return self.has_hoeing_tool & self.logic.tool.can_water()
@cached_property
def has_farming_and_watering_tools(self) -> StardewRule:
return self.has_hoeing_tool & self.logic.tool.can_water()
@cached_property
def has_hoeing_tool(self) -> StardewRule:
if CustomLogicOptionName.bomb_hoeing in self.options.custom_logic:
return self.logic.tool.has_tool(Tool.hoe) | self.logic.has_any(Bomb.cherry_bomb, Bomb.bomb, Bomb.mega_bomb)
return self.logic.tool.has_tool(Tool.hoe)
def has_fertilizer(self, tier: int) -> StardewRule:
if tier <= 0:
return True_()
assert 0 <= tier <= 3
if tier == 0:
return self.logic.true_
if tier == 1:
return self.logic.has(Fertilizer.basic)
if tier == 2:
return self.logic.has(Fertilizer.quality)
if tier >= 3:
if tier == 3:
return self.logic.has(Fertilizer.deluxe)
return self.logic.false_
@@ -45,7 +60,8 @@ class FarmingLogic(BaseLogic):
@cache_self1
def can_plant_and_grow_item(self, seasons: Union[str, Tuple[str]]) -> StardewRule:
if seasons == (): # indoor farming
return (self.logic.region.can_reach(Region.greenhouse) | self.logic.farming.has_island_farm()) & self.logic.farming.has_farming_tools
return (self.logic.region.can_reach(Region.greenhouse) & self.logic.farming.has_farming_and_watering_tools) |\
(self.logic.farming.has_island_farm() & self.logic.farming.has_farming_tools_and_water)
if isinstance(seasons, str):
seasons = (seasons,)
@@ -53,6 +69,6 @@ class FarmingLogic(BaseLogic):
return self.logic.or_(*(self.logic.region.can_reach(farming_region_by_season[season]) for season in seasons))
def has_island_farm(self) -> StardewRule:
if self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_false:
if self.content.is_enabled(ginger_island_content_pack):
return self.logic.region.can_reach(Region.island_west)
return false_
return self.logic.false_

View File

@@ -3,15 +3,16 @@ from ..options import FestivalLocations
from ..stardew_rule import StardewRule
from ..strings.animal_product_names import AnimalProduct
from ..strings.book_names import Book
from ..strings.craftable_names import Fishing
from ..strings.crop_names import Fruit, Vegetable
from ..strings.festival_check_names import FestivalCheck
from ..strings.fish_names import Fish
from ..strings.forageable_names import Forageable
from ..strings.generic_names import Generic
from ..strings.gift_names import Gift
from ..strings.machine_names import Machine
from ..strings.monster_names import Monster
from ..strings.region_names import Region
from ..strings.season_names import Season
class FestivalLogicMixin(BaseLogicMixin):
@@ -42,7 +43,7 @@ class FestivalLogic(BaseLogic):
FestivalCheck.rarecrow_2: self.logic.money.can_spend(5000),
FestivalCheck.fishing_competition: self.logic.festival.can_win_fishing_competition(),
FestivalCheck.rarecrow_4: self.logic.money.can_spend(5000),
FestivalCheck.mermaid_pearl: self.logic.has(Forageable.secret_note),
FestivalCheck.mermaid_show: self.logic.true_,
FestivalCheck.cone_hat: self.logic.money.can_spend(2500),
FestivalCheck.iridium_fireplace: self.logic.money.can_spend(15000),
FestivalCheck.rarecrow_7: self.logic.money.can_spend(5000) & self.logic.museum.can_donate_museum_artifacts(20),
@@ -94,25 +95,24 @@ class FestivalLogic(BaseLogic):
FestivalCheck.willy_challenge: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.scorpion_carp]),
FestivalCheck.desert_scholar: self.logic.true_,
FestivalCheck.squidfest_day_1_copper: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]),
FestivalCheck.squidfest_day_1_iron: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.has(Fishing.bait),
FestivalCheck.squidfest_day_1_gold: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.has(Fishing.deluxe_bait),
FestivalCheck.squidfest_day_1_iridium: self.logic.festival.can_squidfest_day_1_iridium_reward(),
FestivalCheck.squidfest_day_1_iron: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.fishing.can_use_any_bait(),
FestivalCheck.squidfest_day_1_gold: self.logic.festival.can_squidfest_iridium_reward(),
FestivalCheck.squidfest_day_1_iridium: self.logic.festival.can_squidfest_iridium_reward(),
FestivalCheck.squidfest_day_2_copper: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]),
FestivalCheck.squidfest_day_2_iron: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.has(Fishing.bait),
FestivalCheck.squidfest_day_2_gold: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.has(Fishing.deluxe_bait),
FestivalCheck.squidfest_day_2_iridium: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) &
self.logic.fishing.has_specific_bait(self.content.fishes[Fish.squid]),
FestivalCheck.squidfest_day_2_iron: self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.fishing.can_use_any_bait(),
FestivalCheck.squidfest_day_2_gold: self.logic.festival.can_squidfest_iridium_reward(),
FestivalCheck.squidfest_day_2_iridium: self.logic.festival.can_squidfest_iridium_reward(),
})
for i in range(1, 11):
check_name = f"{FestivalCheck.trout_derby_reward_pattern}{i}"
self.registry.festival_rules[check_name] = self.logic.fishing.can_catch_fish(self.content.fishes[Fish.rainbow_trout])
def can_squidfest_day_1_iridium_reward(self) -> StardewRule:
return self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.fishing.has_specific_bait(self.content.fishes[Fish.squid])
def can_squidfest_iridium_reward(self) -> StardewRule:
return self.logic.fishing.can_catch_fish(self.content.fishes[Fish.squid]) & self.logic.fishing.can_use_specific_bait(Fish.squid)
def has_squidfest_day_1_iridium_reward(self) -> StardewRule:
if self.options.festival_locations == FestivalLocations.option_disabled:
return self.logic.festival.can_squidfest_day_1_iridium_reward()
return self.logic.festival.can_squidfest_iridium_reward()
else:
return self.logic.received(f"Book: {Book.the_art_o_crabbing}")
@@ -122,6 +122,9 @@ class FestivalLogic(BaseLogic):
def can_succeed_luau_soup(self) -> StardewRule:
if self.options.festival_locations != FestivalLocations.option_hard:
return self.logic.true_
return self.can_get_luau_soup_delight()
def can_get_luau_soup_delight(self) -> StardewRule:
eligible_fish = (Fish.blobfish, Fish.crimsonfish, Fish.ice_pip, Fish.lava_eel, Fish.legend, Fish.angler, Fish.catfish, Fish.glacierfish,
Fish.mutant_carp, Fish.spookfish, Fish.stingray, Fish.sturgeon, Fish.super_cucumber)
fish_rule = self.logic.has_any(*(f for f in eligible_fish if f in self.content.fishes)) # To filter stingray
@@ -137,7 +140,9 @@ class FestivalLogic(BaseLogic):
def can_succeed_grange_display(self) -> StardewRule:
if self.options.festival_locations != FestivalLocations.option_hard:
return self.logic.true_
return self.can_get_grange_display_max_score()
def can_get_grange_display_max_score(self) -> StardewRule:
# Other animal products are not counted in the animal product category
good_animal_products = [
AnimalProduct.duck_egg, AnimalProduct.duck_feather, AnimalProduct.egg, AnimalProduct.goat_milk, AnimalProduct.golden_egg, AnimalProduct.large_egg,
@@ -157,7 +162,7 @@ class FestivalLogic(BaseLogic):
fish_rule = self.logic.fishing.can_fish_anywhere(50)
# Hazelnut always available since the grange display is in fall
forage_rule = self.logic.region.can_reach_any((Region.forest, Region.backwoods))
forage_rule = self.logic.region.can_reach_any(Region.forest, Region.backwoods)
# More than half the minerals are good enough
mineral_rule = self.logic.action.can_open_geode(Generic.any)
@@ -186,3 +191,8 @@ class FestivalLogic(BaseLogic):
for rarecrow_number in range(1, 9):
rules.append(self.logic.received(f"Rarecrow #{rarecrow_number}"))
return self.logic.and_(*rules)
def has_golden_pumpkin(self) -> StardewRule:
if self.options.festival_locations == FestivalLocations.option_disabled:
return self.logic.season.has(Season.fall)
return self.logic.received(Gift.golden_pumpkin) & self.logic.season.has(Season.fall)

View File

@@ -0,0 +1,50 @@
from .base_logic import BaseLogic, BaseLogicMixin
from ..data.fish_pond_data import fish_pond_quests
from ..stardew_rule import StardewRule
from ..strings.building_names import Building
from ..strings.fish_names import Fish
class FishPondLogicMixin(BaseLogicMixin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fish_pond = FishPondLogic(*args, **kwargs)
class FishPondLogic(BaseLogic):
def can_get_fish_pond_reward(self, fish: str, population: int, desired_item: str) -> StardewRule:
building_rule = self.logic.building.has_building(Building.fish_pond)
if fish == Fish.any:
return self.logic.fishing.can_fish_anywhere() & building_rule
fish_rule = self.logic.has(fish)
if population <= 1:
return building_rule & fish_rule
assert fish in fish_pond_quests, f"Cannot raise the population of {fish} to {population} in a fish pond to get {desired_item} without knowing the required quest items"
# if fish not in fish_pond_quests:
# return building_rule & fish_rule
item_rules = []
fish_quests = fish_pond_quests[fish]
for i in range(0, population):
if i not in fish_quests:
continue
quests_for_that_level = fish_quests[i]
num_quests = len(quests_for_that_level)
if num_quests <= 0:
continue
level_rules = []
for quest_item in quests_for_that_level:
if quest_item == desired_item:
continue
level_rules.append(self.logic.has(quest_item))
if num_quests <= 2:
item_rules.append(self.logic.count(num_quests, *level_rules))
else:
item_rules.append(self.logic.count(num_quests - 1, *level_rules))
return building_rule & fish_rule & self.logic.and_(*item_rules)

View File

@@ -2,10 +2,11 @@ from functools import cached_property
from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from ..content.vanilla.qi_board import qi_board_content_pack
from ..data import fish_data
from ..data.fish_data import FishItem
from ..options import ExcludeGingerIsland, SpecialOrderLocations
from ..stardew_rule import StardewRule, True_, False_
from ..stardew_rule import StardewRule, True_
from ..strings.ap_names.ap_option_names import CustomLogicOptionName
from ..strings.ap_names.mods.mod_items import SVEQuestItem
from ..strings.craftable_names import Fishing
from ..strings.fish_names import SVEFish
@@ -13,8 +14,7 @@ from ..strings.machine_names import Machine
from ..strings.quality_names import FishQuality
from ..strings.region_names import Region
from ..strings.skill_names import Skill
fishing_regions = (Region.beach, Region.town, Region.forest, Region.mountain, Region.island_south, Region.island_west)
from ..strings.tool_names import FishingRod
class FishingLogicMixin(BaseLogicMixin):
@@ -24,48 +24,80 @@ class FishingLogicMixin(BaseLogicMixin):
class FishingLogic(BaseLogic):
@cached_property
def can_reach_any_fishing_regions(self) -> StardewRule:
return self.logic.region.can_reach_any(Region.beach, Region.town, Region.forest, Region.mountain, Region.island_south, Region.island_west)
@cache_self1
def can_fish_anywhere(self, difficulty: int = 0) -> StardewRule:
return self.logic.fishing.can_fish(difficulty) & self.logic.region.can_reach_any(fishing_regions)
return self.logic.fishing.can_fish(difficulty) & self.logic.fishing.can_reach_any_fishing_regions
def can_fish_in_freshwater(self) -> StardewRule:
return self.logic.fishing.can_fish() & self.logic.region.can_reach_any((Region.forest, Region.town, Region.mountain))
return self.logic.fishing.can_fish() & self.logic.region.can_reach_any(Region.forest, Region.town, Region.mountain)
@cached_property
def has_max_fishing(self) -> StardewRule:
return self.logic.tool.has_fishing_rod(4) & self.logic.skill.has_level(Skill.fishing, 10)
# Advanced Iridium is not necessary for max fishing
return self.logic.tool.has_fishing_rod(FishingRod.iridium) & self.logic.skill.has_level(Skill.fishing, 10)
@cached_property
def can_fish_chests(self) -> StardewRule:
return self.logic.tool.has_fishing_rod(4) & self.logic.skill.has_level(Skill.fishing, 6)
return self.logic.tool.has_fishing_rod(FishingRod.iridium) & self.logic.skill.has_level(Skill.fishing, 6)
@cache_self1
def can_fish_at(self, region: str) -> StardewRule:
return self.logic.fishing.can_fish() & self.logic.region.can_reach(region)
@cache_self1
def can_fish(self, difficulty: int = 0) -> StardewRule:
skill_required = min(10, max(0, int((difficulty / 10) - 1)))
def can_fish_with_cast_distance(self, region: str, distance: int) -> StardewRule:
if distance >= 7:
required_levels = 15
elif distance >= 6:
required_levels = 8
elif distance >= 5:
required_levels = 4
elif distance >= 4:
required_levels = 1
else:
required_levels = 0
return self.logic.fishing.can_fish_at(region) & self.logic.skill.has_level(Skill.fishing, required_levels)
def can_fish(self, difficulty: int = 0, minimum_level: int = 0) -> StardewRule:
skill_required = int((difficulty / 10) - 1)
if difficulty <= 40:
skill_required = 0
if CustomLogicOptionName.extreme_fishing in self.options.custom_logic:
skill_required -= 4
elif CustomLogicOptionName.hard_fishing in self.options.custom_logic:
skill_required -= 2
elif CustomLogicOptionName.easy_fishing in self.options.custom_logic and difficulty > 20:
skill_required += 2
skill_required = min(10, max(minimum_level, skill_required))
skill_rule = self.logic.skill.has_level(Skill.fishing, skill_required)
# Training rod only works with fish < 50. Fiberglass does not help you to catch higher difficulty fish, so it's skipped in logic.
number_fishing_rod_required = 1 if difficulty < 50 else (2 if difficulty < 80 else 4)
return self.logic.tool.has_fishing_rod(number_fishing_rod_required) & skill_rule
if difficulty < 50:
fishing_rod_required = FishingRod.training
elif difficulty < 80:
fishing_rod_required = FishingRod.bamboo
else:
fishing_rod_required = FishingRod.iridium
return self.logic.tool.has_fishing_rod(fishing_rod_required) & skill_rule
@cache_self1
def can_catch_fish(self, fish: FishItem) -> StardewRule:
quest_rule = True_()
if fish.extended_family:
quest_rule = self.logic.fishing.can_start_extended_family_quest()
region_rule = self.logic.region.can_reach_any(fish.locations)
region_rule = self.logic.region.can_reach_any(*fish.locations)
season_rule = self.logic.season.has_any(fish.seasons)
if fish.difficulty == -1:
difficulty_rule = self.logic.fishing.can_crab_pot
else:
difficulty_rule = self.logic.fishing.can_fish(120 if fish.legendary else fish.difficulty)
difficulty_rule = self.logic.fishing.can_fish(120 if fish.legendary else fish.difficulty, fish.minimum_level)
if fish.name == SVEFish.kittyfish:
item_rule = self.logic.received(SVEQuestItem.kittyfish_spell)
@@ -80,18 +112,17 @@ class FishingLogic(BaseLogic):
return self.logic.fishing.can_catch_fish(fish)
def can_start_extended_family_quest(self) -> StardewRule:
if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return False_()
if not self.options.special_order_locations & SpecialOrderLocations.value_qi:
return False_()
return (self.logic.region.can_reach(Region.qi_walnut_room) &
self.logic.and_(*(self.logic.fishing.can_catch_fish(fish) for fish in fish_data.vanilla_legendary_fish)))
if self.content.is_enabled(qi_board_content_pack):
return (self.logic.region.can_reach(Region.qi_walnut_room) &
self.logic.and_(*(self.logic.fishing.can_catch_fish(fish) for fish in fish_data.vanilla_legendary_fish)))
return self.logic.false_
def can_catch_quality_fish(self, fish_quality: str) -> StardewRule:
if fish_quality == FishQuality.basic:
return True_()
return self.logic.true_
if fish_quality == FishQuality.silver:
return self.logic.tool.has_fishing_rod(2)
return self.logic.tool.has_fishing_rod(FishingRod.bamboo)
if fish_quality == FishQuality.gold:
return self.logic.skill.has_level(Skill.fishing, 4) & self.can_use_tackle(Fishing.quality_bobber)
if fish_quality == FishQuality.iridium:
@@ -100,7 +131,7 @@ class FishingLogic(BaseLogic):
raise ValueError(f"Quality {fish_quality} is unknown.")
def can_use_tackle(self, tackle: str) -> StardewRule:
return self.logic.tool.has_fishing_rod(4) & self.logic.has(tackle)
return self.logic.tool.has_fishing_rod(FishingRod.iridium) & self.logic.has(tackle)
def can_catch_every_fish(self) -> StardewRule:
rules = [self.has_max_fishing]
@@ -112,12 +143,27 @@ class FishingLogic(BaseLogic):
return self.logic.and_(*rules)
def can_catch_many_fish(self, number: int) -> StardewRule:
rules = [
self.logic.fishing.can_catch_fish(fish)
for fish in self.content.fishes.values()
]
if number > len(rules):
number = len(rules)
return self.logic.count(number, *rules)
def has_specific_bait(self, fish: FishItem) -> StardewRule:
return self.can_catch_fish(fish) & self.logic.has(Machine.bait_maker)
def can_use_specific_bait(self, fish_name: str) -> StardewRule:
return self.has_specific_bait(self.content.fishes[fish_name]) & self.logic.tool.has_fishing_rod(FishingRod.fiberglass)
def can_use_any_bait(self) -> StardewRule:
return self.logic.has(Fishing.bait) & self.logic.tool.has_fishing_rod(FishingRod.fiberglass)
@cached_property
def can_crab_pot_anywhere(self) -> StardewRule:
return self.logic.fishing.can_crab_pot & self.logic.region.can_reach_any(fishing_regions)
return self.logic.fishing.can_crab_pot & self.can_reach_any_fishing_regions
@cache_self1
def can_crab_pot_at(self, region: str) -> StardewRule:

View File

@@ -1,6 +1,7 @@
from functools import cached_property
from .base_logic import BaseLogic, BaseLogicMixin
from ..data.secret_note_data import RequiredGifts
from ..stardew_rule import StardewRule
from ..strings.animal_product_names import AnimalProduct
from ..strings.gift_names import Gift
@@ -17,3 +18,32 @@ class GiftLogic(BaseLogic):
@cached_property
def has_any_universal_love(self) -> StardewRule:
return self.logic.has_any(Gift.golden_pumpkin, Gift.pearl, "Prismatic Shard", AnimalProduct.rabbit_foot)
def can_gift_to(self, npc: str, item: str) -> StardewRule:
return self.logic.relationship.can_meet(npc) & self.logic.has(item)
def can_gift_any_to(self, npc: str, *items: str) -> StardewRule:
return self.logic.relationship.can_meet(npc) & self.logic.has_any(*items)
def can_fulfill(self, required_gifts: RequiredGifts | list[RequiredGifts]) -> StardewRule:
if isinstance(required_gifts, RequiredGifts):
return self.can_gift_all_to(required_gifts)
return self.can_gift_all_to_all(required_gifts)
def can_gift_all_to(self, required_gifts: RequiredGifts) -> StardewRule:
return self.logic.relationship.can_meet(required_gifts.npc) & self.logic.has_all(*required_gifts.gifts)
def can_gift_all_to_all(self, required_gifts: list[RequiredGifts]) -> StardewRule:
items = [gift for required_gift in required_gifts for gift in required_gift.gifts]
return self.logic.relationship.can_meet_all(*[required_gift.npc for required_gift in required_gifts]) & self.logic.has_all(*items)
def can_give_loved_gifts_to_everyone(self) -> StardewRule:
rules = []
for npc in self.content.villagers:
meet_rule = self.logic.relationship.can_meet(npc)
rules.append(meet_rule)
rules.append(self.has_any_universal_love)
return self.logic.and_(*rules)

View File

@@ -5,8 +5,11 @@ from ..locations import LocationTags, locations_by_tag
from ..mods.mod_data import ModNames
from ..options import options
from ..stardew_rule import StardewRule
from ..strings.ap_names.ap_option_names import SecretsanityOptionName
from ..strings.building_names import Building
from ..strings.crop_names import Fruit
from ..strings.quest_names import Quest
from ..strings.region_names import Region
from ..strings.season_names import Season
from ..strings.wallet_item_names import Wallet
@@ -97,11 +100,8 @@ class GoalLogic(BaseLogic):
def can_complete_gourmet_chef(self) -> StardewRule:
cooksanity_prefix = "Cook "
all_recipes_names = []
exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true
for location in locations_by_tag[LocationTags.COOKSANITY]:
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
continue
if location.mod_name and location.mod_name not in self.options.mods:
if not self.content.are_all_enabled(location.content_packs):
continue
all_recipes_names.append(location.name[len(cooksanity_prefix):])
all_recipes = [all_cooking_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
@@ -110,17 +110,14 @@ class GoalLogic(BaseLogic):
def can_complete_craft_master(self) -> StardewRule:
craftsanity_prefix = "Craft "
all_recipes_names = []
exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true
exclude_masteries = not self.content.features.skill_progression.are_masteries_shuffled
for location in locations_by_tag[LocationTags.CRAFTSANITY]:
if not location.name.startswith(craftsanity_prefix):
continue
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
continue
# FIXME Remove when recipes are in content packs
if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags:
continue
if location.mod_name and location.mod_name not in self.options.mods:
if not self.content.are_all_enabled(location.content_packs):
continue
all_recipes_names.append(location.name[len(craftsanity_prefix):])
all_recipes = [all_crafting_recipes_by_name[recipe_name] for recipe_name in all_recipes_names]
@@ -133,7 +130,6 @@ class GoalLogic(BaseLogic):
other_rules = []
number_of_stardrops_to_receive = 0
number_of_stardrops_to_receive += 1 # The Mines level 100
number_of_stardrops_to_receive += 1 # Old Master Cannoli
number_of_stardrops_to_receive += 1 # Museum Stardrop
number_of_stardrops_to_receive += 1 # Krobus Stardrop
@@ -154,11 +150,41 @@ class GoalLogic(BaseLogic):
else:
other_rules.append(self.logic.relationship.has_hearts_with_any_bachelor(13))
if ModNames.deepwoods in self.options.mods: # Petting the Unicorn
# Old Master Cannoli
if SecretsanityOptionName.easy in self.options.secretsanity:
number_of_stardrops_to_receive += 1
else:
other_rules.append(self.logic.has(Fruit.sweet_gem_berry) & self.logic.region.can_reach(Region.secret_woods))
if self.content.is_enabled(ModNames.deepwoods): # Petting the Unicorn
number_of_stardrops_to_receive += 1
return self.logic.received("Stardrop", number_of_stardrops_to_receive) & self.logic.and_(*other_rules, allow_empty=True)
def can_complete_mad_hatter(self, all_location_names_in_slot: list[str]) -> StardewRule:
if not self.content.features.hatsanity.is_enabled:
raise Exception("Cannot play Mad Hatter Goal without Hatsanity")
rules = []
for hatsanity_location in locations_by_tag[LocationTags.HATSANITY]:
if hatsanity_location.name not in all_location_names_in_slot:
continue
rules.append(self.logic.region.can_reach_location(hatsanity_location.name))
return self.logic.and_(*rules)
def can_complete_ultimate_foodie(self, all_location_names_in_slot: list[str]) -> StardewRule:
if not self.options.eatsanity.value:
raise Exception("Cannot play Ultimate Foodie Goal without Eatsanity")
rules = []
for eatsanity_location in locations_by_tag[LocationTags.EATSANITY]:
if eatsanity_location.name not in all_location_names_in_slot:
continue
rules.append(self.logic.region.can_reach_location(eatsanity_location.name))
return self.logic.and_(*rules)
def can_complete_allsanity(self) -> StardewRule:
return self.logic.has_progress_percent(100)

View File

@@ -1,12 +1,6 @@
from typing import Union, TYPE_CHECKING
from Utils import cache_self1
from .base_logic import BaseLogic, BaseLogicMixin
from .book_logic import BookLogicMixin
from .has_logic import HasLogicMixin
from .received_logic import ReceivedLogicMixin
from .region_logic import RegionLogicMixin
from .time_logic import TimeLogicMixin
from ..options import ExcludeGingerIsland
from ..stardew_rule import StardewRule, HasProgressionPercent
from ..strings.book_names import Book
from ..strings.craftable_names import Consumable
@@ -17,11 +11,6 @@ from ..strings.material_names import Material
from ..strings.region_names import Region
from ..strings.tool_names import Tool
if TYPE_CHECKING:
from .tool_logic import ToolLogicMixin
else:
ToolLogicMixin = object
MIN_MEDIUM_ITEMS = 10
MAX_MEDIUM_ITEMS = 999
PERCENT_REQUIRED_FOR_MAX_MEDIUM_ITEM = 24
@@ -29,7 +18,7 @@ PERCENT_REQUIRED_FOR_MAX_MEDIUM_ITEM = 24
EASY_ITEMS = {Material.wood, Material.stone, Material.fiber, Material.sap}
MIN_EASY_ITEMS = 300
MAX_EASY_ITEMS = 2997
PERCENT_REQUIRED_FOR_MAX_EASY_ITEM = 6
PERCENT_REQUIRED_FOR_MAX_EASY_ITEM = 8
class GrindLogicMixin(BaseLogicMixin):
@@ -59,9 +48,8 @@ class GrindLogic(BaseLogic):
def can_grind_prize_tickets(self, quantity: int) -> StardewRule:
claiming_rule = self.logic.region.can_reach(Region.mayor_house)
return self.logic.and_(claiming_rule, self.logic.has(Currency.prize_ticket),
# Assuming two per month if the player does not grind it.
self.logic.time.has_lived_months(quantity // 2))
help_wanted_rules = self.logic.quest.can_complete_help_wanteds(quantity * 3)
return self.logic.and_(claiming_rule, self.logic.has(Currency.prize_ticket), help_wanted_rules)
def can_grind_fishing_treasure_chests(self, quantity: int) -> StardewRule:
return self.logic.and_(self.logic.has(WaterChest.fishing_chest),
@@ -73,12 +61,23 @@ class GrindLogic(BaseLogic):
# Assuming twelve per month if the player does not grind it.
self.logic.time.has_lived_months(quantity // 12))
def can_grind_weeds(self, quantity: int) -> StardewRule:
regions = [Region.farm, Region.town, Region.forest, Region.secret_woods, Region.backwoods, Region.mountain, Region.railroad, Region.mutant_bug_lair]
if self.options.exclude_ginger_island == ExcludeGingerIsland.option_false:
regions.extend([Region.island_east, Region.island_west])
return self.logic.and_(self.logic.tool.has_scythe(),
self.logic.region.can_reach_all(*regions),
# Assuming 1000 per month if the player does not grind it
self.logic.time.has_lived_months(quantity // 1000))
def can_grind_item(self, quantity: int, item: str | None = None) -> StardewRule:
if item in EASY_ITEMS:
return self.logic.grind.can_grind_easy_item(quantity)
else:
if item is None:
return self.logic.grind.can_grind_medium_item(quantity)
if item in EASY_ITEMS:
return self.logic.grind.can_grind_easy_item(quantity) & self.logic.has(item)
return self.logic.grind.can_grind_medium_item(quantity) & self.logic.has(item)
@cache_self1
def can_grind_medium_item(self, quantity: int) -> StardewRule:
if quantity <= MIN_MEDIUM_ITEMS:

View File

@@ -27,8 +27,14 @@ class HarvestingLogic(BaseLogic):
@cache_self1
def can_forage_from(self, source: ForagingSource) -> StardewRule:
seasons_rule = self.logic.season.has_any(source.seasons)
regions_rule = self.logic.region.can_reach_any(source.regions)
return seasons_rule & regions_rule
if source.require_all_regions:
regions_rule = self.logic.region.can_reach_all(*source.regions)
else:
regions_rule = self.logic.region.can_reach_any(*source.regions)
if source.grind_months == 0:
return seasons_rule & regions_rule
else:
return seasons_rule & regions_rule & self.logic.time.has_lived_months(source.grind_months)
@cache_self1
def can_harvest_tree_from(self, source: HarvestFruitTreeSource) -> StardewRule:

View File

@@ -0,0 +1,28 @@
from functools import cached_property
from .base_logic import BaseLogicMixin, BaseLogic
from ..data.hats_data import HatItem
from ..stardew_rule import StardewRule
from ..strings.fish_names import Fish
from ..strings.region_names import LogicRegion
class HatLogicMixin(BaseLogicMixin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.hat = HatLogic(*args, **kwargs)
class HatLogic(BaseLogic):
@cached_property
def can_get_unlikely_hat_at_outfit_services(self) -> StardewRule:
return self.logic.region.can_reach(LogicRegion.desert_festival) & self.logic.time.has_lived_months(12)
@cached_property
def has_bucket_hat(self) -> StardewRule:
trout_derby_rule = self.logic.region.can_reach(LogicRegion.trout_derby) & self.logic.fishing.can_catch_fish(self.content.fishes[Fish.rainbow_trout])
return trout_derby_rule
def can_wear(self, hat: HatItem) -> StardewRule:
return self.logic.has(hat.clarified_name)

View File

@@ -17,16 +17,20 @@ from .cooking_logic import CookingLogicMixin
from .crafting_logic import CraftingLogicMixin
from .farming_logic import FarmingLogicMixin
from .festival_logic import FestivalLogicMixin
from .fish_pond_logic import FishPondLogicMixin
from .fishing_logic import FishingLogicMixin
from .gift_logic import GiftLogicMixin
from .goal_logic import GoalLogicMixin
from .grind_logic import GrindLogicMixin
from .harvesting_logic import HarvestingLogicMixin
from .has_logic import HasLogicMixin
from .logic_event import all_logic_events
from .hats_logic import HatLogicMixin
from .logic_event import all_item_events
from .meme_items_logic import MemeItemsLogicMixin
from .mine_logic import MineLogicMixin
from .money_logic import MoneyLogicMixin
from .monster_logic import MonsterLogicMixin
from .movie_logic import MovieLogicMixin
from .museum_logic import MuseumLogicMixin
from .pet_logic import PetLogicMixin
from .quality_logic import QualityLogicMixin
@@ -37,28 +41,34 @@ from .relationship_logic import RelationshipLogicMixin
from .requirement_logic import RequirementLogicMixin
from .season_logic import SeasonLogicMixin
from .shipping_logic import ShippingLogicMixin
from .shirts_logic import ShirtLogicMixin
from .skill_logic import SkillLogicMixin
from .source_logic import SourceLogicMixin
from .special_items_logic import SpecialItemsLogicMixin
from .special_order_logic import SpecialOrderLogicMixin
from .tailoring_logic import TailoringLogicMixin
from .time_logic import TimeLogicMixin
from .tool_logic import ToolLogicMixin
from .traveling_merchant_logic import TravelingMerchantLogicMixin
from .wallet_logic import WalletLogicMixin
from .walnut_logic import WalnutLogicMixin
from ..content.game_content import StardewContent
from ..content.vanilla.ginger_island import ginger_island_content_pack
from ..data.craftable_data import all_crafting_recipes
from ..data.museum_data import all_museum_items
from ..data.recipe_data import all_cooking_recipes
from ..mods.logic.magic_logic import MagicLogicMixin
from ..mods.logic.mod_logic import ModLogicMixin
from ..options import ExcludeGingerIsland, StardewValleyOptions
from ..stardew_rule import False_, True_, StardewRule
from ..options import StardewValleyOptions, BundleRandomization, IncludeEndgameLocations
from ..stardew_rule import False_, StardewRule, Or, Reach
from ..strings.animal_names import Animal
from ..strings.animal_product_names import AnimalProduct
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
from ..strings.artisan_good_names import ArtisanGood
from ..strings.boot_names import tier_by_boots
from ..strings.building_names import Building
from ..strings.craftable_names import Consumable, Ring, Fishing, Lighting, WildSeeds
from ..strings.catalogue_names import items_by_catalogue
from ..strings.craftable_names import Consumable, Ring, Fishing, Lighting, WildSeeds, Furniture
from ..strings.crop_names import Fruit, Vegetable
from ..strings.currency_names import Currency
from ..strings.decoration_names import Decoration
@@ -73,16 +83,16 @@ from ..strings.gift_names import Gift
from ..strings.ingredient_names import Ingredient
from ..strings.machine_names import Machine
from ..strings.material_names import Material
from ..strings.metal_names import Ore, MetalBar, Mineral, Fossil, Artifact
from ..strings.metal_names import Ore, MetalBar, Mineral, Fossil
from ..strings.monster_drop_names import Loot
from ..strings.monster_names import Monster
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
from ..strings.special_item_names import SpecialItem
from ..strings.tool_names import Tool, ToolMaterial, FishingRod
from ..strings.villager_names import NPC
from ..strings.wallet_item_names import Wallet
logger = logging.getLogger(__name__)
@@ -93,7 +103,8 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, QualityLogicMixin,
SkillLogicMixin, FarmingLogicMixin, BundleLogicMixin, FishingLogicMixin, MineLogicMixin, CookingLogicMixin, AbilityLogicMixin,
SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin, HarvestingLogicMixin, SourceLogicMixin,
RequirementLogicMixin, BookLogicMixin, GrindLogicMixin, FestivalLogicMixin, WalnutLogicMixin, GoalLogicMixin):
RequirementLogicMixin, BookLogicMixin, GrindLogicMixin, FestivalLogicMixin, WalnutLogicMixin, GoalLogicMixin, SpecialItemsLogicMixin,
MovieLogicMixin, MemeItemsLogicMixin, HatLogicMixin, ShirtLogicMixin, TailoringLogicMixin, FishPondLogicMixin):
player: int
options: StardewValleyOptions
content: StardewContent
@@ -107,15 +118,16 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
self.registry.museum_rules.update({donation.item_name: self.museum.can_find_museum_item(donation) for donation in all_museum_items})
for recipe in all_cooking_recipes:
if recipe.mod_name and recipe.mod_name not in self.options.mods:
if recipe.content_pack and not self.content.is_enabled(recipe.content_pack):
continue
can_cook_rule = self.cooking.can_cook(recipe)
if recipe.meal in self.registry.cooking_rules:
can_cook_rule = can_cook_rule | self.registry.cooking_rules[recipe.meal]
self.registry.cooking_rules[recipe.meal] = can_cook_rule
for recipe in all_crafting_recipes:
if recipe.mod_name and recipe.mod_name not in self.options.mods:
if recipe.content_pack is not None and not self.content.are_all_enabled(recipe.content_pack):
continue
can_craft_rule = self.crafting.can_craft(recipe)
if recipe.item in self.registry.crafting_rules:
@@ -145,11 +157,14 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
"Junimo Kart Small Buff": self.arcade.has_junimo_kart_power_level(2),
"Magic Rock Candy": self.region.can_reach(Region.desert) & self.has("Prismatic Shard"),
"Muscle Remedy": self.money.can_spend_at(Region.hospital, 1000),
"Stardrop": self.received("Stardrop"),
"Iridium Snake Milk": self.quest.can_drink_snake_milk(),
# self.has(Ingredient.vinegar)),
# self.received("Deluxe Fertilizer Recipe") & self.has(MetalBar.iridium) & self.has(SVItem.sap),
# | (self.ability.can_cook() & self.relationship.has_hearts(NPC.emily, 3) & self.has(Forageable.leek) & self.has(Forageable.dandelion) &
# | (self.ability.can_cook() & self.relationship.has_hearts(NPC.jodi, 7) & self.has(AnimalProduct.cow_milk) & self.has(Ingredient.sugar)),
AnimalProduct.any_egg: self.has_any(AnimalProduct.chicken_egg, AnimalProduct.duck_egg),
AnimalProduct.any_milk: self.has_any(AnimalProduct.cow_milk, AnimalProduct.goat_milk),
AnimalProduct.brown_egg: self.animal.has_animal(Animal.chicken),
AnimalProduct.chicken_egg: self.has_any(AnimalProduct.egg, AnimalProduct.brown_egg, AnimalProduct.large_egg, AnimalProduct.large_brown_egg),
AnimalProduct.cow_milk: self.has_any(AnimalProduct.milk, AnimalProduct.large_milk),
@@ -164,9 +179,8 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
AnimalProduct.large_milk: self.animal.has_happy_animal(Animal.cow),
AnimalProduct.milk: self.animal.has_animal(Animal.cow),
AnimalProduct.rabbit_foot: self.animal.has_happy_animal(Animal.rabbit),
AnimalProduct.roe: self.fishing.can_fish_anywhere() & self.building.has_building(Building.fish_pond),
AnimalProduct.squid_ink: self.mine.can_mine_in_the_mines_floor_81_120() | (self.building.has_building(Building.fish_pond) & self.has(Fish.squid)),
AnimalProduct.sturgeon_roe: self.has(Fish.sturgeon) & self.building.has_building(Building.fish_pond),
AnimalProduct.roe: self.fish_pond.can_get_fish_pond_reward(Fish.any, 1, AnimalProduct.roe),
AnimalProduct.squid_ink: self.mine.can_mine_in_the_mines_floor_81_120() | self.fish_pond.can_get_fish_pond_reward(Fish.squid, 1, AnimalProduct.squid_ink) | self.fish_pond.can_get_fish_pond_reward(Fish.midnight_squid, 1, AnimalProduct.squid_ink),
AnimalProduct.truffle: self.animal.has_animal(Animal.pig) & self.season.has_any_not_winter(),
AnimalProduct.void_egg: self.has(AnimalProduct.void_egg_starter), # Should also check void chicken if there was an alternative to obtain it without void egg
AnimalProduct.wool: self.animal.has_animal(Animal.rabbit) | self.animal.has_animal(Animal.sheep),
@@ -174,18 +188,17 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
AnimalProduct.slime_egg_blue: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(3),
AnimalProduct.slime_egg_red: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(6),
AnimalProduct.slime_egg_purple: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(9),
AnimalProduct.slime_egg_tiger: self.can_fish_pond(Fish.lionfish, *(Forageable.ginger, Fruit.pineapple, Fruit.mango)) & self.time.has_lived_months(12) &
AnimalProduct.slime_egg_tiger: self.fish_pond.can_get_fish_pond_reward(Fish.lionfish, 9, AnimalProduct.slime_egg_tiger) & self.time.has_lived_months(12) &
self.building.has_building(Building.slime_hutch) & self.monster.can_kill(Monster.tiger_slime),
AnimalProduct.duck_egg_starter: self.logic.false_, # It could be purchased at the Feast of the Winter Star, but it's random every year, so not considering it yet...
AnimalProduct.dinosaur_egg_starter: self.logic.false_, # Dinosaur eggs are also part of the museum rules, and I don't want to touch them yet.
AnimalProduct.egg_starter: self.logic.false_, # It could be purchased at the Desert Festival, but festival logic is quite a mess, so not considering it yet...
AnimalProduct.golden_egg_starter: self.received(AnimalProduct.golden_egg) & (self.money.can_spend_at(Region.ranch, 100000) | self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 100)),
AnimalProduct.void_egg_starter: self.money.can_spend_at(Region.sewer, 5000) | (self.building.has_building(Building.fish_pond) & self.has(Fish.void_salmon)),
AnimalProduct.void_egg_starter: self.money.can_spend_at(Region.sewer, 5000),
ArtisanGood.aged_roe: self.artisan.can_preserves_jar(AnimalProduct.roe),
ArtisanGood.battery_pack: (self.has(Machine.lightning_rod) & self.season.has_any_not_winter()) | self.has(Machine.solar_panel),
ArtisanGood.caviar: self.artisan.can_preserves_jar(AnimalProduct.sturgeon_roe),
ArtisanGood.cheese: (self.has(AnimalProduct.cow_milk) & self.has(Machine.cheese_press)) | (self.region.can_reach(Region.desert) & self.has(Mineral.emerald)),
ArtisanGood.cloth: (self.has(AnimalProduct.wool) & self.has(Machine.loom)) | (self.region.can_reach(Region.desert) & self.has(Mineral.aquamarine)),
ArtisanGood.cheese: (self.has(AnimalProduct.cow_milk) & self.has(Machine.cheese_press)) | (self.region.can_reach(Region.desert) & self.artisan.can_replicate_gem(Mineral.emerald)),
ArtisanGood.cloth: (self.has(AnimalProduct.wool) & self.has(Machine.loom)) | (self.region.can_reach(Region.desert) & self.artisan.can_replicate_gem(Mineral.aquamarine)),
ArtisanGood.dinosaur_mayonnaise: self.artisan.can_mayonnaise(AnimalProduct.dinosaur_egg),
ArtisanGood.duck_mayonnaise: self.artisan.can_mayonnaise(AnimalProduct.duck_egg),
ArtisanGood.goat_cheese: self.has(AnimalProduct.goat_milk) & self.has(Machine.cheese_press),
@@ -203,11 +216,10 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
Beverage.pina_colada: self.money.can_spend_at(Region.island_resort, 600),
Beverage.triple_shot_espresso: self.has("Hot Java Ring"),
Consumable.butterfly_powder: self.money.can_spend_at(Region.sewer, 20000),
Consumable.far_away_stone: self.region.can_reach(Region.mines_floor_100) & self.has(Artifact.ancient_doll),
Consumable.fireworks_red: self.region.can_reach(Region.casino),
Consumable.fireworks_purple: self.region.can_reach(Region.casino),
Consumable.fireworks_green: self.region.can_reach(Region.casino),
Consumable.golden_animal_cracker: self.skill.has_mastery(Skill.farming),
Consumable.golden_animal_cracker: self.skill.has_mastery(Skill.farming) & (self.fishing.can_fish_chests | self.region.can_reach(Region.skull_cavern_25)),
Consumable.mystery_box: self.received(CommunityUpgrade.mr_qi_plane_ride),
Consumable.gold_mystery_box: self.received(CommunityUpgrade.mr_qi_plane_ride) & self.skill.has_mastery(Skill.foraging),
Currency.calico_egg: self.region.can_reach(LogicRegion.desert_festival),
@@ -229,30 +241,32 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
Fish.snail: self.fishing.can_crab_pot_at(Region.town),
Fishing.curiosity_lure: self.monster.can_kill(self.monster.all_monsters_by_name[Monster.mummy]),
Fishing.lead_bobber: self.skill.has_level(Skill.fishing, 6) & self.money.can_spend_at(Region.fish_shop, 200),
Forageable.hay: self.building.has_building(Building.silo) & self.tool.has_tool(Tool.scythe), #
Forageable.journal_scrap: self.region.can_reach_all((Region.island_west, Region.island_north, Region.island_south, Region.volcano_floor_10)) & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()),#
Forageable.secret_note: self.quest.has_magnifying_glass() & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()), #
Fishing.golden_bobber: self.region.can_reach(LogicRegion.desert_festival) & self.fishing.can_fish_chests,
Forageable.hay: self.building.has_building(Building.silo) & self.tool.has_scythe(), #
Forageable.journal_scrap: self.region.can_reach_all(Region.island_west, Region.island_north, Region.island_south, Region.volcano_floor_10) & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()),#
Forageable.secret_note: self.region.can_reach(LogicRegion.secret_notes), #
Fossil.bone_fragment: (self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.pickaxe)) | self.monster.can_kill(Monster.skeleton),
Fossil.fossilized_leg: self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.pickaxe),
Fossil.fossilized_ribs: self.region.can_reach(Region.island_south) & self.tool.has_tool(Tool.hoe) & self.received("Open Professor Snail Cave"),
Fossil.fossilized_skull: self.action.can_open_geode(Geode.golden_coconut),
Fossil.fossilized_spine: self.fishing.can_fish_at(Region.dig_site),
Fossil.fossilized_tail: self.action.can_pan_at(Region.dig_site, ToolMaterial.copper),
Fossil.fossilized_tail: self.action.can_pan_at(Region.dig_site, ToolMaterial.iridium),
Fossil.mummified_bat: self.region.can_reach(Region.volcano_floor_10),
Fossil.mummified_frog: self.region.can_reach(Region.island_east) & self.tool.has_tool(Tool.scythe),
Fossil.mummified_frog: self.region.can_reach(Region.island_east) & self.tool.has_scythe(),
Fossil.snake_skull: self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.hoe),
Fossil.snake_vertebrae: self.region.can_reach(Region.island_west) & self.tool.has_tool(Tool.hoe),
Furniture.exotic_double_bed: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 50),
Geode.artifact_trove: self.has(Geode.omni) & self.region.can_reach(Region.desert),
Geode.frozen: self.mine.can_mine_in_the_mines_floor_41_80(),
Geode.geode: self.mine.can_mine_in_the_mines_floor_1_40(),
Geode.golden_coconut: self.region.can_reach(Region.island_north),
Geode.magma: self.mine.can_mine_in_the_mines_floor_81_120() | (self.has(Fish.lava_eel) & self.building.has_building(Building.fish_pond)),
Geode.omni: self.mine.can_mine_in_the_mines_floor_41_80() | self.region.can_reach(Region.desert) | self.tool.has_tool(Tool.pan, ToolMaterial.iron) | self.received(Wallet.rusty_key) | (self.has(Fish.octopus) & self.building.has_building(Building.fish_pond)) | self.region.can_reach(Region.volcano_floor_10),
Geode.magma: self.mine.can_mine_in_the_mines_floor_81_120(), # Could add self.fish_pond.can_get_fish_pond_reward(Fish.lava_eel, 9, Geode.magma) but it makes a logic loop
Geode.omni: self.count(2, *(self.mine.can_mine_in_the_mines_floor_81_120(), self.region.can_reach_all((Region.desert, Region.oasis, Region.sewer)), self.tool.has_pan(ToolMaterial.iron), (self.region.can_reach_all((Region.island_west, Region.island_north,)) & self.has(Consumable.treasure_totem)))), # Could add self.fish_pond.can_get_fish_pond_reward(Fish.octopus, 9, Geode.omni) but it makes a logic loop
Gift.bouquet: self.relationship.has_hearts_with_any_bachelor(8) & self.money.can_spend_at(Region.pierre_store, 100),
Gift.golden_pumpkin: self.season.has(Season.fall) | self.action.can_open_geode(Geode.artifact_trove),
Gift.mermaid_pendant: self.region.can_reach(Region.tide_pools) & self.relationship.has_hearts_with_any_bachelor(10) & self.building.has_building(Building.kitchen) & self.has(Consumable.rain_totem),
Gift.golden_pumpkin: self.festival.has_golden_pumpkin(),
Gift.mermaid_pendant: self.region.can_reach(Region.tide_pools) & self.relationship.has_hearts_with_any_bachelor(10) & self.building.has_building(Building.kitchen) & (self.has(Consumable.rain_totem) | self.season.has_any_not_winter()),
Gift.movie_ticket: self.money.can_spend_at(Region.movie_ticket_stand, 1000),
Gift.pearl: (self.has(Fish.blobfish) & self.building.has_building(Building.fish_pond)) | self.action.can_open_geode(Geode.artifact_trove),
Gift.pearl: self.fish_pond.can_get_fish_pond_reward(Fish.blobfish, 9, Gift.pearl) | self.action.can_open_geode(Geode.artifact_trove),
Gift.tea_set: self.season.has(Season.winter) & self.time.has_lived_max_months,
Gift.void_ghost_pendant: self.money.can_trade_at(Region.desert, Loot.void_essence, 200) & self.relationship.has_hearts(NPC.krobus, 10),
Gift.wilted_bouquet: self.has(Machine.furnace) & self.has(Gift.bouquet) & self.has(Material.coal),
@@ -271,15 +285,17 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
Machine.crab_pot: self.skill.has_level(Skill.fishing, 3) & self.money.can_spend_at(Region.fish_shop, 1500),
Machine.enricher: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 20),
Machine.pressure_nozzle: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 20),
Machine.sewing_machine: (self.region.can_reach(Region.haley_house) & self.has(ArtisanGood.cloth)) | (self.received(Machine.sewing_machine) & self.region.can_reach(Region.secret_woods)),
Machine.statue_endless_fortune: self.has_statue_of_endless_fortune(),
Material.cinder_shard: self.region.can_reach(Region.volcano_floor_5),
Material.clay: self.region.can_reach_any((Region.farm, Region.beach, Region.quarry)) & self.tool.has_tool(Tool.hoe),
Material.coal: self.mine.can_mine_in_the_mines_floor_41_80() | self.tool.has_tool(Tool.pan),
Material.fiber: True_(),
Material.clay: self.region.can_reach_any(Region.farm, Region.beach, Region.quarry) & self.tool.has_tool(Tool.hoe),
Material.coal: self.mine.can_mine_in_the_mines_floor_41_80() | self.tool.has_pan(),
Material.fiber: self.ability.can_scythe_vines(),
Material.hardwood: self.tool.has_tool(Tool.axe, ToolMaterial.copper) & (self.region.can_reach(Region.secret_woods) | self.region.can_reach(Region.island_west)),
Material.moss: self.season.has_any_not_winter() & (self.tool.has_tool(Tool.scythe) | self.combat.has_any_weapon) & self.region.can_reach(Region.forest),
Material.moss: self.season.has_any_not_winter() & (self.tool.has_scythe() | self.combat.has_any_weapon) & self.region.can_reach(Region.forest),
Material.sap: self.ability.can_chop_trees(),
Material.stone: self.tool.has_tool(Tool.pickaxe),
Material.wood: self.tool.has_tool(Tool.axe),
Material.stone: self.ability.can_mine_stone(),
Material.wood: self.ability.can_chop_trees(),
Meal.ice_cream: (self.season.has(Season.summer) & self.money.can_spend_at(Region.town, 250)) | self.money.can_spend_at(Region.oasis, 240),
Meal.strange_bun: self.relationship.has_hearts(NPC.shane, 7) & self.has(Ingredient.wheat_flour) & self.has(Fish.periwinkle) & self.has(ArtisanGood.void_mayonnaise),
MetalBar.copper: self.can_smelt(Ore.copper),
@@ -288,13 +304,19 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
MetalBar.iron: self.can_smelt(Ore.iron),
MetalBar.quartz: self.can_smelt(Mineral.quartz) | self.can_smelt("Fire Quartz") | (self.has(Machine.recycling_machine) & (self.has(Trash.broken_cd) | self.has(Trash.broken_glasses))),
MetalBar.radioactive: self.can_smelt(Ore.radioactive),
Ore.copper: self.mine.can_mine_in_the_mines_floor_1_40() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.copper),
Ore.gold: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.gold),
Ore.iridium: self.count(2, *(self.mine.can_mine_in_the_skull_cavern(), self.can_fish_pond(Fish.super_cucumber), self.tool.has_tool(Tool.pan, ToolMaterial.iridium))),
Ore.iron: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_tool(Tool.pan, ToolMaterial.iron),
Ore.radioactive: self.ability.can_mine_perfectly() & self.region.can_reach(Region.qi_walnut_room),
Mineral.any_gem: self.museum.has_any_gem(),
Ore.copper: self.mine.can_mine_in_the_mines_floor_1_40() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_pan(),
Ore.gold: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_pan(ToolMaterial.gold),
Ore.iridium: self.ability.can_mine_perfectly_in_the_skull_cavern() | (self.mine.can_mine_in_the_skull_cavern() & self.tool.has_pan(ToolMaterial.gold)), # Could add self.fish_pond.can_get_fish_pond_reward(Fish.super_cucumber, 9, Ore.iridium) but it makes a logic loop
Ore.iron: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern() | self.tool.has_pan(ToolMaterial.iron),
Ore.radioactive: self.special_order.can_get_radioactive_ore(),
RetainingSoil.basic: self.money.can_spend_at(Region.pierre_store, 100),
RetainingSoil.quality: self.time.has_year_two & self.money.can_spend_at(Region.pierre_store, 150),
SpecialItem.lucky_purple_shorts: self.special_items.has_purple_shorts(),
SpecialItem.trimmed_purple_shorts: self.has(SpecialItem.lucky_purple_shorts) & self.has(Machine.sewing_machine),
SpecialItem.far_away_stone: self.special_items.has_far_away_stone(),
SpecialItem.solid_gold_lewis: self.special_items.has_solid_gold_lewis(),
SpecialItem.advanced_tv_remote: self.special_items.has_advanced_tv_remote(),
SpeedGro.basic: self.money.can_spend_at(Region.pierre_store, 100),
SpeedGro.deluxe: self.time.has_year_two & self.money.can_spend_at(Region.pierre_store, 150),
Trash.broken_cd: self.fishing.can_crab_pot_anywhere,
@@ -312,11 +334,11 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
Fish.clam: self.tool.can_forage(Generic.any, Region.beach),
Fish.cockle: self.tool.can_forage(Generic.any, Region.beach),
WaterItem.green_algae: self.fishing.can_fish_in_freshwater(),
WaterItem.cave_jelly: self.fishing.can_fish_at(Region.mines_floor_100) & self.tool.has_fishing_rod(2),
WaterItem.river_jelly: self.fishing.can_fish_at(Region.town) & self.tool.has_fishing_rod(2),
WaterItem.sea_jelly: self.fishing.can_fish_at(Region.beach) & self.tool.has_fishing_rod(2),
WaterItem.cave_jelly: self.fishing.can_fish_at(Region.mines_floor_100) & self.tool.has_fishing_rod(FishingRod.bamboo),
WaterItem.river_jelly: self.fishing.can_fish_at(Region.town) & self.tool.has_fishing_rod(FishingRod.bamboo),
WaterItem.sea_jelly: self.fishing.can_fish_at(Region.beach) & self.tool.has_fishing_rod(FishingRod.bamboo),
WaterItem.seaweed: self.fishing.can_fish_at(Region.tide_pools),
WaterItem.white_algae: self.fishing.can_fish_at(Region.mines_floor_20),
WaterItem.white_algae: self.fishing.can_fish_at(Region.mines_floor_20) & self.tool.has_fishing_rod(FishingRod.bamboo),
WildSeeds.grass_starter: self.money.can_spend_at(Region.pierre_store, 100),
})
# @formatter:on
@@ -351,6 +373,10 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
obtention_rule = self.registry.item_rules[recipe] if recipe in self.registry.item_rules else False_()
self.registry.item_rules[recipe] = obtention_rule | crafting_rule
if self.options.bundle_randomization == BundleRandomization.option_meme:
self.meme.initialize_rules()
self.registry.item_rules.update(self.registry.meme_item_rules)
self.quest.initialize_rules()
self.quest.update_rules(self.mod.quest.get_modded_quest_rules())
@@ -359,31 +385,59 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
self.special_order.initialize_rules()
self.special_order.update_rules(self.mod.special_order.get_modded_special_orders_rules())
self.shirt.initialize_rules()
self.registry.item_rules.update(self.registry.shirt_rules)
for catalogue in items_by_catalogue:
for item in items_by_catalogue[catalogue]:
self.registry.item_rules[item] = self.has(catalogue)
for boots in tier_by_boots:
self.registry.item_rules[boots] = self.combat.has_specific_boots(boots)
def setup_events(self, register_event: Callable[[str, str, StardewRule], None]) -> None:
for logic_event in all_logic_events:
rule = self.registry.item_rules[logic_event.item]
register_event(logic_event.name, logic_event.region, rule)
self.registry.item_rules[logic_event.item] = self.received(logic_event.name)
for item_event in all_item_events:
rule = self.registry.item_rules[item_event.item]
if isinstance(rule, Or) and bool(reaches := [r for r in rule.current_rules if isinstance(r, Reach) and r.resolution_hint == "Region"]):
logger.debug("Sharding rule for %s in multiple logic events, placed in %s.", item_event.item, [r.spot for r in reaches])
for i, reach in enumerate(reaches):
location_name = f"{item_event.name} sharded_{i}"
new_rule = self.region.can_reach(item_event.region)
register_event(item_event.name, reach.spot, new_rule, location_name=location_name)
remaining_rules = [r for r in rule.current_rules if not isinstance(r, Reach) or r.resolution_hint != "Region"]
if remaining_rules:
register_event(item_event.name, item_event.region, Or(*remaining_rules))
else:
register_event(item_event.name, item_event.region, rule)
self.registry.item_rules[item_event.item] = self.received(item_event.name)
def can_smelt(self, item: str) -> StardewRule:
return self.has(Machine.furnace) & self.has(item)
def has_island_trader(self) -> StardewRule:
if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return False_()
return self.region.can_reach(Region.island_trader)
if self.content.is_enabled(ginger_island_content_pack):
return self.region.can_reach(Region.island_trader)
return self.logic.false_
def has_abandoned_jojamart(self) -> StardewRule:
return self.received(CommunityUpgrade.movie_theater, 1)
return (self.received(CommunityUpgrade.movie_theater, 1) & self.season.has_any_not_winter()) | self.has_movie_theater()
def has_movie_theater(self) -> StardewRule:
return self.received(CommunityUpgrade.movie_theater, 2)
def can_use_obelisk(self, obelisk: str) -> StardewRule:
return self.region.can_reach(Region.farm) & self.received(obelisk)
return self.region.can_reach(Region.farm) & self.building.has_wizard_building(obelisk)
def can_fish_pond(self, fish: str, *items: str) -> StardewRule:
rule = self.building.has_building(Building.fish_pond) & self.has(fish)
if items:
rule = rule & self.has_all(*items)
return rule
def can_purchase_statue_of_endless_fortune(self) -> StardewRule:
return self.money.can_spend_at(Region.casino, 1_000_000)
def has_statue_of_endless_fortune(self) -> StardewRule:
can_purchase_rule = self.can_purchase_statue_of_endless_fortune()
if self.options.include_endgame_locations == IncludeEndgameLocations.option_true:
return can_purchase_rule & self.received(Machine.statue_endless_fortune)
return can_purchase_rule

View File

@@ -1,11 +1,14 @@
from __future__ import annotations
from dataclasses import dataclass
from ..strings.ap_names import event_names
from ..strings.material_names import Material
from ..strings.metal_names import MetalBar, Ore
from ..strings.region_names import Region
all_events = event_names.all_events.copy()
all_logic_events = list()
all_item_events: list[LogicItemEvent] = list()
@dataclass(frozen=True)
@@ -25,9 +28,9 @@ class LogicItemEvent(LogicEvent):
def register_item_event(item: str, region: str = Region.farm):
event = LogicItemEvent(item, region)
all_logic_events.append(event)
all_item_events.append(event)
all_events.add(event.name)
for i in (MetalBar.copper, MetalBar.iron, MetalBar.gold, MetalBar.iridium, Ore.copper, Ore.iron, Ore.gold, Ore.iridium):
for i in (Material.coal, MetalBar.copper, MetalBar.iron, MetalBar.gold, MetalBar.iridium, Ore.copper, Ore.iron, Ore.gold, Ore.iridium):
register_item_event(i)

View File

@@ -0,0 +1,57 @@
from .base_logic import BaseLogicMixin, BaseLogic
from ..data.game_item import ItemTag
from ..strings.ap_names.ap_weapon_names import APWeapon
from ..strings.meme_item_names import MemeItem
from ..strings.ring_names import all_ring_names
from ..strings.special_item_names import NotReallyAnItem
class MemeItemsLogicMixin(BaseLogicMixin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.meme = MemeItemsLogic(*args, **kwargs)
class MemeItemsLogic(BaseLogic):
def initialize_rules(self):
self.registry.meme_item_rules.update({
MemeItem.trap: self.logic.true_,
MemeItem.pot_of_gold: self.can_cheat(),
MemeItem.seed_spot: self.can_cheat(),
MemeItem.green_rain_weeds_0: self.can_cheat(),
MemeItem.lumber: self.can_cheat(),
MemeItem.weeds: self.can_cheat(),
MemeItem.twig: self.can_cheat(),
MemeItem.artifact_spot: self.can_cheat(),
MemeItem.warp_totem_qis_arena: self.can_cheat(),
MemeItem.supply_crate: self.can_cheat(),
MemeItem.slime_crate: self.can_cheat(),
MemeItem.decorative_pot: self.can_cheat(),
MemeItem.camping_stove: self.can_cheat(),
MemeItem.worn_pants: self.has_any_pants(),
MemeItem.worn_left_ring: self.has_any_ring(),
MemeItem.worn_right_ring: self.has_any_ring(),
MemeItem.worn_shirt: self.has_any_shirt(),
MemeItem.worn_boots: self.has_any_boots(),
MemeItem.worn_hat: self.has_any_hat(),
NotReallyAnItem.death: self.logic.true_,
})
def can_cheat(self):
return self.logic.true_
def has_any_pants(self):
return self.logic.true_
def has_any_shirt(self):
return self.logic.true_
def has_any_hat(self):
return self.logic.has_any(*[item.name for item in self.content.find_tagged_items(ItemTag.HAT)])
def has_any_ring(self):
return self.logic.received_any(*all_ring_names)
def has_any_boots(self):
return self.logic.received(APWeapon.footwear)

View File

@@ -2,6 +2,7 @@ from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from .. import options
from ..stardew_rule import StardewRule, True_
from ..strings.ap_names.ap_option_names import CustomLogicOptionName
from ..strings.performance_names import Performance
from ..strings.region_names import Region
from ..strings.skill_names import Skill
@@ -26,8 +27,7 @@ class MineLogic(BaseLogic):
return self.logic.region.can_reach(Region.mines_floor_85)
def can_mine_in_the_skull_cavern(self) -> StardewRule:
return (self.logic.mine.can_progress_in_the_mines_from_floor(120) &
self.logic.region.can_reach(Region.skull_cavern))
return self.logic.region.can_reach(Region.skull_cavern_mining)
@cache_self1
def get_weapon_rule_for_floor_tier(self, tier: int):
@@ -43,23 +43,40 @@ class MineLogic(BaseLogic):
@cache_self1
def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule:
tier = floor // 40
assert floor >= 0
# 0-39, 40-79, 80-119
mine_tier = floor // 40
combat_tier = mine_tier
rules = []
weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier)
if CustomLogicOptionName.extreme_combat in self.options.custom_logic:
combat_tier -= 2
elif CustomLogicOptionName.hard_combat in self.options.custom_logic:
combat_tier -= 1
elif CustomLogicOptionName.easy_combat in self.options.custom_logic:
combat_tier += 1
combat_tier = max(0, combat_tier)
if CustomLogicOptionName.extreme_mining in self.options.custom_logic:
mine_tier -= 2
elif CustomLogicOptionName.hard_mining in self.options.custom_logic:
mine_tier -= 1
elif self.options.tool_progression.is_progressive and CustomLogicOptionName.easy_mining in self.options.custom_logic:
mine_tier += 1
mine_tier = max(0, mine_tier)
weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(combat_tier)
rules.append(weapon_rule)
tool_rule = self.logic.tool.can_mine_using(ToolMaterial.tiers[tier])
tool_rule = self.logic.tool.can_mine_using(ToolMaterial.tiers[min(5, mine_tier + 1)])
rules.append(tool_rule)
# No alternative for vanilla because we assume that you will grind the levels in the mines.
if self.content.features.skill_progression.is_progressive:
skill_level = min(10, max(0, tier * 2))
rules.append(self.logic.skill.has_level(Skill.combat, skill_level))
rules.append(self.logic.skill.has_level(Skill.mining, skill_level))
if tier >= 4:
rules.append(self.logic.cooking.can_cook())
combat_level = min(10, max(0, mine_tier * 2))
mining_level = min(10, max(0, mine_tier * 2))
rules.append(self.logic.skill.has_level(Skill.combat, combat_level))
rules.append(self.logic.skill.has_level(Skill.mining, mining_level))
return self.logic.and_(*rules)
@@ -73,19 +90,47 @@ class MineLogic(BaseLogic):
@cache_self1
def can_progress_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule:
tier = floor // 50
assert floor >= 0
# 0-49, 50-99, 100-149, 150-199, 200-249
mining_tier = floor // 50
combat_tier = mining_tier
rules = []
weapon_rule = self.logic.combat.has_great_weapon
if CustomLogicOptionName.extreme_combat in self.options.custom_logic:
weapon_rule = self.logic.combat.has_decent_weapon
combat_tier -= 2
elif CustomLogicOptionName.hard_combat in self.options.custom_logic:
weapon_rule = self.logic.combat.has_good_weapon
combat_tier -= 1
elif CustomLogicOptionName.easy_combat in self.options.custom_logic:
weapon_rule = self.logic.combat.has_galaxy_weapon
combat_tier += 1
else:
weapon_rule = self.logic.combat.has_great_weapon
combat_tier = max(0, combat_tier)
if CustomLogicOptionName.extreme_mining in self.options.custom_logic:
mining_tier -= 2
elif CustomLogicOptionName.hard_mining in self.options.custom_logic:
mining_tier -= 1
elif self.options.tool_progression.is_progressive and CustomLogicOptionName.easy_mining in self.options.custom_logic:
mining_tier += 1
tool_tier = mining_tier + 2
tool_tier = min(5, max(1, tool_tier))
mining_tier = max(0, mining_tier)
rules.append(weapon_rule)
tool_rule = self.logic.tool.can_mine_using(ToolMaterial.tiers[min(4, max(0, tier + 2))])
tool_rule = self.logic.tool.can_mine_using(ToolMaterial.tiers[tool_tier])
rules.append(tool_rule)
# No alternative for vanilla because we assume that you will grind the levels in the mines.
if self.content.features.skill_progression.is_progressive:
skill_level = min(10, max(0, tier * 2 + 6))
rules.extend((self.logic.skill.has_level(Skill.combat, skill_level),
self.logic.skill.has_level(Skill.mining, skill_level)))
combat_level = min(10, max(0, combat_tier * 2 + 6))
mining_level = min(10, max(0, mining_tier * 2 + 6))
rules.append(self.logic.skill.has_level(Skill.combat, combat_level))
rules.append(self.logic.skill.has_level(Skill.mining, mining_level))
rules.append(self.logic.cooking.can_cook())
return self.logic.and_(*rules)

View File

@@ -1,10 +1,19 @@
from Options import DeathLink
from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from ..data.shop import ShopSource
from ..options import SpecialOrderLocations
from ..content.vanilla.qi_board import qi_board_content_pack
from ..data.shop import ShopSource, HatMouseSource
from ..stardew_rule import StardewRule, True_, HasProgressionPercent, False_, true_
from ..strings.currency_names import Currency
from ..strings.animal_names import Animal
from ..strings.ap_names.ap_option_names import CustomLogicOptionName
from ..strings.ap_names.event_names import Event
from ..strings.artisan_good_names import ArtisanGood
from ..strings.building_names import Building
from ..strings.crop_names import Vegetable
from ..strings.currency_names import Currency, MemeCurrency
from ..strings.food_names import Beverage
from ..strings.region_names import Region, LogicRegion
from ..strings.season_names import Season
qi_gem_rewards = ("100 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems", "25 Qi Gems",
"20 Qi Gems", "15 Qi Gems", "10 Qi Gems")
@@ -20,52 +29,77 @@ class MoneyLogic(BaseLogic):
@cache_self1
def can_have_earned_total(self, amount: int) -> StardewRule:
if CustomLogicOptionName.nightmare_money in self.options.custom_logic:
amount /= 20
elif CustomLogicOptionName.extreme_money in self.options.custom_logic:
amount /= 8
elif CustomLogicOptionName.hard_money in self.options.custom_logic:
amount /= 2
elif CustomLogicOptionName.easy_money in self.options.custom_logic:
amount *= 4
if amount <= 1000:
return self.logic.true_
pierre_rule = self.logic.region.can_reach_all((Region.pierre_store, Region.forest))
willy_rule = self.logic.region.can_reach_all((Region.fish_shop, LogicRegion.fishing))
clint_rule = self.logic.region.can_reach_all((Region.blacksmith, Region.mines_floor_5))
robin_rule = self.logic.region.can_reach_all((Region.carpenter, Region.secret_woods))
shipping_rule = self.logic.shipping.can_use_shipping_bin
pierre_rule = self.logic.region.can_reach_all(Region.pierre_store, Region.forest)
willy_rule = self.logic.region.can_reach_all(Region.fish_shop, LogicRegion.fishing)
clint_rule = self.logic.region.can_reach_all(Region.blacksmith, Region.mines_floor_5)
robin_rule = self.logic.region.can_reach_all(Region.carpenter, Region.secret_woods)
farming_rule = self.logic.farming.can_plant_and_grow_item(Season.not_winter)
if amount <= 2500:
selling_any_rule = pierre_rule | willy_rule | clint_rule | robin_rule | shipping_rule
if amount <= 2000:
selling_any_rule = shipping_rule | pierre_rule | willy_rule | clint_rule | robin_rule
return selling_any_rule
if amount <= 3000:
selling_any_rule = shipping_rule | pierre_rule | willy_rule
return selling_any_rule
if amount <= 5000:
selling_all_rule = (pierre_rule & willy_rule & clint_rule & robin_rule) | shipping_rule
selling_all_rule = shipping_rule | (pierre_rule & farming_rule) | (pierre_rule & willy_rule & clint_rule & robin_rule)
return selling_all_rule
if amount <= 10000:
return shipping_rule
return shipping_rule & farming_rule
seed_rules = self.logic.region.can_reach(Region.pierre_store)
if amount <= 40000:
return shipping_rule & seed_rules
return shipping_rule & seed_rules & farming_rule
percent_progression_items_needed = min(90, amount // 20000)
return shipping_rule & seed_rules & HasProgressionPercent(self.player, percent_progression_items_needed)
return shipping_rule & seed_rules & farming_rule & HasProgressionPercent(self.player, percent_progression_items_needed)
@cache_self1
def can_spend(self, amount: int) -> StardewRule:
if self.options.starting_money == -1:
return True_()
return self.logic.money.can_have_earned_total(amount * 5)
spend_earned_multiplier = 5 # We assume that if you earned 5x an amount, you can reasonably spend that amount on things
return self.logic.money.can_have_earned_total(amount * spend_earned_multiplier)
# Should be cached
def can_spend_at(self, region: str, amount: int) -> StardewRule:
return self.logic.region.can_reach(region) & self.logic.money.can_spend(amount)
def can_shop_from_hat_mouse(self, source: HatMouseSource) -> StardewRule:
money_rule = self.logic.money.can_spend(source.price) if source.price is not None else true_
region_rule = self.logic.region.can_reach(LogicRegion.hat_mouse)
requirements_rule = self.logic.requirement.meet_all_requirements(source.unlock_requirements) if source.unlock_requirements is not None else true_
return money_rule & region_rule & requirements_rule
@cache_self1
def can_shop_from(self, source: ShopSource) -> StardewRule:
season_rule = self.logic.season.has_any(source.seasons)
money_rule = self.logic.money.can_spend(source.money_price) if source.money_price is not None else true_
if source.currency == Currency.money:
money_rule = self.logic.money.can_spend(source.price) if source.price is not None else true_
else:
money_rule = self.logic.money.can_trade_at(source.shop_region, source.currency, source.price) if source.price is not None else true_
item_rules = []
if source.items_price is not None:
for price, item in source.items_price:
item_rules.append(self.logic.has(item) & self.logic.grind.can_grind_item(price, item))
item_rules.append(self.logic.grind.can_grind_item(price, item))
region_rule = self.logic.region.can_reach(source.shop_region)
@@ -74,24 +108,71 @@ class MoneyLogic(BaseLogic):
# Should be cached
def can_trade(self, currency: str, amount: int) -> StardewRule:
if amount == 0:
return True_()
if currency == Currency.money:
return self.logic.true_
if currency == Currency.money or currency == MemeCurrency.bank_money:
return self.can_spend(amount)
if currency == Currency.star_token:
return self.logic.region.can_reach(LogicRegion.fair)
if currency == Currency.qi_coin:
return self.logic.region.can_reach(Region.casino) & self.logic.time.has_lived_months(amount // 1000)
if currency == Currency.qi_gem:
if self.options.special_order_locations & SpecialOrderLocations.value_qi:
number_rewards = min(len(qi_gem_rewards), max(1, (amount // 10)))
return self.logic.received_n(*qi_gem_rewards, count=number_rewards)
number_rewards = 2
return self.logic.received_n(*qi_gem_rewards, count=number_rewards) & self.logic.region.can_reach(Region.qi_walnut_room) & \
self.logic.region.can_reach(Region.saloon) & self.can_have_earned_total(5000)
if self.content.is_enabled(qi_board_content_pack):
return self.logic.received(Event.received_qi_gems, amount * 3)
return self.logic.region.can_reach_all(Region.qi_walnut_room, Region.saloon) & self.can_have_earned_total(5000)
if currency == Currency.golden_walnut:
return self.can_spend_walnut(amount)
return self.logic.has(currency) & self.logic.grind.can_grind_item(amount)
if currency == MemeCurrency.code or currency == MemeCurrency.energy or currency == MemeCurrency.blood:
return self.logic.true_
if currency == MemeCurrency.clic and amount < 100:
return self.logic.true_
if currency == MemeCurrency.clic or currency == MemeCurrency.time:
return self.logic.time.has_lived_months(1)
if currency == MemeCurrency.steps and amount < 6000:
return self.logic.true_
if currency == MemeCurrency.steps:
return self.logic.time.has_lived_months(amount // 10000)
if currency == MemeCurrency.cookies:
return self.logic.time.has_lived_months(amount // 10000)
if currency == MemeCurrency.child:
return self.logic.relationship.has_children(1)
if currency == MemeCurrency.dead_crops:
return self.logic.season.has_all() & self.logic.skill.can_get_farming_xp & self.logic.money.can_spend(amount * 100)
if currency == MemeCurrency.dead_pumpkins:
return self.logic.season.has(Season.fall) & self.logic.season.has_any([Season.spring, Season.summer, Season.winter]) & \
self.logic.has(Vegetable.pumpkin) & self.logic.money.can_spend(amount * 100)
if currency == MemeCurrency.missed_fish:
return self.logic.fishing.can_catch_many_fish(max(1, amount // 4))
if currency == MemeCurrency.honeywell:
return self.logic.has(ArtisanGood.honey) & self.logic.building.has_building(Building.well)
if currency == MemeCurrency.goat:
return self.logic.animal.has_animal(Animal.goat)
if currency == MemeCurrency.sleep_days:
if not self.options.multiple_day_sleep_enabled.value:
return self.logic.false_
if amount > 200:
return self.logic.region.can_reach(Region.farm_house) & self.logic.season.has(Season.winter)
return self.logic.region.can_reach(Region.farm_house)
if currency == MemeCurrency.time_elapsed:
if amount <= 1000:
return self.logic.true_
if amount <= 1400:
return self.logic.has(Beverage.coffee)
if amount <= 1800:
return self.logic.building.has_building(Building.stable)
return self.logic.has(Beverage.coffee) & self.logic.building.has_building(Building.stable)
if currency == MemeCurrency.deathlinks:
if self.options.death_link == DeathLink.option_true:
return self.logic.time.has_lived_months(amount)
return self.logic.false_
return self.logic.grind.can_grind_item(amount, currency)
# Should be cached
def can_trade_at(self, region: str, currency: str, amount: int) -> StardewRule:

View File

@@ -3,12 +3,9 @@ from typing import Iterable, Union, Hashable
from Utils import cache_self1
from .base_logic import BaseLogicMixin, BaseLogic
from .combat_logic import CombatLogicMixin
from .has_logic import HasLogicMixin
from .region_logic import RegionLogicMixin
from .time_logic import TimeLogicMixin, MAX_MONTHS
from .. import options
from .time_logic import MAX_MONTHS
from ..data import monster_data
from ..data.fish_data import ginger_island_river
from ..stardew_rule import StardewRule
from ..strings.generic_names import Generic
from ..strings.region_names import Region
@@ -24,11 +21,11 @@ class MonsterLogic(BaseLogic):
@cached_property
def all_monsters_by_name(self):
return monster_data.all_monsters_by_name_given_mods(self.options.mods.value)
return monster_data.all_monsters_by_name_given_content_packs(self.content.registered_packs)
@cached_property
def all_monsters_by_category(self):
return monster_data.all_monsters_by_category_given_mods(self.options.mods.value)
return monster_data.all_monsters_by_category_given_content_packs(self.content.registered_packs)
def can_kill(self, monster: Union[str, monster_data.StardewMonster], amount_tier: int = 0) -> StardewRule:
if amount_tier <= 0:
@@ -40,30 +37,30 @@ class MonsterLogic(BaseLogic):
return self.logic.monster.can_kill_any(self.all_monsters_by_name.values()) & time_rule
monster = self.all_monsters_by_name[monster]
region_rule = self.logic.region.can_reach_any(monster.locations)
region_rule = self.logic.region.can_reach_any(*monster.locations)
combat_rule = self.logic.combat.can_fight_at_level(monster.difficulty)
return region_rule & combat_rule & time_rule
@cache_self1
def can_kill_many(self, monster: monster_data.StardewMonster) -> StardewRule:
def can_kill_many(self, monster: Union[str, monster_data.StardewMonster]) -> StardewRule:
return self.logic.monster.can_kill(monster, MAX_MONTHS / 3)
@cache_self1
def can_kill_max(self, monster: monster_data.StardewMonster) -> StardewRule:
def can_kill_max(self, monster: Union[str, monster_data.StardewMonster]) -> StardewRule:
return self.logic.monster.can_kill(monster, MAX_MONTHS)
# Should be cached
def can_kill_any(self, monsters: (Iterable[monster_data.StardewMonster], Hashable), amount_tier: int = 0) -> StardewRule:
def can_kill_any(self, monsters: (Iterable[Union[str, monster_data.StardewMonster]], Hashable), amount_tier: int = 0) -> StardewRule:
return self.logic.or_(*(self.logic.monster.can_kill(monster, amount_tier) for monster in monsters))
# Should be cached
def can_kill_all(self, monsters: (Iterable[monster_data.StardewMonster], Hashable), amount_tier: int = 0) -> StardewRule:
def can_kill_all(self, monsters: (Iterable[Union[str, monster_data.StardewMonster]], Hashable), amount_tier: int = 0) -> StardewRule:
return self.logic.and_(*(self.logic.monster.can_kill(monster, amount_tier) for monster in monsters))
def can_complete_all_monster_slaying_goals(self) -> StardewRule:
rules = [self.logic.time.has_lived_max_months]
exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true
exclude_island = not self.content.is_enabled(ginger_island_river)
island_regions = [Region.volcano_floor_5, Region.volcano_floor_10, Region.island_west, Region.dangerous_skull_cavern]
for category in self.all_monsters_by_category:
if exclude_island and all(all(location in island_regions for location in monster.locations)

Some files were not shown because too many files have changed in this diff Show More